如何使用ThinkPHP高效实现长连接请求处理?

2026-05-20 13:551阅读0评论SEO问题
  • 内容介绍
  • 文章标签
  • 相关推荐

本文共计1014个文字,预计阅读时间需要5分钟。

如何使用ThinkPHP高效实现长连接请求处理?

ThinkPHP 本身不支持真正意义上的长连接请求,所有长连接都需脱离 PHP-FPM 模式,改用 Swoole 或 Workerman 等常驻进程方案;在标准 Web 部署下,仅能依赖伪长轮询模拟。

为什么不能在控制器里用 sleep() 挂起请求

PHP-FPM 是同步阻塞模型,每个请求独占一个 worker 进程。写 sleep(5)while (!Redis::get($key)) { usleep(100000); } 会导致该 worker 被锁死,无法处理新请求。压测时很快出现超时、502、FPM 进程耗尽。

  • 即使加了 set_time_limit(0),也只延长脚本执行时间,不解决进程阻塞本质
  • Nginx 默认 proxy_read_timeout 是 60 秒,超时后主动断开,客户端收不到响应
  • MySQL 连接可能因 wait_timeout 中断,触发 PDOException: MySQL server has gone away

扫码登录这类场景该用伪长轮询而非真长连接

客户端每 1–2 秒发一次 GET 请求,服务端查 Redis,有结果立刻返回,无结果快速返回空 JSON(如 {"code": 0, "msg": "waiting"}),不挂起、不阻塞。

  • Redis key 建议用 SET scan_login:{$uuid} {"uid":123,"status":"success"} EX 300,避免 HSET 或序列化兼容问题
  • 客户端轮询要加随机抖动(如 Math.random() * 600 - 300),防请求雪崩
  • 服务端不要查数据库——轮询接口只读 Redis,写入由扫码确认动作原子完成
  • 若用 Redis 集群,确保 Redis::connection() 指向同一逻辑库,否则 key 分散查不到

要用真长连接,必须换运行模型:Swoole 或 GatewayWorker

ThinkPHP 原生 Db、Request、Session 等组件依赖 PHP-FPM 生命周期,无法直接用于 WebSocket 或 TCP 长连接上下文。必须通过适配层桥接。

立即学习“PHP免费学习笔记(深入)”;

  • 选 Swoole:装 swoole 扩展,用 think-swoole 官方扩展启动 HTTP/WebSocket 服务,Db 可继续用,但需关掉 break_reconnect(Swoole 下连接生命周期不同)
  • 选 GatewayWorker:装 workerman/gateway-worker + topthink/think-worker,启动命令是 php think worker:gateway,业务逻辑写在 Events.php 里,不再走 MVC 流程
  • 别混用:Nginx + PHP-FPM 下配 websocket:// 协议无效;wss 必须经 Nginx 反代,且配置 UpgradeConnection

连接复用和断连兜底不能全信 break_reconnect

break_reconnect => true 只在单次请求内生效,且仅对非事务查询起作用。它不是后台心跳,而是“报错后重试下一条 SQL”。

  • 事务中连接断开(如 Db::startTrans() 后 wait_timeout 到期),TP 直接抛异常,不会重连
  • 用了 mysqli 驱动?break_reconnect 完全不识别,必须切回 pdo_mysql
  • Swoole 常驻进程下,连接被多个请求共享,break_reconnect 无法跨请求生效,此时应靠连接池或定期 PING
  • 真正稳的方案是:MySQL 端设 wait_timeout = 300,应用层每次请求结束显式调用 Db::close()(TP6.1+ 支持),或依赖自动析构但需确保没异常中断

复杂点在于:你以为在配“长连接”,其实是在协调 PHP 运行模型、MySQL 连接状态、框架连接管理、Redis 状态同步四者的节奏。任何一个环节错位,就会出现连接泄漏、重复创建、秒级超时或数据不一致。别只盯着配置项,先确认你用的是哪种部署方式。

标签:PHPThinkPHP

本文共计1014个文字,预计阅读时间需要5分钟。

如何使用ThinkPHP高效实现长连接请求处理?

ThinkPHP 本身不支持真正意义上的长连接请求,所有长连接都需脱离 PHP-FPM 模式,改用 Swoole 或 Workerman 等常驻进程方案;在标准 Web 部署下,仅能依赖伪长轮询模拟。

为什么不能在控制器里用 sleep() 挂起请求

PHP-FPM 是同步阻塞模型,每个请求独占一个 worker 进程。写 sleep(5)while (!Redis::get($key)) { usleep(100000); } 会导致该 worker 被锁死,无法处理新请求。压测时很快出现超时、502、FPM 进程耗尽。

  • 即使加了 set_time_limit(0),也只延长脚本执行时间,不解决进程阻塞本质
  • Nginx 默认 proxy_read_timeout 是 60 秒,超时后主动断开,客户端收不到响应
  • MySQL 连接可能因 wait_timeout 中断,触发 PDOException: MySQL server has gone away

扫码登录这类场景该用伪长轮询而非真长连接

客户端每 1–2 秒发一次 GET 请求,服务端查 Redis,有结果立刻返回,无结果快速返回空 JSON(如 {"code": 0, "msg": "waiting"}),不挂起、不阻塞。

  • Redis key 建议用 SET scan_login:{$uuid} {"uid":123,"status":"success"} EX 300,避免 HSET 或序列化兼容问题
  • 客户端轮询要加随机抖动(如 Math.random() * 600 - 300),防请求雪崩
  • 服务端不要查数据库——轮询接口只读 Redis,写入由扫码确认动作原子完成
  • 若用 Redis 集群,确保 Redis::connection() 指向同一逻辑库,否则 key 分散查不到

要用真长连接,必须换运行模型:Swoole 或 GatewayWorker

ThinkPHP 原生 Db、Request、Session 等组件依赖 PHP-FPM 生命周期,无法直接用于 WebSocket 或 TCP 长连接上下文。必须通过适配层桥接。

立即学习“PHP免费学习笔记(深入)”;

  • 选 Swoole:装 swoole 扩展,用 think-swoole 官方扩展启动 HTTP/WebSocket 服务,Db 可继续用,但需关掉 break_reconnect(Swoole 下连接生命周期不同)
  • 选 GatewayWorker:装 workerman/gateway-worker + topthink/think-worker,启动命令是 php think worker:gateway,业务逻辑写在 Events.php 里,不再走 MVC 流程
  • 别混用:Nginx + PHP-FPM 下配 websocket:// 协议无效;wss 必须经 Nginx 反代,且配置 UpgradeConnection

连接复用和断连兜底不能全信 break_reconnect

break_reconnect => true 只在单次请求内生效,且仅对非事务查询起作用。它不是后台心跳,而是“报错后重试下一条 SQL”。

  • 事务中连接断开(如 Db::startTrans() 后 wait_timeout 到期),TP 直接抛异常,不会重连
  • 用了 mysqli 驱动?break_reconnect 完全不识别,必须切回 pdo_mysql
  • Swoole 常驻进程下,连接被多个请求共享,break_reconnect 无法跨请求生效,此时应靠连接池或定期 PING
  • 真正稳的方案是:MySQL 端设 wait_timeout = 300,应用层每次请求结束显式调用 Db::close()(TP6.1+ 支持),或依赖自动析构但需确保没异常中断

复杂点在于:你以为在配“长连接”,其实是在协调 PHP 运行模型、MySQL 连接状态、框架连接管理、Redis 状态同步四者的节奏。任何一个环节错位,就会出现连接泄漏、重复创建、秒级超时或数据不一致。别只盯着配置项,先确认你用的是哪种部署方式。

标签:PHPThinkPHP