1 Reactor 线程模型
讨论 Netty 线程模型的时候,一般会想到经典的 Reactor 线程模型。尽管不同的 NIO 框架对 Reactor 线程模型实现不太一样,但本质上还是遵循 Reactor 基础线程模型
1.1 单线程模型
所有的 IO 操作都在一个 NIO 线程完成,NIO 线程职责如下
- 作为 NIO 服务端,接收客户 TCP 连接
- 作为 NIO 客户端,向服务端发起 TCP 连接
- 读取通信对端请求或应答消息
- 向通信对端发送请求或应答消息
单线程模型存在如下问题:
- 性能问题:一个 NIO 处理成千上万条链路,消息编解码、读取、发送处理不及时
- 可靠性问题:一个 NIO 线程意外中断或进入死循环则会导致整个系统通信模块不可以
1.2 多线程模型
多线程模型最大特点是一组 NIO 线程来处理 IO 操作,特点如下
- 一个专门的 NIO 线程 - Acceptor 线程用于监听服务端,接收客户端的 TCP 连接请求
- 网络 I/O操作、读写等由一个 NIO 线程池负责
- 一个线程可以操作多条链路,每个链路只由一个线程负责,防止出现并发问题
多线程模型存在如下问题:
如果百万并发客户端连接或者服务端要进行比较耗时的安全认证可能会出现一个 Acceptor 线程性能不足的问题
1.3 主从多线程模型
主从多线程模型特点是用于处理客户端连接不再是一个 NIO 线程,而是一个线程池,可以有效解决上述问题
2 Netty 线程模型
Netty 线程模型不是一成不变的,通过设置不同的启动参数来实现不同的线程模型
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workGroup = new NioEventLoopGroup();
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(bossGroup, workGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 1024)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
List<ChannelHandler> handlers = supplier.get();
for (ChannelHandler handler: handlers) {
socketChannel.pipeline().addLast(handler);
}
}
});
服务端启动的时候,创建了两个 EventLoopGroup,实际上是两个独立的 Reactor 线程池,一个用于接收客户端 TCP 连接,一个用于处理 IO 相关的读写操作