Netty18——ChannelPipeline 调度 handler的剖析

 当一个请求进来的时候,ChannelPipeline 是如何调用内部的这些 handler 的呢?
 当一个请求进来的时候,会第一个调用 pipeline 的 相关方法,如果是入站事件,这些方法由 fire 开头,表示开始管道的流动。从head节点开始,每个handler处理完成后,接着让后面的 handler 继续处理。

一、ChannelPipeline调度Handler的过程

 在DefaultChannelPipeline中有个fireChannelActive()方法,是请求入站的开始,源码如下:

@Override
public final ChannelPipeline fireChannelActive() {
    AbstractChannelHandlerContext.invokeChannelActive(head);
    return this;
}

 在有请求入站时,会先调用DefaultChannelPipeline的fireChannelActive()方法,从head节点开始往后执行,fireChannelActive()调用了AbstractChannelHandlerContext.invokeChannelActive(head):

//是否开启新线程执行invokeChannelActive()
static void invokeChannelActive(final AbstractChannelHandlerContext next) {
    EventExecutor executor = next.executor();
    if (executor.inEventLoop()) {
        next.invokeChannelActive();
    } else {
        executor.execute(new Runnable() {
            @Override
            public void run() {
                next.invokeChannelActive();
            }
        });
    }
}

 接着调用了AbstractChannelHandlerContext的invokeChannelActive(),该方法会调用具体的Handler的channelActive()方法:

private void invokeChannelActive() {
    if (invokeHandler()) {
        try {
        	//调用具体Handler的channelActive()方法
            ((ChannelInboundHandler) handler()).channelActive(this);
        } catch (Throwable t) {
            notifyHandlerException(t);
        }
    } else {
    	// 接着下一个handler继续执行
        fireChannelActive();
    }
}

 需要注意的是,我们自己在实现ChannelInboundHandler的channelActive()方法时,如果我们想继续执行后面的handler,一定要在方法中调用ctx.fireChannelActive()才可以,否则后面的handler无法得到执行

@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
    Thread.sleep(1000);
    System.out.println(">>>ABossGroupHandler channelActive..." + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
    // 继续执行下一个handler,若不调用该方法,后面的handler不会执行
    ctx.fireChannelActive();
}

 调用ctx.fireChannelActive(),后又会调用invokeChannelActive(findContextInbound()):

@Override
public ChannelHandlerContext fireChannelActive() {
    // 又回到最上面的那个方法上去了
    invokeChannelActive(findContextInbound());
    return this;
}

private AbstractChannelHandlerContext findContextInbound() {
    AbstractChannelHandlerContext ctx = this;
    do {
        // 获取到下一个Context,继续执行
        ctx = ctx.next;
    } while (!ctx.inbound);
    return ctx;
}

 上面的方法调用中会会获取下一个Context,接着调用最上面的那个invokeChannelActive()方法,如此依次调用下来,直到调用到tailContext结束。过程如下图所示:
在这里插入图片描述

二、DefaultChannelPipeline的实现
2.1 DefaultChannelPipeline对ChannelInboundInvoker的实现

 这里只罗列了fire开头的方法,这些方法都是具体事件发生时会触发的方法,这些事件发生后会通过DefaultChannelPipeline的fireXxx方法去调用具体Handler的对应方法,实现回调和用户业务逻辑的织入:

public class DefaultChannelPipeline implements ChannelPipeline{
    @Override
    public final ChannelPipeline fireChannelActive() {
        AbstractChannelHandlerContext.invokeChannelActive(head);
        return this;
    }

    @Override
    public final ChannelPipeline fireChannelInactive() {
        AbstractChannelHandlerContext.invokeChannelInactive(head);
        return this;
    }

    @Override
    public final ChannelPipeline fireExceptionCaught(Throwable cause) {
        AbstractChannelHandlerContext.invokeExceptionCaught(head, cause);
        return this;
    }

    @Override
    public final ChannelPipeline fireUserEventTriggered(Object event) {
        AbstractChannelHandlerContext.invokeUserEventTriggered(head, event);
        return this;
    }

    @Override
    public final ChannelPipeline fireChannelRead(Object msg) {
        AbstractChannelHandlerContext.invokeChannelRead(head, msg);
        return this;
    }

    @Override
    public final ChannelPipeline fireChannelReadComplete() {
        AbstractChannelHandlerContext.invokeChannelReadComplete(head);
        return this;
    }

    @Override
    public final ChannelPipeline fireChannelWritabilityChanged() {
        AbstractChannelHandlerContext.invokeChannelWritabilityChanged(head);
        return this;
    }
}

 这些fireXxx方法都是inbound的方法,也就是说都是入站事件发生时调用的方法,这里的head节点对象都体现为ChannelInboundInvoker类型的形态(从多态的角度来说,虽然head节点对象也实现了ChannelOutboundInvoker接口,但在此处它不体现ChannelOutboundInvoker的形态),然后通过AbstractChannelHandlerContext的相应静态方法调用ChannelInboundInvoker的具体实现的方法,最终调用到Handler的对应方法。

2.2 DefaultChannelPipeline对ChannelOutboundInvoker的实现
public class DefaultChannelPipeline implements ChannelPipeline{
    @Override
    public final ChannelFuture bind(SocketAddress localAddress) {
        return tail.bind(localAddress);
    }

    @Override
    public final ChannelFuture connect(SocketAddress remoteAddress) {
        return tail.connect(remoteAddress);
    }

    @Override
    public final ChannelFuture connect(SocketAddress remoteAddress, SocketAddress localAddress) {
        return tail.connect(remoteAddress, localAddress);
    }

    @Override
    public final ChannelFuture disconnect() {
        return tail.disconnect();
    }

    @Override
    public final ChannelFuture close() {
        return tail.close();
    }

    @Override
    public final ChannelFuture deregister() {
        return tail.deregister();
    }

    @Override
    public final ChannelPipeline flush() {
        tail.flush();
        return this;
    }

    @Override
    public final ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise) {
        return tail.bind(localAddress, promise);
    }

    @Override
    public final ChannelFuture connect(SocketAddress remoteAddress, ChannelPromise promise) {
        return tail.connect(remoteAddress, promise);
    }

    @Override
    public final ChannelFuture connect(SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise) {
        return tail.connect(remoteAddress, localAddress, promise);
    }

    @Override
    public final ChannelFuture disconnect(ChannelPromise promise) {
        return tail.disconnect(promise);
    }
}

 这些方法都是出站事件的实现,调用的都是outbound类型的tail节点的相应方法来处理的,都是outbound事件。
 出站由tail开始,入站从head开始。因为出站是从内部向外写,从tail开始能够让前面的handler对后面的handler产生的信息进行处理,防止handler被遗漏。反之,入站是从head开始向内部输入,让后面的handler能够处理前面输入的数据。虽然head也实现了outbound接口,但不是从head开始执行出站任务。

三、ChannelPipeline 调度 handler 的总结

 ①Context 包装 handler,多个 Context 在 pipeline 中形成了双向链表,入站方向叫 inbound,由 head 节点开始,出站方向叫 outbound ,由 tail 节点开始。
 ②而节点间的传递通过 AbstractChannelHandlerContext 类内部的 fire 系列方法,找到当前节点的下一个节点不断的向后传播。以一个过滤器的模式完成对所有handler 的调度。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值