Netty入门到超神系列-Netty使用Protobuf编码解码

简介: 当我们的Netty客户端和服务端进行通信时数据在传输的过程中需要进行序列化,比如以二进制数据进行传输,那么我们的业务数据就需要有相应的编码器进行编码为二进制数据,当服务端拿到二进制数据后需要有相应的解码器进行解码得到真实的业务数据。

前言

数据在网络传输的过程中需要序列化或和反序列化,也就需要用到编码器和解码器,本篇文章主要是探讨Netty中的编码解码器以及Protobuf的使用。

Netty中的编码解码

当我们的Netty客户端和服务端进行通信时数据在传输的过程中需要进行序列化,比如以二进制数据进行传输,那么我们的业务数据就需要有相应的编码器进行编码为二进制数据,当服务端拿到二进制数据后需要有相应的解码器进行解码得到真实的业务数据。如下图:

当Netty发送或者接受一个消息的时候,就将会发生一次数据转换。入站消息会被解码:从字节转换为另一种格式(比如java对象);如果是出站消息,它会被编码成字节。

Netty提供一系列实用的编解码器,他们都实现了ChannelInboundHadnler或者ChannelOutboundHandler接口。在这些类中,channelRead方法已经被重写了。以入站为例,对于每个从入站Channel读取的消息,这个方法会被调用。随后,它将调用由解码器所提供的decode()方法进行解码,并将已经解码的字节转发给ChannelPipeline中的下一个ChannelInboundHandler。

在Netty提供了一些编码解码器如下:

  • StringEncoder : 针对于字符串的编码器
  • StringDecoder : 针对于字符串的解码器
  • ObjectEncoder : 针对于Java对象的编码器
  • ObjectDecoder : 针对于Java对象的解码器
  • MessageToByteEncoder:把message转换成byte的编码器 。
  • ByteToMessageDecoder :把byte转换成message的解码器,会有粘包拆包问题。
  • ReplayingDecoder:对ByteToMessageDecoder的扩展

对于 ObjectEncoder 和 ObjectDecoder而言它只是针对Java对象的编码解码, 换句话说底层使用的是Java的序列化技术,而Java序列化存在如下问题

  • 效率低下
  • 无法夸语言,只能支持java
  • 序列化后体积庞大,远远大于二进制编码

所以我们引入了 google 的 protobuf来解决这一问题

Protobuf认识

Protobuf 是 google 谷歌开源的项目,它是一种灵活,高效,自动化机制的结构数据序列化方法;它是一种跨语言、可扩展的序列化结构数据的方法,它可用于(数据)通信协议(RPC)、数据存储等。其具有以下特点:

  • 语言无关、平台无关。即 ProtoBuf 支持 Java、C++、Python 等多种语言,支持多个平台
  • 高效。类比XML,比 XML 更小(3 ~ 10倍)、更快(20 ~ 100倍)、更为简单
  • 扩展性、兼容性好。你可以更新数据结构,而不影响和破坏原有的旧程序

你可以定义数据的结构,然后使用特殊生成的源代码轻松的在各种数据流中使用各种语言进行编写和读取结构数据。你甚至可以更新数据结构,而不破坏由旧数据结构编译的已部署程序

在Netty中提供了针对于protobuf的编码器 ProtobufEncoder 和 解码器 ProtobufDecoder

使用Protobuf生成POJO

官方文档:http://developers.google.com/protocol-buffers/docs/proto

使用Protobuf需要使用protobuf 编译器编译器生成Java代码,步骤如下

  1. 定义以.proto结尾的文件
  2. 使用protoc.exe编译.proto文件生成java代码
  3. 编写Netty案例

第一步:编写.proto文件 , 可以使用IDEA编写,文件名我这里是User.proto

//定义版本syntax="proto3";
//生成的代码的类名和文件名optionjava_outer_classname="UserPOJO";
//使用messae管理数据,注意名字不要和java_outer_classname冲突messageUser{
//定义ID属性,int32对应java的,注意这里的1指的是需要而不是值int64id=1;
//定义一个name属性,类型为字符串String,注意这里的2指的是需要而不是值stringname=2;
//定义一个数组,爱好repeatedstringfavorite=3;
}

这个表格显示了在.proto文件内可以指定的类型, 与自动生成的相对类型!

第二步:安装proto.exe,下载地址:https://github.com/protocolbuffers/protobuf/releases,解压后进入bin目录

第三步:拷贝User.proto文件到bin目录,使用protoc.exe生产java文件 ,执行命令protoc.exe --java_out=. User.proto

生成的文件如下

Netty使用Protobuf编解码

还是以Netty的 Server/Client 模式为例来演示,只是这里的数据使用protobuf的编解码。

第一步:搭建工程,把UserPOJO.java文件拷贝到项目中,此时该类会报错,这需要导入一个依赖

<dependency><groupId>com.google.protobuf</groupId><artifactId>protobuf-java</artifactId><version>3.4.0</version></dependency><dependency><groupId>io.netty</groupId><artifactId>netty-all</artifactId><version>4.1.42.Final</version></dependency>

第二步:创建Netty服务端:NettyServer

publicclassNettyServer {
publicstaticvoidmain(String[] args) {
NioEventLoopGroupbossGroup=newNioEventLoopGroup();
NioEventLoopGroupworkGroup=newNioEventLoopGroup();
ServerBootstrapbootstrap=newServerBootstrap();
try {
bootstrap.group(bossGroup,workGroup)
                .channel(NioServerSocketChannel.class)
                .childHandler(newChannelInitializer<SocketChannel>() {
@OverrideprotectedvoidinitChannel(SocketChannelch) throwsException {
ChannelPipelinepipeline=ch.pipeline();
//服务端:使用protobuf解码器,需要指定解码器解码哪种类型pipeline.addLast("decoder",newProtobufDecoder(UserPOJO.User.getDefaultInstance()));
pipeline.addLast(newServerHandler());
                    }
                });
//启动服务器ChannelFuturesync=bootstrap.bind(2000).sync();
sync.channel().closeFuture().sync();
        } catch (InterruptedExceptione) {
e.printStackTrace();
        }finally {
bossGroup.shutdownGracefully();
workGroup.shutdownGracefully();
        }
    }
}

这里我们通过pipeline添加了protobuf的解码器

第三步:编写服务端的handler

publicclassServerHandlerextendsChannelInboundHandlerAdapter {
@OverridepublicvoidchannelRead(ChannelHandlerContextctx, Objectmsg) throwsException {
//把消息强转成UserPOJO.UserUserPOJO.Useruser= (UserPOJO.User) msg;
System.out.println("拿到数据:"+user.getName());
    }
@OverridepublicvoidexceptionCaught(ChannelHandlerContextctx, Throwablecause) throwsException {
ctx.channel().close();
    }
}

第四步:编写客户端:NettyClient

publicclassNettyClient {
publicstaticvoidmain(String[] args) {
NioEventLoopGroupeventLoopGroup=newNioEventLoopGroup();
Bootstrapbootstrap=newBootstrap();
bootstrap.group(eventLoopGroup)
                .channel(NioSocketChannel.class)
                .handler(newChannelInitializer<SocketChannel>() {
@OverrideprotectedvoidinitChannel(SocketChannelch) throwsException {
ChannelPipelinepipeline=ch.pipeline();
//给客户端增加编码器,使用ProtobufEncoder编码器pipeline.addLast("encoder",newProtobufEncoder());
pipeline.addLast(newClientHandler());
                    }
                });
try {
ChannelFuturesync=bootstrap.connect("127.0.0.1", 2000).sync();
sync.channel().closeFuture().sync();
        } catch (InterruptedExceptione) {
e.printStackTrace();
        }finally {
eventLoopGroup.shutdownGracefully();
        }
    }
}

客户端添加了ProtobufEncoder编码器

第五步:编写客户端的handler

publicclassClientHandlerextendsChannelInboundHandlerAdapter {
@OverridepublicvoidchannelRead(ChannelHandlerContextctx, Objectmsg) throwsException {
    }
@OverridepublicvoidchannelActive(ChannelHandlerContextctx) throwsException {
//当建立连接,就发送一个对象给服务端UserPOJO.Useruser=UserPOJO.User.newBuilder().setId(1).setName("zs").build();
ctx.writeAndFlush(user);
System.out.println("客户端发送 user="+user.getName());
    }
@OverridepublicvoidexceptionCaught(ChannelHandlerContextctx, Throwablecause) throwsException {
ctx.channel().close();
    }
}

代码编写完成,最终项目结构如下

第六步:测试,依次启动NettyServer,NettyClient ,效果如下

客户端:

服务端:

protobuf的内容就介绍到这里把,喜欢的话给个好评把。

目录
相关文章
|
6月前
|
缓存 网络协议 算法
Netty的基础入门(上)
Netty的基础入门(上)
223 1
|
6月前
|
缓存 网络协议 算法
《跟闪电侠学Netty》阅读笔记 - Netty入门程序解析
《跟闪电侠学Netty》阅读笔记 - Netty入门程序解析
211 0
|
9天前
|
消息中间件 编解码 网络协议
Netty从入门到精通:高性能网络编程的进阶之路
【11月更文挑战第17天】Netty是一个基于Java NIO(Non-blocking I/O)的高性能、异步事件驱动的网络应用框架。使用Netty,开发者可以快速、高效地开发可扩展的网络服务器和客户端程序。本文将带您从Netty的背景、业务场景、功能点、解决问题的关键、底层原理实现,到编写一个详细的Java示例,全面了解Netty,帮助您从入门到精通。
44 0
|
3月前
|
移动开发 网络协议 算法
(十)Netty进阶篇:漫谈网络粘包、半包问题、解码器与长连接、心跳机制实战
在前面关于《Netty入门篇》的文章中,咱们已经初步对Netty这个著名的网络框架有了认知,本章的目的则是承接上文,再对Netty中的一些进阶知识进行阐述,毕竟前面的内容中,仅阐述了一些Netty的核心组件,想要真正掌握Netty框架,对于它我们应该具备更为全面的认知。
200 2
|
6月前
|
存储 消息中间件 缓存
Netty的基础入门(下)
Netty的基础入门(下)
122 0
|
6月前
|
移动开发 网络协议 Java
通信密码学:探秘Netty中解码器的神奇力量
通信密码学:探秘Netty中解码器的神奇力量
196 0
|
6月前
|
前端开发 网络协议 Java
Netty入门指南:从零开始的异步网络通信
Netty入门指南:从零开始的异步网络通信
180 0
|
6月前
|
消息中间件 缓存 Java
《跟闪电侠学Netty》阅读笔记 - 开篇入门Netty
《跟闪电侠学Netty》阅读笔记 - 开篇入门Netty
172 0
|
存储 缓存 NoSQL
跟着源码学IM(十一):一套基于Netty的分布式高可用IM详细设计与实现(有源码)
本文将要分享的是如何从零实现一套基于Netty框架的分布式高可用IM系统,它将支持长连接网关管理、单聊、群聊、聊天记录查询、离线消息存储、消息推送、心跳、分布式唯一ID、红包、消息同步等功能,并且还支持集群部署。
13501 1
|
6月前
|
消息中间件 Oracle Dubbo
Netty 源码共读(一)如何阅读JDK下sun包的源码
Netty 源码共读(一)如何阅读JDK下sun包的源码
132 1