Java多线程高并发系列:(十)Reactor线程模型

2025-02-27 19:40
370
0

Reactor 线程模型是一种事件驱动的并发编程模型,通过事件驱动+NIO多路复用,尤其适合网络服务器开发。其核心思想是将事件分发与业务处理解耦,通过有限的线程处理大量并发连接。

Reactor 模式是基于事件驱动的,它有一个或多个输入源,如网络连接、文件描述符等。Reactor 线程会不断地监听这些输入源上的事件,比如数据可读、可写等事件。当有事件发生时,Reactor 会将事件分发给相应的事件处理器(EventHandler)来处理,从而实现对各种 I/O 操作的异步处理。

常见的Reactor线程模型有三种,分别如下:

一、Reactor单线程模型

单线程模型是 Reactor 线程模型中最基础、最简单的一种实现方式。在这个模型里,所有的 I/O 操作(包括连接的建立、数据的读写)以及业务逻辑的处理都在同一个NIO线程上面完成。该线程会不断地监听多个 I/O 事件源,当有事件发生时,将事件分发给相应的事件处理器进行处理。

上图只是根据功能拆分出不同的几个类,但都在同一个线程中执行。

  • Reactor线程:有客户端连接进来,将客户端连接交给Acceptor处理
  • 接收者(Acceptor):监听服务端,接收请求存储起来,交给调度器处理。
  • 调度器(Dispatcher):如果请求类型不同需要不同的Handler,则可以用调度器对Handler进行调度管理。
  • 处理器(Handler):每个客户端对应一个Handler,处理不同业务负责从channel中读数据、处理数据、写数据。

由于Reactor模型使用的是同步非阻塞IO,所有的IO操作都不会导致阻塞,理论上一个线程可以独立处理所有IO相关操作。从架构层面看,一个NIO线程确实可以完成其承担的职责。例如,通过Acceptor接收客户端的TCP连接请求消息,链路建立成功之后,通过Dispatcher将对应的ByteBuffer派发到指定的Handler上进行消息编码,然后Handler可以通过NIO线程将消息发送给客户端。

对于一些小容量应用场景,可以使用单线程模型,但是对于高负载、大并发的应用却不合适,主要原因如下:

  1. 一个NIO线程同时处理成百上千的链路,性能上无法支撑。即便NIO线程的CPU负荷达到100%,也无法满足海量消息的编码、解码、读取和发送;
  2. 当NIO线程负载过重后,处理速度将变慢,这会导致大量客户端连接超时,超时之后往往会进行重发,这更加重了NIO线程的负载,最终会导致大量消息积压和处理超时,NIO线程会成为系统的性能瓶颈;
  3. 可靠性问题。一旦NIO线程意外进入死循环,会导致整个系统通信模块不可用,不能接收和处理外部消息,造成节点故障。

为了解决这些问题,从而演进出了Reactor多线程模型。

NIO相关知识可以查看我的另一篇文章:《Java多线程高并发系列:(九)BIO与NIO》

二、Reactor多线程模型

Reactor 多线程模型引入了多个线程来分担任务,主要由 Reactor 线程和工作线程池组成。Reactor 线程负责监听网络事件,如连接请求、数据读写等,将这些事件分发给对应的处理器。工作线程池则用于执行具体的业务逻辑处理,从而避免了在单线程中处理大量业务逻辑可能导致的阻塞问题,提高了系统的并发处理能力。

在绝大多数场景下,Reactor多线程模型都可以满足性能需求;但是,在海量连接场景中,一个NIO线程负责监听和处理所有的客户端连接可能会存在性能问题。例如百万客户端并发连接,或者服务端需要对客户端的握手消息进行安全认证,认证本身非常损耗性能。在这类场景下,单独一个Acceptor线程可能会存在性能不足问题,为了解决性能问题,产生了第三种Reactor线程模型——主从Reactor多线程模型。

三、主从Reactor多线程模型

Reactor 主从多线程模型是在 Reactor 多线程模型基础上进一步优化和扩展的一种网络编程模型。


主从Reactor线程模型的特点是:服务端用于接收客户端连接的不再是一个单独的NIO线程,而是一个独立的NIO线程池。Acceptor接收到客户端TCP连接请求处理完成后(可能包含接入认证等),将新创建的SocketChannel注册到IO线程池(subReactor线程池)的某个IO线程上,由它负责SocketChannel的读写和编解码工作。Acceptor线程池只用于客户端的登录、握手和安全认证,一旦链路建立成功,就将链路注册到后端subReactor线程池的IO线程上,由IO线程负责后续的IO操作。

利用主从NIO线程模型,可以解决服务端1个监听线程无法有效处理所有客户端连接的性能不足问题。Netty官方推荐使用该线程模型。它的工作流程总结如下:

  1. 1、从主线程池中随机选择一个Reactor线程作为Acceptor线程,用于绑定监听端口,接收客户端连接。
  2. 2、Acceptor线程接收客户端连接请求之后,创建新的SocketChannel,将其注册到主线程池的其他Reactor线程上,由其负责接入认证、IP黑白名单过滤、握手等操作。
  3. 3、然后业务层的链路正式建立成功,将SocketChannel从主线程池的Reactor线程的多路复用器上摘除,重新注册到Sub线程池的线程上,用于处理IO的读写操作。

 

 

全部评论