上一節學習了inbound
事件的傳播,充分理解了在pipeline
中是如何向一個個handler
傳播事件的,以channelRead
事件也就是讀事件爲例,研究了其處理邏輯。
本節學習outbound
事件的傳播,和inbound
事件有相似之處。以write
事件爲例,進行學習研究。
-
ChannelHandler家族
如下圖,ChannelHandler家族
分爲ChannelInboundHandler
以及ChannelOutboundHandler
,分別定義了入站處理器以及出站處理器,同時也提供了對應的實現。上一節inbound
事件傳播也發現確定是inbound事件
還是outbound事件
是由instanceof
關鍵子判斷是否實現了對應的接口。
對比一下ChannelInboundHandler
和ChannelOutboundHandler
的方法,可以發現,ChannelInboundHandler
的方法以被動觸發爲主,而ChannelOutboundHandler
的方法則是主動行爲。
ChannelInboundHandler | ChannelInboundHandler |
---|---|
channelRegistered (事件觸發:註冊) |
deregister (註銷) |
channelUnregistered (事件觸發:註銷) |
disconnect (取消連接) |
channelActive (事件觸發:激活連接) |
connect (連接) |
channelRead (事件觸發:讀) |
read (讀) |
channelReadComplete (事件觸發:讀完成) |
write (寫) |
userEventTriggered (事件觸發:用戶事件) |
flush (刷緩存) |
channelWritabilityChanged (事件觸發:通道可讀狀態被修改) |
close (關閉連接) |
exceptionCaught (事件觸發:異常) |
bind (綁定端口) |
-
ChannelOutboundHandler
的執行順序正好和ChannelInboundHandler
相反,是倒序的。
class DataServerInitializer extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(
new OutboundHandlerA(),
new OutboundHandlerB(),
new OutboundHandlerC()
);
}
}
class OutboundHandlerA extends ChannelOutboundHandlerAdapter {
@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
System.out.println("OutboundHandlerA : " + msg);
ctx.write(msg, promise);
}
}
class OutboundHandlerB extends ChannelOutboundHandlerAdapter {
@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
System.out.println("OutboundHandlerB : " + msg);
ctx.write(msg, promise);
}
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
ctx.executor().schedule(() -> {
ctx.channel().write("hello world");
}, 3, TimeUnit.SECONDS);
}
}
class OutboundHandlerC extends ChannelOutboundHandlerAdapter {
@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
System.out.println("OutboundHandlerC : " + msg);
ctx.write(msg, promise);
}
}
改變添加handler
的順序
class DataServerInitializer extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(
new OutboundHandlerB(),
new OutboundHandlerA(),
new OutboundHandlerC()
);
}
}
- 跟蹤
ctx.channel().write("hello world");
@Override
public ChannelFuture write(Object msg) {
//從pipeline開始調用
return pipeline.write(msg);
}
@Override
public final ChannelFuture write(Object msg) {
//從尾節點開始傳播
return tail.write(msg);
}
@Override
public ChannelFuture write(Object msg) {
//添加一個回調Promise,包裝channel和executor
return write(msg, newPromise());
}
最終調用到AbstractChannelHandlerContext#write()
方法,主要是做了兩件事
-
findContextOutbound
方法找到下一個ChannelOutboundHandlerContext
- 判斷是否需要
flush
,選擇執行write
回調方法之後是否執行flush
回調方法
private void write(Object msg, boolean flush, ChannelPromise promise) {
ObjectUtil.checkNotNull(msg, "msg");
try {
if (isNotValidPromise(promise, true)) {
ReferenceCountUtil.release(msg);
// cancelled
return;
}
} catch (RuntimeException e) {
ReferenceCountUtil.release(msg);
throw e;
}
//查找下一個ChannelOutboundHandlerContext
AbstractChannelHandlerContext next = findContextOutbound();
final Object m = pipeline.touch(msg, next);
EventExecutor executor = next.executor();
if (executor.inEventLoop()) {
//判斷是否刷新
if (flush) {
//執行寫並刷新方法
next.invokeWriteAndFlush(m, promise);
} else {
//執行寫方法
next.invokeWrite(m, promise);
}
} else {
final AbstractWriteTask task;
if (flush) {
task = WriteAndFlushTask.newInstance(next, m, promise);
} else {
task = WriteTask.newInstance(next, m, promise);
}
if (!safeExecute(executor, task, promise, m)) {
// We failed to submit the AbstractWriteTask. We need to cancel it so we decrement the pending bytes
// and put it back in the Recycler for re-use later.
//
// See https://github.com/netty/netty/issues/8343.
task.cancel();
}
}
}
-
findContextOutbound
方法找到下一個ChannelOutboundHandlerContext
private AbstractChannelHandlerContext findContextOutbound() {
//循環往前查找,通過outbound屬性判斷
AbstractChannelHandlerContext ctx = this;
do {
ctx = ctx.prev;
} while (!ctx.outbound);
return ctx;
}
- 執行
write
回調方法
private void invokeWrite(Object msg, ChannelPromise promise) {
//判斷handler的狀態是可以執行回調函數的
if (invokeHandler()) {
invokeWrite0(msg, promise);
} else {
write(msg, promise);
}
}
private void invokeWrite0(Object msg, ChannelPromise promise) {
try {
//執行回調函數write
((ChannelOutboundHandler) handler()).write(this, msg, promise);
} catch (Throwable t) {
notifyOutboundHandlerException(t, promise);
}
}
-
invokeWriteAndFlush
執行完write
回調方法之後執行flush
回調方法
private void invokeWriteAndFlush(Object msg, ChannelPromise promise) {
if (invokeHandler()) {
//執行write
invokeWrite0(msg, promise);
//執行flush
invokeFlush0();
} else {
writeAndFlush(msg, promise);
}
}
private void invokeFlush0() {
try {
//回調flush方法
((ChannelOutboundHandler) handler()).flush(this);
} catch (Throwable t) {
notifyHandlerException(t);
}
}
- 通過跟蹤源碼,也不難發現無論是從
tail
節點開始還是從當前節點開始調用write
方法,最終都會到head
節點。而頭節點正是使用unsafe
來具體完成這些操作的。
@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) {
unsafe.write(msg, promise);
}
@Override
public void flush(ChannelHandlerContext ctx) {
unsafe.flush();
}