onworkerstart 初始化数据库连接是唯一安全且推荐的做法,因workerman多进程模型要求每个子进程独享持久连接,全局或请求中初始化均会导致并发冲突或连接耗尽,且须配置自动重连与异常捕获。

对,onWorkerStart 回调中初始化数据库连接,是唯一安全且推荐的做法。因为 Workerman 是多进程常驻内存模型,每个 Worker 子进程独立运行、生命周期长,而数据库连接这类资源必须按进程隔离复用,不能共享,也不能在每次请求时新建。
为什么只能在 onWorkerStart 初始化?
全局初始化(如文件顶部 new PDO)会导致所有子进程共用一个连接句柄,极易引发并发冲突、连接被意外关闭或状态错乱;
在 onConnect 或 onMessage 中初始化,等于每来一个连接或每收一条消息就建一次连接,快速耗尽数据库连接池,还可能触发超时、认证失败等问题。
只有 onWorkerStart 每个子进程启动时执行一次,天然匹配“一个进程、一个持久连接”的最佳实践。
初始化时必须开启自动重连
长连接运行数小时甚至数天后,MySQL 可能因 wait_timeout 主动断开,PHP 进程却 unaware,后续查询就会报“MySQL server has gone away”。解决方法不是手动检测,而是依赖驱动层自动恢复:
- PDO:设置
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION(必须!否则异常不抛出),并启用break_reconnect => true(ThinkORM)或底层配置PDO::MYSQL_ATTR_INIT_COMMAND+ 合理超时 - MySQLi:调用
$mysqli->options(MYSQLI_OPT_CONNECT_TIMEOUT, 3),并确保 php.ini 中mysqli.reconnect = On(PHP 7.0+ 默认开启)
ORM 使用要注意连接实例隔离
像 ThinkORM 或 Eloquent,不能直接用 Db::table()->find() 这类静态门面调用——它默认操作的是全局单例,跨进程不安全。正确方式是:
- 在 onWorkerStart 中显式调用
Db::connect([...])获取当前进程专属连接实例 - 后续所有查询都基于该实例,例如
$db = Db::connect(); $db->table('user')->find(1); - 务必 try-catch PDOException 并记录日志,避免一次查询失败导致整个进程连接不可用
Redis 等其他连接同理
Redis 客户端(如 predis、phpredis)、HTTP 客户端(如 Guzzle with handler pool)、消息队列连接等,只要需要长期持有、高并发复用,全部遵循同一原则:只在 onWorkerStart 中创建并赋值给 $worker 或全局静态属性(注意进程间不共享),后续 onMessage 中直接复用。











