Pipeline之Inbound事件传播

耗尽温柔 提交于 2020-01-25 21:45:32

ChannelHandler继承体系

ChannelHandler:所有逻辑处理器的抽象。

public interface ChannelHandler {

    // handler添加完成回调
    void handlerAdded(ChannelHandlerContext ctx) throws Exception;

    // handler删除完成回调
    void handlerRemoved(ChannelHandlerContext ctx) throws Exception;

    // 异常回调
    @Deprecated
    void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception;

    // 指定handler为共享handler,可重复添加到pipeline
    @Inherited
    @Documented
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @interface Sharable {
        // no value
    }
}

 ChannelInboundHandler:基于ChannelHandler,扩展了一些Inbound事件。

public interface ChannelInboundHandler extends ChannelHandler {

    // channel注册回调,当channel注册到NioEventLoop对应的Selector是触发该回调
    void channelRegistered(ChannelHandlerContext ctx) throws Exception;

    // channel注销回调
    void channelUnregistered(ChannelHandlerContext ctx) throws Exception;

    // channel激活回调
    void channelActive(ChannelHandlerContext ctx) throws Exception;

    // channel失效回调
    void channelInactive(ChannelHandlerContext ctx) throws Exception;

    // 读到数据时执行该方法
    void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception;

    // 数据读完的回调
    void channelReadComplete(ChannelHandlerContext ctx) throws Exception;

    // 用户自定义的触发事件
    void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception;

    // 改变channel的可写状态
    void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception;

    // 异常捕获
    @Override
    @SuppressWarnings("deprecation")
    void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception;
}

事件传播过程

定义三个自定义的InBoundHandler类:

InBoundHandlerA

public class InBoundHandlerA extends ChannelInboundHandlerAdapter {

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        System.out.println("InBoundHandlerA: " + msg);
        ctx.fireChannelRead(msg);
    }
}

InBoundHandlerB 

public class InBoundHandlerB extends ChannelInboundHandlerAdapter {
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        System.out.println("InBoundHandlerB: " + msg);
        ctx.fireChannelRead(msg);
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) {
        System.out.println("InBoundHandlerB: channelActive");
        ctx.channel().pipeline().fireChannelRead("hello world");
    }
}

 InBoundHandlerC

public class InBoundHandlerC extends ChannelInboundHandlerAdapter {
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        System.out.println("InBoundHandlerC: " + msg);
        ctx.fireChannelRead(msg);
    }
}

 在启动类中添加这三个handler


    public static void main(String[] args) throws Exception {
        // 配置服务端的 NIO线程组
        // boss线程组用于网络事件的监听
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        // worker线程组用于SocketChannel的网络读写
        EventLoopGroup workerGroup = new NioEventLoopGroup();

        try {
            // NIO服务端的辅助启动类,目的是降低服务端开发的复杂度
            ServerBootstrap b = new ServerBootstrap();
            // 配置两大线程组
            b.group(bossGroup, workerGroup)
                    // 配置服务端channel,在服务启动时通过反射创建channel实例
                    .channel(NioServerSocketChannel.class)
                    // 配置TCP基本属性
                    .childOption(ChannelOption.TCP_NODELAY, true)
                    // 客户端创建连接时绑定基本属性
                    .childAttr(AttributeKey.newInstance("childAttr"), "childAttrValue")
                    // 配置服务端启动过程逻辑处理器
                    .handler(new ServerHandler())
                    // 配置业务处理链 handler pipeline
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        public void initChannel(SocketChannel ch) {
                            // Inbound事件传播
                            ch.pipeline().addLast(new InBoundHandlerA());
                            ch.pipeline().addLast(new InBoundHandlerB());
                            ch.pipeline().addLast(new InBoundHandlerC());

                        }
                    });

            // 前面都是一些属性配置的逻辑,真正的服务端启动在此处开始
            // 绑定端口,正式启动server端服务
            ChannelFuture f = b.bind(8888).sync();
            // 同步等待,直至服务端监听端口关闭
            f.channel().closeFuture().sync();
        } finally {
            // 优雅退出,释放线程池资源
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }

启动服务端,通过“telnet 127.0.0.1 8888”测试一下调用结果:

fireChannelRead方法源码 

    @Override
    public final ChannelPipeline fireChannelRead(Object msg) {
        AbstractChannelHandlerContext.invokeChannelRead(head, msg);
        return this;
    }
    
    @Override
    public ChannelHandlerContext fireChannelRead(final Object msg) {
        // findContextInbound方法遍历链表,寻找下一个inbound节点,传播事件
        invokeChannelRead(findContextInbound(), 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 {
                // 调用channelRead方法
                ((ChannelInboundHandler) handler()).channelRead(this, msg);
            } catch (Throwable t) {
                notifyHandlerException(t);
            }
        } else {
            fireChannelRead(msg);
        }
    }

执行过程总结: 

因为InBoundHandlerB实现了channelActive方法,所以在channel被激活之后会首先调用该方法。

在channelActive方法中,是通过 “ctx.channel().pipeline().fireChannelRead("hello world");” 从head节点开始进行事件传播,传播顺序为:head->A->B->C->tail,与添加时的顺序是一样的。

而在channelRead方法中,是通过“ctx.fireChannelRead(msg);”从当前节点开始向下传播,直到tail。

fireChannelRead是从head节点或当前节点开始传播,先找到下一个inbound节点,然后再执行其channelRead方法,最后由tail节点做一些收尾工作并释放。


SimpleChannelInboundHandler

在上面了解到的事件传播机制中,资源的释放都是由tail节点完成的。如果由于某种原因,资源没有被传递到tail节点,这时就需要节点自己释放资源。

SimpleChannelInboundHandler提供了channelRead模板方法,封装了资源释放的逻辑。

当我们自定义的handler节点需要自己释放资源时,可以通过继承SimpleChannelInboundHandler重写channelRead0来实现。

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        boolean release = true;
        try {
            if (acceptInboundMessage(msg)) {
                @SuppressWarnings("unchecked")
                I imsg = (I) msg;
                // 业务handler实现
                channelRead0(ctx, imsg);
            } else {
                release = false;
                ctx.fireChannelRead(msg);
            }
        } finally {
            if (autoRelease && release) {
                ReferenceCountUtil.release(msg);
            }
        }
    }

    protected abstract void channelRead0(ChannelHandlerContext ctx, I msg) throws Exception;

 

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!