reactor
为什么有reactorreactor解决了什么问题因为reactor好。2.1.1讲的传统一请求一线程有下面几个问题每个连接消耗一个线程线程栈通常占用 8MBulimit -s 查看1000 个连接就需要 8GB 内存。线程切换上下文切换需要内核保存/恢复寄存器、页表等CPU 开销随连接数线性增长。阻塞 I/O 浪费 CPU当read没有数据时线程会进入内核睡眠状态直到数据到达。此时线程无法做任何其他工作。即使连接上有少量数据大部分时间线程都在等待CPU 利用率极低。占着坑不干活无法支撑高并发在 C10K一万并发问题中创建一万个线程既不现实也不稳定。因此必须寻找一种单线程处理大量连接的方法这就是 Reactor 诞生的原因。Reactor 模式的核心思想Reactor 模式的核心是使用一个 I/O 多路复用器如 epoll、poll、select同时监听多个文件描述符fd上的事件。当某个 fd 就绪可读、可写等时epoll返回就绪的 fd 集合然后 Reactor 调用预先准备好的相应回调函数处理该事件。整个过程是非阻塞的。其关键组件事件多路复用器epoll_wait/select/kqueue事件处理器针对不同事件读、写、异常的回调函数acceptcbrecv cb send cb事件注册/注销接口epoll_ctl recv callback accpet cb事件循环Reactor不断调用多路复用器获取就绪事件并分发通过这种方式一个线程可以同时管理成千上万个连接且每个连接上的 I/O 操作都不会阻塞线程因为只有当数据已经在内核缓冲区中时即可读回调函数才会被调用当 socket 发送缓冲区有空间时即可写才会触发写回调。结合代码#define CONNECTION_SIZE 1048576 // 支持 100 万连接 struct conn conn_list[CONNECTION_SIZE]; // 每个 fd 对应一个 conn 结构 int epfd; // epoll 文件描述符reactor设置一个conn结构体为每一个连接保存状态。同时加入到数组当中。该数组一fd为索引实现o1时间的访问。开始工作后如果有新的连接进来fd按照数值放入数组中对应的下标中。结构体内部的recv callback回调函数指向accpet cb。分配clientfd。调用event register函数分配好revc cbsend cb调用set event把该fd添加进epoll中。进入while循环后epoll ctl会监听。如果有事件会进入就绪链表。直接把链表里的fd放到epoll event里面。如果是epoll in。那么会调用recv callback回调函数。该函数指向recv cb。接收完数据后会把数据从rbuffer传给wbuffer该fd的事件会被设置为epoll out。recv调用时因为 epoll 已经保证该 fd 可读内核中已经有数据了recv只是把数据从内核拷贝出来而已。所以recv会立即返回读取到的数据不会阻塞。如果是epoll out 事件。调用 send callback 回调函数。该函数指向send cb。发送完数据后fd会被设置为epoll in。而且发送数据时如果一次send没有发完例如 socket 发送缓冲区满传统的阻塞模型会一直等待。在 Reactor 中我们可以只发送一部分然后继续保持EPOLLOUT监听待下次可写时继续发送。通过事件驱动避免阻塞。如果没有reactor的话每个连接一个线程线程数等于连接数内存爆炸。阻塞 I/Orecv会一直等到对方发来数据该线程在此期间什么都做不了。即使连接空闲线程也占用内核资源。发送阻塞如果客户端接收窗口为 0send会阻塞导致该线程卡死影响其他连接虽然其他线程仍可运行但线程资源已被浪费。而在 Reactor 模式下所有这些阻塞点都变成了事件驱动没有数据时recv_cb不会被调用发送缓冲区满时EPOLLOUT不会触发send_cb不会执行线程去处理其他就绪的连接。因此单线程可以高效支撑成千上万的连接。五、Reactor 模式带来的具体好处结合代码优势在代码中如何体现高并发conn_list大小 1048576理论上支持百万连接而epoll_wait一次返回多个就绪事件。低内存每个连接只占用一个struct conn约几 KB因为缓冲区固定大小没有额外线程栈。非阻塞 I/Orecv/send在触发事件时才会在回调中调用epoll 保证可读/可写不会阻塞。动态事件切换set_event(fd, EPOLLOUT, 0)和set_event(fd, EPOLLIN, 0)实现读/写状态的精确转换。业务解耦ws_request不涉及网络 I/O只操作缓冲区和协议状态可独立测试和修改。总结为什么要有 ReactorReactor 是解决 C10K/C1000K 问题的标准模式。它通过事件多路复用和非阻塞 I/O使得一个线程能够管理成千上万个连接避免了传统多线程模型的内存爆炸和上下文切换开销。同时它将业务逻辑与网络 I/O 分离让开发者可以专注于协议实现而底层的高性能并发由 Reactor 框架保证。在代码中mainepoll_wait是 Reactor 的心脏accept_cb、recv_cb、send_cb是负责具体 I/O 的肢体ws_request、handshark、编解码函数则是业务大脑。它们各司其职共同构成了一个简洁但完整的 Reactor 模式 WebSocket 服务器。如果没有 Reactor需要为每个 WebSocket 连接创建独立线程这在小规模测试中或许可行但在生产环境中面对几千个浏览器客户端时系统会迅速崩溃。因此要有Reactor 。什么是reactorreactor是一种事件驱动的网络架构。核心思想是用一个或尽量少的线程配合io多路复用epoll同时监听十万以上的文件描述符事件。当事件连接可读可写时自动调用相应的回调函数来做处理。ractor什么时候使用1.连接数量巨大。如果需要处理成千上万的链接一线程一连接会消耗大量的资源2.很多io压根没干活。建立连接后很多连接都没有数据来回传只有几个io会传数据造成资源的浪费。而reactor可以来回切换有数据就绪的话加进就绪队列。只处理就绪队列。那么reactor是否真的实现百万并发reactor的测试服务器虚拟机ubuntu 2204.1 4核心 内存7.5g客户端1ubuntu 2204.1 2核心 内存4g客户端2ubuntu 2204.1 2核心 内存4g客户端3ubuntu 1604.6 2核心 内存4g得到验证reactor用在哪里Web服务器如Nginx采用多阶段事件处理模型、Node.js基于事件驱动、非阻塞I/O模型。反向代理与负载均衡如Nginx。应用服务器如Tomcat、Jetty等其内部的NIO模块通常都基于Reactor模式实现。即时通讯IM与聊天服务器需同时处理大量用户的长连接Reactor是理想选择。游戏服务器许多高性能游戏服务器引擎如Skynet都基于Reactor模式构建以支持大量玩家的同时在线和实时交互。基于reactor 实现了webserver和websocket使用wrk -c50 -t10 -d10s http://192.168.:2000/来测试wrk命令行工具用于发起 HTTP 请求测量服务器的吞吐量和延迟。-c50设置并发连接数为50即模拟 50 个客户端同时与服务器建立连接。-t10设置线程数为10wrk 会启动 10 个线程来发送请求通常每个线程管理一部分连接。-d10s设置测试时长为10 秒10s表示 10 seconds。http://192.168.:2000/目标服务器的地址使用 HTTP 协议IP 为192.168.137.132端口2000根路径用 10 个线程、50 个并发连接持续 10 秒访问指定的 HTTP 服务运行在 192.168. 的 2000 端口最终输出请求总数、每秒请求数QPS、延迟分布等性能指标。线程统计Thread Stats2.1 延迟Latency指标值说明Avg556.37 µs平均每个请求的响应延迟约0.56 毫秒极低Stdev147.61 µs标准差较小延迟分布比较集中Max8.13 ms最大延迟约 8 毫秒没有出现长尾延迟/- Stdev82.37%约 82% 的请求落在平均值 ±1 个标准差范围内即 408.76 µs ~ 704.04 µs大部分请求响应非常稳定结论服务延迟表现优秀处于微秒级到毫秒级适合对延迟敏感的业务。2.2 单线程每秒请求数Req/Sec指标值说明Avg8.88k平均每个线程每秒处理8880个请求Stdev1.29k各线程之间的吞吐量差异不大Max18.02k某个线程在某个瞬时秒内最高处理了 18020 个请求/- Stdev75.92%约 76% 的“线程-秒”采样落在平均值附近3. 总体统计总请求数887441约 88.7 万次请求测试时长10.10s实际耗时略多于设定的 10 秒通常是 wrk 完成所有请求并收尾的时间读取数据量173.50 MB假设每个响应包体大小约 200 字节左右关键性能指标Requests/sec (QPS)87864.91整体每秒处理约8.79 万个请求非常高。Transfer/sec17.18 MB/s网络吞吐约17 MB/秒。4. 性能评估维度表现评价延迟平均 1ms最大 8ms极低适合实时性要求高的服务吞吐量QPS ≈ 8.8 万非常高常见于高效 HTTP 服务器如 Nginx、Node.js 异步模型、Rust、Go 等稳定性延迟标准差小最大/平均比值 ≈ 14.6波动小性能稳定资源利用10 线程 50 连接处理 88 万请求并发模型合理没有成为瓶颈