写在前面
本文是 Nginx 学习笔记系列的最后一篇,深入 Nginx 的底层实现:Master/Worker 进程模型、事件驱动机制、请求处理的 11 个阶段、内存管理,以及 Nginx 为什么这么快。最后对比 Nginx、OpenResty 和 Envoy。
一、Master/Worker 进程模型
1.1 进程架构
1
2
3
4
5
6
7
8
9
10
|
Nginx 启动后的进程结构:
PID PPID COMMAND
1001 1 nginx: master process ← Master 进程(以 root 运行)
1002 1001 nginx: worker process ← Worker 进程(以 www-data 运行)
1003 1001 nginx: worker process
1004 1001 nginx: worker process
1005 1001 nginx: worker process ← 4 个 Worker = 4 核 CPU
1006 1001 nginx: cache manager process ← 缓存管理
1007 1001 nginx: cache loader process ← 缓存加载
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
进程分工:
Master 进程:
- 读取和验证配置文件
- 创建、绑定、监听 Socket
- 管理 Worker 进程(fork、监控、重启)
- 处理信号(reload、reopen、stop)
- 不处理任何业务请求
Worker 进程:
- 接收和处理客户端请求
- 每个 Worker 独立、互不干扰
- 一个 Worker 崩溃不影响其他 Worker
- Worker 之间通过共享内存通信
Cache Manager:
- 管理磁盘缓存
- 检查缓存有效期,删除过期缓存
Cache Loader:
- Nginx 启动时加载磁盘缓存到内存索引
- 加载完成后退出
|
1.2 为什么不用多线程
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
多进程 vs 多线程:
Nginx 选择多进程:
✓ Worker 崩溃不影响其他 Worker(隔离性好)
✓ 无锁设计,无死锁风险
✓ 配合 epoll 事件驱动,单进程处理万级并发
✓ 方便利用多核(每个 Worker 绑定一个 CPU 核心)
多线程的问题:
✗ 线程间共享内存,需要大量锁
✗ 锁竞争导致性能下降
✗ 一个线程崩溃可能导致整个进程崩溃
✗ 调试困难
实际上 Nginx 的 Worker 内部也有辅助线程(file aio),
但核心请求处理是单线程事件驱动。
|
1.3 进程管理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
# 查看 Nginx 进程
ps aux | grep nginx
# Worker 数量配置
# nginx.conf
worker_processes auto; # 自动 = CPU 核心数
# 或手动指定
worker_processes 4;
# 绑定 Worker 到 CPU 核心(减少 CPU 切换开销)
worker_cpu_affinity auto;
# Worker 进程优先级
worker_priority -5; # 值越小优先级越高(-20 到 19)
# Worker 最大打开文件数
worker_rlimit_nofile 65535;
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
Master 处理信号:
TERM, INT → 优雅停止
QUIT → 优雅停止(等请求处理完)
HUP → 重新加载配置
USR1 → 重新打开日志文件
USR2 → 升级可执行文件(热升级)
WINCH → 优雅停止 Worker
reload 时发生了什么:
1. Master 收到 HUP 信号
2. 重新读取配置文件
3. 如果配置合法,fork 新的 Worker 进程
4. 向旧 Worker 发送 QUIT 信号
5. 旧 Worker 处理完当前请求后退出
6. 全程无中断(零停机 reload)
|
二、事件驱动机制(epoll)
2.1 为什么需要事件驱动
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
传统方式(多进程/线程模型):
每个连接分配一个线程
连接空闲时,线程阻塞在 read() 上
1万个连接 = 1万个线程
问题:
✗ 线程占用内存(每个线程约 8MB 栈空间)
✗ 线程切换开销大(上下文切换、CPU 缓存失效)
✗ 难以扩展到 C10K(万级并发)
事件驱动模型:
一个线程管理所有连接
只在连接有事件(可读/可写)时才处理
空闲连接不消耗 CPU
优势:
✓ 少量线程即可处理万级并发
✓ 空闲连接零开销
✓ CPU 只在做有用的工作
|
2.2 I/O 多路复用技术
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
Linux I/O 多路复用的演进:
1. select
- 最多监听 1024 个文件描述符(FD)
- 每次调用需要传入所有 FD,内核遍历全部
- O(n) 复杂度,性能差
2. poll
- 没有连接数限制
- 仍然需要传入所有 FD,内核遍历全部
- O(n) 复杂度
3. epoll(Linux 2.6+)
- 没有连接数限制
- 只返回就绪的 FD,不需要遍历全部
- O(1) 复杂度,性能极好
- Nginx 在 Linux 上的默认选择
4. kqueue(FreeBSD / macOS)
- 类似 epoll,性能优秀
- Nginx 在 macOS 上的默认选择
|
2.3 epoll 工作原理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
epoll 的三个系统调用:
1. epoll_create()
创建 epoll 实例(红黑树 + 就绪链表)
2. epoll_ctl()
注册/修改/删除要监听的 FD
内核会为每个 FD 注册回调
3. epoll_wait()
阻塞等待就绪事件
只返回有事件的 FD(不需要遍历全部)
工作流程:
┌─────────────┐
│ epoll 实例 │
│ ┌───────────┐ │
│ │ 红黑树 │ │ ← 存储所有注册的 FD
│ │ (FD 集合) │ │
│ └───────────┘ │
│ ┌───────────┐ │
│ │ 就绪链表 │ │ ← 有事件的 FD(由内核回调自动添加)
│ │ (活跃 FD) │ │
│ └───────────┘ │
└─────────────┘
网络数据到达 → 网卡中断 → 内核回调 → FD 加入就绪链表
epoll_wait() 返回就绪链表 → Nginx 处理活跃连接
关键优势:
不需要遍历所有连接,只处理有事件的
1万个连接中可能只有 100 个活跃 → 只处理 100 个
|
2.4 Nginx 的事件模型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
# nginx.conf events 块
events {
# 使用 epoll(Linux 默认)
use epoll;
# 每个 Worker 的最大连接数
worker_connections 65535;
# 尽可能多地接收连接
multi_accept on;
# 禁用 accept 互斥锁(Worker 数少时)
accept_mutex off;
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
Nginx Worker 的事件循环(简化版):
while (true) {
events = epoll_wait(epoll_fd, ...); // 等待事件
for (event in events) {
if (event 是新连接) {
accept 新连接;
将新连接注册到 epoll;
}
else if (event 可读) {
读取请求数据;
处理请求;
生成响应;
}
else if (event 可写) {
发送响应数据;
}
}
}
注意:
- 事件循环中不执行阻塞操作
- 文件 I/O 用线程池(file aio)
- DNS 解析有缓存,避免阻塞
|
2.5 连接处理流程
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
一个 HTTP 请求的完整生命周期:
1. 客户端发起 TCP 连接
→ 三次握手完成,epoll 通知有新连接
2. Worker accept 连接
→ 创建 connection 对象
→ 注册到 epoll 监听读事件
3. 客户端发送 HTTP 请求
→ epoll 通知可读事件
→ Worker 读取请求数据
→ 解析 HTTP 请求行、头部、请求体
4. 处理请求
→ 匹配 location
→ 执行 rewrite、access、content 等阶段
→ 生成响应
5. 发送响应
→ 注册可写事件到 epoll
→ epoll 通知可写时发送数据
6. keepalive 复用或关闭连接
→ keepalive:连接保持,等待下一个请求
→ 超时或 Connection: close:关闭连接
整个过程:一个 Worker 线程处理,不创建新线程
|
三、请求处理完整流程(11 个阶段)
3.1 HTTP 请求处理管道
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
|
Nginx 把 HTTP 请求处理分为 11 个阶段,模块按阶段执行:
┌─────────────────────────────────────────┐
│ HTTP Request │
└──────────────┬──────────────────────────┘
▼
┌─────────────────────────────────────────┐
│ 1. POST_READ 读取请求后 │ ngx_http_realip_module
│ (获取真实 IP) │
└──────────────┬──────────────────────────┘
▼
┌─────────────────────────────────────────┐
│ 2. SERVER_REWRITE server 级重写 │ ngx_http_rewrite_module
│ (server 块中的 rewrite) │
└──────────────┬──────────────────────────┘
▼
┌─────────────────────────────────────────┐
│ 3. FIND_CONFIG 查找配置 │ Nginx 核心
│ (匹配 location) │
└──────────────┬──────────────────────────┘
▼
┌─────────────────────────────────────────┐
│ 4. REWRITE location 级重写 │ ngx_http_rewrite_module
│ (location 块中的 rewrite) │
└──────────────┬──────────────────────────┘
▼
┌─────────────────────────────────────────┐
│ 5. POST_REWRITE 重写后处理 │ Nginx 核心
│ (检查是否需要重新匹配 location) │
└──────────────┬──────────────────────────┘
▼
┌─────────────────────────────────────────┐
│ 6. PREACCESS 访问前检查 │ ngx_http_limit_conn_module
│ (限流、连接数限制) │ ngx_http_limit_req_module
└──────────────┬──────────────────────────┘
▼
┌─────────────────────────────────────────┐
│ 7. ACCESS 访问控制 │ ngx_http_access_module
│ (IP 黑白名单、认证) │ ngx_http_auth_basic_module
└──────────────┬──────────────────────────┘
▼
┌─────────────────────────────────────────┐
│ 8. POST_ACCESS 访问后处理 │ Nginx 核心
│ (satisfy 配合 access 阶段) │
└──────────────┬──────────────────────────┘
▼
┌─────────────────────────────────────────┐
│ 9. PRECONTENT 内容前处理 │ ngx_http_try_files_module
│ (try_files) │
└──────────────┬──────────────────────────┘
▼
┌─────────────────────────────────────────┐
│ 10. CONTENT 生成内容 │ ngx_http_proxy_module
│ (代理、静态文件、FastCGI) │ ngx_http_static_module
└──────────────┬──────────────────────────┘
▼
┌─────────────────────────────────────────┐
│ 11. LOG 记录日志 │ ngx_http_log_module
│ (access_log) │
└─────────────────────────────────────────┘
|
3.2 各阶段详解
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
|
# 以下配置展示了各阶段对应的指令
server {
listen 80;
server_name example.com;
# 阶段 1: POST_READ — 获取真实 IP
set_real_ip_from 10.0.0.0/8;
real_ip_header X-Forwarded-For;
real_ip_recursive on;
# 阶段 2: SERVER_REWRITE — server 级重写
rewrite ^/old-api/(.*)$ /api/$1 last;
location /api/ {
# 阶段 4: REWRITE — location 级重写
rewrite ^/api/v1/(.*)$ /api/v2/$1 break;
# 阶段 6: PREACCESS — 限流
limit_req zone=api_limit burst=50 nodelay;
limit_conn conn_limit 20;
# 阶段 7: ACCESS — 访问控制
allow 10.0.0.0/8;
allow 192.168.0.0/16;
deny all;
auth_basic "Restricted";
auth_basic_user_file /etc/nginx/.htpasswd;
# 阶段 9: PRECONTENT — try_files
try_files $uri $uri/ @proxy;
# 阶段 10: CONTENT — 生成内容
proxy_pass http://backend;
}
location @proxy {
proxy_pass http://backend;
}
# 阶段 11: LOG — 日志
access_log /var/log/nginx/example.com.access.log detailed;
}
|
3.3 阶段执行的特点
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
1. 顺序执行
请求按阶段 1→2→3→...→11 顺序经过各阶段
2. 模块注册
每个模块可以注册到特定阶段
同一阶段可以有多个模块,按配置顺序执行
3. 阶段跳转
rewrite 的 last 会跳回阶段 3(FIND_CONFIG)
rewrite 的 break 跳过后续 rewrite,进入下一阶段
return 直接跳到阶段 11(LOG)
4. 内容阶段只有一个执行
proxy_pass、fastcgi_pass、root/alias 只能选一个
先匹配到的执行
5. 日志阶段总是执行
无论前面的阶段返回什么状态码,日志阶段都会执行
|
四、内存管理
4.1 Nginx 的内存管理策略
1
2
3
4
5
6
7
8
9
10
11
12
|
为什么不直接用 malloc/free:
问题:
✗ 频繁的 malloc/free 导致内存碎片
✗ 每次分配有系统调用开销
✗ 容易忘记 free 导致内存泄漏
✗ 多线程环境下需要锁
Nginx 的方案:
✓ 内存池(pool)— 批量分配,一次性释放
✓ 共享内存 — Worker 间通信
✓ slab 分配器 — 管理共享内存中的固定大小对象
|
4.2 内存池(ngx_pool_t)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
内存池结构:
ngx_pool_t
├── d: last, end, next, failed ← 数据区链表
├── max: 最大可分配大小
├── current: 当前工作的小块内存池
├── chain: 缓冲区链表
├── large: 大块内存链表
├── cleanup: 清理回调链表
└── hostnet: 子内存池
┌──────────────────────────────┐
│ pool │ ← 一次 malloc 分配一整块(如 4KB)
│ ┌────────────────────────┐ │
│ │ 已使用 │ 空闲 │ │ ← last 指针标记空闲起始位置
│ └────────────────────────┘ │
│ next pool │ ← 空间不够时,再 malloc 一块链上
│ ┌────────────────────────┐ │
│ │ 空闲 │ │
│ └────────────────────────┘ │
└──────────────────────────────┘
小块分配(≤ max,通常 ≤ 4096 字节):
从当前 pool 的空闲区域直接切出
移动 last 指针即可,极快
不需要 free,整个 pool 一起释放
大块分配(> max):
直接 malloc 分配
加入 large 链表管理
pool 销毁时一起释放
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
内存池的分配和释放:
创建:
pool = ngx_create_pool(4096, log) // 分配 4KB 内存池
小块分配:
p = ngx_palloc(pool, 128) // 从池中切 128B,极快
p = ngx_pnalloc(pool, 256) // 同上,不要求对齐
大块分配:
p = ngx_palloc(pool, 8192) // > max,直接 malloc
使用完毕:
ngx_destroy_pool(pool) // 一次性释放所有内存
重置(复用):
ngx_reset_pool(pool) // 重置 last 指针,不释放内存
优势:
✓ 小块分配只需要移动指针,O(1) 复杂度
✓ 没有内存碎片(同一 pool 中连续分配)
✓ 不需要逐个 free,一键销毁
✓ 每个请求一个 pool,请求结束销毁,不会泄漏
|
4.3 请求与内存池的生命周期
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
一个 HTTP 请求的内存管理:
请求开始
→ 创建请求级内存池(request pool)
请求处理过程中
→ 解析请求行 → palloc 分配
→ 解析请求头 → palloc 分配
→ 读取请求体 → palloc 分配
→ 生成响应 → palloc 分配
→ 所有分配都从池中获取,不需要 free
请求结束
→ 销毁请求级内存池
→ 所有内存一次性释放
→ 不会泄漏任何字节
对比传统方式:
传统:malloc/free 配对,容易漏 free
Nginx:只管分配,不管释放,请求结束统一释放
|
4.4 共享内存
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
Worker 进程之间需要共享数据(如限流的计数器):
Nginx 共享内存:
ngx_shm_zone_t
├── shm_zone: 共享内存区域名称
├── shm_size: 共享内存大小
├── data: 自定义数据
└── init: 初始化回调
使用场景:
limit_req_zone — 限流计数器
limit_conn_zone — 连接数计数器
ssl_session_cache — SSL 会话缓存
proxy_cache — 代理缓存
upstream — 负载均衡状态
配置示例:
limit_req_zone $binary_remote_addr zone=api:10m rate=100r/s;
# ↑ IP ↑ 名称 ↑ 10MB 共享内存
|
4.5 slab 分配器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
共享内存中使用 slab 分配器管理固定大小的对象:
slab 分配器结构:
┌─────────────────────────────────────────────┐
│ 共享内存(如 10MB) │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ Page 0 │ │ Page 1 │ │ Page 2 │ ... │
│ └─────────┘ └─────────┘ └─────────┘ │
│ 每个 Page 4KB │
│ │
│ Page 内部按 slot 大小分割: │
│ slot 大小:8, 16, 32, 64, 128, 256, ... │
│ │
│ 如 slot_size = 64: │
│ ┌───┬───┬───┬───┬───┬───┬───┬───┐ │
│ │64B│64B│64B│64B│64B│64B│64B│64B│ ← 8个slot│
│ └───┴───┴───┴───┴───┴───┴───┴───┘ │
└─────────────────────────────────────────────┘
工作方式:
分配:根据对象大小选择合适的 slot,从 page 中分配一个 slot
释放:标记 slot 为空闲,可以复用
无碎片:固定大小分配,不会产生碎片
应用场景:
限流:每个 IP 的计数器是固定大小(如 64B),用 slab 分配
SSL 缓存:每个会话条目大小固定
负载均衡:upstream 的状态信息
|
五、为什么 Nginx 这么快
5.1 性能数据
1
2
3
4
5
6
7
8
9
10
11
12
13
|
典型性能基准:
静态文件服务:
简单 HTML:50,000+ req/s(单机)
小文件(<10KB):30,000+ req/s
反向代理:
简单代理:20,000+ req/s
带 SSL:10,000+ req/s
对比 Apache(prefork 模式):
同等硬件:Nginx 快 2-10 倍
内存占用:Nginx 低 5-20 倍
|
5.2 快的原因总结
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
|
1. 事件驱动 + epoll
单线程处理万级并发
只处理活跃连接,空闲连接零开销
epoll O(1) 复杂度,不随连接数增长而变慢
2. 零拷贝(sendfile)
静态文件不需要经过用户空间
内核直接从文件描述符拷贝到 Socket
减少两次内存拷贝和上下文切换
传统方式:
磁盘 → 内核缓冲区 → 用户空间 → Socket 缓冲区 → 网卡
sendfile:
猖盘 → 内核缓冲区 → 网卡(直接 DMA 传输)
3. 内存池
批量分配,一次性释放
小块内存分配只需移动指针(O(1))
无内存碎片,无泄漏
4. 无锁设计
Worker 进程独立,互不干扰
不需要互斥锁,无锁竞争
共享内存用原子操作和 slab 分配器
5. 高效的 I/O 处理
sendfile 零拷贝
tcp_nopush 优化数据包发送
tcp_nodelay 禁用 Nagle 算法
文件 AIO(异步 I/O)处理大文件
6. 精心优化的数据结构
红黑树:定时器、location 匹配
单/双链表:缓冲区管理
哈希表:变量查找
基数树:IP 匹配
7. 模块化架构
只编译需要的模块
减少不必要的代码路径
|
5.3 Nginx 优化配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
# 综合性能优化配置
user www-data;
worker_processes auto; # 自动匹配 CPU 核心数
worker_cpu_affinity auto; # 自动绑定 CPU
worker_rlimit_nofile 100000; # 提高文件描述符限制
events {
worker_connections 65535; # 每个Worker最大连接数
use epoll; # 使用 epoll
multi_accept on; # 一次性接收所有新连接
accept_mutex off; # Worker多时关闭互斥锁
}
http {
sendfile on; # 零拷贝
tcp_nopush on; # 优化数据包发送
tcp_nodelay on; # 禁用 Nagle
keepalive_timeout 65;
keepalive_requests 100000; # 一个长连接最多处理多少请求
# 文件描述符缓存
open_file_cache max=10000 inactive=60s;
open_file_cache_valid 90s;
open_file_cache_min_uses 2;
open_file_cache_errors on;
# 连接优化
reset_timedout_connection on; # 超时连接直接 reset
client_body_timeout 12;
send_timeout 10;
# 压缩
gzip on;
gzip_comp_level 4;
gzip_min_length 256;
gzip_types text/plain text/css application/javascript application/json;
# 输出缓冲
output_buffers 1 32k;
postpone_output 1460; # 累积到 MSS 大小再发送
}
|
六、Nginx vs OpenResty vs Envoy
6.1 对比总览
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
特性 Nginx OpenResty Envoy
─────────────────────────────────────────────────────────────
基础 Nginx 核心 Nginx + LuaJIT C++ 自研
语言 C C + Lua C++
配置 静态配置文件 静态 + Lua 动态 xDS 动态 API
可编程性 模块(C) Lua 脚本 WASM / Lua / C++
动态配置 需 reload 运行时动态 实时推送
负载均衡 基础策略丰富 同 Nginx + Lua 高级(区域感知、加权等)
可观测性 基础日志 Lua 扩展 内置(Metrics、Tracing、Logging)
服务发现 手动配置 Lua 脚本 原生支持(DNS、xDS、EDS)
gRPC 支持 基础 基础 原生(双向流)
HTTP/3 实验性 实验性 支持
管理 API 信号(reload) Lua API REST gRPC API
定位 Web/代理服务器 可编程 Web 平台 服务网格代理
诞生年份 2004 2011 2016
|
6.2 OpenResty
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
OpenResty = Nginx + LuaJIT + 大量 Lua 库
核心特点:
在 Nginx 中嵌入 Lua 虚拟机
可以用 Lua 脚本处理请求(不限于静态配置)
几乎可以访问 Nginx 所有内部 API
典型用途:
- API 网关(Kong 基于 OpenResty)
- WAF(ModSecurity 替代)
- 动态路由
- 限流、认证(复杂逻辑)
- 缓存(Redis + Lua)
示例:
location /api {
content_by_lua_block {
local redis = require "resty.redis"
local red = redis:new()
red:set_timeouts(1000, 1000, 1000)
local ok, err = red:connect("127.0.0.1", 6379)
local res, err = red:get("cache:" .. ngx.var.uri)
if res then
ngx.say(res)
return
end
-- 缓存未命中,请求后端
local res = ngx.location.capture("/backend" .. ngx.var.uri)
red:setex("cache:" .. ngx.var.uri, 300, res.body)
ngx.say(res.body)
}
}
适用场景:
需要动态逻辑、API 网关、复杂认证
不想写 C 模块但需要超越静态配置
|
6.3 Envoy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
Envoy = Lyft 开源的 L4/L7 代理,CNCF 项目
核心特点:
面向微服务和云原生设计
动态配置 API(xDS 协议)
内置可观测性(Metrics、Tracing、Logging)
Istio 的数据平面
优势:
- 动态配置:通过 xDS API 实时更新,不需要 reload
- 服务发现:原生支持 DNS、EDS、Consul 等
- 高级负载均衡:区域感知、故障注入、灰度发布
- 可观测性:原生 Prometheus 指标、分布式追踪
- gRPC 一等公民
- WASM 扩展
xDS 协议:
LDS(Listener) — 监听器配置
RDS(Route) — 路由配置
CDS(Cluster) — 集群(upstream)配置
EDS(Endpoint) — 集群成员(服务发现)
SDS(Secret) — 证书配置
适用场景:
微服务架构、服务网格
Kubernetes Ingress / Gateway
需要动态配置和高级可观测性的场景
|
6.4 如何选择
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
选 Nginx:
✓ 传统 Web 服务、反向代理、负载均衡
✓ 静态文件服务
✓ 配置变更不频繁
✓ 团队熟悉 Nginx
✓ 社区最成熟,资料最多
选 OpenResty:
✓ 需要动态逻辑(API 网关、WAF)
✓ 想用脚本扩展 Nginx
✓ 已有 Nginx 基础设施
✓ 使用 Kong API 网关
选 Envoy:
✓ 微服务架构 / 服务网格
✓ 需要动态配置(Kubernetes 环境)
✓ Istio 服务网格
✓ 需要高级可观测性
✓ gRPC 为主的服务
很多公司的架构:
边缘层:Nginx(HTTPS 终端、静态资源、基础代理)
内部层:Envoy(微服务间通信、服务网格)
|
七、小结
本文深入了 Nginx 的底层原理:
- 进程模型:Master 管理 Worker,Worker 独立处理请求,崩溃不扩散
- 事件驱动:epoll 实现高效 I/O 多路复用,单线程处理万级并发
- 请求处理:11 个阶段按顺序执行,模块注册到特定阶段
- 内存管理:内存池批量分配/释放,slab 管理共享内存
- 为什么快:事件驱动 + 零拷贝 + 内存池 + 无锁 + 高效数据结构
- 技术选型:Nginx(传统 Web)、OpenResty(可编程代理)、Envoy(云原生/服务网格)
系列总结
四篇 Nginx 学习笔记到此结束:
- 基础与核心配置 — 安装、配置结构、静态文件、虚拟主机、location
- 反向代理与负载均衡 — 代理配置、负载策略、健康检查、四层/七层
- HTTPS、性能优化与实战 — 证书、压缩、缓存、限流、.NET 部署
- 深入原理 — 进程模型、epoll、11 阶段、内存管理、技术对比
Nginx 的学习曲线:配置入门不难,但深入理解原理需要时间。建议边学边练,在自己的服务器上实际配置和调优。