Swoole中open_cpu_affinity亲和性的区别
Swoole中无open_cpu_affinity配置,实际生效的是process_cpu_affinity。开启后,每个Worker进程被绑定到独立CPU核心,但仅对Worker有效,且worker_num超过逻辑CPU数时失效。手动绑定更灵活,但在容器环境下需谨慎。可用ps命令检查绑定状态,避免配置未生效。
如果在 Swoole 文档里搜 open_cpu_affinity,大概率会空手而归——它压根不存在。这其实是一个流传已久的“幽灵配置”,大概率是早期社区笔记混淆了 Nginx 的 worker_cpu_affinity,或者是某篇文档的手误。真正管用的开关只有一个:process_cpu_affinity。说一个关键结论:Swoole 的 CPU 亲和性配置,名字千万别写错,否则费了半天劲配置,实际上根本就没生效。
为什么搜不到 open_cpu_affinity?
你猜怎么着?这个配置键在 Swoole 的官方源码、PHP 扩展注册参数、php --ri swoole 输出、以及 Swoole 5.x 的 Server->set() 支持键名里,全都找不到。它更像是社区笔记里的一次笔误——可能是把 process_cpu_affinity 手误写成了 open_...。实际被 Swoole 内核识别并生效的配置只有两个:
process_cpu_affinity:布尔值,控制是否开启 Worker 进程的自动 CPU 核心轮转绑定swoole_set_cpu_affinity():运行时函数,用于在WorkerStart回调中手动指定绑定哪些逻辑 CPU
process_cpu_affinity = true 时到底做了什么?
设为 true 后,Swoole 主进程在 fork 出每个 Worker 后,会立刻调用 sched_setaffinity() 系统调用,把这个 Worker 钉在一个独占的逻辑 CPU 上。分配规则很简单:Worker ID 0 绑 CPU 0,Worker ID 1 绑 CPU 1,以此类推,超了就循环取模。这个过程是不可逆的,而且系统也不会检查那个 CPU 到底存不存在、是不是已经超载了。
有几个点需要警惕:
- 这个绑定只对 Worker 进程有效。Task 进程默认不参与,除非你在
onTask回调里手动调用swoole_set_cpu_affinity()。 - 如果
worker_num大于逻辑 CPU 总数(比如 8 个 Worker 跑在一台 4 核 8 线程的机器上),就会出现多个 Worker 抢同一个逻辑 CPU 的情况,亲和性设置就近乎失效了。 - 它虽然能保证你的 Worker 不会被系统调度器“踢”到别的核上,但不能阻止其他进程挤进来跑。
手动绑定是更灵活的路子,但深坑也不少
想要更精细的控制,可以在 WorkerStart 回调里用 swoole_set_cpu_affinity([0, 2]),这样就能让某个 Worker 只在 CPU 0 和 CPU 2 上运行(软亲和)。对比之下,process_cpu_affinity => true 是硬邦邦的单核绑定(硬亲和),灵活性差一些。
但手动绑定的坑也很实在:
- 传入空数组或非法编号(比如
[99])不会触发报错,亲和性就默默地没生效。 - 在 Manager 进程或主进程中调用
swoole_set_cpu_affinity()会静默失败(返回false),因为这个函数只对子进程有效。 - 在 Docker 这种容器环境里,
/proc/cpuinfo显示的 CPU 逻辑核数可能和宿主机不一致,swoole_cpu_num()的返回值也可能不准。稳妥的做法是结合 cgroups 的资源限制,再显式指定 CPU 编号。
到底绑没绑上?用命令说话
别光看配置项的状态,用系统命令验证才是王道:
ps -eo pid,args,psr | grep "php.*server"
输出里的 psr 列就是进程当前正在运行的逻辑 CPU 编号。如果看到多个 Worker 都挤在 psr=0,那就说明 process_cpu_affinity 没生效,或者 worker_num 设得太大了。
其实说到底,性能的瓶颈往往不只在“有没有绑定”这件事上。真正拖后腿的,经常是把高 I/O 的协程调用、CPU 密集计算、日志刷盘这些行为全都混在同一个 Worker 里——哪怕绑死了 CPU,缓存污染和 TLB miss 照样能让你性能跳水。


































