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

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


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

相关文章
|
10月前
|
缓存 编解码 网络协议
Netty基础—8.Netty实现私有协议栈
本文系统性地阐述了私有协议栈的实现框架,首先介绍了私有协议的基本概念和通信模型,然后详细说明了协议栈的消息定义、链路建立与关闭机制,以及心跳检测、重连和重复登录保护等稳定性设计,接着重点描述了核心组件包括ChannelHandler体系、客户端与服务端架构以及会话ID处理器等关键模块,最后涵盖了数据包结构与编解码实现方案,整体呈现了从协议规范到具体功能实现的完整技术架构。
|
存储 数据安全/隐私保护
Netty实战(十三)WebSocket协议(一)
WebSocket 协议是完全重新设计的协议,旨在为 Web 上的双向数据传输问题提供一个切实可行的解决方案,使得客户端和服务器之间可以在任意时刻传输消息,因此,这也就要求它们异步地处理消息回执。
906 0
|
Rust Dubbo 网络协议
通过 HTTP/2 协议案例学习 Java & Netty 性能调优:工具、技巧与方法论
通过 HTTP/2 协议案例学习 Java & Netty 性能调优:工具、技巧与方法论
12929 82
|
网络协议 前端开发 Linux
快来体验快速通道,netty中epoll传输协议详解
在前面的章节中,我们讲解了kqueue的使用和原理,接下来我们再看一下epoll的使用。两者都是更加高级的IO方式,都需要借助native的方法实现,不同的是Kqueue用在mac系统中,而epoll用在liunx系统中。
|
Java Spring
Springboot整合Netty,自定义协议实现
以上就是在Spring Boot中整合Netty并实现自定义协议的基本步骤。你需要根据你的自定义协议的具体需求,来实现你的编码器、解码器和处理器。
1185 0
|
编解码 JSON 网络协议
Netty使用篇:Http协议编解码
Netty使用篇:Http协议编解码
|
Nacos
Netty自定义消息协议的实现逻辑处理粘包拆包、心跳机制
Netty自定义消息协议的实现逻辑处理粘包拆包、心跳机制
397 0
|
数据安全/隐私保护
Netty实战(十四)WebSocket协议(二)
我们之前说过为了将 ChannelHandler 安装到 ChannelPipeline 中,需要扩展了ChannelInitializer,并实现 initChannel()方法
529 0
|
前端开发 Java fastjson
Netty系列(一):Springboot整合Netty,自定义协议实现
Netty是由JBOSS提供的一个java开源框架,现为 [Github](https://github.com/netty/netty)上的独立项目。Netty提供异步的、事件驱动的网络应用程序框架和工具,用以快速开发高性能、高可靠性的网络服务器和客户端程序。
1317 0
Netty系列(一):Springboot整合Netty,自定义协议实现
|
JSON 网络协议 算法
由浅入深Netty协议设计与解析
由浅入深Netty协议设计与解析
292 0