阅读 Netty启动引导类Bootstrap模块
- 服务器启动引导类ServerBootstrap
group :设置线程组模型,Reactor线程模型对比EventLoopGroup。NIOEventLoopGroup就是一个线程池实现,通过设置不同的NIOEventLoopGroup方式就可以对应三种不同的Reactor线程模型。这里只给出服务端的配置,对于客户端都是一样的。
- 单线程
单线程实例:
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup).channel(NioServerSocketChannel.class);
//.....
上面实例化了一个NIOEventLoopGroup,构造参数是1表示是单线程的线程池。然后接着我们调用 b.group(bossGroup) 设置了服务器端的 EventLoopGroup. 有些朋友可能会有疑惑: 我记得在启动服务器端的 Netty 程序时, 是需要设置 bossGroup 和 workerGroup 的, 为什么这里就只有一个 bossGroup?
其实很简单, ServerBootstrap 重写了 group 方法:
@Override
public ServerBootstrap group(EventLoopGroup group) {
return group(group, group);
}
因此当传入一个 group 时, 那么 bossGroup 和 workerGroup 就是同一个 NioEventLoopGroup 了。这时候呢, 因为 bossGroup 和 workerGroup 就是同一个 NioEventLoopGroup, 并且这个 NioEventLoopGroup 只有一个线程, 这样就会导致 Netty 中的 acceptor 和后续的所有客户端连接的 IO 操作都是在一个线程中处理的。
对应到 Reactor 的线程模型中, 我们这样设置 NioEventLoopGroup 时, 就相当于 Reactor 单线程模型。
- 多线程
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class);
//...
bossGroup 中只有一个线程, 在workerGroup线程池中我没有指定线程数量,所以默认是 CPU 核心数乘以2, 因此对应的到 Reactor 线程模型中, 我们知道, 这样设置的 NioEventLoopGroup 其实就是 Reactor 多线程模型.
- 主从线程
EventLoopGroup bossGroup = new NioEventLoopGroup(4);
EventLoopGroup workerGroup = new NioEventLoopGroup();
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class);
//...
Netty 的服务器端的 acceptor 阶段, 没有使用到多线程, 因此上面的主从多线程模型 在 Netty 的服务器端是不存在的.
服务器端的 ServerSocketChannel 只绑定到了 bossGroup 中的一个线程, 因此在调用 Java NIO 的 Selector.select 处理客户端的连接请求时, 实际上是在一个线程中的, 所以对只有一个服务的应用来说, bossGroup 设置多个线程是没有什么作用的, 反而还会造成资源浪费。
经 Google, Netty 中的 bossGroup 为什么使用线程池的原因大家众所纷纭, 不过我在 stackoverflow 上找到一个比较靠谱的答案:
the creator of Netty says multiple boss threads are useful if we share NioEventLoopGroup between different server bootstraps, but I don’t see the reason for it.
意思就是说:netty作者说:我们在不同的服务器引导之间共享NioEventLoopGroup,多个boss线程是有用的,但我没有看到它的原因。
-
channel:设置channel通道类型NioServerSocketChannel、OioServerSocketChannel(过期不推荐使用)
-
childHandler: 用于对每个通道里面的数据处理;
-
option: 作用于每个新建立的channel,设置TCP连接中的一些参数,如下
* ChannelOption.SO_BACKLOG: 存放已完成三次握手的请求的等待队列的最大长度; * ChannelOption.TCP_NODELAY: 为了解决Nagle的算法问题,默认是false, 要求高实时性,有数据时马上发送,就将该选项设置为true关闭Nagle算法;如果要减少发送次数,就设置为false,会累积一定大小后再发送; * 知识拓展: [https://baike.baidu.com/item/Nagle%E7%AE%97%E6%B3%95/5645172](https://baike.baidu.com/item/Nagle%E7%AE%97%E6%B3%95/5645172) [https://www.2cto.com/article/201309/241096.html](https://www.2cto.com/article/201309/241096.html)
-
childOption: 作用于被accept之后的连接
- 客户端启动引导类Bootstrap
- remoteAddress: 服务端地址
- handler:和服务端通信的处理器
补充网络安全知识
TCP三次握手Linux服务器TCP连接底层知识:
- syn queue:半连接队列,洪水攻击,tcp_max_syn_backlog。
- accept queue:全连接队列, net.core.somaxconn。
- 系统默认的somaxconn参数要足够大 ,如果backlog比somaxconn大,则会优先用后者 https://github.com/netty/netty/blob/4.1/common/src/main/java/io/netty/util/NetUtil.java#L250