webman原生websocket路由不能替代网关,因其单进程架构缺乏连接管理、跨进程广播和分流能力;必须使用webman/gateway-worker插件实现gateway/business/register三进程解耦架构。

Webman 本身不是 WebSocket 网关,强行把它当网关用,会在连接数破万、需跨节点广播、要 header 分流或协议透传时直接卡死或丢消息。真要做高性能 WebSocket 网关,必须用 webman/gateway-worker 插件,它才是 Workerman 官方 GatewayWorker 架构的 Webman 封装——不是自己手写 onMessage 广播逻辑,也不是靠 Worker::$connections 硬扛多进程隔离。
为什么不能直接用 Webman 原生 WebSocket 路由做网关
Webman 的 Route::websocket() 只是把 WebSocket 请求转给一个继承 Webman\WebSocket\Connection 的类处理,本质仍是单 Worker 进程内的 HTTP 升级流程,不带连接管理、无注册中心、不支持 worker 分离(gateway/business/register 三进程解耦)。典型后果:
- 所有连接挤在同一个进程里,
$connection->worker->connections只能遍历本进程连接,其他 worker 里的客户端收不到广播 - 无法实现“连接保活 + 消息路由 + 业务处理”分层,心跳检测和消息解析混在一起,
onMessage里一加 DB 查询就阻塞整个进程 - 没有 register 中心,做不到节点发现;没 business worker,没法把消息转发到 Laravel/Swoole/Java 后端服务
- 路由只认 path,
X-Api-Version或Authorization字段完全不可用于分流
必须用 webman/gateway-worker 插件启动三进程架构
安装后,配置 config/plugin/webman/gateway-worker/process.php 是关键,它定义了 gateway、business worker、register 三个独立进程的行为。常见错误是只改 enable => true 就以为启用了网关——实际没配 process.php,等于没装。
WebSocket 8.18.2 是该协议规范的一个重要迭代版本,主要优化了连接稳定性与数据传输效率。它通过全双工通信机制,允许客户端与服务器在单一长连接上实时交换数据,大幅降低传统 HTTP 轮询的开销。该版本增强了心跳保活、自动重连及二进制帧传输能力,适用于即时通讯、在线游戏及金融行情推送等低延迟场景,为开发者提供更可靠的实时网络交互基础。
-
gateway进程负责维持海量长连接、收发帧、心跳检测(pingInterval必须设为 25~30 秒,且pingData要是合法 JSON 字符串,否则某些前端库解析失败) -
business进程才是真正写业务逻辑的地方,通过Events::onMessage()接收 gateway 转发的消息,再调用后端服务或查 Redis,它不碰 TCP 连接,可放心异步操作 -
register进程必须唯一且常驻,地址写死在 gateway 和 business 的registerAddress里,格式是text://127.0.0.1:1236,少一个text://前缀就会连不上
Nginx 代理 WebSocket 时必须显式透传 Upgrade 头
不配 Nginx,客户端直连 gateway-worker 端口(如 7272)当然可以,但线上必须过 Nginx 做负载和 TLS 终结。此时若漏掉任一配置项,WebSocket 握手直接 400 或静默断连。
-
proxy_set_header Upgrade $http_upgrade和proxy_set_header Connection "upgrade"必须成对出现,“upgrade”必须是字面量双引号字符串,不能写成$connection_upgrade -
proxy_read_timeout至少设为pingInterval * 3(比如 pingInterval=25,则设 75),否则 Nginx 先断连,gateway 还以为连接活着 -
proxy_buffering off必须关闭,否则 Pong 帧被缓存延迟返回,客户端心跳超时主动断开 - 路径匹配要精确,
location /ws/ { ... }和 gateway 的listen => 'websocket://0.0.0.0:7272'必须语义一致,别让 Nginx 把/ws代理成/
业务逻辑必须剥离到 BusinessWorker,禁止在 Gateway 进程里写数据库或 Redis
Gateway 进程只做连接管理:收帧、发帧、心跳、踢人。任何阻塞操作都会拖垮整个连接池。你看到的“CPU 飙高但连接数不高”“部分用户收不到消息”,90% 是因为有人在 Events::onMessage() 之外的地方(比如自定义 gateway handler)写了 mysqli_query() 或 file_get_contents()。
- 身份绑定不要用
$connection->session()->set('uid', 123)—— session 默认文件存储,不跨进程也不跨机器;应统一在Events::onConnect()里解析 token,然后$connection->uid = 123并写 Redis 记录在线状态 - 广播消息必须走
GatewayClient::sendToAll()或GatewayClient::sendToUid(),而不是手动遍历$worker->connections;前者会自动跨进程、跨机器(配合 Redis)下发 -
onClose回调里必须显式调用GatewayClient::closeClient($client_id)并删 Redis 中的在线 key,否则 Redis 里残留大量“假在线”用户,后续sendToUid()会往已断开的连接发包,触发EPIPE错误
最易被忽略的一点:GatewayWorker 架构下,onMessage 在 Gateway 进程中收的是原始 WebSocket 帧数据(可能是文本或二进制),但 Events::onMessage() 在 BusinessWorker 进程中收到的已经是 PHP 数组或对象——中间的序列化/反序列化由 GatewayWorker 自动完成。如果你自己在 Gateway 层做了 JSON 编解码,又在 Business 层再解一次,就会出现 json_decode(null) 或嵌套解析错误。务必检查前端发来的 $data 类型,用 is_string($data) && !empty($data) 做前置校验。










