netty 数据如何传到worker线程&handler链式调用

https://github.com/wuyinxian124/nettybook2.git
使用com.phei.netty.frame.delimiter.EchoServer做实验
使用这个工程时要处理一下pom的包冲突,否则调试的时候回显"alternative"之类让你选择代码的操作
俩问题
1、数据如何传到worker线程
2、netty中如何实现多个handler链式调用

数据如何传到worker线程

        @Override
        public void read() {
            ...省略很多行...
            ByteBuf byteBuf = null;
            int messages = 0;
            boolean close = false;
            try {
                int byteBufCapacity = allocHandle.guess();
                int totalReadAmount = 0;
                do {
                    byteBuf = allocator.ioBuffer(byteBufCapacity);//分配bytebuf
                    int writable = byteBuf.writableBytes();
                    int localReadAmount = doReadBytes(byteBuf);//将数据写入新分配的bytebuf
                    ...省略很多行...
        }

在这里插入图片描述
调用堆栈

read:119, AbstractNioByteChannel$NioByteUnsafe (io.netty.channel.nio)
processSelectedKey:485, NioEventLoop (io.netty.channel.nio)
processSelectedKeysOptimized:452, NioEventLoop (io.netty.channel.nio)
run:346, NioEventLoop (io.netty.channel.nio)
run:794, SingleThreadEventExecutor$5 (io.netty.util.concurrent)
run:748, Thread (java.lang)

在这里插入图片描述
上图中的pipeline.fireChannelRead触发了handler的链式调用

netty中如何实现多个handler链式调用

在这里插入图片描述

上图中DefaultChannelHandlerContext的pipeline字段指向DefaultChannelPipeline。因为图已经很乱了,所以就不连接pipeline和DefaultChannelPipeline

创建

使用

接收远程数据
io.netty.channel.ChannelPipeline中的方法开始传播事件。
比如服务端接收数据时调用堆栈如下

channelRead:37, EchoServerHandler (com.phei.netty.frame.delimiter)
invokeChannelReadNow:74, ChannelHandlerInvokerUtil (io.netty.channel)
invokeChannelRead:138, DefaultChannelHandlerInvoker (io.netty.channel)
fireChannelRead:320, DefaultChannelHandlerContext (io.netty.channel)
channelRead:103, MessageToMessageDecoder (io.netty.handler.codec)
invokeChannelReadNow:74, ChannelHandlerInvokerUtil (io.netty.channel)
invokeChannelRead:138, DefaultChannelHandlerInvoker (io.netty.channel)
fireChannelRead:320, DefaultChannelHandlerContext (io.netty.channel)
channelRead:154, ByteToMessageDecoder (io.netty.handler.codec)
invokeChannelReadNow:74, ChannelHandlerInvokerUtil (io.netty.channel)
invokeChannelRead:138, DefaultChannelHandlerInvoker (io.netty.channel)
fireChannelRead:320, DefaultChannelHandlerContext (io.netty.channel)
fireChannelRead:846, DefaultChannelPipeline (io.netty.channel)
read:127, AbstractNioByteChannel$NioByteUnsafe (io.netty.channel.nio)
processSelectedKey:485, NioEventLoop (io.netty.channel.nio)
processSelectedKeysOptimized:452, NioEventLoop (io.netty.channel.nio)
run:346, NioEventLoop (io.netty.channel.nio)
run:794, SingleThreadEventExecutor$5 (io.netty.util.concurrent)
run:748, Thread (java.lang)

上面的调用堆栈中有几点需要注意。
1、有些调用不断出现

invokeChannelReadNow:74, ChannelHandlerInvokerUtil (io.netty.channel)
invokeChannelRead:138, DefaultChannelHandlerInvoker (io.netty.channel)
fireChannelRead:320, DefaultChannelHandlerContext (io.netty.channel)

2、调用链的起始位置在fireChannelRead:846, DefaultChannelPipeline (io.netty.channel)

    @Override
    public ChannelPipeline fireChannelRead(Object msg) {
        head.fireChannelRead(msg);//第846行
        return this;
    }

3、若要事件传递下去有俩条件
首先需要handler主动调用fireChannelRead方法
比如在ByteToMessageDecoder中

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        if (msg instanceof ByteBuf) {
            ....................
            try {
                .........
            } catch (DecoderException e) {
                throw e;
            } catch (Throwable t) {
                throw new DecoderException(t);
            } finally {
                .........
                for (int i = 0; i < size; i ++) {
                    ctx.fireChannelRead(out.get(i));
                }
                out.recycle();
            }
        } else {
            ctx.fireChannelRead(msg);
        }
    }

或是MessageToMessageDecoder

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        RecyclableArrayList out = RecyclableArrayList.newInstance();
        try {
            ....................
        } catch (DecoderException e) {
            throw e;
        } catch (Exception e) {
            throw new DecoderException(e);
        } finally {
            int size = out.size();
            for (int i = 0; i < size; i ++) {
                ctx.fireChannelRead(out.get(i));
            }
            out.recycle();
        }
    }

比如EchoServerHandler这样不调用fireChannelRead,事件就无法传递下去

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg)
	    throws Exception {
	String body = (String) msg;
	System.out.println("This is " + ++counter + " times receive client : ["
		+ body + "]");
	body += "$_";
	ByteBuf echo = Unpooled.copiedBuffer(body.getBytes());
	ctx.writeAndFlush(echo);
    }

如果在EchoServerHandler中调用fireChannelRead,事件将传递给尾部的DefaultChannelHandlerContext。虽然没实验检测过,但是我觉得直接传递给尾部的DefaultChannelHandlerContext也不会有问题。

发送数据到远程

writeAndFlush:430, DefaultChannelHandlerContext (io.netty.channel)
writeAndFlush:438, DefaultChannelHandlerContext (io.netty.channel)
channelRead:42, EchoServerHandler (com.phei.netty.frame.delimiter)
invokeChannelReadNow:74, ChannelHandlerInvokerUtil (io.netty.channel)
invokeChannelRead:138, DefaultChannelHandlerInvoker (io.netty.channel)
fireChannelRead:320, DefaultChannelHandlerContext (io.netty.channel)
channelRead:103, MessageToMessageDecoder (io.netty.handler.codec)
invokeChannelReadNow:74, ChannelHandlerInvokerUtil (io.netty.channel)
invokeChannelRead:138, DefaultChannelHandlerInvoker (io.netty.channel)
fireChannelRead:320, DefaultChannelHandlerContext (io.netty.channel)
channelRead:154, ByteToMessageDecoder (io.netty.handler.codec)
invokeChannelReadNow:74, ChannelHandlerInvokerUtil (io.netty.channel)
invokeChannelRead:138, DefaultChannelHandlerInvoker (io.netty.channel)
fireChannelRead:320, DefaultChannelHandlerContext (io.netty.channel)
fireChannelRead:846, DefaultChannelPipeline (io.netty.channel)
read:127, AbstractNioByteChannel$NioByteUnsafe (io.netty.channel.nio)
processSelectedKey:485, NioEventLoop (io.netty.channel.nio)
processSelectedKeysOptimized:452, NioEventLoop (io.netty.channel.nio)
run:346, NioEventLoop (io.netty.channel.nio)
run:794, SingleThreadEventExecutor$5 (io.netty.util.concurrent)
run:748, Thread (java.lang)

由于本例中没有与MASK_WRITEMASK_FLUSH匹配的handler,所以在io.netty.channel.DefaultChannelHandlerContext#writeAndFlush(Object msg, ChannelPromise promise)中直接调用了pipeline链表中的头context

    @Override
    public ChannelFuture writeAndFlush(Object msg, ChannelPromise promise) {
        DefaultChannelHandlerContext next;
        next = findContextOutbound(MASK_WRITE);//没有找到MASK_WRITE,直接返回了pipeline链表中的头节点
        next.invoker.invokeWrite(next, msg, promise);
        next = findContextOutbound(MASK_FLUSH);//没有找到MASK_FLUSH,直接返回了pipeline链表中的头节点
        next.invoker.invokeFlush(next);//里面调用了io.netty.channel.socket.nio.NioSocketChannel#doWriteBytes
        return promise;
    }

1、如果实现了MASK_WRITEMASK_FLUSH匹配的handler,应该可以产生调用链。
DefaultChannelHandlerContext中skipFlags字段用于标识此上下文中的handler某些函数是否跳过。比如对于TailHandler,由于其write方法中标有@Skip注释,在findContextOutbound中就会直接跳过所属上下文。而对于HeadHandler其write方法没有标注@Skip注释,所以在findContextOutbound中可以被返回。
2、上面的代码中使用的是findContextOutbound,如果是"接收远程数据",则是findContextInbound。"in"和"out"可能和pipeline中链表调用顺序有关。"in"从头到尾,"out"从尾到头
3、注意上面代码使用了两次findContextOutbound分别找了不同的handler。不过不会产生树状调用链,因为代码中已经调用了invokeWriteinvokeFlush,这俩函数中不会直接或间接调用invokeWriteAndFlushNow这类东西

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值