swoole中static变量跨请求持续存在,第1000次请求时$counter值为999;必须在onworkerstart中重置,onrequest内unset无效。

static 变量在 Swoole 里不会随请求销毁
PHP-FPM 下写 static $count = 0,每次请求都是全新进程,$count 从 0 开始;Swoole 的 Worker 进程常驻内存,$count 会持续累加,第 1000 次请求进来时它已经是 999。这不是 bug,是模型差异导致的必然行为。
常见翻车点:
- 用
static $user存登录态,第二个用户直接拿到第一个用户的$user -
static $cache = []不设上限也不清理,数组越滚越大,最终 OOM - 类中
static $pdo复用连接,但没做失效检测,后续请求抛PDOException: SQLSTATE[HY000]: General error: 2006 MySQL server has gone away
onWorkerStart 是重置 static 的唯一可靠时机
onWorkerStart 是每个 Worker 进程启动(含平滑重启、max_request 触发重启)时**必定执行且只执行一次**的回调。所有需要“每进程清零”的静态状态,必须在这里显式重置。
正确做法示例:
$server->on('WorkerStart', function ($server, $worker_id) {
// 强制重置关键静态变量
User::$currentId = null;
Cache::$pool = [];
// 或更彻底:反射清空整个类的静态属性(慎用)
$ref = new ReflectionClass(User::class);
foreach ($ref->getStaticProperties() as $name => $value) {
User::$$name = null;
}
});
注意:onRequest 里 unset(static::$x) 无效 —— PHP 不允许 unset 静态属性,只会报 Warning。
别把 static 当 request-local 缓存用
想缓存当前请求数据?static 是错的。它属于进程级,不是请求级。协程间共享、跨请求污染是默认行为。
Swoole 6.1.1 是一个专为 PHP 设计的高性能事件驱动并发网络引擎。作为稳定版,它修复了编译时对 zlib 依赖的缺失及 curl 模块的内存安全风险。该版本支持协程、多线程与多进程架构,内置 TCP/HTTP/WebSocket 服务器,能够显著提升 PHP 在微服务、实时通信等场景下的执行效率与并发能力。
替代方案按场景选:
- 纯协程内临时用:直接用局部变量或
go(function () use ($data) { ... }) - 需跨函数传递:用
Swoole\Coroutine::getContext()存当前协程上下文,key 命名加前缀防冲突,比如'req_user_id' - 要复用又隔离:改用
Swoole\Table,按$request->fd或生成的$req_id作 key,生命周期可控 - 框架集成:ThinkPHP 8.x 推荐用
app('request')->cache,Laravel 可绑定RequestScopedCache到容器
trait 和继承中的 static 容易误判隔离性
两个类 A 和 B 都 use CacheTrait,trait 里定义了 static $counter —— 实际上 A::$counter 和 B::$counter 是两份独立变量。你以为“用了同一个 trait 就共享”,其实完全不共享。
更隐蔽的是子类继承父类 static 属性:虽然语法上能访问,但修改 Child::$config 不会影响 Parent::$config(PHP 7.4+ 后期静态绑定已分离)。如果代码依赖“子类改父类 static”,运行结果会和预期严重不符。
真正需要跨类共享?明确用全局唯一标识(如 CacheManager::getInstance()->set(...)),别依赖语言特性隐式行为。
最危险的不是用了 static,而是没意识到它在 Swoole 里活得比你想象中久得多 —— 一个 Worker 进程可能服务几小时,中间经历上百个用户、上千次请求,所有 static 状态都在 silently 积累。










