一、什么是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協議來通信時,就需要自己實現,同時還需要考慮大端、小端模式的轉換問題。