不能在onmessage里每次new pdo或connection,因为workerman常驻内存,重复创建连接会累积导致“too many connections”;根本原因是空闲超时后mysql主动断连,而非网络中断,必须在onworkerstart中单进程初始化、ping检测并自动重建连接。

Workerman 连接 MySQL 必须复用连接,不能在 onMessage 里每次 new 一个实例;报 MySQL server has gone away 的根本原因是连接空闲超时后未检测就直接复用,不是网络断了,而是 MySQL 主动关掉了这个“睡太久”的连接。
为什么不能在 onMessage 里 new PDO 或 new Connection?
Workerman 是常驻进程,onMessage 会被反复调用。每次 new 一个新连接,旧连接又没 close,就会累积——很快触发 SQLSTATE[HY000] [1040] Too many connections。这不是连不上,是连爆了。
- PHP-FPM 每次请求新建进程,连接自然释放;Workerman 完全是另一套生命周期
-
PDO::ATTR_PERSISTENT => true在 Workerman 下反而更危险:持久连接不会随请求结束而清理,子进程 fork 后可能继承失效句柄 - 即使用了
Workerman\MySQL\Connection,如果初始化位置错了(比如写在Worker::runAll()前),也会被主进程创建、子进程继承,导致多个 worker 共用同一 socket
onWorkerStart 初始化时必须做三件事
每个 worker 子进程启动时,单独建一个连接,并带上存活检测和自动恢复逻辑:
- 用
global $db或$worker->db存储实例,确保该 worker 内复用 - 初始化后立即调一次
$db->ping()(Workerman\MySQL\Connection内部不自动 ping,得自己加) - 捕获
MySQL server has gone away错误后,手动重建连接:先 unset 原实例,再 new 一个新的赋值回去
示例片段:
$worker->onWorkerStart = function ($worker) {
global $db;
$db = new Workerman\MySQL\Connection('127.0.0.1', 3306, 'user', 'pass', 'test');
// 主动 ping 一次,避免首次 query 就失败
if (!$db->ping()) {
$db = new Workerman\MySQL\Connection('127.0.0.1', 3306, 'user', 'pass', 'test');
}
};
扩展缺失会导致 Fatal error:Undefined class constant 'MYSQL_ATTR_INIT_COMMAND'
这个错误和 MySQL 连接逻辑无关,纯粹是 PHP 缺少扩展。运行 php -m 查看是否含 pdo 和 pdo_mysql:
- CentOS/RHEL:PHP 7+ 用
yum install php70w-pdo php70w-mysqlnd(注意不是php-mysql) - Ubuntu/Debian:PHP 7.0+ 用
apt-get install php7.0-mysql,PHP 8.x 改为php-mysql - Windows:检查
php.ini是否启用了extension=php_pdo.dll和extension=php_pdo_mysql.dll,路径要对(extension_dir指向正确的ext目录)
装完务必重启 PHP CLI 环境(不是 Web 服务器),再跑 php -m | grep pdo 确认。
事务和 prepare 语句千万别跨回调使用
同一个 Worker 进程内,onMessage 是串行执行的,但事务一旦 begin 就没 commit/rollback,后续所有请求都会卡在下一条 SQL 上,现象是大量请求延迟突增或报 Lock wait timeout exceeded。
- prepare 也不能复用:
$stmt = $db->prepare(...)返回的对象绑定的是当前连接上下文,下次onMessage调用时连接可能已重置,$stmt->execute()会直接失败 - 所有事务操作必须在单次
onMessage内闭环:begin → query → commit/rollback,不能靠全局变量存$db->beginTransaction()状态 - 复杂查询建议用 raw SQL +
bindValues(),比链式 builder 更可控,也更容易调试
最易被忽略的一点:Workerman 的连接复用不是“省事”,而是“必须”。它不像 Web 请求那样有天然边界,一个连不上,整个 worker 就废了——而你往往直到用户反馈慢、查日志看到一堆 gone away 才意识到问题出在初始化位置。











