Zixhy头像
关注

手撕Reactor模型实现同步高并发服务器及Muduo网络库深入解析

手撕Reactor模型实现同步高并发服务器及Muduo网络库深入解析


第一章 Reactor网络模型深度剖析与架构演进

1.1 Reactor模型的基本概念与核心思想

Reactor(反应器)模式是一种经典的基于事件驱动(Event-Driven)的高性能并发I/O设计模式。其核心思想可以用一句话概括:“I/O多路复用监听事件,事件就绪后非阻塞分发给对应的事件处理器(Handler)进行处理”

在传统的多线程/多进程模型中,服务端通常采用阻塞式I/O(Blocking I/O)加“每连接一线程”(Thread-per-Connection)的模式。当面对数万甚至数十万高并发连接时,操作系统的线程调度开销、上下文切换损耗以及每个线程固定占用的栈空间(如默认8MB)会迅速耗尽系统资源,导致系统吞吐量严重下降甚至崩溃。

Reactor模式解耦了连接建立、I/O监听与具体业务处理:

  1. 统一事件循环(Event Loop):单个或多个线程持续运行事件循环,借助底层的I/O多路复用机制(Linux下的 epoll、BSD/macOS下的 kqueue、Windows下的 IOCP 类似机制)同时监听海量文件描述符(fd)。
  2. 非阻塞I/O(Non-blocking I/O):套接字全部设置为非阻塞模式,防止单个fd的数据未准备好而导致整个事件循环线程被挂起。
  3. 回调与分发(Demultiplex & Dispatch):当在事件循环中监听到某个文件描述符fd发生读、写、错误或关闭事件就绪时,Reactor在该事件循环中,将该事件分发给绑定的Channel或回调函数执行逻辑进行处理。

Reactor核心工作流程

就绪事件集合

连接就绪

读就绪

写就绪

异常/关闭

客户端连接 / 数据到达

I/O多路复用器 epoll_wait

事件分发器 Reactor/Demultiplexer

Acceptor 处理握手连接

Read Handler 读取网络数据并处理业务

Write Handler 发送缓冲区数据

Error/Close Handler 回收套接字资源


1.2 Reactor模型与Proactor模型的对比

Reactor与Proactor是异步网络编程中最常对比的两种设计范式,其最本质的区别在于**“I/O操作是由操作系统内核完成,还是由应用程序自身完成”Reactor模型采用同步方式需要应用层完成网络数据收发然后继续操作,但采用事件驱动方式而不会进行阻塞,如本文实现的3种Reactor模型和Muduo网络库,即其通过I/O多路复用方式先监听的事件就绪后才进行同步操作不会阻塞;Proactor模型采用异步方式立即返回而不会进行阻塞,在内核完成网络数据收发后通过回调函数方式等通知应用层继续操作,如Boost库实现的异步并并发服务器、Windows下的IOCP等**:

对比维度Reactor 模型(反应式)Proactor 模型(前摄式)
I/O模型基础同步非阻塞 I/O(Sync Non-blocking I/O + I/O Multiplexing)异步 I/O(Asynchronous I/O,如 POSIX aio, Linux io_uring, Windows IOCP,Boost库的异步IO)
就绪通知内容“通知何时可读/可写”(内核告知应用层某fd已就绪,应用层自己调用 read/write 搬移数据)“通知I/O操作已完成”(内核完成网卡缓冲区与用户空间缓冲区的数据搬移后,再通知应用层)
数据缓冲区操作用户空间在事件就绪后主动申请/提供缓冲区并调用读取系统调用应用层在发起异步请求时提前提供缓冲区,内核在后台将数据填入该缓冲区
操作系统支持几乎所有现代OS完美支持(Linux epoll、FreeBSD kqueue、Solaris evportWindows原生IOCP支持优秀;Linux传统 aio 仅支持直接文件I/O,直到内核5.1引入 io_uring 才趋于完善
编程与调试复杂度模型清晰直观,容易追踪调用链路,异常处理可控异步生命周期管理复杂,缓冲区在I/O进行期间不可被释放或重用,调试难度较高
模拟实现机制本生为同步多路复用模式可用 Reactor + 线程池模拟 Proactor(如Boost.Asio的epoll后端实现)

1.3 实际工业级应用场景

Reactor模式因其轻量、高吞吐、资源消耗可控的特点,成为了互联网高性能服务基础设施的标准设计范式:

  1. 高性能反向代理与Web服务器
    • Nginx:采用==多进程单Reactor(对等模式)架构==,基于 epoll 实现数万级别的并发请求路由与负载均衡。
    • Envoy:现代云原生服务网格数据平面,采用多线程Reactor模型,每个工作线程独立运行事件循环。
  2. 高性能网络通信框架
    • Netty(Java):Java领域高并发底座,其 BossGroup(主Reactor负责连接接受)与 WorkerGroup(从Reactor负责I/O与编解码)是经典的主从多Reactor范式。
    • Muduo(C++):陈硕先生设计的高性能C++非阻塞网络库,贯彻“one loop per thread + thread pool”哲学。
  3. 分布式存储与内存数据库
    • Redis 6.0+:核心执行引擎虽然维持单线程以确保内存命令操作原子性,但网络I/O已引入多线程Reactor(I/O Threads)负责数据读写与协议解析。
  4. RPC框架与分布式中间件
    • gRPC、brpc、Dubbo、Kafka通信层:底层均为Reactor多路复用模型,用于保障成千上万个微服务节点之间的高吞吐长连接保活与数据交互。

第二章 本项目中三种Reactor模型的实现思路与架构对比

在本项目 ReactorTCPNet 中,框架提供了3种可灵活切换的反应器架构:

  1. 单反应器模式(SINGLE)
  2. 多反应器主从模式(MASTER_SLAVE)
  3. 多反应器对等模式(PEER_TO_PEER)

2.1 单反应器模式(SINGLE)

2.1.1 实现思路
  • 整个服务器仅创建一个 EpollTaskScheduler(索引为0)并在一个专有线程或主线程中运行。
  • Acceptor 的监听描述符 _sockfd 挂载在该唯一反应器的 epoll 上。
  • 当有新连接到达时,AcceptReadCallback 将新连接的套接字分配给该唯一反应器(actor = _eventloop->GetIndexActor(0))。
  • 所有连接的建立、断开、数据读写、业务处理和定时任务都在这同一个线程与事件循环中串行运行
2.1.2 优点与局限
  • 优点:结构最简单,完全无线程切换损耗,无需对连接映射表 _tcpconnections_map 或读写队列加重锁。
  • 局限无法利用现代服务器多核CPU算力;如果某个业务处理耗时较长,整个服务器的所有网络连接均会被阻塞延迟响应
2.1.3 数据与控制流图

单反应器模式 (Single Reactor)

唯一事件循环内部串行处理

TCP连接 / 数据

TCP连接 / 数据

TCP连接 / 数据

Accept事件

读写事件

定时事件

创建新连接并注册到本Actor

唯一 EventLoop / EpollTaskScheduler (Thread 0)

客户端 1

客户端 2

客户端 N

epoll_wait() 捕获全部事件

Acceptor 连接接收

TcpConnection 集合 (fd 1..N)

TimerEventQueue 定时器队列

ReadBuf 读取与业务解析

WriteBuf 发送响应


2.2 多反应器主从模式(MASTER_SLAVE)

2.2.1 实现思路
  • 对应经典的 主-从 Reactor(Main-Sub Reactor / Boss-Worker) 模式,也是本项目 Start() 的默认配置。
  • EventLoop 创建 NNN 个反应器线程(通常 NNN 等于 CPU 核心数):
    • 第0个反应器作为主反应器(Master Reactor):专门注册 Acceptor 监听描述符,只负责高吞吐地进行 TCP 三次握手与新连接接入,还负责关键的定时器任务回调
    • 1∼N−11 \sim N-11N1 个反应器作为从反应器(Slave/Sub Reactors):专门管理在主反应器成功建立连接,被分配给本从反应器的客户端 TcpConnection负责网络数据的并发读写、拆包、业务处理和定时器任务处理
  • 调度分发机制(负载均衡算法):当 Master 中的 Acceptor 读就绪并完成 accept() 获取客户端 sockfd 后,调用 _eventloop->GetEnableActor()。默认通过轮询算法(Round-Robin) 计算选出一个从反应器,将新创建的客户端 Channel 注册到选中的从反应器中。
2.2.2 优点与局限
  • 优点:职责划分清晰,连接建立与业务I/O彻底解耦;多从反应器能充分释放多核CPU性能高频突发的建立连接请求不会受到数据传输瓶颈的影响
  • 局限:主反应器单个线程只负责连接接入,在轻负载或极少新连接的静态长连接场景下,主反应器CPU利用率偏低。
2.2.3 数据与控制流图

多反应器主从模式 (Master-Slave Reactor)

Sub Reactor 2 内部

Sub Reactor 1 内部

Master Reactor (Actor 0)

连接请求 SYN

分配 fd 1

分配 fd 2

分配 fd N

数据读写请求

数据读写请求

数据读写请求

从反应器 Sub Reactor 2 (Thread 2)

海量客户端请求

主反应器 Master Reactor (Thread 0)

epoll_wait()

Acceptor: 监听与接受连接

调度算法 GetEnableActor (轮询分发)

从反应器 Sub Reactor 1 (Thread 1)

从反应器 Sub Reactor K (Thread K)

epoll_wait()

TcpConnection 1 (ReadBuf/WriteBuf)

TimerEventQueue 定时器

epoll_wait()

TcpConnection 2 (ReadBuf/WriteBuf)

TimerEventQueue 定时器


2.3 多反应器对等模式(PEER_TO_PEER)

2.3.1 实现思路
  • 对标 Nginx 核心架构模型。系统中创建的 NNN 个反应器(EpollTaskScheduler)彼此地位完全对等,没有任何一个专属的 Master 线程
  • Acceptor 在初始化监听套接字 _sockfd 后,通过循环为每个反应器分别实例化一个关联同一 _sockfdChannel,并挂载到每一个反应器的 epoll 实例上。
  • 内核级防惊群保障:为了防止在多反应器监听同一 _sockfd 时产生严重的惊群效应(Thundering Herd Problem),项目在 acceptchannel->AddReadEvent(true)添加了 EPOLLEXCLUSIVE 标志(Linux 4.5+ 内核特性),由系统内核确保有新连接到达时仅唤醒一个反应器的事件循环,防止惊群效应
  • 本地归属原则:即谁连接谁管理通信,当某个对等反应器被唤醒并完成客户端握手后,AcceptReadCallback(int sockfd, std::weak_ptr<EpollTaskScheduler> acceptor_actor) 会直接将新建的 TcpConnection 注册到捕获该连接的同一个反应器中(actor = acceptor_actor.lock()),极大提升了 CPU 缓存局部性(Cache Locality)。
2.3.2 优点与局限
  • 优点:资源利用极其均衡,在核数较少或短连接高吞吐场景下,不会浪费哪怕一个专属核心;减少了跨线程分配调度的上下文传递开销。
  • 局限:若某些短连接执行极耗时的计算任务,可能造成各个对等线程负载稍微不均衡;依赖操作系统内核对 EPOLLEXCLUSIVESO_REUSEPORT 的底层支持。
2.3.3 数据与控制流图

多反应器对等模式 (Peer-to-Peer Reactor)

Peer 1 处理流程

Peer 0 处理流程

注册-带EPOLLEXCLUSIVE

握手接收

注册到本反应器内

处理IO读写

握手接收

注册到本反应器内

处理IO读写

内核互斥唤醒一个线程

内核互斥唤醒一个线程

注册-带EPOLLEXCLUSIVE

注册-带EPOLLEXCLUSIVE

对等反应器 Peer1 / Thread1

服务器 Listen Socket _sockfd

对等反应器 Peer N

对等反应器 Peer0 / Thread0

创建 TcpConnection A

处理A的业务与数据

创建 TcpConnection B

处理B的业务与数据

客户端A发起连接

客户端B发起连接


2.4 三种模式全方位横向对比

评估维度单反应器模式 (SINGLE)多反应器主从模式 (MASTER_SLAVE)多反应器对等模式 (PEER_TO_PEER)
线程模型单线程单Loop1个Master线程 + N−1N-1N1 个Slave线程NNN 个完全平等的Worker线程
Acceptor位置挂载在唯一线程的Epoll上专职挂载在第0个Master线程Epoll上挂载在全部 NNN 个线程Epoll上(带 EPOLLEXCLUSIVE
新连接分配机制直接分配给当前线程Master通过轮询算法分发给Slave线程哪个线程命中唤醒,连接就就地绑定在该线程
惊群风险防范无惊群(单一监听)无惊群(主反应器单一监听)依靠Linux内核的 EPOLLEXCLUSIVE 标志防惊群
多核扩展能力差(只能单核运转)极佳(I/O密集多核并行处理)优秀(各线程完全独立自主)
连接与处理解耦无解耦彻底解耦连接建立与I/O处理在各自线程内部闭环
适用场景调试阶段、轻量级嵌入式设备、I/O量较小场景通用大型高并发网络服务(如Netty、Muduo默认范式)短连接高频吞吐服务、CPU密集或核数较少的服务器(如Nginx模式)

第三章 本项目核心模块划分与功能实现精讲

本项目源码结构清晰,严格遵循面向对象设计原则,实现了从底层的套接字操作到高层事件循环的完整封装。以下按模块逐一总结剖析:

项目模块分层架构

TcpServer (服务器核心管理、模式调度、连接表维护)

Acceptor (监听套接字、连接捕获、多模式绑定)

TcpConnection (业务连接抽象、生命周期管理、事件回调驱动)

EventLoop (多反应器线程池与调度引擎)

ReadBuf (自动扩容读缓冲) / WriteBuf (Packet消息队列写缓冲)

Channel (文件描述符fd事件抽象与分发器)

EpollTaskScheduler -> TaskScheduler (定时任务与IO事件联合调度)

ChannelEventEpoll (epoll_create/wait/ctl 面向对象封装)

TimerEventQueue / Timer (基于 timerfd / 时间戳的高精度定时器)

SockTool (套接字非阻塞、地址复用底层工具)

3.1 Channel 模块

  • 功能职责:将文件描述符 fd 与其所关注的 epoll 事件(EPOLLIN, EPOLLOUT, EPOLLRDHUP, EPOLLERR, EPOLLHUP)进行深度解耦封装,并维护对应epoll事件的回调函数表。
  • 关键设计细节
    • 支持 ET(边缘触发)LT(水平触发) 切换,开启 ET 时自动通过 SockTool::SetNonBlockfd 设为非阻塞。
    • 支持 EPOLLEXCLUSIVE 标志位添加,为对等模式下的套接字提供防惊群支撑。
    • 仿函数 operator()(uint32_t events) 内部定义了科学严谨的事件处理优先级:
      EPOLLERR (错误)→EPOLLHUP (挂起/全关)→EPOLLRDHUP (半关)→EPOLLPRI (带外)→EPOLLIN (可读)→EPOLLOUT (可写)\text{EPOLLERR (错误)} \to \text{EPOLLHUP (挂起/全关)} \to \text{EPOLLRDHUP (半关)} \to \text{EPOLLPRI (带外)} \to \text{EPOLLIN (可读)} \to \text{EPOLLOUT (可写)}EPOLLERR (错误)EPOLLHUP (挂起/全关)EPOLLRDHUP (半关)EPOLLPRI (带外)EPOLLIN (可读)EPOLLOUT (可写)
      当出现严重错误时,先调用错误回调再强制执行关闭回调,防止资源悬挂。

3.2 ChannelEventEpoll 模块

  • 功能职责:对 Linux epoll 系统调用(epoll_create1, epoll_ctl, epoll_wait)进行 RAII 级面向对象封装。
  • 关键设计细节
    • 维护 std::unordered_map<int, std::shared_ptr<Channel>> _channel_map,实现根据就绪 fd 快速检索对应的 Channel
    • 提供 AddChannelRemoveChannelUpdateChannel 方法,通过互斥锁 _channelmap_mutex 保障多线程增删 Channel 的安全性。
    • operator()() 中调用 epoll_wait(),就绪后直接触发 (*channel)(events) 回调。

3.3 定时器模块:Timer 与 TimerEventQueue

  • 功能职责:为网络库提供毫秒级精度的定时事件驱动(如连接超时剔除、心跳检测、定时轮询等)。
  • 关键设计细节
    • Timer 封装了任务超时时间戳、周期执行标志(again)以及业务执行函数。
    • TimerEventQueue 采用基于优先级的有序结构维护定时器;在基于事件驱动的反应器中,与 timerfd 相结合,将定时事件无缝转化为 epoll 的可读事件,实现“I/O事件与定时任务共用同一套驱动逻辑”。

3.4 任务调度模块:TaskScheduler 与 EpollTaskScheduler

  • 功能职责:事件调度执行器,构成了每个 Reactor 线程进行任务执行的心脏
  • 关键设计细节
    • TaskScheduler 基类负责管理和循环轮询执行 TimerEventQueue 中的定时任务。
    • EpollTaskScheduler 继承自 TaskScheduler,额外内嵌了 ChannelEventEpoll 成员。
    • 重写的 operator()() 构成了统一的事件调度中心:先处理 _channelevent() 捕获的全部网络 I/O 就绪事件,随后执行基类 TaskScheduler::operator()() 驱动当前到达的定时事件,实现了 I/O事件 + 业务定时任务 的单线程串行安全调度。

3.5 事件循环模块:EventLoop

  • 功能职责:多反应器管理引擎,负责管理整个反应器线程池与任务分发逻辑,内部可以启动多个任务调用线程,每个任务调用对应一个Reactor线程
  • 关键设计细节
    • 内部包含 std::vector<std::shared_ptr<EpollTaskScheduler>>std::vector<std::shared_ptr<std::thread>>
    • 默认根据主机的 CPU 核心数启动等量的后台 Reactor 线程,并令每个线程独立执行对应反应器的 (*scheduler)()
    • 提供了灵活的主从调度算法接口 SetActorIndexCallback,默认内置轮询算法 CalculateActorIndex,实现均匀地将新建连接打散到各个子反应器中。

3.6 监听器模块:Acceptor

  • 功能职责:负责服务端监听套接字的生命周期管理(socket →\to bind →\to listen →\to accept)。
  • 关键设计细节
    • 套接字创建时配置了 SO_REUSEADDR 属性,避免服务重启时的 TIME_WAIT 端口占用问题
    • 核心函数 Accept() 能够根据传入的 ReactorModel(单反应器、主从模式、对等模式)执行差异化的挂载策略:主从模式下仅挂载于第0个反应器,对等模式下遍历所有反应器同时挂载并配置 EPOLLEXCLUSIVE

3.7 连接管理与缓冲模块:TcpConnection(ReadBuf 、 WriteBuf)

  • 功能职责:表示单个连接成功的客户端,承载单个客户端的长生命周期,处理数据的安全读取、解析以及非阻塞异步发送的回调逻辑。
  • 关键设计细节
    • TcpConnection客户端连接器:表示一个连接成功的客户端,通过该连接器实现与客户端的通信。内部包括Channel客户端通信套接字封装对象 ,还包括对应网路数据接收或发送的相应事件回调处理逻辑;采用ReadBuf 成员实现客户端数据接收缓冲区采用WriteBuf成员维护客户端数据包发送缓冲队列
    • ReadBuf 自动扩容:内置两阶段数据容灾机制。可写空间不足时,先利用 memmove 将未读数据搬移至缓冲区头部;若仍不足单次最大读取尺寸(4096字节),则执行2倍动态扩容,最大上限控制在10MB,既防止内存碎片又保障了处理大报文的能力。
    • WriteBuf 数据包队列:发送逻辑基于 std::queue<Packet> 队列管理,通过 Packet 结构体记录已发送偏移 _sent_len,保证在并发写以及内核写缓冲区满导致 EAGAIN 时,数据包依然能够严格按序发送,不会出现乱序或数据丢包。
    • std::enable_shared_from_this 安全机制:确保当异步回调触发时,能够安全提升弱指针,有效防止在底层连接析构时执行回调产生野指针崩溃。

3.8 服务端控制器模块:TcpServer

  • 功能职责:整个网络库的顶层门面(Facade),代表一个TCP服务器,串联 AcceptorEventLoop 与全局客户端TcpConnection连接表 _tcpconnections_map
  • 关键设计细节
    • 采用虚函数机制暴露核心扩展点:AcceptReadCallbackCreateClientConnection 被声明为虚函数,允许上层业务派生类通过继承定制专属的连接对象与业务逻辑
    • 提供 AddClientConnectionRemoveClientConnection,使用 std::mutex 确保在并发关闭时连接对象的正确回收与内存清理。

第四章 如何使用本Reactor模型继承实现其他业务场景的服务器

本项目的架构设计高度模块化,通过**类派生重写(Inheritance & Override)回调机制(Callback Registration)**两种途径,开发者可以轻而易举地将其扩展为各类型的应用层协议服务器(如 HTTP 服务器、WebSocket 推送服务器、私有二进制 RPC 服务器等)。

4.1 继承扩展的关键步骤与设计模式

业务层扩展架构

派生

派生

重写虚函数

实例化

重写/注册业务回调

重写/注册关闭回调

基类: TcpServer

子类: CustomBusinessServer

基类: TcpConnection

子类: CustomBusinessConnection

CreateClientConnection()

BussinessReadEventCallback (数据协议解析)

BussinessCloseEventCallback (状态清理)

  1. 继承 TcpConnection:派生出定制连接类(如 MyBusinessConnection),增加特定协议的解析上下文(如状态机状态、未完成报文缓存、协议头解析结构体等)。
  2. 重写 CreateClientConnection:在自定义的 MyBusinessServer 中重载该虚函数,使其返回派生连接类的智能指针对象。
  3. 注册业务协议处理回调:通过 SetReadTcpCBDealCB 等接口,将业务解析与逻辑分发代码挂载到连接上。

4.2 实践示范:基于本框架构建一个回显(Echo)与数据转换服务器

以下代码展示了如何利用本项目框架快速派生实现一个业务服务器:

#include "TcpServer.h"
#include <iostream>
#include <algorithm>

// 步骤1:派生自定义业务连接类
class EchoConnection : public TcpConnection {
public:
    EchoConnection(int sockfd, std::shared_ptr<EpollTaskScheduler> actor)
        : TcpConnection(sockfd, actor)
    {
        // 绑定数据读取与协议处理回调
        SetReadTcpCBDealCB(std::bind(&EchoConnection::OnBusinessRead, this, std::placeholders::_1));
        // 绑定连接关闭回调
        SetCloseTcpCBDealCB(std::bind(&EchoConnection::OnBusinessClose, this, std::placeholders::_1));
    }

private:
    // 步骤2:实现应用层业务逻辑
    void OnBusinessRead(ReadBuf& readbuf) {
        uint32_t len = readbuf.ReadableDataSize();
        if (len == 0) return;

        char* data = readbuf.ReadStartPos();
        std::string message(data, len);

        std::cout << "[EchoServer] 收到客户端数据 [" << GetSockFD() << "]: " << message << std::endl;

        // 业务操作:如将收到的文本转换为大写并原样回显
        std::string echo_resp = "Echo: " + message;
        
        // 消费读缓冲区中的数据
        readbuf.OffsetStartPos(len);

        // 发送响应报文给客户端
        Send(echo_resp.size(), echo_resp.c_str());
    }

    void OnBusinessClose(std::shared_ptr<TcpConnection> conn) {
        std::cout << "[EchoServer] 客户端连接已关闭: fd=" << conn->GetSockFD() << std::endl;
    }
};

// 步骤3:派生业务服务器类
class EchoServer : public TcpServer {
public:
    EchoServer(int cpus, std::string ip, uint16_t port)
        : TcpServer(cpus, ip, port) {}

protected:
    // 步骤4:重写连接工厂方法,注入自定义连接对象
    std::shared_ptr<TcpConnection> CreateClientConnection(
        int sockfd, std::shared_ptr<EpollTaskScheduler> actor) override 
    {
        return std::make_shared<EchoConnection>(sockfd, actor);
    }
};

// 启动入口
int main() {
    // 启动一个主从Reactor模式的Echo服务器
    EchoServer server(4, "0.0.0.0", 8080);
    std::cout << "EchoServer 正在 0.0.0.0:8080 启动..." << std::endl;
    server.Start(TcpServer::ReactorModel::MASTER_SLAVE);
    return 0;
}

第五章 Muduo网络库核心API、核心类及高并发HTTP服务器实战

Muduo 是由陈硕开发的一款针对 Linux 平台的基于 C++11/C++03 的工业级非阻塞高性能网络通信库。它全面贯彻了 “Non-blocking I/O + One loop per thread” 设计思想。

5.1 Muduo 核心类及其关键 API 详解

管理主事件循环

维护客户端连接池

读写缓冲区

EventLoop

+loop() : void

+quit() : void

+runInLoop(Functor cb) : void

+queueInLoop(Functor cb) : void

+runAfter(double delay, TimerCallback cb) : TimerId

+runEvery(double interval, TimerCallback cb) : TimerId

+wakeup() : void

TcpServer

+setConnectionCallback(const ConnectionCallback& cb) : void

+setMessageCallback(const MessageCallback& cb) : void

+setWriteCompleteCallback(const WriteCompleteCallback& cb) : void

+setThreadNum(int numThreads) : void

+start() : void

TcpConnection

+send(const void* message, int len) : void

+send(const StringPiece& message) : void

+shutdown() : void

+forceClose() : void

+setContext(const boost::any& context) : void

+getContext() : boost::any

+connected() : bool

+peerAddress() : const InetAddress&

Buffer

+readableBytes() : size_t

+writableBytes() : size_t

+prependableBytes() : size_t

+peek() : const char

+retrieve(size_t len) : void

+retrieveAll() : void

+retrieveAllAsString() : string

+append(const char* data, size_t len) : void

+readFd(int fd, int* savedErrno) : ssize_t


5.1.1 EventLoop

每个线程最多拥有一个 EventLoop 对象,负责运行事件循环、分发 I/O 事件及执行跨线程任务队列。

void loop()
  • 功能:启动事件循环,阻塞于 I/O 多路复用器(epoll_wait),就绪后依次触发活动通道的事件回调与 pending functors。
  • 参数:无。
  • 返回值void
void quit()
  • 功能:退出事件循环。如果是跨线程调用,会先通过 wakeup() 唤醒处于阻塞中的 Loop 线程,随后安全退出循环。
  • 参数:无。
  • 返回值void
void runInLoop(const Functor& cb)
  • 功能:在当前 Loop 对应线程中执行用户指定的函数对象 cb。如果调用方恰好就在当前 Loop 线程,则立即同步执行;如果是其他线程调用,则自动放入待执行队列并唤醒 Loop 线程。
  • 参数cb - 待执行的任务仿函数/Lambda(类型为 std::function<void()>)。
  • 返回值void
void queueInLoop(const Functor& cb)
  • 功能:将任务 cb 压入当前 Loop 的任务队列中,并不立即同步执行,由 Loop 线程在下一轮循环时异步调用执行。
  • 参数cb - 待排队的任务回调。
  • 返回值void
TimerId runAfter(double delay, const TimerCallback& cb)
  • 功能:在指定的延迟秒数后单次执行指定的定时回调函数。
  • 参数delay - 延迟时间(单位:秒,支持浮点小数表示微秒级精度);cb - 定时触发的回调函数。
  • 返回值TimerId - 定时器唯一标识,可用于后续取消。
TimerId runEvery(double interval, const TimerCallback& cb)
  • 功能:以固定时间间隔周期性执行指定回调函数。
  • 参数interval - 循环触发周期(单位:秒);cb - 定时触发的回调函数。
  • 返回值TimerId

5.1.2 TcpServer

对外提供 TCP 服务器的配置、生命周期管理与事件回调钩子注入。

TcpServer(EventLoop* loop, const InetAddress& listenAddr, const string& nameArg, Option option = kNoReusePort)
  • 功能:构造函数,初始化绑定的监听地址、主反应器指针及选项。
  • 参数loop - 主事件循环指针;listenAddr - 监听 IP 与端口对象;nameArg - 服务器标识名称;option - 是否开启端口复用。
void setThreadNum(int numThreads)
  • 功能:配置子反应器(I/O 线程池)的工作线程总数。
  • 参数numThreads - 线程数量。当传入 0 时退化为单反应器;传入 N>0N > 0N>0 时演变为标准主从 Reactor 模式。
  • 返回值void
void start()
  • 功能:正式启动线程池并开始监听套接字连接事件。多次调用安全幂等。
  • 参数:无。
  • 返回值void
void setConnectionCallback(const ConnectionCallback& cb)
  • 功能:设置连接建立及连接断开时的用户业务通知回调。
  • 参数cb - 签名规范为 void(const TcpConnectionPtr&) 的回调函数。
  • 返回值void
void setMessageCallback(const MessageCallback& cb)
  • 功能:设置有网络数据可读并已成功读取至用户输入缓冲区后的业务处理回调。
  • 参数cb - 签名规范为 void(const TcpConnectionPtr&, Buffer*, Timestamp) 的回调函数。
  • 返回值void
void setWriteCompleteCallback(const WriteCompleteCallback& cb)
  • 功能:设置当发送缓冲区数据完全排空(内核写就绪并全部发出)时的通知回调,常用于高并发大文件低压流控。
  • 参数cb - 签名规范为 void(const TcpConnectionPtr&)
  • 返回值void

5.1.3 TcpConnection

代表一条已建立连接的客户端 TCP 信道。

void send(const void* message, int len) 与 void send(const StringPiece& message)
  • 功能:向对端发送数据。线程安全:若非当前 I/O 线程调用,内部会自动派发到对应的 Loop 线程中执行。当内核缓冲区不可写时自动暂存进用户输出 Buffer 并监听可写事件。
  • 参数:待发送的数据指针及长度或字符串片段。
  • 返回值void
void shutdown()
  • 功能:半关闭连接的写端(发送 TCP FIN 分节)。若当前发送缓冲区仍有未发送数据,会等待数据完全发送完毕后再关闭写端。
  • 参数:无。
  • 返回值void
void forceClose()
  • 功能:强制立即关闭当前 TCP 连接,不等待残留数据发送完毕。
  • 参数:无。
  • 返回值void
void setContext(const boost::any& context) 
const boost::any& getContext()
  • 功能:在连接对象上绑定/获取任意类型的协议解析上下文对象(如绑定 HTTP 请求解析器、用户会话状态 Session 等)。
  • 参数context - 任意类型数据容器。
  • 返回值:上下文引用或指针。
bool connected() const
  • 功能:获取当前连接是否处于正常活跃已连接状态。
  • 参数:无。
  • 返回值:布尔值(true 表示处于连接态)。

5.1.4 TcpClient

客户端连接端点,用于对外主动发起 TCP 连接并具备断线自动重连能力。

void connect()
  • 功能:发起非阻塞连接握手。
  • 参数:无。
  • 返回值void
void disconnect()
  • 功能:主动断开当前已建立的连接。
  • 参数:无。
  • 返回值void
void stop()
  • 功能:停止客户端并停止尝试自动重连。
  • 参数:无。
  • 返回值void
void enableRetry()
  • 功能:开启断线指数避让重连机制。
  • 参数:无。
  • 返回值void

5.1.5 Buffer 应用层缓冲区类

Muduo 自研的带内部偏移与自增长特性的双指针高性能网络缓冲区。

size_t readableBytes() const
  • 功能:获取当前可读取的有效数据字节长度。
  • 返回值:字节数。
size_t writableBytes() const
  • 功能:获取当前缓冲区末尾剩余可写容量空间。
  • 返回值:字节数。
const char* peek() const
  • 功能:获取指向未读数据起始位置的常量指针(只读访问,不挪动读取偏移)。
  • 返回值:内存指针。
void retrieve(size_t len) 与 void retrieveAll()
  • 功能:消费指定字节数或清空整个读取缓冲区,使读游标向后推进。
  • 参数len - 已读取消费的字节数。
  • 返回值void
string retrieveAllAsString()
  • 功能:将缓冲区内现存的所有有效可读数据抽取并构造转化为 std::string,同时清空缓冲区。
  • 返回值:包含全部有效数据的字符串。
ssize_t readFd(int fd, int* savedErrno)
  • 功能:巧妙利用 readv 分散读,结合栈上 64KB 临时缓冲区,在避免预分配大内存的同时保证单次读取能够彻底读完套接字中堆积的数据,防止 ET/LT 模式下的截断或重复触发。
  • 参数fd - 读取的文件描述符;savedErrno - 错误码输出指针。
  • 返回值:实际读取的总字节数。


5.2 Muduo HTTP 核心组件解析

Muduo 并在其拓展层(muduo/net/http)提供了 HTTP 基础服务抽象,其架构由四个关键类组成:

  1. HttpRequest
    • 封装 HTTP 请求头、请求方法(GET, POST, HEAD, PUT 等)、请求路径(Path)、查询参数(Query)及版本号(HTTP/1.0, HTTP/1.1)。
    • 提供 setMethod, setPath, setQuery, addHeader, getHeader 等方法。
  2. HttpResponse
    • 封装服务端 HTTP 响应报文,包含状态码(200 OK, 404 Not Found 等)、响应头(Content-Type, Content-Length, Connection 等)及响应体(Body)。
    • 提供 appendToBuffer(Buffer* output) 方法,负责将结构化响应组装为标准 HTTP 格式字节流。
  3. HttpContext
    • HTTP 协议解析状态机。维护当前连接的解析状态:kExpectRequestLine(期待请求行) →\to kExpectHeaders(期待头部) →\to kExpectBody(期待请求体) →\to kGotAll(解析完成)。
    • 提供 parseRequest(Buffer* buf, Timestamp receiveTime) 方法推进状态机。
  4. HttpServer
    • TcpServer 的进一步封装,内部默认拦截 setMessageCallback,驱动 HttpContext 进行报文反序列化。
    • 提供 setHttpCallback(const HttpCallback& cb) 接口,当收到并解析出一个完整有效的 HttpRequest 后,回调上层业务生成 HttpResponse

5.3 实战:基于 Muduo 网络库实现一个高并发 HTTP 服务器

下面是一个基于 Muduo 网络库核心组件实现的现代化高性能 HTTP 服务器示例程序。程序包含请求解析、多路由支持、Keep-Alive 长连接保活处理以及静态响应构建:

#include <muduo/net/http/HttpServer.h>
#include <muduo/net/http/HttpRequest.h>
#include <muduo/net/http/HttpResponse.h>
#include <muduo/net/EventLoop.h>
#include <muduo/base/Logging.h>

#include <iostream>
#include <string>
#include <map>

using namespace muduo;
using namespace muduo::net;

// 业务路由与请求处理函数
void onRequest(const HttpRequest& req, HttpResponse* resp) {
    std::cout << "[HttpServer] 收到请求: Method=" << req.methodString()
              << " Path=" << req.path() << std::endl;

    // 路由分发逻辑
    if (req.path() == "/") {
        // 首页路由
        resp->setStatusCode(HttpResponse::k200Ok);
        resp->setStatusMessage("OK");
        resp->setContentType("text/html; charset=utf-8");
        resp->addHeader("Server", "Muduo-HighPerf-HttpServer/1.0");
        
        std::string html_body = 
            "<!DOCTYPE html>"
            "<html><head><title>Muduo HTTP Server</title></head>"
            "<body>"
            "<h1>欢迎访问基于 Muduo 的高性能 HTTP 服务器</h1>"
            "<p>架构模式:主从多Reactor + 非阻塞I/O (One Loop Per Thread)</p>"
            "</body></html>";
        resp->setBody(html_body);
    } 
    else if (req.path() == "/api/status") {
        // RESTful API 状态检查接口
        resp->setStatusCode(HttpResponse::k200Ok);
        resp->setStatusMessage("OK");
        resp->setContentType("application/json; charset=utf-8");
        resp->addHeader("Server", "Muduo-HighPerf-HttpServer/1.0");
        
        std::string json_body = "{\"code\":0,\"status\":\"healthy\",\"threads\":4}";
        resp->setBody(json_body);
    } 
    else {
        // 404 资源不存在处理
        resp->setStatusCode(HttpResponse::k404NotFound);
        resp->setStatusMessage("Not Found");
        resp->setCloseConnection(true); // 非法路由直接通知关闭连接
        resp->setBody("<html><body><h1>404 Not Found</h1></body></html>");
    }
}

int main(int argc, char* argv[]) {
    // 设置日志级别
    Logger::setLogLevel(Logger::INFO);

    // 1. 初始化主事件循环 (Master EventLoop)
    EventLoop loop;

    // 2. 配置监听地址:监听 0.0.0.0:8000 端口
    InetAddress listenAddr(8000);

    // 3. 实例化 HttpServer 对象
    HttpServer server(&loop, listenAddr, "HighPerfHttpServer");

    // 4. 注册业务处理回调函数
    server.setHttpCallback(onRequest);

    // 5. 设置工作线程数量(设置为4个工作从反应器线程)
    // 主循环线程专门负责 accept,4个从反应器线程独立负责 HTTP 请求解析与响应发送
    server.setThreadNum(4);

    LOG_INFO << "HttpServer 启动中,监听端口: 8000,工作线程数: 4";
    
    // 6. 启动服务器与事件循环
    server.start();
    loop.loop();

    return 0;
}
5.3.1 运行流程说明
  1. 主线程初始化EventLoop loop 启动并持有主 Reactor,HttpServer 在端口 8000 创建监听套接字并注册进主 Loop。
  2. 多从线程池建立:调用 server.setThreadNum(4) 后,Muduo 底层创建包含 4 个独立工作子线程的 EventLoopThreadPool
  3. 连接分发与就绪:当外部高并发 HTTP 请求到达时,主 Reactor 线程处理握手,并通过轮询将新连接无锁安全分配至某一个子线程中的 EventLoop
  4. 事件驱动与序列化:子线程独立读取客户端报文并利用 HttpContext 执行协议解析,完成后调用 onRequest 回调填充 HttpResponse,最终由 Buffer 安全高效地写回客户端。

转载自 CSDN-专业IT技术社区

原文链接:https://blog.csdn.net/weixin_52198406/article/details/165008891

文章来源转载

评论

赞0

评论列表

微信小程序
QQ小程序

关于作者

点赞数:0
关注数:0
粉丝:0
文章:0
关注标签:0
加入于:--