netty handler的执行顺序(1)

本文详细探讨了Netty中Handler的执行顺序及机制,重点分析了Handler如何在ChannelPipeline中组织,并通过具体代码示例解释了消息从ChannelRead传递到各个Handler的过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

handler的概念,让我想到了其他的一些东西,就好像servlet当中的filter,spring当中的interceptor。 在handler当中,能够完成各种各样的工作,协议流的编解码、特殊信息的拦截、请求数量的统计等等,或者可以这样说,所有的业务层面的东西,都需要在handler当中来完成。

我会按照上行和下行两类来分析handler的执行顺序,今天先来下行的。

按照我最初的想象,所有的handler应该都是同一类东西,那么我们业务执行的方法,也就是我们继承netty提供的父类之后,Override的方法,应该是同一个,可是我实际使用当中发现不是这么回事,有时候是一个decode方法,有时候是一个messageReceived方法,这是什么道理。基于这个困惑,才会有了今天的这篇文章。

首先是一个stacktrace的图。

ProtocolAnaDecoder是我自己写的一个协议解析类,继承自 ByteToMessageDecoder ,在这个类里面依次调用了3个方法,channelRead(),callDecode(),decode()。这其中decode,是我们自己实现的,其他2个方法来自父类。

再看另一个图。

NettyServerHandler继承自SimpleChannelInboundHandler,这里依次调用了channelRead(),messageReceived()这样2个方法。

到此为止基本就解决了我的第一个疑惑,最初都来自channelRead。那么这个 channelRead 虽然在各种handler当中都有实现,但是它的最初的定义来自ChannelHandler,这是一个interface。而它的实现ChannelHandlerAdapter,基本可以看做netty当中所有handler的老祖宗。(这里之所以要说基本,是因为有2个web相关的handler interface,直接继承了 ChannelHandler,但这个不是我们今天讨论的重点)

继续,就该是ChannelHandlerInvokerUtil.invokeChannelReadNow,看代码吧。

    public static void invokeChannelReadNow(final ChannelHandlerContext ctx, final Object msg) {
        try {
            ctx.handler().channelRead(ctx, msg);
        } catch (Throwable t) {
            notifyHandlerException(ctx, t);
        }
    }

清楚明白,很好理解。

然后是DefaultChannelHandlerInvoker.invokeChannelRead,代码如下:

@Override
    public void invokeChannelRead(final ChannelHandlerContext ctx, final Object msg) {
        if (msg == null) {
            throw new NullPointerException("msg");
        }

        if (executor.inEventLoop()) {
            invokeChannelReadNow(ctx, msg);
        } else {
            safeExecuteInbound(new Runnable() {
                @Override
                public void run() {
                    invokeChannelReadNow(ctx, msg);
                }
            }, msg);
        }
    }

executor.inEventLoop() ,当前channel的 executor 是否处于时间循环当中,好吧,到目前为止,我也不知道什么时候会走到else里面去,这里只好留待以后再去搞搞清楚了。

再往前走,DefaultChannelHandlerContext.fireChannelRead,代码如下:

public ChannelHandlerContext fireChannelRead(Object msg) {
        DefaultChannelHandlerContext next = findContextInbound(MASK_CHANNEL_READ);
        next.invoker.invokeChannelRead(next, msg);
        return this;
    }

handler的依次执行就在这里面体现了。

继续,DefaultChannelPipeline.fireChannelRead,代码如下:

public ChannelPipeline fireChannelRead(Object msg) {
        head.fireChannelRead(msg);
        return this;
    }

好了,如果没记错的话,我们最初声明一个netty的时候,就是把一系列的handler加到了channel pipeline当中。那么这一系列的handler在pipeline当中是如何保存的呢。我首先先看一下 DefaultChannelPipeline 的构造函数:

public DefaultChannelPipeline(AbstractChannel channel) {
        if (channel == null) {
            throw new NullPointerException("channel");
        }
        this.channel = channel;

        TailHandler tailHandler = new TailHandler();
        tail = new DefaultChannelHandlerContext(this, null, generateName(tailHandler), tailHandler);

        HeadHandler headHandler = new HeadHandler(channel.unsafe());
        head = new DefaultChannelHandlerContext(this, null, generateName(headHandler), headHandler);

        head.next = tail;
        tail.prev = head;
    }

首先生命了一个tail和一个head,然后把这2个对象构成了一个双向链表。

再看一下addlast方法:

private void addLast0(final String name, DefaultChannelHandlerContext newCtx) {
        checkMultiplicity(newCtx);

        DefaultChannelHandlerContext prev = tail.prev;
        newCtx.prev = prev;
        newCtx.next = tail;
        prev.next = newCtx;
        tail.prev = newCtx;

        name2ctx.put(name, newCtx);

        callHandlerAdded(newCtx);
    }

很清楚,在链表当中插入一个元素。再对照一下前面的代码,首先从head开始,但它并不完成实际工作,直接取它的next来执行,之后依次便利链表。

到此为止,下行的情况下,handler的执行顺序, 基本都很清楚。

 

转载于:https://my.oschina.net/dongtianxi/blog/703416

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值