协程中file_get_contents崩溃因swoole未启用全钩子;全局变量在worker中持久存在;nginx代理websocket需配置upgrade和connection头;多worker间状态不共享需用redis或table统一管理。

协程里用file_get_contents就崩?钩子没开全
不是所有阻塞操作都能被 Swoole 自动协程化。比如file_get_contents、curl_exec、原生mysqli_connect这些,默认不走协程调度,一调用就卡死整个 worker,其他协程全等它——这不是慢,是“假死”。
根本原因:Swoole 的协程 Hook 是可选的,得手动启用,且要覆盖全:
-
Swoole\Runtime::enableCoroutine(SWOOLE_HOOK_ALL)必须在co::run之前调用,越早越好(建议放在入口文件第一行) - 如果只传
SWOOLE_HOOK_TCP,那file、cURL、MySQLi等依然阻塞 - PHP 8.1+ 某些内置函数(如
stream_socket_client)需额外确认是否被钩住,可用var_dump(Swoole\Runtime::getHookFlags())验证
全局变量在 Worker 里“越活越久”?不是 bug 是设计
global $count或static $cache在 Swoole 里不会随请求结束自动清空。一个 Worker 进程里,第一次请求$count++变成 1,第二次进来还是 1,再加就是 2……值一直累加,直到 Worker 重启。
这不是内存泄漏,是进程常驻模型的必然结果:
- 每个 Worker 是独立 PHP 进程,全局/静态变量在其生命周期内持续存在
- 不同 Worker 之间的全局变量完全隔离,所以 A 浏览器连 Worker#1,B 连 Worker#2,彼此看不到对方的
$count - 想“每次请求干净开始”?别依赖全局变量,改用
$_SERVER['request_time']做上下文标识,或把状态存在Channel、Co\WaitGroup这类协程安全结构里
Nginx 代理 WebSocket 断连频繁?缺了两个关键 Header
直接ws://ip:9501能通,但走 Nginx 后隔几秒就掉线——八成是代理配置漏了协议升级头。WebSocket 握手本质是 HTTP Upgrade 请求,Nginx 默认当普通 HTTP 处理,不透传Upgrade和Connection,后端收不到,连接就降级为短连接。
正确配置只需补两行,其他照旧:
-
proxy_http_version 1.1(必须,HTTP/1.0 不支持 Upgrade) -
proxy_set_header Upgrade $http_upgrade(把客户端发来的 Upgrade 头透传过去) -
proxy_set_header Connection "upgrade"(固定值,告诉 Nginx 别关闭 TCP 连接)
顺带提醒:proxy_pass http://127.0.0.1:9501末尾千万别加/,否则路径会被重写,Swoole 收到的uri可能变成//导致路由错乱。
Worker 数设成 CPU 核数,结果用户状态对不上?跨进程数据不共享
默认worker_num = cpu_count,4 核机器起 4 个 Worker。A 用户连上 Worker#1,存了fd → uid映射;B 用户连 Worker#2,查不到 A 的状态——这不是 Bug,是 Swoole 多进程模型的天然边界。
想让所有 Worker 看到同一份在线状态?不能靠进程内数组:
- 用
Redis或Table(Swoole 共享内存表)存连接关系,读写都走统一存储 - 用
Server->sendMessage()或Server->push()跨 Worker 推送消息,但注意目标fd必须在本 Worker,跨 Worker 要先查表定位 - 别用
global或static存用户列表,那只是单个 Worker 的“局部真相”
最易忽略的一点:哪怕你用了 Redis,也要给连接加超时清理逻辑,否则断网不触发onClose,fd会永远挂在表里——长连接管理,从来不是“连上就完事”。










