Workerman 是 PHP 生产环境最稳妥的 WebSocket 方案,兼容 PHP 7.4–8.5,但必须启用 pcntl 和 posix 扩展,且需禁用 CLI 模式 Opcache;监听地址应为 0.0.0.0 而非 127.0.0.1,Nginx 反代 WSS 时须透传 Upgrade 头,高并发广播需异步解耦。

Workerman 是目前 PHP 生产环境中最稳妥、最易落地的 WebSocket 实现方案,不依赖 Swoole 扩展,PHP 7.4–8.5 全版本兼容,但必须启用 pcntl 和 posix 扩展,否则进程根本起不来。
Workerman 启动失败:90% 是 pcntl/posix 没开或 CLI 模式 Opcache 干扰
Workerman 启动时卡住、报 pcntl_fork(): fork() failed 或直接无响应,基本就是这两个原因:
-
pcntl和posix扩展未启用:运行php -m | grep pcntl和php -m | grep posix,缺一不可;宝塔用户需在「PHP 设置 → 安装扩展」中勾选并重启 PHP 服务(不是只重启 Nginx) -
opcache.enable_cli=1开启了:Workerman 在 CLI 下动态加载类,Opcache 会缓存旧类导致热更新失效甚至启动失败;务必在php.ini中设为opcache.enable_cli=0,然后重启 PHP - Windows 下默认不支持
pcntl,得用workerman/workerman-for-win,且只能前台运行(去掉-d参数),不能守护进程
WebSocket 服务监听地址写成 127.0.0.1:2346 导致前端连不上
浏览器访问的是公网域名或局域网 IP,而服务只绑了本地回环地址,必然连接拒绝。这不是跨域问题,是网络层直连失败。
- 监听地址必须写成
websocket://0.0.0.0:2346,不能省略协议前缀,也不能写成ws://(那是客户端用的) - 云服务器(如阿里云、腾讯云)还需检查安全组是否放行该端口;本地开发可用
lsof -i :2346(macOS/Linux)或netstat -ano | findstr :2346(Windows)确认端口空闲 - 前端 JS 连接 URL 必须严格匹配协议:HTTP 页面用
new WebSocket('ws://your-domain.com:2346'),HTTPS 页面必须用wss://,否则浏览器静默拒绝,不报错也不触发onerror
广播消息遍历 $worker->connections 在高并发下会阻塞
Workerman 默认是单线程事件循环,onMessage 回调里 foreach 所有连接 push 数据,一旦连接数过千,一次广播就可能卡住整个 worker 进程,新连接进不来、心跳也断。
立即学习“PHP免费学习笔记(深入)”;
- 简单场景(foreach ($worker->connections as $conn) { $conn->send($data); }
- 中等负载(几百连接)建议加
if ($conn->isWebSocketConnection)过滤非 WebSocket 连接,避免误推 - 真实生产环境(>1000 连接)必须解耦:把要推送的数据发到 Redis Pub/Sub,另起一个独立 Worker 订阅并异步分发,或用
Timer::add()分批推送,避免阻塞主循环
Nginx 反向代理 WSS 时漏传 Upgrade 头,前端卡在 handshake
Workerman 本身不处理 TLS,wss:// 必须靠 Nginx 终止 SSL 并透传 WebSocket 协议头,否则握手阶段就失败,浏览器控制台只显示 Connection closed before receiving a handshake response。
- Nginx 配置里必须包含这两行:
proxy_set_header Upgrade $http_upgrade;和proxy_set_header Connection "upgrade"; -
proxy_pass地址必须指向 Workerman 的 HTTP 监听地址(如http://127.0.0.1:2346),不能带ws://协议 - 如果用了自签名证书,前端 Chrome 会拦截,需手动访问
https://your-domain.com:2346并点“高级 → 继续访问”一次,让证书被信任
Workerman 的简洁性是一把双刃剑:它把协议细节封装得很好,但连接管理、广播、心跳、SSL 终止这些事全得你手动补全——尤其当连接数从几十涨到上千时,那些看似“能跑就行”的代码,会在某个凌晨三点突然开始丢消息、积压连接、CPU 拉满。别迷信 demo 里的几行 foreach,先想清楚你的峰值连接数和推送频率。


















