Netty结合Protobuf进行编解码的方法
一般在使用netty时,数据传输的时候都会选择对传输的数据进行编解码,编码后的数据变小, 有利于在有限的带宽下传输更多的数据。
由于java本身序列化的缺点较多(无法跨语言,序列化后的码流太大,序列化的性能太低等),业界主流的编解码框架主要有如下三个:
- Google的Protobuf
- Facebook的Thrift
- JBoss的Marshalling
1. 什么是Protobuf? Protobuf全称是Google Protocol Buffers, 它是谷歌公司开源的一个序列化框架。
它将数据结构以.proto文件进行描述,通过代码生成工具可以生成对应数据结构的POJO对象和Protobuf相关的方法和属性。
它的特点如下:
- 结构化数据存储格式
- 高效的编解码性能
- 语言无关、平台无关、扩展性好
- 官方支持多个语言(java,c++,python,c#等)
下载后,将压缩包进行解压。这里主要用到bin目录下的protoc.exe。
3. 定义好proto文件 SubscribeReq.proto
// 区分不同的protobuf版本,必须有syntax = "proto2"; package netty; // 生成的目标类的包路径option java_package = "cn.ddlover.nettystudy.protobuf"; // 生成的目标类的名字option java_outer_classname = "SubscribeReqProto"; message SubscribeReq{required int32 subReqID = 1; required string userName = 2; required string productName = 3; repeated string address = 4; }
SubscribeResp.proto
syntax = "proto2"; package netty; option java_package = "cn.ddlover.nettystudy.protobuf"; option java_outer_classname = "SubscribeRespProto"; message SubscribeResp{required int32 subReqID = 1; required int32 respCode = 2; required string desc = 3; }
此时项目结构如下
文章图片
重点关注一下两个proto文件的位置,是位于项目的根路径下
4. 分别执行以下命令 D:\xhb\protoc-3.6.1-win32\bin\protoc.exe --java_out=.\src\main\java SubscribeResp.proto
D:\xhb\protoc-3.6.1-win32\bin\protoc.exe --java_out=.\src\main\java SubscribeReq.proto
这里需要把protoc.exe的位置换成自己的, --java_out命令设置的是proto文件中java_package指令的父目录。
5. 此时项目结构如下
文章图片
下面开始代码方面的开发,
1. 先贴一波maven的配置
4.0.0 cn.ddlover nettystudy1.0-SNAPSHOT 1.8 io.netty netty-all4.1.33.Final com.google.protobuf protobuf-java3.6.1 org.projectlombok lombok1.18.4
2. 这里贴上完整代码的项目结构
文章图片
3. SubReqServer.java
import cn.ddlover.nettystudy.handler.SubReqServerHandler; import cn.ddlover.nettystudy.protobuf.SubscribeReqProto; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelOption; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.handler.codec.protobuf.ProtobufDecoder; import io.netty.handler.codec.protobuf.ProtobufEncoder; import io.netty.handler.codec.protobuf.ProtobufVarint32FrameDecoder; import io.netty.handler.codec.protobuf.ProtobufVarint32LengthFieldPrepender; import io.netty.handler.logging.LogLevel; import io.netty.handler.logging.LoggingHandler; /** * Protobuf版本图书订购代码 */public class SubReqServer {private static final int PORT = 8080; public static void main(String[] args) {bind(PORT); } private static void bind(int port) {EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workerGroup = new NioEventLoopGroup(); ServerBootstrap b = new ServerBootstrap(); try {b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class).option(ChannelOption.SO_BACKLOG, 100).handler(new LoggingHandler(LogLevel.INFO)).childHandler(new ChannelInitializer() {@Overrideprotected void initChannel(SocketChannel socketChannel) throws Exception {// 半包处理socketChannel.pipeline().addLast(new ProtobufVarint32FrameDecoder()); socketChannel.pipeline().addLast(new ProtobufDecoder(SubscribeReqProto.SubscribeReq.getDefaultInstance())); socketChannel.pipeline().addLast(new ProtobufVarint32LengthFieldPrepender()); socketChannel.pipeline().addLast(new ProtobufEncoder()); socketChannel.pipeline().addLast(new SubReqServerHandler()); }}); ChannelFuture future = b.bind(port).sync(); future.channel().closeFuture().sync(); } catch (InterruptedException e) {e.printStackTrace(); }finally {bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); }}}
4. SubReqServerHandler.java
import cn.ddlover.nettystudy.protobuf.SubscribeReqProto; import cn.ddlover.nettystudy.protobuf.SubscribeRespProto; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; public class SubReqServerHandler extends ChannelInboundHandlerAdapter {@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {SubscribeReqProto.SubscribeReq req = (SubscribeReqProto.SubscribeReq)msg; if ("张三".equals(req.getUserName())) {System.out.println("Server accept clietn subscribe req : ["+req.toString()+"]"); ctx.writeAndFlush(resp(req.getSubReqID())); }} private SubscribeRespProto.SubscribeResp resp(int subReqID) {SubscribeRespProto.SubscribeResp.Builder builder = SubscribeRespProto.SubscribeResp.newBuilder(); builder.setSubReqID(subReqID); builder.setRespCode(0); builder.setDesc("netty书籍下单成功,3天后将会送到你的住处"); return builder. build(); } @Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {cause.printStackTrace(); ctx.close(); }}
5. SubReqClient.java
import cn.ddlover.nettystudy.handler.SubReqClientHandler; import cn.ddlover.nettystudy.protobuf.SubscribeRespProto; import io.netty.bootstrap.Bootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelOption; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioSocketChannel; import io.netty.handler.codec.protobuf.ProtobufDecoder; import io.netty.handler.codec.protobuf.ProtobufEncoder; import io.netty.handler.codec.protobuf.ProtobufVarint32FrameDecoder; import io.netty.handler.codec.protobuf.ProtobufVarint32LengthFieldPrepender; public class SubReqClient {private static final String HOST = "localhost"; private static final int PORT = 8080; public static void main(String[] args) {connect(HOST, PORT); } private static void connect(String host, int port) {EventLoopGroup group = new NioEventLoopGroup(); Bootstrap b = new Bootstrap(); try {b.group(group).channel(NioSocketChannel.class).option(ChannelOption.TCP_NODELAY, true).handler(new ChannelInitializer() {@Overrideprotected void initChannel(SocketChannel socketChannel) throws Exception { // 半包处理socketChannel.pipeline().addLast(new ProtobufVarint32FrameDecoder()); socketChannel.pipeline().addLast(new ProtobufDecoder(SubscribeRespProto.SubscribeResp.getDefaultInstance())); socketChannel.pipeline().addLast(new ProtobufVarint32LengthFieldPrepender()); socketChannel.pipeline().addLast(new ProtobufEncoder()); socketChannel.pipeline().addLast(new SubReqClientHandler()); }}); ChannelFuture f = b.connect(host, port).sync(); f.channel().closeFuture().sync(); } catch (InterruptedException e) {e.printStackTrace(); }finally {group.shutdownGracefully(); }}}
6. SubReqClientHandler.java
import cn.ddlover.nettystudy.protobuf.SubscribeReqProto; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; import java.util.ArrayList; import java.util.List; public class SubReqClientHandler extends ChannelInboundHandlerAdapter { @Overridepublic void channelActive(ChannelHandlerContext ctx) throws Exception {for (int i =0; i<10; i++) {ctx.write(subReq(i)); }ctx.flush(); } private SubscribeReqProto.SubscribeReq subReq(int i) {SubscribeReqProto.SubscribeReq.Builder builder = SubscribeReqProto.SubscribeReq.newBuilder(); builder.setSubReqID(i); builder.setUserName("张三"); builder.setProductName("Netty Book"); List address = new ArrayList<>(); address.add("NanJing YuHuaTai"); address.add("BeiJing LiuLiChang"); address.add("ShenZhen HongShuLin"); builder.addAllAddress(address); return builder.build(); } @Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {System.out.println("Receive server response : ["+ msg +"]"); } @Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {cause.printStackTrace(); ctx.close(); }}
代码部分到此就结束了,然后分别运行SubReqServer和SubReqClient, 就可以发现运行成功了。
这里要注意的事,调用toString的时候,中文在控制台打印的是字节,而调用对应属性的getter方法的时候,是可以做中文的判断的,显然这里protobuf的toString没有被修改好呀。
当然,我们也可以发现,netty本身已经封装好了对谷歌的protobuf的支持。Netty还是很强大的。
【Netty结合Protobuf进行编解码的方法】到此这篇关于Netty结合Protobuf进行编解码的文章就介绍到这了,更多相关Netty结合Protobuf编解码内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
推荐阅读
- 【38】“劳逸结合”的重要性
- springboot结合redis实现搜索栏热搜功能及文字过滤
- Netty|Netty 源码之 FastThreadLocal
- 产融结合—黄生
- 囚徒健身与传统中医健身结合
- RxJava结合Retrofit使用
- nlp|Keras(十一)梯度带(GradientTape)的基本使用方法,与tf.keras结合使用
- 浅谈产品思维
- Netty|Netty 示例6 集成Protobuf
- 16.vue中Js动画与Velocity.js库结合