【JAVA 网络编程系列】Netty -- Netty 接受新数据

【1】主循环中处理 Read 事件

public final class NioEventLoop extends SingleThreadEventLoop {

    private void processSelectedKey(SelectionKey k, AbstractNioChannel ch) {
        final AbstractNioChannel.NioUnsafe unsafe = ch.unsafe();
        ...
        try {
            // 检索此键的就绪操作集
            int readyOps = k.readyOps();
            ...
            // 处理 Read / Accept 事件
            if ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0) {
                unsafe.read();
            }
        } catch (CancelledKeyException ignored) {
            // 处理异常
            unsafe.close(unsafe.voidPromise());
        }
    }
}

【2】Read 事件的处理流程

【2.1】处理接受新数据请求时的 unsafe 继承结构

针对 NioSocketChannel 其 unsafe 对应的是 NioSocketChannelUnsafe 类;

Java整合netty实现设备实时数据显示到前端_数据

【2.2】数据读取流程

public abstract class AbstractNioByteChannel extends AbstractNioChannel {

    protected class NioByteUnsafe extends AbstractNioUnsafe {
        @Override
        public final void read() {
            // 获取 ServerSocketChannel 中的 ChannelConfig 实例
            final ChannelConfig config = config();
            // 获取 ServerSocketChannel 中的 ChannelPipeline 实例
            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
                    // 默认地,通过各种参数判断当前操作系统是否允许池化、Unsafe、堆外这三个指标
                    // 当然,这些参数也可以通过启动参数来控制
                    byteBuf = allocHandle.allocate(allocator);
                    // 读取数据到ByteBuf中
                    // lastBytesRead
                    // Set the bytes that have been read for the last read operation
                    // 设置上次读取操作已读取的字节
                    // doReadBytes 方法为
                    // protected abstract int doReadBytes(ByteBuf buf) throws Exception; 由子类覆写
                    allocHandle.lastBytesRead(doReadBytes(byteBuf));
                    // lastBytesRead
                    // Get the amount of bytes for the previous read operation.
                    // 获取上一次读取操作的字节数
                    if (allocHandle.lastBytesRead() <= 0) {
                        // 未读取到数据释放ByteBuf
                        // nothing was read. release the buffer.
                        byteBuf.release();
                        byteBuf = null;
                        // 标记 close 标志
                        close = allocHandle.lastBytesRead() < 0;
                        if (close) {
                            readPending = false;
                        }
                        break;
                    }
                    // incMessagesRead
                    // Increment the number of messages that have been read for the current read loop
                    // 增加当前读取循环已读取的消息数
                    allocHandle.incMessagesRead(1);
                    readPending = false;
                    // 触发 ChannelHandler 的 channelRead() 方法
                    pipeline.fireChannelRead(byteBuf);
                    byteBuf = null;
                    // continueReading
                    // Determine if the current read loop should should continue
                    // 确定当前读取循环是否应继续
                } while (allocHandle.continueReading());
                // The read has completed
                // 标记读取已完成
                allocHandle.readComplete();
                // 触发 ChannelHandler 的 channelReadComplete() 方法
                pipeline.fireChannelReadComplete();
                // 数据接受完毕
                if (close) {
                    // 关闭 Channel
                    closeOnRead(pipeline);
                }
            } catch (Throwable t) {
                // 处理异常
                handleReadException(pipeline, byteBuf, t, close, allocHandle);
            } finally {
                // 当没有未读信息并且Channel不是AutoRead的
                if (!readPending && !config.isAutoRead()) {
                    // 移除与读相关的兴趣事件集
                    removeReadOp();
                }
            }
        }
    }
}
public class NioSocketChannel extends AbstractNioByteChannel 
    implements io.netty.channel.socket.SocketChannel {

    @Override
    protected int doReadBytes(ByteBuf byteBuf) throws Exception {
        // 分配接受处理相关的Handle
        final RecvByteBufAllocator.Handle allocHandle = unsafe().recvBufAllocHandle();
        // 设置可读取的长度
        allocHandle.attemptedBytesRead(byteBuf.writableBytes());
        // 调用ByteBuf的writeBytes()方法
        // 第一个参数是Java原生的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);
        // 调用setBytes()方法
        // 第一个参数是写入的位置,第二参数是SocketChannel,第三个参数可写入的长度
        int writtenBytes = setBytes(writerIndex, in, length);
        if (writtenBytes > 0) {
            writerIndex += writtenBytes;
        }
        return writtenBytes;
    }
}

【2.3】数据在 Pipeline 中的传递过程

Pipeline 中传递数据流程图示

Java整合netty实现设备实时数据显示到前端_ide_02

public class DefaultChannelPipeline implements ChannelPipeline {

    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 {
            // 释放ByteBuf的引用
            ReferenceCountUtil.release(msg);
        }
    }

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

    final class HeadContext extends AbstractChannelHandlerContext
        implements ChannelOutboundHandler, ChannelInboundHandler {

        @Override
        public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
            // 调用ChannelHandlerContext的fireChannelRead()方法
            // 触发下一个Context中Handler的调用
            ctx.fireChannelRead(msg);
        }
    }

    final class TailContext extends AbstractChannelHandlerContext implements ChannelInboundHandler {

        @Override
        public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
            // 数据传递到TailContext则打印日志并释放ByteBuf
            // 目的
            // 1.实现 ChannelPipeline 的完整性,有头有尾
            // 2.回收资源
            onUnhandledInboundMessage(msg);
        }
    }
}
abstract class AbstractChannelHandlerContext extends DefaultAttributeMap
        implements ChannelHandlerContext, ResourceLeakHint {

    static void invokeChannelRead(final AbstractChannelHandlerContext next, Object msg) {
        // 创建信息数据
        final Object m = next.pipeline.touch(ObjectUtil.checkNotNull(msg, "msg"), next);
        // 获取Channel的执行器
        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) {
        // invokeHandler() 确定是否已经调用过
        // ChannelHandler#handlerAdded(ChannelHandlerContext)
        if (invokeHandler()) {
            try {
                // 调用里面的ChannelHandler的channelRead()方法
                ((ChannelInboundHandler) handler()).channelRead(this, msg);
            } catch (Throwable t) {
                // 异常处理
                notifyHandlerException(t);
            }
        } else {
            // 从Pipeline中确定一个inbound的HandlerContext
            // 并从该HandlerContext发开触发ChannelHandler的channelRead()方法
            fireChannelRead(msg);
        }
    }

    @Override
    public ChannelHandlerContext fireChannelRead(final Object msg) {
        // 寻找下一个可用的ChannelHandlerContext
        // 从Pipeline中确定一个inbound的HandlerContext
        // 并从该HandlerContext发开触发ChannelHandler的channelRead()方法
        invokeChannelRead(findContextInbound(), msg);
        return this;
    }

    private AbstractChannelHandlerContext findContextInbound() {
        AbstractChannelHandlerContext ctx = this;
        do {
            ctx = ctx.next;
        } while (!ctx.inbound);
        return ctx;
    }
}
@Sharable
public class EchoServerHandler extends ChannelInboundHandlerAdapter {

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        // 此处读取数据后,写回客户端
        // 没有调用ctx.fireChannelRead(msg);
        // 此时数据不会继续向下一个ChannelHandlerContext传递
        ctx.write(msg);
    }
}

参考致谢

本博客为博主学习笔记,同时参考了网上众博主的博文以及相关专业书籍,在此表示感谢,本文若存在不足之处,请批评指正。

【1】慕课专栏,网络编程之Netty一站式精讲

【2】极客时间,Netty源码剖析与实战