# NGINX 核心模块与事件模块详解 ## 1. ngx_core_module (核心模块) ngx_core_module 是 NGINX 的核心模块,负责配置与系统资源、进程管理相关的全局指令。这些指令只能在主上下文(main context)中使用。 ### 1.1 进程管理指令 #### worker_processes 设置 worker 进程的数量。 **语法**:`worker_processes number | auto;` **默认值**:`worker_processes 1;` **上下文**:main ```nginx 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 ```nginx # 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` 表示绑定到 CPU0 - `0010` 表示绑定到 CPU1 - `0100` 表示绑定到 CPU2 - `1000` 表示绑定到 CPU3 **优势**: - 减少 CPU 缓存失效(cache miss) - 避免进程在不同核心间迁移 - 提升缓存命中率 --- #### worker_priority 设置 worker 进程的调度优先级(类似 nice 命令)。 **语法**:`worker_priority number;` **默认值**:`worker_priority 0;` **上下文**:main ```nginx # 高优先级(关键服务) worker_priority -5; # 低优先级(后台任务) worker_priority 10; ``` **说明**: - 负数表示更高优先级(-20 为最高) - 正数表示更低优先级(19 为最低) - 需要 root 权限才能设置为负值 --- #### worker_rlimit_nofile 设置每个 worker 进程可以打开的最大文件描述符数量。 **语法**:`worker_rlimit_nofile number;` **默认值**:无(使用系统默认值) **上下文**:main ```nginx 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 ```nginx worker_rlimit_core 500M; worker_rlimit_core 1G; ``` --- #### worker_shutdown_timeout 设置 worker 进程优雅关闭的超时时间。 **语法**:`worker_shutdown_timeout time;` **默认值**:无 **上下文**:main ```nginx 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 ```nginx # 基本配置 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 ```nginx debug_points abort; # 遇到调试点时产生核心转储 debug_points stop; # 遇到调试点时停止进程 ``` --- #### master_process 启用或禁用 master 进程模式。 **语法**:`master_process on | off;` **默认值**:`master_process on;` **上下文**:main ```nginx master_process off; # 单进程模式(仅用于开发调试) ``` **注意**:生产环境必须保持 `on`,禁用 master 进程会导致无法热重载配置。 --- #### daemon 设置是否以守护进程模式运行。 **语法**:`daemon on | off;` **默认值**:`daemon on;` **上下文**:main ```nginx daemon off; # 前台运行(用于 Docker 或 systemd) ``` --- ### 1.3 进程标识指令 #### pid 设置存储 NGINX master 进程 PID 的文件路径。 **语法**:`pid file;` **默认值**:`pid logs/nginx.pid;` **上下文**:main ```nginx pid /var/run/nginx.pid; pid /run/nginx/nginx.pid; ``` --- #### lock_file 设置锁文件的路径。 **语法**:`lock_file file;` **默认值**:编译时指定 **上下文**:main ```nginx lock_file /var/run/nginx.lock; ``` --- ### 1.4 用户权限指令 #### user 设置 worker 进程运行的用户和组。 **语法**:`user user [group];` **默认值**:`user nobody nobody;` **上下文**:main ```nginx user nginx; # 仅指定用户,组与用户同名 user nginx nginx; # 指定用户和组 user www-data www-data; # Debian/Ubuntu 默认 ``` **注意**:master 进程以 root 运行,worker 进程以此处指定的用户运行。 --- #### group 单独设置 worker 进程的组(从 1.21.5 版本开始)。 **语法**:`group group;` **默认值**:无 **上下文**:main ```nginx group nginx; ``` --- ### 1.5 环境变量指令 #### env 定义环境变量,允许 NGINX 保留或修改这些变量。 **语法**:`env variable[=value];` **默认值**:`env TZ;` **上下文**:main ```nginx 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;` **上下文**:任意 ```nginx # 包含单个文件 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+ ```nginx # 加载动态模块 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 { ... } ``` **编译动态模块**: ```bash ./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 ```nginx ssl_engine /dev/crypto; # 使用硬件加密设备 ``` **说明**: - 用于启用硬件 SSL 加速卡(如 OpenSSL 硬件引擎) - 需要系统支持相应的加密硬件设备 - 可以显著提升 SSL/TLS 加密解密性能 --- #### timer_resolution 设置系统调用 `gettimeofday()` 的时间戳解析度,减少调用次数。 **语法**:`timer_resolution interval;` **默认值**:无 **上下文**:main ```nginx timer_resolution 100ms; # 每 100ms 更新一次时间戳 timer_resolution 1s; # 每 1s 更新一次时间戳 ``` **作用**: - 减少 `gettimeofday()` 系统调用次数 - 降低 CPU 使用率 - 适用于高并发场景 **注意**:日志时间戳和限速功能可能不够精确。 --- #### working_directory 设置 worker 进程的工作目录,用于写入核心转储文件。 **语法**:`working_directory directory;` **默认值**:编译时前缀目录 **上下文**:main ```nginx 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 ```nginx 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 ```nginx 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 基础配置结构 ```nginx events { # 事件模块配置 worker_connections 1024; use epoll; multi_accept on; } ``` **注意**:`events` 块只能出现在主上下文中,且每个配置文件只能有一个。 --- ### 2.2 连接管理指令 #### worker_connections 设置每个 worker 进程可以同时处理的最大连接数。 **语法**:`worker_connections number;` **默认值**:`worker_connections 512;` **上下文**:events ```nginx events { worker_connections 1024; # 开发环境 worker_connections 4096; # 生产环境 worker_connections 10240; # 高并发环境 } ``` **连接数计算**: ``` 总并发连接数 = worker_processes * worker_connections ``` **注意**:一个连接可能占用 2-3 个文件描述符(客户端连接 + 上游连接)。 --- ### 2.3 事件处理指令 #### use 指定使用的事件处理方法。 **语法**:`use method;` **默认值**:自动检测(根据操作系统选择最优方法) **上下文**:events ```nginx 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 ```nginx 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 ```nginx 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 ```nginx events { accept_mutex on; accept_mutex_delay 100ms; # 快速重试 accept_mutex_delay 1s; # 较慢重试 } ``` --- ### 2.4 调试指令 #### debug_connection 启用对特定客户端连接的调试日志。 **语法**:`debug_connection address | CIDR | unix:;` **默认值**:无 **上下文**:events ```nginx 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 ```nginx # 大量 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 ```nginx # 大量自定义变量 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)**: ```c // 只要 fd 处于可读/可写状态,epoll_wait 就会返回 epoll_ctl(epfd, EPOLL_CTL_ADD, fd, {EPOLLIN, ...}); ``` **边缘触发(Edge Triggered, ET)**: ```c // 仅在状态变化时通知,需要一次性读取所有数据 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 多路复用机制。 #### 使用方法 ```c // 打开 /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+ 引入的高性能事件端口机制。 #### 使用方法 ```c // 创建事件端口 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 ```c fd_set readfds; FD_ZERO(&readfds); FD_SET(fd, &readfds); select(fd + 1, &readfds, NULL, NULL, &timeout); ``` **限制**: - 文件描述符数量限制(通常是 1024) - O(n) 线性扫描 - 每次调用需要重新设置 fd_set #### poll ```c struct pollfd fds[] = {{fd, POLLIN, 0}}; poll(fds, nfds, timeout); ``` **改进**: - 无 fd 数量限制 - 但仍然是 O(n) 线性扫描 #### 适用场景 - 低并发环境 - 需要跨平台兼容性 - 嵌入式系统 --- ## 5. 各平台最佳配置 ### 5.1 Linux (2.6.39+) ```nginx # /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 ```nginx # /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 (开发环境) ```nginx # /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 ```nginx # /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 进程优化 ```nginx # 匹配 CPU 核心数 worker_processes auto; worker_cpu_affinity auto; # 提高文件描述符限制 worker_rlimit_nofile 65535; # 开发调试时 # master_process off; # 单进程模式(仅开发) # daemon off; # 前台运行(Docker/systemd) ``` ### 6.2 事件处理优化 ```nginx 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+ ```nginx 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+ ```nginx # 默认情况下,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 配置建议**: ```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 内核参数优化 ```bash # /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 文件描述符限制 ```bash # /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 系统限制检查 ```bash # 检查系统文件描述符限制 cat /proc/sys/fs/file-max # 检查进程限制 ulimit -n # 检查 NGINX 实际使用 ss -s cat /proc/$(pgrep -o nginx)/limits | grep "Max open files" ``` ### 7.4 配置示例 ```nginx # 支持 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 监控指标 ```nginx # 启用 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 服务器 ```nginx 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 高性能反向代理 ```nginx 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` 限制 **解决**: ```nginx worker_rlimit_nofile 65535; ``` ```bash # 永久修改 echo "fs.file-max = 2097152" >> /etc/sysctl.conf sysctl -p # 检查 ulimit -n ``` ### 9.2 "worker_connections are not enough" 错误 **原因**:并发连接数超过 `worker_connections` 限制 **解决**: ```nginx events { worker_connections 10240; # 增加连接数 } ``` ### 9.3 性能下降排查 ```bash # 检查 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 文件路径错误 - 权限不足 **检查**: ```nginx pid /var/run/nginx.pid; # 确保路径正确 ``` ```bash # 检查 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 - 事件结构 ```c // 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 - 事件操作接口 ```c // 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 主循环 ```c // 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 定时器红黑树实现 ```c // 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 模块核心 ```c // 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 队列优先级: ```c 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 异步握手 ```c // 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 管理 ``` 核心连接结构: ```c // 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/`*