简介
咱们晓得ChannelHandler有两个十分重要的子接口,别离是ChannelOutboundHandler和ChannelInboundHandler,基本上这两个handler接口定义了所有channel inbound和outbound的解决逻辑。
不论是ChannelHandler还是ChannelOutboundHandler和ChannelInboundHandler,简直他们中所有的办法都带有一个ChannelHandlerContext参数,那么这个ChannelHandlerContext到底是做什么用的呢?它和handler、channel有什么关系呢?
ChannelHandlerContext和它的利用
相熟netty的敌人应该都接触过ChannelHandlerContext,如果没有的话,这里有一个简略的handler的例子:
public class ChatServerHandler extends SimpleChannelInboundHandler<String> { @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { log.info("accepted channel: {}", ctx.channel()); log.info("accepted channel parent: {}", ctx.channel().parent()); // channel沉闷 ctx.write("Channel Active状态!\r\n"); ctx.flush(); }}
这里的handler继承了SimpleChannelInboundHandler,只须要实现对应的办法即可。这里实现的是channelActive办法,在channelActive办法中,传入了一个ChannelHandlerContext参数,咱们能够通过应用ChannelHandlerContext来调用它的一些办法。
先来看一下ChannelHandlerContext的定义:
public interface ChannelHandlerContext extends AttributeMap, ChannelInboundInvoker, ChannelOutboundInvoker {
首先ChannelHandlerContext是一个AttributeMap,能够用来存储多个数据。
而后ChannelHandlerContext继承了ChannelInboundInvoker和ChannelOutboundInvoker,能够触发inbound和outbound的一些办法。
除了继承来的一些办法之外,ChannelHandlerContext还能够作为channel,handler和pipline的沟通桥梁,因为能够从ChannelHandlerContext中获取到对应的channel,handler和pipline:
Channel channel();ChannelHandler handler();ChannelPipeline pipeline();
还要留神的是ChannelHandlerContext还返回一个EventExecutor,用来执行特定的工作:
EventExecutor executor();
接下来,咱们具体看一下ChannelHandlerContext的实现。
AbstractChannelHandlerContext
AbstractChannelHandlerContext是ChannelHandlerContext的一个十分重要的实现,尽管AbstractChannelHandlerContext是一个抽象类,然而它基本上实现了ChannelHandlerContext的所有性能。
首先看一下AbstractChannelHandlerContext的定义:
abstract class AbstractChannelHandlerContext implements ChannelHandlerContext, ResourceLeakHint
AbstractChannelHandlerContext是ChannelHandlerContext的一个具体实现。
通常来说一个handler对应一个ChannelHandlerContext,然而在一个程序中可能会有多于一个handler,那么如何在一个handler中获取其余的handler呢?
在AbstractChannelHandlerContext中有两个同样是AbstractChannelHandlerContext类型的next和prev,从而使得多个AbstractChannelHandlerContext能够构建一个双向链表。从而能够在一个ChannelHandlerContext中,获取其余的ChannelHandlerContext,从而取得handler解决链。
volatile AbstractChannelHandlerContext next; volatile AbstractChannelHandlerContext prev;
AbstractChannelHandlerContext中的pipeline和executor都是通过构造函数传入的:
AbstractChannelHandlerContext(DefaultChannelPipeline pipeline, EventExecutor executor, String name, Class<? extends ChannelHandler> handlerClass) { this.name = ObjectUtil.checkNotNull(name, "name"); this.pipeline = pipeline; this.executor = executor; this.executionMask = mask(handlerClass); // Its ordered if its driven by the EventLoop or the given Executor is an instanceof OrderedEventExecutor. ordered = executor == null || executor instanceof OrderedEventExecutor; }
可能有敌人会有疑难了,ChannelHandlerContext中的channel和handler是如何失去的呢?
对于channel来说,是通过pipeline来获取的:
public Channel channel() { return pipeline.channel(); }
对于handler来说,在AbstractChannelHandlerContext中并没有对其进行实现,须要在继承AbstractChannelHandlerContext的类中进行实现。
对于EventExecutor来说,能够通过构造函数向AbstractChannelHandlerContext传入一个新的EventExecutor,如果没有传入或者传入为空的话,则会应用channel中自带的EventLoop:
public EventExecutor executor() { if (executor == null) { return channel().eventLoop(); } else { return executor; } }
因为EventLoop继承自OrderedEventExecutor,所以它也是一个EventExecutor。
EventExecutor次要用来异步提交工作来执行,事实上ChannelHandlerContext中简直所有来自于ChannelInboundInvoker和ChannelOutboundInvoker的办法都是通过EventExecutor来执行的。
对于ChannelInboundInvoker来说,咱们以办法fireChannelRegistered为例:
public ChannelHandlerContext fireChannelRegistered() { invokeChannelRegistered(findContextInbound(MASK_CHANNEL_REGISTERED)); return this; } static void invokeChannelRegistered(final AbstractChannelHandlerContext next) { EventExecutor executor = next.executor(); if (executor.inEventLoop()) { next.invokeChannelRegistered(); } else { executor.execute(new Runnable() { @Override public void run() { next.invokeChannelRegistered(); } }); } }
fireChannelRegistered调用了invokeChannelRegistered办法,invokeChannelRegistered则调用EventExecutor的execute办法,将实在的调用逻辑封装在一个runnable类中执行。
留神,在调用executor.execute办法之前有一个executor是否在eventLoop中的判断。如果executor曾经在eventLoop中了,那么间接执行工作即可,不须要启用新的线程。
对于ChannelOutboundInvoker来说,咱们以bind办法为例,看一下EventExecutor是怎么应用的:
public ChannelFuture bind(final SocketAddress localAddress, final ChannelPromise promise) { ObjectUtil.checkNotNull(localAddress, "localAddress"); if (isNotValidPromise(promise, false)) { // cancelled return promise; } final AbstractChannelHandlerContext next = findContextOutbound(MASK_BIND); EventExecutor executor = next.executor(); if (executor.inEventLoop()) { next.invokeBind(localAddress, promise); } else { safeExecute(executor, new Runnable() { @Override public void run() { next.invokeBind(localAddress, promise); } }, promise, null, false); } return promise; }
能够看到执行的逻辑和invokeChannelRegistered办法很相似,也是先判断executor在不在eventLoop中,如果在的话间接执行,如果不在则放在executor中执行。
下面的两个例子中都调用了next的相应办法,别离是next.invokeChannelRegistered和next.invokeBind。
咱们晓得ChannelHandlerContext只是一个封装,它自身并没有太多的业务逻辑,所以next调用的相应办法,实际上是Context中封装的ChannelInboundHandler和ChannelOutboundHandler中的业务逻辑,如下所示:
private void invokeUserEventTriggered(Object event) { if (invokeHandler()) { try { ((ChannelInboundHandler) handler()).userEventTriggered(this, event); } catch (Throwable t) { invokeExceptionCaught(t); } } else { fireUserEventTriggered(event); } }
private void invokeBind(SocketAddress localAddress, ChannelPromise promise) { if (invokeHandler()) { try { ((ChannelOutboundHandler) handler()).bind(this, localAddress, promise); } catch (Throwable t) { notifyOutboundHandlerException(t, promise); } } else { bind(localAddress, promise); } }
所以,从AbstractChannelHandlerContext能够得悉,ChannelHandlerContext接口中定义的办法都是调用的handler中具体的实现,Context只是对handler的封装。
DefaultChannelHandlerContext
DefaultChannelHandlerContext是AbstractChannelHandlerContext的一个具体实现。
咱们在解说AbstractChannelHandlerContext的时候提到过,AbstractChannelHandlerContext中并没有定义具体的handler的实现,而这个实现是在DefaultChannelHandlerContext中进行的。
DefaultChannelHandlerContext很简略,咱们看一下它的具体实现:
final class DefaultChannelHandlerContext extends AbstractChannelHandlerContext { private final ChannelHandler handler; DefaultChannelHandlerContext( DefaultChannelPipeline pipeline, EventExecutor executor, String name, ChannelHandler handler) { super(pipeline, executor, name, handler.getClass()); this.handler = handler; } @Override public ChannelHandler handler() { return handler; }}
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/
最艰深的解读,最粗浅的干货,最简洁的教程,泛滥你不晓得的小技巧等你来发现!
欢送关注我的公众号:「程序那些事」,懂技术,更懂你!