【计网】实现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…

【网络安全 | 甲方安全建设】分布式系统、Redis分布式锁及Redisson看门狗机制

未经许可,不得转载。 文章目录 分布式系统分布式系统的核心特性分布式系统的典型架构分布式锁概念Redis 分布式锁原理互斥性锁释放锁的唯一性具体实现Redisson分布式锁分布式系统 分布式系统是一种由多台计算机(节点)组成的系统,这些节点通过网络相互连接并协同工作,共同…

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;基于逐月数据我们采用求年累计值的方法得到逐年降水栅格数据&#…

【前端】Svelte:核心语法和组件基础

本教程将详细介绍 Svelte 的核心语法和组件基础&#xff0c;包括 <script>、<style> 和 HTML 模板的使用方法。我们将通过实例掌握双向数据绑定、条件渲染、循环等基本语法&#xff0c;最后编写简单的交互式组件。 Svelte 组件机制 Svelte 使用组件化结构来组织代…

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) {…

Vue.js开发基础——数据绑定/响应式数据绑定

一、单文件组件 一个基本的单文件组件&#xff1a; &#xff08;三个标签顺序可随意更改&#xff09; <template> <!-- 此处编写组件的结构 --> </template> <script> /* 此处编写组件的逻辑 */ </script> <style> /* 此处编写组件的样…

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

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

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

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

交流电压1000V以上但不超过35KV的交流调速电气传动系统额定值规定

交流电压1000V以上但不超过35KV的交流调速电气传动系统的额定值规定&#xff0c;主要遵循国家标准GB/T 12668.4-2006《调速电气传动系统 第4部分&#xff1a;一般要求 交流电压1000V以上但不超过35kV的交流调速电气传动系统额定值的规定》。以下是该标准的一些关键内容和要点&a…

Spring-Day8

AOP通知 前置通知Befor&#xff1a;目标方法之前执行 Aspect // 标记为了切面类 Component // 必须声明为 Spring 的 bean public class LogAspect {// 实现方法用时 切点表达式// 前置通知Before("execution(* com.example.c4_aop.UserService.*(..) )")public void…

Vue3 -- 新组件【谁学谁真香系列6】

Teleport Teleport是什么?–Teleport是一种能够将我们的组件html结构移动到指定位置的技术。 父组件: <template><div calss="outer"><h2>我是App组件</h2><img src="https://z1.ax1x.com/2023/11/19/piNxLo4.jpg" alt=&qu…

go语言中的接口含义和用法详解

在Go语言中&#xff0c;接口&#xff08;interface&#xff09;是一种抽象类型&#xff0c;它定义了一组方法签名而不实现具体的方法。接口在Go语言中起到了类似于其他语言中“面向接口编程”的作用&#xff0c;是实现多态和解耦的重要工具。以下是Go中接口的含义及其用法的详细…