netty 系列之:channelHandlerContext 详解
简介
我们知道 ChannelHandler 有两个非常重要的子接口,分别是 ChannelOutboundHandler 和 ChannelInboundHandler,基本上这两个 handler 接口定义了所有 channel inbound 和 outbound 的处理逻辑。
不管是 ChannelHandler 还是 ChannelOutboundHandler 和 ChannelInboundHandler,几乎他们中所有的方法都带有一个 ChannelHandlerContext 参数,那么这个 ChannelHandlerContext 到底是做什么用的呢?它和 handler、channel 有什么关系呢?
ChannelHandlerContext 和它的应用
熟悉 netty 的朋友应该都接触过 ChannelHandlerContext,如果没有的话,这里有一个简单的 handler 的例子:
这里的 handler 继承了 SimpleChannelInboundHandler,只需要实现对应的方法即可。这里实现的是 channelActive 方法,在 channelActive 方法中,传入了一个 ChannelHandlerContext 参数,我们可以通过使用 ChannelHandlerContext 来调用它的一些方法。
先来看一下 ChannelHandlerContext 的定义:
首先 ChannelHandlerContext 是一个 AttributeMap,可以用来存储多个数据。
然后 ChannelHandlerContext 继承了 ChannelInboundInvoker 和 ChannelOutboundInvoker,可以触发 inbound 和 outbound 的一些方法。
除了继承来的一些方法之外,ChannelHandlerContext 还可以作为 channel,handler 和 pipline 的沟通桥梁,因为可以从 ChannelHandlerContext 中获取到对应的 channel,handler 和 pipline:
还要注意的是 ChannelHandlerContext 还返回一个 EventExecutor,用来执行特定的任务:
接下来,我们具体看一下 ChannelHandlerContext 的实现。
AbstractChannelHandlerContext
AbstractChannelHandlerContext 是 ChannelHandlerContext 的一个非常重要的实现,虽然 AbstractChannelHandlerContext 是一个抽象类,但是它基本上实现了 ChannelHandlerContext 的所有功能。
首先看一下 AbstractChannelHandlerContext 的定义:
AbstractChannelHandlerContext 是 ChannelHandlerContext 的一个具体实现。
通常来说一个 handler 对应一个 ChannelHandlerContext,但是在一个程序中可能会有多于一个 handler,那么如何在一个 handler 中获取其他的 handler 呢?
在 AbstractChannelHandlerContext 中有两个同样是 AbstractChannelHandlerContext 类型的 next 和 prev,从而使得多个 AbstractChannelHandlerContext 可以构建一个双向链表。从而可以在一个 ChannelHandlerContext 中,获取其他的 ChannelHandlerContext,从而获得 handler 处理链。
AbstractChannelHandlerContext 中的 pipeline 和 executor 都是通过构造函数传入的:
可能有朋友会有疑问了,ChannelHandlerContext 中的 channel 和 handler 是如何得到的呢?
对于 channel 来说,是通过 pipeline 来获取的:
对于 handler 来说,在 AbstractChannelHandlerContext 中并没有对其进行实现,需要在继承 AbstractChannelHandlerContext 的类中进行实现。
对于 EventExecutor 来说,可以通过构造函数向 AbstractChannelHandlerContext 传入一个新的 EventExecutor,如果没有传入或者传入为空的话,则会使用 channel 中自带的 EventLoop:
因为 EventLoop 继承自 OrderedEventExecutor,所以它也是一个 EventExecutor。
EventExecutor 主要用来异步提交任务来执行,事实上 ChannelHandlerContext 中几乎所有来自于 ChannelInboundInvoker 和 ChannelOutboundInvoker 的方法都是通过 EventExecutor 来执行的。
对于 ChannelInboundInvoker 来说,我们以方法 fireChannelRegistered 为例:
fireChannelRegistered 调用了 invokeChannelRegistered 方法,invokeChannelRegistered 则调用 EventExecutor 的 execute 方法,将真实的调用逻辑封装在一个 runnable 类中执行。
注意,在调用 executor.execute 方法之前有一个 executor 是否在 eventLoop 中的判断。如果 executor 已经在 eventLoop 中了,那么直接执行任务即可,不需要启用新的线程。
对于 ChannelOutboundInvoker 来说,我们以 bind 方法为例,看一下 EventExecutor 是怎么使用的:
可以看到执行的逻辑和 invokeChannelRegistered 方法很类似,也是先判断 executor 在不在 eventLoop 中,如果在的话直接执行,如果不在则放在 executor 中执行。
上面的两个例子中都调用了 next 的相应方法,分别是 next.invokeChannelRegistered 和 next.invokeBind。
我们知道 ChannelHandlerContext 只是一个封装,它本身并没有太多的业务逻辑,所以 next 调用的相应方法,实际上是 Context 中封装的 ChannelInboundHandler 和 ChannelOutboundHandler 中的业务逻辑,如下所示:
所以,从 AbstractChannelHandlerContext 可以得知,ChannelHandlerContext 接口中定义的方法都是调用的 handler 中具体的实现,Context 只是对 handler 的封装。
DefaultChannelHandlerContext
DefaultChannelHandlerContext 是 AbstractChannelHandlerContext 的一个具体实现。
我们在讲解 AbstractChannelHandlerContext 的时候提到过,AbstractChannelHandlerContext 中并没有定义具体的 handler 的实现,而这个实现是在 DefaultChannelHandlerContext 中进行的。
DefaultChannelHandlerContext 很简单,我们看一下它的具体实现:
DefaultChannelHandlerContext 中额外提供了一个 ChannelHandler 属性,用来存储传入的 ChannelHandler。
到此 DefaultChannelHandlerContext 可以传入 ChannelHandlerContext 中一切必须的 handler,channel,pipeline 和 EventExecutor。
总结
本节我们介绍了 ChannelHandlerContext 和它的几个基本实现,了解到了 ChannelHandlerContext 是对 handler,channel 和 pipline 的封装,ChannelHandlerContext 中的业务逻辑,实际上是调用的是底层的 handler 的对应方法。这也是我们在自定义 handler 中需要实现的方法。
本文已收录于 http://www.flydean.com/04-4-netty-channelhandlercontext/
最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧等你来发现!
欢迎关注我的公众号:「程序那些事」,懂技术,更懂你!
版权声明: 本文为 InfoQ 作者【程序那些事】的原创文章。
原文链接:【http://xie.infoq.cn/article/bb4a9fc63849ba43938b4f704】。文章转载请联系作者。
评论