【Netty】ChannelPipeline以及事件执行流程源码解析

在前两篇文章中,介绍了netty的整体启动流程,以及请求处理、数据读写的流程。但是对于netty来说,串联整个请求处理的核心组件是通过pipeline,所以本篇我们梳理下相关的概念。

各个组件的关系如下:
channel 维护一个channelPipeline,而channelPipeline通过内部持有的双向链表头和尾部节点。而每个channelHandler又封装在channelHandlerContext中。
在这里插入图片描述
既然了解了基本组件的关系,接下来我们从pipeLine的初始化、添加节点、删除节点进行分析,以及介绍是如何通过channelPipeline进行数据读写的过程的。

pipeline 初始化

在上一篇文章中,NioServerSocketChannel 通过反射的方式来实例化,进而会调用父类初始化,所以就会创建一个pipeLine属性。

    protected AbstractChannel(Channel parent) {
        this.parent = parent;
        id = newId();
        // 用于负责底层的connect register read write操作等
        unsafe = newUnsafe();
        // 底层实际是 DefaultChannelPipeline
        // 每个Channel都有自己的pipeline,当有请求事件发生时,pipeline负责调用相应的hander进行处理
        pipeline = newChannelPipeline();
    }
    
	// 底层实际是new了一个 DefaultChannelPipeline
	protected DefaultChannelPipeline newChannelPipeline() {
        return new DefaultChannelPipeline(this);
    }

可以看到构造方法中,构建了头和尾部节点。然后首尾相连接。

    protected DefaultChannelPipeline(Channel channel) {
        // 异步处理结果
        succeededFuture = new SucceededChannelFuture(channel, null);
        voidPromise =  new VoidChannelPromise(channel, true);
		// 构建了一个头节点和尾部节点
        tail = new TailContext(this);
        head = new HeadContext(this);
		// 头节点的next 指向 尾节点
        head.next = tail;
        // 尾节点前置节点 指向 头节点
        tail.prev = head;
    }

在这里插入图片描述

class TailContext extends AbstractChannelHandlerContext implements ChannelInboundHandler 
class HeadContext extends AbstractChannelHandlerContext implements ChannelOutboundHandler,ChannelInboundHandler

可以看到头节点即是一个InboundHandler 也是一个 OutboundHandler ,而尾部节点只是一个 InboundHandler

pipeline 添加节点

    .childHandler(new ChannelInitializer<SocketChannel>() {
        @Override
        protected void initChannel(SocketChannel ch) throws Exception {
            ch.pipeline().addLast(new LoggingHandler());
        }
    })

前置处理

通过遍历多个handler进行添加,如果是多个的话

    public final ChannelPipeline addLast(EventExecutorGroup executor, ChannelHandler... handlers) {
        ObjectUtil.checkNotNull(handlers, "handlers");

        for (ChannelHandler h: handlers) {
            if (h == null) {
                break;
            }
            addLast(executor, null, h);
        }

        return this;
    }

添加节点

该方法是核心方法,1.判断是否重复 2.生成节点 3.添加到新的节点中。

   // group 可以不使用IOeventLoop
    // name 是否指定名字,默认的名字策略
    // handler 添加的handler
    @Override
    public final ChannelPipeline addLast(EventExecutorGroup group, String name, ChannelHandler handler) {
        final AbstractChannelHandlerContext newCtx;
        synchronized (this) {
            // 目前handler是不是已经存在pipeline
            checkMultiplicity(handler);

            // 创建channelcontext
            newCtx = newContext(group, filterName(name, handler), handler);

            // 添加到双向链表中
            addLast0(newCtx);

            // If the registered is false it means that the channel was not registered on an eventLoop yet.
            // In this case we add the context to the pipeline and add a task that will call
            // ChannelHandler.handlerAdded(...) once the channel is registered.
            if (!registered) {
                newCtx.setAddPending();
                callHandlerCallbackLater(newCtx, true);
                return this;
            }

            EventExecutor executor = newCtx.executor();
            if (!executor.inEventLoop()) {
                callHandlerAddedInEventLoop(newCtx, executor);
                return this;
            }
        }
        // 触发add方法
        callHandlerAdded0(newCtx);
        return this;
    }

检查是否重复

检查是否重复,其实就是判断当前节点的added属性是否为true,以及判断包含注解@Sharable,如果不包含共享注解 并且添加过,就抛出异常

    private static void checkMultiplicity(ChannelHandler handler) {
        if (handler instanceof ChannelHandlerAdapter) {
            ChannelHandlerAdapter h = (ChannelHandlerAdapter) handler;
            // added 如果添加过 true  否则就是false
            // 是否有 @Sharable

            // 没有贡献注解并且 已经添加过 抛出异常
            if (!h.isSharable() && h.added) {
                throw new ChannelPipelineException(
                        h.getClass().getName() +
                        " is not a @Sharable handler, so can't be added or removed multiple times.");
            }
            // 设置标志位
            h.added = true;
        }
    }
是否可见的

其实就是获取当前Class对象,然后判断是否包含注解

    public boolean isSharable() {
        Class<?> clazz = getClass();
        Map<Class<?>, Boolean> cache = InternalThreadLocalMap.get().handlerSharableCache();
        Boolean sharable = cache.get(clazz);
        if (sharable == null) {
            sharable = clazz.isAnnotationPresent(Sharable.class);
            cache.put(clazz, sharable);
        }
        return sharable;
    }

生成名字

    private String filterName(String name, ChannelHandler handler) {
        // 如果没有默认名字 自动生成
        if (name == null) {
            return generateName(handler);
        }
        // 查看是否有冲突
        checkDuplicateName(name);
        return name;
    }
    private String generateName(ChannelHandler handler) {
        // handler 的名字,通过fastThreadLocal 把名字存储在线程中
        Map<Class<?>, String> cache = nameCaches.get();
        Class<?> handlerType = handler.getClass();
        String name = cache.get(handlerType);
        // 如果没有名字
        if (name == null) {
            // 生成一个名字
            name = generateName0(handlerType);
            // 存储在线程中
            cache.put(handlerType, name);
        }

        // It's not very likely for a user to put more than one handler of the same type, but make sure to avoid
        // any name conflicts.  Note that we don't cache the names generated here.
        if (context0(name) != null) {
            String baseName = name.substring(0, name.length() - 1); // Strip the trailing '0'.
            for (int i = 1;; i ++) {
                String newName = baseName + i;
                if (context0(newName) == null) {
                    name = newName;
                    break;
                }
            }
        }
        return name;
    }
    private static String generateName0(Class<?> handlerType) {
        // 类民#0
        return StringUtil.simpleClassName(handlerType) + "#0";
    }

判断后续节点是否有名字冲突。其实就是链表遍历

    private AbstractChannelHandlerContext context0(String name) {
        AbstractChannelHandlerContext context = head.next;
        while (context != tail) {
            if (context.name().equals(name)) {
                return context;
            }
            context = context.next;
        }
        return null;
    }

生成节点

    private AbstractChannelHandlerContext newContext(EventExecutorGroup group, String name, ChannelHandler handler) {
        return new DefaultChannelHandlerContext(this, childExecutor(group), name, handler);
    }

添加到双向链表中

    private void addLast0(AbstractChannelHandlerContext newCtx) {
        AbstractChannelHandlerContext prev = tail.prev;
        newCtx.prev = prev;
        newCtx.next = tail;
        prev.next = newCtx;
        tail.prev = newCtx;
    }

在这里插入图片描述
在这里插入图片描述

如何执行具体的流程

上面通过源码的分析可以发现,构成一个双向链表。那么对于客户端发起的请求,是如何将请求发送给channelPipeLine进行执行的。

在这里插入图片描述
在这里插入图片描述

以read分析

在这里插入图片描述

NioByteUnsafe.read
当客户端发起一个写事件时候,服务端对应的开启读操作,然后将数据写入到byteBuf中,通过如下方法调用整个pipeline事件

	pipeline.fireChannelRead(byteBuf);
    public final ChannelPipeline fireChannelRead(Object msg) {
        AbstractChannelHandlerContext.invokeChannelRead(head, msg);
        return this;
    }
    static void invokeChannelRead(final AbstractChannelHandlerContext next, Object msg) {
        final Object m = next.pipeline.touch(ObjectUtil.checkNotNull(msg, "msg"), next);
        EventExecutor executor = next.executor();
        if (executor.inEventLoop()) {
            next.invokeChannelRead(m);
        } else {
            executor.execute(new Runnable() {
                @Override
                public void run() {
                    next.invokeChannelRead(m);
                }
            });
        }
    }
    private void invokeChannelRead(Object msg) {
        if (invokeHandler()) {
            try {
                final ChannelHandler handler = handler();
                final DefaultChannelPipeline.HeadContext headContext = pipeline.head;
                // 可能是head节点
                if (handler == headContext) {
                    headContext.channelRead(this, msg);
                } else if (handler instanceof ChannelDuplexHandler) {
                	// 这里是自定义的读操作
                    ((ChannelDuplexHandler) handler).channelRead(this, msg);
                } else {
                    ((ChannelInboundHandler) handler).channelRead(this, msg);
                }
            } catch (Throwable t) {
                invokeExceptionCaught(t);
            }
        } else {
            fireChannelRead(msg);
        }
    }
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
    	// 将消息传递给下一个channelHandler
        ctx.fireChannelRead(msg);
    }
    public ChannelHandlerContext fireChannelRead(final Object msg) {
        invokeChannelRead(findContextInbound(MASK_CHANNEL_READ), msg);
        return this;
    }

这里其实就是查找下一个channelInHandlerContext。

    private AbstractChannelHandlerContext findContextInbound(int mask) {
        AbstractChannelHandlerContext ctx = this;
        EventExecutor currentExecutor = executor();
        do {
            ctx = ctx.next;
        } while (skipContext(ctx, currentExecutor, mask, MASK_ONLY_INBOUND));
        return ctx;
    }

对于outBoundHandler来说,是从相反的尾部节点开始操作。

    public final ChannelFuture bind(SocketAddress localAddress) {
        return tail.bind(localAddress);
    }

而至此,也通过3篇文章详细介绍了 启动流程、线程处理流程读写操作,以及pipeline是如何处理事件的。
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

qxlxi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值