【JAVA 网络编程系列】Netty -- Netty 服务接受连接处理分析

【JAVA 网络编程系列】Netty -- Netty 服务接受连接处理分析

【1】ServerBootstrapAcceptor 类型 ChannelHandler 加入 Pipeline

public class ServerBootstrap extends AbstractBootstrap<ServerBootstrap, ServerChannel> {

    @Override
    void init(Channel channel) throws Exception {
        ...
        p.addLast(new ChannelInitializer<Channel>() {
            @Override
            public void initChannel(final Channel ch) throws Exception {
                ...
                // 这样调用可以启动一个主循环并在启动完毕后执行Runnable任务
                // 此处实现了将ServerBootstrapAcceptor类ChannelHandler加入到ChannelPipeline中
                ch.eventLoop().execute(new Runnable() {
                    @Override
                    public void run() {
                        pipeline.addLast(new ServerBootstrapAcceptor(
                                ch, currentChildGroup, currentChildHandler
                                , currentChildOptions, currentChildAttrs));
                    }
                });
                ...
            }
        });
        ...
    }
}

ch.eventLoop().execute 的处理过程详见 【JAVA 网络编程系列】Netty -- ch.eventLoop().execute分析

【2】主循环中处理 Accept 事件

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());
        }
    }
}

【3】Accept 事件的处理流程

【3.1】处理连接请求时的 unsafe 继承结构

针对 NioServerSocketChannel 其 unsafe 对应的是 NioMessageUnsafe 类;

【3.2】NioMessageUnsafe -- public void read() 方法

public abstract class AbstractNioMessageChannel extends AbstractNioChannel {

    private final class NioMessageUnsafe extends AbstractNioUnsafe {

        private final List<Object> readBuf = new ArrayList<Object>();

        @Override
        public void read() {
            assert eventLoop().inEventLoop();
            // 获取 ServerSocketChannel 中的 ChannelConfig 实例
            final ChannelConfig config = config();
            // 获取 ServerSocketChannel 中的 ChannelPipeline 实例
            final ChannelPipeline pipeline = pipeline();
            // 初始化内存分配器
            final RecvByteBufAllocator.Handle allocHandle = unsafe().recvBufAllocHandle();
            // 重置已累积的所有计数器,并建议下一个读取循环应读取多少消息/字节
            allocHandle.reset(config);

            boolean closed = false;
            Throwable exception = null;
            try {
                try {
                    do {
                        // 读取消息,doReadMessages 为抽象方法由子类覆写
                        // protected abstract int doReadMessages(List<Object> buf) throws Exception;
                        int localRead = doReadMessages(readBuf);
                        if (localRead == 0) {
                            break;
                        }
                        if (localRead < 0) {
                            closed = true;
                            break;
                        }
                        // Increment the number of messages that have been read for the current read loop
                        // 增加当前读取循环已读取的消息数
                        allocHandle.incMessagesRead(localRead);
                        // Determine if the current read loop should should continue
                        // 确定当前读取循环是否应继续
                    } while (allocHandle.continueReading());
                } catch (Throwable t) {
                    // 处理异常
                    exception = t;
                }

                int size = readBuf.size();
                for (int i = 0; i < size; i ++) {
                    readPending = false;
                    // 触发 ChannelHandler 的 channelRead() 方法
                    // 此处 pipeline 实际上是 NioServerSocketChannel 对应的 ChannelPipeline
                    // 服务启动完成后则该 ChannelPipeline 中的双向链表为
                    // head<=>LoggingHandler<=>ServerBootstrapAcceptor<=>tail
                    // 其中 ServerBootstrapAcceptor 这个 ChannelHandler 会往子 ChannelPipeline 中
                    添加子 ChannelHandler
                    pipeline.fireChannelRead(readBuf.get(i));
                }
                // 清空 Buffer
                readBuf.clear();
                // The read has completed
                // 标记读取已完成
                allocHandle.readComplete();
                // 触发 ChannelHandler 的 channelReadComplete() 方法
                pipeline.fireChannelReadComplete();
                // 处理异常
                if (exception != null) {
                    // 即使发生IOException也不应关闭ServerChannel,因为它通常可以继续接受传入的连接
                    closed = closeOnReadError(exception);
                    // 触发 ChannelHandler 的 exceptionCaught() 方法
                    pipeline.fireExceptionCaught(exception);
                }
                // 若需要关闭则关闭Channel
                if (closed) {
                    inputShutdown = true;
                    if (isOpen()) {
                        close(voidPromise());
                    }
                }
            } finally {
                // 当没有未读信息并且Channel不是AutoRead的
                if (!readPending && !config.isAutoRead()) {
                    // 移除与读相关的兴趣事件集
                    removeReadOp();
                }
            }
        }
    }
}

【3.3】NioServerSocketChannel -- protected int doReadMessages(List<Object> buf) throws Exception

public class NioServerSocketChannel extends AbstractNioMessageChannel
                             implements io.netty.channel.socket.ServerSocketChannel {

    @Override
    protected int doReadMessages(List<Object> buf) throws Exception {
        // JAVA 原生方法接受连接
        SocketChannel ch = SocketUtils.accept(javaChannel());
        try {
            if (ch != null) {
                // 构造了一个Netty的NioSocketChannel并把Java原生SocketChannel传入
                // 此时创建了一个 NioSocketChannel
                // 此时该 NioSocketChannel 实例的 pipeline 中还是只有 head 和 tail 两个 Handler,还无法处理消息
                buf.add(new NioSocketChannel(this, ch));
                return 1;
            }
        } catch (Throwable t) {
            // 处理异常,发生异常后会关闭 Channel
            logger.warn("Failed to create a new channel from an accepted socket.", t);
            try {
                ch.close();
            } catch (Throwable t2) {
                logger.warn("Failed to close a socket.", t2);
            }
        }
        return 0;
    }
}
public final class SocketUtils {

    public static SocketChannel accept(final ServerSocketChannel serverSocketChannel) throws IOException {
        try {
            // 调用Java原生的aacept()方法创建一个SocketChannel
            // Netty 最终还是调用的 Java 原生的 SeverSocketChannel 的 accept () 方法来创建一个 SocketChannel,
            // 并把这个 SocketChannel 绑定到 Netty 自己的 NioSocketChannel 中
            return AccessController.doPrivileged((PrivilegedExceptionAction<SocketChannel>) 
            () -> serverSocketChannel.accept());
        } catch (PrivilegedActionException e) {
            throw (IOException) e.getCause();
        }
    }
}

【3.4】ServerBootstrapAcceptor 类中的处理逻辑

public class ServerBootstrap extends AbstractBootstrap<ServerBootstrap, ServerChannel> {

    private static class ServerBootstrapAcceptor extends ChannelInboundHandlerAdapter {

        @Override
        @SuppressWarnings("unchecked")
        public void channelRead(ChannelHandlerContext ctx, Object msg) {
            // msg 为 Channel 类型的变量
            // 此处的msg即为readBuf.get(i),即NioSocketChannel,也就是子Channel
            // 见 AbstractNioMessageChannel 类中
            // public void read() 方法中的调用 pipeline.fireChannelRead(readBuf.get(i));
            final Channel child = (Channel) msg;
            // 添加子ChannelHandler此处同样是以ChannelInitializer的形式添加的
            child.pipeline().addLast(childHandler);
            // 设置子Channel的配置等信息
            // 将 childOptions 设置到 Channel 中
            setChannelOptions(child, childOptions, logger);
            // 将 childAttrs 设置到 Channel 中
            for (Entry<AttributeKey<?>, Object> e: childAttrs) {
                child.attr((AttributeKey<Object>) e.getKey()).set(e.getValue());
            }
            try {
                // 将子Channel注册到workerGroup中的一个EventLoop上
                
                childGroup.register(child).addListener(new ChannelFutureListener() {
                    @Override
                    public void operationComplete(ChannelFuture future) throws Exception {
                        if (!future.isSuccess()) {
                            // 关闭该Channel
                            forceClose(child, future.cause());
                        }
                    }
                });
            } catch (Throwable t) {
                // 关闭该Channel
                forceClose(child, t);
            }
        }
    }
}

【3.5】子 Channel 的注册逻辑

public abstract class SingleThreadEventLoop extends SingleThreadEventExecutor implements EventLoop {

    @Override
    public ChannelFuture register(Channel channel) {
        // 创建一个默认的 ChannelPromise 类实例
        return register(new DefaultChannelPromise(channel, this));
    }

    @Override
    public ChannelFuture register(final ChannelPromise promise) {
        ObjectUtil.checkNotNull(promise, "promise");
        // 调用的是channel的unsafe的register()方法
        // 调用 io.netty.channel.AbstractChannel.AbstractUnsafe
        // public final void register(EventLoop eventLoop, final ChannelPromise promise) 方法
        // 此后的流程与 initAndRegister() 方法中的逻辑一致
        promise.channel().unsafe().register(this, promise);
        return promise;
    }
}

参考致谢

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

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

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

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值