题为谷歌开源项目ProtoBuf一探(基础环境配置)文章奠定了走进ProtoBuf的世界的路基,环境配置完成后,就正式地开启旅程。
第一站:ProtoBuf语义
既然是一门新的编程语言(使命是定义数据结构),我们就需要去掌握其中语义。学习其它任何语言都一样。语法规则搞懂了,剩下的就是逻辑码代码。以下面代码为例,附带注释。
syntax = "proto2";//定义协议版本,现在出来了proto3 package tutorial;//定义该文件的包名 option java_package = "com.example.tutorial";//定义输出java文件的包名 option java_outer_classname = "AddressBookProtos";//定义输出java文件的类名 message Person { //定义消息数据结构名称 required string name = 1;//字段修饰 required int32 id = 2; optional string email = 3; enum PhoneType {//定义枚举类型 MOBILE = 0; HOME = 1; WORK = 2; } message PhoneNumber { required string number = 1; optional PhoneType type = 2 [default = HOME];//默认值为HOME=1 } repeated PhoneNumber phones = 4; } message AddressBook { repeated Person people = 1; }
从上述代码中可以看出,Person
消息数据结构包含若干基本类型,如bool
,int32
,string
,enum
等。数据类型前有且仅有这三种修饰符。如下表所示。
修饰符 | 含义 |
required | 字段属性为必须,否则消息被视为未初始化 |
optional | 字段属性为可选,若该字段未被初始,就会持有其设置的默认值;也可以不设置,系统会设置默认值 |
repeated | 字段属性为重复,重复的字段在Protocol Buffer中保存 |
在代码中,也能看到可以嵌套消息数据结构。这在输出的Java文件得出对应的嵌套静态常量类。这样就极大地丰富了自定义协议的灵活性。可以任意组合出N种类型的消息数据结构。
Protocol Buffer系统默认值如下表所示:
数据类型 | 默认值 |
int32 | 0 |
string | 空字符串 |
enum | 第一个值 |
bool | false |
如果枚举类型中,枚举类型的必须是32bit 的整数值;想让其中两个值相等,必须开启别号功能即option allow_alias = true
,如下代码所述。
enum EnumAllowingAlias { option allow_alias = true; UNKNOWN = 0; STARTED = 1; RUNNING = 1; } enum EnumNotAllowingAlias { UNKNOWN = 0; STARTED = 1; // RUNNING = 1; // Uncommenting this line will cause a compile error inside Google and a warning message outside. }
其它需要注意的地方请参照官方指导1
第二站:入驻Netty城
Netty作为一种高扩展-异步-事件驱动型通信框架已经享誉国内外。在该框架中,拥有市场上存在的很多协议的编解码器,如HTTP,WebSocket,MQTT,SMTP,SSL等。这极大地缩短了开发者的研发周期。避免重复造轮子。
在写好ChannelInboundHandler
或者ChannelOutboundHandler
处理器后,就需要进行测试,或许你会采用开启客户端和服务端来看是否工作正常,这样显得太笨重了。而且有小问题也不容易查看出。这时一个嵌入的通道在Netty中应运而生,它就是EnbededChannel
,它的两对方法极为重要。其余方法请看官方API2。如下表所示。
方法 | 作用 |
public boolean writeOutbound(Object… msgs) | 向通道中写出站数据 |
public T readOutbound() | 从通道中读经过出站处理器处理后的数据(如编码) |
public boolean writeInbound(Object… msgs) | 向通道中写出入站数据 |
public T readInbound() | 从通道中读经过入站处理器处理后的数据(如解码) |
当自定义好ProtoBuf
协议后,编译出java文件,导入项目工程中。然后利用Netty自带的编解码器进行测试。代码如下。
@Test public void testAlarmProto(){ //构建消息类型 MessageProto.MessageBase.Header.Builder header =MessageProto.MessageBase.Header.newBuilder(); header.setType(MessageProto.MessageBase.MessageType.SERVICE_REQ); MessageProto.MessageBase.Builder message = MessageProto.MessageBase.newBuilder(); message.setHeader(header.build()); //构建带有处理器的通道 EmbeddedChannel ch = new EmbeddedChannel(); ch.pipeline().addLast("frameDecoder",new ProtobufVarint32FrameDecoder()); ch.pipeline().addLast("decoder",new ProtobufDecoder(MessageProto.MessageBase.getDefaultInstance())); ch.pipeline().addLast("frameEncoder",new ProtobufVarint32LengthFieldPrepender()); ch.pipeline().addLast("encoder",new ProtobufEncoder()); /** * TODO:先写入数据进行编码,然后再读出数据进行解码 */ ch.writeOutbound(message.build()); ByteBuf byteBuf = ch.readOutbound(); System.out.println(byteBuf.toString()); ch.writeInbound(byteBuf); ch.finish(); MessageProto.MessageBase messageBase = ch.readInbound(); System.out.println(messageBase.getHeader().getType().getNumber()); System.out.println(messageBase.getBody().getContext().getBuildingPart()); }
能够从POJO到POJO就表示成功 ? 。
https://developers.google.com/protocol-buffers/docs/proto ↩︎
https://netty.io/4.1/api/index.html ↩︎