5.不完全的 ServerBootstrap 启动过程源码分析

通过名字可以看出它是一个服务端的引导, 就是为了引导创建 Channel.

Bootstrap 的继承结构

在 netty 的代码中, 类 ServerBootstrap 和类 Bootstrap 都继承自基类 AbstractBootstrap:

channel类型 用于引导的bootstrap实现类
ServerChannel ServerBootstrap
Channel Bootstrap

引导启动流程

实例化 ServerBootstrap 后会调用, group 方法来设置 NioEventLoopGroup.

也就是说 group 方法必须调用, 否则会出现 java.lang.IllegalStateException: group not set 异常.

public ServerBootstrap group(EventLoopGroup parentGroup, EventLoopGroup childGroup) {
    super.group(parentGroup);
    if (this.childGroup != null) {
        throw new IllegalStateException("childGroup set already");
    }
    this.childGroup = ObjectUtil.checkNotNull(childGroup, "childGroup");
    return this;
}

group 方法只能被调用一次, 否则也会抛出异常.

在本篇笔记最开始有说过, 引导器为了引导创建 Channel, 而创建哪个 Channel 是通过 channel 方法确定.

该方法最终会调用 channelFactory 方法并会给 channelFactory 变量赋值, 最终会通过反射调用无参构造函数创建对应的实例.

@Deprecated
public B channelFactory(ChannelFactory<? extends C> channelFactory) {
    ObjectUtil.checkNotNull(channelFactory, "channelFactory");
    if (this.channelFactory != null) {
        throw new IllegalStateException("channelFactory set already");
    }

    this.channelFactory = channelFactory;
    return self();
}

optionchildOption 都是 ConcurrentHashMap 类型, 是给对应的 io.netty.channel.AbstractChannel 和 Java 中的 SocketSocketChannel 以及 SocketChannel 设置参数的.

最后在设置一些 ChannelHandler 用于处理 java.nio.channels.SocketChannel 请求, 该方法是必须. 但是还有一个与之类似的 handler() 方法, 该方法不是必须的, 主要用来处理 accept 过程.

使用 bind 方法来绑定监听端口, 最终会调用 doBind 方法.

final ChannelFuture initAndRegister() {
    Channel channel = null;
    try {
        /*
         * 通过 ChannelFactory 创建对应的 NioServerSocketChannel,
         * 通过该类的无参构造方法创建 sun.nio.ch.ServerSocketChannelImpl 
         * 并设置 configureBlocking 为 false, 
         * 以及创建 DefaultChannelPipeline 和 NioMessageUnsafe.
         */
        channel = channelFactory.newChannel();
        // 该方法是抽象方法, 由于是创建服务端则会调用 ServerBootstrap 中的实现.
        init(channel);
    } catch (Throwable t) {
        if (channel != null) {
            channel.unsafe().closeForcibly();
            return new DefaultChannelPromise(channel, GlobalEventExecutor.INSTANCE).setFailure(t);
        }
        return new DefaultChannelPromise(new FailedChannel(), GlobalEventExecutor.INSTANCE).setFailure(t);
    }

    ChannelFuture regFuture = config().group().register(channel);
    if (regFuture.cause() != null) {
        if (channel.isRegistered()) {
            channel.close();
        } else {
            channel.unsafe().closeForcibly();
        }
    }

    return regFuture;
}

config().group().register(channel), 之前两个方法的返回值是啥, 就不说了主要说一下 register 方法. 该方法会做以下操作:

  1. 从 bossGroup 中获取一个 EventLoop 后将 NioServerSocketChannel 与当前的 EventLoop 包装成 DefaultChannelPromise.
  2. 然后通过 NioMessageUnsafe 实例中的 register 方法, 将 ServerSocketChannelImpl 注册到 Selector.
    注意: 这里只是注册, 并不监听任何事件.
  3. 如果是首次注册 NioServerSocketChannel, 则会执行之前的 ChannelInitializer, 也就是下面 init 方法中添加的.
  4. 调用 ChannelHandler 的 channelRegistered 方法, 表示 NioServerSocketChannel 注册完成, 也就是注册到了 EventLoop 上.

下面是 ServerBootstrap 中的 init 方法实现.

// 这里的参数就是 NioServerSocketChannel
void init(Channel channel) {
    // 设置 Channel 的 Option, 会使用 option 方法设置的参数.
    setChannelOptions(channel, options0().entrySet().toArray(EMPTY_OPTION_ARRAY), logger);
    // 设置 Channel 的 Attribute, 这个就是 业务数据 随着业务流转, 在特定地方可以取出.
    setAttributes(channel, attrs0().entrySet().toArray(EMPTY_ATTRIBUTE_ARRAY));

    ChannelPipeline p = channel.pipeline();

    final EventLoopGroup currentChildGroup = childGroup;
    final ChannelHandler currentChildHandler = childHandler;
    final Entry<ChannelOption<?>, Object>[] currentChildOptions = childOptions.entrySet().toArray(EMPTY_OPTION_ARRAY);
    final Entry<AttributeKey<?>, Object>[] currentChildAttrs = childAttrs.entrySet().toArray(EMPTY_ATTRIBUTE_ARRAY);

    // 将这个 ChannelHandle 添加到 ChannelPipeline 的尾部.
    // 注意: 这里是 Channel 注册之前添加的任务, 还没有执行.
    p.addLast(new ChannelInitializer<Channel>() {
        // 注意该方法, 该方法在执行完成后, 该实例会从 ChannelPipeline 中移除.
        @Override
        public void initChannel(final Channel ch) {
            final ChannelPipeline pipeline = ch.pipeline();
            ChannelHandler handler = config.handler();
            if (handler != null) {
                // 添加 ServerBootstrap 中 handler 的 ChannelHandler.
                pipeline.addLast(handler);
            }

            // 给服务端的 eventLoop 添加一个任务.
            ch.eventLoop().execute(new Runnable() {
                @Override
                public void run() {
                    /**
                     * 这个类非常重要.
                     * 
                     * 会给 NioSocketChannel 中的 pipeline 添加 ChannelHandler.
                     * 设置 NioSocketChannel 和 SocketChannel 的 option, 以及设置业务数据.
                     */
                    pipeline.addLast(new ServerBootstrapAcceptor(
                            ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
                }
            });
        }
    });
}

initAndRegister() 方法执行完成后, 才会执行 doBind0 来绑定端口和设置 Channel 关心事件.

下面是我自己画的简易启动过程, 配合上面看.

讨论

1.bossGroup 设置多个线程, 只会使用其中一个还是都会使用?
你可能会看到有些例子中会使用有参构造方法来创建 bossGroup , 例如 new NioEventLoopGroup(1); 当然有些例子也会也是无参构造方法.
当然有些文章也会说 Netty 支持三种线程模型, 说的有理有据, 然你无法反驳.
但是我个人认为 Netty 只有一种线程模型, 就是从 主从 Reactor 多线程 模型演变来的.
因此 bossGroup 设置多个线程, 也只会使用其中一个.

2.如何让 bossGroup 使用多个线程来处理连接
后续更新, 因为我也不知道.

ChannelOption 参数

public static final ChannelOption<ByteBufAllocator> ALLOCATOR = valueOf("ALLOCATOR");
public static final ChannelOption<RecvByteBufAllocator> RCVBUF_ALLOCATOR = valueOf("RCVBUF_ALLOCATOR");
public static final ChannelOption<MessageSizeEstimator> MESSAGE_SIZE_ESTIMATOR = valueOf("MESSAGE_SIZE_ESTIMATOR");
public static final ChannelOption<Integer> CONNECT_TIMEOUT_MILLIS = valueOf("CONNECT_TIMEOUT_MILLIS");
public static final ChannelOption<Integer> WRITE_SPIN_COUNT = valueOf("WRITE_SPIN_COUNT");
public static final ChannelOption<WriteBufferWaterMark> WRITE_BUFFER_WATER_MARK = valueOf("WRITE_BUFFER_WATER_MARK");
public static final ChannelOption<Boolean> ALLOW_HALF_CLOSURE = valueOf("ALLOW_HALF_CLOSURE");
public static final ChannelOption<Boolean> AUTO_READ = valueOf("AUTO_READ");
public static final ChannelOption<Boolean> AUTO_CLOSE = valueOf("AUTO_CLOSE");
public static final ChannelOption<Boolean> SO_BROADCAST = valueOf("SO_BROADCAST");
public static final ChannelOption<Boolean> SO_KEEPALIVE = valueOf("SO_KEEPALIVE");
public static final ChannelOption<Integer> SO_SNDBUF = valueOf("SO_SNDBUF");
public static final ChannelOption<Integer> SO_RCVBUF = valueOf("SO_RCVBUF");
public static final ChannelOption<Boolean> SO_REUSEADDR = valueOf("SO_REUSEADDR");
public static final ChannelOption<Integer> SO_LINGER = valueOf("SO_LINGER");
public static final ChannelOption<Integer> SO_BACKLOG = valueOf("SO_BACKLOG");
public static final ChannelOption<Integer> SO_TIMEOUT = valueOf("SO_TIMEOUT");
public static final ChannelOption<Integer> IP_TOS = valueOf("IP_TOS");
public static final ChannelOption<InetAddress> IP_MULTICAST_ADDR = valueOf("IP_MULTICAST_ADDR");
public static final ChannelOption<NetworkInterface> IP_MULTICAST_IF = valueOf("IP_MULTICAST_IF");
public static final ChannelOption<Integer> IP_MULTICAST_TTL = valueOf("IP_MULTICAST_TTL");
public static final ChannelOption<Boolean> IP_MULTICAST_LOOP_DISABLED = valueOf("IP_MULTICAST_LOOP_DISABLED");
public static final ChannelOption<Boolean> TCP_NODELAY = valueOf("TCP_NODELAY");
public static final ChannelOption<Boolean> SINGLE_EVENTEXECUTOR_PER_GROUP = valueOf("SINGLE_EVENTEXECUTOR_PER_GROUP");

参考资料

Bootstrap

posted @ 2020-08-18 16:04  scikstack  阅读(303)  评论(0编辑  收藏  举报