Workerman中不能直接使用mysqli或PDO长连接,因其阻塞、有状态、不支持自动重连且无法跨进程共享;推荐使用workerman/mysql异步客户端,支持连接池、自动重连和超时控制。
mysqli 或 PDO 长连接连 MySQLWorkerman 是常驻内存的异步 PHP 框架,而 mysqli 和 PDO 的默认连接是阻塞式、有状态的,一旦连接被某个进程复用或超时断开,后续请求会直接报错(比如 MySQL server has gone away)。更关键的是:Workerman 的子进程(Worker)会持续运行,但 MySQL 连接不会自动重连,也不会跨进程共享。
所以你不能写这样的代码:
$pdo = new PDO('mysql:host=127.0.0.1;dbname=test', $user, $pass);
// 然后在 onMessage 里反复用 $pdo->query(...) —— 这会崩$pdo->getAttribute(PDO::ATTR_CONNECTION_STATUS) 不可靠)wait_timeout 默认 8 小时,但中间网络抖动、防火墙中断等都会提前 kill 连接)max_connections
workerman/mysql 异步协程客户端官方维护的 workerman/mysql 是基于 MySQL 协议实现的纯 PHP 异步客户端,支持连接池、自动重连、超时控制,且不依赖 ext-mysqlnd 或 libmysqlclient。它和 Workerman 的事件循环天然兼容。
安装:
composer require workerman/mysql
基本用法(在 Worker 启动时初始化连接池):
use Workerman\MySQL\Connection;$mysql = new Connection([ 'host' => '127.0.0.1', 'port' => 3306, 'user' => 'root', 'password' => '123456', 'database' => 'test', 'charset' => 'utf8mb4', 'read_timeout' => 5, // 读超时秒数 'write_timeout' => 5, ]);
// 查询示例(注意:返回的是 Promise,需在回调中处理) $mysql->query('SELECT * FROM user WHERE id = ?', [1], function($result) { var_dump($result); });
'pool_size' => 20 扩容query()
Connection 实例上调用 begin()/commit(),不能跨实例PDO,必须按请求新建 + 显式销毁仅适用于低并发、调试或简单脚本场景。核心原则:**绝不复用连接对象,每个请求 new 一次,用完 unset**。
// ✅ 正确(在 onMessage 内部创建)
public function onMessage($connection, $data)
{
try {
$pdo = new PDO('mysql:host=12
7.0.0.1;dbname=test;charset=utf8mb4', 'root', '123456', [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
]);
$stmt = $pdo->prepare('SELECT * FROM user WHERE id = ?');
$stmt->execute([$id]);
$user = $stmt->fetch();
$connection->send(json_encode($user));
} catch (Exception $e) {
$connection->send('db error');
} finally {
// ⚠️ 必须显式销毁,触发析构关闭连接
unset($pdo);
}
}
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,否则错误静默失败PDO::ATTR_PERSISTENT => true,持久连接在 Workerman 下完全不可控,极易导致连接泄漏用 workerman/mysql 时,连接池行为不像 Swoole 那样透明,容易误判“连不上”是网络问题还是池耗尽。几个关键点:
'pool_wait_timeout' => 3 秒后抛异常)'pool_idle_timeout' => 60)后会被自动回收,避免僵死连接netstat -an | grep :3306 | wc -l 观察实际建连数,确认没超出 MySQL 的 max_connections
$mysql->query('SELECT 1'),失败则 exit(1),避免 Worker 带病运行最常被忽略的是连接池大小和 MySQL 侧的 wait_timeout 不匹配——比如池设了 50,但 MySQL 只允许 30 连接,结果一半请求永远卡在等待池释放,日志里却只显示“timeout”,不是“connection refused”。