小标
2018-12-24
来源 :
阅读 1250
评论 0
摘要:本文主要向大家介绍了【云计算】Netty学习笔记之Channel,通过具体的内容向大家展现,希望对大家学习云计算有所帮助。
本文主要向大家介绍了【云计算】Netty学习笔记之Channel,通过具体的内容向大家展现,希望对大家学习云计算有所帮助。
前言
ChannelHandler是netty中的核心处理部分,我们使用netty的绝大部分代码都写在这部分,所以了解它的一些机制和特性是很有必要的
Channel
Channel接口抽象了底层socket的一些状态属性以及调用方法
image
针对不同类型的socket提供不同的子类实现。
image
Channel生命周期
image
ChannelHandler
ChannelHandler用于处理Channel对应的事件
ChannelHandler接口里面只定义了三个生命周期方法,我们主要实现它的子接口ChannelInboundHandler和ChannelOutboundHandler,为了便利,框架提供了ChannelInboundHandlerAdapter,ChannelOutboundHandlerAdapter和ChannelDuplexHandler这三个适配类,在使用的时候只需要实现你关注的方法即可
ChannelHandler生命周期方法
image
ChannelHandler里面定义三个生命周期方法,分别会在当前ChannelHander加入ChannelHandlerContext中,从ChannelHandlerContext中移除,以及ChannelHandler回调方法出现异常时被回调
ChannelInboundHandler
image
介绍一下这些回调方法被触发的时机
回调方法
触发时机
client
server
channelRegistered 当前channel注册到EventLoop true true
channelUnregistered 当前channel从EventLoop取消注册 true true
channelActive 当前channel激活的时候 true true
channelInactive 当前channel不活跃的时候,也就是当前channel到了它生命周期末 true true
channelRead 当前channel从远端读取到数据 true true
channelReadComplete channel read消费完读取的数据的时候被触发 true true
userEventTriggered 用户事件触发的时候
channelWritabilityChanged channel的写状态变化的时候触发
可以注意到每个方法都带了ChannelHandlerContext作为参数,具体作用是,在每个回调事件里面,处理完成之后,使用ChannelHandlerContext的fireChannelXXX方法来传递给下个ChannelHandler,netty的codec模块和业务处理代码分离就用到了这个链路处理
ChannelOutboundHandler
image.png
回调方法
触发时机
client
server
bind bind操作执行前触发 false true
connect connect 操作执行前触发 true false
disconnect disconnect 操作执行前触发 true false
close close操作执行前触发 false true
deregister deregister操作执行前触发
read read操作执行前触发 true true
write write操作执行前触发 true true
flush flush操作执行前触发 true true
注意到一些回调方法有ChannelPromise这个参数,我们可以调用它的addListener注册监听,当回调方法所对应的操作完成后,会触发这个监听
下面这个代码,会在写操作完成后触发,完成操作包括成功和失败
ChannelInboundHandler和ChannelOutboundHandler的区别
个人感觉in和out的区别主要在于ChannelInboundHandler的channelRead和channelReadComplete回调和ChannelOutboundHandler的write和flush回调上,ChannelOutboundHandler的channelRead回调负责执行入栈数据的decode逻辑,ChannelOutboundHandler的write负责执行出站数据的encode工作。其他回调方法和具体触发逻辑有关,和in与out无关。
ChannelHandlerContext
每个ChannelHandler通过add方法加入到ChannelPipeline中去的时候,会创建一个对应的ChannelHandlerContext,并且绑定,ChannelPipeline实际维护的是ChannelHandlerContext 的关系
在DefaultChannelPipeline源码中可以看到会保存第一个ChannelHandlerContext以及最后一个ChannelHandlerContext的引用
而在AbstractChannelHandlerContext源码中可以看到
volatile AbstractChannelHandlerContext next;volatile AbstractChannelHandlerContext prev;每个ChannelHandlerContext之间形成双向链表
ChannelPipeline
在Channel创建的时候,会同时创建ChannelPipeline
protected AbstractChannel(Channel parent) {在ChannelPipeline中也会持有Channel的引用
protected DefaultChannelPipeline newChannelPipeline() { return new DefaultChannelPipeline(this);ChannelPipeline会维护一个ChannelHandlerContext的双向链表
final AbstractChannelHandlerContext head;final AbstractChannelHandlerContext tail;链表的头尾有默认实现
protected DefaultChannelPipeline(Channel channel) { this.channel = ObjectUtil.checkNotNull(channel, "channel");我们添加的自定义ChannelHandler会插入到head和tail之间,如果是ChannelInboundHandler的回调,根据插入的顺序从左向右进行链式调用,ChannelOutboundHandler则相反
具体关系如下,但是下图没有把默认的head和tail画出来,这两个ChannelHandler做的工作相当重要
image
上面的整条链式的调用是通过Channel接口的方法直接触发的,如果使用ChannelContextHandler的接口方法间接触发,链路会从ChannelContextHandler对应的ChannelHandler开始,而不是从头或尾开始
HeadContext
HeadContext实现了ChannelOutboundHandler,ChannelInboundHandler这两个接口
class HeadContext extends AbstractChannelHandlerContext因为在头部,所以说HeadContext中关于in和out的回调方法都会触发
关于ChannelInboundHandler,HeadContext的作用是进行一些前置操作,以及把事件传递到下一个ChannelHandlerContext的ChannelInboundHandler中去
看下其中channelRegistered的实现
从语义上可以看出来在把这个事件传递给下一个ChannelHandler之前会回调ChannelHandler的handlerAdded方法
而有关ChannelOutboundHandler接口的实现,会在链路的最后执行,看下write方法的实现
这边的unsafe接口封装了底层Channel的调用,之所以取名为unsafe,是不需要用户手动去调用这些方法。这个和阻塞原语的unsafe不是同一个
也就是说,当我们通过Channel接口执行write之后,会执行ChannelOutboundHandler链式调用,在链尾的HeadContext ,在通过unsafe回到对应Channel做相关调用
从netty Channel接口的实现就能论证这个
TailContext
TailContext实现了ChannelInboundHandler接口,会在ChannelInboundHandler调用链最后执行,只要是对调用链完成处理的情况进行处理,看下channelRead实现
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {如果我们自定义的最后一个ChannelInboundHandler,也把处理操作交给下一个ChannelHandler,那么就会到TailContext,在TailContext会提供一些默认处理
protected void onUnhandledInboundMessage(Object msg) { try {比如channelRead中的onUnhandledInboundMessage方法,会把msg资源回收,防止内存泄露
强调一点的是,如果要执行整个链路,必须通过调用Channel方法触发,ChannelHandlerContext引用了ChannelPipeline,所以也能间接操作channel的方法,但是会从当前ChannelHandlerContext绑定的ChannelHandler作为起点开始,而不是ChannelHandlerContext的头和尾
这个特性在不需要调用整个链路的情况下可以使用,可以增加一些效率
上述组件之间的关系
image.png
每个Channel会绑定一个ChannelPipeline,ChannelPipeline中也会持有Channel的引用
ChannelPipeline持有ChannelHandlerContext链路,保留ChannelHandlerContext的头尾节点指针
每个ChannelHandlerContext会对应一个ChannelHandler,也就相当于ChannelPipeline持有ChannelHandler链路
ChannelHandlerContext同时也会持有ChannelPipeline引用,也就相当于持有Channel引用
ChannelHandler链路会根据Handler的类型,分为InBound和OutBound两条链路
本文由职坐标整理并发布,希望对同学们有所帮助。了解更多详情请关注职坐标大数据云计算大数据安全频道!
喜欢 | 0
不喜欢 | 0
您输入的评论内容中包含违禁敏感词
我知道了

请输入正确的手机号码
请输入正确的验证码
您今天的短信下发次数太多了,明天再试试吧!
我们会在第一时间安排职业规划师联系您!
您也可以联系我们的职业规划师咨询:
版权所有 职坐标-一站式AI+学习就业服务平台 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
沪公网安备 31011502005948号