典型表现为while(true)循环中处理RabbitMQ消息时,几小时后报“MySQL server has gone away”或“Lost connection”,因MySQL服务端超时主动断连而PHP未检测重连。

PHP CLI 消费 RabbitMQ 时 MySQL 连接超时断开的典型表现
你在 while (true) 循环里用 amqp_consumer 或 php-amqplib 拉消息,处理逻辑里调了 PDO 或 mysqli 查数据库,跑几小时后突然报错:MySQL server has gone away 或 Lost connection to MySQL server during query。这不是 RabbitMQ 的问题,是 PHP CLI 进程长期运行下 MySQL 连接被服务端主动踢掉(wait_timeout 默认 28800 秒,但实际可能更短),而你的 PDO 实例没做失效检测。
在每次数据库操作前手动检查并重连
CLI 进程不重启,就不能依赖框架的请求生命周期自动重建连接。最直接的办法是在每次执行 SQL 前,确认连接是否还活着:
-
PDO::getAttribute(PDO::ATTR_CONNECTION_STATUS)在部分驱动下不可靠,别依赖它 - 推荐用
$pdo->query("SELECT 1")->fetch()做轻量探测,失败就重建PDO实例 - 把连接封装成一个函数,比如
get_db_connection(),内部带 try/catch 和重试逻辑(最多 1–2 次) - 注意:不要在消息处理中途才检查——得在
try { /* 处理业务 */ }开头就调用,否则事务或预处理语句可能卡在半路
使用 PDO::ATTR_ERRMODE + 异常捕获兜底重连
即使加了前置探测,网络抖动或 MySQL 主从切换仍可能导致查询中途中断。必须对所有数据库调用做异常防御:
- 初始化 PDO 时务必设置:
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION) - 所有
$pdo->prepare()、$stmt->execute()都包在try/catch (PDOException $e)里 - 捕获到
SQLSTATE[HY000] [2006] MySQL server has gone away或[2013] Lost connection时,调用重连函数,再重试当前操作(最多一次,避免死循环) - 注意:事务中出错需先
$pdo->rollBack(),再重连,否则新连接无法继续原事务
AMQP 消费循环里避免连接复用跨消息
RabbitMQ 客户端连接(如 PhpAmqpLib\Connection\AMQPStreamConnection)和 MySQL 连接是两回事,但容易混淆的是:有人把 $channel 或 $connection 实例存在全局变量里,长期复用。这本身没问题,但要注意:
- AMQP 连接可能因网络中断静默失效,
$channel->basic_get()可能卡住或抛出AMQPRuntimeException,此时不能只重连 MySQL - 建议对 AMQP 调用也加超时和异常捕获,例如用
set_time_limit(30)防卡死,捕获后unset($channel); unset($connection);再重建 - 不要让一次消息处理的 DB 连接、AMQP channel、文件句柄等全部共享同一个长生命周期对象——它们的失效边界不同,混在一起会让问题定位变难
真正麻烦的不是重连逻辑本身,而是你得同时监控 MySQL 连接状态、AMQP 通道健康度、以及两者在事务/消息确认中的耦合关系。哪怕只漏掉一次 rollback() 或少写一个 catch,后续消息就会持续失败,还看不出明显报错。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











