swoole全局变量在onrequest中持续累加是常驻进程模型的必然结果:worker进程长期存活,static/global/$globals跨请求保留;正解是业务层用参数传值或新建对象,共享状态用swoole\table或swoole\atomic,禁用闭包引用$this或pdo赋给static变量。

拼多多 PHP 开发岗对 Swoole 的考察,核心不是“会不会用”,而是“有没有在真实高并发场景里踩过坑、调过参、修过内存”。 面试官手上有压测日志、OOM 告警截图、慢请求 trace,你答得越具体(比如 max_request 设成 1200 而不是 “看情况”),越容易过关。
为什么 Swoole 的全局变量在 onRequest 里会持续累加
这不是 bug,是常驻进程模型的必然结果。PHP-FPM 每次请求都重建脚本上下文,而 Swoole 的 Worker 进程启动后就一直活着,static、global、$GLOBALS 全部跨请求保留。
- 现象:定义
static $counter = 0; $counter++后,第 100 次请求时值是 100,不是 1 - 错误解法:试图用
unset()或__destruct清理——协程结束不等于变量销毁,Worker 进程还在跑 - 正解路径:
- 业务层:改用函数参数传值,或每次请求新建对象实例
- 共享状态:需要用
Swoole\Table存计数器,用Swoole\Atomic做原子增减 - 绝对禁止:在闭包里引用
$this或 PDO 实例并赋给 static 变量
task_worker_num 设为 0 时调用 $server->task() 会发生什么
直接抛出致命错误:Fatal error: Uncaught Swoole\Error: task worker is not enabled。不是静默失败,也不是返回 false。
- 常见误操作:本地开发没开 TaskWorker,测试环境也漏配,上线后一调
task()就崩 - 安全写法:投递前先检查
$server->setting['task_worker_num'] > 0 - 更稳妥方案:封装一个
safeTask()方法,内部 fallback 到同步执行(仅限非关键路径) - 注意:
taskwait()和taskWaitMulti()同样依赖 TaskWorker 启用,且超时设置必须显式传,不能靠默认值
协程里用 curl_exec() 为什么会让整个 Worker 卡住
因为 curl_exec() 是原生阻塞函数,未被 Swoole Runtime Hook,它会把当前协程所在的 OS 线程彻底占住,其他协程无法调度。
- 现象:一个请求调了
curl_exec(),后续几十个并发请求全部延迟,stats()显示coroutine_num没涨,worker_cpu_time持续飙升 - 正确替代:
- 用
Swoole\Coroutine\Http\Client(支持 keep-alive、自动重试) - 或启用全量 Hook:
Swoole\Runtime::enableCoroutine(SWOOLE_HOOK_ALL),但需确认所有依赖扩展兼容(如某些老版 Redis 扩展会 crash)
- 用
- 危险操作:在协程里
exec('curl -s ...')或file_get_contents('http://...'),效果等同于curl_exec()
max_request 配置在哪些情况下完全失效
max_request 只在 reactor_num ≥ 1 且 dispatch_mode 不为 5(即非 SWOOLE_DISPATCH_STREAM)时才生效;Base 模式下该配置被忽略。
- 典型失效场景:
- 用
Swoole\Server::set(['mode' => SWOOLE_BASE])启动(调试常用,但线上禁用) - WebSocket Server 配了
'dispatch_mode' => 5(流式分发,用于大文件传输) - Worker 进程因段错误崩溃前,
max_request计数不会触发重启
- 用
- 验证方式:启动后查
$server->stats()['start_time']和'connection_num',若长期运行无重启痕迹,再查ps aux | grep php看 Worker 进程 PID 是否变化 - 拼多多线上惯例:
max_request设为 800~1500,配合reload_async => true减少 reload 期间的请求丢失
真正难的不是记住这些配置项,而是当监控显示 task_queue_len 持续 > 500、worker_idle_time 却接近 0 时,你能立刻判断是 TaskWorker 数量不够,还是某个 task 回调里混进了 sleep(1) 这种硬阻塞——后者会让整个 TaskWorker 队列堵死,比数量不足还致命。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











