Reactor 模式全解:实现非阻塞 I/O 多路复用

4302f48a32e14b49bdb6314e2b0af1ba.png

8d21465ea8f14802aced98dcb5f9bb43.png

6ed7c2d2f8f64ce898ef83d0c1aed539.png

Reactor网络模式是什么?

Reactor网络模式时目前网络最常用的网络模式。如果你使用Netty,那么你在使用Reactor;如果你使用Twisted,那么你子啊使用Reactor;如果你使用netpoll,那么你在使用Reactor。

这里先给出答案:
Reactor = I/O多路复用+非阻塞I/O。

什么是I/O多路复用?

我们还是先使用文字拆解来看看每个词是什么意思吧。

拆词解释

I/O

I/O表示输入和输出,英文为Input/Output。I为输入,O为输出。我们日常编程中操作最多的无非就是网络和文件了,这两类就属于I/O,我们通常称为网络I/O和文件I/O。

下面是两个Java和Go操作文件I/O的例子:
java按行读取文件:

  try (BufferedReader br = new BufferedReader(new FileReader(fileName))) {String line;while ((line = br.readLine()) != null) {System.out.println(line);}} catch (IOException e) {e.printStackTrace();}

 

Go按行读取文件:

    file, _ := os.Open(fileName)defer file.Close()reader := bufio.NewReader(file)for {line,err := reader.ReadString('\n')if err != nil {break}fmt.Print(line)

 

好的,I/O搞清楚了我们就是搞清楚多路。

多路

多路字面意思就是多条路,放在计算机网络编程中的话,一般是指多个通道或者数据源。比如:你的进程或者需要打开很多的文件或者有很多网络连接,并监控这些文件或者网络连接是否发生变化(也就是是否产生一些事件)以进行必要的处理。

复用

复用的字面意思就是重复使用。我们把多路放到计算机网络编程中的话,一般是指重复使用一个或者几个线程,这里的关键是线程一定要很少而且重复使用它来完成I/O+多路。
总的来说就是:重复使用一个或者几个献策会给你来完成多路I/O的变化(事件)处理。

给I/O多路复用下个定义

好了,有了以上的背景或许你已经对I/O多路复用有了自己的理解和定义。这儿我根据自己的理解来对I/O多路复用进行定义:
I/O多路复用就是使用一个或者几个进程或者线程来完成大量通道或者数据源的事件监控和处理。

I/O多路复用复用了什么?

复用了线程。
在没有I/O多路复用之前,可能客户端每创建一个连接服务端都需要新建一个线程来处理事件,这样10K个客户端连接就需要10K个线程,服务端应对这些连接很吃力,因为创建线程有开销,切换线程有开销,还有同步,锁,死锁等问题。

那有了I/O多路复用之后,服务端可能1个线程就可以应对10K个连接的事件。

一般使用什么技术实现I/O多路复用

I/O多路复用技术实现依赖于操作系统,但是主流操作系统都是支持,下面是三大操作系统对I/O多路复用的支持:

  1. Linux: 这个是目前的大哥,Linux使用epoll,当然还有select, poll,目前网络上基本都用epoll
  2. MacOS: Kqueue
  3. Windows: IOCP I/O完成端口

I/O多路复用就告一段落,看下非阻塞I/O。

什么是非阻塞I/O

非阻塞I/O是相对于阻塞I/O而言的,它们之间的区别就是你进行I/O操作时是否阻塞你后续的执行。非阻塞不会阻塞后续执行,而阻塞会。这就好比:
你用某App网上下单到店取一样。假设你直接到店里面用手机下单,你必须在店里等待食物准备好。在这个过程中,你不能去做其他任何事情,直到拿到东西后,你才离开。

而非阻塞I/O就像是你网上下单起手配送,在起手配送期间你可以和你的朋友或者同时唠唠嗑,等骑手把东西送到给你打电话的时候你就下去拿。

总的来说:阻塞I/O中的程序在等待I/O完成时会一直停留在相应操作上,不会执行后续的代码。与之相反,在非阻塞I/O模式下,程序会立即返回一个状态值,如果I/O尚未完成,则程序可以继续执行其他任务,然后随后再次检查I/O状态。
阻塞I/O: 死等
非阻塞I/O:立即返回,下次重试

I/O多路复用和非阻塞I/O组合到一起擦出什么样的火花?

Reactor设计模式结合了非阻塞I/O和I/O多路复用,使得单个线程就能高效地处理多个网络通信。这种结合擦出的“火花”就是使事件驱动的网络服务器变得可能,这种服务器可以以非常轻量级的方式支持大规模并发连接。

在Reactor模式中,一个中央分派器(Reactor)负责监听所有I/O事件(使用select、poll、epoll等系统调用),并且当某个事件发生时,它将调用预先注册的回调函数来处理这些事件。由于采用了非阻塞I/O,这个中央分派器在等待I/O事件时不会被阻塞,这使得它可以在任何给定时间处理上千甚至上万个不同的I/O请求。
下面是单线程的Reactor模型:

单线程Reactor模式

e47b5644f5874f69998ce30f1f2099e7.png

快速实现一个Reactor

  1. 代码关键点1:Reactor线程创建一个事件循环(可能这会勾起你想起Netty的Boss)
 // 创建线程,执行事件循环pthread_t accept_threads[2];for (int i = 0; i < 1; i++) {printf("create acceptor thread. index: %d\n", i);// run_event_loop为事件的处理函数,循环处理pthread_create(&accept_threads[i], NULL, run_event_loop, &server_fd);pthread_detach(accept_threads[i]);}

 

  1. 代码关键点2: 事件处理线程(可能这会勾起你想起Netty的Worker)
// 事件发生后的处理函数: handle_io_event
pthread_create(&worker_thread, NULL, handle_io_event, &client_fd);

 

  1. 代码关键点3: 非阻塞I/O
// 设置文件描述符为非阻塞I/O
fcntl(client_fd, F_SETFL, fcntl(client_fd, F_GETFL, 0) | O_NONBLOCK);// 设置非阻塞
fcntl(server_fd, F_SETFL, fcntl(server_fd, F_GETFL, 0) | O_NONBLOCK);

 

  1. 完整代码
#include <stdio.h>
#include <pthread.h>
#include <sys/epoll.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>#define PORT 12345
#define MAX_EVENTS 10
#define BUFF_SIZE 1024
#define WORKER_SIZE 4// epoll file descriptor
int epoll_fd;// handlers
void* run_event_loop(void* arg);
void* handle_io_event(void* arg);
void wait_to_death();int main() {int server_fd;struct sockaddr_in server_addr;// 创建serverserver_fd = socket(AF_INET, SOCK_STREAM, 0);// 设置非阻塞fcntl(server_fd, F_SETFL, fcntl(server_fd, F_GETFL, 0) | O_NONBLOCK);// 绑定memset(&server_addr, 0, sizeof(server_addr));server_addr.sin_family = AF_INET;server_addr.sin_addr.s_addr = htonl(INADDR_ANY);server_addr.sin_port = htons(PORT);printf("binding\n");bind(server_fd, (struct sockaddr *)&server_addr, sizeof(server_addr));printf("listen\n");// 监听listen(server_fd, MAX_EVENTS);printf("epoll create\n");// epoll创建epoll_fd = epoll_create1(0);struct epoll_event event;event.events = EPOLLIN;event.data.fd = server_fd;printf("epoll add\n");epoll_ctl(epoll_fd, EPOLL_CTL_ADD, server_fd, &event);// 创建线程,执行事件循环pthread_t accept_threads[2];for (int i = 0; i < 1; i++) {printf("create acceptor thread. index: %d\n", i);pthread_create(&accept_threads[i], NULL, run_event_loop, &server_fd);pthread_detach(accept_threads[i]);}wait_to_death();close(epoll_fd);close(server_fd);return 0;
}void* run_event_loop(void* arg) {int server_fd = *(int*)arg;while (1) {struct epoll_event events[MAX_EVENTS];int n = epoll_wait(epoll_fd, events, MAX_EVENTS, -1);for (int i = 0; i < n; i++) {if (events[i].data.fd == server_fd) {// 新连接int client_fd = accept(server_fd, NULL, NULL);// 设置非阻塞fcntl(client_fd, F_SETFL, fcntl(client_fd, F_GETFL, 0) | O_NONBLOCK);// worker线程负责处理这个事件pthread_t worker_thread;pthread_create(&worker_thread, NULL, handle_io_event, &client_fd);}}}
}void* handle_io_event(void* arg) {int client_fd = *(int*)arg;while (1) {char buff[BUFF_SIZE] = {0};int len = read(client_fd, buff, BUFF_SIZE);if (len <= 0) {close(client_fd);struct epoll_event event;event.events = EPOLLIN;event.data.fd = client_fd;epoll_ctl(epoll_fd, EPOLL_CTL_DEL, client_fd, &event);break;}else {printf("Received %s from client\n", buff);}}return NULL;
}void wait_to_death() {sigset_t allset;sigemptyset(&allset);sigaddset(&allset, SIGINT); // Ctrl+Csigaddset(&allset, SIGQUIT); // Ctrl+\int sig;for (;;) {int err = sigwait(&allset, &sig);if (err == 0) {printf("received signal %d, prepare to exit\n", sig);break;}}
}

 

搞定收工,如有错误请指正,谢谢

 

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

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

相关文章

204基于matlab的图像融合

基于matlab的图像融合&#xff0c;包括三种方式&#xff0c;加权、PCA、IHS变换。比较三者融合后的图像差异。程序已调通&#xff0c;可直接运行。 204 matlab 图像融合 信息融合 - 小红书 (xiaohongshu.com)

【二叉树】Leetcode 94. 二叉树的中序遍历【简单】

二叉树的中序遍历 给定一个二叉树的根节点 root &#xff0c;返回 它的 中序 遍历 。 示例 1&#xff1a; 输入&#xff1a;root [1,null,2,3] 输出&#xff1a;[1,3,2] 解题思路 中序遍历是一种二叉树遍历方式&#xff0c;按照“左根右”的顺序遍历二叉树节点。 1、递归…

python 处理png图片无损压缩

代码利用了Pillow库来处理图片的压缩&#xff0c;并使用了 glob 模块来搜索所有的 .png 文件。这个脚本应该能够按照当前的编写来完成预期的工作。 请注意&#xff0c;compress_level9 指定了Pillow保存PNG图片时采用的最大压缩等级。这确保了每张图片都被以可能的最小文件大小…

【Spring】Spring框架中的一个核心接口ApplicationContext 简介,以及入口 Run() 的源码分析

一、简介 ApplicationContext 是Spring框架中的一个核心接口&#xff0c;它是Spring IoC容器的实现之一&#xff0c;用于管理和组织应用程序中的各种Bean&#xff0c;同时提供了一系列功能来支持依赖注入、AOP等特性。 简单来说&#xff0c;ApplicationContext 是一个大型的、…

求两个单链表的差集

归纳编程学习的感悟&#xff0c; 记录奋斗路上的点滴&#xff0c; 希望能帮到一样刻苦的你&#xff01; 如有不足欢迎指正&#xff01; 共同学习交流&#xff01; &#x1f30e;欢迎各位→点赞 &#x1f44d; 收藏⭐ 留言​&#x1f4dd; 但行前路&#xff0c;不负韶华&#…

发车,易安联签约某新能源汽车领军品牌,为科技创新保驾护航

近日&#xff0c;易安联成功签约某新能源汽车领军品牌&#xff0c;为其 数十万终端用户 建立一个全新的 安全、便捷、高效一体化的零信任终端安全办公平台。 随着新能源汽车行业的高速发展&#xff0c;战略布局的不断扩大&#xff0c;技术创新不断引领其市场价值走向高点&am…

移动端Web笔记day03

移动 Web 第三题 01-移动 Web 基础 谷歌模拟器 模拟移动设备&#xff0c;方便查看页面效果&#xff0c;移动端的效果是当手机屏幕发生了变化&#xff0c;页面和页面中的元素也要跟着等比例变化。 屏幕分辨率 分类&#xff1a; 硬件分辨路 -> 物理分辨率&#xff1a;硬件…

GTC 2024 火线评论:DPU 重构文件存储访问

编者按&#xff1a;英伟达2024 GTC 大会上周在美国加州召开&#xff0c;星辰天合 CTO 王豪迈在大会现场参与了 GPU 与存储相关的最新技术讨论&#xff0c;继上一篇《GTC 2024 火线评论&#xff1a;GPU 的高效存储利用》之后&#xff0c;这是他发回的第二篇评论文章。 上一篇文章…

pear-admin 项目结构讲解

上一篇文章介绍了pear-admin用到flask的技术&#xff0c; 深入代码后发现其结构也是令人眼前一亮&#xff0c; 结构化&#xff0c;模块化&#xff0c; 解耦做得非常优秀。 整个项目数据库使用migrate做了版本管理&#xff0c; 使用marshmallow做了序列化&#xff0c;这样数据库…

vue实现文字一个字一个字的显示(开箱即用)

图示&#xff1a; 核心代码 Vue.prototype.$showHtml function (str, haveCallback null) {let timeFlag let abcStr for (let i 0; i < str.length; i) {(function (i) {timeFlag setTimeout(function () {abcStr str[i]haveCallback(abcStr)if ((i 1) str.length…

EPSON推出的实时时钟模块RX8130CE功耗低至300nA、从容应对各种使用场景

随着科技的进步和消费者需求的不断变化&#xff0c;笔记本电脑市场继续展现出强劲的发展势头一方面移动性和轻薄性成为主流&#xff0c;另外一方面性能在不断提升&#xff0c;功能也日益丰富。实时时钟模组&#xff0c;作为提供时间和定时功能的单元模块&#xff0c;是笔记本电…

解决错误LibreSSL SSL_connect: SSL_ERROR_SYSCALL in connection to

react native pod第三方包或者git clone的时候遇到 OpenSSL SSL_connect: SSL_ERROR_SYSCALL in connection to github.com:443两种解决方案 方法一 修改计算机网络配置 由于使用 IPv6 的原因&#xff0c;可能会导致这一问题的出现 系统在解析hostname时使用了ipv6 可以配…

【工具】秘塔AI搜索|强烈推荐,中文免费搜索神器!堪比做报表的员工

网址&#xff1a;https://metaso.cn/ 使用时间&#xff1a;2024/03/27 以前其实用过它家的秘塔写作猫&#xff0c;当时感觉非常不错。 这次看到它出AI搜索&#xff0c;感觉开发者挺有野心和实力的。 推荐原因&#xff1a; 国产产品&#xff0c;中文适用性强。目前还免费。【不…

工业镜头常用参数之实效F(Fno.)和像圈

Fno. 工业镜头中常用到的参数F&#xff0c;有时候用F/#&#xff0c;Fno.来表示&#xff0c;指的是镜头通光能力的参数。它可用镜头焦距及入瞳直径来表示&#xff0c;也可通过镜头数值孔径&#xff08;NA&#xff09;和光学放大倍率&#xff08;β&#xff09;来计算。有效Fno.…

IDEA使用常用的设置

一、IDEA常用设置 可参考&#xff1a;IDEA这样配置太香了_哔哩哔哩_bilibili 波波老师 二、插件 可参考&#xff1a;IDEA好用插件&#xff0c;强烈推荐_哔哩哔哩_bilibili 波波老师 三、其他 学会用点“.” IDEA弹窗Servers certificate is not trusted怎么禁止&#xf…

计算机视觉之三维重建(4)---三维重建基础与极几何

文章目录 一、三维重建基础1.1 问题引入1.2 线性解法1.3 非线性解法1.4 多视图几何的关键问题 二、极几何与基础矩阵2.1 极几何2.2 极几何特例2.3 本质矩阵2.4 本质矩阵的性质2.5 基础矩阵2.6 基础矩阵的性质 三、基础矩阵估计 一、三维重建基础 1.1 问题引入 1. 从单张图像恢…

ROS机器人入门第四课:话题通信

文章目录 ROS机器人入门第四课&#xff1a;话题通信一、话题通信概述&#xff08;一&#xff09;概念&#xff08;二&#xff09;作用 二、话题通信基本操作需求:分析:流程:&#xff08;一&#xff09;发布方解释一些关键的ROS函数和概念&#xff1a; &#xff08;二&#xff0…

QT+Opencv+yolov5实现监测

功能说明&#xff1a;使用QTOpencvyolov5实现监测 仓库链接&#xff1a;https://gitee.com/wangyoujie11/qt_yolov5.git git本仓库到本地 一、环境配置 1.opencv配置 将OpenCV-MinGW-Build-OpenCV-4.5.2-x64文件夹放在自己的一个目录下&#xff0c;如我的路径&#xff1a; …

Spark SQL— Catalyst 优化器

Spark SQL— Catalyst 优化器 1. 目的 本文的目标是描述Spark SQL 优化框架以及它如何允许开发人员用很少的代码行表达复杂的查询转换。我们还将描述Spark SQL如何通过大幅提高其查询优化能力来提高查询的执行时间。在本教程中&#xff0c;我们还将介绍什么是优化、为什么使用…

蓝桥杯练习系统(算法训练)ALGO-967 共线

资源限制 内存限制&#xff1a;256.0MB C/C时间限制&#xff1a;1.0s Java时间限制&#xff1a;3.0s Python时间限制&#xff1a;5.0s 问题描述 给定2维平面上n个整点的坐标&#xff0c;一条直线最多能过几个点&#xff1f; 输入格式 第一行一个整数n表示点的个数   …