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过程如下:

  1. 找到handler对应的ChannelHandlerContext
  2. 从链表中删除ChannelHandlerContext
  3. 触发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;
    }

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,496评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,407评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,632评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,180评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,198评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,165评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,052评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,910评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,324评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,542评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,711评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,424评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,017评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,668评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,823评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,722评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,611评论 2 353

推荐阅读更多精彩内容