两套 WebSocket:daemon 通道、浏览器广播与多实例扇出
30 秒导读: Multica 是「托管型 agent 平台」——你在网页/桌面端派活,本地的 agent 进程(daemon)自动认领、执行、回报,你只管看进度。要做到这种「set it and forget it(设好就不用管)」的体验,页面不能靠轮询,必须服务端主动推。本章讲支撑这套推送的实时层:它由两套各司其职的 WebSocket 组成,中间用 Redis Stream 把多个服务端实例连成一张网。
本章在 Multica 全景中的位置(其余各章见 index):
- 上游是任务判定与状态机(03-task-dispatch-lifecycle)——它决定「有活了」;
- 下游是本地守护进程(02-local-daemon)——它认领并执行;
- 另一端是多端前端(05-frontend-architecture)——它消费本章推来的事件。
本章只讲中间那条实时管道:线协议、两个 hub、多实例扇出、前端如何消费。
1. 这是什么(零基础也能懂)
一句话定义
实时层 = 一条从「服务端」通向「本地 daemon」的控制通道 + 一条从「服务端」通向「浏览器」的广播通道,两条都跑在 WebSocket 上,但用途完全不同。
为什么要两套,而不是一套
它们连接的对象、信任模型、消息方向都不一样,硬塞进一套只会互相拖累:
| 维度 | daemon 通道 | 浏览器通道 |
|---|---|---|
| 连的是谁 | 用户机器上的 daemon 进程 | 网页 / 桌面 / 手机端 |
| 谁主动说话 | 双向:服务端唤醒 daemon,daemon 发 RPC/心跳回来 | 基本单向:服务端推,前端只发订阅/心跳 |
| 认证方式 | Authorization 头 + daemon token / PAT | Cookie 或首帧 token(浏览器设不了自定义头) |
| 核心用途 | 「有活了,来认领」+ 认领 RPC + 心跳 | 「这条 issue 变了,去刷新」 |
| 代码位置 | internal/daemonws/ | internal/realtime/ |
两个端点也是分开注册的(cmd/server/router.go:704 是浏览器 /ws,cmd/server/router.go:772 是 daemon 的 h.DaemonWebSocket)。
一句话直觉
- daemon 通道像「工头对讲机」:工头(服务端)喊一声「3 号工位有活」,工人(daemon)听到后自己跑去领工单——喊话只是提醒,真正领活还要走正式流程(见 §4 的「best-effort 唤醒」)。
- 浏览器通道像「广播喇叭」:办公室里所有人(浏览器标签)都听得到「A 项目进度更新了」,但喇叭只说「变了」,不念全文——听到的人自己去公告栏(数据库)取最新内容(见 §6 的「失效信号」)。