当前位置: 首页 > 后端技术 > Java

Netty系列:ChannelHandlerContext详解

时间:2023-04-01 16:18:39 Java

介绍我们知道ChannelHandler有两个非常重要的子接口,分别是ChannelOutboundHandler和ChannelInboundHandler。基本上,这两个处理程序接口定义了所有通道入站和出站的处理逻辑。不管是ChannelHandler还是ChannelOutboundHandler和ChannelInboundHandler,几乎都有一个ChannelHandlerContext参数,那么这个ChannelHandlerContext是干什么用的呢?它与处理程序和通道有什么关系?熟悉ChannelHandlerContext及其应用的朋友应该对ChannelHandlerContext并不陌生。如果没有,这里是一个简单处理程序的示例:);log.info("接受通道父级:{}",ctx.channel().parent());//通道激活ctx.write("通道激活状态!\r\n");ctx.flush();}}这里的handler继承了SimpleChannelInboundHandler,只需要实现相应的方法即可。这里实现的是channelActive方法。在channelActive方法中,传入了一个ChannelHandlerContext参数,我们可以通过ChannelHandlerContext来调用它的一些方法。首先看ChannelHandlerContext的定义:publicinterfaceChannelHandlerContextextendsAttributeMap,ChannelInboundInvoker,ChannelOutboundInvoker{首先,ChannelHandlerContext是一个AttributeMap,可以用来存储多种数据。然后ChannelHandlerContext继承了ChannelInboundInvoker和ChannelOutboundInvoker,可以触发inbound和outbound的一些方法。ChannelHandlerContext除了继承一些方法外,还可以作为channel、handler和pipeline之间的沟通桥梁,因为对应的channel、handler和pipeline可以从ChannelHandlerContext获取:Channelchannel();ChannelHandlerhandler();ChannelPipelinepipeline();另请注意,ChannelHandlerContext还返回一个EventExecutor来执行特定任务:EventExecutorexecutor();接下来我们看一下ChannelHandlerContext的实现。AbstractChannelHandlerContextAbstractChannelHandlerContext是ChannelHandlerContext的一个非常重要的实现。AbstractChannelHandlerContext虽然是一个抽象类,但是基本上实现了ChannelHandlerContext的所有功能。首先看AbstractChannelHandlerContext的定义:抽象类AbstractChannelHandlerContext实现了ChannelHandlerContext,ResourceLeakHintAbstractChannelHandlerContext是ChannelHandlerContext的具体实现。一般来说,一个handler对应一个ChannelHandlerContext,但是一个程序中的handler可能不止一个,那么如何获取一个handler中的其他handler呢?在AbstractChannelHandlerContext中,有两个next和prev也是AbstractChannelHandlerContext类型,这样多个AbstractChannelHandlerContext就可以构建一个双向链表。这样就可以在一个ChannelHandlerContext中得到其他的ChannelHandlerContext,从而得到handler处理链。volatileAbstractChannelHandlerContext接下来;volatileAbstractChannelHandlerContextprev;AbstractChannelHandlerContext中的管道和执行器都是通过构造函数传入的:姓名”);this.pipeline=管道;this.executor=执行者;this.executionMask=mask(handlerClass);//如果它由EventLoop驱动或给定的Executor是OrderedEventExecutor的实例,则它是有序的。ordered=executor==null||OrderedEventExecutor的执行器实例;}有的朋友可能会有疑问,如何获取ChannelHandlerContext中的channel和handler?对于通道,通过管道获取:publicChannelchannel(){returnpipeline.channel();对于handler,在AbstractChannelHandlerContext中没有实现,需要继承AbstractChannelHandlerContExt类实现对于EventExecutor,可以通过构造函数将一个新的EventExecutor传递给AbstractChannelHandlerContext。如果没有传入或者传入为空,就会使用通道自带的EventLoop:publicEventExecutorexecutor(){if(executor==null){returnchannel().eventLoop();}else{返回执行者;因为EventLoop继承自OrderedEventExecutor,所以它也是一个EventExecutor。EventExecutor主要用于异步提交任务执行。实际上ChannelHandlerContext中ChannelInboundInvoker和ChannelOutboundInvoker的方法几乎都是通过EventExecutor执行的。对于ChannelInboundInvoker来说,我们以方法fireChannelRegistered为例:归还这个;}staticvoidinvokeChannelRegistered(finalAbstractChannelHandlerContextnext){EventExecutorexecutor=next.executor();如果(executor.inEventLoop()){next.invokeChannelRegistered();}else{executor.execute(newRunnable(){@Overridepublicvoidrun(){next.invokeChannelRegistered();}});fireChannelRegistered调用invokeChannelRegistered方法,invokeChannelRegistered调用EventExecutor的execute方法,将真正的调用逻辑封装在一个runnable类中执行。注意在调用executor.execute方法之前有判断executor是否在eventLoop中。如果执行者已经在eventLoop中,那么任务可以直接执行,无需启用新的线程。对于ChannelOutboundInvoker,我们以bind方法为例,看看EventExecutor是如何使用的:if(isNotValidPromise(promise,false)){//取消returnpromise;}finalAbstractChannelHandlerContextnext=findContextOutbound(MASK_BIND);EventExecutor执行器=next.executor();如果(executor.inEventLoop()){next.invokeBind(localAddress,promise);}else{safeExecute(executor,newRunnable(){@Overridepublicvoidrun(){next.invokeBind(localAddress,promise);}},promise,null,false);}返回承诺;可以看到执行逻辑和invokeChannelRegistered方法很相似,也是先判断executor是否在eventLoop中,如果在,则直接执行,如果不在,则在executor中执行。上面两个例子中,调用了next对应的方法,即next.invokeChannelRegistered和next.invokeBind。我们知道ChannelHandlerContext只是一个封装,本身并没有太多的业务逻辑,所以next调用的对应方法其实就是封装在Context中的ChannelInboundHandler和ChannelOutboundHandler中的业务逻辑,如下:privatevoidinvokeUserEventTriggered(Objectevent){if(invokeHandler()){try{((ChannelInboundHandler)handler()).userEventTriggered(这个,事件);}catch(Throwablet){invokeExceptionCaught(t);}}else{fireUserEventTriggered(事件);}}privatevoidinvokeBind(SocketAddresslocalAddress,ChannelPromisepromise){if(invokeHandler()){try{((ChannelOutboundHandler)handler()).bind(this,localAddress,promise);}catch(Throwablet){notifyOutboundHandlerException(t,promise);}}else{绑定(本地地址,承诺);因此,从AbstractChannelHandlerContext我们可以知道,ChannelHandlerContext接口中定义的方法是被调用的handler中的具体实现,Context只是对handlerDefaultChannelHandlerContext的封装DefaultChannelHandlerContext是AbstractChannelHandlerContext的具体实现。我们在讲解AbstractChannelHandlerContext的时候提到过,AbstractChannelHandlerContext并没有定义具体的handler实现,而是这个实现是在DefaultChannelHandlerContext中进行的。DefaultChannelHandlerContext很简单,我们看一下它的具体实现:finalclassDefaultChannelHandlerContextextendsAbstractChannelHandlerContext{privatefinalChannelHandlerhandler;DefaultChannelHandlerContext(DefaultChannelPipelinepipeline,EventExecutorexecutor,Stringname,ChannelHandlerhandler){super(pipeline,executor,name,handler.getClass());this.handler=处理程序;}@OverridepublicChannelHandlerhandler(){返回处理程序;}}DefaultChannelHandlerContext提供了一个额外的ChannelHandler属性来存储传入的ChannelHandler。此时,DefaultChannelHandlerContext可以在ChannelHandlerContext中传入所有必要的处理程序、通道、管道和EventExecutors。小结本节我们介绍了ChannelHandlerContext及其基本实现。我们了解到ChannelHandlerContext是对handler、channel和pipeline的封装。ChannelHandlerContext中的业务逻辑实际上调用了底层处理程序的相应方法。这也是我们需要在自定义处理程序中实现的方法。本文已收录于http://www.flydean.com/04-4-netty-channelhandlercontext/最流行的解读,最深刻的干货,最简洁的教程,很多你不知道的小技巧等着你等你发现!欢迎关注我的公众号:《程序那些事儿》,懂技术,更懂你!