本节摘要:第 01 节讲了事件怎么生成并存入存储,这一节讲客户端如何「感知」这些事件。OpenWork 用的是带游标(cursor)的轮询——客户端定期用一个端点拉取「上次游标之后的新事件」,靠游标保证不丢。本节讲清这套机制:为什么用轮询而非服务器推送、游标如何防丢、轮询的节奏。
先澄清一个常见误解——很多人以为「实时性」就得用 SSE(服务器推送事件)或 WebSocket(双向长连接)。OpenWork 的 Reload 事件用的是普通轮询。为什么?
轮询的优势在这套场景里:
💡 「实时」不等于「推送」:很多场景轮询就够,而且更简单可靠。OpenWork 选择轮询,是务实的工程取舍——不为「看起来实时」而引入长连接的复杂度。
服务端提供一个事件端点(如 GET /workspace/:id/events),客户端调它拉取事件。返回大致:
{ items: [事件1, 事件2, ...], // 新事件 cursor: 当前 seq, // 让客户端下次用 workspaceId: ..., disabled: false }
关键点是 cursor——它是「当前最新的 seq」。客户端下次请求时带上这个 cursor,服务端就只返回「cursor 之后」的事件(增量)。
游标是这套轮询的核心。它保证不丢事件:
第一次请求(无 cursor): │ ▼ 返回所有现存事件 + 当前 cursor(假设 seq=10) │ 客户端记下 cursor=10 │ ▼ 一段时间后,第二次请求(带 cursor=10) │ ▼ 服务端返回 seq>10 的事件(增量)+ 新 cursor(假设 seq=15) │ 客户端记下 cursor=15 │ ▼ 第三次请求(带 cursor=15) ...
游标的本质是「我上次读到哪了」——客户端每次告诉服务端「我从 seq=N 之后要」,服务端返回 N 之后的。这样即使两次轮询之间发生了多个事件,也一个不丢(全在增量里)。
⚠️ 环形缓冲的限制:第 01 节说事件存储是环形缓冲(只留最近 N 条)。如果客户端两次轮询间隔太久,中间事件可能已被挤出缓冲——这时客户端会「丢事件」。但 Reload 场景下这不是大问题(旧 Reload 错过了,下次全量重载补上)。设计是务实的——不追求「绝不丢」,只追求「正常情况下不丢」。
客户端怎么决定「何时轮询」?常见策略:
OpenWork 的前端有一个专门的轮询器(如 session-group-event-poller),负责按某节奏轮询事件端点,拿到事件后分发给相关方处理。
除了工作区级的事件端点,还有几个相关的:
这些端点形态类似(返回 items + cursor),只是作用域不同。客户端按需订阅不同作用域的事件。
客户端拿到事件后,做什么?这是下一节(引擎重载触发链)的主题。简单说:
客户端拿到 Reload 事件 │ ▼ 判断是什么 reason(config? skills? ...) │ ▼ 触发对应的重载动作 │ ▼ 驱动引擎重建实例、重读配置
本节只讲「怎么感知」,下一节讲「感知后怎么办」。
GET /workspace/:id/events,返回 items + cursor。感知讲清了,下一节讲感知后怎么办——引擎重载触发链。