当前位置: 首页 > 后端技术 > Java

Netty系列:在netty中使用protobuf协议

时间:2023-04-01 19:51:56 Java

介绍Netty有很多适配不同协议的编码工具,Google出品的流行protobuf也不例外。Netty提供了ProtobufDecoder和ProtobufEncoder两个工具,以及相应的帧检测。下面我们将通过一个例子来详细说明如何在netty中使用protobuf。定义protobuf,我们举一个最简单的例子。首先,定义一个需要在网络中传输的消息。这里我们定义了一个student对象,它有一个age和一个name属性,如下:="StudentWrapper";消息学生{可选int32年龄=1;optionalstringname=2;}使用以下命令,用于编译:protoc--experimental_allow_proto3_optional-I=。--java_out=。student.proto可以看到生成了3个文件,分别是Student、StudentOrBuilder和StudentWrapper。其中Student和StudentOrBuilder才是我们真正需要用到的对象。定义处理程序在处理程序中,我们主要处理消息。这里我们在clientHandler中构造和发送消息。StudentClientHandler继承SimpleChannelInboundHandler和re-channelActive方法。在这个方法中,我们使用protobuf语法构建一个新的Student实例,并为他设置age和name属性。然后使用ctx.write和ctx.flush方法将其发送到服务器:publicvoidchannelActive(ChannelHandlerContextctx)throwsException{//channelactive//构造一个Student写入到channelStudentstudent=Student.newBuilder().setAge(22).setName("flydean").build();log.info("客户端发送消息{}",student);ctx.write(学生);ctx.flush();}StudentServerHandler也是继承了SimpleChannelInboundHandler,重写了channelRead0方法。当服务器读取学生消息时,将日志输出写回通道供clientHandler读取:publicvoidchannelRead0(ChannelHandlerContextctx,Studentstudent)throwsException{log.info("服务器收到消息{}",student);//写消息ChannelFuturefuture=ctx.write(student);}客户端读取消息后,不作进一步处理,直接输出日志,至此,客户端与服务端的一轮交互完成:publicvoidchannelRead0(ChannelHandlerContextctx,Studentstudent)throwsException{log.info(“客户端收到消息{}”,学生);}上一节中设置ChannelPipeline,无论是在StudentClientHandler还是StudentServerHandler中,我们都假设是通过channel对象是Student,不是原来的ByteBuf。这是怎么做到的?这里需要用到netty提供的帧检测。Netty为protobuf协议提供了ProtobufDecoder和ProtobufEncoder来对protobuf对象进行编码和解码。但是这两个编码器和解码器分别是MessageToMessageEncoder和MessageToMessageDecoder。它们是消息到消息的编码器和解码器,因此它们需要与帧检测结合使用。Netty还提供了与protobuf一起使用的帧检测器,它们是ProtobufVarint32FrameDecoder和ProtobufVarint32LengthFieldPrepender。Varint32指的是protobuf的编码格式,第一个字节使用了一个变量varint。有了framedetector和codec,我们只需要按顺序添加到ChannelPipeline中即可。在客户端,StudentClientInitializer继承自ChannelInitializer,我们需要重写它的initChannel方法:publicvoidinitChannel(SocketChannelch){p.addLast(新的ProtobufVarint32FrameDecoder());p.addLast(newProtobufDecoder(Student.getDefaultInstance()));p.addLast(新的ProtobufVarint32LengthFieldPrepender());p.addLast(新的ProtobufEncoder());p.addLast(newStudentClientHandler());在服务器端,StudentServerInitializer也继承自ChannelInitializer,同样需要重写它的initChannel方法:publicvoidinitChannel(SocketChannelch)throwsException{ChannelPipelinep=ch.pipeline();p.addLast(新的ProtobufVarint32FrameDecoder());p.addLast(newProtobufDecoder(Student.getDefaultInstance()));p.addLast(新的ProtobufVarint32LengthFieldPrepender());p.addLast(新的ProtobufEncoder());p.addLast(newStudentServerHandler());ChannelPipeline也已设置。构建客户端和服务器并运行最后要做的就是构建客户端和服务器并运行它,这与普通的netty客户端和服务器没有区别:BuildStudentClient:publicstaticvoidmain(String[]args)throws异常{EventLoopGroup组=newNioEventLoopGroup();试试{Bootstrapb=newBootstrap();b.group(group).channel(NioSocketChannel.class).handler(newStudentClientInitializer());//建立连接Channelch=b.连接(主机,端口).sync().channel();//等待关闭ch.closeFuture().sync();}最后{group.shutdownGracefully();}}构建StudentServer:publicstaticvoidmain(String[]args)throwsException{EventLoopGroupbossGroup=newNioEventLoopGroup(1);EventLoopGroupworkerGroup=newNioEventLoopGroup();尝试{ServerBootstrapb=newServerBootstrap();b.group(bossGroup,workerGroup).channel(NioServerSocketChannel.class).handler(newLoggingHandler(LogLevel.INFO)).childHandler(newStudentServerInitializer());b.bind(PORT).sync().channel().closeFuture().sync();}最后{bossGroup.shutdownGracefully();workerGroup.shutdownGracefully();}}运行以获取:服务器端:[nioEventLoopGroup-3-1]INFOc.f.protobuf.StudentServerHandler-服务器收到消息年龄:22name:“flydean”[nioEventLoopGroup-2-1]INFOc.f.protobuf.StudentClientHandler-client发送消息age:22name:"flydean"clientside:[nioEventLoopGroup-2-1]INFOc.f.protobuf.StudentClientHandler-client收到消息age:22name:"flydean",可以看出Student消息已经被sentandreceivedsuccessful综上所述,netty提供了很多适配协议的工具类,让我们可以专注于业务逻辑,而不用考虑具体的编码转换问题,非常好用。本文示例可参考:learn-netty4本文已收录于http://www.flydean.com/17-netty-protobuf/最流行的解读,最深刻的干货,最简洁的教程,很多你不知道的小技巧等你来发现!欢迎关注我的公众号:《程序那些事儿》,懂技术,更懂你!