首页 > 其他分享 >一文带你搞懂protobuf

一文带你搞懂protobuf

时间:2024-07-01 22:29:34浏览次数:18  
标签:protobuf proto System println user 一文 搞懂 序列化 out

protobuf简介
1、什么是 protobuf

Protocal Buffers(简称protobuf)是谷歌的一项技术,用于结构化的数据序列化、反序列化。

官方解释:Protocol Buffers 是一种语言无关、平台无关、可扩展的序列化结构数据的方法,它可用于(数据)通信协议、数据存储等。Protocol Buffers 是一种灵活,高效,自动化机制的结构数据序列化方法。可类比 XML,但是比 XML 更小(3 ~ 10倍)、更快(20 ~ 100倍)、更为简单。

你可以定义数据的结构,然后使用特殊生成的源代码轻松的在各种数据流中使用各种语言进行编写和读取结构数据。你甚至可以更新数据结构,而不破坏由旧数据结构编译的已部署程序。

2、为什么使用protobuf

由于 protobuf是跨语言的,所以用不同的语言序列化对象后,生成一段字节码,之后可以其他任何语言反序列化并自用,大大方便了跨语言的通讯,同时也提高了效率。

需要注意: protobuf生成的是字节码,可读性相比略差一点。

二、protobuf数据类型

创建 FileName.proto文件,后缀名称必须是.proto。一般一个文件就代表一个 proto对象。在文件中定义 proto 对象的属性。通过 .proto文件可以生成不同语言的类,用于结构化的数据序列化、反序列化。

protobuf官方文档:https://protobuf.dev/programming-guides/proto3/

定义一个 proto 对象的属性,基本格式如下:

字段标签(可选) 字段类型 字段名称 字段标识符 字段默认值(可选)

关于字段编号(标识符),是字段中唯一且必须的,以 1开始,不能重复,不能跳值,这个是和编译有关系的。

1、基本数据类型

常见基本数据类型:

系统默认值:

  • string:默认为空字符串
  • byte:默认值为空字节
  • bool:默认为false
  • 数值:默认为0
  • enum:默认为第一个元素

示例如下:

syntax = "proto3";

//创建一个 SearchRequest 对象
message SearchRequest {
  string query = 1;
  int32 page_number = 2;
  int32 results_per_page = 3;
}

3、复杂数据类型

3.1 集合List字段

java String、数组在 protobuf 的定义。

message User{
  //list Int
  repeated int32 intList = 1;
  //list String
  repeated string strList = 2;
}

3.2 Map字段

java String、Integer Map 在 protobuf 的定义。

message User{
  // 定义简单的 Map string
  map<string, int32> intMap = 7;
  // 定义复杂的 Map 对象
  map<string, string> stringMap = 8;
}

3.3 对象字段

java对象 List 在 protobuf 的定义。

message User{
  //list 对象
  repeated Role roleList = 6;
}

3.4 Map对象值字段

java 对象 Map 在 protobuf 的定义。

message User{
  // 定义复杂的 Map 对象
  map<string, MapVauleObject> mapObject = 8;
}


// 定义 Map 的 value 对象
message MapVauleObject {
  string code = 1;
  string name = 2;
}

3.5 嵌套对象字段

java 实体类中使用另一个实体类作为字段在 protobuf 的定义。

message User{
  // 对象
  NickName nickName = 4;
}

// 定义一个新的Name对象
message NickName {
  string nickName = 1;
}

三、示例实战

1、基本数据类型

(1).proto文件

syntax = "proto3";

//生成 proto 文件所在包路径(一般不指定, 生成java类之后人为手动加即可)
//package com.example.xxx.model;

//生成 proto 文件所在 java包路径(一般不指定,因为生成的java_outer_classname类中使用到它会使用全限定名)
//option java_package = "com.example.xxx.model";

//生成 proto java文件名(一般指定,文件名+自定义。如果不指定,默认时文件名+OuterClass)
option java_outer_classname = "UserProtoBuf";

message User {

  int32 age = 1;
  int64 timestamp = 2;
  bool enabled = 3;
  float height = 4;
  double weight = 5;
  string userName = 6;
  string Full_Address = 7;
  
}

生成 java类。

protoc --java_out=. person.proto

注意:proto没有指定 package xxx; 所以,我们将java类放到目标包下面时,记得手动导包。

(2)测试类

protobuf数据(字节数组)序列化、反序列化。

public class UserTest {

    public static void main(String[] args) throws Exception {

        // 将数据序列化
        byte[] byteData = getClientPush();
        System.out.println("获取到字节数据:byteData长度="+ byteData.length);
        System.out.println("===========");

        /**
         * 接收数据反序列化:将字节数据转化为对象数据。
         */
        UserProtoBuf.User user = UserProtoBuf.User.parseFrom(byteData);
        System.out.println("user=" + user);
        System.out.println("UserName=" + user.getUserName());
        System.out.println("Timestamp=" + user.getTimestamp());
        System.out.println("Height=" + user.getHeight());
    }

    /**
     * 模拟发送方,将数据序列化后发送
     * @return
     */
    private static byte[] getClientPush() {
        // 按照定义的数据结构,创建一个对象。
        UserProtoBuf.User.Builder user = UserProtoBuf.User.newBuilder();
        user.setAge(18);
        user.setTimestamp(System.currentTimeMillis());
        user.setEnabled(true);
        //user.setHeight(1.88F);
        user.setWeight(66.76D);
        user.setUserName("赵云");
        user.setFullAddress("王者-打野");

        /**
         * 发送数据序列化:将对象数据转化为字节数据输出
         */
        UserProtoBuf.User userBuild = user.build();
        byte[] bytes = userBuild.toByteArray();
        return bytes;
    }

}

2、集合/Map类型

(1).proto文件

syntax = "proto3";

option java_outer_classname = "UserListMapProtoBuf";

message UserListMap {

  string userName = 1;
  //list Int
  repeated int32 intList = 2;
  //list String
  repeated string strList = 3;

  // 定义Map对象<string, int32>
  map<string, int32> intMap = 4;
  // 定义Map对象<string, string>
  map<string, string> stringMap = 5;

}

(2)测试类

public class UserListMapTest {

    public static void main(String[] args) throws Exception {

        // 将数据序列化
        byte[] byteData = getClientPush();
        System.out.println("获取到字节数据:byteData长度="+ byteData.length);
        System.out.println("===========");

        /**
         * 接收数据反序列化:将字节数据转化为对象数据。
         */
        UserListMapProtoBuf.UserListMap userListMap = UserListMapProtoBuf.UserListMap.parseFrom(byteData);
        System.out.println("UserListMap=" + userListMap);
        System.out.println("UserName=" + userListMap.getUserName());
        System.out.println("IntList=" + userListMap.getIntListList());
        System.out.println("StrList=" + userListMap.getStrListList());
        System.out.println("IntMap=" + userListMap.getIntMapMap());
        System.out.println("StringMap=" + userListMap.getStringMapMap());
    }

    /**
     * 模拟发送方,将数据序列化后发送
     * @return
     */
    private static byte[] getClientPush() {
        // 按照定义的数据结构,创建一个对象。
        UserListMapProtoBuf.UserListMap.Builder userListMap = UserListMapProtoBuf.UserListMap.newBuilder();
        userListMap.setUserName("赵云");

        List<Integer> intList = new ArrayList<>();
        List<String> strList = new ArrayList<>();
        intList.add(50);
        intList.add(51);
        strList.add("字符串1");
        strList.add("字符串2");
        userListMap.addAllIntList(intList);
        userListMap.addAllStrList(strList);

        Map<String, String> strMap = new HashMap<>();
        strMap.put("str-k1", "v1");
        strMap.put("str-k2", "v2");
        userListMap.putIntMap("integer-k1", 60);
        userListMap.putIntMap("integer-k2", 61);
        userListMap.putAllStringMap(strMap);


        /**
         * 发送数据序列化:将对象数据转化为字节数据输出
         */
        UserListMapProtoBuf.UserListMap userListBuild = userListMap.build();
        byte[] bytes = userListBuild.toByteArray();
        return bytes;
    }

}

3、嵌套对象类型

(1).proto文件

syntax = "proto3";

option java_outer_classname = "DemoObjectProtoBuf";

message DemoObject {

  string userName = 1;
  //list InnerObject
  repeated InnerObject innerObjectList = 2;

  // 定义Map对象<string, InnerObject>
  map<string, InnerObject> innerObjectMap = 3;

}

// 定义 InnerObject2对象
message InnerObject {
  string name = 1;
  int32 age = 2;
  string code = 3;
}

(2)测试类

public class DemoObjectTest {

    public static void main(String[] args) throws Exception {

        // 将数据序列化
        byte[] byteData = getClientPush();
        System.out.println("获取到字节数据:byteData长度=" + byteData.length);
        System.out.println("===========");

        /**
         * 接收数据反序列化:将字节数据转化为对象数据。
         */
        DemoObjectProtoBuf.DemoObject demoObject = DemoObjectProtoBuf.DemoObject.parseFrom(byteData);
        //System.out.println("DemoObject=" + demoObject);
        System.out.println("UserName=" + demoObject.getUserName());
        List<DemoObjectProtoBuf.InnerObject> innerObjectList = demoObject.getInnerObjectListList();
        for (DemoObjectProtoBuf.InnerObject innerObject : innerObjectList) {
            System.out.println("innerObject=" + innerObject);
            System.out.println("Name=" + innerObject.getName());
        }
        Map<String, DemoObjectProtoBuf.InnerObject> innerObjectMap = demoObject.getInnerObjectMapMap();
        innerObjectMap.forEach((k, v) -> {
            System.out.println("k=" + k);
            System.out.println("v=" + v);
        });

    }

    /**
     * 模拟发送方,将数据序列化后发送
     *
     * @return
     */
    private static byte[] getClientPush() {
        DemoObjectProtoBuf.InnerObject innerObject1 = DemoObjectProtoBuf.InnerObject.newBuilder()
                .setName("in 赵子龙2")
                .setAge(18)
                .setCode("code1").build();
        DemoObjectProtoBuf.InnerObject innerObject2 = DemoObjectProtoBuf.InnerObject.newBuilder()
                .setName("in 赵子龙2")
                .setAge(19)
                .setCode("code2").build();
        List<DemoObjectProtoBuf.InnerObject> innerObjList = new ArrayList<>();
        innerObjList.add(innerObject1);
        innerObjList.add(innerObject2);
        Map<String, DemoObjectProtoBuf.InnerObject> innerObjMap = new HashMap<>();
        innerObjMap.put("k1", innerObject1);
        innerObjMap.put("k2", innerObject2);

        // 按照定义的数据结构,创建一个对象。
        DemoObjectProtoBuf.DemoObject.Builder demoObject = DemoObjectProtoBuf.DemoObject.newBuilder();
        demoObject.setUserName("赵云");
        demoObject.addAllInnerObjectList(innerObjList);
        demoObject.putAllInnerObjectMap(innerObjMap);

        /**
         * 发送数据序列化:将对象数据转化为字节数据输出
         */
        DemoObjectProtoBuf.DemoObject demoObjectBuild = demoObject.build();
        byte[] bytes = demoObjectBuild.toByteArray();
        return bytes;
    }

}

4、引入外部 proto对象类型

外部 proto文件,使用上面的 User.proto。

(1).proto文件

syntax = "proto3";

option java_outer_classname = "Demo2ObjectProtoBuf";

// 引入外部的 proto 对象
import "User.proto";

message Demo2Object {

  string userName = 1; // default = "张三"

  //list Int
  repeated int32 intList = 2;

  //list 对象(User为引入的外部 proto文件)
  repeated User userList = 3;

}

(2)测试类

public class DemoObject2Test {

    public static void main(String[] args) throws Exception {

        // 将数据序列化
        byte[] byteData = getClientPush();
        System.out.println("获取到字节数据:byteData长度=" + byteData.length);
        System.out.println("===========");

        /**
         * 接收数据反序列化:将字节数据转化为对象数据。
         */
        Demo2ObjectProtoBuf.Demo2Object demo2Object = Demo2ObjectProtoBuf.Demo2Object.parseFrom(byteData);
        //System.out.println("Demo2Object=" + demo2Object);
        System.out.println("UserName=" + demo2Object.getUserName());
        List<UserProtoBuf.User> userList = demo2Object.getUserListList();
        for (UserProtoBuf.User user : userList) {
            System.out.println("user=" + user);
        }

    }

    /**
     * 模拟发送方,将数据序列化后发送
     *
     * @return
     */
    private static byte[] getClientPush() {
        UserProtoBuf.User user = UserProtoBuf.User.newBuilder()
        .setAge(18)
        .setTimestamp(System.currentTimeMillis())
        .setEnabled(true)
        //.setHeight(1.88F)
        .setWeight(66.76D)
        .setUserName("赵云")
        .setFullAddress("王者-打野").build();
        List<UserProtoBuf.User> userList = new ArrayList<>();
        userList.add(user);
        userList.add(user);

        // 按照定义的数据结构,创建一个对象。
        Demo2ObjectProtoBuf.Demo2Object.Builder demo2Object = Demo2ObjectProtoBuf.Demo2Object.newBuilder();
        demo2Object.setUserName("赵云");
        demo2Object.addAllUserList(userList);

        /**
         * 发送数据序列化:将对象数据转化为字节数据输出
         */
        Demo2ObjectProtoBuf.Demo2Object demo2ObjectBuild = demo2Object.build();
        byte[] bytes = demo2ObjectBuild.toByteArray();
        return bytes;
    }

}

标签:protobuf,proto,System,println,user,一文,搞懂,序列化,out
From: https://blog.csdn.net/m0_71124168/article/details/140111179

相关文章

  • 一文看懂智能循迹小车的L298N电机驱动模块到底怎么用
    一、L298N电机驱动模块有什么用?  我们在做单片机智能循迹小车的时候,经常看到上面有一个L298N电机驱动模块一端连接着小车的电机,另一端连接着单片机的IO口。  那为什么没有直接用单片机的IO口控制电机呢?  其中一个原因就是单片机输出的功率较小,不足以驱动电机工作......
  • 一文读懂“负载均衡”
    原文链接:https://blog.csdn.net/cyl101816/article/details/135195729负载均衡无处不在,无论是分布式,还是中间件,还是微服务,都需要涉及到负载均衡。一、什么是负载均衡负载均衡是一种在计算机网络和系统架构中使用的技术,用于均衡分发工作负载到多个资源,比如:服务器、计算节点或存储......
  • Stable Diffusion:一文搞懂提示词
    杰出的画作展现了艺术家们独特的想象力、生动的表达力和精湛的技艺。如今AI绘画工具似乎已解决了技艺的问题,创作出理想的画作似乎应该是一件轻而易举的事。但是,AI绘画工具只是机器,如何让它正确地绘制出我们想要的画作呢?这就是本文要讲的内容——AI绘画中非常重要,但容易......
  • 一文带您了解Fiddler的家族产品:Fiddler Classic、FiddlerCore、Fiddler和Cap、Fiddler
    最近更新了一下Fiddler(好久没更新了),然后浏览了一下官方网站,发现fillder的变化还是蛮大的,新出了好多产品,在这里我就把这些产品进行汇总比较,便于大家快速了解,快速选择自己需要的产品!Fiddler版本介绍FiddlerClassic(经典版)我们最为熟知的版本,这个版本是免费的,不过只能在Window......
  • protobufjs解析proto消息出错RangeError: index out of range: 2499 + 10 > 2499解决办
    使用websocket通讯传输protobuf消息的时候,decode的时候出错了:RangeError:indexoutofrange:2499+10>2499Error:invalidwiretype4atoffset1986出现这种错误的时候,99%是因为proto里面的消息类型和服务端发送的消息类型不一致导致的。解决这个问题的办法有......
  • 直播的js代码debug解析找到protobuf消息的定义
    我们都知道直播的弹幕消息是通过websocket发送的,而且是通过protobuf传输的,那么这里面传输了哪些内容,这个proto文件又要怎么定义?每个消息叫什么,消息里面又包含有哪些字段,每个字段又是什么类型?都可以通过js查看得到。找到解析入口上一节我们找到了_decodeFrameOrResponse,这里......
  • 一文说清:大模型AI Agent在企业应用中的6种基础类型
    创作与生成类助手企业知识助手数据分析助手应用/工具助手Web操作助手自定义流程助手本篇将对这几类AI助手分别做进一步探讨。PART\01创作与生成类助手大模型是生成式AI的基础,因此,理解、创作与生成内容是其强项也是最基本的能力。在C端市场,大量的生成类AI工......
  • 一问搞懂Linux信号【上】
    Linux信号在Linux系统中的地位仅此于进程间通信,其重要程度不言而喻。本文我们将从信号产生,信号保存,信号处理三个方面来讲解信号。......
  • 大数据信用报告何处查询?一文解答你的疑问!
    在当今社会,大数据信用的重要性日益凸显,越来越多的人开始关注并查询自己的大数据信用报告。那么,究竟何处可以查询到自己的大数据信用报告呢?接下来,我将为您详细解答。1.手机APP:一些大数据查询的平台推出了自己的手机APP,您可以下载并安装这些APP,通过实名认证后查询自......
  • 一文读懂LLM API应用开发基础(万字长文)
    前言Hello,大家好,我是GISerLiu......