netty 系列之: 在 netty 中使用 protobuf 协议
简介 netty 中有很多适配不同协议的编码工具,对于流行的 google 出品的 protobuf 也不例外。netty 为其提供了 ProtobufDecoder 和 ProtobufEncoder 两个工具还有对应的 frame detection,接下来我们会通过一个例子来详细讲解如何在 netty 中使用 protobuf。
定义 protobuf 我们举个最简单的例子,首先定义一个需要在网络中进行传输的 message,这里我们定义一个 student 对象,他有一个 age 和一个 name 属性,如下所示:
syntax = "proto3";
package com.flydean17.protobuf;
option java_multiple_files = true;option java_package = "com.flydean17.protobuf";option java_outer_classname = "StudentWrapper";
message Student {optional int32 age = 1;optional string name =2;}使用下面的命令,对其进行编译:
protoc --experimental_allow_proto3_optional -I=. --java_out=. student.proto
可以看到生成了 3 个文件,分别是 Student,StudentOrBuilder 和 StudentWrapper。其中 Student 和 StudentOrBuilder 是我们真正需要用到的对象。
定义 handler 在 handler 中,我们主要进行对消息进行处理,这里我们在 clientHandler 中进行消息的构建和发送,StudentClientHandler 继承 SimpleChannelInboundHandler 并重新 channelActive 方法, 在该方法中我们使用 protobuf 的语法,构建一个新的 Student 实例,并给他设置好 age 和 name 两个属性。
然后使用 ctx.write 和 ctx.flush 方法将其发送到 server 端:
StudentServerHandler 也是继承 SimpleChannelInboundHandler,并重写 channelRead0 方法,当 server 端读取到 student 消息的时候,日志输出,并将其回写到 channel 中,供 clientHandler 读取:
当 client 读取到消息之后,直接日志输出,不再做进一步处理,到此,一轮 client 和 server 端的交互就完成了:
设置 ChannelPipeline 在上一节,不管是在 StudentClientHandler 还是在 StudentServerHandler 中,我们都假设 channel 中传递的对象就是 Student,而不是原始的 ByteBuf。这是怎么做到的呢?
这里我们需要使用到 netty 提供的 frame detection,netty 为 protobuf 协议专门提供了 ProtobufDecoder 和 ProtobufEncoder,用于对 protobuf 对象进行编码和解码。
但是这两个编码和解码器分别是 MessageToMessageEncoder 和 MessageToMessageDecoder,他们是消息到消息的编码和解码器,所以还需要和 frame detection 配合使用。
netty 同样提供了和 protobuf 配合使用的 frame detector,他们是 ProtobufVarint32FrameDecoder 和 ProtobufVarint32LengthFieldPrepender。
Varint32 指的是 protobuf 的编码格式,第一个字节使用的是可变的 varint。
有了 frame detector 和编码解码器,我们只需要将其顺序加入 ChannelPipeline 即可。
在客户端,StudentClientInitializer 继承自 ChannelInitializer,我们需要重写其 initChannel 方法:
在服务器端,同样 StudentServerInitializer 也继承自 ChannelInitializer,也需要重写其 initChannel 方法:
这样 ChannelPipeline 也设置完成了。
构建 client 和 server 端并运行最后好做的就是构建 client 和 server 端并运行,这和普通的 netty 客户端和服务器端并没有什么区别:
构建 StudentClient:
public static void main(String[] args) throws Exception {
构建 StudentServer:
public static void main(String[] args) throws Exception {EventLoopGroup bossGroup = new NioEventLoopGroup(1);EventLoopGroup workerGroup = new NioEventLoopGroup();try {ServerBootstrap b = new ServerBootstrap();b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class).handler(new LoggingHandler(LogLevel.INFO)).childHandler(new StudentServerInitializer());
运行可得:
server 端:[nioEventLoopGroup-3-1] INFO c.f.protobuf.StudentServerHandler - server 收到消息 age: 22name: "flydean"
[nioEventLoopGroup-2-1] INFO c.f.protobuf.StudentClientHandler - client 发送消息 age: 22name: "flydean"
client 端:[nioEventLoopGroup-2-1] INFO c.f.protobuf.StudentClientHandler - client 收到消息 age: 22name: "flydean"可见 Student 消息已经发送和接收成功了。
总结 netty 提供了很多和协议适配的工具类,这样我们就可以专注于业务逻辑,不需要考虑具体的编码转换的问题,非常好用。
本文的例子可以参考:learn-netty4
本文已收录于 http://www.flydean.com/17-netty-protobuf/
最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧等你来发现!
欢迎关注我的公众号:「程序那些事」,懂技术,更懂你!
版权声明: 本文为 InfoQ 作者【程序那些事】的原创文章。
原文链接:【http://xie.infoq.cn/article/d6b613bbf17c9bbec3b9bcb2b】。文章转载请联系作者。
评论