构建Netty4通道的体系化思维,这些方法你应该关注
见字如面,我是威哥,一个从普通二本院校毕业,从未曾接触分布式、微服务、高并发到通过技术分享实现职场蜕变,成长为RocketMQ社区优秀布道师、大厂资深架构师,出版《RocketMQ技术内幕》一书,欢迎大家关注「中间件兴趣圈」,设置为星标,一起交流进步。关注「中间件兴趣圈」,回复RMQPDF即可获取两本电子书,浓缩了笔者千亿级消息流转经验。
Netty4 网络篇到目前为止即将进入收官之作,Netty4中关于事件传播机制、服务端、客户端启动流程、读、写事件的处理基本都介绍完了,本文将重点关注一下Netty 通道的其他核心API的实现逻辑,构建完备的网络通道知识体系,为深入理解NIO成为可能。
本文将以NioSocketChannel为例展示如下方法的实现细节。
- boolean isWritable()
- Channel read()
- ChannelFuture write(Object msg)
- ChannelFuture close()
1、boolean isWritable()
判断通道是否可写,其实现如下图所示:
在Netty中为了避免写缓存区无限制的写入导致内存溢出,引入了高低水位线机制,当积压数据超过设置的阔值后会设置为不可写,该方法就是判断通道是否可写。
当写缓存区中的容量超过写缓存区的高水位线时,默认为64K,则写缓存区则变为不可写,只有当写事件触发后,将写缓存区中的数据刷写到通道后,然后剩余待写字节数低于低水位线时,默认32K,则恢复可写状态。
2、Channel read()
这里看不是从网络中读取数据,而是注册读事件。
根据事件传播机制,outbound事件传播机制,最终的事件处理器为HeadContext。
我们再来复习一下AbstractChannel的内部类AbstractUnsafe的beginRead方法的实现细节。
针对NIO的注册事件,Netty给出了最佳实践:
- 首先要判断注册键(SelectionKey)是否有效。
- 然后通过注册键获取已注册的集合interstOps(按位表示)
- 通过位运算设置对应位的值,将表示读事件对应的位设置为1,表示注册读书事件。
3、ChannelFuture write(Object msg)
write是outbound事件,从tail开始传播,会经过用过定义的outbound事件处理器,例如编码器,然后最终抵达HeadContext,接下来我们重点关注HeadContext#write方法,最终会调用AbstractChannel的内部类AbstractUnsafe的write方法。
Channel的write方法,只是将数据写入到OutboundBuffer中,即放入到通道的写缓存区中。
4、ChannelFuture close()
调用该方法并不是关闭通道,而是获取一个Future,通常会注册回调事件,当通道真正被关闭时触发。
close事件类型为OutBound,从Tail开始最终会传播到HeadContext中。
其最最终具体实现在AbstractChannel的内部类AbstractUnsafe的close方法,接下来对其进行详细分析。
Step1:如果通道的待写缓存区为空,没有需要处理的数据,本次调用,只需增加监听器并tong;如果关闭已经完成,对本次调用,只需设置成功标识即可。
Step2:执行真正的关闭并传播相应的事件,其实现关键点:
- 调用doClose0关闭底层的网络通道
- 关闭通道的写缓存区
- 传播 channelInactive(非激活)事件与通道取消注册事件。
5、总结
本文主要的目的是让大家再次感受一下网络通道中其他方法的实现细节,再次体会事件传播机制,并了解各个方法的触发时机以及处理核心要点。
为了让大家在实战中学习Netty,笔者将RocketMQ的网络模块单独抽取成一个框架,可直接用于项目开发中,代码已上传github,欢迎获取
Netty项目实战代码仓库:https://github.com/dingwpmz/netty-learning
文章转载自公众号:中间件兴趣圈