一、前言
Nginx 是目前最流行的反向代理和 Web 服务器软件,在高并发场景下表现优异。本文从底层架构入手,带你理解 Nginx 为什么快、怎么工作,以及如何通过实操验证这些原理。
本文是 Nginx 系列学习的第一篇,基于 Rocky Linux 8.10 + Nginx 1.14.1 环境。
二、Nginx 架构总览
Nginx 采用 Master-Worker 多进程架构,这是它高性能的基石:
┌─────────────────────────────────────┐
│ Master 进程 │
│ PID=169242 │
│ 职责:读取配置、fork Worker、 │
│ 信号管理、平滑升级 │
└──────────┬──────────────────────────┘
│ fork
┌─────┼─────┐
▼ ▼ ▼
┌────────┐ ┌────────┐ ┌────────┐
│Worker 1│ │Worker 2│ │Worker N│
│epoll │ │epoll │ │epoll │
│处理请求│ │处理请求│ │处理请求│
└────────┘ └────────┘ └────────┘
核心组件
| 组件 | 数量 | 职责 |
|---|---|---|
| Master | 1 个 | 读取配置、管理 Worker、信号处理 |
| Worker | 通常 = CPU 核数 | 处理客户端请求,每个 Worker 独立运行 |
| Cache Loader | 可选 | 加载缓存元数据 |
| Cache Manager | 可选 | 管理缓存过期 |
三、关键配置项
# /etc/nginx/nginx.conf
worker_processes auto; # 自动等于 CPU 核数
worker_rlimit_nofile 65535; # 每个 Worker 最大文件描述符
events {
use epoll; # 事件驱动模型(Linux 最佳选择)
worker_connections 65535; # 每个 Worker 最大连接数
multi_accept on; # 一次 accept 多个连接
accept_mutex on; # 避免惊群(默认开启)
}
配置项说明
| 配置项 | 作用 | 建议值 |
|---|---|---|
worker_processes auto |
Worker 数量自动等于 CPU 逻辑核数 | auto 或 CPU 核数 |
worker_connections |
每个 Worker 最大并发连接数 | 65535 |
use epoll |
Linux 下最高效的事件驱动模型 | epoll(Linux 3.9+) |
multi_accept on |
一次 accept 多个连接,减少系统调用 | on |
accept_mutex on |
避免惊群,多个 Worker 轮流 accept | on(默认) |
四、实操验证
验证 1:Master-Worker 进程结构
# 查看 Nginx 进程树
ps -ef | grep nginx
# 输出示例:
# root 169242 1 0 Jul28 ? 00:00:00 nginx: master process /usr/sbin/nginx
# nginx 176131 169242 0 Jul28 ? 00:00:00 nginx: worker process
# nginx 176132 169242 0 Jul28 ? 00:00:00 nginx: worker process
可以看到:1 个 Master(PID 169242),2 个 Worker(PID 176131/176132)。
验证 2:Worker 自动恢复
# 杀死一个 Worker
kill -9 176131
# 查看进程树(1 秒后)
ps -ef | grep nginx
# root 169242 1 0 Jul28 ? 00:00:00 nginx: master process
# nginx 176132 169242 0 Jul28 ? 00:00:00 nginx: worker process
# nginx 177893 169242 0 Jul28 ? 00:00:00 nginx: worker process ← 新 fork 的!
结论: Master 会在 1 秒内自动 fork 新的 Worker 替代死掉的 Worker,保证服务不中断。
验证 3:热重载(Hot Reload)原理
# 修改配置后执行 reload
nginx -s reload
热重载过程:
旧 Master(PID=169242)
│
├── 读取新配置
├── fork 新 Worker(PID=178000, 178001)
│
├── 发送 SIGQUIT 给旧 Worker
│ ├── 旧 Worker 停止 accept(从 epoll 中移除 listen fd)
│ ├── 处理中的活跃连接 → 继续处理,等完成后退出
│ ├── 空闲 keepalive 连接 → 立即关闭
│ └── 所有连接处理完毕 → Worker 退出
│
└── 新 Worker 开始处理新请求(这块是fork出新worker后就开始处理新请求,而不是等旧worker退出)
验证方法:
# 创建一个长连接
python3 -c "
import socket, time
s = socket.socket()
s.connect(('127.0.0.1', 8080))
print('连接已建立')
time.sleep(30) # 保持连接
" &
# 在长连接存活期间执行 reload
nginx -s reload
# 观察旧 Worker 状态(进入 FIN-WAIT-2)
ss -tlnp | grep 8080
验证 4:epoll vs select 性能对比
模拟场景: 服务器有 10000 个连接,其中只有 3 个活跃。
# epoll_vs_select.py 核心逻辑
def select_model():
# O(n):每次遍历 10000 个 fd
return TOTAL_CONNECTIONS * 0.27 # 10000 × 0.27 = 2700
def epoll_model():
# O(1):只返回 3 个活跃事件
return ACTIVE_CONNECTIONS * 0.1 # 3 × 0.1 = 0.3
运行结果:
select 每次循环开销: 2700.0 单位
epoll 每次循环开销: 0.3 单位
epoll 比 select 快: 9000 倍
并发连接数对性能的影响:
连接数 select开销 epoll开销 比例
100 27.0 0.3 90x
1,000 270.0 0.3 900x
10,000 2,700.0 0.3 9,000x
100,000 27,000.0 0.3 90,000x
结论: epoll 的性能与总连接数无关,只与活跃连接数有关。select 的性能与总连接数成正比,连接越多越慢。
五、关键知识点总结
1. 为什么 Nginx 比 Apache 快?
| 对比项 | Apache | Nginx |
|---|---|---|
| 架构 | 多进程/多线程(每个连接一个进程/线程) | 事件驱动(单进程处理多连接) |
| 内存 | 每个连接 ~2MB | 每个连接 ~2.5KB |
| 并发 | 几千连接 | 几万连接 |
| 静态文件 | 一般 | 极快(sendfile 零拷贝) |
2. 惊群问题(Thundering Herd)
当多个 Worker 都监听同一个 socket 时,新连接到达会唤醒所有 Worker,但只有一个能 accept 成功,其他 Worker 白白被唤醒。
Nginx 的解决方案: accept_mutex
-
所有 Worker 竞争
accept_mutex -
竞争成功的 Worker 开始 accept 新连接
-
竞争失败的 Worker 回到 epoll_wait 等待
-
配合
multi_accept on,一次 accept 多个连接,减少竞争频率
3. 为什么新连接不是由 Master 分配?
如果 Master 负责分配连接,Master 会成为瓶颈:
-
所有新连接都要经过 Master → 单点瓶颈
-
需要额外的 IPC 通信 → 延迟增加
-
Master 的 epoll 实例会成为热点
Nginx 的设计: 每个 Worker 独立 epoll 实例,直接 accept,互不依赖,完全并行。
六、F5 工程师看 Nginx 架构
F5 概念 Nginx 对应
────────────────────────────────────
TMM(Traffic Management Microkernel) → Worker 进程
SCCP(Symmetric Control Plane) → Master 进程
硬件加速(ASIC/FPGA) → epoll 事件驱动
FastL4 Profile → stream 模块(四层)
Client/Server SSL Profile → http 模块(七层)
核心区别: F5 用硬件做加速,Nginx 用软件(epoll + 零拷贝)达到接近硬件的高性能。
七、生产建议
-
Worker 数量 = CPU 核数 (或
auto),不要超过物理核数,否则上下文切换反而降低性能 -
worker_rlimit_nofile要设大 ,因为max connection = worker_processes × worker_connections -
accept_mutex默认开启,不要关闭,除非你能确认没有惊群问题 -
multi_accept on建议开启,减少 accept 竞争频率 -
热重载是安全的,旧 Worker 会优雅处理完所有活跃连接再退出
下一篇: Nginx 虚拟主机与 server_name 匹配规则详解