高性能Server的基石:reactor反应堆模式

   业务开发同学只关心业务处理流程。但是我们开发的程序都是运行服务端server上,服务端server接收到IO请求后,是如何处理请求并最终进入业务流程的呢?这里不得不提到reactor反应堆模型。reactor反应堆模型来源于大师Doug Lea在 《Sacalable io in java》中的设计。nginx tomcat redis nodejs dubbo netty 等软件的网络处理模型都是用的reactor反应堆模型。

前置知识

通用网络请求处理流程

一般所有的网络服务,一般分为如下几个步骤:

  • 读请求(read request)
  • 读解析(read decode)
  • 处理程序(process service)
  • 应答编码 (encode reply)
  • 发送应答(send reply)
C10K问题

C10K 问题是这样的:如何在一台物理机上同时服务 10000 个用户?这里 C 表示并发,10K 等于 10000。C10K问题早已解决,现在面临的是C10M问题。

网络IO

请添加图片描述

一次完整的网络IO流程

  • 用户A发起网络写操作
  • 用户B发起网络读操作
  1. 数据准备阶段:内核等待IO设备准备好数据
  2. 数据拷贝阶段:将数据从内核缓冲区拷贝到用户空间缓冲区

阻塞IO

读数据时,如果内核缓冲区没有数据,执行read()操作的线程会挂起在这里,不能做其他的事情

多路复用IO

  IO多路复用是一种高效的IO模型,它的特点是可以同时监控多个文件描述符,提高了应用程序对输入输出操作的管理能力。当用户进程使用select、poll或epoll等系统调用时,它会将需要监控的文件描述符传递给内核,然后内核会在所有文件描述符中寻找就绪的文件描述符,并返回给用户进程。用户进程再根据就绪的文件描述符进行相应的读写操作。

  IO多路复用的优点是可以使用一个线程或进程来处理多个文件描述符,避免了多线程或多进程的开销,提高了系统的并发性和可伸缩性。IO多路复用的缺点是需要额外的系统调用来管理文件描述符,而且在数据拷贝阶段仍然是阻塞的。IO多路复用比较适合网络编程,比如服务器端的并发处理。

两类经典的网络模式

基于线程(进程)的 Thread(Process)-per-connection

请添加图片描述

TPC是Thread Per Connection的缩写,其含义是指每次有新的连接就新建一个线程去专门处理这个连接的请求。

缺点:

  1. 每新增一个客户端需要服务端新开一个线程支持,线程是很重的资源。
  2. 线程多了后,线程上下文切换很耗cpu
  3. 连接建立后,如果当前线程暂时没有数据可读,则线程就阻塞在 Read 操作上,造成线程资源浪费。
基于事件驱动的Reactor模式
单reactor单线(进)程

请添加图片描述

使用场景:客户端的数量有限,业务处理非常快速,比如 Redis ,6.0版本前的redis采用此方案

所有的线程都在同一个线程中循环处理。如果某个请求耗时很长,那么其他的请求只能阻塞等待。

单reactor多线程

请添加图片描述

为了解决单reactor单线程模型的缺陷,多线程模型应运而生。多线程模型引入worker线程池来处理请求的业务逻辑。

绝大部分请求的耗时都是在process这个阶段,因此引入worker线程池对性能的改善十分有效。

当然,这种模型也有明显缺点,连接建立、IO 事件读取以及事件分发完全有单线程处理;比如当某个连接通过系统调用正在读取数据,此时相对于其他事件来说,完全是阻塞状态,新连接无法处理、其他连接的 IO、查询 IO 读写以及事件分发都无法完成。

多reactor多线(进)程

请添加图片描述

在多线程模型中,我们提到,其主要缺陷在于同一时间无法处理大量新连接、IO就绪事件;因此,将主从模式应用到这一块,就可以解决这个问题。

主从 Reactor 模式中,分为了主 Reactor 和 从 Reactor,分别处理 新建立的连接、IO读写事件/事件分发。

  • 一来,主 Reactor 可以解决同一时间大量新连接,将其注册到从 Reactor 上进行IO事件监听处理
  • 二来,IO事件监听相对新连接处理更加耗时,此处我们可以考虑使用线程池来处理。这样能充分利用多核 CPU 的特性,能使更多就绪的IO事件及时处理。

简言之,主从多线程模型由多个 Reactor 线程组成,每个 Reactor 线程都有独立的 Selector 对象。MainReactor 仅负责处理客户端连接的 Accept 事件,连接建立成功后将新创建的连接对象注册至 SubReactor。再由 SubReactor 分配线程池中的 I/O 线程与其连接绑定,它将负责连接生命周期内所有的 I/O 事件。

类比

3 种模式可以用个比喻来理解:(餐厅常常雇佣前台接待员负责迎接顾客,当顾客入坐后,服务员专门为这张桌子服务,点好菜后,厨师负责做饭)

  • 1)单 Reactor 单线程,前台接待员、服务员、厨子是同一个人,全程为顾客服务;
  • 2)单 Reactor 多线程,前台接待员和服务员是一个人,厨师有多个;
  • 3)主从 Reactor 多线程,一个前台接待员负责,多个服务员,多个厨师
reactor线程模型

请添加图片描述

  • Acceptor:请求接收者,作用是在特定端口建立监听。
  • Main Reactor Thread Pool:主 Reactor 模型,主要负责处理 OP_ACCEPT 事件(创建连接),通常一个监听端口使用一个线程。在具体实践时,如果创建连接需要进行授权校验(Auth)等处理逻辑,也可以直接让 Main Reactor 中的线程负责。
  • Subreactor Thread Group( IO 线程组):在 Reactor 模型中也叫做从 Reactor,主要负责网络的读与写。当 Main Reactor Thread 线程收到一个新的客户端连接时,它会使用负载均衡算法从 NIO Thread Group 中选择一个线程,将 OP_READ、OP_WRITE 事件注册在 NIO Thread 的事件选择器中。接下来这个连接所有的网络读与写都会在被选择的这条线程中执行。
  • IO Thread:IO 线程。负责处理网络读写与解码。IO 线程会从网络中读取到二进制流,并从二进制流中解码出一个个完整的请求。业务线程池:通常 IO 线程解码出的请求将转发到业务线程池中运行,业务线程计算出对应结果后,再通过 IO 线程发送到客户端。
  • Worker thread pool: 业务线程池,处理具体的业务。业务开发同学的代码通常在这个线程池工作

reactor bio对比

NIO 模型更适合需要大量在线活跃连接的场景,常见于服务端;BIO 模型则适合只需要支持少量连接的场景,数据库连接池使用的事这个模型。

reactor的代码实现:https://github.com/bruce256/NIO

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/496998.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Fabric V2.5 通用溯源系统——区块链部分设计

本节对Fabric V2.5 通用溯源系统的区块链部分做一个简单的介绍,包括目录结构、文件作用、设计思路。此节内容免费发布在TrueTechLabs Fabric学习交流QQ群。 购买专栏前请认真阅读:《Fabric项目学习笔记》专栏介绍 TrueTechLabs Fabric学习交流QQ群: 一、区块链部分文件目录简…

【北京迅为】《iTOP-3588开发板网络环境配置手册》第2章 电脑、开发板直连交换机或路由器

RK3588是一款低功耗、高性能的处理器,适用于基于arm的PC和Edge计算设备、个人移动互联网设备等数字多媒体应用,RK3588支持8K视频编解码,内置GPU可以完全兼容OpenGLES 1.1、2.0和3.2。RK3588引入了新一代完全基于硬件的最大4800万像素ISP&…

面试必问但日常不愿意看的题

1,做道 this 相关的题,看你对 js 的 this 掌握的如何2,BFC 这样答才完美 1,什么是 BFC?其规则是什么?2,如何触发 BFC3,BFC 到底可以解决什么问题呢3,作用域4,…

pycallgraph,一个好用的 Python 代码可视化库!

目录 前言 什么是pycallgraph库? 安装pycallgraph库 使用pycallgraph库 pycallgraph库的功能特性 1. 支持多种输出格式 2. 支持定制化配置 3. 支持并发程序分析 示例代码 pycallgraph库的应用场景 1. 性能优化 2. 调试和错误排查 3. 代码维护和重构 4. 项目文…

代码遗产:探索祖传代码的历史、挑战与现代融合艺术

✨✨ 欢迎大家来访Srlua的博文(づ ̄3 ̄)づ╭❤~✨✨ 🌟🌟 欢迎各位亲爱的读者,感谢你们抽出宝贵的时间来阅读我的文章。 我是Srlua,在这里我会分享我的知识和经验。&#x…

【算法历练】动态规划副本—路径问题

🎬慕斯主页:修仙—别有洞天 ♈️今日夜电波:宙でおやすみ 1:02━━━━━━️💟──────── 2:45 🔄 ◀️ ⏸ ▶️ ☰ &#…

基于Python网络爬虫的IT招聘就业岗位数据分析可视化推荐系统

文章目录 基于Python网络爬虫的IT招聘就业岗位数据分析可视化推荐系统项目概述招聘岗位数据爬虫分析系统展示用户注册登录系统首页IT招聘数据开发岗-javaIT招聘数据开发岗-PythonIT招聘数据开发岗-AndroidIT招聘数据开发岗-其它招聘岗位数据分析算法方面运维方面测试方面招聘岗…

【Python_Zebra斑马打印机编程学习笔记(三)】解决ZPL指令无法显示中文的问题

解决ZPL指令无法显示中文的问题 解决ZPL指令无法显示中文的问题前言一、问题描述二、字符集、码表文件、字库文件1、字符集2、码表文件3、字库文件 三、两种设置中文字体的方式1、通过设置字符集、码表文件、字库文件改变默认字体2、通过^CF指令设置标准字体名称改变默认字体 解…

进程间通信学习笔记(共享内存)

内存映射概念: 共享内存可以通过mmap()映射普通文件使一个磁盘文件与内存中的一个缓冲区相映射,进程可以像访问普通文件一样对文件进行访问,不必再强调read,write。 mmap的优点: 实现了用户空间和内核空间的高效交互方式 mmap的…

开源大模型LLM大爆发,数据竞赛已开启!如何使用FuseLLM实现大语言模型的知识融合?

开源大模型LLM大爆发,数据竞赛已开启!如何使用FuseLLM实现大语言模型的知识融合? 现在大多数人都知道LLM是什么,以及可以做什么。 人们讨论着它的优缺点,畅想着它的未来, 向往着真正的AGI,又有…

unity初学问题:如何修改图片的坐标

如图,我们想要修改图片的轴心点坐标(Pivot) 选择图片组 打开编辑器在里面修改即可(最下面的Custom Pivot)

C++之atexit-pthread用法详解

目录 1.atexit()函数使用说明 pthread_once() 函数详解 1.atexit()函数使用说明 NAME atexit - 用来注册执行 exit()函数前执行的终止处理程序.SYNOPSIS #include <stdlib.h> int atexit(void (*function)(void))&#xff1b;DESCRIPTION atexit()用来注册终止处理程序&…