要让nginx worker稳定运行在指定物理cpu核上,需基于硬件拓扑独占物理核心、禁用超线程干扰、正确配置worker_processes与worker_cpu_affinity掩码,并关闭accept_mutex、开启multi_accept、绑定网卡软中断,最后通过ps、taskset、mpstat验证绑定效果。

要让 Nginx worker 进程稳定运行在指定物理 CPU 核上、真正避开上下文切换和缓存抖动,关键不是简单写几个“1”,而是从硬件拓扑出发,让每个 worker 独占一个物理核心,并阻断跨核干扰路径。
先确认真实物理核心数,别被超线程带偏
超线程(HT)会让一个物理核暴露为两个逻辑 CPU,但 L1/L2 缓存是共享或半共享的。静态分发、反代等高吞吐场景下,两个 worker 绑在同一个物理核的两个逻辑 ID 上,反而会争抢执行单元和缓存带宽。
- 运行 lscpu | grep -E "Core\(s\) per socket|Socket\(s\)",算出物理核心总数(例如:2 sockets × 8 cores = 16 物理核)
- 再用 nproc 或 lscpu | grep "CPU(s):" 查逻辑核总数(如显示 32,说明 HT 已开)
- 若你有 16 物理核,就设 worker_processes 16;不要设 32,除非压测证明小包低延迟确实受益于全逻辑核
手动写掩码:位序严格对齐,最低位永远是 core 0
每组掩码是一个二进制数,长度必须等于系统逻辑 CPU 总数,但只在物理核对应位置置 1。例如 16 物理核 + HT = 32 逻辑核,你想只用前 16 个逻辑 ID(即每个物理核的第一个线程),掩码就该是 16 个 “1” 左对齐、补 16 个 “0”:
- 16 核(无 HT):worker_processes 16;worker_cpu_affinity 0000000000000001 0000000000000010 ……(共 16 组,每组 16 位)
- 16 物理核 + HT(32 逻辑核),只用物理核 0–15 的第一个线程(逻辑 ID 0,2,4…30):可写为 worker_cpu_affinity 01010101010101010101010101010101(32 位,偶数位为 1),再拆成 16 组,每组对应一个 worker
- 更稳妥的做法:用十六进制简化,如 8 核写成 01 02 04 08 10 20 40 80,不易数错位
绑定只是起点,必须关锁+多收+限域
只写 worker_cpu_affinity,不调其他参数,等于给跑车装了定速巡航却踩着刹车——worker 被绑住了,但还在排队等 accept 锁、一次只收一个连接、软中断在隔壁核收包再搬运过来。
- accept_mutex off;:关闭连接接入串行锁(Nginx ≥ 1.11.3 默认已关,旧版本务必显式关)
- multi_accept on;:让每个 worker 一次从内核事件队列里取多个就绪连接,减少 epoll 唤醒次数
- 把网卡软中断也绑到相同物理核:查 cat /proc/interrupts | grep eth 找对应 IRQ 号,再执行 echo 1 > /proc/irq/XX/smp_affinity_list(XX 换成实际号,1 表示 core 0)
验证是否真绑上了,别信配置文件
改完 reload 后,必须看进程实际运行状态:
- ps -eo pid,args,psr | grep 'nginx: worker':第三列 PSR 应是互不重复的整数(如 0,1,2,3…),且全部落在你期望的物理核 ID 范围内
-
taskset -cp
:输出应明确显示“current affinity list: 0”这类单核列表 - mpstat -P ALL 1:压测时观察各核 %usr 是否均衡上升,避免某核 100% 而其余空闲——那是绑定失败或配套没调好










