内容说明:
  1. 说明下什么是ProtoBuf
  2. ProtoBuf的序列化和反序列化
  3. ProtoBuf的优势
  4. 前后端使用ProtoBuf交互
ProtoBuf简介:
Google 的 ProtoBuf ==> 将文件转成二进制文件

protocol buffers 是一种语言无关、平台无关、可扩展的序列化结构数据的方法,它可用于(数据)通信协议、数据存储等。


Protocol Buffers 是一种灵活,高效,自动化机制的结构数据序列化方法-可类比 XML,但是比 XML 更小(3 ~ 10倍)、更快(20 ~ 100倍)、更为简单。
你可以定义数据的结构,然后使用特殊生成的源代码轻松的在各种数据流中使用各种语言进行编写和读取结构数据。你甚至可以更新数据结构,而不破坏由旧数据结构编译的已部署程序。

使用ProtoBuf是定义(前后端) ==> .proto类型

这个比较麻烦小编会在专门写一篇文件说明 ==> 待补充(前后端使用ProtoBuf交互)


ProtoBuf和Json和xml的小结:
  1. XML、JSON、ProtoBuf 都具有数据结构化和数据序列化的能力
  2. XML、JSON 更注重数据结构化,关注人类可读性和语义表达能力
  3. ProtoBuf 更注重数据序列化,关注效率、空间、速度,人类可读性差,语义表达能力不足(为保证极致的效率,会舍弃一部分元信息)
  4. ProtoBuf 的应用场景更为明确,XML、JSON 的应用场景更为丰富
使用场景

1、跨平台的RPC数据传输
或者说:需要和其它系统做消息交换的,对消息大小很敏感的,那么protobuf适合了,它语言无关,消息空间相对xml和json等节省很多
.
2、在一个需要大量的数据传输的场景中,如果数据量很大,那么选择protobuf可以明显的减少数据量,减少网络IO,从而减少网络传输所消耗的时间


ProtoBuf的序列化和反序列化:

1、引入依赖

<!--引入protostuff依赖-->
 <dependency>
      <groupId>io.protostuff</groupId>
      <artifactId>protostuff-core</artifactId>
      <version>1.6.0</version>
  </dependency>

  <dependency>
      <groupId>io.protostuff</groupId>
      <artifactId>protostuff-runtime</artifactId>
      <version>1.6.0</version>
  </dependency>

2、将protobuf封装成基础库

package sqy.test01.util;

import io.protostuff.LinkedBuffer;
import io.protostuff.ProtostuffIOUtil;
import io.protostuff.Schema;
import io.protostuff.runtime.RuntimeSchema;

import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author suqinyi
 * @Date 2021/5/10
 */
public class ProtostuffUtils {
    /**
     * 避免每次序列化都重新申请Buffer空间
     */
    private static LinkedBuffer buffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE);
    /**
     * 缓存Schema
     */
    private static Map<Class<?>, Schema<?>> schemaCache = new ConcurrentHashMap<>();

    /**
     * 序列化方法,把指定对象序列化成字节数组
     *
     * @param obj
     * @param <T>
     * @return
     */
    @SuppressWarnings("unchecked")
    public static <T> byte[] serialize(T obj) {
        Class<T> clazz = (Class<T>) obj.getClass();
        Schema<T> schema = getSchema(clazz);
        byte[] data;
        try {
            data = ProtostuffIOUtil.toByteArray(obj, schema, buffer);
        } finally {
            buffer.clear();
        }

        return data;
    }

    /**
     * 反序列化方法,将字节数组反序列化成指定Class类型
     *
     * @param data
     * @param clazz
     * @param <T>
     * @return
     */
    public static <T> T deserialize(byte[] data, Class<T> clazz) {
        Schema<T> schema = getSchema(clazz);
        T obj = schema.newMessage();
        ProtostuffIOUtil.mergeFrom(data, obj, schema);
        return obj;
    }

    @SuppressWarnings("unchecked")
    private static <T> Schema<T> getSchema(Class<T> clazz) {
        Schema<T> schema = (Schema<T>) schemaCache.get(clazz);
        if (Objects.isNull(schema)) {
            //这个schema通过RuntimeSchema进行懒创建并缓存
            //所以可以一直调用RuntimeSchema.getSchema(),这个方法是线程安全的
            schema = RuntimeSchema.getSchema(clazz);
            if (Objects.nonNull(schema)) {
                schemaCache.put(clazz, schema);
            }
        }

        return schema;
    }
}

3、序列化和反序列化测试

3.1、简单数据的实体类

编写实体类:Student

package sqy.test01.pojo;
import io.protostuff.Tag;

/**
 * @author suqinyi
 * @Date 2021/5/10
 */
public class Student {

	/**
	 * @Tag,要么所有属性都有@Tag注解,
	 * 要么都没有,不能一个类中只有部分属性有@Tag注解
	 */
    @Tag(1)
    private String name;
    @Tag(2)
    private String studentNo;
    @Tag(3)
    private int age;
    @Tag(4)
    private String schoolName;

    
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getStudentNo() {
        return studentNo;
    }
    public void setStudentNo(String studentNo) {
        this.studentNo = studentNo;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public String getSchoolName() {
        return schoolName;
    }
    public void setSchoolName(String schoolName) {
        this.schoolName = schoolName;
    }
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", studentNo='" + studentNo + '\'' +
                ", age=" + age +
                ", schoolName='" + schoolName + '\'' +
                '}';
    }
}

测试代码:

package sqy.test01;

import sqy.test01.pojo.Student;
import sqy.test01.util.ProtostuffUtils;

import java.util.Arrays;

/**
 * @author suqinyi
 * @Date 2021/5/10
 */
public class test02 {

    public static void main(String[] args) {
        Student student = new Student();
        student.setName("lance");
        student.setAge(28);
        student.setStudentNo("13123131");
        student.setSchoolName("BJUT");

        byte[] serializerResult = ProtostuffUtils.serialize(student);

        System.out.println("serializer result:"+Arrays.toString(serializerResult));

        Student deSerializerResult = ProtostuffUtils.deserialize(serializerResult, Student.class);

        System.out.println("deSerializerResult:"+deSerializerResult.toString());
    }
}

效果:

3.2、复杂一点的数据(2个实体类)

第一步:简化编程添加lombok插件和注解

  1. 先安装lombok插件
  2. 在pom文件补充一下依赖
  3. lombok可以说是入侵式编程,业界对他褒贬不一,下一篇小结下他的优劣之处
<!--lombok-->
 <dependency>
     <groupId>org.projectlombok</groupId>
     <artifactId>lombok</artifactId>
     <version>1.16.10</version>
     <scope>provided</scope>
 </dependency>

第二步:定义俩个实体类User 和 Group

import lombok.Builder;
import lombok.Data;

/**
 * @author suqinyi
 * @Date 2021/5/10
 */
//lombok注解
@Data//自动生成get/set方法
@Builder//启用链式编程
public class User {
    private String id;
    private String name;
    private Integer age;
    private String desc;

}

//==================第二个实体类======================

import lombok.Builder;
import lombok.Data;

@Data
@Builder
public class Group {
    private String id;
    private String name;
    private User user;
}

第三步:测试代码

package sqy.test01;

import sqy.test01.pojo.Group;
import sqy.test01.pojo.User;
import sqy.test01.util.ProtostuffUtils;

import java.util.Arrays;

/**
 * @author suqinyi
 * @Date 2021/5/10
 */
public class test01 {
    /**
     * 验证序列化功能
     */
    public static void main(String[] args) {
        //创建一个user对象
        User user = User.builder().id("1").age(20).name("张三").desc("programmer").build();
        //创建一个Group对象==》将user放入group中
        Group group = Group.builder().id("1").name("分组1").user(user).build();
        //使用ProtostuffUtils序列化
        byte[] data = ProtostuffUtils.serialize(group);
        System.out.println("序列化后:" + Arrays.toString(data));
        Group result = ProtostuffUtils.deserialize(data, Group.class);
        System.out.println("反序列化后:" + result.toString());
    }

}

效果:

完结

对于ProtoBuf的基准测试,小编感觉没多大必要
对于json系类各种json(gson、fastjson、jackson…)性能的基准测试到可以关注测试一下