Fork me on GitHub

Reactor模型的三种实现

前言

Netty,一个无法拒绝的基于java语言编写的网络通信框架,其线程模型是最为复杂,也是面试官最爱考的问题之一。Netty的线程模型是基于Reactor模型发展来的,所以有必要了解Reactor模型的发展历程。

经典模式

最开始的网络服务设计,即传统BIO JAVA网络服务那样,一个客户端请求过来,服务端分配一个hander处理read->biz->send等所有工作。如图: 传统BIO网络模式.png

Reactor 模式

与传统BIO服务不同,Reactor模式是基于事件驱动的。 对每个IO事件,Reactor通过分发(dispatch)给不同的hander处理各自的业务请求。简单的说,就是IO复用+线程池的组合,如图: Reactor模式基础架构.png 解释:

  1. 客户端发起请求事件到Reactor,Reactor通过IO复用接受请求事件。
  2. Reactor不处理业务操作,而是把业务处理操作分配给工作线程池处理。
  3. 工作线程完成业务操作后再将结果返回给客户端。

单Reactor单线程

即通过一个main线程处理接受请求事件->read->handle biz->send 一系列的操作。 单Reactor单线程.png 这种方式的缺点非常明显,只用一个线程处理,非常容易出现瓶颈,而且无法充分利用现代计算机多核CPU的优势;低可靠性,一旦main线程意外,则整个系统通信模块就会故障,节点不可用。

优点:模型简单,没有多线程、多进程通信,不存在竞争问题。

单Reactor多线程

单Reactor多线程.png 解释:

  1. Reactor通过acceptor接受客户端请求事件。
  2. Reactor接受到事件后,把相应的业务分发出给相应的hander处理,hander负责read和send响应数据。
  3. hander只负责响应事件,不做具体业务处理,通过read读取数据后,会把业务交给线程池的某个worker线程处理业务。
  4. 线程处理完业务后,会把响应返回给hander,再由hander响应,通过send发送给客户端。
  • 优点:相较于单Reactor单线程模式,它把业务操作放在线程池里处理,能充分利用CPU多核处理能力
  • 缺点:多线程数据共享和访问比较复杂,Reactor处理所有事件监听和响应,Reactor还是单线程的,高并发场景还是容易出现性能瓶颈。

主从多线程

针对单Reactor多线程模型中,Reactor只有一个线程,在高并发场景下很容易出现性能瓶颈,因此可以使Reactor在多线程中运行,如图。 主从多线程模式.png

针对单Reactor多线程模型中,Reactor只有一个线程,在高并发场景下很容易出现性能瓶颈,因此可以使Reactor在多线程中运行,如图。

解释:

  1. Reactor主线程通过 mainReactor 对象通过select监听连接事件,收到事件交给 acceptor 处理连接事件。
  2. acceptor 处理连接事件后,mainReactor 将连接分配给一个 subReactor。
  3. subReactor 将连接键入连接队列进行监听,并创建 handler 进行事件处理。
  4. handler 在 read 操作后再将连接交给工作线程池(worker thread pool)处理业务,由线程池分配独立线程进行业务操作。
  5. 线程处理完业务后,会把响应返回给hander,再由hander响应,通过send发送给客户端。
  6. mainReactor 可以对于多个 subReactor ,因此称此模型为主从多线程模型。
  • 优点:父线程和子线程的数据交互简单,职责明确,父线程只需要接受新连接,而子线程只需完成业务处理,子线程也无须返回数据。
  • 缺点:复杂度高

后记

参考资料:《Scalable IO In Java》,是java.util.concurrent包的作者,大师Doug Lea关于分析与构建可伸缩的高性能IO服务的一篇经典文章。本文的图片截图均截自此书,建议先阅读大师的经典文章。

-------------本文结束,感谢您的阅读-------------
贵在坚持,如果您觉得本文还不错,不妨打赏一下~
0%