Netty的ChannelPipeline职责链维护和调用过程源码的解析(三)

4 篇文章 0 订阅

        在前面两篇文章中我们介绍了EventLoopGroup和ServerBootstrap的初始化和启动的源码解析,在启动过程中,我们可以在Channel中自定义handler来处理业务,但是在netty中我们必须通过pipeline的方式进行添加,就如下面的代码实现

public static void main(String[] args) {
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            NettyServerHandlerTest1 handlerTest1 = new NettyServerHandlerTest1();
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            serverBootstrap.group(bossGroup,workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .option(ChannelOption.SO_BACKLOG,1024)
                    .handler(new LoggingHandler(LogLevel.INFO))
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            ChannelPipeline pipeline = socketChannel.pipeline();
                            pipeline.addLast(handlerTest1);
                        }
                    });
            ChannelFuture channelFuture = serverBootstrap.bind(8010).sync();
            channelFuture.channel().closeFuture().sync();
        }catch (Exception e) {
            e.printStackTrace();
        }finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }

          我们启动服务时看下pipeline的初始添加值过程,通过源码跟踪我们可以得到以下的大概流程图

        结合上面的代码pipeline.addLast()我们直接进入到DefaultChannelPipeline.addLast(ChannelHandler... handlers)方法,然后在通过源码跟踪进入到如下的代码中

public final ChannelPipeline addLast(EventExecutorGroup group, String name, ChannelHandler handler) {
        final AbstractChannelHandlerContext newCtx;
        // 同步块操作
        synchronized (this) {
            // 检查定义的handler是否使用了@ChannelHandler.Sharable注解并且是否已经添加过
            checkMultiplicity(handler);

            // 定义DefaultChannelHandlerContext类
            newCtx = newContext(group, filterName(name, handler), handler);

            // 对newCtx值的前后链表进行赋值操作
            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()) {
                // 异步初始化handler在EventLoop调用
                callHandlerAddedInEventLoop(newCtx, executor);
                return this;
            }
        }
        // handler在EventLoop调用
        callHandlerAdded0(newCtx);
        return this;
    }

       通过上面的一些步骤就完成了对pipeline中的大体维护过程,当然pipeline还有addFirst,addBefore等方法,需要特别说明的是checkMultiplicity()方法

private static void checkMultiplicity(ChannelHandler handler) {
        if (handler instanceof ChannelHandlerAdapter) {
            ChannelHandlerAdapter h = (ChannelHandlerAdapter) handler;
            // 判断是否实现了@ChannelHandler.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;
        }
    }
public boolean isSharable() {
        /**
         * Cache the result of {@link Sharable} annotation detection to workaround a condition. We use a
         * {@link ThreadLocal} and {@link WeakHashMap} to eliminate the volatile write/reads. Using different
         * {@link WeakHashMap} instances per {@link Thread} is good enough for us and the number of
         * {@link Thread}s are quite limited anyway.
         *
         * See <a href="https://github.com/netty/netty/issues/2289">#2289</a>.
         */
        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;
    }

         分析完pipeline的handler维护过程,我们在来了解下pipeline数据处理的过程,跟踪源码大概的流程图如下

      我们先启动客户端,然后跟踪服务端的pipeline处理流程,服务端的pipeline入口是DefaultChannelPipeline.fireChannelRead(Object msg)方法

public ChannelHandlerContext fireChannelRead(final Object msg) {
        invokeChannelRead(findContextInbound(), msg);
        return this;
    }
static void invokeChannelRead(final AbstractChannelHandlerContext next, Object msg) {
        // 判断是否是ReferenceCounted类型的数据,如果是则进行具体实现处理,否则直接返回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 {
                // 调用接口实现类去完成自己的逻辑业务操作
                ((ChannelInboundHandler) handler()).channelRead(this, msg);
            } catch (Throwable t) {
                notifyHandlerException(t);
            }
        } else {
            fireChannelRead(msg);
        }
    }

       通过上面的代码分析,其实职责链的代码还是相对容易的,最后都是调用自己的实现类来完成最终的业务操作,这里需要注意的是如果自己的handler实现类没有@ChannelHandler.Sharable注解,那么多个客户端来连接的时候会报xxx is not a @Sharable handler, so can't be added or removed multiple times的错误。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值