答案是:需先用lscpu确认物理拓扑,按物理核数(非逻辑核数)设置worker_processes和worker_cpu_affinity,用十六进制掩码精准绑定,避免超线程干扰与numa跨节点访问。

在 128 核服务器上手动配置 worker_cpu_affinity 压榨负载均衡极限,核心不是“填满所有核”,而是精准匹配物理拓扑、规避 NUMA 跨节点访存、隔离干扰、并让每个 worker 真正跑满——否则容易陷入缓存抖动、TLB 刷新和内存带宽争抢,吞吐反而下降。
先确认真实硬件结构,别被 128 这个数字带偏
128 逻辑核 ≠ 128 物理核。常见情况是:
- 双路服务器:2 × AMD EPYC 9654(96 核 / 192 线程)或 2 × Intel Xeon Platinum 8480+(60 核 / 120 线程),启用超线程后显示 128+ 逻辑 CPU —— 但物理核可能只有 64 或 60
- 单路服务器:如 AMD EPYC 9754(128 核 / 256 线程),此时 128 是物理核数,超线程开启后为 256 逻辑核
务必运行以下命令确认:
lscpu | grep -E "(Socket|Core|Thread|CPU\(s\))"重点关注三组值:
- Socket(s):插槽数(通常 1 或 2)
- Core(s) per socket:每槽物理核数
- Thread(s) per core:是否启用超线程(=1 表示关闭,=2 表示开启)
例如输出为:
Socket(s): 2
Core(s) per socket: 32
Thread(s) per core: 2
→ 实际是 2 × 32 = 64 物理核,128 逻辑核。此时不建议设 128 个 worker 并绑定全部逻辑核,优先按 64 个物理核来规划。
手动掩码写法:128 位二进制太长?用十六进制分组替代
Nginx 支持十六进制掩码(比写 128 位二进制现实得多)。规则是:
- 每组掩码对应一个 worker 进程,顺序即 fork 启动顺序
- 每组为 32 位十六进制数(8 字符),覆盖 32 个逻辑 CPU;128 核需 4 组 → 总共 4 × 32 = 128 位
- 最低位(最右字节的 bit 0)始终对应逻辑 CPU 0
假设你确认有 128 个逻辑 CPU(0–127),且希望每个 worker 独占一个物理核(即只用前 64 个逻辑核,跳过超线程对),可这样配:
worker_processes 64;worker_cpu_affinity 00000001 00000002 00000004 00000008
00000010 00000020 00000040 00000080
00000100 ……(共 64 组,每组左移一位);
更实用的做法是:用脚本生成(Python/awk 均可),避免手误。例如 64 组掩码中第 n 组(从 0 开始)为:printf "%08x" $((1 。
严禁重复掩码:如两个 worker 都写 00000001,它们会挤在逻辑 CPU 0 上,彻底失去多核意义。
NUMA 感知绑定:双路服务器必须考虑节点局部性
128 核服务器大概率是双路 NUMA 架构(如 2×64),每个 CPU 插槽配独立内存控制器。若 worker 在 node0,却访问 node1 的 upstream 缓存或 SSL 证书,延迟飙升。
查 NUMA 拓扑:
numactl --hardware典型输出含:
available: 2 nodes (0-1)
node 0 cpus: 0 1 2 ... 63
node 0 size: 256 GB
node 1 cpus: 64 65 ... 127
node 1 size: 256 GB
推荐策略:
- 将 Nginx worker 按物理核均分到两个 NUMA 节点(如各 32 个),并确保上游服务(upstream)、SSL 密钥、proxy_cache 目录也部署在同一节点内存中
- 配置示例(64 worker,每节点 32 个):
worker_processes 64;
worker_cpu_affinity auto 0000000000000000ffffffff 0000000000000000ffffffff ... ;
→ 前 32 个 worker 只允许在 CPU 0–63(node0)运行,后 32 个只允许在 64–127(node1)运行
也可用 numactl --cpunodebind=0 --membind=0 nginx 启动主进程,再配合 worker_cpu_affinity 细化控制。
配套调优缺一不可:绑核只是起点
仅设 worker_cpu_affinity 不会自动提升吞吐。必须同步调整:
- accept_mutex off;:Nginx ≥ 1.11.3 默认关闭,旧版本务必显式关掉,否则所有 worker 争 accept 锁,变成串行
- multi_accept on;:让每个 worker 一次从 epoll 拿多个就绪连接,避免频繁唤醒
-
worker_connections 65535;:配合
worker_rlimit_nofile 65535;和系统级ulimit -n 65535,否则连接数卡在默认 1024 -
网卡软中断绑定:用
ethtool -L eth0 combined 64设置队列数,并用echo 0-63 > /proc/irq/*/smp_affinity_list将收包软中断绑定到同一批 CPU,避免网络包跨核搬运
验证是否生效的关键命令:
ps -eo pid,args,psr | grep 'nginx: worker' | head -20taskset -cp $(pgrep -f "nginx: worker" | head -1)
grep -i "cpus_allowed_list" /proc/$(pgrep -f "nginx: worker" | head -1)/status
理想结果:PSR 列分散在 0–63(或按 NUMA 分布),taskset 显示单个 CPU ID,cpus_allowed_list 是窄范围(如 “0” 或 “0-31”),而非 “0-127”。










