在硬件性能有限的早期,C10K 问题(即单机如何高效处理上万并发连接)成为网络服务架构的一大挑战。为应对这一瓶颈,操作系统和应用程序层面相继引入了多项关键优化------从非阻塞 I/O、I/O 多路复用(如 epoll、kqueue),到事件驱动模型和高效的内存管理机制。
如今,Nginx 已成为高并发场景下的事实标准,广泛应用于全球各大互联网系统。它之所以能轻松支撑数万乃至数十万并发连接,正是深度结合了现代硬件特性与操作系统能力的结果。通过深入理解 Nginx 在硬件与系统层面的设计与优化策略,我们不仅能掌握其高性能的根源,也能更好地将其应用于实际工程实践。
一、简介
Nginx(发音为 "engine-x")是一款高性能、轻量级的开源 Web 服务器和反向代理服务器,同时也支持负载均衡、HTTP 缓存、流媒体传输等功能。它最初由俄罗斯开发者 Igor Sysoev 于 2004 年发布,旨在解决 C10K(即同时处理一万个并发连接)问题。如今,Nginx 已成为全球最流行的 Web 服务器之一,广泛应用于高并发、高可用性的互联网服务架构中。
1.1 特性
- 高并发、低内存消耗:采用事件驱动、异步非阻塞的架构模型(如 epoll/kqueue),能高效处理大量并发连接。
- 反向代理与负载均衡:支持 HTTP、HTTPS、TCP/UDP 等协议的反向代理,并提供多种负载均衡算法。
- 静态文件服务:高效处理静态资源(如 HTML、CSS、JS、图片等)。
- 模块化设计:功能通过模块实现,核心模块 + 第三方模块灵活扩展。
- 热部署与平滑升级:支持不停机更新配置或升级版本。
- 缓存机制:可作为 HTTP 缓存服务器,提升响应速度。
- 安全控制:支持访问控制、限速、SSL/TLS 加密等安全功能。
二、历史背景
- 开发者:Igor Sysoev(俄罗斯工程师)
- 动机:解决 C10K 问题(即单机处理 10,000+ 并发连接)
- 灵感来源:Apache 在高并发下的性能瓶颈(每个连接一个进程/线程)
- 首次公开发布 :2004 年 10 月 4 日 (版本 0.1.0)
初始版本功能:
尽管是早期版本,但已具备 Nginx 核心架构和基础能力:
核心架构
- Master-Worker 多进程模型
- 事件驱动 + 非阻塞 I/O (基于
kqueue/select,Linux 下尚未用 epoll) - 配置文件驱动 (
nginx.conf)
基础 HTTP 功能
- 静态文件服务(HTML、图片等)
- 虚拟主机(
server块,基于 IP 或端口) - 基本的 MIME 类型支持
- 日志记录(access log)
三、核心架构分析
这里关于业务逻辑就不分析了,只分析下他是如何做到高并发的,由于不讲业务逻辑部分,所以会比较简单。
为了达到c10K,进行了如下优化:
- nginx选取了性能最高的C语言开发尽可能的发挥硬件性能
- 为了更好地适应任务调度,采用了Master-Worker 多进程模型
- 避免阻塞和轮询,采用了事件驱动 + 非阻塞 I/O(初版为select/poll ,这里直接跳过为epoll)
- 文件操作,采用零拷贝技术,直接硬盘→内核→socke(网卡)不经过用户态,减少内存使用和cpu计算
3.1 Master-Worker 多进程模型
Master 进程:1 个,由启动用户(如 root)运行
Worker 进程:N 个,默认等于 CPU 核心数(worker_processes auto;)

Master-Worker模型
Master 进程(管理者):
- 读取并验证配置文件(nginx.conf)
- 绑定监听端口(如 80/443),创建监听 socket
- fork 出多个 Worker 子进程
- 接收并处理外部信号:
- SIGHUP:重载配置(reload)
- SIGUSR1:重新打开日志文件(log rotate)
- SIGQUIT / SIGTERM:优雅关闭或强制退出
- 监控 Worker 状态:若 Worker 异常退出,可自动重启(取决于配置)
Worker 进程(工作者):
- 继承 Master 创建的监听 socket
- 每个 Worker 独立运行一个事件循环(event loop)
- 使用 epoll(Linux)、kqueue(BSD)等机制监听所有连接
- 异步、非阻塞地处理 HTTP/TCP 请求
- 完全独立:进程间无共享内存(除只读配置外),避免锁竞争
为什么采用多进程而非多线程:
| 对比项 | 多进程(Nginx) | 多线程(Apache prefork/worker) |
|---|---|---|
| 隔离性 | 进程崩溃不影响其他 Worker | 线程崩溃可能导致整个进程挂掉 |
| 内存开销 | 稍高(但 COW 优化) | 较低,但线程栈仍需内存 |
| 编程复杂度 | 无共享状态,无需锁 | 需处理线程同步、锁竞争 |
| 扩展性 | 天然利用多核 | 受限于 GIL(某些语言)或锁瓶颈 |
| 稳定性 | 极高 | 中等 |
Nginx 选择 多进程 + 单线程事件循环,是为了在性能、稳定性和简洁性之间取得最佳平衡。
3.2 完整流程-epoll(epoll 与 Master-Worker 的协同机制)
Master-Worker模型结合epoll模型,完美的把硬件的性能提升上来,下面是流程

在 Nginx 中,epoll 并非由 Master 进程使用,而是完全运行于每个 Worker 进程内部。这种设计实现了"控制面"与"数据面"的彻底分离:
- Master 负责管理(Control Plane):不碰任何网络 I/O,只做进程调度与配置加载;
- Worker 负责处理(Data Plane):每个 Worker 独立持有 epoll 实例,全权处理连接生命周期
请求处理全流程(结合 epoll):
-
启动阶段
- Master 创建监听 socket(如
0.0.0.0:80),但不进入事件循环。 - fork 出 N 个 Worker 进程,所有 Worker 继承该监听 socket 的文件描述符(FD)。
- Master 创建监听 socket(如
-
连接到来
- 内核接收到 TCP SYN 包,完成三次握手,将新连接放入 accept 队列。
- 所有 Worker 的 epoll 实例均监听该监听 FD,因此可能多个 Worker 被唤醒(惊群问题)。
-
accept 竞争
- 若启用
accept_mutex on(旧版默认),仅一个 Worker 能抢到互斥锁并调用accept(); - 若启用
reuseport(推荐),内核直接将连接分发给某个 Worker,无竞争、无锁、无惊群。
- 若启用
-
注册新连接到 epoll
-
成功 accept 后,Worker 将新连接的 socket FD 加入自己的 epoll 实例:
epoll_ctl(epfd, EPOLL_CTL_ADD, client_fd, &event);此后该连接的所有读写事件均由同一个 Worker 异步处理 ,保证请求处理的局部性(cache-friendly)。
-
-
事件驱动处理
-
Worker 调用
epoll_wait()阻塞等待事件:int n = epoll_wait(epfd, events, max_events, timeout); -
当有数据可读(如 HTTP 请求到达)或可写(如后端响应返回),epoll 立即返回就绪 FD 列表。
-
Worker 非阻塞地读取请求、解析、代理、生成响应、写回客户端,全程无 sleep、无阻塞。
-
-
连接复用或关闭
- 若为 Keep-Alive 连接,FD 继续保留在 epoll 中,等待下一次请求;
- 否则,关闭 FD,从 epoll 移除,释放资源。
单个 Worker 可同时监控 数万个 FD ,而 epoll_wait() 的时间复杂度为 O(1) (仅返回活跃事件),彻底摆脱了 select/poll 的 O(n) 扫描瓶颈。
四、Nginx 如何突破单机并发五大瓶颈
结合:让 CPU 忙于计算,而非等待 I/O;让资源用在刀刃上,而非浪费在切换与拷贝
Nginx 的高性能不仅是软件设计的结果,更依赖底层硬件与操作系统的配合:
具体优化如下:
| 瓶颈类型 | 传统方案问题 | Nginx 优化策略 |
|---|---|---|
| 1. 进程/线程调度 | 每连接一线程 → 内存爆炸、上下文切换开销大 | 单线程事件循环 + 多进程 • 每 Worker 无阻塞处理万级连接 • 无线程栈开销(节省 2MB/线程) |
| 2. 内存消耗 | 每连接占用 MB 级内存 | 轻量连接结构 • 内核 struct sock ≈ 3--8KB • 应用层无额外线程栈 • 32GB 内存可支撑 200 万+ 空闲连接 |
| 3. 文件描述符限制 | 默认 1024,无法扩展 | 系统级调优 • ulimit -n 1000000 • worker_rlimit_nofile • Linux 支持单进程百万 FD |
| 4. I/O 多路复用效率 | select/poll O(n) 扫描,拷贝 FD 集合 |
epoll 事件驱动 • 内核维护红黑树 + 就绪链表 • epoll_wait() 仅返回活跃 FD • 时间复杂度 O(1),无无效遍历 |
| 5. 磁盘 I/O 瓶颈 | 同步 read/write 阻塞线程 | 零拷贝 + 异步 I/O • sendfile():磁盘 → 网卡,绕过用户态 • aio(Linux AIO 或 thread pool)处理大文件 |
https://blog.csdn.net/p731heminyang/article/details/154754364由于这里是1w连接(只是连接不是并发,并发包含业务处理,后续篇章讲解),我们通过之前的单机并发 计算,如果调优内核参数:
1、每个连接申请大概16kb内存
2、一个进程大概最多支持1048576个句柄(已经达到百万了,不是瓶颈)
3、由于采用事件模式epoll,事件复杂度为O(1),cpu不是瓶颈没问题(注意连接数,没有数据处理,当然如果是https的连接还涉及到加解密计算那么cpu也会成为瓶颈,这里主要是设计等待io问题)
那么瓶颈主要是在内存,那么16kbx10000 约为157M 就可以搞定1w连接,所以老式电脑完全没问题,当然要注意 "是连接数,不是qps、tps" 这种哈,这种不再单单算一个连接,而是整体业务消耗了。
五、结束:
Nginx 的成功,本质上是 对操作系统能力的极致利用:
- 它没有发明新理论,而是将 epoll、sendfile、mmap、SO_REUSEPORT 等内核特性组合成一套高效流水线;
- 它摒弃了"为每个连接分配资源"的旧范式,转而采用"事件驱动 + 资源复用"的新模型;
- 它证明了:单机百万并发并非神话,而是合理架构 + 系统调优 + 硬件升级的必然结果。