Netty接收新数据原理

从上一节可以知道,接收新连接的处理是在NioEventLooprun() 方法中,里面有个方法叫做processSelectedKey(),里面对于四种事件都有相应的判断并交给ChannelUnsafe属性来处理。

processSelectedKeys()

public final class NioEventLoop extends SingleThreadEventLoop {
    private void processSelectedKeys() {
        if (selectedKeys != null) {
            // 监听到事件, selectedKeys肯定不为null
            processSelectedKeysOptimized();
        } else {
            processSelectedKeysPlain(selector.selectedKeys());
        }
    }

    private void processSelectedKeysOptimized() {
        for (int i = 0; i < selectedKeys.size; ++i) {
            final SelectionKey k = selectedKeys.keys[i];
            // null out entry in the array to allow to have it GC'ed once the Channel close
            // See https://github.com/netty/netty/issues/2363
            selectedKeys.keys[i] = null;
            // 取出SelectionKey中的附件
            // Java原生的java.nio.channels.SocketChannel把NioSocketChannel作为附件注册到Selector
            // 所以这里取出来的就是Netty中的NioSocketChannel
            final Object a = k.attachment();

            if (a instanceof AbstractNioChannel) {
                // 走的是这里
                processSelectedKey(k, (AbstractNioChannel) a);
            } else {
                @SuppressWarnings("unchecked")
                NioTask<SelectableChannel> task = (NioTask<SelectableChannel>) a;
                processSelectedKey(k, task);
            }

            if (needsToSelectAgain) {
                // null out entries in the array to allow to have it GC'ed once the Channel close
                // See https://github.com/netty/netty/issues/2363
                selectedKeys.reset(i + 1);

                selectAgain();
                i = -1;
            }
        }
    }

    private void processSelectedKey(SelectionKey k, AbstractNioChannel ch) {
        // 这里取出来的 NioUnsafe 是 io.netty.channel.nio.AbstractNioByteChannel.NioByteUnsafe
        final AbstractNioChannel.NioUnsafe unsafe = ch.unsafe();
        if (!k.isValid()) {
            // 这里是一些异常校验, 省略......
        }
        try {
            // 读取事件类型
            int readyOps = k.readyOps();
            // 如果是 CONNECT 事件
            if ((readyOps & SelectionKey.OP_CONNECT) != 0) {
                // remove OP_CONNECT as otherwise Selector.select(..) will always return without blocking
                // See https://github.com/netty/netty/issues/924
                int ops = k.interestOps();
                ops &= ~SelectionKey.OP_CONNECT;
                k.interestOps(ops);

                unsafe.finishConnect();
            }

            // 如果是 WRITE 事件
            if ((readyOps & SelectionKey.OP_WRITE) != 0) {
                // Call forceFlush which will also take care of clear the OP_WRITE once there is nothing left to write
                ch.unsafe().forceFlush();
            }

            // 如果是 READ 事件或者 ACCEPT 事件
            // ACCEPT: 有新连接
            // READ: 有可读数据
            if ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0) {
                unsafe.read();
            }
        } catch (CancelledKeyException ignored) {
            unsafe.close(unsafe.voidPromise());
        }
    }
}

NioByteUnsafe#read()

来到NioByteUnsaferead方法

public abstract class AbstractNioByteChannel extends AbstractNioChannel {
    protected class NioByteUnsafe extends AbstractNioUnsafe {
        @Override
        public final void read() {
            final ChannelConfig config = config();
            if (shouldBreakReadReady(config)) {
                clearReadPending();
                return;
            }
            final ChannelPipeline pipeline = pipeline();
            // ByteBuf分配器
            final ByteBufAllocator allocator = config.getAllocator();
            final RecvByteBufAllocator.Handle allocHandle = recvBufAllocHandle();
            allocHandle.reset(config);

            ByteBuf byteBuf = null;
            boolean close = false;
            try {
                do {
                    // ① 通过allocator创建一个ByteBuf
                    byteBuf = allocHandle.allocate(allocator);
                    // ② 读取数据到ByteBuf中
                    allocHandle.lastBytesRead(doReadBytes(byteBuf));
                    if (allocHandle.lastBytesRead() <= 0) {
                        // nothing was read. release the buffer.
                        byteBuf.release();
                        byteBuf = null;
                        close = allocHandle.lastBytesRead() < 0;
                        if (close) {
                            // There is nothing left to read as we received an EOF.
                            readPending = false;
                        }
                        break;
                    }

                    allocHandle.incMessagesRead(1);
                    readPending = false;
                    // ③ 触发channelRead()
                    // 数据在ChannelPipeline中传递
                    pipeline.fireChannelRead(byteBuf);
                    byteBuf = null;
                } while (allocHandle.continueReading());

                allocHandle.readComplete();
                // ④ 触发channelReadComplete()
                pipeline.fireChannelReadComplete();

                if (close) {
                    closeOnRead(pipeline);
                }
            } catch (Throwable t) {
                handleReadException(pipeline, byteBuf, t, close, allocHandle);
            } finally {
                if (!readPending && !config.isAutoRead()) {
                    removeReadOp();
                }
            }
        }
    }
}

① 通过allocHandle创建一个ByteBuf,这里默认创建的是io.netty.buffer.PooledUnsafeDirectByteBuf

② 读取数据到ByteBuf中,ByteBuf内部其实也是对java.nio.ByteBuffer的封装,底层也是从java.nio.channels.SocketChannel读取数据到ByteBuffer

③ 触发ChannelPipeline中的ChannelHandlerchannelRead()方法,即读取到的数据会在ChannelPipeline中传播。

④ 触发ChannelPipeline中的ChannelHandlerchannelReadComplete()方法。

doReadBytes(byteBuf)

看看doReadBytes(byteBuf)内部是如何把数据读到ByteBuf

public class NioSocketChannel extends AbstractNioByteChannel implements io.netty.channel.socket.SocketChannel {
    @Override
    protected int doReadBytes(ByteBuf byteBuf) throws Exception {
        final RecvByteBufAllocator.Handle allocHandle = unsafe().recvBufAllocHandle();
        // 设置可读取的长度
        allocHandle.attemptedBytesRead(byteBuf.writableBytes());
        // 调用ByteBuf的writeBytes()方法, 写入数据到ByteBuf
        // 第一个参数是Java原生的java.nio.channels.SocketChannel, 第二个参数是可读取的长度
        return byteBuf.writeBytes(javaChannel(), allocHandle.attemptedBytesRead());
    }
}

public abstract class AbstractByteBuf extends ByteBuf {
    @Override
    public int writeBytes(ScatteringByteChannel in, int length) throws IOException {
        // 确保容量足够, 不够会扩容
        ensureWritable(length);
         // 第一个参数是写入的位置 第二参数是SocketChannel 第三个参数可写入的长度
        int writtenBytes = setBytes(writerIndex, in, length);
        if (writtenBytes > 0) {
            writerIndex += writtenBytes;
        }
        return writtenBytes;
    }
}

abstract class PooledByteBuf<T> extends AbstractReferenceCountedByteBuf {
    ByteBuffer tmpNioBuf;
    @Override
    public final int setBytes(int index, ScatteringByteChannel in, int length) throws IOException {
        try {
            // 调用Java原生SocketChannel的read()方法
            // read()方法的参数是Java原生的ByteBuffer
            return in.read(internalNioBuffer(index, length));
        } catch (ClosedChannelException ignored) {
            return -1;
        }
    }
    @Override
    public final ByteBuffer internalNioBuffer(int index, int length) {
        checkIndex(index, length);
        return _internalNioBuffer(index, length, false);
    }

    final ByteBuffer _internalNioBuffer(int index, int length, boolean duplicate) {
        index = idx(index);
        // 这里的duplicate为false
        ByteBuffer buffer = duplicate ? newInternalNioBuffer(memory) : internalNioBuffer();
        buffer.limit(index + length).position(index);
        return buffer;
    }

    protected final ByteBuffer internalNioBuffer() {
        ByteBuffer tmpNioBuf = this.tmpNioBuf;
        if (tmpNioBuf == null) {
            this.tmpNioBuf = tmpNioBuf = newInternalNioBuffer(memory);
        } else {
            tmpNioBuf.clear();
        }
        return tmpNioBuf;
    }

}

最后返回的实际上是this.tmpNioBuf,它的类型的是java.nio.ByteBuffer
因此,NettyByteBuf底层也是包装了Java原生的ByteBuffer


pipeline.fireChannelRead(byteBuf)

接着看数据如何在ChannelPipeline中传播,查看pipeline.fireChannelRead(byteBuf)内部

public class DefaultChannelPipeline implements ChannelPipeline {
    // ①
    @Override
    public final ChannelPipeline fireChannelRead(Object msg) {
        // 从HeadContext开始调用
        AbstractChannelHandlerContext.invokeChannelRead(head, msg);
        return this;
    }
}

abstract class AbstractChannelHandlerContext implements ChannelHandlerContext, ResourceLeakHint {
    // ②
    static void invokeChannelRead(final AbstractChannelHandlerContext next, Object msg) {
        // 这里返回的还是msg
        final Object m = next.pipeline.touch(ObjectUtil.checkNotNull(msg, "msg"), next);
        EventExecutor executor = next.executor();
        // 判断当前线程是不是当前EventLoop中的线程
        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 {
                // 调用里面的ChannelHandler的channelRead()方法
                ((ChannelInboundHandler) handler()).channelRead(this, msg);
            } catch (Throwable t) {
                invokeExceptionCaught(t);
            }
        } else {
            fireChannelRead(msg);
        }
    }
}

public class DefaultChannelPipeline implements ChannelPipeline {
    final class HeadContext extends AbstractChannelHandlerContext
                implements ChannelOutboundHandler, ChannelInboundHandler {
        // ④
        @Override
        public void channelRead(ChannelHandlerContext ctx, Object msg) {
            // 调用ChannelHandlerContext的fireChannelRead()方法
            // 触发下一个Context中Handler的调用
            ctx.fireChannelRead(msg);
        }
    }
}

abstract class AbstractChannelHandlerContext implements ChannelHandlerContext, ResourceLeakHint {
    // ⑤ 
    @Override
    public ChannelHandlerContext fireChannelRead(final Object msg) {
        // findContextInbound是寻找下一个ChannelInboundHandler
        invokeChannelRead(findContextInbound(MASK_CHANNEL_READ), 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);
                }
            });
        }
    }

}

从上面①②③④⑤可以知道,对于入站消息:

  • ChannelPipeline是从HeadContext开始执行的

  • 同一个Channel的所有ChannelHandler的执行都会放在EventLoop中执行,所以它们是线程安全的

  • 调用ctx.fireChannelRead(msg)即可触发下一个ChannelHandler的执行

对于我们的EchoServer程序,它里面有四个Handler
HeadContext<=>LoggineHandler<=>EchoServerHandler<=>TailContextEchoServerHandlerchannelRead()方法为:

public class EchoServerHandler extends ChannelInboundHandlerAdapter {
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
         // 读取数据后写回客户端
         ctx.write(msg);
//         if (msg instanceof ByteBuf) {
//             ByteBuf buf = (ByteBuf) msg;
//             System.out.println(ctx.channel() + "--->" + buf.toString(CharsetUtil.UTF_8));
//         }
    }
}

这里并没有调用ctx.fireChannelRead(msg),所以流程并不会走到TailContext

HeadContext,不仅是一个ChannelHandlerContext,也是一个ChannelInboundHandler,同时也是一个ChannelOutboundHandler

TailContext,不仅是一个ChannelHandlerContext,同时也是一个ChannelInboundHandler

看看TailContextchannelRead(ChannelHandlerContext ctx, Object msg)方法做了什么:

public class DefaultChannelPipeline implements ChannelPipeline {
    final class TailContext extends AbstractChannelHandlerContext implements ChannelInboundHandler {
        @Override
        public void channelRead(ChannelHandlerContext ctx, Object msg) {
            onUnhandledInboundMessage(ctx, msg);
        }
    }
    protected void onUnhandledInboundMessage(ChannelHandlerContext ctx, Object msg) {
        onUnhandledInboundMessage(msg);
        if (logger.isDebugEnabled()) {
            logger.debug("Discarded message pipeline : {}. Channel : {}.",
                         ctx.pipeline().names(), ctx.channel());
        }
    }
    protected void onUnhandledInboundMessage(Object msg) {
        try {
            logger.debug(
                    "Discarded inbound message {} that reached at the tail of the pipeline. " +
                            "Please check your pipeline configuration.", msg);
        } finally {
            ReferenceCountUtil.release(msg);
        }
    }
}

可以看到,最后一行释放了ByteBuf的引用,对于池化的ByteBuf,可以让它们重新回到池中,对于非池化的ByteBuf,可以释放它们占用的内存。

总结

当有新的数据可以读取时:

  1. 创建一个ByteBufByteBuf内部是对Java原生的ByteBuffer的封装
  2. 调用Java原生SocketChannelread()方法,将数据读取到ByteBuf内部的ByteBuffer
  3. 调用pipeline.fireChannelRead(byteBuf),这里会依次调用pipeline中的channelRead(ChannelHandlerContext ctx, Object msg)方法
  4. 调用pipeline.fireChannelReadComplete(),这里会依次调用pipeline中的channelReadComplete(ChannelHandlerContext ctx)
  • 9
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值