netty源码解析四之接收网络连接

四:接收网络连接

1. 接收客户端连接核心流程框架总览

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • 在do…while{…}循环read loop中不断的调用JDK NIO serverSocketChannel.accept()方法来接收完成三次握手的客户端连接NioSocketChannel。
  • 将接收到的客户端连接NioSocketChannel临时保存在List<Object> readBuf集合中,后续会服务端NioServerSocketChannel的pipeline中通过ChannelRead事件来传递,最终会在ServerBootstrapAcceptor这个ChannelHandler中被处理初始化,并将其注册到Sub Reator Group中。

main reactor线程退出read loop循环的条件有两个:

  1. 在限定的16次读取中,已经没有新的客户端连接要接收了。退出循环。
  2. 从NioServerSocketChannel中读取客户端连接的次数达到了16次,无论此时是否还有客户端连接都需要退出循环。
private final class NioMessageUnsafe extends AbstractNioUnsafe {
    //存放连接建立后,创建的客户端SocketChannel
    private final List<Object> readBuf = new ArrayList<Object>();

    @Override
    public void read() {
        //必须在Main Reactor线程中执行
        assert eventLoop().inEventLoop();
        final ChannelConfig config = config();
        final ChannelPipeline pipeline = pipeline();
        //创建接收数据Buffer分配器(用于分配容量大小合适的byteBuffer用来容纳接收数据)
        //在接收连接的场景中,这里的allocHandle只是用于控制read loop的循环读取创建连接的次数。以及判断是否该结束read loop转去执行异步任务。
        final RecvByteBufAllocator.Handle allocHandle = unsafe().recvBufAllocHandle();
        allocHandle.reset(config);

        boolean closed = false;
        Throwable exception = null;
        try {
            try {
                do {
                    //建立channel,并设置事件为read
                    //返回值localRead表示接收到了多少客户端连接,客户端连接通过accept方法只会一个一个的接收,所以这里的localRead正常情况下都会返回1
                    int localRead = doReadMessages(readBuf);
                    //已无新的连接可接收则退出read loop
                    if (localRead == 0) {
                        break;
                    }
                    if (localRead < 0) {
                        closed = true;
                        break;
                    }

                    //统计在当前事件循环中已经读取到的  Message数量(创建连接的个数)
                    allocHandle.incMessagesRead(localRead);
                    //判断是否已经读满16次,或者在16次读取以内已经没有新的客户端连接要接收了
                } while (continueReading(allocHandle));
            } catch (Throwable t) {
                exception = t;
            }
            //遍历所有暂存在readBuf里面的客户端连接,传播通道可读事件
            int size = readBuf.size();
            for (int i = 0; i < size; i ++) {
                readPending = false;
                //当通道缓冲区可读,会调用fireChannelRead,触发通道可读事件
                //初始化客户端SocketChannel,并将其绑定到Sub Reactor线程组中的一个Reactor上
                pipeline.fireChannelRead(readBuf.get(i));
            }
            //清除本次accept 创建的客户端SocketChannel集合
            readBuf.clear();
            allocHandle.readComplete();
            //通道缓冲区读完,调用fireChannelReadComplete,触发通道读完事件
            pipeline.fireChannelReadComplete();

            if (exception != null) {
                closed = closeOnReadError(exception);

                pipeline.fireExceptionCaught(exception);
            }

            if (closed) {
                inputShutdown = true;
                if (isOpen()) {
                    close(voidPromise());
                }
            }
        } 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();
            }
        }
    }
}
  • 首先断言,确保在Main Reactor线程中执行。而main reactor中主要注册的是服务端NioServerSocketChannel,主要负责处理OP_ACCEPT事件,所以当前main reactor线程是在NioServerSocketChannel中执行接收连接的工作。

  • 通过config()获取ChannelConfig对象。该config是在初始化NioServerSocketChannel时设置的。

    public NioServerSocketChannel(ServerSocketChannel channel) {
        //设置非阻塞
        super(null, channel, SelectionKey.OP_ACCEPT);
        //相当于把本类和ServerSocketChannel存储配置中
        config = new NioServerSocketChannelConfig(this, javaChannel().socket());
    }
    

接着获取pipeline()。

  • RecvByteBufAllocator.Handle allocHandle统计read loop中接收客户端连接的次数,以及判断是否该结束read loop转去执行异步任务。当达到16次之后会退出循环。

当这一切准备就绪之后,main reactor线程就开始在do{....}while(...)循环中接收客户端连接了。

  • 在 read loop中通过调用doReadMessages函数接收完成三次握手的客户端连接,底层会调用到JDK NIO ServerSocketChannel的accept方法,从内核全连接队列中取出客户端连接。返回值localRead表示接收到了多少客户端连接,客户端连接通过accept方法只会一个一个的接收,所以这里的localRead正常情况下都会返回1
protected int doReadMessages(List<Object> buf) throws Exception {
    //建立连接
    SocketChannel ch = SocketUtils.accept(javaChannel());

    try {
        //如果此时并没有客户端连接时accept调用就会立刻返回null并不会阻塞。因为设置的非阻塞。
        if (ch != null) {
            buf.add(new NioSocketChannel(this, ch));
            return 1;
        }
    } catch (Throwable t) {
        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;
}
  • 随后将接收到的客户端连接占时存放到List<Object> readBuf集合中。
private final List<Object> readBuf = new ArrayList<Object>();
  • allocHandle.incMessagesRead(localRead)统计本次事件循环中接收到的客户端连接个数,最后在read loop末尾通过allocHandle.continueReading判断是否达到了限定的16次。从而决定main reactor线程是继续接收客户端连接还是转去执行异步任务。

满足退出条件后,随后开始遍历readBuf,在NioServerSocketChannel的pipeline中传播ChannelRead事件。

int size = readBuf.size();
for (int i = 0; i < size; i ++) {
    readPending = false;
    //当通道缓冲区可读,会调用fireChannelRead,触发通道可读事件
    //初始化客户端SocketChannel,并将其绑定到Sub Reactor线程组中的一个Reactor上
    pipeline.fireChannelRead(readBuf.get(i));
}

最终pipeline中的ChannelHandler(ServerBootstrapAcceptor)会响应ChannelRead事件,并在相应回调函数中初始化客户端NioSocketChannel,并将其注册到Sub Reactor Group中。此后客户端NioSocketChannel绑定到的sub reactor就开始监听处理客户端连接上的读写事件了。

Netty整个接收客户端的逻辑过程如下图步骤1,2,3所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

2. RecvByteBufAllocator简介

在创建NioServerSocketChannel的时候会创建NioServerSocketChannelConfig

public NioServerSocketChannel(ServerSocketChannel channel) {
    //设置非阻塞
    super(null, channel, SelectionKey.OP_ACCEPT);
    //相当于把本类和ServerSocketChannel存储配置中
    config = new NioServerSocketChannelConfig(this, javaChannel().socket());
}

在创建NioServerSocketChannelConfig的时候会创建RecvByteBufAllocator

public DefaultChannelConfig(Channel channel) {
    this(channel, new AdaptiveRecvByteBufAllocator());
}

NioServerSocketChannel中的RecvByteBufAllocator实际类型为AdaptiveRecvByteBufAllocator,顾名思义,这个类型的RecvByteBufAllocator可以根据Channel上每次到来的IO数据大小来自适应动态调整ByteBuffer的容量。

对于服务端NioServerSocketChannel来说,它上边的IO数据就是客户端的连接,它的长度和类型都是固定的,所以在接收客户端连接的时候并不需要这样的一个ByteBuffer来接收,我们会将接收到的客户端连接存放在List<Object> readBuf集合中

对于客户端NioSocketChannel来说,它上边的IO数据时客户端发送来的网络数据,长度是不定的,所以才会需要这样一个可以根据每次IO数据的大小来自适应动态调整容量的ByteBuffer来接收。

2.1 RecvByteBufAllocator.Handle的获取

final RecvByteBufAllocator.Handle allocHandle = unsafe().recvBufAllocHandle();
@Override
public RecvByteBufAllocator.Handle recvBufAllocHandle() {
    if (recvHandle == null) {
        recvHandle = config().getRecvByteBufAllocator().newHandle();
    }
    return recvHandle;
}

获取AdaptiveRecvByteBufAllocator执行newHandle创建Handle

public class AdaptiveRecvByteBufAllocator extends DefaultMaxMessagesRecvByteBufAllocator {
      private final int minIndex;
      private final int maxIndex;
      private final int initial;
      @Override
    public Handle newHandle() {
        //创建自适应动态调整容量的ByteBuffer分配器。
        return new HandleImpl(minIndex, maxIndex, initial);
    }
      private final class HandleImpl extends MaxMessageHandle {
        //最小容量在扩缩容索引表中的index
        private final int minIndex;
        //最大容量在扩缩容索引表中的index
        private final int maxIndex;
        //当前容量在扩缩容索引表中的index 初始33 对应容量2048
        private int index;
        //预计下一次分配buffer的容量,初始:2048
        private int nextReceiveBufferSize;
        //是否缩容
        private boolean decreaseNow;

        HandleImpl(int minIndex, int maxIndex, int initial) {
            this.minIndex = minIndex;
            this.maxIndex = maxIndex;
            //在扩缩容索引表中二分查找到最小大于等于initial 的容量
            index = getSizeTableIndex(initial);
            nextReceiveBufferSize = SIZE_TABLE[index];//2048
        }
        
      }
}

这个Handle里边保存了每次从Channel中读取IO数据的容量指标,方便下次读取时分配合适大小的buffer

每次在使用allocHandle前需要调用allocHandle.reset(config);重置里边的统计指标。

public abstract class MaxMessageHandle implements ExtendedHandle {
          private ChannelConfig config;
        //每次事件轮询时,最多读取16次
        //可在启动配置类ServerBootstrap中通过ChannelOption.MAX_MESSAGES_PER_READ选项设置。
        private int maxMessagePerRead;
          //本次事件轮询总共读取的message数,NioServerSocketChannel指的是接收连接的数量
        //用于统计read loop中总共接收的连接个数,NioSocketChannel中表示读取数据的次数
        //每次read loop循环后会调用allocHandle.incMessagesRead增加记录接收到的连接个数
        private int totalMessages;
          //本次事件轮询总共读取的字节数,主要用于sub reactor在接收客户端NioSocketChannel上的网络数据用的
        private int totalBytesRead;
  
          public void reset(ChannelConfig config) {
            this.config = config;
            //默认每次最多读取16次
            maxMessagePerRead = maxMessagesPerRead();
            totalMessages = totalBytesRead = 0;
        }

}
  • maxMessagePerRead:用于控制每次read loop里最大可以循环读取的次数,默认为16次,可在启动配置类ServerBootstrap中通过ChannelOption.MAX_MESSAGES_PER_READ选项设置。
  • totalMessages:用于统计read loop中总共接收的连接个数,每次read loop循环后会调用allocHandle.incMessagesRead增加记录接收到的连接个数。
@Override
public final void incMessagesRead(int amt) {
    //增加记录接收到的连接个数。
    totalMessages += amt;
}
  • totalBytesRead:用于统计在read loop中总共接收到客户端连接上的数据大小,这个字段主要用于sub reactor在接收客户端NioSocketChannel上的网络数据用的,本文我们介绍的是main reactor接收客户端连接,所以这里并不会用到这个字段。这个字段会在sub reactor每次读取完NioSocketChannel上的网络数据时增加记录。
public void lastBytesRead(int bytes) {
    //增加上次读取接收到的客户端数据大小,记录总读取个数
    lastBytesRead = bytes;
    if (bytes > 0) {
        totalBytesRead += bytes;
    }
}
  • 每次read loop末尾会调用allocHandle.continueReading()方法来判断读取连接次数是否已满16次,来决定main reactor线程是否退出循环。
do {
    //建立channel,并设置事件为read
    //返回值localRead表示接收到了多少客户端连接,客户端连接通过accept方法只会一个一个的接收,所以这里的localRead正常情况下都会返回1
    int localRead = doReadMessages(readBuf);
    //已无新的连接可接收则退出read loop
    if (localRead == 0) {
        break;
    }
    if (localRead < 0) {
        closed = true;
        break;
    }

    //统计在当前事件循环中已经读取到的  Message数量(创建连接的个数)
    allocHandle.incMessagesRead(localRead);
    //判断是否已经读满16次,或者在16次读取以内已经没有新的客户端连接要接收了
} while (continueReading(allocHandle));
public boolean continueReading(UncheckedBooleanSupplier maybeMoreDataSupplier) {
    return config.isAutoRead() &&
            //respectMaybeMoreData=false:表示是否满载而归都继续读取,直到读取不到数据在退出循环。
            //maybeMoreDataSupplier.get():判断本次读取byteBuffer是否满载而归。respectMaybeMoreData=true表示如果本次循环装满了byteBuffer,则后面可能还有数据,在次读取。如果byteBuffer没装满则退出循环。
            (!respectMaybeMoreData || maybeMoreDataSupplier.get()) &&
            //读取次数是否超过16次,超过则退出循环
            totalMessages < maxMessagePerRead &&
            //当客户端NioSocketChannel上的OP_READ事件活跃时,sub reactor线程在read loop中是否读取到了网络数据。没读到则退出read-loop
           totalBytesRead > 0;
    //这里有一个bug
    // totalBytesRead字段主要记录sub reactor线程在处理客户端NioSocketChannel中OP_READ事件活跃时,总共在read loop中读取到的网络数据,
    // 而这里是main reactor线程在接收客户端连接所以这个字段并不会被设置。totalBytesRead字段的值在本文中永远会是0。
    //所以每次main会接收到一个accept事件就返回。
    //新版本添加了ignoreBytesRead 参数,用于忽略mian Reactor
}
  • totalMessages < maxMessagePerRead:判断main reactor线程在read loop中的读取次数是否超过了16次。如果超过16次就会返回false,main reactor线程退出循环。
  • totalBytesRead > 0:当客户端NioSocketChannel上的OP_READ事件活跃时,sub reactor线程在read loop中是否读取到了网络数据。没读到则退出read-loop。

3. doReadMessages接收客户端连接

protected int doReadMessages(List<Object> buf) throws Exception {
    //建立连接
    SocketChannel ch = SocketUtils.accept(javaChannel());

    try {
        //如果此时并没有客户端连接时accept调用就会立刻返回null并不会阻塞。因为设置的非阻塞。
        if (ch != null) {
            buf.add(new NioSocketChannel(this, ch));
            return 1;
        }
    } catch (Throwable t) {
        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;
}
  • 获取jdk原生ServerSocketChannel
@Override
protected ServerSocketChannel javaChannel() {
    return (ServerSocketChannel) super.javaChannel();
}
  • 通过JDK NIO 原生ServerSocketChannelaccept方法获取JDK NIO 原生客户端连接SocketChannel
public static SocketChannel accept(final ServerSocketChannel serverSocketChannel) throws IOException {
    try {
        return AccessController.doPrivileged(new PrivilegedExceptionAction<SocketChannel>() {
            @Override
            public SocketChannel run() throws IOException {
                return serverSocketChannel.accept();
            }
        });
    } catch (PrivilegedActionException e) {
        throw (IOException) e.getCause();
    }
}

3.1 创建客户端NioSocketChannel

public class NioSocketChannel extends AbstractNioByteChannel implements io.netty.channel.socket.SocketChannel {
public NioSocketChannel(Channel parent, SocketChannel socket) {
    super(parent, socket);
    config = new NioSocketChannelConfig(this, socket.socket());
}
}

这里会注册OP_READ事件

protected AbstractNioByteChannel(Channel parent, SelectableChannel ch) {
    super(parent, ch, SelectionKey.OP_READ);
}

3.2 对比NioSocketChannel与NioServerSocketChannel的不同

  • Channel的层次不同

    • 由于客户端NioSocketChannel是在main reactor接收连接时在服务端NioServerSocketChannel中被创建的,所以在创建客户端NioSocketChannel的时候会通过构造函数指定了parent属性为NioServerSocketChanel。并将JDK NIO 原生SocketChannel封装进Netty的客户端NioSocketChannel中。

    而在Reactor启动过程中创建NioServerSocketChannel的时候parent属性指定是null。因为它就是顶层的Channel,负责创建客户端NioSocketChannel

  • 向Reactor注册的IO事件不同

    • 客户端NioSocketChannel向Sub Reactor注册的是SelectionKey.OP_READ事件,而服务端NioServerSocketChannel向Main Reactor注册的是SelectionKey.OP_ACCEPT事件
  • 功能属性不同造成继承结构的不同

  • 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

客户端NioSocketChannel继承的是AbstractNioByteChannel,而服务端NioServerSocketChannel继承的是AbstractNioMessageChannel

客户端NioSocketChannel主要处理的是服务端与客户端的通信,这里涉及到接收客户端发送来的数据,而Sub Reactor线程NioSocketChannel中读取的正是网络通信数据单位为Byte

服务端NioServerSocketChannel主要负责处理OP_ACCEPT事件,创建用于通信的客户端NioSocketChannel。这时候客户端与服务端还没开始通信,所以Main Reactor线程NioServerSocketChannel的读取对象为Message。这里的Message指的就是底层的SocketChannel客户端连接。

客户端NioSocketChannel结构如下:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

4. ChannelRead事件的响应

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

main reactor就会退出read loop循环后,readBuf会存所有accept的客户端channel。随后遍历该集合,在NioServerSocketChannel的pipeline中传播ChannelRead事件。外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

int size = readBuf.size();
for (int i = 0; i < size; i ++) {
    readPending = false;
    //当通道缓冲区可读,会调用fireChannelRead,触发通道可读事件
    //初始化客户端SocketChannel,并将其绑定到Sub Reactor线程组中的一个Reactor上
    pipeline.fireChannelRead(readBuf.get(i));
}
//清除本次accept 创建的客户端SocketChannel集合
readBuf.clear();

最终ChannelRead事件会传播到ServerBootstrapAcceptor中,这里正是Netty处理客户端连接的核心逻辑所在。

    private static class ServerBootstrapAcceptor extends ChannelInboundHandlerAdapter {
public void channelRead(ChannelHandlerContext ctx, Object msg) {
    final Channel child = (Channel) msg;

    //将childHandler添加到子channel上
    child.pipeline().addLast(childHandler);

    //将父childOptions设置子channel上
    setChannelOptions(child, childOptions, logger);
    //将父childAttrs设置子channel上
    setAttributes(child, childAttrs);

    try {
        /**
         * 1:在Sub Reactor线程组中选择一个Reactor绑定
         * 2:将客户端SocketChannel注册到绑定的Reactor上
         * 3:SocketChannel注册到sub reactor中的selector上,并监听OP_READ事件
         * */
        childGroup.register(child).addListener(new ChannelFutureListener() {
            @Override
            public void operationComplete(ChannelFuture future) throws Exception {
                if (!future.isSuccess()) {
                    forceClose(child, future.cause());
                }
            }
        });
    } catch (Throwable t) {
        forceClose(child, t);
    }
}
    }
  • 这里会将设置的child前缀的配置初始化到NioSocketChannel中。

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

经过ServerBootstrapAccptor#chanelRead回调的处理之后,此时客户端NioSocketChannel中pipeline的结构为:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

随后会将初始化好的客户端NioSocketChannel向Sub Reactor Group中注册,并监听OP_READ事件

如下图中的步骤3所示:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

5. 向SubReactorGroup中注册NioSocketChannel

childGroup.register(child).addListener(new ChannelFutureListener() {
    @Override
    public void operationComplete(ChannelFuture future) throws Exception {
        if (!future.isSuccess()) {
            forceClose(child, future.cause());
        }
    }
});
  • 从Sub Reactor Group中选取一个Sub Reactor进行绑定
public abstract class MultithreadEventLoopGroup extends MultithreadEventExecutorGroup implements EventLoopGroup {
@Override
public ChannelFuture register(Channel channel) {
    //简单的遍历EventLoop,然后将channel注册到eventLoop上
    return next().register(channel);
}
}
  • 向绑定的Sub Reactor上注册NioSocketChannel
@Override
public ChannelFuture register(Channel channel) {
    //注册channel到绑定的Reactor上
    return register(new DefaultChannelPromise(channel, this));
}
    @Override
    public ChannelFuture register(final ChannelPromise promise) {
        ObjectUtil.checkNotNull(promise, "promise");
        //将channel注册selector上,事件默认0,将accept和父handler注册到pipe,以及剔除初始化的handler
        promise.channel().unsafe().register(this, promise);
        return promise;
    }

这里的promise.channel()NioSocketChannel。底层的unsafe操作类为NioByteUnsafe

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

当注册任务提交后,此时绑定的Sub Reactor线程启动。

5.1 register0

private void register0(ChannelPromise promise) {
    try {
        // check if the channel is still open as it could be closed in the mean time when the register
        // call was outside of the eventLoop
        //1.查看注册操作是否已经取消,或者通道是否仍然打开,因为它可能在注册调用在 eventLoop 之外的同时关闭
        if (!promise.setUncancellable() || !ensureOpen(promise)) {
            return;
        }
        //如果频道从未注册过,则为 true,否则为 false
        boolean firstRegistration = neverRegistered;
        //2.channel注册选择器上
        doRegister();
        //标记注册过
        neverRegistered = false;
        registered = true;

        // Ensure we call handlerAdded(...) before we actually notify the promise. This is needed as the
        // user may already fire events through the pipeline in the ChannelFutureListener.
        //3.初始化channelPipeline,回调pipeline中添加的ChannelInitializer的handlerAdded方法
        //主要将accept和父handler注册到pipe,以及剔除初始化的handler
        pipeline.invokeHandlerAddedIfNeeded();

        //4.设置regFuture为success,触发operationComplete回调,将bind操作放入Reactor的任务队列中,等待Reactor线程执行。
        //会触发io.netty.channel.ChannelFutureListener#operationComplete,进行端口绑定
        safeSetSuccess(promise);
        //5.通道注册完成后,成功绑定NioEventLoop线程后,会调用fireChannelRegistered,触发通道注册事件
        pipeline.fireChannelRegistered();
        // Only fire a channelActive if the channel has never been registered. This prevents firing
        // multiple channel actives if the channel is deregistered and re-registered.
        //对于服务端ServerSocketChannel来说 只有绑定端口地址成功后 channel的状态才是active的。
        //此时绑定操作作为异步任务在Reactor的任务队列中,绑定操作还没开始,所以这里的isActive()是false
        if (isActive()) {
            //6.当通道激活完成后(所有的业务处理器添加,注册的异步任务完成,并且NioEventLoop线程绑定的异步任务完成),会调用fireChannelActive,触发通道激活事件
            if (firstRegistration) {
                //触发channelActive事件
                pipeline.fireChannelActive();
            } else if (config().isAutoRead()) {
                // This channel was registered before and autoRead() is set. This means we need to begin read
                // again so that we process inbound data.
                //
                // See https://github.com/netty/netty/issues/4805
                beginRead();
            }
        }
    } catch (Throwable t) {
        // Close the channel directly to avoid FD leak.
        closeForcibly();
        closeFuture.setClosed();
        safeSetFailure(promise, t);
    }
}
  • 这里 doRegister()方法将NioSocketChannel注册到Sub Reactor中的Selector上。
  • 随后调用pipeline.invokeHandlerAddedIfNeeded()回调客户端NioSocketChannel上pipeline中的所有ChannelHandler的handlerAdded方法,此时pipeline的结构中只有一个ChannelInitializer。最终会在ChannelInitializer#handlerAdded回调方法中初始化客户端NioSocketChannelpipeline
  • 此时客户端NioSocketChannel中的pipeline中的结构就变为了我们自定义的样子,在示例代码中我们自定义的ChannelHandlerEchoServerHandler

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • 当客户端NioSocketChannel中的pipeline初始化完毕后,netty就开始调用safeSetSuccess(promise)方法回调regFuture中注册的ChannelFutureListener,通知客户端NioSocketChannel已经成功注册到Sub Reactor上了。
childGroup.register(child).addListener(new ChannelFutureListener() {
    @Override
    public void operationComplete(ChannelFuture future) throws Exception {
        if (!future.isSuccess()) {
            forceClose(child, future.cause());
        }
    }
});

在服务端NioServerSocketChannel注册的时候我们会在listener中向Main Reactor提交bind绑定端口地址任务。但是在NioSocketChannel注册的时候,只会在listener中处理一下注册失败的情况。

  • 当Sub Reactor线程通知ChannelFutureListener注册成功之后,随后就会调用pipeline.fireChannelRegistered()在客户端NioSocketChannel的pipeline中传播ChannelRegistered事件

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • 紧接着判断是否处于激活状态,服务端NioServerSocketChannel判断是否激活的标准为端口是否绑定成功。

    • @Override
      public boolean isActive() {
          // As java.nio.ServerSocketChannel.isBound() will continue to return true even after the channel was closed
          // we will also need to check if it is open.
          //查看是否打开连接,或者是否绑定
          return isOpen() && javaChannel().socket().isBound();
      }
      
    • 客户端NioSocketChannel判断是否激活的标准为是否处于Connected状态。那么显然这里肯定是处于connected状态的。

    • public boolean isActive() {
          //判断是否处于连接状态
          SocketChannel ch = javaChannel();
          return ch.isOpen() && ch.isConnected();
      }
      

NioSocketChannel已经处于connected状态,这里并不需要绑定端口,所以这里的isActive()返回true

  • 调用pipeline.fireChannelActive()在NioSocketChannel中的pipeline传播ChannelActive事件,最终在pipeline的头结点HeadContext中响应并注册OP_READ事件Sub Reactor中的Selector上。
if (isActive()) {
    //6.当通道激活完成后(所有的业务处理器添加,注册的异步任务完成,并且NioEventLoop线程绑定的异步任务完成),会调用fireChannelActive,触发通道激活事件
    if (firstRegistration) {
        //触发channelActive事件
        pipeline.fireChannelActive();
    } else if (config().isAutoRead()) {
        beginRead();
    }
}

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

public abstract class AbstractNioChannel extends AbstractChannel { {
@Override
protected void doBeginRead() throws Exception {
    // Channel.read() or ChannelHandlerContext.read() was called
    final SelectionKey selectionKey = this.selectionKey;
    //查看SelectionKey是否有效
    if (!selectionKey.isValid()) {
        return;
    }

    readPending = true;

    final int interestOps = selectionKey.interestOps();
    /**
     * 1:ServerSocketChannel 初始化时 readInterestOp设置的是OP_ACCEPT事件
     * 2:SocketChannel 初始化时 readInterestOp设置的是OP_READ事件
     * */
    if ((interestOps & readInterestOp) == 0) {
        //注册accept事件
        selectionKey.interestOps(interestOps | readInterestOp);
    }
}
}

此时Netty中主从Reactor组的结构就变为:外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值