Reactor模式Nginx和Redis为什么都用这个架构
https://www.bilibili.com/video/BV1AzLS69EYr/?spm_id_from333.1387.search.video_card.clickvd_sourcea681ba71d1e6430511f4ae86200b353eDeeparchWorks 2026-05-21 00:03:53你有没有想过一个看HTTP服务器一个做内存数据库两个完全不搭界的项目居然用的是同一套架构links1台机器扛500000QPSREDIS单线程扛100000OS背后藏着同一个名字reactor模式今天我们就扒一扒这个1995年从一篇论文走出来的模式怎么成了所有高并发服务的灵魂我们先抛一个反直觉的问题如果给你一台16核机器让你扛10万并发连接你会怎么设计99%的答案是每个连接开一个线程听起来天经地义对吧可是eager cecif写mix时给了一个完全不一样的答案来看NX的架构最上面那位是master他手里拿着links c o n f不接客只盯着下面四个worker干活中间这四个worker每个人脑袋上都有一个旋转的小圆环那就是apple事件循环下面那一长串小方块是源源不断涌进来的HTTP连接你注意看右下角那个技术活跃连接数9100万但worker就四个每个worker同时在处理2000多个连接这就是reactor模式的精髓线程数远远小于连接数要理解为什么这妖牛我们先得搞清楚reactor到底是什么1995年华盛顿大学的douglas smith教授发表论文第一次把这个模式形式化为四个角色最上层是reactor事件循环本体下面是同步事件多路分解器在LINUX上就是apple在下面是事件处理器接口最底下是业务自己实现的具体处理器。这个分层是所有reactor实现的统一蓝图CHMT不仅写论文还亲手做了一个C网络框架叫ace ace这是90年代最有影响力的网络中间件后来的NXREDISNEI虽然没有直接抄S的代码但都继承了他的灵魂可以说没有思mate这篇论文整个高并发服务领域要晚10年reactor不是只有一种长相常见有三个变种最简单的单reactor单线程从apple下划线weight到业务回调全在一个线程里这就是REDIS5X第二种是单reactor加worker池IO一个线程业务多个线程第三种是主从reactor一个main reactor专门accept新连接N个sob reactor各跑独立APRIL处理读写net就是这套HINKS走的是另一条路等下你就明白你可能在想美联接一线城不挺好吗编程简单隔离干净但在1万连接量级这条路被三堵墙堵死了内存强先杀你LINUX每个线程默认八兆站1万线程直接吃掉80G内存普通服务器一启动就own退一步用更小的站调度墙又来了1万线程在CPU进来回切光是调度器本身就吃30%Cpu再退一步用select select每次都要把整个SD集合从用户太口到内核态1万个SD就是10万次空轮巡三堵墙叠在一起C10K就成了一道死结2002年LINUX2.5.44引入了apple把select的三个痛点全部解决apple的关键创新是把判断哪个FD就绪从轮询挪到了硬中断回调网卡来包硬中断触发软中断。调用SK下划线data下划线ready唤醒apple把就绪的SD挂到一个ready listAPRIL下划线weight只是从这个list low已经准备好的完全不轮询从O嗯变成O就叙述来看一个画面中间这位是接线员周围16部电话如果是select的工作方式他要一步一步去拿起来听有没有想有没有想1万部电话他要轮1万次EPOL不一样电话灯亮才接灯不亮根本不看所以你看现在16部电话只有二步量他只处理这二部剩下14部对他来说不存在这就是为什么apple能扛百万连接reactor模式加上apple c10K就被一脚踢飞一个线程加一个apple sd100万个连接的内存才100兆零次跨线程切换APRIL下划线wait一次返回所有就绪事件这就是HANS1台16核打死APACHE几百个进程的根本原因道理讲完了下面进入硬核环节打开INX源码看真正的reactor长什么样LINUX的进程模型很清晰一个master负责管理N个worker各自跑独立的apple事件循环master只在启动时BINT端口创建listen sd然后fork出N个worker自己再也不处理任何请求所有真正的活都在worker的事件循环里LINUX的作者ego CEO f是俄罗斯工程师当年在rambler门户被APACHE折磨的受不了自己重写了一个他的设计哲学就一句话一个master加N个worker每个worker是一个单线程事件循环这套架构在20年后的今天。仍然是教科书看master的主循环一个永远在睡的死循环核心就那条six spend没事就睡只在收到SCODECYUP这些信号时被唤醒紫禁城死了他去wait pit回收收到reload他就fork新worker加载新配置master本身不处理任何HTTP请求所有活在worker里这是jinx worker的事件循环本体每一轮做三件事抢accept锁掉apple下划线weight分派事件注意中间那个accept下划线MTX它解决了一个经典的鲸群问题N个worker都监听同一个listen sd新连接到达时如果都被唤醒只有一个能accept成功其他白醒所以HINKS让worker排队抢一把锁同时只有一个worker能accept这里就是reactor的灵魂时刻apple下划线weight拿到就绪FD遍历每一个找到对应的connection结构调它的handle回调CHMT论文里的四个角色在这十几行里全部到齐apple下划线weight是多路分解器Data p t二是事件到handler的映射rap handler是event handle接口整个函数就是reactor本体HINKS看完了下一个主角登场REDIS同样是reactor但走的是完全相反的方向REDIS的作者是意大利人SALVATORSANFILIPPO江湖人称ANTARRASS他写REDIS时只想着一件事简单整个REDIS进程就一个线程跑事件循环所有命令串行执行他在REDIS6release note里写过一句话单线程是REDIS简单可推理性的根基。看REDIS5X的状态流一个主线程一个while循环AAPIPO调APPOL下划线weight便利就绪事件读client数据解析协议执行命令写回响应全部串行整个REDIS没有任何线程同步代码这种朴素到极致的设计恰恰是REDIS性能可预测的根源和wings是不是一模一样AE main就是个死循环每一轮调AE process events里面是APPOL下划线weight加事件分派读事件调fl p l c写事件调o file plo c注意那个before sleep钩子这是REDIS6多线程IO的入口等下你就看到REDIS把apple包了一层抽象叫A这样同样一份AC主循环代码在LINUX上用AE下划线apple c在Mac os上用AE下划线Coo c在SOLARIS上用AE下划线FPC跨平台一份代码搞定这个适配层设计是ANTIREST的工程美学之一为什么redis is敢用单线程看这个画面左边一位大厨从食材到锅之间稳稳的走全程一个人这就是REDIS5X右边四个厨子争抢一口锅三个在阻塞等待效率反而更低REDIS操作的是内存纳秒级所得成本远远大于命令执行本身的成本所以单线程在这个场景下反而是最优解但是好景不长到了2020年万兆网卡时代REDIS单线程read和write这两个CISCCALL开始扛不住网卡的吞吐量远远大于单线程CSCO的吞吐量ANITRESS在REDIS6.0加了一个东西看REDIS6的架构中间还是那个主线程跑reactor。循环命令执行仍然单线程但是下面多了一组IO线程专门干read和write这两个CISCO注意IO线程不碰任何数据结构只做协议解析和系统调用命令执行严格在主线程里串行这样dict hit list这些数据结构仍然零锁这段代码是REDIS6多线程IO的精髓主线程把代写客户端round robin分发给N个IO线程自己也认领一份不当甩手掌柜然后用一个自旋等所有IO线程跑完注意这里全程没有锁因为每个client同时只属于一个线程这种用任务分区代替锁的思路是ANTIRESS单线程哲学的延续现在你看到了两种完全不同的reactor实现LINUX多进程多reactorREDIS单进程单reactor为什么殊途同归又为什么分道扬镳你看这个对比就清楚了kinks是CPU密集型SSL握手跟zip压缩需要利用多核所以选多进程每个worker跑独立reactorREDIS是内存密集型数据在一个地址空间最方便所以选单进程靠cluster分片扩展两者都用reactor但变种完全不同其实没有谁对谁错只有谁更适合场景CPU密集就多进程内存密集就单进程共享状态多就用单进程避免锁共享状态少就用多进程隔离故障这是eager和ANTIRESS各自针对自己的问题做出的最优解来跟一个HTTP请求从网卡到wings的完整旅程0.01ms网卡硬中断0.08msapple下划线weight醒来通知worker1毫秒读到请求1.4ms。响应已经写到socket buffer整个链路大概一毫秒reactor的事件分派只占其中几微秒这就是为什么LINUX能在单机做到500000QPS最后一个反直觉的问题携程会取代reactor吗答案是不会因为携程本身就建立在reactor之上左边是reactor的回调风格每一步都要写一个handle状态基地域右边是携程的同步风格看起来在一个函数里顺序执行但你扒开gold net polar tokyo的apple run time源码底层仍然是reactor携程只是把写回调自动化给程序员一个同步代码异步性能的错觉reactor不会过时它是物理规律讲到这里你应该明白了reactor不是某个具体的函数或库而是一种用事件驱动代替现成驱动的世界观在C10K量级上物理规律只允许这一种解我们来回顾一下今天的核心概念reactor是架构模式apple是引擎两者组合解决了C10K问题LINUX走多进程路线REDIS走单进程路线但都是reactor的变种无论是1995年SHMATE的论文还是2002年EGD的wings还是2009年ANTARREST的REDIS都在告诉我们同一件事事件驱动式高并发的唯一答案回到我们一开始的问题为什么LINUX和REDIS用同样的架构不是抄袭是物理规律当连接数远大于核心数时唯一能抗住的就是事件驱动下一次你打开任何一个高性能网络服务的源码先去找他的事件循环你会发现。99%的高性能服务都是这个套路从1995年SHMATE那篇论文开始reactor已经统治了我们这个时代所有的网络服务如果觉得有收获欢迎一键三连我们下期见。--- 由 vCaptions 生成 ---