I/O 传输问题决定着用什么样的通道将数据发送给对方,BIO、NIO 或者 AIO,IO 模型在很大程度上决定了框架的性能。
线程模型问题决定着数据报如何读取?读取之后的编解码在哪个线程进行,编解码后的消息如何派发,Reactor 线程模型的不同,对性能的影响非常大。
- Netty的两个线程池,为什么两个,有什么区别,具体说来。
- Netty初始化的时候需要初始化两个线程池,你能简单说一说吗?
- 线程池默认多少个线程,为什么这么设置?(netty自带的,默认CPU*2)
一、I/O多路复用
9.2 I/O 多路复用:select/poll/epoll | 小林coding (xiaolincoding.com)
I/O 多路复用的特点是通过一种机制使得一个进程能同时等待多个文件描述符,而这些文件描述符(套接字描述符)其中的任意一个进入读就绪状态,select()函数就可以返回,所以说它最大的优势是系统开销小,系统不需要创建或维护新的进程/线程。
select/poll 方式
epoll 方式
二、Netty采用异步非阻塞通信
Netty 的 IO 线程 NioEventLoop 由于聚合了多路复用器 Selector,可以同时并发处理成百上千个客户端 Channel,由于读写操作都是非阻塞的,这就可以充分提升 IO 线程的运行效率,避免由于频繁 IO 阻塞导致的线程挂起。另外,由于 Netty 采用了异步通信模式,一个 IO 线程可以并发处理 N 个客户端连接和读写操作,这从根本上解决了传统同步阻塞 IO 一连接一线程模型,架构的性能、弹性伸缩能力和可靠性都得到了极大的提升。
三、Reactor模型
Reactor 模型 和 Proactor 模型-CSDN博客
单线程模型:所有I/O操作都由一个线程完成,即多路复用、事件分发和处理都是在一个Reactor 线程上完成的。既要接收客户端的连接请求,向服务端发起连接,又要发送/读取请求或应答/响应消息。一个NIO 线程同时处理成百上千的链路,性能上无法支撑,速度慢,若线程进入死循环,整个程序不可用,对于高负载、大并发的应用场景不合适。
多线程模型:有一个NIO 线程(Acceptor) 只负责监听服务端,接收客户端的TCP 连接请求;NIO 线程池负责网络IO 的操作,即消息的读取、解码、编码和发送;1 个NIO 线程可以同时处理N 条链路,但是1 个链路只对应1 个NIO 线程,这是为了防止发生并发操作问题。但在并发百万客户端连接或需要安全认证时,一个Acceptor 线程可能会存在性能不足问题。
主从多线程模型:Acceptor 线程用于绑定监听端口,接收客户端连接,将SocketChannel 从主线程池的 Reactor 线程的多路复用器上移除,重新注册到Sub 线程池的线程上,用于处理I/O 的读写等操作,从而保证 mainReactor 只负责接入认证、握手等操作;
四、Netty 架构按照 Reactor 模式设计和实现
在 Netty 的官方 demo 中,推荐使用主从多线程模型。Netty 通过 Reactor 模型基于多路复用器接收并处理用户请求,内部实现了两个线程池, boss 线程池和 worker 线程池,其中 boss 线程池的线程负责处理请求的 accept 事件,当接收到 accept 事件的请求时,把对应的 socket 封装到一个 NioSocketChannel 中,并交给 worker 线程池,其中 worker 线程池负责请求的 read 和 write 事件,由对应的Handler 处理。
事实上,Netty 的线程模型并非固定不变,通过在启动辅助类中创建不同的 EventLoopGroup 实例并通过适当的参数配置,就可以支持上述三种 Reactor 线程模型。正是因为 Netty 对 Reactor 线程模型的支持提供了灵活的定制能力,所以可以满足不同业务场景的性能诉求。
//单线程模型
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup(1);
//多线程模型
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup(); //默认使用2*CPU核数量的线程池
//主从多线程模型
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
//服务端启动器
ServerBootstrap b = new ServerBootstrap();
//绑定两个工作线程组
b.group(bossGroup, workerGroup)
...
在这段代码中 bossGroup 对应的就是主反应组(MainReactor),workerGroup 对应的是子反应组(subReactor),而NioEventLoopGroup
其实就是一个实现了 Java ExecutorService
的线程池,其中的线程数可定制,若不设置线程数参数,则该参数值默认为2 * CPU核数量
,在ServerBootstrap
的初始化过程中,会为其添加一个实现了acceptor
机制的Handler。