ProtoBuf的序列化和反序列化(基础库)

内容说明:

  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…)性能的基准测试到可以关注测试一下

  • 6
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
ProtobufProtocol Buffers)是一种由Google开发的高效的序列化反序列化技术。它可以将结构化数据转换为紧凑且高效的二进制格式,以便在不同的系统之间进行数据交换和存储。下面是Protobuf序列化反序列化的介绍和示例: 1. 定义消息结构:首先,我们需要定义消息的结构,即使用Protobuf的语法来定义消息的字段和类型。例如,我们可以定义一个名为Person的消息,其中包含姓名和年龄两个字段: ```protobuf syntax = "proto3"; message Person { string name = 1; int32 age = 2; } ``` 2. 编译消息定义:接下来,我们需要使用Protobuf编译器将消息定义编译成相应的代码文件。例如,使用protoc命令将上述消息定义编译成Python代码: ```shell protoc --python_out=. person.proto ``` 3. 序列化:在发送方,我们可以使用生成的代码将消息对象序列化为二进制数据。例如,在Python中,我们可以使用生成的代码创建Person对象,并将其序列化为字节串: ```python from person_pb2 import Person person = Person() person.name = "Alice" person.age = 25 serialized_data = person.SerializeToString() ``` 4. 反序列化:在接收方,我们可以使用生成的代码将接收到的二进制数据反序列化为消息对象。例如,在Python中,我们可以使用生成的代码将字节串反序列化为Person对象: ```python from person_pb2 import Person received_data = b'\n\x05Alice\x10\x19' person = Person() person.ParseFromString(received_data) print(person.name) # 输出:Alice print(person.age) # 输出:25 ``` 通过使用Protobuf进行序列化反序列化,我们可以实现高效的数据交换和存储,同时减少网络传输和磁盘空间的占用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

suqinyi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值