HTTPServer改进思路2(mudou库核心思想融入)
mudou网络库思想理解
Reactor与多线程
服务器构建过程中,不仅仅使用一个Reactor,而是使用多个Reactor,每个Reactor执行自己专属的任务,从而提高响应效率。
首先Reactor是一种事件驱动处理模式,其主要通过IO多路复用机制统一监听想要关心的事件,如果关心的事件有响应后,则将该响应事件分发给进程或者线程去处理,从而提高网络服务器的性能。简单可以理解为服务器利用该模式处理多路请求,然后将它们同步的分发给线程或者进程去处理。
其次构建服务器可以使用多个Reactor,从而来提高服务器对事件的响应效率。将负责连接与客户端通信监控分离开,则是一个Reactor专门负责连处理新连接的请求事件,如果发现有新的连接,则将其分发给子Reactor中去监控,另一个Reactor则主要负责监控客户端是否发送了请求或者其他事件,如果有事件触发,则将其交给线程池处理。
主线Reactor处理新连接 ---->交给子Reactor进行监控 ——>事件发生交给线程处理
最后,Reactor需要将待处理的任务交付给线程或者进程去处理,如果想要使得服务器效率高,肯定要避免线程或者进程的频繁的创建销毁,这样会占用服务器的性能。所以构建线程池与Reactor配合,有助于减少性能消耗。
任务线程池的主要作用则是分配独立的线程去执行Reactor中需要处理的任务,最后将处理好的数据交给Reactor线程,让其完成对客户端的响应。
One Loop Per One Thread
主Reactor负责获取连接,获取新连接后,将新连接分发给子Reactor进行网络通信的事件处理。子Reactor监控各个描述符下的读写等事件,当事件响应的时候分发给线程池中的线程去处理。
主要思想是将某一事件所有的操作放在一个线程中进行,即一个线程对应着一种事件的处理,而每个 Reactor都可以使用线程池让其为自己服务,从而可以并发的实现请求的响应。(每个线程与一个独立的事件循环绑定)
核心思想分析
- 事件驱动
- 每个线程都绑定一个事件循环,处理一系列非阻塞的IO事件
- 事件循环,则是通过事件队列接收事件,同事根据事件类型和回调函数执行该事件对应的操作
- 线程与事件绑定
- 线程间独立运行自己的事件循环,而不会与其他线程共享事件循环
- 这样的好处在于,避免了线程竞争,从而提高并发效率
- 非阻塞I/O
- 事件循环的过程中,I/O操作都是非阻塞的,这样一来,线程就不会因为等待I/O操作,而阻塞等待
- 实现一个线程在同一时间内,可以处理多个I/O请求,从而提高系统的吞吐量
- 任务调度与并发
- 任务被分发到不同的线程,各个线程可以独立处理自己的任务序列
- 通过该方式,可以借助负载均衡机制,确保各个线程的工作量均衡,从而防止某个线程过载。
- 编程简化
- 一个线程对应一个事件循环,所以并发程序的设计过程中,无需考虑线程同步与锁的机制
- 只需要关注如何处理事件以及回调函数即可
一个线程绑定的事件在服务器中通常都是I/O事件
- 连接事件
- 建立连接:客户端尝试连接服务器的时候,服务器的监听线程会收到一个连接建立的事件(Accept)。
- 服务器接收这个连接后,将其分配一个工作线程去处理。(在该网络库的思想中,服务器接收连接后,会将其交给子Reactor监控,子Reactor监控到事件发生后,再从线程池中调取线程去执行)
- 读事件
- 数据到达:客户端发送请求数据到达服务器的时候,服务器的线程收到一个读事件,此时监控读事件的Reactor开始响应。从线程池中调用线程去读取数据,解析HTTP请求,并根据请求的内容做出对应处理
- 写事件
- 数据可写事件:服务器准备好响应数据,并准备将该响应数据发送给客户端的时候,服务器此时会触发写事件,表示可以将数据写入到客户端的连接中。
- 关闭事件
- 连接关闭事件:客户端或者服务端关闭连接的时候,触发这个关闭事件,服务器需要清理相应的资源,确保连接正常关闭,避免该连接浪费服务器资源。
项目改进架构思路
服务器架构通过多线程和事件驱动的架构来高效的处理大量并发请求。服务器同时采用非阻塞I/O模式,避免了阻塞操作带来的性能瓶颈,从而让服务器可以高效的处理大量并发请求。

主要模块功能逻辑
Main Reactor
- 初始化
- 创建EventLoop实例
- 创建并初始化Acceptor,设置监听端口和回调函数
- 接受新连接
- Acceptor中,监听新连接请求
- 使用accept系统调用,同时调用回调函数,为新连接进行对应封装
- 分发新连接
- 主Reactor将新连接分配给Reactor,创建Channel对象管理连接
Sub Reactor
- 初始化
- 创建EventLoop对象
- 创建独立线程去运行EventLoop(一个线程一个Reactor思想)
- 处理I/O事件
- EventLoop中通过EPOLL等待关心事件发生
- 将发生的事件分配给Channel对象
- 事件处理
- Channel根据事件的类型,调用注册的回调函数
- 读事件处理:读取数据并解析HTTP请求
- 写事件处理:发送HTTP响应
Epoller逻辑
- 初始化
- 创建EPOLL实例
- 管理文件描述符
- 添加、更新和删除文件描述符
- 等待监控事件发生
- 利用epoll_wait 等待事件的发生
HttpServer逻辑
- 初始化
- 创建HttpServer实例,同时继承TcpServer的功能
- 注册HTTP请求处理函数
- 处理HTTP请求
- 解析HTTP请求,生成HttpRequsest对象
- 根据请求的路径调用相应的处理函数
- 生成并发送响应
- 调用处理函数后生成HttpResponse对象
- 将响应转化为字符串的形式发送给客户端
Main逻辑
- 注册处理函数
- 注册URL路径以及对应的处理函数
- 处理具体业务逻辑
- 根据具体的需求,处理HTTP请求并生成响应
代码架构设计
Server
#ifndef SERVER_HPP
#define SERVER_HPP#include <vector>
#include <functional>
#include <memory>
#include <sys/epoll.h>
#include <unistd.h>class Channel;
class EventLoop;class Epoller {
private:int _epollFd;std::vector<epoll_event> _events;public:Epoller();~Epoller();void UpdateChannel(Channel* channel);void RemoveChannel(Channel* channel);void Poll(std::vector<Channel*>& activeChannels);
};class Channel {
private:EventLoop* _loop;const int _fd;uint32_t _events;uint32_t _revents;std::function<void()> _readCallback;std::function<void()> _writeCallback;public:Channel(EventLoop* loop, int fd);void SetReadCallback(const std::function<void()>& cb);void SetWriteCallback(const std::function<void()>& cb);void EnableReading();void EnableWriting();void DisableWriting();void DisableAll();void Remove();void HandleEvent();void Update();int Fd() const;uint32_t Events() const;uint32_t Revents() const;void SetRevents(uint32_t revents);
};class EventLoop {
private:bool _quit;std::vector<Channel*> _activeChannels;Epoller _poller;public:EventLoop();void Loop();void Quit();void UpdateChannel(Channel* channel);void RemoveChannel(Channel* channel);
};class Acceptor {
private:EventLoop* _loop;int _listenFd;std::function<void(int)> _newConnectionCallback;public:Acceptor(EventLoop* loop, int port);void SetAcceptCallback(const std::function<void(int)>& cb);void Listen();void HandleRead();
};class SubReactor {
private:EventLoop _loop;std::thread _thread;public:SubReactor();void Run();EventLoop* GetLoop();void Join();
};class MainReactor {
private:EventLoop _loop;Acceptor _acceptor;std::vector<SubReactor*> _subReactors;int _nextReactor;public:MainReactor(int port, int subReactorCount);void NewConnection(int fd);void Run();void JoinSubReactors();
};#endif // SERVER_HPP
HTTP
#ifndef HTTP_HPP
#define HTTP_HPP#include "server.hpp"
#include <unordered_map>
#include <functional>
#include <string>class HttpRequest {
public:std::string _method;std::string _path;std::string _version;std::unordered_map<std::string, std::string> _params;std::unordered_map<std::string, std::string> _headers;std::string _body;// 解析请求
};class HttpResponse {
public:int _status;std::unordered_map<std::string, std::string> _headers;std::string _body;void SetContent(const std::string& content, const std::string& type) {_body = content;_headers["Content-Type"] = type;}// 响应
};class HttpServer : public TcpServer {
private:std::unordered_map<std::string, std::function<void(const HttpRequest&, HttpResponse*)>> _handlers;public:HttpServer(int port);void RegisterHandler(const std::string& path, const std::function<void(const HttpRequest&, HttpResponse*)>& handler);void OnMessage(const PtrConnection& conn, const std::string& message);
};#endif // HTTP_HPP
main
#include "http.hpp"#define WWWROOT "./wwwroot/"int main() {HttpServer server(8888);server.Start();return 0;
}
细节问题梳理
Reactor、Channel|、epoll三者结合
多Reactor模型中三者结合分析
- 每个Reactor都拥有自己的epoll实例:具体也就是每个EventLoop(Reactor)都拥有自己属于自己的epoll实例来管理文件描述符和就绪事件
- 每个Channel对象与一个文件描述符和一个Reactor(EventLoop)关联:新连接到来时,主Reactor接受新连接请求后,将文件描述符分配给子Reactor,子Reactor创建一个Channel对象来管理文件描述符
- 事件循环独立运行:每个EventLoop都是独立运行,用于管理文件描述符上关心的事件
新连接创建后其文件描述符作用
- 主Reactor接收新连接后,accept会返回这个连接的文件描述符,然后将其分发给对应的子Reactor中
- 子Reactor管理连接:Channel类负责将文件描述符上的(读写)事件通知到对应的处理函数
进程与线程关系梳理/新连接文件描述符管理问题
- 首先,服务器启动后作为一个进程运行,多个Reactor则是该进程下的线程
- 主Reactor和子Reactor都是在服务器下作为线程执行
- 每个Reactor线程有自己的文件描述符,管理客户端连接(自己需要关心的特定事件)
- 新连接到来后,内核又会为新连接创建一个新的文件描述符
- 主Reactor又会将这个新获取的文件描述符分发给子Reactor中进行管理和处理
线程池和和任务队列的处理
- 主Reactor接收新连接后,将新连接分发给子Reactor中进行管理和处理
- 子Reactor处理I/O事件后,将其放入任务队列
- 线程池中的线程取出任务并执行任务
- 线程池与事件循环放在一起,子Reactor线程将任务交给线程池处理即可
请求到响应流程分析
- 服务器初始化
- 监听端口;设置线程数量(Reactor数量)注册处理函数
- 启动服务器接口
- 接收新连接
- 主Reactor监听新连接,新连接到达后通过NewConnetion方法将连接分发给从Reactor中进行管理
- 分发连接
- 主Reactor借助LoopThreadPool将新连接交给子Reactor中的EventLoop中进行处理
- 处理请求
- 子Reactor线程的EventLoop处理分配到的连接
- 读取请求数据,同时将其解析为HttpRequest对象
- 调用注册的处理函数对请求进行处理
- 生成响应
- 处理数据,根据传入的请求,生成HttpResponse对象,同时设置响应内容
- 发送响应
- 将生成的响应,转换为字符串格式,发送给客户端
- 关闭连接
- 处理请求和响应后,将连接放入到连接池中备用
相关文章:
HTTPServer改进思路2(mudou库核心思想融入)
mudou网络库思想理解 Reactor与多线程 服务器构建过程中,不仅仅使用一个Reactor,而是使用多个Reactor,每个Reactor执行自己专属的任务,从而提高响应效率。 首先Reactor是一种事件驱动处理模式,其主要通过IO多路复用…...
Kubernetes Secret 详解
Kubernetes Secret 是一种用于存储和管理敏感信息的对象,如密码、OAuth 令牌和 SSH 密钥等。使用 Secret 可以避免将机密数据直接放在 Pod 规约或容器镜像中,从而增加了应用程序的安全性。 Secret 的类型 Kubernetes 支持多种类型的 Secret,包括: Opaque:默认的…...
docker笔记4-部署
docker笔记4-部署 一、部署nginx二、部署Tomcat三、部署ESKibana3.1 部署ES3.2 部署kibana 一、部署nginx docker search nginx #搜索nginx的最新版本docker pull nginx #这里可以指定nginx版本,如果不指定,那么就拉取最新版本latestdocker run -d --na…...
有监督学习基础
基本概念 给定输入有为(x,y),其中x表示学习特征,y表示输出,m表示输入总数,有监督学习旨在根据输入建立能够预测可能输出的模型,大致可以分为回归和分类两种,代表可能输出是无限的或…...
揭开 AI 绘画提示词的神秘密码!
前言 ** 揭秘AI 绘画 ** 提示词的神秘密码 亲爱的朋友们,今天我们要一起探索 AI 绘画世界中那神秘的“密码”——提示词。 在 AI 绘画的奇妙领域里,提示词就像是一把神奇的钥匙,能够开启无尽的创意之门。它是我们与 AI 进行心灵对话的桥…...
macOS 10.15中屏蔽Microsoft Edge浏览器的更新提示
文章目录 1.效果对比2.安装描述文件3.停用描述文件4.高级操作(可选)参考文献 最近在macOS10.15系统,打开Microsoft Edge浏览器,每次打开都有个烦人的提示“ 要获取将来的 microsoft edge 更新,需要 macos 10.15 或更高…...
Qt 实战(3)数据类型 | 3.2、QVariant
文章目录 一、QVariant1、存储数据1.1、存储Qt内置数据1.2、存储自定义数据 2、获取数据3、判断数据类型4、清空数据5、总结 前言: QVariant是Qt框架中一个非常强大且灵活的类,它提供了一种通用的方式来存储和转换几乎任何类型的数据。无论是基本数据类型…...
Docker中安装的postgresql14在启用vector扩展的时候,找不到该扩展的控制文件。
ERROR: could not open extension control file “/usr/share/postgresql/14/extension/vector.control”: No such file or directory 进入容器 docker exec -it CONTAINER ID /bin/bash 1.更新 apt-get apt-get update 2.安装插件 #不同版本对应修改数字即可 apt-get i…...
JS防抖和节流
一、防抖和节流的适用场景 防抖(Debounce): 适合在输入框输入时的实时搜索、窗口大小调整时的resize事件等。节流(Throttle): 适合如页面滚动时的scroll事件、按钮点击时的请求发送等需要控制频率的场景。 …...
OpenWrt 为软件包和docker空间扩容
参考资料 【openwrt折腾日记】解决openwrt固件刷入后磁盘空间默认小的问题,关联openwrt磁盘扩容空间扩容【openwrt分区扩容】轻松解决空间可用不足的尴尬丨老李一瓶奶油的YouTube 划分空间 参考一瓶奶油的YouTube 系统 -> 磁盘管理 -> 磁盘 -> 修改 格…...
重要的工作任务,怎么在电脑桌面设置倒计时?
在日常工作中,我们总是面临着众多工作任务,如何高效地管理和完成这些任务成为了每个职场人的必备技能。为任务设置倒计时,不仅能让我们清晰地看到任务的先后顺序,还能帮助我们更好地把握时间,提高工作效率。想象一下&a…...
Failed to build get_cli:get:的解决方案
项目场景: 今天安装Getx命令行的时候,输入这面文档报了一个错: dart pub global activate get_cli 问题描述 提示:这里描述项目中遇到的问题: 例如:数据传输过程中数据不时出现丢失的情况,偶尔…...
短视频矩阵源码技术分享
在当今数字媒体时代,短视频已成为吸引观众和传递信息的重要手段。对于开发者而言,掌握短视频矩阵源码技术不仅是提升自身技能的需要,更是把握行业发展趋势的必然选择。本文将深入探讨短视频矩阵源码的关键技术要点及其实现方法,帮…...
轮播图自定义内容
官网:Swiper演示 - Swiper中文网 下载: npm i swiper Vue3示例代码: <template><div class"swiper mySwiper"><div class"swiper-wrapper"><div class"swiper-slide"><div>…...
大数据-44 Redis 慢查询日志 监视器 慢查询测试学习
点一下关注吧!!!非常感谢!!持续更新!!! 目前已经更新到了: Hadoop(已更完)HDFS(已更完)MapReduce(已更完&am…...
Istio_01_Istio初识
文章目录 IstioService Mesh Istio Istio: 以服务网格形态用于服务治理的开放平台和基础设施 本质: 以非侵入式治理服务之间的访问和调用服务治理: 流量管理、可观测性、安全性可同时管理多类基础设施(多种网络方案) 如: Istio和Kubernetes架构的结合 Istio通过Kubernetes的域…...
leetcode日记(47)螺旋矩阵Ⅱ
这题思路不难,就是找规律太难了。 我首先的思路是一行一行来,根据规律填入下一行的数组,第i行是由前i个数字(n-2*i)个增序数列后i个数字组成,后来觉得太难找规律了就换了一种思路。 思路大致是先计算出需…...
centos系统mysql主从复制(一主一从)
文章目录 mysql80主从复制(一主一从)一、环境二、服务器master1操作1.开启二进制日志2. 创建复制用户3. 服务器 slave1操作4. 在主数据库中添加数据 mysql80主从复制(一主一从) 一、环境 准备两台服务器,都进行以下操…...
IEDA怎么把springboot项目 启动多个
利用Idea提供的Edit Configurations配置应用参数。 点击Modify Options进行添加应用参数: 确保这里勾选...
Vue 3项目安装Element-Plus
Element Plus 是一个基于 Vue 3 的现代前端UI框架,它旨在提升开发体验,并为开发者提供高效、优雅的组件。如果你正在使用 Vue 3 进行项目开发,那么安装和集成 Element Plus 是一个不错的选择。在本文中,博主将详细介绍如何在 Vue …...
设计模式和设计原则回顾
设计模式和设计原则回顾 23种设计模式是设计原则的完美体现,设计原则设计原则是设计模式的理论基石, 设计模式 在经典的设计模式分类中(如《设计模式:可复用面向对象软件的基础》一书中),总共有23种设计模式,分为三大类: 一、创建型模式(5种) 1. 单例模式(Sing…...
vscode(仍待补充)
写于2025 6.9 主包将加入vscode这个更权威的圈子 vscode的基本使用 侧边栏 vscode还能连接ssh? debug时使用的launch文件 1.task.json {"tasks": [{"type": "cppbuild","label": "C/C: gcc.exe 生成活动文件"…...
高频面试之3Zookeeper
高频面试之3Zookeeper 文章目录 高频面试之3Zookeeper3.1 常用命令3.2 选举机制3.3 Zookeeper符合法则中哪两个?3.4 Zookeeper脑裂3.5 Zookeeper用来干嘛了 3.1 常用命令 ls、get、create、delete、deleteall3.2 选举机制 半数机制(过半机制࿰…...
将对透视变换后的图像使用Otsu进行阈值化,来分离黑色和白色像素。这句话中的Otsu是什么意思?
Otsu 是一种自动阈值化方法,用于将图像分割为前景和背景。它通过最小化图像的类内方差或等价地最大化类间方差来选择最佳阈值。这种方法特别适用于图像的二值化处理,能够自动确定一个阈值,将图像中的像素分为黑色和白色两类。 Otsu 方法的原…...
ffmpeg(四):滤镜命令
FFmpeg 的滤镜命令是用于音视频处理中的强大工具,可以完成剪裁、缩放、加水印、调色、合成、旋转、模糊、叠加字幕等复杂的操作。其核心语法格式一般如下: ffmpeg -i input.mp4 -vf "滤镜参数" output.mp4或者带音频滤镜: ffmpeg…...
VTK如何让部分单位不可见
最近遇到一个需求,需要让一个vtkDataSet中的部分单元不可见,查阅了一些资料大概有以下几种方式 1.通过颜色映射表来进行,是最正规的做法 vtkNew<vtkLookupTable> lut; //值为0不显示,主要是最后一个参数,透明度…...
【Java_EE】Spring MVC
目录 Spring Web MVC 编辑注解 RestController RequestMapping RequestParam RequestParam RequestBody PathVariable RequestPart 参数传递 注意事项 编辑参数重命名 RequestParam 编辑编辑传递集合 RequestParam 传递JSON数据 编辑RequestBody …...
Mysql8 忘记密码重置,以及问题解决
1.使用免密登录 找到配置MySQL文件,我的文件路径是/etc/mysql/my.cnf,有的人的是/etc/mysql/mysql.cnf 在里最后加入 skip-grant-tables重启MySQL服务 service mysql restartShutting down MySQL… SUCCESS! Starting MySQL… SUCCESS! 重启成功 2.登…...
【Android】Android 开发 ADB 常用指令
查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...
深入理解Optional:处理空指针异常
1. 使用Optional处理可能为空的集合 在Java开发中,集合判空是一个常见但容易出错的场景。传统方式虽然可行,但存在一些潜在问题: // 传统判空方式 if (!CollectionUtils.isEmpty(userInfoList)) {for (UserInfo userInfo : userInfoList) {…...
