- Upstream 模块源码实现分析(负载均衡算法、故障转移) - Stream 模块架构分析(TCP/UDP 代理处理流程) - Mail 模块架构概述(IMAP/POP3/SMTP 协议支持) - 事件模块源码架构(定时器、epoll、QUIC) - 变量系统源码实现(索引变量、前缀变量机制) 基于 nginx 1.31.0 源码分析 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
39 KiB
NGINX 核心模块与事件模块详解
1. ngx_core_module (核心模块)
ngx_core_module 是 NGINX 的核心模块,负责配置与系统资源、进程管理相关的全局指令。这些指令只能在主上下文(main context)中使用。
1.1 进程管理指令
worker_processes
设置 worker 进程的数量。
语法:worker_processes number | auto;
默认值:worker_processes 1;
上下文:main
worker_processes auto; # 自动检测 CPU 核心数(推荐)
worker_processes 4; # 固定 4 个进程
worker_processes 8; # 固定 8 个进程
说明:
auto会根据系统可用的 CPU 核心数自动设置- 通常设置为 CPU 核心数或核心数的倍数
- 对于 I/O 密集型任务,可以设置为
CPU核心数 * 2
worker_cpu_affinity
绑定 worker 进程到特定的 CPU 核心,实现 CPU 亲和性。
语法:worker_cpu_affinity cpumask ...;
默认值:无
上下文:main
# 4 核 CPU,每个 worker 绑定一个核心
worker_processes 4;
worker_cpu_affinity 0001 0010 0100 1000;
# 8 核 CPU,每个 worker 绑定一个核心
worker_processes 8;
worker_cpu_affinity 00000001 00000010 00000100 00001000 00010000 00100000 01000000 10000000;
# 使用 auto 模式(自动绑定)
worker_cpu_affinity auto;
位掩码说明:
- 每一位代表一个 CPU 核心,从右到左(LSB 到 MSB)
0001表示绑定到 CPU00010表示绑定到 CPU10100表示绑定到 CPU21000表示绑定到 CPU3
优势:
- 减少 CPU 缓存失效(cache miss)
- 避免进程在不同核心间迁移
- 提升缓存命中率
worker_priority
设置 worker 进程的调度优先级(类似 nice 命令)。
语法:worker_priority number;
默认值:worker_priority 0;
上下文:main
# 高优先级(关键服务)
worker_priority -5;
# 低优先级(后台任务)
worker_priority 10;
说明:
- 负数表示更高优先级(-20 为最高)
- 正数表示更低优先级(19 为最低)
- 需要 root 权限才能设置为负值
worker_rlimit_nofile
设置每个 worker 进程可以打开的最大文件描述符数量。
语法:worker_rlimit_nofile number;
默认值:无(使用系统默认值)
上下文:main
worker_rlimit_nofile 65535;
worker_rlimit_nofile 100000;
计算建议:
worker_rlimit_nofile = worker_connections * 2 + 系统保留
注意:不能超过系统的 fs.file-max 限制。
worker_rlimit_core
设置每个 worker 进程核心转储文件(core dump)的最大大小。
语法:worker_rlimit_core size;
默认值:无
上下文:main
worker_rlimit_core 500M;
worker_rlimit_core 1G;
worker_shutdown_timeout
设置 worker 进程优雅关闭的超时时间。
语法:worker_shutdown_timeout time;
默认值:无
上下文:main
worker_shutdown_timeout 10s;
1.2 日志与调试指令
error_log
配置错误日志的路径和级别。
语法:error_log file [level]; 或 error_log stderr [level];
默认值:error_log logs/error.log error;
上下文:main, http, mail, stream, server, location
# 基本配置
error_log /var/log/nginx/error.log;
error_log /var/log/nginx/error.log warn;
error_log stderr; # 输出到标准错误
error_log stderr debug; # 调试级别
error_log off; # 关闭错误日志
# 不同上下文设置不同级别
error_log /var/log/nginx/error.log error;
http {
error_log /var/log/nginx/http_error.log warn;
server {
error_log /var/log/nginx/server_error.log info;
}
}
日志级别(从低到高):
| 级别 | 说明 |
|---|---|
debug |
调试信息(需要 --with-debug 编译) |
info |
信息性消息 |
notice |
正常但重要的消息 |
warn |
警告消息 |
error |
处理请求时的错误 |
crit |
临界条件 |
alert |
必须立即处理的条件 |
emerg |
系统不可用 |
debug_points
设置调试行为。
语法:debug_points abort | stop;
默认值:无
上下文:main
debug_points abort; # 遇到调试点时产生核心转储
debug_points stop; # 遇到调试点时停止进程
master_process
启用或禁用 master 进程模式。
语法:master_process on | off;
默认值:master_process on;
上下文:main
master_process off; # 单进程模式(仅用于开发调试)
注意:生产环境必须保持 on,禁用 master 进程会导致无法热重载配置。
daemon
设置是否以守护进程模式运行。
语法:daemon on | off;
默认值:daemon on;
上下文:main
daemon off; # 前台运行(用于 Docker 或 systemd)
1.3 进程标识指令
pid
设置存储 NGINX master 进程 PID 的文件路径。
语法:pid file;
默认值:pid logs/nginx.pid;
上下文:main
pid /var/run/nginx.pid;
pid /run/nginx/nginx.pid;
lock_file
设置锁文件的路径。
语法:lock_file file;
默认值:编译时指定
上下文:main
lock_file /var/run/nginx.lock;
1.4 用户权限指令
user
设置 worker 进程运行的用户和组。
语法:user user [group];
默认值:user nobody nobody;
上下文:main
user nginx; # 仅指定用户,组与用户同名
user nginx nginx; # 指定用户和组
user www-data www-data; # Debian/Ubuntu 默认
注意:master 进程以 root 运行,worker 进程以此处指定的用户运行。
group
单独设置 worker 进程的组(从 1.21.5 版本开始)。
语法:group group;
默认值:无
上下文:main
group nginx;
1.5 环境变量指令
env
定义环境变量,允许 NGINX 保留或修改这些变量。
语法:env variable[=value];
默认值:env TZ;
上下文:main
env MYPATH;
env MYPATH=/usr/local/bin;
env PERL5LIB=/path/to/perl/lib;
env OPENSSL_ALLOW_PROXY_CERTS=1;
1.6 配置文件指令
include
包含其他配置文件。
语法:include file | mask;
上下文:任意
# 包含单个文件
include /etc/nginx/mime.types;
# 包含通配符匹配的文件
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
# 包含多个特定文件
include /etc/nginx/conf.d/http.conf;
include /etc/nginx/conf.d/stream.conf;
1.7 动态模块加载指令
load_module
动态加载 NGINX 模块(.so 文件)。
语法:load_module file;
默认值:无
上下文:main
版本:1.9.11+
# 加载动态模块
load_module modules/ngx_http_geoip_module.so;
load_module modules/ngx_stream_module.so;
load_module modules/ngx_http_image_filter_module.so;
worker_processes auto;
events { ... }
编译动态模块:
./configure --with-compat --add-dynamic-module=/path/to/module
make modules
说明:
load_module指令必须在events和http块之前- 模块文件路径可以是绝对路径或相对于前缀目录的路径
- 需要
--with-compat选项确保模块兼容性
1.8 系统相关指令
ssl_engine
指定硬件 SSL 加速设备。
语法:ssl_engine device;
默认值:无
上下文:main
ssl_engine /dev/crypto; # 使用硬件加密设备
说明:
- 用于启用硬件 SSL 加速卡(如 OpenSSL 硬件引擎)
- 需要系统支持相应的加密硬件设备
- 可以显著提升 SSL/TLS 加密解密性能
timer_resolution
设置系统调用 gettimeofday() 的时间戳解析度,减少调用次数。
语法:timer_resolution interval;
默认值:无
上下文:main
timer_resolution 100ms; # 每 100ms 更新一次时间戳
timer_resolution 1s; # 每 1s 更新一次时间戳
作用:
- 减少
gettimeofday()系统调用次数 - 降低 CPU 使用率
- 适用于高并发场景
注意:日志时间戳和限速功能可能不够精确。
working_directory
设置 worker 进程的工作目录,用于写入核心转储文件。
语法:working_directory directory;
默认值:编译时前缀目录
上下文:main
working_directory /var/lib/nginx;
working_directory /var/crash/nginx;
pcre_jit
启用 PCRE JIT(Just-In-Time)编译,加速正则表达式处理。
语法:pcre_jit on | off;
默认值:pcre_jit off;
上下文:main
pcre_jit on;
要求:需要 PCRE 库支持 JIT 编译。
1.8 线程池指令
thread_pool
定义用于多线程读取和发送文件的线程池配置。
语法:thread_pool name threads=number [max_queue=number];
默认值:thread_pool default threads=32 max_queue=65536;
上下文:main
thread_pool default threads=32 max_queue=65536;
thread_pool io_pool threads=64 max_queue=131072;
# 在 location 中使用
location /videos/ {
aio threads=io_pool;
}
2. ngx_events_module (事件模块)
ngx_events_module 是 NGINX 的事件处理核心模块,配置在独立的 events 块中。
2.1 基础配置结构
events {
# 事件模块配置
worker_connections 1024;
use epoll;
multi_accept on;
}
注意:events 块只能出现在主上下文中,且每个配置文件只能有一个。
2.2 连接管理指令
worker_connections
设置每个 worker 进程可以同时处理的最大连接数。
语法:worker_connections number;
默认值:worker_connections 512;
上下文:events
events {
worker_connections 1024; # 开发环境
worker_connections 4096; # 生产环境
worker_connections 10240; # 高并发环境
}
连接数计算:
总并发连接数 = worker_processes * worker_connections
注意:一个连接可能占用 2-3 个文件描述符(客户端连接 + 上游连接)。
2.3 事件处理指令
use
指定使用的事件处理方法。
语法:use method;
默认值:自动检测(根据操作系统选择最优方法)
上下文:events
events {
use epoll; # Linux 2.6+
use kqueue; # FreeBSD/macOS
use /dev/poll; # Solaris
use eventport; # Solaris 10+
use select; # 通用(效率低)
use poll; # 通用(效率低)
}
可用方法:
- Linux:
epoll,select,poll - FreeBSD/macOS:
kqueue,select,poll - Solaris:
/dev/poll,eventport,select,poll
multi_accept
设置 worker 进程是否一次接受多个新连接。
语法:multi_accept on | off;
默认值:multi_accept off;
上下文:events
events {
multi_accept on; # 一次 accept 所有可用连接
multi_accept off; # 一次 accept 一个连接(默认)
}
说明:
on:在epoll/kqueue触发时,尽可能多地接受新连接off:每次只接受一个新连接,然后回到事件循环
适用场景:
- 高并发短连接:建议开启
- 长连接/WebSocket:建议关闭
accept_mutex
启用 worker 进程间的 accept 互斥锁,防止惊群问题。
语法:accept_mutex on | off;
默认值:accept_mutex off;(1.11.3+)
上下文:events
events {
accept_mutex on; # 启用互斥锁(旧版本默认)
accept_mutex off; # 禁用互斥锁(现代 Linux 推荐)
}
说明:
- Linux 2.6.39+ 使用
EPOLLEXCLUSIVE/SO_REUSEPORT,不需要互斥锁 - 旧版本系统建议开启,防止惊群问题
- 现代系统建议关闭,减少锁竞争
accept_mutex_delay
设置 worker 进程尝试重新获取 accept 互斥锁的间隔时间。
语法:accept_mutex_delay time;
默认值:accept_mutex_delay 500ms;
上下文:events
events {
accept_mutex on;
accept_mutex_delay 100ms; # 快速重试
accept_mutex_delay 1s; # 较慢重试
}
2.4 调试指令
debug_connection
启用对特定客户端连接的调试日志。
语法:debug_connection address | CIDR | unix:;
默认值:无
上下文:events
events {
# 调试特定 IP
debug_connection 192.168.1.1;
# 调试网段
debug_connection 192.168.1.0/24;
# 调试本地 socket
debug_connection unix:;
# 调试多个来源
debug_connection 127.0.0.1;
debug_connection 10.0.0.0/8;
}
要求:NGINX 必须使用 --with-debug 编译。
2.5 哈希表优化指令
types_hash_max_size 与 types_hash_bucket_size
控制 MIME 类型哈希表的内存分配。
语法:
types_hash_max_size size;types_hash_bucket_size size;
默认值:
types_hash_max_size 1024;types_hash_bucket_size 64;
上下文:http, server, location
# 大量 MIME 类型时调整
http {
types_hash_max_size 2048;
types_hash_bucket_size 128;
}
说明:
types_hash_max_size:设置 MIME 类型哈希表的最大条目数types_hash_bucket_size:设置每个哈希桶的大小(必须是 2 的幂)- 当配置大量自定义 MIME 类型时需要调整
variables_hash_max_size 与 variables_hash_bucket_size
控制变量哈希表的大小。
语法:
variables_hash_max_size size;variables_hash_bucket_size size;
默认值:
variables_hash_max_size 1024;variables_hash_bucket_size 64;
上下文:http
# 大量自定义变量
http {
variables_hash_max_size 2048;
variables_hash_bucket_size 128;
}
说明:
- 当使用大量
map指令或自定义变量时需要增大 - 如果启动时报 "could not build variables_hash" 错误,需要调整这些值
3. 事件模型深入对比
| 模型 | 平台 | 内核要求 | FD 限制 | 触发模式 | 特性 |
|---|---|---|---|---|---|
| epoll | Linux | 2.6+ | 内存限制 | ET/LT | EPOLLEXCLUSIVE (2.6.39+) |
| kqueue | FreeBSD/macOS | 4.1+ | 内存限制 | ET | EVFILT_TIMER/SIGNAL/PROC |
| eventport | Solaris | 10+ | 内存限制 | — | 多事件源支持 |
| /dev/poll | Solaris | — | 内存限制 | — | 状态持久化 |
| select | POSIX | — | 1024 | — | 不推荐生产使用 |
| poll | POSIX | — | 内存限制 | — | 不推荐生产使用 |
epoll vs kqueue 性能对比:
- epoll:Linux 标准,ET 模式需要循环读取直到 EAGAIN,支持
EPOLLEXCLUSIVE(2.6.39+)解决惊群问题 - kqueue:FreeBSD/macOS 标准,API 更优雅统一,支持定时器/信号/进程事件过滤,天然无惊群问题
触发模式说明:
- ET(Edge Triggered,边缘触发):仅在状态变化时通知,需一次性处理所有数据
- LT(Level Triggered,水平触发):只要就绪就通知,不需要一次性读完
4. 连接处理方法详解
4.1 epoll (Linux)
epoll 是 Linux 2.6 内核引入的高效 I/O 多路复用机制。
工作原理
┌─────────────────────────────────────────┐
│ 用户空间 │
│ ┌──────────┐ ┌───────────────┐ │
│ │ Worker 1 │ │ epoll_wait() │ │
│ │ Worker 2 │ │ │ │
│ │ Worker 3 │ │ 获取就绪事件 │ │
│ └──────────┘ └───────────────┘ │
│ │ │ │
│ ▼ ▼ │
│ ┌───────────────────────────────┐ │
│ │ epoll 实例(内核) │ │
│ │ ┌─────┐ ┌─────┐ ┌─────┐ │ │
│ │ │ FD1 │ │ FD2 │ │ FD3 │ ... │ │
│ │ └─────┘ └─────┘ └─────┘ │ │
│ └───────────────────────────────┘ │
└─────────────────────────────────────────┘
优势
- 无文件描述符数量限制:仅受系统内存限制
- O(1) 复杂度:添加、删除、查询都是常数时间
- 边缘触发(ET)和水平触发(LT):支持两种模式
- 内核态存储:不需要在每次调用时传递整个 fd 集合
触发模式
水平触发(Level Triggered, LT):
// 只要 fd 处于可读/可写状态,epoll_wait 就会返回
epoll_ctl(epfd, EPOLL_CTL_ADD, fd, {EPOLLIN, ...});
边缘触发(Edge Triggered, ET):
// 仅在状态变化时通知,需要一次性读取所有数据
epoll_ctl(epfd, EPOLL_CTL_ADD, fd, {EPOLLIN | EPOLLET, ...});
NGINX 使用边缘触发模式,要求:
- 必须设置 socket 为非阻塞模式
- 必须循环读取直到
EAGAIN
4.2 kqueue (FreeBSD/macOS)
kqueue 是 FreeBSD 引入的高性能事件通知机制,macOS 也支持。
工作原理
┌─────────────────────────────────────────┐
│ 用户空间 │
│ ┌──────────┐ ┌───────────────┐ │
│ │ Worker 1 │ │ kevent() │ │
│ │ Worker 2 │ │ │ │
│ │ Worker 3 │ │ 获取 kevent │ │
│ └──────────┘ └───────────────┘ │
│ │ │ │
│ ▼ ▼ │
│ ┌───────────────────────────────┐ │
│ │ kqueue 实例(内核) │ │
│ │ ┌─────────────────────┐ │ │
│ │ │ 内核事件队列 │ │ │
│ │ │ EVFILT_READ │ │ │
│ │ │ EVFILT_WRITE │ │ │
│ │ │ EVFILT_TIMER │ │ │
│ │ │ EVFILT_SIGNAL │ │ │
│ │ └─────────────────────┘ │ │
│ └───────────────────────────────┘ │
└─────────────────────────────────────────┘
特点
- 高效的事件过滤:支持多种事件类型(socket、文件、进程、信号、定时器)
- 原子操作:添加和获取事件是原子操作
- 无惊群问题:支持
EV_DISPATCH模式
事件类型
| 过滤器 | 说明 |
|---|---|
EVFILT_READ |
文件描述符可读 |
EVFILT_WRITE |
文件描述符可写 |
EVFILT_TIMER |
定时器到期 |
EVFILT_SIGNAL |
信号到达 |
EVFILT_PROC |
进程事件 |
4.3 /dev/poll (Solaris)
Solaris 特有的 I/O 多路复用机制。
使用方法
// 打开 /dev/poll 设备
int dpfd = open("/dev/poll", O_RDWR);
// 写入要监视的文件描述符
write(dpfd, &pollfd_array, sizeof(pollfd_array));
// 获取就绪事件
ioctl(dpfd, DP_POLL, &dvpoll);
特点
- 状态持久化:写入的 fd 会一直监视,直到被显式移除
- 避免重复传递 fd 集合:与
poll()不同,不需要每次传递整个集合 - Solaris 原生支持:在该平台性能优异
4.4 eventport (Solaris)
Solaris 10+ 引入的高性能事件端口机制。
使用方法
// 创建事件端口
int port = port_create();
// 关联文件描述符
port_associate(port, PORT_SOURCE_FD, fd, POLLIN, user_data);
// 获取事件
port_get(port, &event, NULL);
特点
- 自动重新关联:支持
PORT_SOURCE_FD自动重新关联 - 多事件源:支持文件、进程、信号、定时器等多种事件源
- Solaris 推荐:Solaris 10+ 的首选事件机制
4.5 select/poll (通用)
标准的 POSIX I/O 多路复用机制,几乎所有平台都支持。
select
fd_set readfds;
FD_ZERO(&readfds);
FD_SET(fd, &readfds);
select(fd + 1, &readfds, NULL, NULL, &timeout);
限制:
- 文件描述符数量限制(通常是 1024)
- O(n) 线性扫描
- 每次调用需要重新设置 fd_set
poll
struct pollfd fds[] = {{fd, POLLIN, 0}};
poll(fds, nfds, timeout);
改进:
- 无 fd 数量限制
- 但仍然是 O(n) 线性扫描
适用场景
- 低并发环境
- 需要跨平台兼容性
- 嵌入式系统
5. 各平台最佳配置
5.1 Linux (2.6.39+)
# /etc/nginx/nginx.conf
user nginx;
worker_processes auto;
worker_cpu_affinity auto;
worker_rlimit_nofile 65535;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 10240;
use epoll;
multi_accept on;
accept_mutex off; # Linux 2.6.39+ 不需要
}
http {
sendfile on;
tcp_nopush on;
tcp_nodelay on;
# ...
}
5.2 FreeBSD
# /usr/local/etc/nginx/nginx.conf
user www;
worker_processes auto;
worker_rlimit_nofile 65535;
events {
worker_connections 10240;
use kqueue;
multi_accept on;
accept_mutex off;
}
5.3 macOS (开发环境)
# /usr/local/etc/nginx/nginx.conf
user nobody;
worker_processes auto;
worker_rlimit_nofile 10240;
events {
worker_connections 1024;
use kqueue;
multi_accept off; # 开发环境建议关闭
}
5.4 Solaris
# /etc/nginx/nginx.conf
user webservd;
worker_processes auto;
worker_rlimit_nofile 65535;
events {
worker_connections 10240;
use /dev/poll; # 或 eventport
multi_accept on;
}
6. 性能调优建议
6.1 Worker 进程优化
# 匹配 CPU 核心数
worker_processes auto;
worker_cpu_affinity auto;
# 提高文件描述符限制
worker_rlimit_nofile 65535;
# 开发调试时
# master_process off; # 单进程模式(仅开发)
# daemon off; # 前台运行(Docker/systemd)
6.2 事件处理优化
events {
# 高并发场景
worker_connections 10240;
# 使用最优事件机制
use epoll; # Linux
# use kqueue; # FreeBSD/macOS
# 连接接受策略
multi_accept on; # 高并发短连接
multi_accept off; # 长连接/WebSocket
# 互斥锁(现代 Linux 不需要)
accept_mutex off;
# 异步 I/O 请求数(epoll + aio 场景)
worker_aio_requests 64; # 默认 32
}
6.3 新增指令详解
worker_aio_requests
设置使用 epoll 和 aio 时,单个 worker 进程的最大未完成异步 I/O 操作数。
语法:worker_aio_requests number;
默认值:worker_aio_requests 32;
上下文:events
版本:1.1.4+
events {
worker_connections 10240;
use epoll;
multi_accept on;
worker_aio_requests 64; # 提高异步 I/O 并发
}
http {
# 配合 aio 使用
location /videos/ {
aio threads;
sendfile on;
}
}
适用场景:
- 高并发文件传输(视频、图片服务)
- 使用
aio threads异步 I/O - 大文件下载服务
ssl_object_cache_inheritable
控制 SSL 对象(证书、密钥等)在配置重载时是否继承。
语法:ssl_object_cache_inheritable on | off;
默认值:ssl_object_cache_inheritable on;
上下文:main
版本:1.27.4+
# 默认情况下,SSL 对象在 reload 时会继承
ssl_object_cache_inheritable on;
http {
server {
listen 443 ssl;
# 静态证书路径 - 支持继承
ssl_certificate /etc/nginx/ssl/server.crt;
ssl_certificate_key /etc/nginx/ssl/server.key;
}
}
说明:
- 开启时,未修改的 SSL 证书/密钥在 reload 时复用
- 变量形式加载的 SSL 对象无法继承
- 关闭时,每次 reload 都重新加载所有 SSL 对象
6.4 EPOLLEXCLUSIVE 标志详解
版本支持:Linux 2.6.39+,nginx 1.11.3+
EPOLLEXCLUSIVE 是 Linux 内核提供的标志,用于解决多 worker 进程的惊群问题。
传统惊群问题:
新连接到达
│
┌──────────┬─────────┴─────────┬──────────┐
▼ ▼ ▼ ▼
Worker 1 Worker 2 Worker 3 Worker 4
(唤醒) (唤醒) (唤醒) (唤醒)
│ │ │ │
└──────────┴─────────┬─────────┴──────────┘
▼
只有一个 accept 成功
其他唤醒浪费 CPU
使用 EPOLLEXCLUSIVE 后:
新连接到达
│
┌──────────┬─────────┴─────────┬──────────┐
▼ ▼ ▼ ▼
Worker 1 Worker 2 Worker 3 Worker 4
(唤醒) (休眠) (休眠) (休眠)
│
▼
accept 成功,处理连接
nginx 配置建议:
# Linux 2.6.39+ 不需要 accept_mutex
events {
use epoll;
accept_mutex off; # 关闭互斥锁,使用 EPOLLEXCLUSIVE
multi_accept on; # 可以开启,EPOLLEXCLUSIVE 已解决惊群
}
优势:
- 减少不必要的进程唤醒
- 降低 CPU 使用率
- 提高高并发场景性能
注意事项:
- 仅对
epoll有效 - 需要内核 2.6.39+
- nginx 1.11.3+ 自动启用
6.3 内核参数优化
# /etc/sysctl.conf
# 连接队列
net.core.somaxconn = 65535
net.core.netdev_max_backlog = 65535
net.ipv4.tcp_max_syn_backlog = 65535
# TCP 优化
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_fin_timeout = 30
net.ipv4.tcp_keepalive_time = 1200
net.ipv4.tcp_keepalive_intvl = 15
net.ipv4.tcp_keepalive_probes = 5
# 端口范围
net.ipv4.ip_local_port_range = 1024 65535
# 文件描述符
fs.file-max = 2097152
fs.nr_open = 2097152
6.4 文件描述符限制
# /etc/security/limits.conf
nginx soft nofile 65535
nginx hard nofile 65535
# 或使用 systemd
# /etc/systemd/system/nginx.service.d/override.conf
[Service]
LimitNOFILE=65535
7. 连接数计算公式
7.1 基本公式
总并发连接数 = worker_processes × worker_connections
7.2 详细计算
Web 服务器场景
所需 worker_connections = (预期并发连接数 / worker_processes) × 2
# 例如:预期 100,000 并发,8 个 worker
# worker_connections = (100000 / 8) × 2 = 25,000
乘以 2 的原因:
- 客户端连接占用 1 个 fd
- 如果反向代理,上游连接占用 1 个 fd
反向代理场景
所需 worker_connections = (
预期并发连接数 +
(upstream_keepalive × upstream_count)
) / worker_processes × 2
# 例如:预期 50,000 并发,4 个 worker
# 2 个 upstream,每个 keepalive 32
# worker_connections = (50000 + 64) / 4 × 2 ≈ 25,032
7.3 系统限制检查
# 检查系统文件描述符限制
cat /proc/sys/fs/file-max
# 检查进程限制
ulimit -n
# 检查 NGINX 实际使用
ss -s
cat /proc/$(pgrep -o nginx)/limits | grep "Max open files"
7.4 配置示例
# 支持 100,000 并发连接的完整配置
user nginx;
worker_processes 16; # 16 核服务器
worker_cpu_affinity auto;
worker_rlimit_nofile 200000; # 必须 > worker_connections * 2
events {
worker_connections 65535; # 100000/16 ≈ 6250,留足余量
use epoll;
multi_accept on;
accept_mutex off;
}
http {
# 长连接优化
keepalive_timeout 60s;
keepalive_requests 10000;
# 上游连接池
upstream backend {
server 192.168.1.1:8080;
server 192.168.1.2:8080;
keepalive 256;
keepalive_timeout 60s;
keepalive_requests 10000;
}
}
7.5 监控指标
# 启用 stub_status 监控
server {
location /nginx_status {
stub_status;
allow 127.0.0.1;
deny all;
}
}
关键指标解读:
Active connections: 291 # 当前活跃连接数
server accepts handled requests # 总接受/处理/请求数
16630948 16630948 31070465
Reading: 6 Writing: 128 Waiting: 157 # 读/写/等待状态连接数
连接状态说明:
Reading:正在读取请求头Writing:正在处理请求或发送响应Waiting:保持连接(keep-alive),等待新请求
8. 完整配置示例
8.1 高性能 Web 服务器
user nginx;
worker_processes auto;
worker_cpu_affinity auto;
worker_rlimit_nofile 100000;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 65535;
use epoll;
multi_accept on;
accept_mutex off;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for" '
'rt=$request_time uct="$upstream_connect_time" '
'uht="$upstream_header_time" urt="$upstream_response_time"';
access_log /var/log/nginx/access.log main buffer=32k flush=5s;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
keepalive_requests 10000;
open_file_cache max=10000 inactive=20s;
open_file_cache_valid 30s;
open_file_cache_min_uses 2;
gzip on;
gzip_comp_level 6;
gzip_min_length 1000;
gzip_types text/plain text/css application/json application/javascript;
server {
listen 80 backlog=65535;
server_name example.com;
location / {
root /var/www/html;
try_files $uri $uri/ =404;
}
}
}
8.2 高性能反向代理
user nginx;
worker_processes auto;
worker_rlimit_nofile 200000;
error_log /var/log/nginx/error.log warn;
events {
worker_connections 65535;
use epoll;
multi_accept on;
accept_mutex off;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
# 上游服务器
upstream backend {
zone upstream_backend 64k;
server 192.168.1.10:8080 weight=5;
server 192.168.1.11:8080 weight=5;
server 192.168.1.12:8080 backup;
keepalive 256;
keepalive_timeout 60s;
keepalive_requests 10000;
}
# 代理优化
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=main:100m max_size=1g;
server {
listen 80 backlog=65535;
location / {
proxy_pass http://backend;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_buffering on;
proxy_buffer_size 8k;
proxy_buffers 8 32k;
proxy_busy_buffers_size 64k;
proxy_connect_timeout 5s;
proxy_send_timeout 30s;
proxy_read_timeout 30s;
proxy_cache main;
proxy_cache_key $scheme$request_method$host$request_uri;
proxy_cache_valid 200 10m;
proxy_cache_valid 404 1m;
}
}
}
9. 常见问题排查
9.1 "too many open files" 错误
原因:
worker_rlimit_nofile设置过低- 系统
fs.file-max限制
解决:
worker_rlimit_nofile 65535;
# 永久修改
echo "fs.file-max = 2097152" >> /etc/sysctl.conf
sysctl -p
# 检查
ulimit -n
9.2 "worker_connections are not enough" 错误
原因:并发连接数超过 worker_connections 限制
解决:
events {
worker_connections 10240; # 增加连接数
}
9.3 性能下降排查
# 检查 worker 进程是否均匀分布
ps -eo pid,psr,comm | grep nginx
# 检查连接状态
ss -ant | awk '{print $1}' | sort | uniq -c
# 检查系统负载
top -p $(pgrep -d',' nginx)
# 查看文件描述符使用
cat /proc/$(pgrep -o nginx)/limits
9.4 热升级失败
原因:
- PID 文件路径错误
- 权限不足
检查:
pid /var/run/nginx.pid; # 确保路径正确
# 检查 PID 文件
ls -la /var/run/nginx.pid
# 手动指定 PID 路径
nginx -s reload -p /etc/nginx -c nginx.conf
10. 事件模块源码架构分析
基于 nginx 1.31.0 源码(lib/nginx/src/event/)。
10.1 核心数据结构
ngx_event_s - 事件结构
// src/event/ngx_event.h:30-138
struct ngx_event_s {
void *data; // 关联的连接或其他数据
unsigned write:1; // 是否写事件
unsigned accept:1; // 是否接受连接事件
unsigned instance:1; // 实例标记(防止 stale event)
unsigned active:1; // 是否已添加到事件驱动
unsigned disabled:1; // 是否禁用
unsigned ready:1; // 是否就绪
unsigned oneshot:1; // 是否一次性事件
unsigned eof:1; // 是否 EOF
unsigned error:1; // 是否错误
unsigned timedout:1; // 是否超时
unsigned timer_set:1; // 是否设置了定时器
unsigned posted:1; // 是否已投递到队列
ngx_event_handler_pt handler; // 事件处理函数
ngx_rbtree_node_t timer; // 定时器红黑树节点
ngx_queue_t queue; // posted 队列节点
};
ngx_event_actions_t - 事件操作接口
// src/event/ngx_event.h:166-183
typedef struct {
ngx_int_t (*add)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);
ngx_int_t (*del)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);
ngx_int_t (*add_conn)(ngx_connection_t *c);
ngx_int_t (*del_conn)(ngx_connection_t *c, ngx_uint_t flags);
ngx_int_t (*notify)(ngx_event_handler_pt handler);
ngx_int_t (*process_events)(ngx_cycle_t *cycle, ngx_msec_t timer, ngx_uint_t flags);
ngx_int_t (*init)(ngx_cycle_t *cycle, ngx_msec_t timer);
void (*done)(ngx_cycle_t *cycle);
} ngx_event_actions_t;
10.2 Worker 主循环
// src/event/ngx_event.c:195-264
void ngx_process_events_and_timers(ngx_cycle_t *cycle)
{
// 1. 计算定时器超时
timer = ngx_event_find_timer();
// 2. 尝试获取 accept mutex(防止惊群)
if (ngx_trylock_accept_mutex(cycle) == NGX_OK) {
flags |= NGX_POST_EVENTS;
}
// 3. 处理事件(调用 epoll_wait/kevent 等)
ngx_process_events(cycle, timer, flags);
// 4. 处理 posted accept events(优先)
ngx_event_process_posted(cycle, &ngx_posted_accept_events);
// 5. 释放 accept mutex
if (ngx_accept_mutex_held) {
ngx_unlock_accept_mutex();
}
// 6. 处理 posted events
ngx_event_process_posted(cycle, &ngx_posted_events);
// 7. 处理超时定时器
ngx_event_expire_timers();
}
10.3 定时器红黑树实现
// src/event/ngx_event_timer.c
// 全局红黑树
ngx_rbtree_t ngx_event_timer_rbtree;
static ngx_rbtree_node_t ngx_event_timer_sentinel;
// 添加定时器
void ngx_event_add_timer(ngx_event_t *ev, ngx_msec_t timer)
{
key = ngx_current_msec + timer; // 绝对时间
ngx_rbtree_insert(&ngx_event_timer_rbtree, &ev->timer);
}
// 查找最近超时
ngx_msec_int_t ngx_event_find_timer(void)
{
node = ngx_rbtree_min(root, sentinel); // O(log N)
timer = node->key - ngx_current_msec;
return timer > 0 ? timer : 0;
}
10.4 epoll 模块核心
// src/event/modules/ngx_epoll_module.c:784-936
static ngx_int_t ngx_epoll_process_events(...)
{
// 调用 epoll_wait
events = epoll_wait(ep, event_list, nevents, timer);
for (i = 0; i < events; i++) {
c = event_list[i].data.ptr;
// 处理读事件
if ((event_list[i].events & EPOLLIN) && rev->active) {
rev->ready = 1;
if (flags & NGX_POST_EVENTS) {
ngx_post_event(rev, queue);
} else {
rev->handler(rev);
}
}
// 处理写事件
if ((event_list[i].events & EPOLLOUT) && wev->active) {
wev->ready = 1;
// 同上...
}
}
}
10.5 Posted 事件队列
三级 posted 队列优先级:
ngx_queue_t ngx_posted_accept_events; // accept 事件(最高优先级)
ngx_queue_t ngx_posted_next_events; // next 事件(次优先级)
ngx_queue_t ngx_posted_events; // 普通事件
处理顺序:accept → next → regular → timers
10.6 SSL 异步握手
// src/event/ngx_event_openssl.c:2201
ngx_int_t ngx_ssl_handshake(ngx_connection_t *c)
{
n = SSL_do_handshake(c->ssl->connection);
if (n == 1) {
c->ssl->handshaked = 1;
return NGX_OK;
}
sslerr = SSL_get_error(c->ssl->connection, n);
// WANT_READ/WANT_WRITE -> 注册事件,等待回调后递归调用
}
10.7 QUIC 模块架构
src/event/quic/(25 个文件)
├── ngx_event_quic.c # 入口:包分发、连接生命周期
├── ngx_event_quic_transport.c # 包解析/组装、帧解析
├── ngx_event_quic_output.c # 发包、GSO
├── ngx_event_quic_protection.c # AEAD 加解密、HKDF
├── ngx_event_quic_ssl.c # QUIC-TLS 集成
├── ngx_event_quic_streams.c # 流管理、流控
├── ngx_event_quic_ack.c # ACK 生成、范围跟踪
├── ngx_event_quic_migration.c # 连接迁移、路径验证
└── ngx_event_quic_connid.c # 连接 ID 管理
核心连接结构:
// src/event/quic/ngx_event_quic_connection.h:222-309
struct ngx_quic_connection_s {
ngx_quic_send_ctx_t send_ctx[3]; // Initial/Handshake/Application
ngx_quic_congestion_t congestion; // 拥塞控制
ngx_quic_streams_t streams; // 流管理
ngx_msec_t avg_rtt; // RTT 估计
// ...
};
11. 关键源码路径
| 功能 | 文件路径 |
|---|---|
| 事件核心 | src/event/ngx_event.c:195-264 |
| 定时器 | src/event/ngx_event_timer.c |
| epoll 模块 | src/event/modules/ngx_epoll_module.c:784 |
| kqueue 模块 | src/event/modules/ngx_kqueue_module.c:507 |
| SSL 实现 | src/event/ngx_event_openssl.c:2201 |
| SSL 对象缓存 | src/event/ngx_event_openssl_cache.c |
| 连接接受 | src/event/ngx_event_accept.c:21-341 |
| 连接建立 | src/event/ngx_event_connect.c:20-331 |
| QUIC 核心 | src/event/quic/ngx_event_quic.c |
源码分析基于 nginx 1.31.0
源码目录:lib/nginx/src/event/