reactor的原理与实现

网络模型

  • 前情回顾
  • 服务器模型 Reactor和 Proactor
    • Reactor模型
    • Proactor模型
    • 同步I/O模拟Poractor模型
    • Libevent,libev,libuv
      • 优先级
      • 事件循环
      • 线程安全

前情回顾

网络IO,会涉及到两个系统对象:
  一个是用户空间调用的进程或线程
  一个是内核空间的内核系统
如果发生IO操作read时,会奖励两个阶段:
  1. 等待数据准备就绪
  2. 将数据从内核拷贝到进程或线程中
因为在以上两个阶段会有不同的情况发生,所以就出现了多种网络IO模型

服务器模型 Reactor和 Proactor

  对高并发编程,网络连接上的消息处理,可以分为两个阶段:等待消息准备好、消息处理。当使用默认的阻塞套接字时(例如上面提到的 1 个线程捆绑处理 1 个连接),往往是把这两个阶段合而为一,这样操作套接字的代码所在的线程就得睡眠来等待消息准备好,这导致了高并发下线程会频繁的睡眠、唤醒,从而影响了 CPU 的使用效率

  高并发编程方法当然就是把两个阶段分开处理。即,等待消息准备好的代码段,与处理消息的代码段是分离的。当然,这也要求套接字必须是非阻塞的,否则,处理消息的代码段很容易导致条件不满足时,所在线程又进入了睡眠等待阶段。

  Q: 那么问题来了,等待消息准备好这个阶段怎么实现?它毕竟还是等待,这意味着线程还是要睡眠的!
  A: 解决办法就是,线程主动查询,或者让 1 个线程为所有连接而等待!这就是 IO 多路复用了。多路复用就是处理等待消息准备好这件事的,它可以同时处理多个连接!它也可能“等待”,所以它也会导致线程睡眠,然而这不要紧,因为它一对多、可以监控所有连接。这样,当线程被唤醒执行时,就一定是有一些连接准备好被我们的代码执行了

作为一个高性能服务器程序通常需要考虑处理三类事件: I/O 事件,定时事件、信号。两种高效的事件处理模型:Reactor 和 Proactor

Reactor模型

  首先来回想一下普通函数调用的机制:程序调用某函数,函数执行,程序等待,函数将结果和控制权返回给程序,程序继续处理。
  Reactor 释义“反应堆”,是一种事件驱动机制。和普通函数调用的不同之处在于:应用程序不是主动的调用某个 API 完成处理,而是恰恰相反,Reactor 逆置了事件处理流程,应用程序需要提供相应的接口并注册到 Reactor 上,如果相应的事件发生,Reactor 将主动调用应用程序注册的接口,这些接口又称为“回调函数”
在这里插入图片描述
  Reactor 模式是处理并发 I/O 比较常见的一种模式,用于同步 I/O,中心思想是将所有要处理的 I/O 事件注册到一个中心 I/O 多路复用器上,同时主线程/进程阻塞在多路复用器上;一旦有 I/O 事件到来或是准备就绪(文件描述符或 socket 可读、写),多路复用器返回并将事先注册的相应 I/O 事件分发到对应的处理器中。

Reactor 模型有三个重要的组件:

  • 多路复用器:由操作系统提供,在 linux 上一般是 select, poll, epoll 等系-统调用。
  • 事件分发器:将多路复用器中返回的就绪事件分到对应的处理函数中。
  • 事件处理器:负责处理特定事件的处理函数。
    在这里插入图片描述
    具体流程如下:
      1. 注册读就绪事件和相应的事件处理器;
      2. 事件分离器等待事件;
      3. 事件到来,激活事件分发器,事件分发器调用事件对应的处理器;
      4. 事件处理器完成实际的读操作,处理读到的数据,注册新的事件,然后返还控制权。

Reactor 模式是编写高性能网络服务器的必备技术之一,它具有如下的优点:

  • 响应快,不必为单个同步时间所阻塞,虽然 Reactor 本身依然是同步的;
  • 编程相对简单,可以最大程度的避免复杂的多线程及同步问题,并且避免了多线程/进
    程的切换开销;
  • 可扩展性,可以方便的通过增加 Reactor 实例个数来充分利用 CPU 资源;
  • 可复用性,reactor 框架本身与具体事件处理逻辑无关,具有很高的复用性;

  Reactor 模型开发效率上比起直接使用 IO 复用要高,它通常是单线程的,设计目标是希望单线程使用一颗 CPU 的全部资源,但也有附带优点,即每个事件处理中很多时候可以不考虑共享资源的互斥访问。可是缺点也是明显的,现在的硬件发展,已经不再遵循摩尔定
律,CPU 的频率受制于材料的限制不再有大的提升,而改为是从核数的增加上提升能力,当程序需要使用多核资源时,Reactor 模型就会悲剧,。
  为什么使用多核资源时,Reactor会悲剧呢?如果程序业务很简单,例如只是简单的访问一些提供了并发访问的服务,就可以直接开启多个反应堆,每个反应堆对应一颗 CPU 核心,这些反应堆上跑的请求互不相关,这是完全可以利用多核的。例如 Nginx 这样的 http 静态服务器。

Proactor模型

在这里插入图片描述
在这里插入图片描述
具体流程如下:

  1. 处理器发起异步操作,并关注 I/O 完成事件
  2. 事件分离器等待操作完成事件
  3. 分离器等待过程中,内核并行执行实际的 I/O 操作,并将结果数据存入用户自定义缓冲
    区,最后通知事件分离器读操作完成
  4. I/O 完成后,通过事件分发器呼唤处理器
  5. 事件处理器处理用户自定义缓冲区中的数据

  从上面的处理流程,我们可以发现 proactor 模型最大的特点就是 Proactor 最大的特点是:使用异步 I/O。所有的 I/O 操作都交由系统提供的异步 I/O 接口去执行。工作线程仅仅负责业务逻辑
  在 Proactor 中,用户函数启动一个异步的文件操作。同时将这个操作注册到多路复用器上。多路复用器并不关心文件是否可读或可写而是关心这个异步读操作是否完成异步操作是操作系统完成,用户程序不需要关心。多路复用器等待直到有完成通知到来。当操作系统完成了读文件操作——将读到的数据复制到了用户先前提供的缓冲区之后,通知多路复用器相关操作已完成。多路复用器再调用相应的处理程序,处理数据。
  Proactor 增加了编程的复杂度,但给工作线程带来了更高的效率。Proactor 可以在系统态将读写优化,利用 I/O 并行能力,提供一个高性能单线程模型。在 windows 上,由于没有 epoll 这样的机制,因此提供了 IOCP 来支持高并发, 由于操作系统做了较好的优化,windows 较常采用 Proactor 的模型利用完成端口来实现服务器
  在 linux 上,在2.6 内核出现了 aio 接口,但 aio 实际效果并不理想,它的出现,主要是解决 poll 性能不佳的问题,但实际上经过测试,epoll 的性能高于 poll+aio,并且 aio 不能处理 accept,
因此 linux 主要还是以 Reactor 模型为主。在不使用操作系统提供的异步 I/O 接口的情况下,还可以使用 Reactor 来模拟 Proactor,差别是:使用异步接口可以利用系统提供的读写并行能力,而在模拟的情况下,这需要在用户态实现。具体的做法只需要这样:

  1. 注册读事件(同时再提供一段缓冲区)
  2. 事件分离器等待可读事件
  3. 事件到来,激活分离器,分离器(立即读数据,写缓冲区)调用事件处理器
  4. 事件处理器处理数据,删除事件(需要再用异步接口注册)
    我们知道,Boost.asio 库采用的即为 Proactor 模型。不过 Boost.asio 库在 Linux 平台采用
    epoll 实现的 Reactor 来模拟 Proactor,并且另外开了一个线程来完成读写调度。

同步I/O模拟Poractor模型

在这里插入图片描述

  1. 主线程往 epoll 内核事件表中注册 socket 上的读就绪事件。
  2. 主线程调用 epoll_wait 等待 socket 上有数据可读。
  3. 当 socket 上有数据可读时,epoll_wait 通知主线程。主线程从 socket 循环读取数据,
    直到没有更多数据可读,然后将读取到的数据封装成一个请求对象并插入请求队列。
  4. 睡眠在请求队列上的某个工作线程被唤醒,它获得请求对象并处理客户请求,然后
    往 epoll 内核事件表中注册 socket 上的写就绪事件。
  5. 主线程调用 epoll_wait 等待 socket 可写。
  6. 当 socket 可写时,epoll_wait 通知主线程。主线程往 socket 上写入服务器处理客户
    请求的结果。

  两个模式的相同点,都是对某个 IO 事件的事件通知(即告诉某个模块,这个 IO 操作可以进行或已经完成)。在结构上两者也有相同点:demultiplexor 负责提交 IO 操作(异步)、查询设备是否可操作(同步),然后当条件满足时,就回调注册处理函数。
  不同点在于,异步情况下(Proactor),当回调注册的处理函数时,表示 IO 操作已经完成;同步情况下(Reactor),回调注册的处理函数时,表示 IO 设备可以进行某个操作(can read or can write),注册的处理函数这个时候开始提交操作。

Libevent,libev,libuv

  libevent :名气最大,应用最广泛,历史悠久的跨平台事件库;
  libev :较 libevent 而言,设计更简练,性能更好,但对 Windows 支持不够好;
  libuv :开发 node 的过程中需要一个跨平台的事件库,他们首选了 libev,但又要支持Windows,故重新封装了一套,linux 下用 libev 实现,Windows 下用 IOCP 实现;

优先级

  libevent: 激活的事件组织在优先级队列中,各类事件默认的优先级是相同的,可以通过设置事件的优先级使其优先被处理
  libev: 也是通过优先级队列来管理激活的时间,也可以设置事件优先级
  libuv: 没有优先级概念,按照固定的顺序访问各类事件

事件循环

  libevent: event_base 用于管理事件
  libev: 激活的事件组织在优先级队列中,各类事件默认的优先级是相同的,
  libuv: 可以通 过设置事件的优先级 使其优先被处理

线程安全

  event_base 和 loop 都不是线程安全的,一个 event_base 或 loop 实例只能在用户的一个线程内访问(一般是主线程),注册到 event_base 或者 loop 的 event 都是串行访问的,即每个执行过程中,会按照优先级顺序访问已经激活的事件,执行其回调函数。所以在仅使用一个event_base 或 loop 的情况下,回调函数的执行不存在并行关系。

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

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

相关文章

07 Vue3框架简介

文章目录 一、Vue3简介1. 简介2. 相关网站3. 前端技术对比4. JS前端框架5. Vue核心内容6. 使用方式 二、基础概念1. 创建一个应用2. 变量双向绑定(v-model)3. 条件控制(v-if)4. 数组遍历(v-for)5. 绑定事件…

leetcode 38. 外观数列(medium)(优质解法)

链接:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台 代码: class Solution {public String countAndSay(int n) {//要进行 n - 1 次描述才能得到结果// last 代表当前要描述的字符串String last"1";// ret 代表描述…

应用安全技术措施

目录 身份鉴别 访问控制 安全审计 剩余信息保护 通信完整性 通信保密性 抗抵赖 软件容错 身份鉴别 进阶 访问控制 进阶 安全审计 进阶 剩余信息保护 进阶 通信完整性 通信保密性 抗抵赖 软件容错 ~over~

TypeScript学习(进阶篇)

一、元组 数组合并了相同类型的对象,而元组(Tuple)合并了不同类型的对象。 元组起源于函数编程语言(如 F#),这些语言中会频繁使用元组。 简单的例子 定义一对值分别为 string 和 number 的元组&#xf…

MES系统是什么?MES系统的功能有哪些?

在现代制造业的快速发展中,所有规模的企业都面临着类似的挑战:如何提高生产效率、确保产品质量、减少浪费、降低成本,同时迅速响应市场变化。而在这个过程中,传统企业管理往往有以下几个典型痛点: 纸质文件堆叠如山&a…

麦肯锡产品经理问题解决流程终极指南

您是否想知道世界上最成功的产品经理如何始终如一地提供不仅满足而且超出预期的解决方案?秘密可能就在于世界上最负盛名的咨询公司之一麦肯锡公司所磨练的方法论。本文深入探讨了麦肯锡的问题解决流程,该流程专为希望提升水平的产品经理量身定制。 01. 麦…

Kafka生产环境问题总结与性能优化实践

Kafka可视化管理工具kafka-manager 安装及基本使用可参考: httos://wwwcnbloas.com/dadonaaa/o/8205302.html 线上环境规划 1. 消息丢失情况: 消

实战教学:零食百货商城小程序开发全程指导

随着移动设备的普及和互联网技术的发展,小程序成为越来越多人的选择,特别是在购物方面。开发一个零食百货商城小程序,可以让你在手机上随时随地购买各种零食,方便快捷。本文将为你提供全程指导,让你轻松开发一个成功的…

MySql的mvcc原理

目录 一、什么是mvcc? 二、什么是当前读,快照读? 当前读 快照读 三、mvcc实现原理 版本链 undo日志 Undo log 的用途 Read View(读视图) Read View几个属性 五、RR、RC级别下生成时机 一、什么是mvcc? mvcc全称Multi-Version Concurrency Control,即…

迪文屏开发保姆级教程5—表盘时钟和文本RTC显示

这篇文章要讲啥事呢? 本篇文章主要介绍了在DGBUS平台上使用表盘时钟和文本时钟RTC显示功能的方法。 文哥悄悄话: 官方开发指南PDF:(不方便下载的私聊我发给你) https://download.csdn.net/download/qq_21370051/8864…

Flowable-升级为7.0.0.M2-第一节

目录 升级jdk升级springboot到3.1.3升级数据库连接池druid-spring-boot-3-starter到1.2.20升级mybatis-plus到3.5.3.2升级flowable到7.0.0.M2 最近有些读者一直问flowable如何升级到7.0.0.M2,接下来我就一步步的把flowable升级到7.0.0.M2 升级jdk flowable7.x采用的…

算法导论复习(四)主方法的专题

主方法我们要记住的是什么呢?

浅谈矿山井下IT配电系统绝缘监测的应用

摘要:众所周知,能源作为经济发展的重要引擎,堪称是国民经济的命脉。采矿业一直是能源工业的重要支柱,我国的矿业生产主要来自井下开采,环境恶劣,就以煤矿为例,煤矿井下存在水、火、瓦斯、煤尘、…

Shell三剑客:awk(awk编辑编程)二

一、IF 语句 IF 条件语句语法格式 #方式一: if (condition)action #方式二:使用花括号语法格式 if (condition) {action1;action2; ... } {if(表达式){语句1;语句2;...}} IF 语句实例 #判断数字是奇数还是偶数 [rootlocalhost ~…

11.1Linux串口应用程序开发

UART简介 UART的全称是Universal Asynchronous Receiver and Transmitter,即异步发送和接收。 串口在嵌入式中用途非常的广泛,主要的用途有: 打印调试信息;外接各种模块:GPS、蓝牙; 串口因为结构简单、稳…

css的定位

为什么需要定位? 场景: 某个元素可以自由的在一个盒子内移动位置,并且压住其他盒子当我们滚动窗口的时候,盒子是固定屏幕某个位置的。 这二个需求,使用标准流和浮动的方式是无法实现的或者是不容易实现,所以…

【C++】string

文章目录 1. 标准库中的string类1.1 string类 2 string类的常用接口说明2.1 string类对象的常见构造2.2 string类对象的容量操作2.3. string类对象的访问及遍历操作2.4 string类对象的修改操作2.5 string类非成员函数2.6 vs和g下string结构的说明 1. 标准库中的string类 1.1 s…

ELK----filebeat日志收集工具

ELK:filebeat日志收集工具和logstash相同 filebeat是一个轻量级的日志收集工具,所使用的系统资源比logstash部署和启动时使用的资源要小的多。 filebeat可以运行在非java环境。他可以代理logstash在非java环境上收集日志 filebeat无法实现数据的过滤&…

基于JavaServelet的同学录管理系统(Java毕业设计)

点击咨询源码 大家好,我是DeBug,很高兴你能来阅读!作为一名热爱编程的程序员,我希望通过这些教学笔记与大家分享我的编程经验和知识。在这里,我将会结合实际项目经验,分享编程技巧、最佳实践以及解决问题的…

锐捷配置DHCP动态分配地址

一、实验拓扑 二、实验目的 配置DHCP为四个部门动态分配IP地址 三、实验配置 第一步:配置基本IP地址并对端口进行规划 SW1 SW1(config)#vlan range 10,20,30,40,100 SW1(config-vlan-range)#exit SW1(config)#interface vlan 10 SW1(config-if-VLAN 10)#ip addres…