在编写Netty服务端程序的时候,我们设置了两个线程池,一个用于处理 Accept 事件,一个用于处理读写事件,这个其实就是Reactor模式。
一般网络请求都要经过以下几个处理过程:
每次来一个客户端都启动一个新的线程来处理,每一个handler都在它自己的线程中。
这种方式编码简单,但是,随着服务请求量越来越大,启动的线程数量会越来越多,最后,会导致服务端的线程无限增多,然而,其实大部分的线程可能都处于 IO 阻塞状态,并没有使用到 CPU,无法充分利用 CPU。
单线程模式下,所有客户端的所有事件都在一个线程中完成,这就出现了一个新的问题,如果哪个请求有阻塞,直接影响了所有请求的处理速度,所以,就进化出了 Reactor 的多线程模式。
还是把 IO 事件放在 Reactor 线程中来处理,同时,把业务处理逻辑放到单独的线程池中来处理,这个线程池我们称为工作线程池或者业务线程池。
此时,如果业务处理逻辑中有 IO 阻塞,则不会影响其它请求的处理,能很大程度提高系统的并发量。
但是,这种模式还不够完美,一个客户端连接过程需要三次握手,是一个比较耗时的操作,将 Accept 事件和 Read 事件与 Write 事件放在一个 Reactor 中来处理,明显降低了 Read 和 Write 事件的响应速度。而且,一个 Reactor 只有一个线程,也无法利用多核 CPU 的性能提升。因此,又自然而然的出现了 Reactor 主从模式。
Reactor 主从模式把 Accept 事件的处理单独拿出来放到主 Reactor 中来处理,把 Read 和 Write 事件放到子 Reactor 中来处理,而且,像这样的子 Reactor 我们可以启动多个,充分利用多核 CPU 的资源。
基于主从模式可以有很多种变异的模式,比如使用子 Reactor 线程池来处理业务逻辑。
Reactor 单线程模式,只有一个 Reactor,也就是一个线程处理所有事务,所以,在 Netty 中,只需要声明一个 EventLoopGroup 就可以了。
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(bossGroup);
Reactor 多线程模式,实际上还是只有一个 Reactor,但是这个 Reactor 只负责处理 IO 事件,而不负责处理业务逻辑,所以,在 Netty 中,需要将业务逻辑的处理,也就是 Handler,放到另外的线程池中。
EventLoopGroup bossGroup = new NioEventLoopGroup(1); // 一个Reactor
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(bossGroup);
// Handler使用线程池处理
Reactor 主从模式,有一个主 Reactor 和多个子 Reactor,但是,业务逻辑的处理还是在线程池中,所以,在 Netty 中,需要声明两个不同的 EventLoopGroup,Handler 依然使用线程池处理。
EventLoopGroup bossGroup = new NioEventLoopGroup(1); // 一个主Reactor
EventLoopGroup workerGroup = new NioEventLoopGroup(); // 多个子Reactor
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(bossGroup, workerGroup);
// Handler使用线程池处理
Reactor 变异主从模式,业务线程池和子 Reactor 池合并为一,所以,在 Netty 中,Handler 放在子 Reactor 池中处理即可,默认情况,Netty 也是使用的这种模式。
EventLoopGroup bossGroup = new NioEventLoopGroup(1); // 一个主Reactor
EventLoopGroup workerGroup = new NioEventLoopGroup(); // 多个子Reactor
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(bossGroup, workerGroup);