静态成员和全局单例可复用连接,避免Workerman中重复初始化;需在onWorkerStart初始化并做ping检测,禁存资源句柄与大数组,静态方法适合工具类而非业务逻辑。

静态成员和全局单例能复用连接,避免重复初始化
Workerman 进程常驻内存,类的静态属性、全局变量在请求间不会销毁。这意味着数据库连接、Redis 客户端、HTTP 客户端等资源,只要初始化一次,后续所有请求都能直接复用——不用每次 new PDO()、不用反复 connect(),省掉 TCP 握手、认证、释放等开销。
常见错误是把连接写在 onMessage 或控制器方法里,导致每来一个请求都新建连接,很快打满数据库最大连接数,或触发 MySQL server has gone away 错误。
- 正确做法:在 Worker 启动前(如
Worker::onWorkerStart)初始化连接,并赋值给类静态属性或全局变量 - 必须做连接健康检查:静态连接可能被服务端主动断开,使用前建议加
ping()或reconnect()逻辑 - 避免在静态属性中存 PHP 资源句柄(如
fopen()返回的 resource),这类资源在 fork 后可能失效
为什么不用非静态单例?它反而更占内存
静态方法/静态属性的初始化时机是“类首次被使用时”,一旦初始化就长期驻留内存;而非静态单例(比如 DCL 模式)虽然按需创建,但在 Workerman 场景下没优势——因为进程本就长驻,且单例对象大概率会在第一个请求就创建,最终效果一样,还多一层判断开销。
更重要的是,静态方式更符合 Workerman 的生命周期模型:你不需要管理对象生命周期,也不用担心依赖注入容器在多次请求中重建实例。
- 静态单例适合 DB、Cache、Logger 等无状态或强复用型资源
- 非静态单例更适合需要按请求隔离状态的场景(比如带用户上下文的 Request 对象),但这类对象不该放静态域
- 别在静态属性里存大数组或缓存全量数据,容易引发内存缓慢增长(OOM 风险)
静态方法不是万能的,注意它的副作用
静态方法调用快、无实例开销,但它不可 mock、不可继承、无法被依赖注入替换。在单元测试或需要多租户隔离的业务中,过度使用会降低可维护性。
典型陷阱是把业务逻辑全塞进静态方法,结果发现没法针对不同客户返回不同策略,最后只能硬编码 if-else 分支。
- 静态方法适合纯工具函数(如
StringUtils::formatJson())、连接管理器(Db::connection()) - 含业务规则、需扩展、需上下文参数的方法,优先用普通对象 + 依赖注入
- 静态方法内避免调用其他静态方法形成强耦合链,例如
OrderService::create() → Payment::pay() → Sms::send()
实际代码里怎么写才安全
下面这段是推荐写法:在 onWorkerStart 中初始化,用静态属性持有,每次使用前检查有效性。
class Db
{
private static $instance = null;
public static function getInstance()
{
if (self::$instance === null || !self::$instance->ping()) {
self::$instance = new PDO('mysql:host=127.0.0.1;dbname=test', $user, $pass, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_PERSISTENT => true,
]);
}
return self::$instance;
}
}
Worker::onWorkerStart = function ($worker) {
// 强制初始化一次,避免首个请求延迟
Db::getInstance();
};
真正容易被忽略的是:静态连接不等于“永远可用”。网络抖动、DB 主从切换、防火墙超时都会让它失效。只靠 if (null === $x) 判断远远不够,必须加活跃性检测。


















