簡介
我們知道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/
最通俗的解讀,最深刻的乾貨,最簡潔的教程,眾多你不知道的小技巧等你來發現!
歡迎關注我的公眾號:「程式那些事」,懂技術,更懂你!