swoole不能直接替换php-fpm,因其运行模型本质不同:php-fpm是请求级短生命周期(加载→执行→销毁),swoole是进程常驻型,复用类、配置与连接池,全局对象长期存活易致内存泄漏与数据污染。

直接说结论:Swoole 基础面试题不考“怎么装”,而是盯住三个锚点——进程模型是否真理解、协程是否真用过、内存泄漏是否踩过坑。
为什么 Swoole 不能直接替换 PHP-FPM?
不是语法不兼容,是运行模型彻底不同。PHP-FPM 是“请求来→启动进程→加载框架→执行→销毁全部资源”,而 Swoole 是“进程常驻→复用已加载的类、配置、连接池”。你写一个 new Redis() 放在全局,FPM 下每次请求都新建;Swoole 下它就一直活着,直到 worker 进程重启。
常见错误现象:
- 在
onReceive外部初始化数据库连接,却没做连接健康检测,跑一段时间后报Connection refused - 把
$_SESSION或global $cache当成请求级变量用,结果多个请求共享同一份数据 - 所有长连接(MySQL、Redis、HTTP 客户端)必须封装成单例 + 心跳/重连逻辑
- 避免在全局作用域 new 任何带状态的对象,改用协程上下文或
Co::getUid()隔离 - 用
max_request = 3000强制 worker 重启,是兜底手段,不是设计思路 - 想缓存一次 HTTP 请求结果供本次请求内多次读取 → 应该用
Co::getContext()或闭包捕获的局部变量 - 想跨协程传参(比如中间件链传递用户 ID)→ 用
Co::pack()/Co::unpack()或 Channel - 写了个
function genTraceId() { static $id = null; return $id ?: $id = uniqid(); },结果所有协程拿到同一个 ID - 在协程里调用 Laravel 的
app()容器,却没意识到容器本身不是协程安全的 - 频繁崩溃会触发密集 fork,消耗 CPU 和进程号资源
- 重启期间该 worker 正在处理的请求直接丢弃(除非你用了
reload_async = true) - 在
onWorkerError回调里记录完整堆栈,别只打"worker died" - 用
set_error_handler()+register_shutdown_function()捕获未捕获异常,避免直接 crash - 测试阶段打开
log_level = 5,看日志里有没有WORKER EXIT或SEGMENTATION FAULT
实操建议:
协程里为什么不能用 static 变量?
因为 static 变量绑定的是函数作用域,不是协程作用域。同一个函数被多个协程并发调用时,它们共用一份 static 存储 —— 协程 A 写入,协程 B 下一秒就读到,且无法预测顺序。
使用场景:
Swoole 6.1.1 是一个专为 PHP 设计的高性能事件驱动并发网络引擎。作为稳定版,它修复了编译时对 zlib 依赖的缺失及 curl 模块的内存安全风险。该版本支持协程、多线程与多进程架构,内置 TCP/HTTP/WebSocket 服务器,能够显著提升 PHP 在微服务、实时通信等场景下的执行效率与并发能力。
容易踩的坑:
worker 进程崩溃后,master 怎么知道该不该重启?
靠信号和退出码。Swoole master 进程监听 worker 的 SIGCHLD 信号,一旦收到,就用 waitpid() 拿退出状态。如果退出码非 0(比如段错误、PHP Fatal Error),master 就拉起新 worker;如果是正常退出(exit(0)),则视为业务主动结束,不重启。
性能影响:
实操建议:
真正难的不是记住 master/worker/taskWorker 的名字,而是当你看到线上 worker 进程数缓慢上涨 或 内存 RSS 持续增长但不释放,能立刻反应过来:是不是某个协程里忘了 close Redis 连接?是不是有个 Channel 没被 consume 就丢了引用?这些细节,文档不写,但线上会咬人。










