Swoole通过常驻进程和协程改写PHP生命周期,static变量跨请求保留,需手动清理或用协程上下文隔离;数据库须用协程连接池;禁用同步sleep;max_request不宜设0。

不是“Swoole比FPM快”,而是它彻底改写了PHP的生命周期——FPM每次请求都从零加载,Swoole只在启动时加载一次,之后所有请求共享同一份内存空间。
为什么静态变量在Swoole里会跨请求保留?
因为Swoole Worker进程常驻内存,static 变量、类静态属性、全局数组(如 $GLOBALS)不会随请求结束而销毁。这和FPM中每次请求都是全新进程、所有变量重置完全不同。
- FPM下:
static $counter = 0; $counter++;每次请求都输出1 - Swoole下:同一Worker内,该变量持续递增,可能变成
1→2→3… 直到进程重启 - 容易踩的坑:用
static缓存用户数据、未清空的连接句柄、重复注册的定时器,都会导致数据污染或内存泄漏 - 正确做法:在
onRequest或onClose回调里手动重置关键状态;或改用协程上下文Co::getContext()隔离请求级数据
数据库连接为什么不能直接 new PDO()?
FPM模式下每次请求新建连接、用完即关,没问题;但Swoole里如果每次请求都 new PDO(),连接数会指数级增长,很快打满MySQL最大连接数(max_connections),报错 SQLSTATE[HY000] [1040] Too many connections。
Swoole 6.1.1 是一个专为 PHP 设计的高性能事件驱动并发网络引擎。作为稳定版,它修复了编译时对 zlib 依赖的缺失及 curl 模块的内存安全风险。该版本支持协程、多线程与多进程架构,内置 TCP/HTTP/WebSocket 服务器,能够显著提升 PHP 在微服务、实时通信等场景下的执行效率与并发能力。
- 根本原因:PDO实例是长生命周期对象,不会自动释放,且不支持复用
- 必须用连接池:如
Swoole\Coroutine\MySQL或第三方库(如hyperf/database的协程连接池) - 注意配置项:
max_idle_time控制空闲连接回收时间,min_connections避免冷启动抖动 - 别碰
mysqlnd同步驱动——它在协程里会阻塞整个Worker
协程里 sleep() 和 usleep() 为什么不能用?
它们是同步系统调用,会挂起整个协程调度器,导致当前Worker无法处理其他请求。现象是:一个请求卡住,整条连接后续请求全被堵死,QPS断崖下跌。
- 替代方案:用
Co::sleep(1)(秒级)或Co::usleep(1000)(微秒级),它们是协程安全的非阻塞等待 - 常见误用场景:循环里写
sleep(1)做轮询、延迟重试、防刷限流 - 更优解:用
Swoole\Timer::tick()或Swoole\Timer::after(),把延迟逻辑交给事件循环管理 - 注意:
time_nanosleep()、pcntl_alarm()等系统级函数同样不兼容协程
max_request 设为 0 真的安全吗?
设为 0 表示不限制单个Worker处理请求数,看似“省事”,实则埋下严重隐患:内存缓慢增长、连接泄漏、静态变量无限累积,最终触发OOM Killer杀掉Worker进程,服务出现毛刺甚至雪崩。
- 推荐值:生产环境设为
1000 ~ 5000,视内存占用和业务复杂度调整 - 它只对同步模式(
SWOOLE_PROCESS)有效;异步Server(SWOOLE_BASE)下该配置被忽略 - 真正可控的清理时机在
onWorkerExit,但此时已无法访问请求上下文,只能做资源归还(如关闭未释放的文件句柄) - 比
max_request更细粒度的控制是配合onRequest+onClose手动清理,但这要求你清楚每处资源的生命周期
最易被忽略的一点:Swoole不是“换个扩展就能提速”的黑盒,它是把PHP从“脚本语言”推向“服务端语言”的范式切换——你写的每一行代码,都在和常驻内存、共享资源、协程调度共舞。没想清楚变量在哪初始化、在哪销毁,问题一定在深夜出现。

















