protobuf使用详解

news2025/1/11 23:56:58

一、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、复杂数据类型

下面通过 Java数据类型来理解定义的 proto属性。并引入 protobuf-java依赖:

       <!-- https://mvnrepository.com/artifact/com.google.protobuf/protobuf-java -->
        <dependency>
            <groupId>com.google.protobuf</groupId>
            <artifactId>protobuf-java</artifactId>
            <version>3.19.1</version>
        </dependency>

3.1 集合List字段

Java String、Integer List 在 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类。

注意: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;
    }

}

– 求知若饥,虚心若愚。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1137558.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Power BI 傻瓜入门 11. 可视化数据

本章内容包括&#xff1a; 掌握Power BI中可用的各种可视化选项决定何时使用特定的可视化技术了解Power BI版本之间的可视化配置差异 “一张图片能说出千言万语”是许多人使用Power BI的原因之一。您已经导入了数据&#xff0c;可能有数百万条记录&#xff0c;现在您想了解数…

二维码制作教程:如何制作一个文件二维码?

文件二维码&#xff0c;即PDF、PPT、Word、Excel、图片、MP3音频文件、MP4视频文件等生成的二维码。因为二维码传播信息效率高、成本低&#xff0c;越来越多的企业在日常工作中都选择使用二维码来传送文件。比如&#xff0c;市场部年度推广计划书、财务季度报表汇总、年会流程通…

【Superset】自定义授权认证,接入内部系统二次开发

想要将内部系统认证与superset打通,必须要了解superset的认证体系。 Superset的认证体系 Superset的认证体系可以通过以下几种方式进行配置: 基于LDAP认证:Superset可以集成LDAP以验证用户身份。在这种情况下,Superset将根据LDAP中的用户信息进行身份验证,并从LDAP中获取…

家用智能安防系统包括哪几个部分?如何应用?

智能家居安防系统除了传统的安全装置之外&#xff0c;还有可以进行语音控制、视频监控、移动侦测等高科技智能化功能的升级版&#xff0c;这对于现代社会中的人们来说是一个不错的选择&#xff0c;也是生活水平的一次提升。今天&#xff0c;小编就为大家介绍一下智能家居安防系…

系列五、BeanDefinition

一、概述 BeanDefinition是一个接口&#xff0c;主要负责存储bean的定义信息&#xff0c;决定bean的生产方式&#xff0c;类似于说明书。后续BeanFactory就可以根据这些信息生产bean了。比如实例化&#xff1a;可以通过class进行反射得到实例对象&#xff0c;比如lazy&#xff…

输入/输出应用程序接口和设备驱动程序接口

文章目录 1.输入/输出应用程序接口1.字符设备接口2.块设备接口3.网络设备接口1.网络设备套接字通信 4.阻塞/非阻塞I/O 2.设备驱动程序接口1.统一标准的设备驱动程序接口 1.输入/输出应用程序接口 1.字符设备接口 get/put系统调用:向字符设备读/写一个字符 2.块设备接口 read/wr…

宝塔Python3.7安装模块报错ModuleNotFoundError: No module named ‘Crypto‘解决办法

前言 今晚遇到一个问题&#xff0c;宝塔服务器上安装脚本的模块时&#xff0c;出现以下报错&#xff0c;这里找到了解决办法 Traceback (most recent call last):File "/www/wwwroot/unifysign/fuck_chaoxing/fuck_xxt.py", line 4, in <module>from Crypto.…

统计数(C++)

系列文章目录 进阶的卡莎C++_睡觉觉觉得的博客-CSDN博客数1的个数_睡觉觉觉得的博客-CSDN博客双精度浮点数的输入输出_睡觉觉觉得的博客-CSDN博客足球联赛积分_睡觉觉觉得的博客-CSDN博客大减价(一级)_睡觉觉觉得的博客-CSDN博客小写字母的判断_睡觉觉觉得的博客-CSDN博客纸币(…

大模型时代,AI如何成为数实融合的驱动力?

10月25日&#xff0c;百度APP、百家号联合中兴通讯举办的“时代的增量“主题沙龙第二期在北京顺利召开。本期沙龙围绕“数实融合新视角”邀请学界、业界、媒体从业者等领域专家出席&#xff0c;以产学研相结合的视角深入探讨数实融合的最新技术趋势&#xff0c;并围绕数实融合在…

AFsim编译-Windows

AFsim软件的核心应用及服务包括&#xff1a; sensor_plot&#xff1a;传感器覆盖和天线增益绘制工具&#xff1b; engage&#xff1a;武器交战分析工具&#xff1b; weapon_tools&#xff1a;武器建模工具&#xff1b; mission&#xff1a;任务仿真工具&#xff1b; post_…

go创建完美的枚举类型

文章目录 一.前言二. 枚举基本要素描述三. 枚举设计源码3.1 EnumCommon-通用能力3.2 Enum_news 业务枚举3.3 定制化业务枚举 一.前言 用惯了springboot和Jakarta.才发现springboot和Jakarta的语言是多么精妙! 一些场景我们需要使用枚举: 如建立字典值映射,仅通过代码实现方便快…

【RocketMQ】揭开事务消息的神秘面纱

【RocketMQ】揭开事务消息的神秘面纱 参考资料&#xff1a; 解析 RocketMQ 业务消息——“事务消息”—— 阿里云 RocketMQ事务消息, 图文、源码学习探究~ RocketMQ实战一&#xff1a;事务消息保证分布式事务的一致性 RocketMQ源码分析13&#xff1a;事务消息 《RocketMQ技术内…

Miniconda、Vscode下载和conda源、pip源设置

1、常用软件下载 1、Miniconda软件下载&#xff1a; windows网址&#xff1a;https://mirrors.tuna.tsinghua.edu.cn/anaconda/miniconda/?CS&OA 2、最新版Miniconda下载网址&#xff1a;https://docs.conda.io/projects/miniconda/en/latest/ 3、常用代码编辑器VsCode下…

haproxy 负载均衡

haproxy负载均衡 haproxy&#xff1a;基于C语言开发的开源软件 支持高性能的tcp和http负载均衡器&#xff0c;工作中用的版本1.5.9 haproxy功能&#xff1a;主要用于高并发的web站点&#xff0c;工作原理和nginx、lvs都一样 haproxy缺点: 单节点部署&#xff0c;单实例运行。代…

3DCAT+东风日产:共建线上个性化订车实时云渲染方案

近年来&#xff0c;随着5G网络和云计算技术的不断发展&#xff0c;交互式3D实时云看车正在成为一种新的看车方式。 与传统的到4S店实地考察不同&#xff0c;消费者可以足不出户&#xff0c;通过网络与终端设备即可实现全方位展示、自选汽车配色、模拟效果、快捷选车并进行个性…

ToLua使用原生C#List和Dictionary

ToLua是使用原生C#List 介绍Lua中使用原生ListC#调用luaLua中操作打印测试如下 Lua中使用原生DictionaryC#调用luaLua中操作打印测试如下 介绍 当你用ToLua时C#和Lua之间肯定是会互相调用的&#xff0c;那么lua里面使用List和Dictionary肯定是必然的&#xff0c;在C#中可以调用…

基本分段存储管理方式

与分页区别-离散分配时分配地址空间的基本单位不同 一、概述 1.分段 将进程地址空间按程序自身逻辑关系划分为若干个段&#xff0c;每个段都有段名&#xff0c;且从0开始编址 逻辑地址段号(段名)段内地址(段内偏移量) 程序中的段名会被翻译成对应段号&#xff0c;段内单元…

ASPICE项目实战

目录 一、前言 二、ASPICE是什么 三、ASPICE项目实施 一、前言 ASPICE全称“AutomotiveSoftware ProcessImprovement and CapacityDetermination”&#xff0c;即汽车软件过程改进及能力评定为什么要做ASPICE&#xff1f;满足主机厂为OEM资质要求&#xff1b;改善产品研发质…