Netty源码分析-终结篇

1.Promise 与 Future

https://docs.scala-lang.org/zh-cn/overviews/core/futures.html

2.Handler的各种姿势

2.1.ChannelHandlerContext

每个ChannelHandler被添加到ChannelPipeline后,都会创建一个ChannelHandlerContext并
与之创建的ChannelHandler关联绑定。ChannelHandlerContext允许Channelllandler与其他的
ChannelHandler实现进行交互。ChannelHandlerContext不会改变添加到其中的ChannelHandler,因此它是安全的。
下图显示了ChannelHandlerContext、ChannelHandler、ChannelPipeline的关系:

2.2.Channel的状态模型

Netty有一个简单但强大的状态横型,并完美映射到ChannelInboundHandler的各个方法。下面
是Channel生命周期四个不同的状态:
1.channelUnregistered
2.channe1Registered
3.channelActive
Channel的状态在其生命周期中变化,因为状态变化需要触发,下面显示了Channel状态变化

image.png | left | 551x273

2.3.ChannelHandler和其他子类

Handler的类继承图

image.png | left | 608x326

2.4.ChannelHandler中的方法

Netty 定义了良好的类型层次结构来表示不同的处理程序类型,所有的类型的父类是 Channe1Handler 。 Channe1Handler提供了在其生命周期内添加或从 ChannelPipeline 中删除的方法。
1 . handlerAdded , Channe1Handler添加实际上下文中准备处理事件
2 . handlerRemoved ,将 Channe1Handler 从实际上下文中删除,不再处理事件
3 . exceptionCaught ,处理抛出的异常

Netty 还提供了一个实现了 Channe1Handler 的抽象类 Channe1HandlerAdapter 。 Channe1HandlerAdapter 实现了父类的所有方法,基本上就是传递事件到 ChannelPipeline 中的下一个 Channe1Handler直到结束。我们也可以直接继承于Channe1HandlerAdapter,然后重写里面的方法。

2.5.ChannelInboundHandler

ChannelInboundHandler 提供了一些方法再接收数据或Channel状态改变时被调用。下面是ChannelInboundHandler 的一些方法:

1.channelRegistered,ChannelHandlerContext的Channel被注册到 EventLoop;
2.channelUnregistered,ChannelHandlerContext 的Channel从EventLoop中注销
3.channelActive,ChannelHandlerContext 的Channel 已激活
4.channelInactive,ChannelHandlerContext 的 Channel 结束生命周期
5.channelRead,从当前Channel的对端读取消息
6.0hanne1ReadComplete,消息读取完成后执行
7.userEventTriggered,一个用户事件被触发
8.channelWritabilityChanged,改变通道的可写状态,可以使用Channel.isWritable()检查
9.exceptionCaught,重写父类ChannelHandler的方法,处理异常

Netty提供了一个实现了ChannelInboundHandler接口并继承ChannelHandlerAdapter的类:
ChannelInboundHandlerAdapter 。ChannelInboundHandlerAdapter实现了
ChannelInboundHandler的所有方法,作用就是处理消息并将消息转发到ChannelPipeline中的
下一个,ChannelHandler。ChannelInboundHandlerAdapter的channelRead方法处理完消息后不
会自动释放消息,若想自动释放收到的消息,可以使用SimpleChannelInboundHandler。

扫描二维码关注公众号,回复: 3223568 查看本文章

3.编码和解码

3.1.TCP粘包/拆包

TCP是一个“流”协议,所谓流,就是没有界限的一长串二进制数据。TCP作为传输层协议并不了解上层业务数据的具体含义,它会根据TCP缓冲区的实际情况进行数据包的划分,所以在业
务上认为是一个完整的包,可能会被TCP拆分成多个包进行发送,也有可能把多个小的包到装成
一个大的数据包发这,这就是所谓的TCP粘包和拆包问题

3.2.粘包问题的解决策略

由于底层的TCP无法理解上层的业务数据,所以在底层是无法保证数据包不被拆分和重组的,这
个问题只能通过上层的应用协议栈设计来解决。业界的主流协议的解决方案,可以归纳如下:

1.消息定长,报文大小固定长度,例如毎个报文的长度固定为200字节,如果不够空位补空格;
2.包尾添加特殊分隔符,例如毎条报文结朿都添加回车换行符(例如FTP协议)或者指定特殊
字符作为报文分隔符,接收方通过特殊分隔符切分报文区分;
3.将消息分为消息头和消息体,消息头中包含表示信患的总长度(或者消息体长度)的字段;
4.更复杂的自定义应用层协议。

3.3.编码解码技术

通常我们也习惯将編码(Encode〉称为序列化(serialization),它将对象序列化为字节数组,
用于网络传输.数据持久化或者其它用途。
反之,解码(Decode)/反序列化(deserialization)把从网络、磁盘等读取的字节数组还原成
原始对象(通常是原j始对象的烤贝),以方便后续的业务逻辑操作。
逬行远程跨逬程服务调用时(例如狀(:调用),需要使用特定的编解码技术.对需要逬行网络传
输的对象做編码或者解码,以便完成远程调用。

3.4.Netty为什么要提供编解码框架

作为一个高性能的异步、NI0通信框架,編解码框架是Netty的重要组成部分。尽管站在微内核
的角度看,编解码框架丼不是Netty微内核的组成部分,但是通过Channelllandler定制扩展出的編解码框架却是不可或缺的。

然而,我们已经知道在Netty中,从网络读取的Inbound消息.需.要经过解码,将二进制的数据
报转換成应用层协议消息或者业务消息,才能够被上层的应用逻辑识别和处理:同理,用户发送
到网络的Outbound业务消息,索要经过编码转换成二进制字节数组(对于Netty就是ByteBuf)
才能够发送到网络对端。編码和解码功能是NIO框架的有机租成部分,无论是由业务定制扩展实
现,还是NIO框茉内I编解码能力,该功能是必不可少的。

为了降低用户的开发难度,Netty对常用的功能和API做了装饰,以屏蔽底层的实现细节。编解
码功能的定制,对于熟悉Netty底层实现的开发者而言,直接基于ChannelHandler扩展开发,
难度并不是很大,但是对于大多数初学者或不愿意去了解底层实现细节的用户.需要提供给他
们更简单的类库和API,而不是ChannelHandler
Netty在这方面做得非常出色,针对編解码功能,它既提供了通用的編解码框架供用户扩展,又
提供了常用的编解码类库供用户直接使用。在保证定制扩展性的基础之上,尽景降低用户的开发
工作量和开发门槛,提升开发效率。Netty预置的编解码功能列表如下:base64、Protobuf、spdy等。

image.png | left | 148x246

3.5.Netty粘包和拆包解决方案

3.5.1.Netty中常用的解码器 链接

Netty提供了多个解码器,可以进行分包的操作,分别是:
* LineBasedFrameDecoder
* DelimiterBasedFrameDecoder(添加特殊分隔符报文来分包)
* FixedLengthFrameDecoder(使用定长的报文来分包)
* LengthFieldBasedFrameDecoder

LineBasedFrameDecoder解码器

LineBasedFrameDecoder是回车换行解码器,如果用户发送的消息以回车换行符作为消息结束的标识,则可以直接使用Netty的LineBasedFrameDecoder对消息进行解码,只需要在初始化Netty服务端或者客户端时将LineBasedFrameDecoder正确的添加到ChannelPipeline中即可,不需要自己重新实现一套换行解码器。

DelimiterBasedFrameDecoder解码器

DelimiterBasedFrameDecoder是分隔符解码器,用户可以指定消息结束的分隔符,它可以自动完成以分隔符作为码流结束标识的消息的解码。回车换行解码器实际上是一种特殊的DelimiterBasedFrameDecoder解码器。

FixedLengthFrameDecoder解码器

FixedLengthFrameDecoder是固定长度解码器,它能够按照指定的长度对消息进行自动解码,开发者不需要考虑TCP的粘包/拆包等问题,非常实用。
对于定长消息,如果消息实际长度小于定长,则往往会进行补位操作,它在一定程度上导致了空间和资源的浪费。但是它的优点也是非常明显的,编解码比较简单,因此在实际项目中仍然有一定的应用场景。

LengthFieldBasedFrameDecoder解码器

大多数的协议(私有或者公有),协议头中会携带长度字段,用于标识消息体或者整包消息的长度,例如SMPP、HTTP协议等。由于基于长度解码需求的通用性,以及为了降低用户的协议开发难度,Netty提供了LengthFieldBasedFrameDecoder,自动屏蔽TCP底层的拆包和粘包问题,只需要传入正确的参数,即可轻松解决“读半包“问题。

猜你喜欢

转载自blog.csdn.net/CharJay_Lin/article/details/82726400
今日推荐