一、什么是Google Protocol Buffer(protobuf官方网站)
下面是官网给的解释:
Protocol buffers are a language-neutral, platform-neutral extensible mechanism for serializing structured data. – think XML, but smaller, faster, and simpler.
协议缓冲区是一种和语言无关、平台无关的可扩展机制,用于序列化结构化的数据。相比于xml,它更小,更快,更简单。数据缓冲区常用语通信协议和数据存储。
二、ProtoBuf的性能
序列化测试对比:
Ser Time + Deser Time(ns)
下面两个网站是效率测试实验:
- https://code.google.com/archive/p/thrift-protobuf-compare/wikis/Benchmarking.wiki
- https://github.com/eishay/jvm-serializers/wiki
三、使用Intellij IDEA插件自动生成Java类
参考我的另一篇文章:Google Protocol Buffer 的使用(一)
四、Netty和protobuf整合
准备我们的proto文件
syntax = "proto3";
package com.netty.protobuf;
option java_outer_classname = "UserInfoProto";
//用户信息
message UserInfo{
//姓名
string name = 1;
//住址
repeated Address address= 2;
//年龄
uint32 age = 3;
}
//用户常用住址
message Address{
string addressname = 1;
uint32 adressno = 2;
}
服务器中设置protobuf编码器和解码器
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
//protobuf解码器
pipeline.addLast(new ProtobufVarint32FrameDecoder());
pipeline.addLast(new ProtobufDecoder(UserInfoProto.UserInfo.getDefaultInstance()));
//protobuf编码器
pipeline.addLast(new ProtobufVarint32LengthFieldPrepender());
pipeline.addLast(new ProtobufEncoder());
pipeline.addLast(new NettyServerHandler());
}
- ProtobufVarint32FrameDecoder是protobuf方式的解码器,用于解决TCP粘包和拆包问题
- ProtobufDecoder 中设置我们的proto文件生成的实例,其实就是我们的目标Java类,设置方式为:UserInfoProto.UserInfo.getDefaultInstance()
- ProtobufVarint32LengthFieldPrepender和ProtobufEncoder是protobuf方式的编码器
处理类中写protobuf数据
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
LOGGER.info("client {} connected.", ctx.channel().remoteAddress());
UserInfoProto.UserInfo user = UserInfoProto.UserInfo.newBuilder()
.setName("server")
.setAge(18)
.addAddress(
UserInfoProto.Address.newBuilder()
.setAddressname("beijing 001")
.setAdressno(911))
.build();
ctx.writeAndFlush(user);
}
- 这里通过UserInfoProto.UserInfo.newBuilder()使用的时间其类的建造者模式设置用户相关信息。
- 再通过ChannelHandlerContext的writeAndFlush方法写用户数据。
处理器中读protobuf数据
private int count = 0;
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
UserInfoProto.UserInfo message = (UserInfoProto.UserInfo) msg;
LOGGER.info("server received message {}:{}", ++count, message);
}
- channelRead中的Object对象通过解码之后就是一个protobuf类对象,所以可以强转:UserInfoProto.UserInfo message = (UserInfoProto.UserInfo) msg;
五、注意事项(TCP读半包处理)
这里我们只是简单使用了netty自带的ProtobufVarint32FrameDecoder解码器来处理读半包问题,我们还可以自己继承ByteToMessageDecoder类实现一个定制化的解码器。比如我们使用Java客户端和C++服务器通过protobuf协议来通信时,就需要自己实现,同时还需要考虑大端、小端模式的转换问题。