Protobuf 的使用
1.创建一个.proto文件,定义好消息体
idea中Protobuf Support插件可以支持.proto文件高亮显示。 (我的.proto文件由协议制定方提供)
2.将.proto文件转成Java类(我用的是其中2.2提供的方法)
2.1一般的做法,是执行protoc命令,依次将.proto文件转成Java类:
protoc.exe -I=d:/tmp --java_out=d:/tmp d:/tmp/monitor_data.proto
不过gRPC官方推荐了一种更优雅的使用姿势,可以通过maven轻松搞定
2.2 pom.xml文件配置
<properties>
<grpc.version>1.6.1</grpc.version>
<protobuf.version>3.3.0</protobuf.version>
</properties>
<dependencies>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-netty</artifactId>
<version>${grpc.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-protobuf</artifactId>
<version>${grpc.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-stub</artifactId>
<version>${grpc.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<version>${protobuf.version}</version>
</dependency>
</dependencies>
<build>
<extensions>
<extension>
<groupId>kr.motd.maven</groupId>
<artifactId>os-maven-plugin</artifactId>
<version>1.5.0.Final</version>
</extension>
</extensions>
<plugins>
<plugin>
<groupId>org.xolstice.maven.plugins</groupId>
<artifactId>protobuf-maven-plugin</artifactId>
<version>0.5.0</version>
<configuration>
<protocArtifact>com.google.protobuf:protoc:${protobuf.version}:exe:${os.detected.classifier}</protocArtifact>
<pluginId>grpc-java</pluginId>
<pluginArtifact>io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier}</pluginArtifact>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>compile-custom</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
3.编译生成Java类
使用maven的编译命令,即可在target中看到根据.proto文件生成的Java类。
注意:
- 需要根据实际生成java文件的路径更改.proto文件上方的package包路径;
- 若生成的java文件不可用,考虑是不是java文件过大,超过idea设置的默认值,调整该参数需要找到bin路径下的idea.properties文件,更改idea.max.intellisense.filesize参数;
- 编译找不到.proto文件路径时,在.proto文件所在的文件夹上右键,设置目录为源文件根目录,或将.proto文件放到编译路径下。
protobuf优点
1、性能好/效率高
- 时间开销:XML 格式化(序列化)的开销还好;但是 XML 解析(反序列化)的开销就不敢恭维了。 但是 protobuf 在这个方面就进行了优化。可以使序列化和反序列化的时间开销都减短。
- 空间开销:也减少了很多
2、有代码生成机制
比如写一个类似结构体的内容
message testA { required int32 m_testA = 1; }
像写一个这样的结构,protobuf 可以自动生成它的 .h 文件和点 .cpp 文件。
protobuf 将对结构体 testA 的操作会封装成一个类。
3、支持向后兼容和向前兼容
当客户端和服务器同时使用一个协议时,客户端在协议中增加一个字节,并不会影响客户端的使用。
4、支持多种编程语言
在Google官方发布的源代码中包含了
- C++
- C#
- Dart
- Go
- Java
- Kotlin
- Python
protobuf缺点
1、二进制格式导致可读性差
为了提高性能,protobuf 采用了二进制格式进行编码。这直接导致了可读性差,影响开发测试时候的效率。当然,在一般情况下,protobuf 非常可靠,并不会出现太大的问题。
2、缺乏自描述
一般来说,XML 是自描述的,而 protobuf 格式则不是。它是一段二进制格式的协议内容,并且不配合写好的结构体是看不出来什么作用的。
3、通用性差
protobuf 虽然支持了大量语言的序列化和反序列化,但仍然并不是一个跨平台和语言的传输标准。在多平台消息传递中,对其他项目的兼容性并不是很好,需要做相应的适配改造工作。相比 json 和 XML,通用性还是没那么好。
protobuf高效的秘密在于它的编码格式,它采用了 TLV(tag-length-value) 编码格式。每个字段都有唯一的 tag 值,它是字段的唯一标识。length 表示 value 数据的长度,length 不是必须的,对于固定长度的 value,是没有 length 的。value 是数据本身的内容。
对于 tag 值,它有 field_number 和 wire_type 两部分组成。field_number 就是在前面的 message 中我们给每个字段的编号,wire_type 表示类型,是固定长度还是变长的。 wire_type 当前有0到5一共6个值,所以用3个 bit 就可以表示这6个值。tag 结构如下图。
wire_type 值如下表, 其中3和4已经废弃,我们只需要关心剩下的4种。对于 Varint 编码数据,不需要存储字节长度 length。这种情况下,TLV 编码格式退化成 TV 编码。对于64-bit和32-bit也不需要 length,因为type值已经表明了长度是8字节还是4字节。