絮叨
为了学习Netty,我们前面铺垫了那么多,NIO Java的零拷贝,UNIX的I/O模型等等。 下面是前面系列的链接
- 小六六学Netty系列之Java BIO
- 小六六学Netty系列之Java NIO(一)
- 小六六学Netty系列之Java NIO(二)
- 小六六学Netty系列之unix IO模型
- 小六六学Netty系列之Java 零拷贝
- 小六六学Netty系列之初识Netty
- 小六六学Netty系列之再遇Netty
- 小六六学Netty系列之Netty群聊
虽然说不至于学会了很多的东西,但是一遍走下来,至少对Netty 有了一点基本的认识,我相信如果你要再去使用它,或者深入它,这些基础肯定是有用的,我们继续加油。
Google Protobuf
编码和解码的基本介绍
- 编写网络应用程序时,因为数据在网络中传输的都是二进制字节码数据,在发送数据时就需要编码,接收数据时就需要解码
- codec(编解码器) 的组成部分有两个:decoder(解码器)和 encoder(编码器)。encoder 负责把业务数据转换成字节码数据,decoder 负责把字节码数据转换成业务数据
Netty 本身的编码解码的机制和问题分析
- Netty 自身提供了一些 codec(编解码器)
- Netty 提供的编码器
- StringEncoder,对字符串数据进行编码
- ObjectEncoder,对 Java 对象进行编码
- Netty 提供的解码器
- StringDecoder, 对字符串数据进行解码
- ObjectDecoder,对 Java 对象进行解码
- Netty 本身自带的 ObjectDecoder 和 ObjectEncoder 可以用来实现 POJO 对象或各种业务对象的编码和解码,底层使用的仍是 Java 序列化技术 , 而Java 序列化技术本身效率就不高,存在如下问题
- 无法跨语言
- 序列化后的体积太大,是二进制编码的 5 倍多。
- 序列化性能太低
=> 引出 新的解决方案 [Google 的 Protobuf]
Java序列化
这个知识点比较重要,所以小六六重新整理了一篇文章给大家哦
Protobuf
Protobuf基本介绍
- Protobuf 是 Google 发布的开源项目,全称 Google Protocol Buffers,是一种轻便高效的结构化数据存储格式,可以用于结构化数据串行化,或者说序列化。它很适合做数据存储或 RPC[远程过程调用 remote procedure call ] 数据交换格式 。目前很多公司 http+json 转成 tcp+protobuf
- 参考文档 : developers.google.com/protocol-bu… 语言指南
- Protobuf 是以 message 的方式来管理数据的
支持跨平台、跨语言,即[客户端和服务器端可以是不同的语言编写的] (支持目前绝大多数语言,例如 C++、C#、Java、python 等)
- 高性能,高可靠性
- 使用 protobuf 编译器能自动生成代码,Protobuf 是将类的定义使用.proto 文件进行描述。说明,在idea 中编写 .proto 文件时,会自动提示是否下载 .ptotot 编写插件. 可以让语法高亮。
- 然后通过 protoc.exe 编译器根据.proto 自动生成.java 文件
- protobuf 使用示意图
网络异常,图片无法展示
|
具体使用
小六六这边就不一一举例说明了,毕竟我也是第一次接触嘛,先了解了解,后面用到了就去找案例就好了,稍微说一下
先写一个这样的执行文件
网络异常,图片无法展示
|
然后通过它的编译器生成你需要的实体
网络异常,图片无法展示
|
然后再Netty中使用
网络异常,图片无法展示
|
大概就是这么个流程,其实我们只要知道我们的目的是什么了就好了,我们就是加快序列化的速度嘛
Netty编解码器和handler的调用机制
基本说明
- netty的组件设计:Netty的主要组件有Channel、EventLoop、ChannelFuture、ChannelHandler、ChannelPipe等
- ChannelHandler充当了处理入站和出站数据的应用程序逻辑的容器。例如,实现ChannelInboundHandler接口(或ChannelInboundHandlerAdapter),你就可以接收入站事件和数据,这些数据会被业务逻辑处理。当要给客户端发送响应时,也可以从ChannelInboundHandler冲刷数据。业务逻辑通常写在一个或者多个ChannelInboundHandler中。ChannelOutboundHandler原理一样,只不过它是用来处理出站数据的
- ChannelPipeline提供了ChannelHandler链的容器。以客户端应用程序为例,如果事件的运动方向是从客户端到服务端的,那么我们称这些事件为出站的,即客户端发送给服务端的数据会通过pipeline中的一系列ChannelOutboundHandler,并被这些Handler处理,反之则称为入站的
网络异常,图片无法展示
|
再讲编码解码器
- 当Netty发送或者接受一个消息的时候,就将会发生一次数据转换。入站消息会被解码:从字节转换为另一种格式(比如java对象);如果是出站消息,它会被编码成字节。
- Netty提供一系列实用的编解码器,他们都实现了ChannelInboundHadnler或者ChannelOutboundHandler接口。在这些类中,channelRead方法已经被重写了。以入站为例,对于每个从入站Channel读取的消息,这个方法会被调用。随后,它将调用由解码器所提供的decode()方法进行解码,并将已经解码的字节转发给ChannelPipeline中的下一个ChannelInboundHandler。
解码器-ByteToMessageDecoder
- 关系继承图
网络异常,图片无法展示
|
我们可以看到还是继承了ChannelInboundHandler
- 由于不可能知道远程节点是否会一次性发送一个完整的信息,tcp有可能出现粘包拆包的问题,这个类会对入站数据进行缓冲,直到它准备好被处理.
- 一个关于ByteToMessageDecoder实例分析
当服务端读取客户端传过来的数据的时候,第一步就是要解码
public class ToIntegerDecoder extends ByteToMessageDecoder { @Override protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception { if (in.readableBytes() >= 4) { out.add(in.readInt()); } } } 复制代码
网络异常,图片无法展示
|
Netty的handler链的调用机制
实例要求:
- 使用自定义的编码器和解码器来说明Netty的handler 调用机制
- 客户端发送long -> 服务器
- 服务端发送long -> 客户端
其实小六六主要想说的还是handler的一个调用链机制,一个双向链表,然后一个个handler去处理
网络异常,图片无法展示
|
来看看下面这个案例,按照上面的,是从客户端发送数据到服务端,然后服务端返回数据给客户端,所以我们先写客户端
- MyClient
package com.xiaoliuliu.netty.hander; import io.netty.bootstrap.Bootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioSocketChannel; /** * @author 小六六 * @version 1.0 * @date 2020/9/1 21:20 */ public class MyClient { public static void main(String[] args) throws Exception { EventLoopGroup group = new NioEventLoopGroup(); try { Bootstrap bootstrap = new Bootstrap(); bootstrap.group(group).channel(NioSocketChannel.class) .handler(new MyClientInitializer()); //自定义一个初始化类 ChannelFuture channelFuture = bootstrap.connect("localhost", 7000).sync(); channelFuture.channel().closeFuture().sync(); } finally { group.shutdownGracefully(); } } } 复制代码
- MyClientInitializer
package com.xiaoliuliu.netty.hander; import com.atguigu.netty.inboundhandlerandoutboundhandler.MyByteToLongDecoder2; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPipeline; import io.netty.channel.socket.SocketChannel; /** * @author 小六六 * @version 1.0 * @date 2020/9/1 21:20 */ public class MyClientInitializer extends ChannelInitializer<SocketChannel> { @Override protected void initChannel(SocketChannel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); //加入一个出站的handler 对数据进行一个编码 pipeline.addLast(new MyLongToByteEncoder()); //这时一个入站的解码器(入站handler ) //pipeline.addLast(new MyByteToLongDecoder()); pipeline.addLast(new MyByteToLongDecoder2()); //加入一个自定义的handler , 处理业务 pipeline.addLast(new MyClientHandler()); } } 复制代码