深蓝黑背景封面。左侧上下两组示意图对比:上组标注「Apache:一连接一进程」,8 个连接圆点各自用线连到 8 个进程方块,注「进程数随连接线性涨,内存先扛不住」;下组标注「Nginx:事件驱动」,同样 8 个连接圆点全部汇入一个青色圆环(事件循环),圆环旁注「一个 worker 轮询上万连接」。最下方一条坐标曲线:横轴连接数,灰色陡升线代表进程模型的内存占用,青色平缓线代表事件模型。右侧大标题「Nginx 凭什么比 Apache 扛得住高并发」,副标题「进程模型与事件驱动的架构分野」。左上标注「面试经典 · Web 服务器」,右上圆角胶囊标注「Day 07 · 11-24」。 图 1:同样八个连接,两种模型的资源消耗完全不同。

先说明属性:这是一篇经典题解析,题目出自本地历史面试资料整理,与任何公司的当前招聘无关。「Nginx 和 Apache 的区别」是 Web 服务器方向的保留题目,背特性清单只能拿基础分,真正的考点是并发模型:为什么「一连接一进程」注定扛不住高并发,事件驱动又赢在哪。

Apache 的经典模型:连接数是进程数的函数。

Apache 通过多处理模块(MPM)来选择并发模型,官方文档列出的三款各有分工:prefork 用多个进程、每进程同一时刻只处理一个连接;worker 用多进程加多线程;event 在 worker 基础上把 keep-alive 连接交给专门线程照看。面试语境里的经典对比对象通常是 prefork——它至今仍是兼容老式非线程安全模块时的选择。

prefork 的问题可以用一笔账说清:每个连接独占一个进程,每个进程带着自己独立的内存栈和地址空间。五千个并发连接就是五千个进程,哪怕其中九成连接正闲在 keep-alive 上发呆,进程也得原地候着。内存随连接数线性上涨先碰到天花板,就算内存撑得住,几千个进程之间的上下文切换开销也会把 CPU 耗在调度而不是干活上。这就是 C10K 问题的本质:瓶颈不在 CPU 算力,而在「每个连接一个执行体」这个映射关系本身。

Nginx 的模型:连接只是事件循环里的一个状态。

按 Nginx 官方文档的描述,它有一个 master 进程和若干 worker 进程:master 只负责读配置和维护 worker,worker 才是真正处理请求的,数量固定、通常按 CPU 核数自动设置。每个 worker 内部是事件驱动模型——借助操作系统提供的 epoll 这类机制,一个 worker 把成千上万个连接全部登记在一个事件循环里,哪个连接上有数据就绪就处理哪个,处理完立刻回到循环。

这个架构改掉了映射关系:连接不再对应执行体,只是循环里的一条状态记录。一万个连接占用的只是一万份连接状态内存,而不是一万个进程;CPU 只在「有事件」时醒来干活,空闲连接的成本趋近于零。于是内存占用近似常数,不再随连接数线性涨,单机连接数的上限被抬高了一个数量级以上。worker 之间各自独立、共享监听端口,也避免了多进程抢同一把锁的大部分麻烦。

为什么这个差异在高并发下被放大。

低并发时两种模型都够用,差距出现在连接数爬坡阶段。进程模型每多一个连接就多一份进程内存加一份切换成本,资源曲线是陡升的直线;事件模型的边际成本只是一条状态记录,曲线近似平的。尤其面对大量慢连接、长连接——客户端网络慢、keep-alive 挂着不走——进程模型会被空闲连接活活拖死,事件模型却几乎无感。这正是 Nginx 在静态资源服务和反向代理场景成为默认选项的原因:这两类场景的流量特征就是海量连接加少量计算。

深蓝黑背景概念图。左栏 Apache 架构:一个 prefork 进程池画出 2×3 共 6 个进程方块,每个方块内标「1 连接」,注「每个进程独占内存栈与上下文,空闲连接也占着进程;切换代价随进程数上升」。右栏 Nginx 架构:master 进程框向下分出 2 个 worker 框,每个 worker 内画一个青色圆环标注「epoll 事件循环」,多个连接小圆点汇入圆环,注「worker 数约等于 CPU 核数,连接只是循环里的一个状态」。底部一张三行对比表:并发模型行「一连接一进程 / 线程」对「单线程事件循环」;万级连接内存行「随连接数线性增长」对「近似常数」;适用场景行「动态模块、兼容老模块」对「高并发静态与反向代理」。最底部总结条:高并发瓶颈不在 CPU 而在连接对应的进程与切换开销,事件循环把它摊平成常数。 图 2:架构对照的核心是「连接对应什么」。

那 Apache 是不是该被淘汰?也不是。

诚实的答案要带边界。Apache 的 event MPM 同样用事件机制处理 keep-alive,官方文档也明确现代系统上的默认 MPM 几乎总是 event——所以「Apache 一定慢」早已不成立,裸性能差距在缩小。Apache 真正的护城河在生态:可按需动态加载的模块体系、目录级的 .htaccess 配置、与老式非线程安全模块的兼容(这正是 prefork 仍被保留的原因)。Nginx 的强项则是高并发下的静态内容、反向代理与负载均衡,配置集中、内存稳、行为可预测。工程上的常见答案是两者搭着用:Nginx 在前扛连接,Apache 在后跑动态模块。

回到面试现场,建议的答题结构:先一句话点题「差异在并发模型」,再分别描述 prefork 的「一连接一进程」和 Nginx 的「master + 少量 worker + epoll 事件循环」,然后算那笔内存与切换的账说明为什么高并发放大差距,最后主动补边界——event MPM 的存在、Apache 的生态优势、两者组合的部署形态。答出「Nginx 是事件驱动」是及格线,讲清「瓶颈是连接与执行体的映射关系」才算摸到考点。你在线上见过哪种模型的瓶颈现场?是内存先爆还是 CPU 先被切换吃掉?欢迎分享。