Workerman 4.0.20 中实现稳定数据库连接池需绕过全局单例,通过 PoolInterface 封装 acquire/release/close,维护 $idle/$busy 双数组,结合协程 token 或 WeakMap 保障归属安全,并集成 ping 检测、定时清理与异常自动重建机制。

在 Workerman 4.0.20 中实现稳定、可复用的数据库连接池,必须绕过全局单例陷阱,避免进程间连接共享导致的“MySQL server has gone away”错误或连接句柄泄漏。直接 new Connection() 每次都新建连接会压垮 MySQL 的 max_connections,而裸写静态 pool 数组又无法自动回收失效连接。
连接池抽象层核心结构设计
连接池不是简单堆砌数组,而是要封装状态管理、生命周期控制和连接健康检查三要素。
定义一个 PoolInterface 接口,强制实现 acquire()、release($conn)、close() 三个方法。acquire() 必须返回 Promise 或协程感知对象,因为底层 Connection 是异步非阻塞的;release() 不是销毁连接,而是归还到可用队列并触发心跳检测;close() 需遍历所有连接调用 $conn->close() 并清空内部引用。
Pool 实现类中维护两个数组:【$idle】 存储当前空闲且通过 ping 检测的连接,【$busy】 记录正在被业务逻辑使用的 Connection 实例 ID(非对象本身,防止循环引用)。每次 acquire 优先从 $idle 取,取不到才新建连接并加入 $busy。
连接获取与归还的原子性保障
方法一:基于协程上下文绑定(推荐)
使用 Swoole\Coroutine::getuid() 或 Workerman 自带的 context_id(需 patch 4.0.20 的 Worker::$processId + microtime(true) 拼接),为每个 acquire 请求生成唯一 token。acquire() 返回时将 token 写入 Connection 对象的 custom property,release() 时校验 token 是否匹配,不匹配则拒绝归还——防止 A 进程误放 B 进程的连接。
方法二:基于 WeakMap 管理归属
PHP 8.0+ 支持 WeakMap,声明 static $ownerMap = new WeakMap(); 在 acquire() 后执行 $ownerMap[$conn] = $workerPid; release() 前先 isset($ownerMap[$conn]) && $ownerMap[$conn] === $currentPid,否则触发告警日志并丢弃该连接。
注意:WeakMap 方案在 Workerman 多进程下需配合 IPC 共享内存判断 pid 有效性,否则子进程 fork 后 $ownerMap 不同步。
连接健康检测与自动重建
第一步:在 onWorkerStart 中启动定时器
Worker::$onWorkerStart = function($worker) { Timer::add(30, function() use ($worker) { foreach ($worker->pool->idle as $conn) { if (!$conn->isConnected()) { $conn->close(); } }); };
第二步:acquire() 内部强制 ping
每次从 $idle 取出连接前,必须调用 $conn->query('SELECT 1')->then(function() use ($conn) { return $conn; }, function() use ($conn) { $conn->close(); return null; }); 若失败则跳过该连接,继续取下一个。这一步不可省略,否则会把断连连接分发给业务代码。
第三步:连接异常时自动重建
在 Connection 的 onError 回调里,捕获 'MySQL server has gone away' 或 'Connection refused' 错误后,立即执行 $this->close(),并触发 $worker->pool->rebuildOne() —— 该方法新建一个连接放入 $idle,同时减少当前 $busy 计数,保证池总量恒定。

















