netty源码阅读之pipeline之删除channelHandler

删除channelHandler主要分为以下几个步骤:

1、找到节点

2、链表里面删除找到的那个节点

3、回调删除handler事件。

我们从一个简单的用户代码开始分析:

public class AuthHandler extends SimpleChannelInboundHandler {
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        //
    }

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, ByteBuf password) throws Exception {
        if (paas(password)) {
            ctx.pipeline().remove(this);
        } else {
            ctx.close();
        }
    }

    private boolean paas(ByteBuf password) {
        return false;
    }
}

这是服务端一个验证用户的代码,如果通过了验证,就把这个authHandler删除掉(如果没有通过验证,就关闭这个连接),点进去remove开始分析:

    @Override
    public final ChannelPipeline remove(ChannelHandler handler) {
        remove(getContextOrDie(handler));
        return this;
    }

 

找到节点

handler是被handleContext包装的。

上面的getContextOrDie名字取得有意思,通过handler找到context,没有找到就抛出异常:

    private AbstractChannelHandlerContext getContextOrDie(ChannelHandler handler) {
        AbstractChannelHandlerContext ctx = (AbstractChannelHandlerContext) context(handler);
        if (ctx == null) {
            throw new NoSuchElementException(handler.getClass().getName());
        } else {
            return ctx;
        }
    }

看看怎么找到context哦:

    @Override
    public final ChannelHandlerContext context(ChannelHandler handler) {
        if (handler == null) {
            throw new NullPointerException("handler");
        }

        AbstractChannelHandlerContext ctx = head.next;
        for (;;) {

            if (ctx == null) {
                return null;
            }

            if (ctx.handler() == handler) {
                return ctx;
            }

            ctx = ctx.next;
        }
    }

粗暴得很,居然是从head的下一个节点开始找,找ctx的head和当前的一样,就返回。

 

链表里面删除找到的那个节点

回去remove方法:

    private AbstractChannelHandlerContext remove(final AbstractChannelHandlerContext ctx) {
        assert ctx != head && ctx != tail;

        synchronized (this) {
            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() {
                        callHandlerRemoved0(ctx);
                    }
                });
                return ctx;
            }
        }
        callHandlerRemoved0(ctx);
        return ctx;
    }

这个比较有意思啦,需要assert ctx != head && ctx != tail;也就是保证不是head或者tail,在netty里面,这两个节点是不能删除的。

同步代码块里面,remove0()是执行真正的逻辑删除:

 private static void remove0(AbstractChannelHandlerContext ctx) {
        AbstractChannelHandlerContext prev = ctx.prev;
        AbstractChannelHandlerContext next = ctx.next;
        prev.next = next;
        next.prev = prev;
    }

越看到后面,越觉得源码没有挑战性,这段逻辑就是双向链表的删除逻辑。

至于callHandlerRemoved0方法,就是回调删除事件。

 

回调删除handler事件

点击进去callHandlerRemoved0方法:

   private void callHandlerRemoved0(final AbstractChannelHandlerContext ctx) {
        // Notify the complete removal.
        try {
            try {
                ctx.handler().handlerRemoved(ctx);
            } finally {
                ctx.setRemoved();
            }
        } catch (Throwable t) {
            fireExceptionCaught(new ChannelPipelineException(
                    ctx.handler().getClass().getName() + ".handlerRemoved() has thrown an exception.", t));
        }
    }

首先ctx.handler().handlerRemoved(ctx);就是回调删除事件,然后通过ctx.setRemoved();设置删除的状态:

    final void setRemoved() {
        handlerState = REMOVE_COMPLETE;
    }

 

你可能感兴趣的:(netty)