前言
之前在写Netty的时候,说过要写一篇关于PB的应用,所以现在兑现承诺。在应用的过程中,发现了很多问题,本文主要介绍两个最关键的问题。
- PB如何与java项目融合,自动刷新编译,以及pb文件如何与其他项目共用,互不影响。
- java中如何实现pb的Extension
概述
ProtocolBuff 是 google 提出的的一种数据交换格式,跨语言,跨平台,可扩展。基于这种特性广泛的用于网络数据通信。目前支持的语言有很多,如下:
maven项目集成Java
这里我们主要介绍在一个工程化的项目中如何集成PB,实现PB自动化刷新编译(使用mvn package指令编译),这里我们将引入一个插件protoc-jar-maven-plugin。
- 在pom.xml中加入插件
<plugin>
<groupId>com.github.os72</groupId>
<artifactId>protoc-jar-maven-plugin</artifactId>
<version>3.4.0</version>
<executions>
<execution>
<phase>generate-sources</phase>
<goals>
<goal>run</goal>
</goals>
<configuration>
<protocVersion>2.6.1</protocVersion> <!-- 2.4.1, 2.5.0, 2.6.1, 3.4.0 -->
<includeStdTypes>true</includeStdTypes>
<inputDirectories>
<include>../proto</include>
</inputDirectories>
<outputTargets>
<outputTarget>
<outputDirectory>src/main/java</outputDirectory>
</outputTarget>
</outputTargets>
</configuration>
</execution>
</executions>
</plugin>
重点说明一下配置 configuration
protocVersion:protoc支持的版本,这个需要各端统一。
inputDirectories:pb文件所在的目录,我这里设置成项目工程外的一个目录,目的是为了和其他工程公用一套pb文件
outputTargets:pb文件编译后生产的java文件放置的位置,这里我是放置 src/main/java下,之后你就可以在项目中自由引用。
这样就ok了,在编译maven项目时,你的所有pb文件会跟着自动编译,而且还能和好的与其他工程无缝衔接,免去手动各种烦恼。
PB在java项目中的实战
这里主要说明Extension的问题,对于简单的pb就不做阐述。废话不说直接上代码。
PB文件格式
基础pb文件 Base.proto,所有的extension的pb文件都会import这个文件。
package proto;
// 包格式
message PBDemo
{
oneof body {
TestDemoRequest testdemo_req = 1;
TestDemoResponse testdemo_rsp = 2;
}
}
message TestDemoRequest
{
extensions 100 to max;
}
message TestDemoResponse
{
extensions 100 to max;
}
扩展协议文件 testdemo.proto
package proto.testdemo;
import "base.proto";
extend TestDemoRequest
{
optional HelloRequest hello_req = 101;
}
extend TestDemoResponse
{
optional HelloResponse hello_rsp = 101;
}
message HelloRequest
{
optional string message = 1;
}
message HelloResponse
{
optional string message = 1;
}
这样的一个pb结构是我们在数据通信中经常使用的,结构,与业务扩展性上都非常好。接下来我们看看,在java中如何对这个解包,和打包。
打包示例
// 打第一个message
Testdemo.HelloRequest.Builder reqBuilder = Testdemo.HelloRequest.newBuilder();
reqBuilder.setMessage(msg);
Testdemo.HelloRequest req = reqBuilder.build();
// 打extension
Base.TestDemoRequest.Builder testBuilder = Base.TestDemoRequest.newBuilder();
testBuilder.setExtension(helloReq, req);
// 打最外层的message
Base.PBDemo.Builder baseBuilder = Base.PBDemo.newBuilder();
Base.PBDemo pbDemo = baseBuilder.setTestdemoReq(testBuilder.build()).build();
byte[] packet = pbDemo.toByteArray();
ByteBuf buf = Unpooled.buffer();
buf.writeBytes(packet);
// 发送数据包
channel.writeAndFlush(buf);
解包示例
ByteBuf buf = (ByteBuf)msg;
byte[] packet = new byte[buf.readableBytes()];
buf.readBytes(packet);
ExtensionRegistry registry = ExtensionRegistry.newInstance();
Testdemo.registerAllExtensions(registry);
Base.PBDemo pbdemo = Base.PBDemo.parseFrom(packet, registry);
Testdemo.HelloResponse rsp = pbdemo.getTestdemoRsp().getExtension(Testdemo.helloRsp);
System.out.println(rsp.getMessage().toString());
代码不做过多解释,可以查看一下两个资料:
ExtensionRegistry
PB Extensions
以上代码demo,都在github上,请大家自行下载, [Git地址](https://github.com/cosysun/NettyDemo.git)
总结
PB的打包解包性能,介于二进制和json之间,同时协议的扩展性,以及数据包压缩功能, 独立于语言和平台特性,都足以让其脱颖而出。