Webman 不能直接处理 WebSocket 连接,因其启动的是 HttpServer 而非 WebSocketServer,需搭配 Workerman 独立提供 WebSocket 服务,并通过 Redis 或 HTTP 接口共享用户身份与房间状态,实现指令广播与高并发直播控制。

Webman 本身不直接处理视频流或 WebRTC 推流,它也不是为高并发直播指令设计的底层通信层——真正在线教育直播中的“高并发指令”(如举手、禁言、切课件、弹题)必须走 WebSocket,而 Webman 默认不内置 WebSocket 服务器能力。
Webman 能否直接 handle WebSocket 连接?
不能。Webman 是基于 Swoole 的 HTTP/HTTP2 框架,webman/start.php 启动的是 HttpServer,不是 WebSocketServer。你调用 $app->on('message', ...) 或试图在路由里监听 WebSocket 帧,会直接报错:Call to undefined method Webman\App::on()。
常见错误现象:
- 把 Workerman 的
Worker::onMessage写法照搬到 Webman 中 - 在
app/middleware里尝试拦截 upgrade 请求但没做协议协商 - 前端用
new WebSocket('ws://...')连接 Webman 服务,返回 400 或直接断开
正确做法是:单独起一个 Workerman WebSocket 服务(例如 websocket.php),与 Webman 共享业务逻辑(如用户认证、房间状态),但通信通道分离。
立即学习“PHP免费学习笔记(深入)”;
如何让 Webman 和 WebSocket 服务共享 session 或用户身份?
不能依赖 PHP 的 $_SESSION,因为 WebSocket 进程不经过 HTTP 生命周期。必须显式透传并验证身份凭证。
推荐方式:
- 前端建立 WebSocket 连接时,在 URL query 中带上短期有效的 token:
ws://localhost:2346?token=xxx - WebSocket 服务启动后,在
onConnect回调中调用 Webman 提供的鉴权接口(如http://127.0.0.1:8787/api/v1/auth/verify)做同步校验 - 校验通过后,将用户 ID、教室 ID、角色等写入内存表(如 Swoole\Table)或 Redis,供后续指令广播使用
- 避免在每次
onMessage时都远程调用鉴权接口,否则高并发下成为瓶颈
注意:Redis::hSet('room:1001', 'user_234', 'teacher') 这类操作比 file_put_contents 更可靠,但需确保 Redis 连接池配置合理(max_idle_time 和 min_idle 避免频繁重建连接)。
直播指令广播性能卡点在哪?
不是序列化慢,而是广播路径选择错误。常见误操作:
- 用
foreach ($connections as $conn) { $conn->send($data); }—— 阻塞式逐个发送,1000 人要发 1000 次 - 把指令封装成 JSON 后再压进 Redis Pub/Sub,由每个 WebSocket 进程自己订阅 —— 引入额外网络跳转和反序列化开销
- 未按教室维度隔离连接,导致一条“全体静音”指令推给所有在线用户,而非仅目标教室
Workerman 原生支持组播:$worker->connections 是全局连接池,但你应该用 ConnectionPool + 房间 ID 分组。示例关键逻辑:
// 在 onMessage 中
$room_id = $connection->room_id ?? null;
if ($room_id && isset($this->rooms[$room_id])) {
foreach ($this->rooms[$room_id] as $target_conn) {
if ($target_conn !== $connection && $target_conn->isEstablished()) {
$target_conn->send($payload);
}
}
}
这个 $this->rooms 必须是 Swoole\Table 或 Redis Hash,不能是 PHP 数组——否则多进程下无法共享。
为什么不能只用 Webman + Swoole\WebSocket\Server 拼凑?
可以技术上实现,但违背框架定位,且维护成本陡增。Webman 的中间件、路由、依赖注入体系默认不作用于 WebSocket 生命周期;你手动 new 一个 Swoole\WebSocket\Server 后,onOpen/onMessage 回调里无法直接使用 app()->make(UserService::class),也无法复用 config/database.php 配置。
容易被忽略的点:
- WebSocket 进程 crash 后不会自动重启,而 Webman 的 HTTP 进程有 supervisor 管理
- 日志上下文丢失:HTTP 请求带 trace_id,WebSocket 消息没有天然请求 ID,调试指令链路困难
- HTTPS/WSS 升级需额外配置 Nginx 的
proxy_set_header Upgrade $http_upgrade,且 Webman 不生成证书,全靠反向代理处理
真正稳定的方案是:Webman 专注 HTTP API(登录、课程列表、作业提交),Workerman 专注 WebSocket 指令通道,两者通过 Redis 或本地 Unix socket 通信——分工明确,出问题能快速隔离。



















