Netty中使用protobuf
- 环境准备
下载protoc代码生成器和源码包:http://code.google.com/p/protobuf/downloads/list,
- 制作.Proto文件
格式参考如下:
注:可以按如上内容编写文本文件,然后修改后缀为.Proto。
proto文件结构
从上面的代码可以看出,proto文件的结构非常简单。
- package java_package 定义生成的java类的package名称。
- message定义一种类型,类型以Java中的一个class,Book是类名,在生成Java代码时,就是使用这个类名。
- option optimize_for = LITE_RUNTIME:表示生成的java代码继承GeneratedMessageLite ,继承GeneratedMessageLite的好处是可张念以用jboss提供的编解码器,可以认为是必写项。
- 该类型中包含的字段格式: [限定符 类型 字段名称 = tag [default = 默认值]]
支持的默认类型
.proto Type |
Notes |
C++ Type |
Java Type |
double |
|
double |
double |
float |
|
float |
float |
int32 |
Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint32 instead. |
int32 |
int |
int64 |
Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint64 instead. |
int64 |
long |
uint32 |
Uses variable-length encoding. |
uint32 |
int1 |
uint64 |
Uses variable-length encoding. |
uint64 |
long1 |
sint32 |
Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int32s. |
int32 |
int |
sint64 |
Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int64s. |
int64 |
long |
fixed32 |
Always four bytes. More efficient than uint32 if values are often greater than 228. |
uint32 |
int1 |
fixed64 |
Always eight bytes. More efficient than uint64 if values are often greater than 256. |
uint64 |
long1 |
sfixed32 |
Always four bytes. |
int32 |
int |
sfixed64 |
Always eight bytes. |
int64 |
long |
bool |
|
bool |
boolean |
string |
A string must always contain UTF-8 encoded or 7-bit ASCII text. |
string |
String |
bytes |
May contain any arbitrary sequence of bytes. |
string |
ByteString |
除此之外,还支持枚举,自定义类型等。
protobuf的限定符
- required:一个格式良好的消息一定要含有1个这种字段。表示该值是必须要设置的;如果该项不设值,在序列化时会抛出RuntimeException,推荐不使用该字段,该字段一旦使用,无法更改。
由于一些历史原因,基本数值类型的repeated的字段并没有被尽可能地高效编码。在新的代码中,用户应该使用特殊选项[packed=true]来保证更高效的编码。如:repeated int32 samples = 4 [packed=true];
required是永久性的:在将一个字段标识为required的时候,应该特别小心。如果在某些情况下不想写入或者发送一个required的字段,将原始该字段修饰符更改为optional可能会遇到问题——旧版本的使用者会认为不含该字段的消息是不完整的,从而可能会无目的的拒绝解析。在这种情况下,你应该考虑编写特别针对于应用程序的、自定义的消息校验函数。Google的一些工程师得出了一个结论:使用required弊多于利;他们更愿意使用optional和repeated而不是required。当然,这个观点并不具有普遍性。
- optional:消息格式中该字段可以有0个或1个值(不超过1个)。不存在时使用默认值,默认值可自定义如[default = 10]。系统默认值如下:int = 0,bool = false,string=""
- repeated:在一个格式良好的消息中,这种字段可以重复任意多次(包括0次)。重复的值的顺序会被保留。表示该值可以重复,相当于java中的List。
guide style
Protocol Buffers官方提供了一个Guide Style。其中介绍了一些定义proto的优秀实践。一句话总结就是名称时,message名称以驼峰方式定义,字段名则需要以如author_name方式定义。字段名在生成Java代码时,会自动转换为符合Java风格的命名方式。
生成. Java文件
将编写的.Proto文件与下载的protoc.exe文件放在同一指定的目录,进入dos并系统编译命令:protoc XXX.proto --java_out=C:/,这时会在你所指令的目录生成相应的java文件。
它的一些缺点
- 不支持大数据集的处理,少于1M
- 不支持Date,Map这些Java内建的对象
- 客户端示例
- 如果Message类中用到repeated 定义的类型,则在客户端中按如下代码操作: builder.addBody(Message.Body.newBuilder().setContent(bytes).build());
- 也可用下面默认的方式传输二进制,这时在节点7中就要使用默认的protobuf解码方式,
byte[] test = "asdfa".getBytes();
ChannelBuffer channelBuffer = ChannelBuffers.buffer(test.length);
// 将 获得到的数组写入 channelBuffer中
channelBuffer.writeBytes(test);
// 发送到服务器端
ChannelFuture lastWriteFuture = channel.write(channelBuffer);
- 服务器端示例
- ServerHandler示例
- 如果使用默认的Protobuf解码方式传输二进制,则接收时按以下代码接收:
if((e.getMessage() instanceof ChannelBuffer)){
ChannelBuffer channelBuffer =(ChannelBuffer)e.getMessage();
byte[] bytes = channelBuffer.array();
}
- PipelineFactory示例
- 其中,以下两行代码是默认的Protobuf解码类型,如果到自定义解码,这两行可以注释掉,反之把自定义的加码解码注释掉.
pipeline.addLast("frameDecoder", new ProtobufVarint32FrameDecoder());
pipeline.addLast("frameEncoder", new ProtobufVarint32LengthFieldPrepender());