简介

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端:

    public void channelActive(ChannelHandlerContext ctx) throws Exception {
// channel活跃
//构建一个Student,并将其写入到channel中
Student student= Student.newBuilder().setAge(22).setName("flydean").build();
log.info("client发送消息{}",student);
ctx.write(student);
ctx.flush();
}

StudentServerHandler也是继承SimpleChannelInboundHandler,并重写channelRead0方法,当server端读取到student消息的时候,日志输出,并将其回写到channel中,供clientHandler读取:

    public void channelRead0(ChannelHandlerContext ctx, Student student) throws Exception {
log.info("server收到消息{}",student);
// 写入消息
ChannelFuture future = ctx.write(student);
}

当client读取到消息之后,直接日志输出,不再做进一步处理,到此,一轮client和server端的交互就完成了:

    public void channelRead0(ChannelHandlerContext ctx, Student student) throws Exception {
log.info("client收到消息{}",student);
}

设置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方法:

    public void initChannel(SocketChannel ch) {
ChannelPipeline p = ch.pipeline(); p.addLast(new ProtobufVarint32FrameDecoder());
p.addLast(new ProtobufDecoder(Student.getDefaultInstance())); p.addLast(new ProtobufVarint32LengthFieldPrepender());
p.addLast(new ProtobufEncoder()); p.addLast(new StudentClientHandler());
}

在服务器端,同样StudentServerInitializer也继承自ChannelInitializer,也需要重写其initChannel方法:

    public void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline p = ch.pipeline(); p.addLast(new ProtobufVarint32FrameDecoder());
p.addLast(new ProtobufDecoder(Student.getDefaultInstance())); p.addLast(new ProtobufVarint32LengthFieldPrepender());
p.addLast(new ProtobufEncoder()); p.addLast(new StudentServerHandler());
}

这样ChannelPipeline也设置完成了。

构建client和server端并运行

最后好做的就是构建client和server端并运行,这和普通的netty客户端和服务器端并没有什么区别:

构建StudentClient:

   public static void main(String[] args) throws Exception {

        EventLoopGroup group = new NioEventLoopGroup();
try {
Bootstrap b = new Bootstrap();
b.group(group)
.channel(NioSocketChannel.class)
.handler(new StudentClientInitializer());
// 建立连接
Channel ch = b.connect(HOST, PORT).sync().channel();
// 等待关闭
ch.closeFuture().sync();
} finally {
group.shutdownGracefully();
}
}

构建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()); b.bind(PORT).sync().channel().closeFuture().sync();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}

运行可得:

server端:
[nioEventLoopGroup-3-1] INFO c.f.protobuf.StudentServerHandler - server收到消息age: 22
name: "flydean" [nioEventLoopGroup-2-1] INFO c.f.protobuf.StudentClientHandler - client发送消息age: 22
name: "flydean" client端:
[nioEventLoopGroup-2-1] INFO c.f.protobuf.StudentClientHandler - client收到消息age: 22
name: "flydean"

可见Student消息已经发送和接收成功了。

总结

netty提供了很多和协议适配的工具类,这样我们就可以专注于业务逻辑,不需要考虑具体的编码转换的问题,非常好用。

本文的例子可以参考:learn-netty4

本文已收录于 http://www.flydean.com/17-netty-protobuf/

最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧等你来发现!

欢迎关注我的公众号:「程序那些事」,懂技术,更懂你!

netty系列之:在netty中使用protobuf协议的更多相关文章

  1. netty系列之:在netty中使用native传输协议

    目录 简介 native传输协议的依赖 netty本地传输协议的使用 总结 简介 对于IO来说,除了传统的block IO,使用最多的就是NIO了,通常我们在netty程序中最常用到的就是NIO,比如 ...

  2. netty系列之:使用netty搭建websocket服务器

    目录 简介 netty中的websocket websocket的版本 FrameDecoder和FrameEncoder WebSocketServerHandshaker WebSocketFra ...

  3. netty系列之:在netty中处理CORS

    目录 简介 服务端的CORS配置 CorsConfigBuilder CorsHandler netty对cors的支持 总结 简介 CORS的全称是跨域资源共享,他是一个基于HTTP-header检 ...

  4. netty系列之: 在netty中使用 tls 协议请求 DNS 服务器

    目录 简介 支持DoT的DNS服务器 搭建支持DoT的netty客户端 TLS的客户端请求 总结 简介 在前面的文章中我们讲过了如何在netty中构造客户端分别使用tcp和udp协议向DNS服务器请求 ...

  5. netty系列之:使用netty搭建websocket客户端

    目录 简介 浏览器客户端 netty对websocket客户端的支持 WebSocketClientHandshaker WebSocketClientCompressionHandler netty ...

  6. netty系列之:使用netty实现支持http2的服务器

    目录 简介 基本流程 CleartextHttp2ServerUpgradeHandler Http2ConnectionHandler 总结 简介 上一篇文章中,我们提到了如何在netty中配置TL ...

  7. netty系列之:请netty再爱UDT一次

    目录 简介 netty对UDT的支持 搭建一个支持UDT的netty服务 异常来袭 TypeUDT和KindUDT 构建ChannelFactory SelectorProviderUDT 使用UDT ...

  8. go微服务系列(四) - http api中引入protobuf

    1. protobuf相关依赖安装 2. 改造之前的client 2.1 新建proto文件 2.2 运行protoc命令生成go文件 2.3 然后把原来的map修改成具体的类型就可以了 3. 处理j ...

  9. 1. 彤哥说netty系列之开篇(有个问卷调查)

    你好,我是彤哥,本篇是netty系列的第一篇. 欢迎来我的公从号彤哥读源码系统地学习源码&架构的知识. 简介 本文主要讲述netty系列的整体规划,并调查一下大家喜欢的学习方式. 知识点 ne ...

随机推荐

  1. XCTF 3rd-GCTF-2017 hackme

    一.查壳的老生常谈了..2分的题目就不多bb了. 二..elf文件,拖入ida,直接查找字符串,找到对应的函数 三.直接分析: 这里讲道理我当时很懵逼,因为进去这个函数后,发现伪码非常复杂,这里困了挺 ...

  2. 源码解析Java Attach处理流程

    前言 当Java程序运行时出现CPU负载高.内存占用大等异常情况时,通常需要使用JDK自带的工具jstack.jmap查看JVM的运行时数据,并进行分析. 什么是Java Attach 那么JVM自带 ...

  3. Grafana、Prometheus、mtail-日志监控

    一:日志如何监控 在上一篇博客Grafana.Prometheus-监控平台中,简单了解了Grafana与Prometheus对项目做特定的监控打点,可视化的配置操作. 但是对于没有设置监控或者不容易 ...

  4. 手写Spring框架,是时候撸个AOP与Bean生命周期融合了!

    作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 嘎小子,这片代码水太深你把握不住! 在电视剧<楚汉传奇>中有这么一段刘邦 ...

  5. python之数据驱动Excel+ddt操作(方法二)

    一.Mail163数据如下: 二.Excel+ddt代码如下: import xlrdimport unittestfrom selenium import webdriverfrom seleniu ...

  6. POJ4007 Flood-it! 题解

    调得我快死了啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊!!!! 先自己写了几发,老是 T,然后去看题解,大体思路居然都差不多,估计是自己写挂了orz. 几乎所有题解都有个vis数组,真 nm 看不懂到底是什么意思 ...

  7. dubbo(四)

    前言 1.浅谈架构的发展 首先,要了解dubbo,就得了解,它是在什么背景下产生的?这就需要从架构的发展说起. 孟老师从事软件开发2008年份,那时候我上高一,那个时候,淘宝.京东都还没有火起来.那个 ...

  8. [IOI2005]mea

    IOI 读完题,感觉这个题并不是很难,那我是不是可以去IOI了: 最先考虑暴力,发现完全行不通,所以,我们考虑其他方法.突然发现:其实在确定 \(s_1\) 的时候,整个序列就可以确定了,所以我们考虑 ...

  9. Python输出格式化

    参考链接:https://m.jb51.net/article/33631.htm 要求:以固定长度在中间输出某字符串,剩余部分用其他符号补齐.如:"Hello World"  - ...

  10. Python基础之控制台打印不同颜色字符串

    参考文章:https://www.cnblogs.com/daofaziran/p/9015284.html 打印各种颜色的文字,但是需要传入文字 print_color.py "" ...