掘金 后端 ( ) • 2024-03-31 21:20

theme: cyanosis

大家好,我是大明哥,一个专注「死磕 Java」系列创作的硬核程序员。 本文已收录到我的技术网站:https://skjava.com。有全网最优质的系列文章、Java 全栈技术文档以及大厂完整面经


回答

Reactor 模式是一种高效处理并发网络事件的设计模式,它通过一组 Reactor 线程(事件循环组)来监听和分发网络事件(如连接、读取、写入),并结合非阻塞 I/O 和用户定义的事件处理器来实现。

Reactor 模式的核心思想是把响应 IO 事件和业务处理进行分离。它通过一个或者多个线程监听 I/O 事件,将已经准备就绪的 I/O 事件分发给业务线程去处理。其核心组件有三个:

  • Reactor 组件:这是 Reactor 模式的核心。Reactor 组件负责监听和分发事件。在一个无限循环中,它等待 I/O 事件的到来,然后将这些就绪的 I/O 事件快速分发给相应的处理程序。在 Netty 中,这通常对应于 EventLoop 组件。
  • Acceptor 组件:请求连接器。Reactor 组件接收到 client 连接事件后,会将其转发给 Acceptor,Acceptor 则会接受 Client 的连接,建立对应的Handler,并向 Reactor注册此Handler。
  • Handler 组件:求处理器,负责具体事件的处理程序。当 Reactor 组件将事件分发给它们时,它们负责处理这些事件。在 Netty 中,这些处理器通常是用户自定义的 ChannelHandler,用于处理特定的事件,如接收数据、异常处理等。

在 Reactor 模式中,主要有三种模型:

  • 单 Reactor 单线程模型。
  • 多线程 Reactor 模型。
  • 主从多 Reactor 多线程模型。

详解

单 Reactor 单线程模型

单线程 Reactor 模型,即所有的 I/O 处理和业务逻辑都在同一个线程(即 Reactor 线程)中执行。

处理流程如下

  • Reactor 通过 Select 监听 I/O 事件,收到事件后由 Dispatch 来分发。
  • 如果是建立连接事件,则由 Acceptor 进行处理,Acceptor 会通过 accept 方法获取链接,并创建一个 Handler 对象来处理后续的响应事件。
  • 如果不是建立连接事件,则将该事件交由当前连接的 Handler 来处理。Handler 按照 read —> 业务处理 —> send 的流程来完成整个事件。

优点

该模型是将所有处理逻辑放在一个线程中实现,模型简单,没有多线程、进程通信、竞争的问题

缺点

由于只有一个线程,无法充分利用多核CPU 的性能,性能堪忧。同时Handler 在处理某个连接上的业务时,整个进程无法处理其他连接事件,很容易导致性能瓶颈。

还有一个比较严重的可靠性问题,如果线程意外终止,或者进入死循环,则会导致整个线程都无法接受和处理事件了,造成节点故障。

单 Reactor 多线程

单线程存在性能瓶颈,那我们就引入多线程方案。

多线程 Reactor 模型,它将处理 I/O 就绪时间的线程和处理业务逻辑 Handler的线程分开了,每个 Handler 由一个独立的线程来处理。

Reactor 接受请求后,根据请求类型来进行分发,分发逻辑与单Reactor单线程模型一样,不同之处在于单 Reactor 多线程 的 Handler 不再进行业务处理了,它只负责接受和发送:Handler接受数据后,会将数据发送给 Worker 线程池中的线程处理,该线程才是处理业务的真正线程,线程将业务处理完成后,将数据发送给 Handler,由 Handler 再 send 出去。

优点

由于 Handler 使用了多线程模式,则可以利用充分利用CPU的性能。

缺点

Handler使用多线程模式,则会涉及到数据共享的问题,需要考虑互斥,实现肯定比单Reactor单线程模式复杂一些。但是在实际开发过程中,我们一般都会让业务处理是无状态的,一般不会用共享变量,所以在大多数业务场景中 Handler 的开发并不会复杂太多。

单Reactor,一个线程处理事件监听、分发、响应,对于高并发场景,容易造成性能瓶颈。

多 Reactor 多线程模型

单Reactor多线程模式虽然解决了 Handler 单线程的性能问题,但是 Reactor 还是单线程的,对于高并发场景还是会有性能瓶颈,所以需要对 Reactor 也调整为多线程模式

  • 主线程中的 MainReactor 对象通过 select 监听事件,接收到事件后通过 Dispatch 进行分发,如果事件类型为建立连接则将事件分发给 Acceptor 进行连接建立
  • 如果收到的事件不是连接,则他将事件分发个某个 SubReactor,SubrReactor 将连接加入到连接队列进行监听,并创建 Handler 进行各种事件处理
  • 如果有新的事件发生,SubReactor 则会调用当前连接的 Handler 来进行处理。Handler 通过 read 读取数据后,将数据发送给 Worker 线程进行处理,Worker 线程池则会分配线程进行业务处理,处理完成后返回结果,Handler 接受结果后,通过 send 发送给客户端

优点

该模式主线程和子线程分工明确,主线程只负责接收新连接,子线程负责完成后续的业务处理,同时主线程和子线程的交互也很简单,子线程接收主线程的连接后,只管业务处理即可,无须关注主线程

缺点

模型复杂。

这种模式适用于高并发场景,广泛运用于各种项目中,如大名鼎鼎的Netty。