Netty nio 数据流入流出分析

要分析流程最直接就是通过源码断点调试分析了,我这里就直接在handler里的channelRead0里抛出异常,查看下执行的堆栈。

java.lang.Exception: xxxxxxxxxxxxxxxxxxxxxxxx
    at com.test.MsgHandler.channelRead0(MsgHandler.java:26)
    at com.test.MsgHandler.channelRead0(MsgHandler.java:1)
    at io.netty.channel.SimpleChannelInboundHandler.channelRead(SimpleChannelInboundHandler.java:105)
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362)
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348)
    at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:340)
    at io.netty.handler.codec.MessageToMessageDecoder.channelRead(MessageToMessageDecoder.java:102)
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362)
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348)
    at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:340)
    at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1334)
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362)
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348)
    at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:926)
    at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:134)
    at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:644)
    at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:579)
    at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:496)
    at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:458)
    at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:858)
    at io.netty.util.concurrent.DefaultThreadFactory$DefaultRunnableDecorator.run(DefaultThreadFactory.java:138)
    at java.lang.Thread.run(Thread.java:745)

Netty 服务器端配置

    public void initServer() throws Exception{
         ServerBootstrap b = new ServerBootstrap(); // (2)
         b.group(bossGroup, workerGroup)
          .channel(NioServerSocketChannel.class) // (3)
          .childHandler(new ChannelInitializer<SocketChannel>() { // (4)
              @Override
              public void initChannel(SocketChannel ch) throws Exception {
                  ch.pipeline().addLast(new StringDecoder(CHARSET));
                  ch.pipeline().addLast(new StringEncoder(CHARSET));
                  ch.pipeline().addLast(new MsgHandler());
              }
          });
         b.bind(9191).sync();
    }

一个解码和编码 再加一个自定义的Handler 结合上面的异常堆栈来分析这次数据的流入过程

at io.netty.util.concurrent.DefaultThreadFactory$DefaultRunnableDecorator.run

从异常堆栈的最下面开始分析。这个类的源代码是

    private static final class DefaultRunnableDecorator implements Runnable {

        private final Runnable r;

        DefaultRunnableDecorator(Runnable r) {
            this.r = r;
        }

        @Override
        public void run() {
            try {
                r.run();
            } finally {
                FastThreadLocal.removeAll();
            }
        }
    }

可以看出这个类实现了Runnable接口,对Runnable做了一层封装。finally 执行对线程绑定的变量释放

at io.netty.util.concurrent.SingleThreadEventExecutor$5.run

执行具体的业务逻辑是由线程来执行的 这里可以看出使用的是SingleThreadEventExecutor抽象类,
run方法的实现是由NioEventLoop来重写的,run方法里调用了processSelectedKeys();在调用
processSelectedKeysOptimized();这个方法里发现是Nio的连接 就走了 processSelectedKey(k, (AbstractNioChannel) a);方法

@Override
        public final void read() {
            final ChannelConfig config = config();
            final ChannelPipeline pipeline = pipeline();
            final ByteBufAllocator allocator = config.getAllocator();
            final RecvByteBufAllocator.Handle allocHandle = recvBufAllocHandle();
            allocHandle.reset(config);

            ByteBuf byteBuf = null;
            boolean close = false;
            try {
                do {
                    byteBuf = allocHandle.allocate(allocator);
                    allocHandle.lastBytesRead(doReadBytes(byteBuf));
                    if (allocHandle.lastBytesRead() <= 0) {
                        // nothing was read. release the buffer.
                        byteBuf.release();
                        byteBuf = null;
                        close = allocHandle.lastBytesRead() < 0;
                        break;
                    }

                    allocHandle.incMessagesRead(1);
                    readPending = false;
                    pipeline.fireChannelRead(byteBuf);
                    byteBuf = null;
                } while (allocHandle.continueReading());

                allocHandle.readComplete();
                pipeline.fireChannelReadComplete();

                if (close) {
                    closeOnRead(pipeline);
                }
            } catch (Throwable t) {
                handleReadException(pipeline, byteBuf, t, close, allocHandle);
            } finally {
                // Check if there is a readPending which was not processed yet.
                // This could be for two reasons:
                // * The user called Channel.read() or ChannelHandlerContext.read() in channelRead(...) method
                // * The user called Channel.read() or ChannelHandlerContext.read() in channelReadComplete(...) method
                //
                // See https://github.com/netty/netty/issues/2254
                if (!readPending && !config.isAutoRead()) {
                    removeReadOp();
                }
            }
        }

这里开始读取ByteBuf数据,调用 pipeline.fireChannelRead(byteBuf);方法抛出读取事件,这里就会开始从
pipeline里的头开始处理 一直到用户端的handler结束了。

 @Override
    public final ChannelPipeline fireChannelRead(Object msg) {
        AbstractChannelHandlerContext.invokeChannelRead(head, msg);
        return this;
    }

DefaultChannelPipeline里记录了第一个ChannelHandlerContext head
一个handler 有一个对应的ChannelHandlerContext context 保留了一些变量。
ChannelHandlerContext 调用invokeChannelRead 之后
((ChannelInboundHandler) handler()).channelRead(this, msg);获取对应的handler来执行channelRead方法。

@Override
        public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
            ctx.fireChannelRead(msg);
        }

这里可以看到head里没有啥处理直接把这个消息通过ChannelHandlerContext 继续往下传递。

@Override
    public ChannelHandlerContext fireChannelRead(final Object msg) {
        invokeChannelRead(findContextInbound(), msg);
        return this;
    }

ChannelHandlerContext 就开始查找自己的下一个ChannelHandlerContext 。因为在in的链表上自己加了一个StringDecoder 所以这里应该执行到解码阶段

 @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        CodecOutputList out = CodecOutputList.newInstance();
        try {
            if (acceptInboundMessage(msg)) {
                @SuppressWarnings("unchecked")
                I cast = (I) msg;
                try {
                    decode(ctx, cast, out);
                } finally {
                    ReferenceCountUtil.release(cast);
                }
            } else {
                out.add(msg);
            }
        } 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.getUnsafe(i));
            }
            out.recycle();
        }
    }

在这里调用子类实现的decode(ctx, cast, out);方法 解码成一个完整的自定义的对象String 然后通过fireChannelRead 抛出到下一个ChannelHandlerContext中去,下一个就是我们自定义的handler了。

@Override
    protected void channelRead0(ChannelHandlerContext arg0, String str) throws Exception {
        // TODO Auto-generated method stub
        arg0.writeAndFlush(str).addListener(new GenericFutureListener<Future<? super Void>>() {
            @Override
            public void operationComplete(Future<? super Void> future) throws Exception {
                // TODO Auto-generated method stub
                if(future.isSuccess()){
                    System.out.println();
                }
            }
        });
        i++;
        if(i >= 10000){
            throw new Exception("xxxxxxxxxxxxxxxxxxxxxxxx");
        }
    }

这里就收到解码器传过来的完整对象。这里添加了一个GenericFutureListener ,Netty不推荐,也不希望使用同步调用Future的实现来等待结果的返回,因为这样对性能影响太大。所以调用operationComplete是由io线程处理完之后通过事件再主动通知回来。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值