Swoole常驻内存下未重置静态变量、单例、全局数组等会导致跨请求数据污染;需在onRequest入口显式清空、隔离单例、修复协程上下文、断开重建外部连接、禁用危险缓存。

在Swoole常驻内存模式下,同一Worker进程处理多个请求时,静态变量、单例对象、全局数组、协程局部存储等状态未重置,会导致A用户看到B用户的数据、计数器持续递增、数据库连接复用超时、缓存错乱等数据异常问题。
重置类静态属性与全局变量
第一步:在onRequest回调入口处,显式清空所有已知污染源。
直接遍历并重置你项目中所有含static $cache、static $counter、static $instance的类——不要依赖PHP自动销毁,它们在常驻进程中永不释放。例如UserService::$cache = []、RequestCounter::$count = 0。
方法一:用反射暴力重置(适用于第三方类或无法修改源码的场景):注意:仅对明确知道结构的类使用,否则可能破坏内部逻辑
反射获取所有静态属性→遍历设为null或初始值→调用resetForRequest()若存在该方法。
方法二:封装统一清理函数,在每次请求开始前集中调用App::clear() + 自定义resetAllStatics(),其中resetAllStatics()需手动注册所有需清空的类名和属性键。
【必须在onRequest最开头执行,晚于任何业务逻辑调用】
隔离单例与容器实例
Yii2、ThinkPHP、Laravel等框架的Yii::$app、App::$instance、Container::getInstance()都是进程级单例,跨请求复用即等于跨用户共享上下文。
对Yii2:在onRequest中调用Yii::$app->set('user', new UserIdentity()) → 强制重建用户组件;再执行Yii::$app->db->close() → 断开旧连接,随后触发重新连接。
对ThinkPHP:调用App::clear()清除容器绑定后,【必须手动重置Db类静态连接池】,否则Db::connect()仍返回上次请求残留的PDO实例。
对Laravel Octane:启用--warm 启动时预热服务,但务必配置octane.php中'cleanup' => true,并在AppServiceProvider@register中移除所有静态缓存注册。
修复协程上下文混用
协程内使用Co::set()修改hook_flags、设置超时、切换IO驱动后,若不恢复原值,后续协程将继承错误配置,引发随机读写失败或超时异常。
① 在协程启动前保存原始配置:$origin = Co::stats();
Swoole 6.1.1 是一个专为 PHP 设计的高性能事件驱动并发网络引擎。作为稳定版,它修复了编译时对 zlib 依赖的缺失及 curl 模块的内存安全风险。该版本支持协程、多线程与多进程架构,内置 TCP/HTTP/WebSocket 服务器,能够显著提升 PHP 在微服务、实时通信等场景下的执行效率与并发能力。
② 执行业务逻辑(含Co::set()变更);
③ 协程结束前强制还原:Co::set($origin);
更稳妥的做法是避免在协程中调用Co::set(),改用协程本地化参数传递——比如file_get_contents($url, false, stream_context_create([...]))而非全局Co::set(['socket_timeout' => 5])。
使用Co::getUid()关联数据时,必须配合Co::defer()注册清理回调:不加defer就return,数据永远滞留
Co::defer(fn() => unset($GLOBALS['coroutine_context'][Co::getUid()]));
断开并重建外部连接
MySQL、Redis、HTTP客户端等连接对象持有底层资源句柄,常驻进程中不关闭就会累积或失效。
MySQL连接:每次请求前检查$connection->ping(),失败则$connection->close() → new PDO(...)重建;禁用ThinkPHP的'deploy' => 1读写分离池,改用单连接+连接超时控制。
Redis连接:不要复用Redis::connect()返回的长连接实例;改用连接池(如co-redis-pool)或每次请求new Redis() → connect() → setOption(REDIS_OPT_READ_TIMEOUT, 1) → 调用后立即$redis->close()。
HTTP客户端:禁止全局static $http = new Swoole\Http\Client();改用协程内new Swoole\Coroutine\Http\Client(),请求结束自动释放底层socket。
禁用危险缓存模式
方法一:删除所有形如static $cache = []、global $tempData的缓存声明,全部替换为请求级存储。
方法二:用协程ID作键存入Co::getuid()映射数组,但必须配对Co::defer清理——否则协程退出后数据残留。
方法三:改用Redis或APCu做缓存,设置key带request_id或coroutine_id前缀,过期时间≤30秒。
【严禁在控制器或Service层写static $data = []】 这类代码在Swoole中等于主动埋雷,第100次请求时该数组已存满100个用户数据。

















