🎬 个人主页:艾莉丝努力练剑
❄专栏传送门:《C语言》《数据结构与算法》《C/C++干货分享&学习过程记录》 《Linux操作系统编程详解》《笔试/面试常见算法:从基础到进阶》《Python干货分享》
⭐️为天地立心,为生民立命,为往圣继绝学,为万世开太平
🎬 艾莉丝的简介:

文章目录
- 前言
-
- 一、 开头部分(框架引入)
-
- 1. 整体学习框架导入语
- 2. 结构化知识导图
- 1 ~> 核心接口实现:EnableReadWrite
-
- 1.1 接口设计背景与核心逻辑
- 1.2 Reactor 层接口实现
- 1.3 Poller 层能力支撑:ModEvents 与 EpollCtlHelper
-
- 1.3.1 ModEvents 接口初始实现
- 1.3.2 EpollCtlHelper 公共封装
- 1.4 发送逻辑与接口的联动
- 1.5 EnableReadWrite知识图谱
- 2 ~> 协议层与异常处理修正
-
- 2.1 协议解析失败的逻辑修正
- 2.2 接收端异常处理链路
- 3 ~> 连接生命周期:删除机制实现
-
- 3.1 删除操作的时序原则
- 3.2 Reactor 层 DelConnection 实现
- 3.3 Poller 层 DelEvents 实现
-
- 3.3.1 EPOLL_CTL_DEL 的系统调用规范
- 3.3.2 代码实现与边界修正
- 4 ~> 核心代码细节与边界场景
-
- 4.1 写事件使能的两种触发方案
- 4.2 报文时序保证
- 4.3 事件派发 LoopOnce 逻辑
- 5 ~> 连接保活机制
-
- 5.1 连接活跃时间戳设计
- 5.2 保活检测的执行时机
- 5.3 淘汰策略的工程化扩展
- 6 ~> Reactor 模式核心理论
-
- 6.1 反应堆模式的本质
- 6.2 半同步半异步特性
- 6.3 单 Reactor 与多 Reactor 的典型应用
- 7 ~> 多进程 Reactor 改造方案
-
- 7.1 核心架构:Master-Slaver 进程池
- 7.2 One Thread One Loop 设计思想
- 7.3 负载均衡与连接分发逻辑
- 8 ~> 多线程 Reactor 改造方案
-
- 8.1 方案一:管道通知式多线程
- 8.2 方案二:eventfd 事件通知机制
-
- 8.2.1 eventfd 核心特性
- 8.2.2 eventfd 两种工作模式
- 8.2.3 基于 eventfd 的多线程 Reactor 架构
- 9 ~> 知识图谱部分
-
- 9.1 补充:实现 ModEvents(Poller.hpp)(内部实现一个EpollCtlHelper函数,方便使用这个方法和AddEvents,因为代码都差不多)
- 9.2 测试:客户端直接拿网络版本计算器里面实现过的(OnlineCalClient.cc)
- 9.3 补充:HandlerRequest里面小修改(Protocol.hpp)
- 9.4 补充:改一下客户端(有问题,会刷屏)
- 9.5 补充:测试一下走到异常处理(telnet模拟错误的请求)
- 9.6 补充:实现 DelConnection(Reactor.hpp)&& 实现DelEvents(Poller.hpp)
- 9.7 测试
- 9.8 细节问题
- 9.9 单Reactor模式收尾
- 9.10 多Reactor模式
- 10 ~> 补足细节之后的单Reactor的代码部分说明
- 结尾部分:核心总结
-
- 1. 接口实现类核心考点
- 2. 异常与边界类高频坑点
- 3. 事件机制核心原理
- 4. 连接保活机制考点
- 5. 多执行流改造核心架构
- 6. Reactor 模式理论考点
- 结尾

前言
一、 开头部分(框架引入)
1. 整体学习框架导入语
本文围绕 Linux 高性能网络服务器的核心架构 ——Reactor 反应堆模式展开,覆盖单 Reactor 模型的代码收尾、连接全生命周期管理、异常边界处理,以及多进程 / 多线程架构的改造方案。全篇基于 epoll 的 ET 边缘触发模式,深度拆解读写事件动态使能、内核事件同步、连接销毁时序、连接保活、跨执行流事件通知等工程级细节,同时对原代码中的遗留缺陷、边界隐患进行逐一审计与修正,形成逻辑闭环的复习体系,是网络服务端开发的核心考核内容。
2. 结构化知识导图
Reactor代码收尾与多执行流改造
+- 核心接口:EnableReadWrite
| |– 接口功能:动态控制套接字读写事件关心
| |– Reactor层:存在性校验 + 事件位运算 + 内核同步
| `– Poller层:ModEvents + EpollCtlHelper公共封装
+- 协议与异常处理修正
| |– 协议层:解析失败退出进程→返回空串+错误码
| `– IO层:错误码判定→触发连接异常销毁
+- 连接删除机制
| |– 销毁时序:epoll移除 → 关闭fd → 移除管理节点
| |– Reactor层:DelConnection接口
| `– Poller层:DelEvents + EPOLL_CTL_DEL规范
+- 核心代码细节
| |– 写触发:直接调用Sender / 使能EPOLLOUT自动触发
| |– 时序保证:单Reactor下请求应答严格有序
| `– 事件派发:LoopOnce超时机制 + 错误事件转换
+- 连接保活机制
| |– 时间戳字段:_lasttimestamp
| |– 更新时机:读写事件触发时调用Update
| `– 淘汰逻辑:定时遍历 + 超时连接销毁
+- Reactor模式理论
| |– 本质:事件驱动 + 回调分发
| |– 特性:半同步半异步
| `– 分类:单Reactor(Redis) / 多Reactor(Nginx)
+- 多进程Reactor改造
| |– 架构:Master-Slaver进程池
| |– 核心思想:One Thread One Loop
| `– 分发:管道通知 + 子进程自主accept
`– 多线程Reactor改造
|– 方案1:管道通知分发连接fd
|– 方案2:eventfd轻量事件通知
| |– eventfd特性:计数器、原子性、支持epoll
| `– 两种模式:普通模式 / 信号量模式
`– 架构:Master线程accept + 任务队列 + eventfd唤醒
1 ~> 核心接口实现:EnableReadWrite
1.1 接口设计背景与核心逻辑
在非阻塞网络编程中,发送缓冲区的状态是动态变化的:当数据一次性无法全部发送完毕时,需要让 epoll 开始关心 EPOLLOUT 事件,等待发送缓冲区空闲后自动继续发送;当数据全部发送完成后,需要取消对 EPOLLOUT 的关心,避免频繁触发无效的写就绪事件。 EnableReadWrite 接口的核心作用,就是动态调整指定套接字在 epoll 上的事件监听状态,实现发送流程的自动化事件驱动。
1.2 Reactor 层接口实现
接口接收三个参数:套接字 sockfd、是否使能读事件 enableread、是否使能写事件 enablewrite。 执行流程:
void EnableReadWrite(int sockfd, bool enableread, bool enablewrite)
{
if(!IsConnectionExists(sockfd))
return;
uint32_t events = ((enableread ? EPOLLIN : 0) | (enablewrite ? EPOLLOUT : 0) | EPOLLET);
_connections[sockfd]->SetEvents(events);
// 事件变更写透到内核
_epoller->ModEvents(sockfd, events);
}
[批注]
原笔记中存在函数名大小写不一致、sockfd 笔误为 sockta 的问题,此处已统一修正。同时补充:在 AddConnection 流程中,必须设置回指指针conn->_R = this,让 Connection 对象能够反向调用 Reactor 的接口,这是连接自主修改事件的前提,属于典型的双向关联设计。
1.3 Poller 层能力支撑:ModEvents 与 EpollCtlHelper
1.3.1 ModEvents 接口初始实现
ModEvents 对应系统调用 epoll_ctl 的 EPOLL_CTL_MOD 操作,用于修改内核中指定套接字的监听事件。初始实现与 AddEvents 高度相似,仅操作码不同,存在大量冗余代码。
1.3.2 EpollCtlHelper 公共封装
为消除代码冗余,抽取公共辅助函数 EpollCtlHelper,接收套接字、事件集合、操作码三个参数,统一执行 epoll_ctl 调用。AddEvents、ModEvents、后续的 DelEvents 均可复用该函数。
private:
int EpollCtlHelper(int sockfd, uint32_t events, int oper)
{
struct epoll_event ev;
ev.events = events;
ev.data.fd = sockfd;
return epoll_ctl(_epfd, oper, sockfd, &ev);
}
public:
void AddEvents(int sockfd, uint32_t events)
{
int n = EpollCtlHelper(sockfd, events, EPOLL_CTL_ADD);
if(n < 0)
{
LOG(LogLevel::FATAL) << "AddEvents error!";
}
}
void ModEvents(int sockfd, uint32_t events)
{
int n = EpollCtlHelper(sockfd, events, EPOLL_CTL_MOD);
if(n < 0)
{
LOG(LogLevel::FATAL) << "ModEvents error!";
}
}
[批注]
辅助函数仅负责执行系统调用,错误处理由上层业务接口负责,是合理的分层设计。同时需要注意:EPOLL_CTL_MOD 操作必须保证套接字已被添加至 epoll,否则会返回系统错误,这也是 Reactor 层先做存在性校验的核心原因。
1.4 发送逻辑与接口的联动
在 Sender 发送完成后,根据输出缓冲区_outbuffer 是否为空判断发送状态,自动调用 EnableReadWrite 调整事件:
- 缓冲区为空:数据全部发送完毕,关闭写事件关心,仅保留读事件。
- 缓冲区非空:本次发送未全部完成,开启写事件关心,等待下一次写就绪时自动继续发送。
// 发送完成后状态判断
if(_outbuffer.empty())
_R->EnableReadWrite(_sockfd, true, false);
else
_R->EnableReadWrite(_sockfd, true, true);
整个发送流程完全由 epoll 事件驱动,无需上层业务手动循环重试,实现了 IO 事件与业务逻辑的解耦。
1.5 EnableReadWrite知识图谱

2 ~> 协议层与异常处理修正
2.1 协议解析失败的逻辑修正
早期多进程版本的协议处理中,报文解析失败会直接调用 exit (1) 终止进程。但在单 Reactor 模型中,所有连接共享同一个进程,单个连接的协议错误不能导致整个服务退出,必须修正该逻辑。
修正后的 HandlerRequest 执行逻辑:
std::string HandlerRequest(std::string &streamstr, int *code)
{
LOG(LogLevel::DEBUG) << "Enter HandlerRequest";
std::string resp_package;
// 1. 检测报文完整性
while(true)
{
std::string jsonstring;
int n = UnPackage(streamstr, &jsonstring);
if(n == 0)
{
*code = 0;
LOG(LogLevel::DEBUG) << "解析完毕";
return resp_package;
}
else if(n == –1)
{
*code = –1;
LOG(LogLevel::DEBUG) << "协议解析失败";
return std::string();
}
// 反序列化、业务计算、封装响应
// …
}
}
[批注]
原笔记中遗留的 exit (1) 是典型的多进程到单 Reactor 迁移的适配遗漏。单 Reactor 模型下,单个连接的异常必须只影响自身,必须通过连接销毁的方式处理,绝不能终止整个服务进程,这是服务端高可用的基础原则。
2.2 接收端异常处理链路
在 IOHandler 的 Recver 函数中,调用协议处理函数后,根据返回的错误码分支处理:
- 错误码为 0:业务处理正常,将响应追加到输出缓冲区
- 错误码非 0:协议或业务异常,调用 Excepter 执行连接销毁流程
同时,recv 系统调用返回 0(对端关闭连接)、返回负值且错误码不是 EAGAIN/EWOULDBLOCK 时,也都会触发 Excepter 异常处理。
3 ~> 连接生命周期:删除机制实现
3.1 删除操作的时序原则
连接销毁必须严格遵循执行顺序,否则会触发内核错误或野指针问题:
3.2 Reactor 层 DelConnection 实现
DelConnection 是连接销毁的统一入口,首先做存在性校验,然后严格按照上述时序执行销毁。
void DelConnection(int sockfd)
{
if (!IsConnectionExists(sockfd))
return;
LOG(LogLevel::INFO) << "delete sockfd: " << sockfd;
// 1. 从epoll中移除
_epoller->DelEvents(sockfd);
// 2. 关闭文件描述符
_connections[sockfd]->Close();
// 3. 从连接管理表中移除
_connections.erase(sockfd);
}
[批注]
原笔记中该行日志误加了std::endl,而自定义日志类未重载 endl 的处理,会导致模板参数推导失败、编译报错。正确做法是直接输出字符串与整数,换行由日志类内部统一处理,这是自定义日志组件的常见踩坑点。
同时,Connection 基类需要定义纯虚函数 Close,由派生类分别实现:
- IOHandler::Close:直接调用 close 关闭业务套接字
- Listener::Close:调用封装好的 TcpSocket::Close 关闭监听套接字
3.3 Poller 层 DelEvents 实现
3.3.1 EPOLL_CTL_DEL 的系统调用规范
epoll_ctl 的 EPOLL_CTL_DEL 操作,在 Linux 2.6.9 之后的内核中,event 参数可以传 nullptr,内核会忽略该参数。传入无效的事件结构体虽多数情况下不会报错,但属于不规范写法。
3.3.2 代码实现与边界修正
在 EpollCtlHelper 中增加对 DEL 操作的特殊处理,DEL 操作直接传 nullptr,不填充 event 结构体:
int EpollCtlHelper(int sockfd, uint32_t events, int oper)
{
if (oper == EPOLL_CTL_DEL)
{
return epoll_ctl(_epfd, oper, sockfd, nullptr);
}
struct epoll_event ev;
ev.events = events;
ev.data.fd = sockfd;
return epoll_ctl(_epfd, oper, sockfd, &ev);
}
void DelEvents(int sockfd)
{
EpollCtlHelper(sockfd, 0, EPOLL_CTL_DEL);
}
[批注]
原笔记中直接传 0 作为 events 参数给 DEL 操作,虽功能上不会出错,但不符合系统调用最佳实践。标准做法是 DEL 操作直接传入 nullptr 作为 event 参数,避免无意义的参数传递,同时兼容所有内核版本。
4 ~> 核心代码细节与边界场景
4.1 写事件使能的两种触发方案
当业务层产生待发送数据时,有两种方式触发发送流程:
- 方案一:直接调用 Sender:数据写入输出缓冲区后,立刻调用 Sender 尝试发送。一次性发完则关闭写事件,没发完则自动开启写事件等待下一次就绪。优点是延迟低,数据产生后立即尝试发送。
- 方案二:仅使能写事件:数据写入输出缓冲区后,不直接调用发送,仅调用 EnableReadWrite 开启写事件。由于 epoll 的特性,使能 EPOLLOUT 时会立刻触发一次写就绪事件,由事件循环自动调用 Sender。优点是代码统一,所有发送都由事件循环驱动。
// 方案一:直接触发发送
if(!_outbuffer.empty())
Sender();
// 方案二:仅使能写事件,由事件循环触发
if(!_outbuffer.empty())
_R->EnableReadWrite(_sockfd, true, true);
[批注]
epoll 的 EPOLLOUT 事件在使能时默认触发一次就绪,本质是因为写缓冲区默认处于空闲状态,只要使能写事件就满足就绪条件。这一特性是方案二能够成立的底层基础,也是 epoll 写事件的核心特性,属于高频考点。
4.2 报文时序保证
单 Reactor 模型下,所有 IO 事件和业务处理都在同一个线程 / 进程中串行执行,因此请求的接收、处理、响应的顺序严格一致:先到达的请求先处理,先处理的请求先响应,不存在时序错乱问题。 这也是单 Reactor 模型的核心优势之一:无需考虑并发时序问题,业务逻辑开发简单,无锁开销。
4.3 事件派发 LoopOnce 逻辑
LoopOnce 是 Reactor 的单次事件循环,核心流程:
void LoopOnce(int timeout)
{
int n = _epoller->WaitEvents(revs, gnum, timeout);
for(int i = 0; i < n; i++)
{
int sockfd = revs[i].data.fd;
uint32_t revents = revs[i].events;
if ((revents & EPOLLERR) | (revents & EPOLLHUP))
revents = (EPOLLIN | EPOLLOUT); // 错误转换为读写事件,交由后续逻辑处理异常
if ((revents & EPOLLIN) && IsConnectionExists(sockfd))
_connections[sockfd]->Recver();
if ((revents & EPOLLOUT) && IsConnectionExists(sockfd))
_connections[sockfd]->Sender();
}
}
[批注]
将错误事件转换为读写事件是非常巧妙的设计:错误发生时,读写操作必然会返回错误,进而触发 Excepter 销毁连接。这样就不需要单独编写错误事件的处理逻辑,所有异常都收敛到读写的错误分支中,简化了代码结构。
5 ~> 连接保活机制
5.1 连接活跃时间戳设计
网络服务中存在大量半打开连接、静默连接,长期占用文件描述符资源,需要通过保活机制淘汰超时不活跃的连接。 实现基础是在 Connection 基类中增加_lasttimestamp字段,记录连接最近一次活跃的时间戳,同时提供 Update 方法更新时间戳。
class Connection
{
// 其他成员省略
protected:
uint64_t _lasttimestamp; // 最近活跃时间戳
public:
void Update()
{
_lasttimestamp = CurrentTimeStamp();
}
};
其中 CurrentTimeStamp 通过系统调用 time 获取当前秒级时间戳,返回 uint64_t 类型避免溢出。
5.2 保活检测的执行时机
时间戳更新时机:每次 Recver、Sender 被调用时,开头先调用 Update () 更新时间戳,代表该连接当前处于活跃状态。 检测时机:将 Dispatcher 的 epoll_wait 超时设置为固定值(如 1000ms),每次 LoopOnce 结束后,执行一次 ConnectionKeepAlive 检测。
void Dispatcher()
{
int timeout = 1000;
while(true)
{
LoopOnce(timeout);
ConnectionKeepAlive();
}
}
5.3 淘汰策略的工程化扩展
基础实现可以遍历_connections 映射表,逐个比较时间戳,超时则调用 DelConnection 销毁。 工程级优化方案:使用最小堆管理连接的超时时间,堆顶就是最早超时的连接,每次只需要检查堆顶是否超时即可,将检测复杂度从 O (n) 降到 O (1)。需要注意:连接每次活跃时都要更新堆中的位置,保证堆的有序性。
6 ~> Reactor 模式核心理论
6.1 反应堆模式的本质
Reactor 反应堆模式是一种事件驱动的 IO 处理架构,核心思想是:
- 预先为每个文件描述符注册对应的事件回调函数
- 通过 IO 多路复用(epoll/select/poll)统一监听事件
- 当某个文件描述符的事件就绪时,触发对应的回调函数执行对应的 IO 操作
可以类比为 “打地鼠” 模型:每个连接是一个地洞,事件就绪就是地鼠露头,回调函数就是锤子,哪个地鼠露头就打哪个。
6.2 半同步半异步特性
Reactor 是典型的半同步半异步架构:
- 异步:事件的到来是异步的,连接建立、数据到达的时间完全不可预知,由多路复用器统一监听。
- 同步:事件触发后的 IO 操作、业务处理是同步执行的,在当前执行流中完成,不会异步转交。
6.3 单 Reactor 与多 Reactor 的典型应用
- 单 Reactor:所有事件监听、IO 处理、业务逻辑都在同一个执行流中完成。典型代表是 Redis,因为 Redis 是内存数据库,业务处理极快,瓶颈不在 CPU,单 Reactor 足以支撑高并发,且避免了锁的开销。
- 多 Reactor:将监听、IO 处理、业务拆分到多个执行流中,充分利用多核 CPU。典型代表是 Nginx,采用多进程 Reactor 架构。
[批注]
存在一种常见错误方案:单 Reactor + 后端线程池。该方案将业务处理丢给线程池,但会导致请求应答的时序无法保证:线程调度顺序不可控,后到的请求可能先处理完,造成响应乱序。因此该方案在严格时序要求的场景下不推荐使用。
7 ~> 多进程 Reactor 改造方案
7.1 核心架构:Master-Slaver 进程池
多进程 Reactor 采用 Master-Slaver 架构:
7.2 One Thread One Loop 设计思想
每个 Slaver 进程都有自己独立的 Reactor、独立的事件循环,从连接建立到连接销毁全生命周期都在同一个进程内完成,这就是 “One Thread One Loop”(一个执行流一个事件循环),是服务器领域公认的优秀设计。 该设计的核心优势:
- 无锁:每个进程只管理自己的连接,没有共享资源,不需要锁
- 时序保证:单个连接的所有操作都在同一个进程内,时序严格有序
- 故障隔离:单个子进程崩溃不影响其他子进程,服务整体可用
7.3 负载均衡与连接分发逻辑
Master 进程分发连接时,可以采用轮询、随机等负载均衡策略,选择一个 Slaver 子进程,向其管道写入数据,唤醒对应的子进程执行 accept。 子进程被唤醒后:
8 ~> 多线程 Reactor 改造方案
8.1 方案一:管道通知式多线程
该方案与多进程架构逻辑完全一致,将进程替换为线程:
- 主线程作为 Master,监听 listen 套接字
- 多个工作线程作为 Slaver,每个线程拥有独立的 Reactor
- 主线程与工作线程之间通过管道通信,分发连接
与多进程的区别:线程共享文件描述符表,因此主线程可以先 accept 获取新连接的 fd,再通过管道将 fd 数值传递给工作线程,工作线程直接将 fd 加入自己的 Reactor 即可。 该方案的缺点:管道属于内核级通信,需要经历用户态到内核态的数据拷贝,对于多线程场景来说开销偏大,不够轻量化。
8.2 方案二:eventfd 事件通知机制
8.2.1 eventfd 核心特性
eventfd 是 Linux 提供的轻量级事件通知机制,本质是内核维护的一个 64 位计数器,以文件描述符的形式提供给用户态。 核心特性:
- 低开销:仅维护计数器,内核成本远低于管道
- 支持多路复用:可以加入 epoll、poll、select 进行监听
- 原子性:读写操作都是原子的,适合高并发场景
- 仅通知不传输数据:只能传递事件信号,不能传递具体消息内容
8.2.2 eventfd 两种工作模式
// 普通模式创建
int efd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
// 信号量模式创建
int efd = eventfd(0, EFD_SEMAPHORE | EFD_NONBLOCK | EFD_CLOEXEC);
[批注]
原笔记中对普通模式的读取描述存在偏差:普通模式下,read 会将计数器的当前值全部读出,同时将计数器清零,而不是 “读取一次减 1”。信号量模式才是每次读取减 1,两者的语义区别是核心考点,必须严格区分。
8.2.3 基于 eventfd 的多线程 Reactor 架构
架构设计:
该方案结合了用户态任务队列和内核态事件通知:
- 任务队列用于传递具体的连接 fd,在用户态完成,开销低
- eventfd 仅用于唤醒工作线程,解决了 “工作线程在 epoll_wait 阻塞,无法感知任务队列有新数据” 的问题 完美兼顾了性能和事件驱动的统一性。
9 ~> 知识图谱部分
9.1 补充:实现 ModEvents(Poller.hpp)(内部实现一个EpollCtlHelper函数,方便使用这个方法和AddEvents,因为代码都差不多)

9.2 测试:客户端直接拿网络版本计算器里面实现过的(OnlineCalClient.cc)

9.3 补充:HandlerRequest里面小修改(Protocol.hpp)

9.4 补充:改一下客户端(有问题,会刷屏)

9.5 补充:测试一下走到异常处理(telnet模拟错误的请求)

9.6 补充:实现 DelConnection(Reactor.hpp)&& 实现DelEvents(Poller.hpp)

9.7 测试

9.8 细节问题

9.9 单Reactor模式收尾

9.10 多Reactor模式

10 ~> 补足细节之后的单Reactor的代码部分说明

光是目录结构就已经如此夸张了,本文字数已经达到12000字了,再放这几乎相当于一个标准项目的代码行数的“基于单Reactor的网络版本计算器”到本文的正文中就有点太长了,大家不用担心,艾莉丝会专门更新一篇【Reactor模式】的代码篇来作为Linux网络部分多路转接、也是整个Linux部分的收官之作,不过Linux学习并没有到此为止,远远没有结束!还得继续努力复习巩固、查漏补缺才行!
结尾部分:核心总结
1. 接口实现类核心考点
- EnableReadWrite 的位运算事件组装逻辑,ET 模式的强制开启规则
- epoll_ctl 三种操作(ADD/MOD/DEL)的使用场景与参数规范
- EpollCtlHelper 封装的设计思想,EPOLL_CTL_DEL 传 nullptr 的标准写法
- 连接销毁的严格时序:先 epoll 移除,再关 fd,最后移除管理节点;顺序颠倒会导致内核报错或野指针问题
- Connection 回指指针的作用:让连接对象能够主动调用 Reactor 接口修改自身事件状态
2. 异常与边界类高频坑点
- 单 Reactor 下禁止单个连接异常终止整个进程,协议解析失败必须走连接销毁流程,这是多进程迁移到单 Reactor 的典型适配错误
- 自定义日志类不可直接使用 std::endl,会导致模板参数推导失败,属于 C++ 自定义流输出的常见踩坑点
- EPOLLERR/EPOLLHUP 的标准处理方式:转换为读写事件统一收敛异常逻辑,避免重复编写错误处理代码
- ET 模式下必须循环读写直到返回 EAGAIN/EWOULDBLOCK,确保本次事件触发的所有数据全部处理完毕
3. 事件机制核心原理
- EPOLLOUT 使能即触发就绪的底层原因:写缓冲区默认空闲,使能即满足就绪条件
- 两种写触发方案的优劣对比:直接调用延迟低,使能触发代码统一
- 单 Reactor 的时序保证原理:串行执行无并发调度干扰,请求与应答严格按顺序对应
- 单 Reactor + 线程池方案的本质缺陷:线程调度不可控导致响应时序错乱,严格时序场景禁用
4. 连接保活机制考点
- 时间戳的更新时机与检测时机的设计逻辑
- 超时检测的两种实现方案:遍历法复杂度 O (n),最小堆法复杂度 O (1)
- epoll_wait 超时参数的双重作用:避免无限阻塞 + 定时执行保活等后台任务
5. 多执行流改造核心架构
- One Thread One Loop 的核心思想与三大优势:无锁、时序保证、故障隔离
- Master-Slaver 架构的职责划分:Master 仅负责分发,Slaver 负责连接全生命周期管理
- 多进程与多线程 Reactor 的分发差异:多进程由子进程自主 accept,多线程由主线程 accept 后传递 fd
- eventfd 与管道的优劣对比:eventfd 开销更低、仅支持事件通知、无法传输数据;管道支持数据传输、开销更高
- eventfd 两种模式的语义区别:普通模式一次性清零,信号量模式每次减 1,适用场景不同
- eventfd + 任务队列架构解决的核心问题:同时支持 epoll 事件等待与用户态任务分发,兼顾性能与事件驱动统一性
6. Reactor 模式理论考点
- 半同步半异步的具体含义:事件到来是异步,IO 与业务处理是同步
- 单 Reactor 的典型代表与适用场景:Redis、CPU 非瓶颈的纯 IO 业务
- 多 Reactor 的典型代表与适用场景:Nginx、CPU 密集型高并发业务
- Reactor 模式的核心价值:用单执行流支撑大量并发连接,适配互联网 “连接多、活跃少” 的典型场景
结尾
uu们,本文的内容到这里就全部结束了,艾莉丝在这里再次感谢您的阅读!
|
艾莉丝努力练剑
C/C++ & Linux 底层探索者 | 一个正在努力练剑的技术博主 👀 ❤️ ⭐ 💬 不要忘记给博主“一键四连”哦! “今日练剑达成!” “技术之路难免有困惑,但同行的人会让前进更有方向。” |
结语:希望对学习Linux相关内容的uu有所帮助,不要忘记给博主“一键四连”哦!
往期回顾:
【Linux网络】多路转接epoll(四)Reactor模式:基于epoll的高性能网络服务器设计与实现(中)代码细节
🗡博主在这里放了一只小狗,大家看完了摸摸小狗放松一下吧!🗡
૮₍ ˶ ˊ ᴥ ˋ˶₎ა

网硕互联帮助中心







评论前必须登录!
注册