【计网】实现reactor反应堆模型 --- 多线程方案优化 ,OTOL方案

在这里插入图片描述

没有一颗星,
会因为追求梦想而受伤,
当你真心渴望某样东西时,
整个宇宙都会来帮忙。
--- 保罗・戈埃罗 《牧羊少年奇幻之旅》---

实现Reactor反应堆模型

  • 1 重新认识Reactor
  • 2 普通线程池
  • 3 OTOL方案
    • 3.1 多进程版
    • 3.2 多线程版

1 重新认识Reactor

Reactor是反应堆模型,那么什么叫反应堆呢?反应堆可以理解为对应事件的管理容器

反应堆中会有一个一个的连接Connection对象(对文件描述符fd的封装),通过Reactor内部的EPOLL模型,获取到事件可以激活对应文件描述符,执行事件!执行事件会分为listenfd与普通套接字,都有对应的上层模块!

简单来说,反应堆模型可以理解为打地鼠:
在这里插入图片描述
这里的洞代表一个一个的Connection,整个土地是Reactor模型,地鼠就是事件。地鼠处理,就要使用锤子处理事件!

反应堆模型是基于事驱动的网络服务器设计的主流模式。
Reactor模型的核心思想是将多个并发请求通过同步事件多路分解和分发到相应的请求处理线程(或进程),从而实现高吞吐量和低延迟的服务。

这样的反应堆模型有mudou库,libevent库供我们使用。

2 普通线程池

线程:在进程内部运行,是CPU调度的基本单位。同一个进程中的线程共享地址空间,但也有独立的栈空间。

之前我们实现过线程池:其中有一个任务队列,用于分配任务给线程执行。那么如何将Reactor与线程池耦合起来?我们可以把报文解析方法交给线程池来执行,线程池内部会调用线程执行这个任务!也就是把业务处理的控制权交给线程池

线程池处理时,如果没发完依然会调用EPOLL将写事件写入到内核中进行托管!下一次再次调用进行发送!

void Recver(Connection *conn){// LOG(DEBUG , "client发送信息: %d\n" , conn->Sockfd());// 进行正常读写 --- 非阻塞读取while (true){char buffer[buffersize];ssize_t n = ::recv(conn->Sockfd(), buffer, sizeof(buffer) - 1, 0);if (n > 0){// buffer是一个数据块 添加到conn的输入缓冲区中buffer[n] = 0;conn->AppendInbuffer(buffer);// 数据交给上层处理}else if (n == 0){// 连接断开LOG(INFO, "客户端[%s]退出, 服务器准备关闭fd: %d\n", conn->GerInetAddr().AddrStr().c_str(), conn->Sockfd());conn->_handler_excepter(conn); // 统一执行异常处理return;}else{// 本轮数据读完了if (errno == EWOULDBLOCK){// 这是唯一出口break;}// 信号中断else if (errno == EINTR){continue;}// 出现异常else{conn->_handler_excepter(conn);return;}}}// 读取完毕,我们应该处理数据了!// 加入协议// std::cout << "Inbuffer 内容:" << conn->Inbuffer() << std::endl;//_process(conn);// 加入线程池 --- 进行绑定PackageParse parse;task_t func = std::bind(&PackageParse::Execute, &parse, conn);_tp->Equeue(func);}//....// 线程池ThreadPool<task_t> *_tp = ThreadPool<task_t>::GetInstance();

我们可以来看一下效果:
在这里插入图片描述

这样业务处理的逻辑就交给了线程池来进行处理!

但是这里面有两点是很不舒服的:

  1. 如果线程池现在正在处理fd的读事件,而此时此刻该fd的读事件在托管中就绪了,那么又会启动一个线程处理同一个fd的读事件!这样就冲突了!线程不安全!
  2. 我们的处理中Reactor是可以进行数据发送的,线程池也可以进行数据发送,这样会有可能进行冲突!线程不安全!

所以首先对于Connection内部的输入输出缓冲区是要加锁来进行保护的!然后重要的是,让线程池不能发送数据,线程池想要发送数据必须也通过EPOLL来进行发送!

线程池的回调函数设置为这样:

void Execute(Connection *conn){LOG(INFO, "service start!!!\n");while (true){// 1.报文解析std::string str = Decode(conn->Inbuffer()); // 通过去报头获取报文// std::cout << "str: " << str << std::endl;// 连接当前没有完整的报文! --- 直接退出if (str.empty())break;// 到这里说明有完整的报文!!!auto req = Factory::BuildRequestDefault();// 2.反序列化 得到 Requestreq->Deserialize(str);// auto res = Factory::BuildResponseDefault();// 3.业务处理auto res = cal.Calculator(req);// 4.进行序列化处理std::string ret;res->Serialize(&ret);std::cout << "ret: " << ret << std::endl;// 5.加入报头std::string package = Encode(ret);// std::cout << "package: \n"<< package << std::endl;// 6.发回数据// 直接进行发送 , 怎么知道写入条件不满足呢? 通过错误码errno是EAGAIN即可。conn->AppendOutbuffer(package);}// 到了这里 说明至少处理了一个请求 只是有一个应答// 进行发回数据// 方法1:直接发回数据// if (!conn->Outbuffer().empty())//     conn->_handler_sender(conn);// 方法2:将写事件设置进入EPOLL就可以了 会自动进行读取// 我不负责发送!if (!conn->Outbuffer().empty())conn->GetReactor()->EnableConnectionReadWrite(conn->Sockfd(), true, true);}

这样激活一下对fd的写事件关心,从此以后所有的IO全部都由Reactor自动处理,线程池只需要复杂线程安全的处理请求和应答即可!

这种模式叫做半同步半异步模式:

  • Reactor处理检测事件就绪和IO处理,这里是同步进行的!
  • 线程池处理业务处理,是并发异步处理的!

共用7个部分处理:

  1. 接受请求
  2. 解析报文
  3. 反序列化
  4. 业务处理
  5. 序列化
  6. 构建应答
  7. 发送应答

上面的方案中,Reactor负责的是接受请求工作,其余交给线程池处理,这样就会有同一个fd被多个线程使用的情况!

3 OTOL方案

那么还可以怎么做呢?
我们让Reactor一个人直接做所有的事情!这样肯定不会处理一个fd被多线程处理的情况,但是要怎么做才能保证多线程的效率还可以保证安全呢?

  • 我们使用线程池,每一个线程中都有一个Reactor执行所有的任务!这样就会有多个Reactor多执行流处理任务!
  • 然后通过一个中心Reactor调控这些普通Reactor!不负责IO和业务处理,只负责连接事件和新的Sockfd的派发工作!

在这里插入图片描述
这种方案叫做One Thread One Loop!可以通过多进程和多线程实现

3.1 多进程版

在这里插入图片描述

  • 主进程将listensockfd添加到自己的Reactor中!只有他可以获取新连接,一旦有新连接到来,父进程不负责读取!而是通过管道告诉子进程底层就绪了,可以获取连接。
  • 管道读端rfd也是文件描述符,子进程中的Reactor对rfd进行等待.
  • 子进程可以通过对该rfd的关心知道此时可以获取连接,然后进行读取获取新连接了!在子进程中负责这个Sockfd的生命周期!

3.2 多线程版

  • 主线程将listensockfd添加到自己的Reactor中!只有他可以获取新连接,一旦有新连接到来,主线程将文件描述符暂时存储到一个容器中。
  • 当主线程获取到新连接,此时会有一个容器储存着文件描述符,此时通过命名管道唤醒新线程将文件描述符读取走!
  • 这里保证线程安全不能使用条件变量!因为新线程不仅要等待连接,还需要处理业务!所以只能子啊epoll等待,其他场景不能等待!

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

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

相关文章

langgraph_plan_and_execute

整体入门demo 教程概览 欢迎来到LangGraph教程&#xff01; 这些笔记本通过构建各种语言代理和应用程序&#xff0c;介绍了如何使用LangGraph。 快速入门&#xff08;Quick Start&#xff09; 快速入门部分通过一个全面的入门教程&#xff0c;帮助您从零开始构建一个代理&a…

UnixBench和Geekbench进行服务器跑分

1 概述 服务器的基准测试&#xff0c;常见的测试工具有UnixBench、Geekbench、sysbench等。本文主要介绍UnixBench和Geekbench。 1.1 UnixBench UnixBench是一款开源的测试UNIX系统基本性能的工具&#xff08;https://github.com/kdlucas/byte-unixbench&#xff09;&#x…

布谷直播源码部署服务器关于数据库配置的详细说明

布谷直播源码搭建部署配置接口数据库 /public/db.php&#xff08;2019年8月后的系统在该路径下配置数据库&#xff0c;老版本继续走下面的操作&#xff09; 在项目代码中执行命令安装依赖库&#xff08;⚠️注意&#xff1a;如果已经有了vendor内的依赖文件的就不用执行了&am…

Gen-RecSys——一个通过生成和大规模语言模型发展起来的推荐系统

概述 生成模型的进步对推荐系统的发展产生了重大影响。传统的推荐系统是 “狭隘的专家”&#xff0c;只能捕捉特定领域内的用户偏好和项目特征&#xff0c;而现在生成模型增强了这些系统的功能&#xff0c;据报道&#xff0c;其性能优于传统方法。这些模型为推荐的概念和实施带…

太速科技-440-基于XCVU440的多核处理器多输入芯片验证板卡

基于XCVU440的多核处理器多输入芯片验证板卡 一、板卡概述 本板卡系我司自主研发的基于6U CPCI处理板&#xff0c;适用于多核处理器多输入芯片验证的应用。芯片采用工业级设计。 基于XCVU440T的多核处理器多输入芯片验证板卡基于6U CPCI架构&#xff0c;是单机中的一个…

SpringBoot框架在共享汽车管理中的应用

3系统分析 3.1可行性分析 通过对本共享汽车管理系统实行的目的初步调查和分析&#xff0c;提出可行性方案并对其一一进行论证。我们在这里主要从技术可行性、经济可行性、操作可行性等方面进行分析。 3.1.1技术可行性 本共享汽车管理系统采用SSM框架&#xff0c;JAVA作为开发语…

【数据分享】1901-2023年我国省市县镇四级的逐年降水数据(免费获取/Shp/Excel格式)

之前我们分享过1901-2023年1km分辨率逐月降水栅格数据和Shp和Excel格式的省市县四级逐月降水数据&#xff0c;原始的逐月降水栅格数据来源于彭守璋学者在国家青藏高原科学数据中心平台上分享的数据&#xff01;基于逐月数据我们采用求年累计值的方法得到逐年降水栅格数据&#…

Javaweb-book书籍借阅系统-开源计划-起源-003

效果视频&#xff1a; https://www.bilibili.com/video/BV1w5m6YkEW3/?spm_id_from333.999.0.0项目地址&#xff1a; https://gitee.com/lucky-six/Javaweb-book

基于springboot+vu的二手车交易系统(全套)

一、系统架构 前端&#xff1a;vue | element-ui | html 后端&#xff1a;springboot | mybatis-plus 环境&#xff1a;jdk1.8 | mysql | maven | nodejs 二、代码及数据库 三、功能介绍 01. web端-首页1 02. web端-首页2 03. web端-注册 04. web端-登录 05. w…

系统架构师2023版:习题

架构设计基础 计算机基础 目前处理器市场中存在 CPU 和 DSP 两种类型的处理器&#xff0c;分别用于不同的场景&#xff0c;这两种处理器具有不同的体系结构&#xff0c;DSP采用()。 A.冯诺依曼结构 B.哈佛结构 C.FPGA 结构 D.与 GPU 相同的结构 解析:…

C++ | Leetcode C++题解之第552题学生出勤记录II

题目&#xff1a; 题解&#xff1a; class Solution { public:static constexpr int MOD 1000000007;vector<vector<long>> pow(vector<vector<long>> mat, int n) {vector<vector<long>> ret {{1, 0, 0, 0, 0, 0}};while (n > 0) {…

智能化SCRM方案助力企业高效管理与营销转型

内容概要 现代企业面临着复杂多变的市场环境&#xff0c;传统的管理与营销方式常常无法满足日益增长的需求。这时&#xff0c;智能化SCRM方案便应运而生&#xff0c;为企业带来了新的机遇与挑战。智能化SCRM方案不仅仅是一个单一的工具&#xff0c;它更像是一个全面的解决方案…

Axure是什么软件?全方位解读助力设计入门

在产品设计和开发领域&#xff0c;Axure是一款大名鼎鼎且功能强大的软件&#xff0c;它为专业人士和团队提供了卓越的设计支持&#xff0c;帮助他们将创意转化为实际可操作的产品原型。 一、Axure 的基本介绍 Axure是一款专业的原型设计工具&#xff0c;主要用于创建交互式的…

linux之文件(上)

linux之文件&#xff08;上&#xff09; 一.文件的预备知识二.C语言的文件接口和linux的系统接口2.1fopen2.2fclose2.3open2.4close2.5write2.6read 三.文件与系统3.1文件描述符3.2 标准输入&#xff0c;标准输出和标准错误3.3fd的分配规则 四.重定向4.1重定向的概念4.2重定向的…

CSS的配色

目录 1 十六进制2 CSS中的十六进制2.1 十六进制颜色的基本结构2.2 十六进制颜色的范围2.3 简写形式2.4 透明度 3 CSS的命名颜色4 配色4.1 色轮4.2 互补色4.3 类似色4.4 配色工具 日常在开发小程序中&#xff0c;客户总是希望你的配色是美的&#xff0c;但是美如何定义&#xff…

YOLOv11融合[ECCV2024]自调制特征聚合SMFA模块及相关改进思路|YOLO改进最简教程

YOLOv11v10v8使用教程&#xff1a; YOLOv11入门到入土使用教程 YOLOv11改进汇总贴&#xff1a;YOLOv11及自研模型更新汇总 《SMFANet: A Lightweight Self-Modulation Feature Aggregation Network for Efficient Image Super-Resolution》 一、 模块介绍 论文链接&#xff1…

数据库SQLite的使用

SQLite是一个C语言库&#xff0c;实现了一个小型、快速、独立、高可靠性、功能齐全的SQL数据库引擎。SQLite文件格式稳定、跨平台且向后兼容。SQLite源代码属于公共领域(public-domain)&#xff0c;任何人都可以免费将其用于任何目的。源码地址&#xff1a;https://github.com/…

【大模型】Spring AI Alibaba 对接百炼平台大模型使用详解

目录 一、前言 二、Spring AI概述 2.1 spring ai是什么 2.2 Spring AI 核心能力 2.3 Spring AI 应用场景 三、Spring AI Alibaba 介绍 3.1 Spring AI Alibaba 是什么 3.2 Spring AI Alibaba 核心特点 3.3 Spring AI Alibaba 应用场景 四、SpringBoot 对接Spring AI Al…

Java:HTTP/HTTPS

HTTP HTTP(全称"超文本传输协议")是一种应用最广泛的应用层协议; 文本=>字符串 超文本:可以传输文本,图片,语言等等其他的各种数据... HTTP各种应用场景: 1.使用浏览器打开网页; 2.打开手机APP; 3.后端程序,都是"分布式/微服务"体系结构; HTTPS可…

【Promise】JS 异步之宏队列与微队列

文章目录 1 原理图2 说明3 相关面试题3.1 面试题13.2 面试题23.3 面试题33.4 面试题4 1 原理图 2 说明 JS 中用来存储待执行回调函数的队列包含 2 个不同特定的队列&#xff1a;宏队列和微队列。宏队列&#xff1a;用来保存待执行的宏任务(回调)&#xff0c;比如&#xff1a;定…