netty原始碼分析(20)- 刪除ChannelHandler過程
上一節學習了新增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; }