主从Reactor高并发服务器

文章目录

  • Reactor模型的典型分类
    • 单Reactor单线程
    • 单Reactor多线程
    • 多Reactor多线程
    • 本项目中实现的主从Reactor One Thread One Loop
    • 各模型的优点与缺点
  • 项目分解
    • Reactor服务器模块
      • Buffer
      • Socket
      • Channel
      • Epoller
      • TimerWheel
      • EventLoop
      • Any
      • Connection
      • Acceptor
      • LoopThread
      • LoopThreadPool
      • TcpServer
    • HTTP服务器模块
      • Util
      • Request和Response
      • Response
      • Context
      • HttpServer

本篇博客是对自己实现的主从Reactor高并发服务器的总结。

Reactor模型的典型分类

单Reactor单线程

image-20231006205240973

单Reactor多线程

image-20231006205313039

多Reactor多线程

image-20231006205348577

本项目中实现的主从Reactor One Thread One Loop

image-20231006211351458

各模型的优点与缺点

单Reactor单线程

  • 优点:实现简单,不涉及到进程/线程间通信以及资源争抢;
  • 缺点:由于所有操作均在单线程中串行执行,一旦有任务处理较慢或者请求较多时,容易导致后面的任务处理或者请求得不到响应。并且由于是单线程,没有充分利用好CPU多核资源,最终非常容易达到性能瓶颈。

单Reactor多线程

  • 优点:利用了CPU多核资源;
  • 缺点:单个Reactor线程不仅处理了新建连接请求,而且还处理了数据通信请求,也就是管理了所有的fd上的一切事件,在高并发场景下也非常容易达到性能评价。

多Reactor多线程

  • 优点:充分利用了CPU多核资源,主Reactor只负责获取连接,副Reactor负责已获取的连接,各司其职,解决了前面两种模型的性能问题;
  • 缺点:实现复杂。

主从Reactor One Thread One Loop

由于也采用了主从Reactor模式,所以性能不差,但为了服务器的实现更简单,放弃了线程池的实现。

项目分解

本项目共分为两大模块:Reactor服务器模块基于Reactor服务器模块实现的HTTP服务器模块

下面的项目分解只是简单的说明了一下各模块的功能,项目源码中有详细的注释讲解,所以强烈建议搭配项目源码一起食用。

Reactor服务器模块

服务器模块共有以下子模块

image-20231007160642203

Buffer

recv并不能够保证读取到一个完整的协议数据,所以必须要将读取到的数据先暂存起来,然后上层检查数据完整性,若完整则拿走数据,不完整则一直等读取到一个完整的协议数据时再拿走数据,那么这时就需要一个缓冲区能暂时存放recv读取到的数据。并且写入数据时,也不能直接调用send,因为fd是要被epoll监控的,但用户又不知道什么时候调用,所以用户可以直接将数据写入缓冲区中,当fd上的写事件触发时,会自动将缓冲区中的数据send到fd中。

本模块就实现的是这样的一个缓冲区。

缓冲区结构如下:

image-20231006212557141

Socket

封装系统调用socket,使对于socket的各项操作更加方便。

Channel

Channel模块是对一个fd进行事件监控管理以及事件回调管理的!

功能大概有:

  • 开启/关闭fd的事件监控(读、写);

  • 关闭fd的所有事件监控;

  • 判断fd的事件监控是否被开启了;

  • 设置事件触发后的回调函数(读事件、写事件、错误事件、关闭事件、任意事件);

  • 调用已经触发的事件回调函数。

但要注意,关于fd的开启/关闭事件监控并不是真正在Channel模块执行的,而是在Epoller模块执行的。Channel模块只是将fd的相关监控操作和相关事件回调整合在了一起。

Epoller

Epoller模块是对epoll系列操作进行的封装,让对fd的事件监控操作更加简单。

通过传入一个Channel指针,获取到fd需要监控的事件,然后Epoller模块就把这些事件进行监控,而当有事件触发时,Epoller模块就把已经触发的事件通过Channel传出,再由Channel内部调用事件回调。

功能大概有:

  • 添加/更新事件监控;
  • 移除事件监控;
  • 开始事件监控。

TimerWheel

TimerWheel是一个定时任务管理模块。

大致思想就是:将任务封装到TimerTask的析构函数中,然后用shared_ptr管理起来放入TimerWheel中的vector里,每隔一秒就清空一下vector里的元素,此时调用析构函数,就会调用定时任务了。

image-20231007164736067

每隔一秒,step_就前进一步,step_走到哪里,就清空哪里,然后当最后一个shared_ptr调用析构函数时,就会调用定时任务。

step_的每秒移动是根据timerfd技术来实现的。

创建一个timerfd,让内核每隔一秒写入一次,然后用Channel管理timerfd,注册一个读事件,在读事件里++step_,这样内核每隔一秒写入一次,就触发一次读事件,就会++step_一次。

EventLoop

EventLoop模块就是副Reactor模块,封装了Epoller模块和TimerWheel模块,并且一个EventLoop就是一个线程。

大致功能有:

  • 更新/移除事件监控(调用Epoller接口);
  • 添加/刷新/取消/移除定时任务;
  • 添加任务到任务队列中;
  • 启动事件监控(调用Epoller接口),调用事件回调(调用Channel接口),执行任务队列中的任务。

关于任务队列,要详细说一下:

对于一个连接,用户所有关于连接的操作都是线程不安全的,比如在某个事件回调执行过程中,用户开辟了一个线程池,这个线程池都是共享这个连接的,那么假设有若干个线程同时对定时任务进行操作,就会出现线程安全问题。所以用户所有的对于连接的操作都是非线程安全的,但是又不能给每个连接的接口都添加锁,这样效率就太低了。于是就有了一个解决办法,在EventLoop模块里创建一个任务队列,所有的连接的接口在调用时都进行一下判断(接口内部判断),若是副Reactor线程就直接执行接口,若是其它线程,就将该任务压入队列中,由副Reactor线程统一执行。这样就避免了多线程对于连接访问的线程安全问题。

上面功能的第四点是在同一函数中执行的,那么就会出现一种情况,任务队列中有任务了,但此时没有事件触发,epoll_wait被阻塞,最终导致任务队列中的任务得不到及时执行。所以这里用了eventfd技术解决。eventfd用Channel管理起来,注册一个读事件,然后在将任务添加到任务队列后往eventfd里写入数据,此时就会触发读事件,epoll_wait不会被阻塞,任务队列中的任务也就能够被及时执行了。

Any

Any模块是模仿C++17中的any类实现的。

TCP服务器并不知道上层要运用什么协议,也就无法用一个特定类型保存上层的上下文信息,所以用一个Any类来保存上层的上下文信息。

实现思路

要实现一个类,能够存放任意类型的数据,那么该类必定不能是模版类,模版类不能自动推演类型,并且模版类在实例化之后就只能存放单一类型的数据了。但是函数模版可以自动推演类型,于是就想到将类的构造函数设置成模版函数,成员变量为void *指针,但是void *太不安全了。于是又想到,在Any类的内部创建一对父子类,子类是模版类,成员变量为父类指针,在Any的构造函数中new一个子类对象用父类指针管理,就能够实现简易的Any类。

Connection

Connection模块是子模块中最复杂的模块,是对Buffer、Socket、Channel、Any、模块的整合,还关联了EventLoop模块。

大致功能就是:

  • 设置任务回调函数(连接创建成功的回调,消息到来的回调,任意事件回调 . . . . . .);
  • 发送/读取数据;
  • 开启/关闭非活跃连接销毁;
  • 关闭连接;
  • 切换协议。

Connection模块所有的对外提供的接口在调用时都要判断是否和副Reactor线程是同一个线程,是则直接执行,不是则压入队列。但是对于关闭连接的操作,无需进行判断,应该直接压入队列,关闭连接必须要在所有的事件触发函数执行完之后,在队列中执行。

假设有一种场景:非活跃连接销毁时间是10s,1、2、3、4、5号都有事件触发,1号事件执行了20s,那么timerfd就超时了20次,假设2、3、4中有一个就是timerfd事件,然后指针走了20下,再然后后面还没来得及执行的事件的连接就被销毁了,此时再去执行触发事件就会发生错误。所以关闭连接的操作必须要在触发事件全部调用完之后,在任务队列中执行。

Acceptor

Acceptor模块也就是主Reactor模块,负责获取新连接,内部有一个EventLoop和一个Channel来管理监听套接字。

LoopThread

该模块将EventLoop和线程强绑定在了一起。为什么非要这么做呢?

因为EventLoop模块在初始化的时候获取当前线程ID,那么用户可能在一个线程内部创建好几个EventLoop,然后再将这几个EventLoop分配给其它线程,这时虽然一个EventLoop占一个线程,但此时EventLoop内部的线程ID和实际所处的线程ID是不一样的。

LoopThreadPool

将LoopThread模块封装成一个线程池,更加方便了服务器对于LoopThread数量的掌控。

TcpServer

是对所有模块的整合,但主要的成员也就是一个主Reactor(一个EventLoop和一个Acceptor)、一个LoopThreadPool。

主要功能有:

  • 设置任务回调函数(连接创建成功的回调,消息到来的回调,任意事件回调 . . . . . .);
  • 设置LoopThreadPool的线程数量;
  • 开启非活跃连接销毁;
  • 添加定时任务。

HTTP服务器模块

image-20231009202423756

Util

该模块提供了一些工具函数,比如字符串分割函数、读文件、写文件、编码、解码等。

Request和Response

该模块存放了解析后的Http请求报文数据,并且还提供了一些方法能够快速获取Request数据。

Response

该模块存放了解析后的Http响应报文数据,并且还提供了一些方法能够快速获取Response数据。

Context

该模块是接收Request的上下文模块,服务端接收到的数据有可能并不是一个一条完整的Http报文,所以需要该模块来记录下接收Http报文的过程(上下文)。

HttpServer

对上面所有模块的整合,并且设置了不同的Http请求与回调方法的映射。

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

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

相关文章

根据前序遍历结果构造二叉搜索树

根据前序遍历结果构造二叉搜索树-力扣 1008 题 题目说明: 1.preorder 长度>1 2.preorder 没有重复值 直接插入 解题思路: 数组索引[0]的位置为根节点,与根节点开始比较,比根节点小的就往左边插,比根节点大的就往右…

AI如何帮助Salesforce从业者找工作?

在当今竞争激烈的就业市场中,找到满意的工作是一项艰巨的任务。成千上万的候选人竞争一个岗位,你需要利用一切优势从求职大军中脱颖而出。 这就是AI的用武之地,特别是像ChatGPT这样的人工智能工具,可以成为你的秘密武器。本篇文章…

知识图谱系列4:neo4j学习

这是一篇还不错的教程,我将会针对其中的Cypher语法在这篇帖子内提出问题,以便学习与复习。 MATCH是什么操作? 小括号()代表什么?(n)代表什么? MATCH (n) DETACH DELETE n是什么含义&#xff1…

使用poi-tl循环导出word报表

先看模板和导出的效果 模板 效果 根据模板循环生成表格,每个表格再循环填充数据,也就是两层循环,第一层循环是学生学期信息,第二层循环是学生的成绩数据。 第一个循环 {{?listTable}} {{/}}第二个循环 {{reportList}} 表格…

【深度学习实验】循环神经网络(二):使用循环神经网络(RNN)模型进行序列数据的预测

目录 一、实验介绍 二、实验环境 1. 配置虚拟环境 2. 库版本介绍 三、实验内容 0. 导入必要的工具包 1. RNN模型 a. 初始化__init__ b. 前向传播方法forward 2. 训练和预测 a. 超参数 b. 创建模型实例 c. 模型训练 d. 预测结果可视化 3. 代码整合 经验是智慧之父…

磁盘满了对日志打印(Logback)的影响

背景 我们生产环境有一个服务半夜报警:磁盘剩余空间不足10%,请及时处理。排查后发现是新上线的一个功能,日志打太多导致的,解决方法有很多,就不赘述了。领导担心报警不及时、或者报警遗漏,担心磁盘满了对线…

【Monorepo实战】pnpm+turbo+vitepress构建公共组件库文档系统

Monorepo架构可以把多个独立的系统放到一起联调,本文记录基于pnpm > workspace功能,如何构建将vitepress和组件库进行联调,并且使用turbo进行任务顺序编排。 技术栈清单: pnpm 、vitepress 、turbo 一、需求分析 1、最终目标…

“高级Vue状态管理 - Vuex的魅力与应用“

目录 引言1. Vuex的简介1.1 什么是Vuex?1.2 Vuex的核心概念 2. Vuex的值获取与改变(综合案例)3. Vuex的异步请求总结 引言 在现代Web开发中,前端应用变得越来越复杂。随着应用规模的扩大和数据流的复杂性增加,有效地管理应用的状态成为了一项…

澳大利亚教育部宣布ChatGPT将被允许在澳学校使用!

教育部长最近宣布,从 2024 年起,包括 ChatGPT 在内的人工智能将被允许在所有澳大利亚学校使用。 (图片来源:卫报) 而早些时候,澳洲各高校就已经在寻找与Chatgpt之间的平衡了。 之前,悉尼大学就…

【例题】逆波兰表达式求值(图解+代码)

【例题】逆波兰表达式求值(图解代码) 这里写目录标题 【例题】逆波兰表达式求值(图解代码)逆波兰表达式解释优点转换计算代码 题目描述 : 逆波兰表示法是一种将运算符(operator)写在操作数(operand)后面的描述程序(算式…

java过滤非中英文的特殊字符,四字节表情字符

过滤非中英文的特殊字符 /*** 过滤特殊字符* param str str* return String*/ public static String filterStr(String str) {if (StringUtils.isBlank(str)) {return str;}String regEx "[~!#$%^&*()|{}:;,\\[\\].<>/?~&#xff01;#&#xffe5;%……&…

Android---Synchronized 和 ReentrantLock

Synchronized 基本使用 1. 修饰实例方法 public class SynchronizedMethods{private int sum 0;public synchronized void calculate(){sum sum 1;} } 这种情况下的锁对象是当前实例对象&#xff0c;因此只有同一个实例对象调用此方法才会产生互斥效果&#xff1b;不同的…

Jenkins 添加节点Node报错JNI error has occurred UnsupportedClassVersionError

节点日志 报错信息如下 Error: A JNI error has occurred, please check your installation and try again Exception in thread “main” java.lang.UnsupportedClassVersionError: hudson/remoting/Launcher has been compiled by a more recent version of the Java Runtime…

LeetCode【300】最长递增子序列

题目&#xff1a; 思路&#xff1a; 通常来说&#xff0c;子序列不要求连续&#xff0c;而子数组或子字符串必须连续&#xff1b;对于子序列问题&#xff0c;第一种动态规划方法是&#xff0c;定义 dp 数组&#xff0c;其中 dp[i] 表示以 i 结尾的子序列的性质。在处理好每个…

vue3+ts项目02-安装eslint、prettier和sass

创建项目 项目创建 安装eslint yarn add eslint -D生成配置文件 npx eslint --init安装其他插件 yarn add -D eslint-plugin-import eslint-plugin-vue eslint-plugin-node eslint-plugin-prettier eslint-config-prettier eslint-plugin-node babel/eslint-parser vue-e…

关于IDEA中gradle项目bootrun无法进入断点以及gradle配置页面不全的解决方案

问题背景 在使用gradle编写的bootrun&#xff0c;采用debug方式启动项目时&#xff0c;无法进入断点&#xff0c;程序正常运行 并发现象1 此处无法识别为大象图标 点击右键后&#xff0c;没有圈中的这个选项 并发现象2 图片圈中的位置缺失 问题原因 正常的 run 命令是通过…

小程序, 多选项

小程序, 多选项 <view class"my-filter-btnwrap"><block wx:for"{{archiveList}}" wx:key"index"><view class"my-filter-btnitem text-ellipsis {{item.checked ? active : }}" data-index"{{index}}" wx…

CTF 全讲解:[SWPUCTF 2021 新生赛]Do_you_know_http

文章目录 参考环境题目hello.php雾现User-Agent伪造 User-AgentHackBarHackBar 插件的获取修改请求头信息 雾散 a.php雾现本地回环地址与客户端 IP 相关的 HTTP 请求头X-Forwarded-For 雾散 参考 项目描述搜索引擎Bing、GoogleAI 大模型文心一言、通义千问、讯飞星火认知大模型…

什么是全流程的UI设计?它与单页面的视觉设计有什么区别?

在软件产品研发流程中&#xff0c;产品交互设计一般是根据项目需求&#xff0c;做出设计方案&#xff0c;以求解决某个问题。而全流程的设计不再局限于短暂或者单个页面的视觉优化&#xff0c;而是追求持续性地参与&#xff0c;以全局性整体性地提升产品体验。 在软件内的信息传…

广州华锐互动:候车室智能数字孪生系统实现交通信息可视化

随着科技的不断发展&#xff0c;数字化技术在各个领域得到了广泛的应用。智慧车站作为一种新型的交通服务模式&#xff0c;通过运用先进的数字化技术&#xff0c;为乘客提供了更加便捷、舒适的出行体验。 将智慧车站与数字孪生大屏结合&#xff0c;可以将实际现实世界的实体车站…