netty系列之:channelHandlerContext详解
文章目錄
- 簡介
- ChannelHandlerContext和它的應用
- AbstractChannelHandlerContext
- DefaultChannelHandlerContext
- 總結(jié)
簡介
我們知道ChannelHandler有兩個非常重要的子接口,分別是ChannelOutboundHandler和ChannelInboundHandler,基本上這兩個handler接口定義了所有channel inbound和outbound的處理邏輯。
不管是ChannelHandler還是ChannelOutboundHandler和ChannelInboundHandler,幾乎他們中所有的方法都帶有一個ChannelHandlerContext參數(shù),那么這個ChannelHandlerContext到底是做什么用的呢?它和handler、channel有什么關(guān)系呢?
ChannelHandlerContext和它的應用
熟悉netty的朋友應該都接觸過ChannelHandlerContext,如果沒有的話,這里有一個簡單的handler的例子:
public class ChatServerHandler extends SimpleChannelInboundHandler<String> {@Overridepublic 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狀態(tài)!\r\n");ctx.flush();} }這里的handler繼承了SimpleChannelInboundHandler,只需要實現(xiàn)對應的方法即可。這里實現(xiàn)的是channelActive方法,在channelActive方法中,傳入了一個ChannelHandlerContext參數(shù),我們可以通過使用ChannelHandlerContext來調(diào)用它的一些方法。
先來看一下ChannelHandlerContext的定義:
public interface ChannelHandlerContext extends AttributeMap, ChannelInboundInvoker, ChannelOutboundInvoker {首先ChannelHandlerContext是一個AttributeMap,可以用來存儲多個數(shù)據(jù)。
然后ChannelHandlerContext繼承了ChannelInboundInvoker和ChannelOutboundInvoker,可以觸發(fā)inbound和outbound的一些方法。
除了繼承來的一些方法之外,ChannelHandlerContext還可以作為channel,handler和pipline的溝通橋梁,因為可以從ChannelHandlerContext中獲取到對應的channel,handler和pipline:
Channel channel(); ChannelHandler handler(); ChannelPipeline pipeline();還要注意的是ChannelHandlerContext還返回一個EventExecutor,用來執(zhí)行特定的任務(wù):
EventExecutor executor();接下來,我們具體看一下ChannelHandlerContext的實現(xiàn)。
AbstractChannelHandlerContext
AbstractChannelHandlerContext是ChannelHandlerContext的一個非常重要的實現(xiàn),雖然AbstractChannelHandlerContext是一個抽象類,但是它基本上實現(xiàn)了ChannelHandlerContext的所有功能。
首先看一下AbstractChannelHandlerContext的定義:
abstract class AbstractChannelHandlerContext implements ChannelHandlerContext, ResourceLeakHintAbstractChannelHandlerContext是ChannelHandlerContext的一個具體實現(xiàn)。
通常來說一個handler對應一個ChannelHandlerContext,但是在一個程序中可能會有多于一個handler,那么如何在一個handler中獲取其他的handler呢?
在AbstractChannelHandlerContext中有兩個同樣是AbstractChannelHandlerContext類型的next和prev,從而使得多個AbstractChannelHandlerContext可以構(gòu)建一個雙向鏈表。從而可以在一個ChannelHandlerContext中,獲取其他的ChannelHandlerContext,從而獲得handler處理鏈。
volatile AbstractChannelHandlerContext next;volatile AbstractChannelHandlerContext prev;AbstractChannelHandlerContext中的pipeline和executor都是通過構(gòu)造函數(shù)傳入的:
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中并沒有對其進行實現(xiàn),需要在繼承AbstractChannelHandlerContext的類中進行實現(xiàn)。
對于EventExecutor來說,可以通過構(gòu)造函數(shù)向AbstractChannelHandlerContext傳入一個新的EventExecutor,如果沒有傳入或者傳入為空的話,則會使用channel中自帶的EventLoop:
public EventExecutor executor() {if (executor == null) {return channel().eventLoop();} else {return executor;}}因為EventLoop繼承自O(shè)rderedEventExecutor,所以它也是一個EventExecutor。
EventExecutor主要用來異步提交任務(wù)來執(zhí)行,事實上ChannelHandlerContext中幾乎所有來自于ChannelInboundInvoker和ChannelOutboundInvoker的方法都是通過EventExecutor來執(zhí)行的。
對于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() {@Overridepublic void run() {next.invokeChannelRegistered();}});}}fireChannelRegistered調(diào)用了invokeChannelRegistered方法,invokeChannelRegistered則調(diào)用EventExecutor的execute方法,將真實的調(diào)用邏輯封裝在一個runnable類中執(zhí)行。
注意,在調(diào)用executor.execute方法之前有一個executor是否在eventLoop中的判斷。如果executor已經(jīng)在eventLoop中了,那么直接執(zhí)行任務(wù)即可,不需要啟用新的線程。
對于ChannelOutboundInvoker來說,我們以bind方法為例,看一下EventExecutor是怎么使用的:
public ChannelFuture bind(final SocketAddress localAddress, final ChannelPromise promise) {ObjectUtil.checkNotNull(localAddress, "localAddress");if (isNotValidPromise(promise, false)) {// cancelledreturn promise;}final AbstractChannelHandlerContext next = findContextOutbound(MASK_BIND);EventExecutor executor = next.executor();if (executor.inEventLoop()) {next.invokeBind(localAddress, promise);} else {safeExecute(executor, new Runnable() {@Overridepublic void run() {next.invokeBind(localAddress, promise);}}, promise, null, false);}return promise;}可以看到執(zhí)行的邏輯和invokeChannelRegistered方法很類似,也是先判斷executor在不在eventLoop中,如果在的話直接執(zhí)行,如果不在則放在executor中執(zhí)行。
上面的兩個例子中都調(diào)用了next的相應方法,分別是next.invokeChannelRegistered和next.invokeBind。
我們知道ChannelHandlerContext只是一個封裝,它本身并沒有太多的業(yè)務(wù)邏輯,所以next調(diào)用的相應方法,實際上是Context中封裝的ChannelInboundHandler和ChannelOutboundHandler中的業(yè)務(wù)邏輯,如下所示:
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接口中定義的方法都是調(diào)用的handler中具體的實現(xiàn),Context只是對handler的封裝。
DefaultChannelHandlerContext
DefaultChannelHandlerContext是AbstractChannelHandlerContext的一個具體實現(xiàn)。
我們在講解AbstractChannelHandlerContext的時候提到過,AbstractChannelHandlerContext中并沒有定義具體的handler的實現(xiàn),而這個實現(xiàn)是在DefaultChannelHandlerContext中進行的。
DefaultChannelHandlerContext很簡單,我們看一下它的具體實現(xiàn):
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;}@Overridepublic ChannelHandler handler() {return handler;} }DefaultChannelHandlerContext中額外提供了一個ChannelHandler屬性,用來存儲傳入的ChannelHandler。
到此DefaultChannelHandlerContext可以傳入ChannelHandlerContext中一切必須的handler,channel,pipeline和EventExecutor。
總結(jié)
本節(jié)我們介紹了ChannelHandlerContext和它的幾個基本實現(xiàn),了解到了ChannelHandlerContext是對handler,channel和pipline的封裝,ChannelHandlerContext中的業(yè)務(wù)邏輯,實際上是調(diào)用的是底層的handler的對應方法。這也是我們在自定義handler中需要實現(xiàn)的方法。
本文已收錄于 http://www.flydean.com/04-4-netty-channelhandlercontext/
最通俗的解讀,最深刻的干貨,最簡潔的教程,眾多你不知道的小技巧等你來發(fā)現(xiàn)!
歡迎關(guān)注我的公眾號:「程序那些事」,懂技術(shù),更懂你!
總結(jié)
以上是生活随笔為你收集整理的netty系列之:channelHandlerContext详解的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: netty系列之:channelPipe
- 下一篇: 网络标准之:IANA定义的传输编码