快速上手ProtoBuf

news2024/12/22 17:15:28

目录:

  1. 需求:
  2. 引⼊ProtoBuf包
  3. 创建.proto⽂件
  4. 编译contacts.proto⽂件,⽣成JAVA⽂件
  5. 编译contacts.proto⽂件后会⽣成什么
  6. 序列化与反序列化的使⽤
  7. ⼩结ProtoBuf使⽤流程

1.需求:

在快速上手中,会编写第一版本的通讯录1.0。在通讯录1.0版本中,将实现:·

  • 对一个联系人的信息使用PB进行序列化,并将结果打印出来。
  • 对序列化后的内容使用PB进行反序列,解析出联系人信息并打印出来。·
  • 联系人包含以下信息:姓名、年龄。

通过通讯录1.0,我们便能了解使用ProtoBuf初步要掌握的内容,以及体验到ProtoBuf的完整使用流程。(注意需要创建maven项目)

2.引⼊ProtoBuf包

<!-- protobuf ⽀持 Java 核⼼包 -->
        <dependency>
            <groupId>com.google.protobuf</groupId>
            <artifactId>protobuf-java</artifactId>
            <version>3.21.11</version>
        </dependency>

3.创建.proto⽂件

将新建的.proto⽂件统⼀放在项⽬中的 /src/main/proto ⽬录下。

⽂件规范:

  • 创建.proto⽂件时,⽂件命名应该使⽤全⼩写字⺟命名,多个字⺟之间⽤ _ 连接。例如:lower_snake_case.proto 。
  • 书写.proto⽂件代码时,应使⽤2个空格的缩进。
  • 我们为通讯录1.0在 /src/main/proto/start ⽬录下新建⽂件: contacts.proto

添加注释:

  • 向⽂件添加注释,可使⽤ // 或者 /* ... */
     

指定proto3语法:

ProtocolBuffers语⾔版本3,简称proto3,是.proto⽂件最新的语法版本。proto3简化了Protocol
Buffers语⾔,既易于使⽤,⼜可以在更⼴泛的编程语⾔中使⽤。它允许你使⽤Java,C++,Python等多种语⾔⽣成protocolbuffer代码。在.proto⽂件中,要使⽤syntax = "proto3"; 来指定⽂件语法为proto3,并且必须写在除去注释内容的第⼀⾏。如果没有指定,编译器会使⽤proto2语法。
在通讯录1.0的contacts.proto⽂件中,可以为⽂件指定proto3语法,内容如下:
 

syntax = "proto3";

package声明符:
package是⼀个可选的声明符,能表⽰.proto⽂件的命名空间,在项⽬中要有唯⼀性。它的作⽤是为了避免我们定义的消息出现冲突。在通讯录1.0的contacts.proto⽂件中,可以声明其命名空间,内容如下:

syntax = "proto3";
package start;

添加JAVA选项:
.proto⽂件中可以声明许多选项,使⽤ option 标注。选项能影响proto编译器的某些处理⽅式。
这这个部分,对于选项能⼒不进⾏展开,后⾯学习proto3语法详解部分,会再介绍。
在通讯录1.0的contacts.proto⽂件中,新增JAVA选项,内容如下:

syntax = "proto3";
package start;

option java_multiple_files = true; // 编译后⽣成的⽂件是否分为多个⽂件
option java_package = "com.example.start"; // 编译后⽣成⽂件所在的包路径
option java_outer_classname="ContactsProtos"; // 编译后⽣成的proto包装类的类名

定义消息(message):

  • 消息(message):要定义的结构化对象,我们可以给这个结构化对象中定义其对应的属性内容。这⾥再提⼀下为什么要定义消息?
  • 在⽹络传输中,我们需要为传输双⽅定制协议。定制协议说⽩了就是定义结构体或者结构化数据,⽐如,tcp,udp报⽂就是结构化的。
  • 再⽐如将数据持久化存储到数据库时,会将⼀系列元数据统⼀⽤对象组织起来,再进⾏存储。
  • 所以ProtoBuf就是以message的⽅式来⽀持我们定制协议字段,后期帮助我们形成类和⽅法来使⽤。在通讯录1.0中我们就需要为联系⼈定义⼀个message。

.proto⽂件中定义⼀个消息类型的格式为:

message 消息类型名{
    
}
//    消息类型命名规范:使⽤驼峰命名法,⾸字⺟⼤写。

 为contacts.proto(通讯录1.0)新增联系⼈message,内容如下:

syntax = "proto3";
package start;

option java_multiple_files = true; // 编译后⽣成的⽂件是否分为多个⽂件
option java_package = "com.example.start"; // 编译后⽣成⽂件所在的包路径
option java_outer_classname = "ContactsProtos"; // 编译后⽣成的proto包装类的类名

message PeopleInfo{

}

定义消息字段:

  • 在message中我们可以定义其属性字段,字段定义格式为:字段类型字段名=字段唯⼀编号;
  • 字段名称命名规范:全⼩写字⺟,多个字⺟之间⽤ _ 连接。
  • 字段类型分为:标量数据类型和特殊类型(包括枚举、其他消息类型等)。
  • 字段唯⼀编号:⽤来标识字段,⼀旦开始使⽤就不能够再改变。
  • 该表格展⽰了定义于消息体中的标量数据类型,以及编译.proto⽂件之后⾃动⽣成的类中与之对应的字段类型。在这⾥展⽰了与JAVA语⾔对应的类型。
.proto TypeNotesJava Type
doubledouble
floatfloat
int32

使用变长编码[1]。负数的编码效率较低

——若字段可能为负值,应使用sint32代替。

int
int64

使用变长编码[1]。负数的编码效率较低

——若字段可能为负值,应使用sint64代替。

long
uint32使用变长编码[1]。int[2]
uint64使用变长编码[1]。long[2]
sint32

使用变长编码[1]。符号整型。负值的

编码效率高于常规的int32类型。

int
sint64

使用变长编码[1]。符号整型。负值的

编码效率高于常规的int64类型。

long
fixed32

定长4字节。若值常大于2^28则会比

uint32更高效。

int
fixed64

定长8字节。若值常大于2N56则会比

uint64更高效。

long
sfixed32定长4字节。int
sfixed64定长8字节。long
boolboolean
string包含UTF-8和ASCII编码的字符串,长度不能超过
2^32。
String
bytes可包含任意的字节序列但长度不能超过2^32。

ByteString

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 变⻓编码是指:经过protobuf编码后,原本4字节或8字节的数可能会被变为其他字节数。
  • 在Java中,⽆符号32位和⽆符号64位整数使⽤它们对应的有符号整数来表⽰,这时第⼀个bit位仅是简单地存储在符号位中。
     

更新contacts.proto(通讯录1.0),新增姓名、年龄字段:

syntax = "proto3";
package start;

option java_multiple_files = true; // 编译后⽣成的⽂件是否分为多个⽂件
option java_package = "com.example.start"; // 编译后⽣成⽂件所在的包路径
option java_outer_classname = "ContactsProtos"; // 编译后⽣成的proto包装类的类名

message PeopleInfo{
  string name = 1;
  int32 age = 2;
}
  • 在这⾥还要特别讲解⼀下字段唯⼀编号的范围:
  • 1~536,870,911(2^29-1),其中19000~19999不可⽤。
  • 19000~19999不可⽤是因为:在Protobuf协议的实现中,对这些数进⾏了预留。如果⾮要在.proto⽂件中使⽤这些预留标识号,例如将name字段的编号设置为19000,编译时就会报警:
// 消息中定义了如下编号,代码会告警:
// Field numbers 19,000 through 19,999 are reserved for the protobuf
//    implementation
    string name = 19000;

值得⼀提的是,范围为1~15的字段编号需要⼀个字节进⾏编码,16~2047内的数字需要两个字节
进⾏编码。编码后的字节不仅只包含了编号,还包含了字段类型。所以1~15要⽤来标记出现⾮常频繁的字段,要为将来有可能添加的、频繁出现的字段预留⼀些出来。


4.编译contacts.proto⽂件,⽣成JAVA⽂件

编译的⽅式有两种:使⽤命令⾏编译;使⽤maven插件编译。

⽅式⼀:使⽤命令⾏编译,编译命令⾏格式为:

    protoc [--proto_path=IMPORT_PATH] --java_out=DST_DIR path/to/file.proto

    protoc 是 Protocol Buffer 提供的命令⾏编译⼯具。

    --proto_path 指定 被编译的.proto⽂件所在⽬录,可多次指定。可简写成 -I

    IMPORT_PATH 。如不指定该参数,则在当前⽬录进⾏搜索。当某个.proto ⽂件 import 其他
    .proto ⽂件时,或需要编译的 .proto ⽂件不在当前⽬录下,这时就要⽤-I来指定搜索⽬录。

    --java_out= 指编译后的⽂件为 JAVA ⽂件。

    OUT_DIR 编译后⽣成⽂件的⽬标路径。
    path/to/file.proto 要编译的.proto⽂件。

编译contacts.proto⽂件命令如下:

protoc -I src\main\proto\start\ --java_out=src\main\java contacts.proto

运行结果:

 

⽅式⼆:使⽤maven插件编译

<build>
        <plugins>
            <plugin>
                <groupId>org.xolstice.maven.plugins</groupId>
                <artifactId>protobuf-maven-plugin</artifactId>
                <version>0.6.1</version>
                <configuration>
                    <!-- 本地安装的protoc.exe的⽬录 -->
                    <protocExecutable>
                        E:\softwares_tl\protobuf_tl\protoc-21.11-win64\bin\protoc.exe
                    </protocExecutable>
                    <!-- proto⽂件放置的⽬录,默认为/src/main/proto -->
                    <protoSourceRoot>
                        ${project.basedir}/src/main/proto
                    </protoSourceRoot>
                    <!-- ⽣成⽂件的⽬录,默认⽣成到target/generated-sources/protobuf/ -->
                    <outputDirectory>
                        ${project.basedir}/src/main/java
                    </outputDirectory>
                    <!-- 是否清空⽬标⽬录,默认值为true。这个最好设置为false,以免误删项⽬⽂件!!! -->
                    <clearOutputDirectory>
                        false
                    </clearOutputDirectory>
                </configuration>
            </plugin>
        </plugins>
    </build>

运行结果:

5.编译contacts.proto⽂件后会⽣成什么

  • 编译contacts.proto⽂件后,会⽣成所选择语⾔的代码,我们选择的是JAVA,编译后⽣成了三个⽂件: ContactsProtos.java PeopleInfo.java PeopleInfoOrBuilder.java 。如果在contacts.proto⽂件中不设置option java_multiple_files = true; ,或将其置为false ,则会把这三个⽂件合成为⼀个ContactsProtos.java ⽂件。

对于编译⽣成的JAVA代码,我们主要关注 PeopleInfo.java ,其内容为:

  • 在.proto⽂件中定义的每⼀个message,都会⽣成⼀个⾃⼰的⾃定义 message 类,每⼀个⾃定义 message 类还有⼀个内部 Builder 类。 Builder 类的作⽤就是可以创建 message 类的实例。
  • 在message 类中,主要包含:
    •  获取字段值的get⽅法,⽽没有set⽅法。
    • 序列化和反序列化⽅法。
    • newBuilder()静态⽅法:⽤来创建Builder。
  • 在 Builder 类中,主要包含:
    • 编译器为每个字段提供了获取和设置⽅法,以及能够操作字段的⼀些⽅法。
    • build()⽅法:主要是⽤来构造出⼀个⾃定义类对象。

PeopleInfo.java中PeopleInfo类部分代码展⽰(为了简洁,⽅法省略了具体实现):

上述的例⼦中:

  • 获取字段值的get⽅法,⽽没有set⽅法。
  • parseFrom()系列静态⽅法提供了反序列化消息对象的能⼒。
  • newBuilder()静态⽅法:⽤来创建Builder。

PeopleInfo.java中Builder内部类部分代码展⽰(为了简洁,⽅法省略了具体实现):


 

上述的例⼦中:

  • 包含⼀个build()⽅法:主要是⽤来构造出⼀个⾃定义类对象。
  • 每个字段都有set设置和get获取的⽅法。
  • 每个字段都有⼀个clear⽅法,可以将字段重新设置回empty状态。

那之前提到的序列化⽅法在哪⾥呢?其实在⾃定义消息类继承的接⼝MessageLite 中,提供了序列化消息实例的⽅法。

注意:

  • 序列化的结果为⼆进制字节序列,⽽⾮⽂本格式。
  • 以上两种序列化的⽅法没有本质上的区别,只是序列化后输出的格式不同,可以供不同的应⽤场景使⽤。

 6.序列化与反序列化的使⽤

创建⼀个测试⽂件FastStart.java,⽅法中我们实现:

  • 对⼀个联系⼈的信息使⽤PB进⾏序列化,并将结果打印出来。
  • 对序列化后的内容使⽤PB进⾏反序列,解析出联系⼈信息并打印出来。

FastStart.java

package com.example.start;

import com.google.protobuf.InvalidProtocolBufferException;

import java.util.Arrays;

public class FastStart {
    public static void main(String[] args) throws InvalidProtocolBufferException {
        PeopleInfo p1 = PeopleInfo.newBuilder().setName("张三").setAge(20).build();

        //序列化
        byte[] bytes = p1.toByteArray();
        System.out.println("序列化结果为:");
        System.out.println(Arrays.toString(bytes));

        //反序列化
        PeopleInfo p2 = PeopleInfo.parseFrom(bytes);

        System.out.println("反序列化结果为:");
        System.out.println("姓名:" + p2.getName() + " 年龄:" + p2.getAge());
    }
}

运行结果:


 

  • ProtoBuf是把联系⼈对象序列化成了⼆进制序列,这⾥⽤byte[]来作为接收⼆进制序列的容器,帮助我们看到序列化后的结果。
  • 所以相对于xml和JSON来说,因为被编码成⼆进制,破解成本增⼤,ProtoBuf编码是相对安全的。

7.⼩结ProtoBuf使⽤流程


 

  • 编写.proto⽂件,⽬的是为了定义结构对象(message)及属性内容。
  • 使⽤protoc编译器编译.proto⽂件,⽣成⼀系列接⼝代码。
  • 依赖⽣成的接⼝,实现对.proto⽂件中定义的字段进⾏设置和获取,和对message对象进⾏序列化和反序列化。
  • 总的来说:ProtoBuf是需要依赖通过编译⽣成的JAVA代码来使⽤的。有了这种代码⽣成机制,开发⼈员再也不⽤吭哧吭哧地编写那些协议解析的代码了(⼲这种活是典型的吃⼒不讨好)。

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

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

相关文章

【JZ36 二叉搜索树与双向链表】

目录 1.题目描述2.算法思想3.代码实现 1.题目描述 2.算法思想 注意点&#xff1a;为什么要引用传参&#xff1f;原因如下&#xff1a; 3.代码实现 class Solution { public:void inorder(TreeNode* cur,TreeNode*& prev){if(curnullptr){return ;}inorder(cur->left,…

pve组网实现公网访问pve,访问电脑,访问pve中的openwrt同时经过openwrt穿透主路由地址nginx全公网访问最佳办法测试研究...

一台路由器 做主路由 工控机 装pve虚拟机 虚拟机里面装一个openwrt, 外网可以直接访问pve,可以访问pve里的openwrt 一台主机 可选择连 有4个口&#xff0c;分别eth0,eth1,eth2,eth3 pve有管理口 这个情况下 &#xff0c;没有openwrt 直接电脑和pve管理口连在一起就能进pve管理界…

vue3 + vite + ts 封装 SvgIcon组件

环境 vite vue3 ts "vue": "^3.3.4", "vite": "^4.4.0", "typescript": "^5.0.2",# 需要下载的依赖 "vite-plugin-svg-icons": "^2.0.1",不同版本可能存在一定差异, 这篇文章不可能对应所…

数学建模(一)前继概念

课程推荐&#xff1a;数学建模老哥_哔哩哔哩_bilibili 目录 一、什么是数学建模&#xff1f; 二、数学建模的一般步骤 三、数学建模赛题类型 1.预测型 2. 评价类 3.机理分析类 4. 优化类 一、什么是数学建模&#xff1f; 数学建模是利用数学方法解决实际问题的一种实践。…

关于ISO27701隐私信息安全管理体系介绍

01 什么是ISO27701 ISO27701是对ISO27001信息安全管理和ISO27002安全控制的隐私扩展&#xff0c;全称《安全技术—扩展ISO27001和ISO27002的隐私信息管理—要求与指南》&#xff0c;是ISO标准委员会以ISO 27001为基准&#xff0c;以ISO27552为蓝本&#xff0c;建立发布的隐私…

Flink多流处理之Broadcast(广播变量)

写过Spark批处理的应该都知道,有一个广播变量broadcast这样的一个算子,可以优化我们计算的过程,有效的提高效率;同样在Flink中也有broadcast,简单来说和Spark中的类似,但是有所区别,首先Spark中的broadcast是静态的数据,而Flink中的broadcast是动态的,也就是源源不断的数据流.在…

docker-compose redis 一直启动失败

环境&#xff1a; centos 8.x 背景 使用docker-compose 来启动redis docker-compose.yml 如下&#xff1a; version: 3.3 services:redis:image: redis:latestrestart: alwayscontainer_name: redisports:- 6379:6379volumes:- ./data:/redis/data- ./redis.conf:/redis/re…

JMeter 查看 TPS 数据,详细指南

TPS 是软件测试结果的测量单位。一个事务是指一个客户机向服务器发送请求然后服务器做出反应的过程。客户机在发送请求时开始计时&#xff0c;收到服务器响应后结束计时&#xff0c;以此来计算使用的时间和完成的事务个数。在 JMeter 中&#xff0c;我们可以使用以下方法查看 T…

AI一键生成数字人

AI一键生成数字人,不玩虚的 阅读时长&#xff1a;10分钟 本文内容&#xff1a; 结合开源AI&#xff0c;一键生成短视频发布到常见的某音&#xff0c;某手平台&#xff0c;狠狠赚一笔 前置知识&#xff1a; 基本的 python 编程知识Jupyter Notebook 使用过Linux 使用过 先上源码…

Linux Idea启动项目打印堆栈日志(JMX监控日志)

说明 Idea更新至新版本&#xff08;2023.1&#xff09;后&#xff0c; 在Linux环境下默认会开启JMX监控并输出日志。 关闭JMX监控 打开Configurations配置面板。打开Modify options(ALt M)选项面板。勾选Disable JMX endpoints。 修改Configurations模板 确定不需要打印…

【算法题】螺旋矩阵I (求解n阶螺旋矩阵问题)

一、问题的提出 螺旋矩阵是一种常见的矩阵形式&#xff0c;它的特点是按照螺旋的方式排列元素。n阶螺旋矩阵是指矩阵的大小为nn&#xff0c;其中n为正整数。 二、解决的思路 当N1时&#xff0c;矩阵为; 当N2时&#xff0c;矩阵为; 当N>2(N为偶数如N4)时&#xff0c;矩阵…

通达OA SQL注入漏洞【CVE-2023-4166】

通达OA SQL注入漏洞【CVE-2023-4166】 一、产品简介二、漏洞概述三、影响范围四、复现环境POC小龙POC检测工具: 五、修复建议 免责声明&#xff1a;请勿利用文章内的相关技术从事非法测试&#xff0c;由于传播、利用此文所提供的信息或者工具而造成的任何直接或者间接的后果及损…

简单网络-跨网段通信

跨网段通信 1.为PC1&#xff0c;PC2配置IP地址、子网掩码和网关后用PC1 ping PC2&#xff0c;并用wireshark抓包g0/0/2的数据包。结果发现不能ping通&#xff0c;而PC1在发送广播包&#xff0c;寻找10.0.1.254网关&#xff0c;说明PC1找不到网关 2.给路由器e0/0/0端口配置网关1…

git一次错误merge的回滚

场景&#xff1a;提交到sit的代码&#xff0c;结果解决冲突merge了DEV的代码&#xff0c;所以要回滚到合并之前的代码 &#xff08;原因是我再网页上处理了冲突&#xff0c;他就自动merge了,如图—所以还是idea处理冲突&#xff0c;可控&#xff09; 方式二&#xff1a; &…

【Java多线程】CompletableFuture 异步多线程

1. 回顾 Future 一些业务场景我们需要使用多线程异步执行任务&#xff0c;加快任务执行速度。 JDK5新增了Future接口&#xff0c;用于描述一个异步计算的结果。 虽然 Future 以及相关使用方法提供了异步执行任务的能力&#xff0c;但是对于结果的获取却是很不方便&#xff0…

docker安装Nacos的《小白专用》详细教程

1.CentOS安装docker 安装docker yum -y install docker 设置开机自启 systemctl enable docker 启动docker systemctl start docker 查看docker当前的版本 docker version做到这里呢基本上你的docker就安装了一大部分了&#xff0c;当然也有那些无法安装的人&#xff0c;那我建…

prometheus监控k8s服务并告警到钉钉

一、监控k8s集群 要监控k8s集群需要使用到以下服务用于收集监控的资源信息&#xff0c;node_exporter用于监控k8s集群节点的资源信息&#xff0c;kube-state-metrics用于监控k8s集群的deployment、statefulset、daemonset、pod等的状态&#xff0c;cadvisor用于监控k8s集群的p…

爆肝整理,Python自动化测试-Pytest参数化实战封装,一篇打通...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 参数化&#xff1…

Gradio:交互式Python数据应用程序的新前沿

一、说明 什么是Gradio以及如何使用Gradio在Python中创建DataApp或Web界面&#xff1f;使用 Gradio 将您的 Python 数据科学项目转换为交互式应用程序。 摄影&#xff1a;Elijah Merrell on Unsplash Gradio是一个Python库&#xff0c;允许我们快速为机器学习模型创建可定制的接…

工程英语翻译怎样做效果比较好

我们知道&#xff0c;高质量的工程翻译可以有效指导工程项目操作的执行&#xff0c;但市场上专业的工程英语翻译人才严重不足。那么&#xff0c;工程英语翻译难吗&#xff0c;怎样翻译工程英语比较好&#xff1f; 业内人士指出&#xff0c; 工程翻译具有用词专业、涉及领域广、…