Netty Pipeline源码分析(2)

本文详细分析了Netty Pipeline的inBound和outBound事件传播机制,包括SimpleChannelInboundHandler的使用,异常处理实践,以及Pipeline的创建、节点管理和异常处理。通过示例和源码解读,阐述了事件从HeadContext传播到TailContext的过程,强调了ChannelHandler添加顺序的重要性,同时提供了最佳异常处理建议。
摘要由CSDN通过智能技术生成

原文链接:https://wangwei.one/posts/netty-pipeline-source-analyse-2.html

前面 ,我们分析了Netty Pipeline的初始化及节点添加与删除逻辑。接下来,我们将来分析Pipeline的事件传播机制。

Netty版本:4.1.30

inBound事件传播

示例

我们通过下面这个例子来演示Netty Pipeline的事件传播机制。

public class NettyPipelineInboundExample {
   

    public static void main(String[] args) {
   
        EventLoopGroup group = new NioEventLoopGroup(1);
        ServerBootstrap strap = new ServerBootstrap();
        strap.group(group)
                .channel(NioServerSocketChannel.class)
                .localAddress(new InetSocketAddress(8888))
                .childOption(ChannelOption.TCP_NODELAY, true)
                .childHandler(new ChannelInitializer<SocketChannel>() {
   
                    @Override
                    protected void initChannel(SocketChannel ch) throws Exception {
   
                        ch.pipeline().addLast(new InboundHandlerA());
                        ch.pipeline().addLast(new InboundHandlerB());
                        ch.pipeline().addLast(new InboundHandlerC());
                    }
                });
        try {
   
            ChannelFuture future = strap.bind().sync();
            future.channel().closeFuture().sync();
        } catch (InterruptedException e) {
   
            e.printStackTrace();
        } finally {
   
            group.shutdownGracefully();
        }
    }
}

class InboundHandlerA extends ChannelInboundHandlerAdapter {
   

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
   
        System.out.println("InboundHandler A : " + msg);
        // 传播read事件到下一个channelhandler
        ctx.fireChannelRead(msg);
    }

}

class InboundHandlerB extends ChannelInboundHandlerAdapter {
   

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
   
        System.out.println("InboundHandler B : " + msg);
        // 传播read事件到下一个channelhandler
        ctx.fireChannelRead(msg);
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
   
        // channel激活,触发channelRead事件,从pipeline的heandContext节点开始往下传播
        ctx.channel().pipeline().fireChannelRead("Hello world");
    }
}

class InboundHandlerC extends ChannelInboundHandlerAdapter {
   

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
   
        System.out.println("InboundHandler C : " + msg);
        // 传播read事件到下一个channelhandler
        ctx.fireChannelRead(msg);
    }
}

源码

通过 telnet 来连接上面启动好的netty服务,触发channel active事件:

$ telnet 127.0.0.1 8888

按照InboundHandlerA、InboundHandlerB、InboundHandlerC的添加顺序,控制台输出如下信息:

InboundHandler A : Hello world
InboundHandler B : Hello world
InboundHandler C : Hello world

若是调用它们的添加顺序,则会输出对应顺序的信息,e.g:

...

ch.pipeline().addLast(new InboundHandlerB());
ch.pipeline().addLast(new InboundHandlerA());
ch.pipeline().addLast(new InboundHandlerC());

...

输出如下信息:

InboundHandler B : Hello world
InboundHandler A : Hello world
InboundHandler C : Hello world

源码分析

强烈建议 下面的流程,自己通过IDE的Debug模式来分析

待netty启动成功,通过telnet连接到netty,然后通过telnet终端输入任意字符(这一步才开启Debug模式),进入Debug模式。

触发channel read事件,从下面的入口开始调用

public class DefaultChannelPipeline implements ChannelPipeline {
   
	
	...
	
    // 出发channel read事件
	@Override
    public final ChannelPipeline fireChannelRead(Object msg) {
   
        // 从head节点开始往下传播read事件
        AbstractChannelHandlerContext.invokeChannelRead(head, msg);
        return this;
    }
	
	...

}

调用 AbstractChannelHandlerContext 中的 invokeChannelRead(head, msg) 接口:

abstract class AbstractChannelHandlerContext extends DefaultAttributeMap
        implements ChannelHandlerContext, ResourceLeakHint {
   

	...

	// 调用channel read
    static void invokeChannelRead(final AbstractChannelHandlerContext next, Object msg) {
   
        // 获取消息
        final Object m = next.pipeline.touch(ObjectUtil.checkNotNull(msg, "msg"), next);
        // 获取 EventExecutor
        EventExecutor executor = next.executor();
        // true
        if (executor.inEventLoop()) {
   
        	// 调用下面的invokeChannelRead接口:invokeChannelRead(Object msg)
            next.invokeChannelRead(m);
        } else {
   
            executor.execute(new Runnable() {
   
                @Override
                public void run() {
   
                    next.invokeChannelRead(m);
                }
            });
        }
    }
    
    private void invokeChannelRead(Object msg) {
   
        if (invokeHandler()) {
   
            try {
   
            	// handler():获取当前遍历到的channelHandler,第一个为HeandContext,最后为TailContext
            	// 调用channel handler的channelRead接口
                ((ChannelInboundHandler) handler
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值