swoole常驻内存模型导致global/static变量跨请求累积,因onrequest是回调而非新脚本入口;max_request仅对swoole_process模式下worker进程生效,协程阻塞需启用swoole_hook_all或改用协程客户端。

Swoole 的常考面试题,90% 不在源码实现细节上抠字眼,而是在「常驻内存模型」与「PHP 传统生命周期」的冲突点上设问。你不需要读懂 swoole_server.c,但必须清楚哪些行为在 onRequest 里会出事、哪些配置项在哪个进程生效、哪些函数调用会悄悄破坏协程上下文。
为什么 global/static 变量在 onRequest 里会“越用越大”
因为 onRequest 是回调,不是新脚本执行入口。PHP 不会重置 global $counter 或 static $i = 0 —— 它们绑定在 Worker 进程的 PHP 生命周期里,随请求不断累加。
- 现象:
global $req_count; $req_count++;第 100 次请求时值为 100,不是 1 - 本质:Swoole 没有每次请求都
include 'index.php',而是复用已加载的符号表 - 别用
unset($GLOBALS['xxx'])试图清理——它只清当前作用域引用,不销毁底层 zval - 正确做法:状态存
Swoole\Table(跨 Worker 原子操作)或Redis(跨机器),局部逻辑用纯函数+参数传递
max_request 到底对谁生效、什么时候失效
max_request 是 Worker 进程的“寿命计数器”,但它有明确的适用边界,不是万能内存保险丝。
Swoole 6.1.1 是一个专为 PHP 设计的高性能事件驱动并发网络引擎。作为稳定版,它修复了编译时对 zlib 依赖的缺失及 curl 模块的内存安全风险。该版本支持协程、多线程与多进程架构,内置 TCP/HTTP/WebSocket 服务器,能够显著提升 PHP 在微服务、实时通信等场景下的执行效率与并发能力。
- 仅对
SWOOLE_PROCESS模式下的同步阻塞型 Server 生效(如 HTTP、TCP) - 在
SWOOLE_BASE模式下完全无效——该模式下每个连接独占一个进程,max_request无意义 - TaskWorker 进程走的是
max_task_request配置,和max_request无关 - 协程内未释放的资源(如未
close()的Swoole\Coroutine\Http\Client)会在进程退出前持续占用内存,max_request不会提前触发 GC
协程里调用 file_get_contents 为什么卡死整个 Worker
默认情况下,file_get_contents 是同步阻塞的,它不经过 Swoole 的 IO Hook 层,直接调用 libc 的 read(),协程调度器无法感知、无法挂起。
- 错误写法:
file_get_contents('http://api.example.com')→ 卡住当前协程,其他协程也等不到 CPU - 正确路径一:启用全量 Hook:
Swoole\Runtime::enableCoroutine(SWOOLE_HOOK_ALL),再调用原生函数(注意兼容性风险) - 正确路径二:改用协程原生客户端:
(new Swoole\Coroutine\Http\Client)->get() - 关键区别:
SWOOLE_HOOK_FILE只影响本地文件读写,不影响网络请求;SWOOLE_HOOK_CURL已废弃,不要依赖
flock() 写日志为什么还是丢数据
很多人以为加了 flock() 就万事大吉,但 POSIX 文件锁在多进程场景下极易因使用姿势错误而失效。
- 必须满足三个条件才真正有效:文件以
'a'或'w'模式打开 +flock($fp, LOCK_EX)在fwrite()前调用 + 所有 Worker 进程使用同一文件句柄路径(不能各自fopen同名文件) - 常见坑:
file_put_contents($log, $msg, FILE_APPEND)底层仍是fopen + fwrite,且没调flock,等于没锁 - 更稳方案:Worker 进程通过
$server->task()把日志投递给 TaskWorker,由单个进程串行落盘;或用 Unix Socket 转发给独立日志进程 - 注意:
flock()是 advisory lock(建议性锁),若某进程故意不调用它,照样能写——所以必须全量统一约束
$_SERVER 在 WebSocket 握手后不再更新、register_shutdown_function 在协程中可能不触发、opcache 在 CLI 模式下若开启会固化旧字节码。这些不是 bug,是模型切换后的必然结果。










