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();
}
option
与 childOption
都是 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
方法. 该方法会做以下操作:
- 从 bossGroup 中获取一个 EventLoop 后将 NioServerSocketChannel 与当前的 EventLoop 包装成 DefaultChannelPromise.
- 然后通过 NioMessageUnsafe 实例中的 register 方法, 将 ServerSocketChannelImpl 注册到 Selector.
注意: 这里只是注册, 并不监听任何事件. - 如果是首次注册 NioServerSocketChannel, 则会执行之前的 ChannelInitializer, 也就是下面 init 方法中添加的.
- 调用 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");