Muduo网络库大总结
Muduo网络库大总结
目录
- 目的
- 知识储备
- IO模型
- 阻塞与非阻塞
- 五种IO模型
- epoll原理
- select/poll的缺点
- epoll的优势
- LT与ET模式
- Reactor模型
- muduo核心模块
- 扩展功能
目的
- 理解阻塞、非阻塞、同步、异步的概念
- 掌握Unix/Linux五种IO模型
- 深入理解epoll原理及优势
- 掌握Reactor模型设计
- 学习C++网络库的代码设计范式
- 实现基于事件驱动和线程池的面向对象编程
- 通过重构muduo库,用C++11替代Boost依赖
知识储备
关键点 | 说明 |
---|---|
TCP/UDP协议 | 理解传输层协议特性 |
IO复用编程 | 掌握select/poll/epoll接口的使用 |
多线程编程 | 熟悉pthread、C++20协程等并发模型 |
推荐书籍 | 《Linux高性能服务器编程》《UNIX环境高级编程》《鸟哥的Linux私房菜》 |
IO模型
阻塞与非阻塞
阶段 | 行为描述 |
---|---|
数据准备 | 阻塞:线程挂起等待数据 非阻塞:立即返回状态(EAGAIN) |
数据读写 | 同步:用户线程自行读写 异步:内核完成读写后通过回调/信号通知用户线程 |
陈硕观点:阻塞/非阻塞均为同步IO,只有使用特定API(如aio_read)才是异步IO。
五种IO模型
-
阻塞IO
- 进程全程阻塞,直到数据完成内核到用户空间的拷贝
-
非阻塞IO
while (recv(fd, buf, size, 0) == EAGAIN); // 轮询直到数据就绪
-
IO复用
-
通过select/poll/epoll监听多个fd,核心流程:
应用阻塞于epoll_wait -> 就绪事件触发 -> 同步处理数据
-
-
信号驱动
- 注册SIGIO信号处理函数,数据就绪时通过信号通知应用层
- 内核异步通知,数据读写仍需同步完成
-
异步IO
struct aiocb {int aio_fildes; // 文件描述符void *aio_buf; // 缓冲区指针// ...其他字段 }; aio_read(&aiocb); // 内核完成数据准备和拷贝后通知应用
epoll原理
select/poll的缺点
问题 | 描述 |
---|---|
FD数量限制 | select默认支持1024个fd |
内存拷贝开销 | 每次调用需复制fd集合到内核 |
遍历时间复杂度 | O(n)轮询所有fd |
触发方式 | 仅支持水平触发(LT) |
epoll的优势
- 高效数据结构
- 使用红黑树存储监控的fd,插入/删除时间复杂度为O(log n)
- 就绪事件通过双向链表返回,无需遍历所有fd
- 内核-用户共享
- 通过
epoll_create
创建上下文,避免重复拷贝fd集合
- 通过
- 触发模式支持
- LT模式:数据未读完持续通知
- ET模式:仅通知一次(需一次
read
处理完)
LT与ET模式对比
特性 | LT模式 | ET模式 |
---|---|---|
数据完整性 | 保证数据完全处理 | 可能需多次调用read |
系统调用次数 | 较多(每次处理部分数据) | 较少(一次处理全部数据) |
公平性 | 更好(均衡处理所有连接) | 可能饿死小数据量连接 |
跨平台 | 支持 | 仅Linux支持 |
muduo选择LT的原因:避免数据丢失、简化编程模型、更好的跨平台兼容性。
Reactor模型
核心组件
组件 | 功能描述 |
---|---|
Event | 封装文件描述符和关注的事件(读/写/错误) |
Reactor | 事件循环调度中心,调用epoll_wait 监听事件 |
Demultiplex | 事件分发器(如EPollPoller ) |
EventHandler | 事件处理器(如Channel 中的回调函数) |
muduo的多Reactor架构
MainReactor(单线程)└─ 监听新连接(Acceptor)
SubReactors(线程池)└─ 处理已建立连接的IO事件
- 优势:
- 主从线程分工明确,避免单线程瓶颈
- 每个SubReactor绑定独立线程,实现负载均衡
muduo核心模块
模块 | 功能描述 |
---|---|
Channel | 封装fd与事件回调(读/写/关闭) |
Poller | 基于epoll的事件监听(EPollPoller 实现) |
EventLoop | Reactor核心,驱动事件循环(loop.loop() ) |
EventLoopThread | 将EventLoop与线程绑定(one loop per thread) |
Buffer | 应用层读写缓冲区(支持自动扩容) |
TcpConnection | 管理TCP连接生命周期(数据收发、状态转换) |
TcpServer | 组合Acceptor和EventLoopThreadPool,提供服务器接口 |
扩展功能
-
定时器
- 实现方案:时间轮(高效)、红黑树(精确)
-
协议支持
- HTTP/RPC:基于Buffer解析协议
- DNS:集成第三方库(如c-ares)
-
性能优化
- 调整TCP参数(
SO_REUSEPORT
、TCP_NODELAY
) - 使用wrk/Jmeter进行压测
- 调整TCP参数(
-
代码重构
-
用
std::shared_ptr
替代Boost智能指针 -
基于C++11特性简化异步逻辑
-
muduo库核心模块交互关系图示
muduo库核心模块解析
1. TcpServer
- 核心职责:整个服务器的核心类,负责管理监听端口和处理新连接
- 子模块:
- Acceptor:负责监听新连接并接受新连接
- EventLoopThreadPool:管理多个线程,每个线程运行一个
EventLoop
2. EventLoopThreadPool
- 核心职责:管理多个事件循环线程池
- 子模块:
- EventLoopThread:每个线程包含一个
EventLoop
,负责处理分配给它的事件
- EventLoopThread:每个线程包含一个
3. EventLoopThread
- 核心职责:将事件循环与线程绑定
- 子模块:
- EventLoop:每个线程运行一个
EventLoop
,负责处理事件循环
- EventLoop:每个线程运行一个
4. EventLoop
- 核心职责:驱动事件循环的核心单元
- 子模块:
- Poller:负责监听和分发事件(如
epoll
实现) - ChannelList:存储所有注册的
Channel
对象
- Poller:负责监听和分发事件(如
5. TcpConnection
- 核心职责:管理单个TCP连接的生命周期
- 子模块:
- Channel:封装了
Socket
的事件处理逻辑(如读写回调) - Buffer:用于存储读取和发送的数据(应用层缓冲区)
- Socket:封装了底层的套接字操作(如
bind
/listen
)
- Channel:封装了
6. Acceptor
- 核心职责:专门处理新连接建立
- 子模块:
- Channel:封装监听套接字(
listenfd
)的事件处理逻辑 - Socket:封装底层的监听套接字操作(如
accept
)
- Channel:封装监听套接字(
muduo库服务端启动与事件处理流程
-
启动监听
• 「TcpServer
」调用「Acceptor
」的方法启动监听端口。 -
新连接到来
• 「Acceptor
」通过监听套接字(listenfd
)接收到新连接后,通知「TcpServer
」。 -
分配连接
• 「TcpServer
」将新连接分配给「EventLoopThreadPool
」(事件循环线程池)。 -
选择 EventLoop
• 「EventLoopThreadPool
」通过轮询算法选择一个「EventLoop
」来处理该连接的后续事件。 -
注册 Channel
• 被选中的「EventLoop
」将新连接对应的「Channel
」注册到其管理的「Poller
」中。 -
返回活跃事件
• 「Poller
」通过epoll_wait
检测到活跃事件后,将就绪的「Channel
」列表返回给「EventLoop
」。 -
处理回调
• 「EventLoop
」遍历活跃的「Channel
」,调用其绑定的读/写/关闭事件回调函数。 -
数据读写
• 「Channel
」的回调函数通过「Buffer
」进行数据读写操作:- 读操作:从内核接收缓冲区拷贝数据到应用层「
Buffer
」 - 写操作:将「
Buffer
」中的数据写入内核发送缓冲区
- 读操作:从内核接收缓冲区拷贝数据到应用层「
-
应用处理
• 读取的数据通过「TcpConnection
」传递给上层应用逻辑(如HTTP解析、业务计算等)。
关键设计特点
- 主从Reactor模式
- Main Reactor(主EventLoop):仅处理新连接建立(
Acceptor
) - Sub Reactors(子EventLoop池):处理已建立连接的I/O事件
- Main Reactor(主EventLoop):仅处理新连接建立(
- 线程模型
one loop per thread
:每个EventLoop绑定到一个独立线程- 通过 EventLoopThreadPool 实现负载均衡
- 资源管理
- TcpConnection 生命周期由
shared_ptr
管理 - Channel 在所属EventLoop线程内操作,避免竞态条件
- TcpConnection 生命周期由
- 高效事件分发
- Poller 使用epoll的LT模式,确保数据完整性
- 活跃事件通过
ChannelList
直接传递,减少中间拷贝
模块职责表
模块 | 核心职责 | 依赖关系 |
---|---|---|
TcpServer | 组合管理Acceptor和线程池 | Acceptor, EventLoopPool |
Acceptor | 监听新连接并创建TcpConnection | Socket, Channel |
EventLoopThread | 将EventLoop与线程绑定 | EventLoop, Thread |
Poller | 监听fd事件并返回活跃Channel | Channel |
TcpConnection | 管理TCP连接的数据读写和状态转换 | Channel, Buffer, Socket |
Buffer | 提供应用层读写缓冲区 | 无 |