Netty「基石」之Reactor模式

1,698 阅读6分钟

「我正在参与掘金会员专属活动-源码共读第一期,点击参与

前言

本篇文章主要讲解Reactor模型, Reactor线程模式经典的很, 但是还是有很多小伙伴不知道, 接下来我们一起学习一下Netty的基础——Reactor模型

往期文章:

Reactor 模型特性

  • Reactor模型是基于事件驱动的, 通过一个或多个输入同时传递给服务端处理
  • 服务端程序处理传入的多个请求, 并发到相应的处理程序
  • 基于 IO多路复用技术, 多个连接同时共用一个多路复用器, 应用程序只需在一个阻塞对象等待, 无需阻塞等到所有连接, 当某个连接上有新数据可以处理时, 应用程序的线程从阻塞状态返回, 开始处理这个连接上的业务
  • 基于线程池技术复用线程资源: 不必为每个连接创建专用的线程, 应用程序将连接上的业务处理任务分配给线程池中的线程进行处理, 一个线程可以处理多个连接的业务

传统 BIO 服务

在传统的 BIO服务端采用的是单线程单连接的处理模型, 这种线程模型的弊端很明显, 当有大量的客户端并发时服务端会压力剧增, 同时线程利用率很低

image.png

相应的, 在传统BIO服务上的Reactor模型的基本形态如下图所示

image.png

单 Reactor 单线程模型

image.png

图片来源于网络

这种模式的工作流程如下:

  • Reactor监听客户端请求事件, 收到事件之后通过dispatch进行分发
  • 如果事件是建立连接的请求事件, 则由Acceptor通过accept处理连接请求, 然后创建一个Handler对象处理完连接之后的后续业务处理
  • 如果不是建立连接的请求事件, 则由Reactor分发给对应的Handler处理
    • Handler只负责响应事件, 不做具体的业务处理操作
  • Handler处理流程:
    • read 读取数据
    • worker 线程池处理
    • 处理之后返回给 Handler
    • send 发送结果返回给 client

优点

  • 模型简单
  • 没有多线程, 进程通信, 竞争的问题

缺点

  • 存在性能问题, 只有单线程, 无法完全发挥 CPU 的性能, 在Handler处理某个连接上个业务的时候, 无法处理其他连接事件, 容易导致性能瓶颈
  • 存在可靠性问题, 若线程意外终止或者进入死循环, 则整个系统都将不可用, 不能接收和处理消息

单 Reactor 多线程模型

image.png

图片来源于网络

这种模式的工作流程如下:

  • Reactor对象监听连接事件, 收到事件后通过dispatch进行分发
  • 如果事件建立连接的请求事件,则由acceptor接收连接, 然后创建一个Handler对象处理连接建立后的业务
  • 如果事件不是建立连接的请求事件,则由Reactor对象分发给连接对应的Handler处理
  • worker线程池会分配独立线程来完成真正的业务处理, 并将处理结果返回给Handler, Handler通过Send向客户端发送相应数据

优点

充分利用了多核CPU的处理能力

缺点

多线程数据共享和控制比较复杂, Reactor处理所有的事件监听和响应都在单线程内运行, 还是很容易出现性能瓶颈

主从 Reactor 多线程模型

主从Reactor模型的核心思想: 主反应堆栈只负责分发Acceptor的连接建立, 已连接套接字上的IO事件交给SubReactor负责分发, 其中SubReactor的数量可以根据 CPU核心的数量灵活配置

image.png

图片来源于网络

工作流程

  • Reactor对象监听连接事件, 收到事件后通过Acceptor处理客户端连接事件
  • Acceptor处理完客户端连接事件之后, MainReactor将连接分配给SubReactor
  • SubReactor将连接加入自己的连接队列进行监听, 并创建Handler对事件进行处理
  • 当连接上有新的事件发生,SubReactor就会调用相应的Handler进行处理
  • Handler通过read从连接上读取数据, 并分发给worker线程池进行处理
  • worker线程池会分配独立线程来完成业务处理, 并将结果返回给Handler
  • Handler收到worker线程池的结果之后, 通过send向客户端返回相应数据

一个MainReactor可以对应多个SubReactor

优点

  • MainReactor线程与SubReactor线程的职责明确
    • MainReactor只负责接受新连接
    • SubReactor只负责数据的业务处理
  • MainReactor线程与SubReactor线程的数据交互简单
    • MainReactor只需要将新数据传递给SubReactor
    • SubReactor不需要将数据传递回给MainReactor, 直接将数据返回给客户端
  • 多个SubReactor能够应对更高的并发请求

缺点

编程负责度较高

Netty 的 Reactor 模型

image.png

图片来源于网络

  • Netty有两组线程池, 分别是BossNioEventLoopGroupWorkerNIOEventLoopGroup, 每个线程池都是一组NioEventLoop, BossNioEventLoopGroup主要负责与客户端建立连接, workerNioEventLoopGroup主要负责处理连接上的读写
  • NioEventLoopGroup相当于一个事件循环组, 在这个组内有很多个NioEventLoop死循环处理事件的线程, 每个NioEventLoop都包含一个Selector, 用于监听Channel
  • 每个事件循环组BossNioEventLoopGroup都会执行以下三个步骤
    • select:轮询注册在SererSocketChannel上的appect事件
    • processSelectedKeys:处理appect事件, 与客户端建立连接, 生成一个NioSocketChannel, 并将其注册到某一个NioEventLoopSelector
    • runAllTasks: 再去循环处理任务队列中的其他任务
  • 每个事件循环组WorkerNioEventLoopGroup都会执行以下三个步骤
    • select:轮询注册在SererSocketChannel上的read事件和write事件
    • processSelectedKeys:在对应的Channel上处理read事件和write`事件
    • runAllTasks: 再去循环处理任务队列中的其他任务
  • 在上面两个processSelectedKeys步骤中,会使用Pipeline管道),Pipeline中引用了 Channel,即通过Pipeline可以获取到对应的ChannelPipeline中维护了很多的处理器(拦截处理器、过滤处理器、自定义处理器等)



本文内容到此结束了

如有收获欢迎点赞👍收藏💖关注✔️,您的鼓励是我最大的动力。

如有错误❌疑问💬欢迎各位大佬指出。

我是 宁轩 , 我们下次再见