Reactor 模式

前言

image-20230624110659064

针对传统阻塞 I/O 服务模型的 2 个缺点:

  • 当并发数很大,就会创建大量的线程,占用很大系统资源
  • 连接创建后,如果当前线程暂时没有数据可读,该线程会阻塞在 Handler对象中的read 操作,导致上面的处理线程资源浪费

Reactor 模式的解决方案:本质就是I/O多路复用

  • 多个连接共用一个阻塞对象ServiceHandler,应用程序只需要在一个阻塞对象等待,无需阻塞等待所有连接。
  • 当某个连接有新的数据可以处理时,操作系统通知应用程序,线程从阻塞状态返回,开始进行业务处理。

概述

Reactor 模式也叫 Dispatcher 模式,即 I/O 多路复用监听事件,收到事件后,根据事件类型分配(Dispatch)给某个线程

核心组件:

  • Reactor(也就是那个ServiceHandler):

    Reactor 在一个单独的线程中运行,负责监听和分发事件,分发给适当的处理线程来对 IO 事件做出反应。

  • Handlers(处理线程EventHandler):

    处理线程执行 I/O 事件要完成的实际事件。

分类:

  • Reactor 单线程
  • Reactor 多线程
  • 主从 Reactor 多线程

单Reactor单线程

通过单一Reactor调用select来监听网络的连接请求,收到事件后通过 Dispatch 进行分发。

  • 如果是建立连接请求事件,则由 Acceptor 通过 Accept 处理连接请求,然后创建一个 Handler 对象处理连接完成后的后续业务处理
  • 如果不是建立连接事件,则 Reactor 会分发调用连接对应的 Handler 来响应

结构图:

image-20230624111254368

缺点:

性能问题,只有一个线程,无法完全发挥多核 CPU 的性能。Handler在处理某个连接上的业务时,整个进程无法处理其他连接事件,很容易导致性能瓶颈、甚至导致整个节点故障。

Netty对应的代码

1
2
3
EventLoopGroup eventGroup = new NioEventLoopGroup(1);
serverBootstrap serverBootstrap = new serverBootstrap();
serverBootstrap.group(eventGroup);

单 Reactor多线程

同样是通过单一Reactor调用select来监听网络的连接请求,收到事件后通过 Dispatch 进行分发。

  • 如果是建立连接请求事件,则由 Acceptor 通过 Accept 处理连接请求,然后创建一个 Handler 对象处理连接完成后的后续业务处理。
  • 如果不是连接请求,则由 Reactor 分发调用连接对应的 handler 来处理,但是这里handler只负责响应事件,不负责处理事件,而是交由Worker线程来进行业务处理。

结构图:

image-20230624153915017

缺点:

  • 单一的Reactor承担了所有时间监听,性能瓶颈明显,多线程数据共享和访问比较复杂。

Netty对应的代码

1
2
3
4
// 默认2倍核心数
EventLoopGroup eventGroup = new NioEventLoopfroup();
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(eventGroup);

主从 Reactor 多线程

优化了前两种方案的单一Reactor性能瓶颈问题,这个方案让Reactor以主从的多线程方式运行。

  • Reactor 主线程 MainReactor 对象通过 select 监听连接事件,收到事件后,通过 Acceptor 处理连接事件。
  • Acceptor 处理连接事件后,MainReactor 将连接分配给 SubReactorSubReactor再将对应事件交由handler处理。
  • handler再分发给后面的worker 线程处理。

结构图:

image-20230624154445226

Netty对应的代码

1
2
3
4
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(bossGroup, workerGroup);