yii2不能直接运行websocket服务,因其http短生命周期与websocket长连接模型冲突;需借助workerman或swoole等独立进程实现,复用yii业务逻辑时应在进程启动阶段初始化轻量application实例并静态复用。

Yii2 不能直接跑 WebSocket 服务
直接在 yii\web\Application 里启动 WebSocket 是无效的。HTTP 请求是短生命周期,而 WebSocket 需要常驻进程维持长连接、心跳、客户端列表管理 —— 这两者底层协议和运行模型完全冲突。$request、$response、Session 等 HTTP 上下文在 WebSocket 回调中不可用,强行复用会导致状态错乱或内存泄漏。
选 Workerman 还是 Swoole?看你的部署约束
Workerman 和 Swoole 是目前 Yii2 生产中最常用的两个方案,但适用场景不同:
-
Workerman:纯 PHP 实现,无需扩展,适合 Docker 容器或共享主机;缺点是单线程阻塞模型,
onMessage里做 DB 查询或文件读写会卡住整个 worker -
Swoole:需编译安装(如
pecl install swoole-4.8.13),支持协程和异步 IO,性能更高;但要求 PHP 环境可控,不兼容某些同步扩展(如mysql_connect) - Ratchet 虽然能跑,但单线程 + 无连接池 + PSR-7 中间件干扰握手,线上基本不用
如果你的服务器能装扩展,优先选 Swoole;如果只能走 Composer + CLI 启动,Workerman 更稳妥。
如何复用 Yii2 的 Model 和 DB?别每次 new Application
常见错误是在 onMessage 回调里反复 new \yii\web\Application —— 这会重复加载配置、重建 DB 连接池、触发大量 autoloader 查找,开销极大。
正确做法是:
- 在 Workerman/Swoole 进程启动阶段(如
Worker::onWorkerStart或 Swoole Server 的start回调)初始化一个轻量\yii\base\Application实例,只加载db、models、components等必要模块 - 将该实例存为静态属性(如
self::$yiiApp),在onMessage中复用它调用User::findOne()或触发事件 - 绝不调用
$app->run()或任何涉及路由、响应渲染的逻辑 - 注意:每个 worker 进程都有独立的 Yii 实例和 DB 连接,这是设计使然,不是 bug
消息推送不是“框架自动推”,而是“业务显式发”
没有魔法监听器能自动感知 ActiveRecord 变更并广播。实时性靠的是业务层主动触发:
- 用户下单后,在订单保存成功后,显式调用
WebSocketServer::broadcast('order.new', $orderData) - 群聊消息入库后,立即通过
Worker::sendToAll()或 Swoole 的$server->push()推送给在线成员 - 避免在
onMessage里做耗时操作(比如 JOIN 5 张表查权限)—— 应提前缓存或异步投递到 Redis 队列由 Worker 处理 - 前端必须自己维护重连逻辑(断网后尝试 ws://host:port,带 token 鉴权),Yii2 不参与连接生命周期
真正难的不是连上 WebSocket,而是连接管理、消息去重、离线消息兜底、多节点广播一致性 —— 这些都不在 Yii2 范畴内,得靠你设计存储结构和中间件协同。











