Netty源码分析(四):关于ChannelPipeline和addLast

ChannelPipeline

1.架构设计:

Channel都有且仅有一个ChannelPipeline与之对应,Channel包含了ChannelPipeline,ChannelPipeline内部包含了N个handler,每一个handler都是由一个线程去执行;

ChannelPipeline内部维护了一个由ChannelHandlerContext组成的双向链表,头为HeadContext,尾为TailHandler(双向链表为自己写的,而不是使用JDK的链表,为了轻量级),并且每个ChannelHandlerContext中又关联着一个ChannelHandler:

在这里插入图片描述

2.ChannelInboundHandler和ChannelOutboundHandler

设计技巧:tail是Inbound为true,head是outbound为true,可以理解为尾进头出,就是由inbound和outbound变量来控制的,这也是使用者要添加handler则使用addLast方法的原因;不过注意这里不能理解成队列,因为是有顺序的

inbound设计

in和out是相对channel、即程序来说的;out即往外面发送东西,in即接收东西。
两者都是异步发生、在线程中响应的,通过线程去执行。
out为请求型的动作,由业务程序来触发;in为响应型的动作,由Netty来触发,业务程序被动接收。

什么时候使用inbound呢?

实现了接口后,可以看到需要实现channelRead0方法,这个方法使用者无须调用,因为是由netty调用的,此接口的其他方法也是如此,因此做业务过程中,最多的就是用inbound,而outbound需要手动去调用

connect方法中,层层调用,到达AbstractChannel.AbstractUnsafe#register0方法

//AbstractChannel.AbstractUnsafe#register0
private void register0(ChannelPromise promise) {
	doRegister();//将SocketChannel注册到selector中,之前已经分析过
    pipeline.fireChannelRegistered();//重点分析
}
//DefaultChannelPipeline#fireChannelRegistered
public final ChannelPipeline fireChannelRegistered() {
    AbstractChannelHandlerContext.invokeChannelRegistered(head);//传入head
    return this;
}
//AbstractChannelHandlerContext#invokeChannelRegistered(AbstractChannelHandlerContext)
static void invokeChannelRegistered(final AbstractChannelHandlerContext next) {
    EventExecutor executor = next.executor();
    if (executor.inEventLoop()) {
        next.invokeChannelRegistered();
    } else {//开启一个线程来执行
        executor.execute(new Runnable() {
            @Override
            public void run() {
                next.invokeChannelRegistered();
            }
        });
    }
}
private void invokeChannelRegistered() {
	((ChannelInboundHandler) handler()).channelRegistered(this);
}
//DefaultChannelPipeline.HeadContext#channelRegistered
public void channelRegistered(ChannelHandlerContext ctx) throws Exception {//
    invokeHandlerAddedIfNeeded();
    ctx.fireChannelRegistered();
}
//AbstractChannelHandlerContext#fireChannelRegistered
public ChannelHandlerContext fireChannelRegistered() {
    invokeChannelRegistered(findContextInbound());
    return this;
}
//AbstractChannelHandlerContext#invokeChannelRegistered(AbstractChannelHandlerContext)
static void invokeChannelRegistered(final AbstractChannelHandlerContext next) {
    EventExecutor executor = next.executor();
    if (executor.inEventLoop()) {
        next.invokeChannelRegistered();
    } else {
        executor.execute(new Runnable() {
            @Override
            public void run() {
                next.invokeChannelRegistered();
            }
        });
    }
}

可以看到,到了这里,即第七段,代码就陷入了循环调用(3段->4段>5段>6段->7段即3段->4段…)

所以看看第六段代码调用的findContextInbound():

//AbstractChannelHandlerContext#findContextInbound
private AbstractChannelHandlerContext findContextInbound() {
    AbstractChannelHandlerContext ctx = this;
    do {
        ctx = ctx.next;
    } while (!ctx.inbound);
    return ctx;
}

设计技巧:只要为inbound就返回,即一直调用,直到等于自己,就全部结束;
这里用inbound来判断,因为head的inbound为false
在这里插入图片描述
注:
链路调用从tail开始,一直往下next,调了head之后回到本身,终止调用;这是一个环形链路
fire才会触发下一个handler

addLast

addLast内部也有多个重载方法,类似Spring的beanFactory给每个bean起名字,为类名首字母小写,这里同样会起名字,名字我们看源码来分析:

private String generateName(ChannelHandler handler) {
    Map<Class<?>, String> cache = nameCaches.get();//会有一个缓存
    Class<?> handlerType = handler.getClass();
    String name = cache.get(handlerType);
    if (name == null) {
        //return StringUtil.simpleClassName(handlerType) + "#0";
        //假设handler名字为PdcHandler,则name为PdcHandler#0
        name = generateName0(handlerType);
        cache.put(handlerType, name);
    }
    return name;
}

如果自己起名,则不能重复

猜你喜欢

转载自blog.csdn.net/qq_41594698/article/details/89894135