上一節學習了添加ChannelHandler
的過程瞭解了,也明白了添加handler
是如何促發handlerAdded
事件。其中還包括了ChannelInitializer
這樣特殊的handler
,在完成了添加handler
的任務後,就從pipeline
中刪除了。
本節研究刪除ChannelHandler
的邏輯。類似ChannelInitializer
這種,handler
用完一次就沒什麼用的場景,類似權限校驗,每次新連接接入需要校驗權限,之後的數據傳輸就不需要了,這時候就可以動態的刪掉權限校驗的handler
。如下
class DataServerInitializer extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(
new AuthHandler(),
new DataServerHandler()
);
}
}
class AuthHandler extends SimpleChannelInboundHandler<ByteBuf> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, ByteBuf password) throws Exception {
//校驗密碼
if (pass(password)) {
//校驗通過,後續不需要繼續校驗,刪除該handler
ctx.pipeline().remove(this);
}else {
ctx.close();
}
}
private boolean pass(ByteBuf password) {
//ignore: 校驗邏輯
return false;
}
}
ChannelHandler
是背ChannelHanderContext
包裝起來的。因此,刪除ChannelHandler
過程如下:
- 找到
handler
對應的ChannelHandlerContext
- 從鏈表中刪除
ChannelHandlerContext
- 觸發
handlerRemoved
事件
@Override
public final ChannelPipeline remove(ChannelHandler handler) {
//先獲取ChannelHandlerContext再刪除
remove(getContextOrDie(handler));
return this;
}
-
getContextOrDie()
找到handler
對應的ChannelHandlerContext
private AbstractChannelHandlerContext getContextOrDie(ChannelHandler handler) {
//獲取ChannelHandlerContext,沒有則拋異常
AbstractChannelHandlerContext ctx = (AbstractChannelHandlerContext) context(handler);
if (ctx == null) {
throw new NoSuchElementException(handler.getClass().getName());
} else {
return ctx;
}
}
@Override
public final ChannelHandlerContext context(ChannelHandler handler) {
//handler爲空則拋異常
if (handler == null) {
throw new NullPointerException("handler");
}
//從鏈表頭開始遍歷到尾,直到,直到找到對應的handler對應的ctx
AbstractChannelHandlerContext ctx = head.next;
for (;;) {
if (ctx == null) {
return null;
}
if (ctx.handler() == handler) {
return ctx;
}
ctx = ctx.next;
}
}
- 先刪除,再調用
callHandlerRemoved0
觸發handlerRemoved
事件
private AbstractChannelHandlerContext remove(final AbstractChannelHandlerContext ctx) {
//斷言,非頭也非尾
assert ctx != head && ctx != tail;
synchronized (this) {
//將ctx從pipeline鏈表中刪除
remove0(ctx);
// If the registered is false it means that the channel was not registered on an eventloop yet.
// In this case we remove the context from the pipeline and add a task that will call
// ChannelHandler.handlerRemoved(...) once the channel is registered.
if (!registered) {
callHandlerCallbackLater(ctx, false);
return ctx;
}
EventExecutor executor = ctx.executor();
if (!executor.inEventLoop()) {
executor.execute(new Runnable() {
@Override
public void run() {
//觸發HandlerRemoved事件
callHandlerRemoved0(ctx);
}
});
return ctx;
}
}
//觸發HandlerRemoved事件
callHandlerRemoved0(ctx);
return ctx;
}
- 從鏈表中刪除
private static void remove0(AbstractChannelHandlerContext ctx) {
//鏈表刪除,修改兩端的前後節點
AbstractChannelHandlerContext prev = ctx.prev;
AbstractChannelHandlerContext next = ctx.next;
prev.next = next;
next.prev = prev;
}
- 觸發
handlerRemoved
事件。該動作獲取了handler
並調用了回調函數handlerRemoved()
,最後修改了handler
狀態,標記已經完成了刪除。
private void callHandlerRemoved0(final AbstractChannelHandlerContext ctx) {
// Notify the complete removal.
try {
//調用ctx的觸發事件方法
ctx.callHandlerRemoved();
} catch (Throwable t) {
fireExceptionCaught(new ChannelPipelineException(
ctx.handler().getClass().getName() + ".handlerRemoved() has thrown an exception.", t));
}
}
final void callHandlerRemoved() throws Exception {
try {
// Only call handlerRemoved(...) if we called handlerAdded(...) before.
if (handlerState == ADD_COMPLETE) {
//獲取handler並觸發事件回調函數
handler().handlerRemoved(this);
}
} finally {
// Mark the handler as removed in any case.
//標記handler已經刪除
setRemoved();
}
}
final void setRemoved() {
//修改handler狀態爲完成刪除
handlerState = REMOVE_COMPLETE;
}