Files
jsowell-charger-web/docs/plan/2026-07-22-todo-center-realtime-evaluation.md
2026-07-22 17:09:33 +08:00

73 lines
5.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 待办中心实时刷新评估
## 结论
当前继续使用首页 60 秒轮询,不新增 WebSocket 或 SSE。
现阶段没有低于 60 秒的待办送达 SLAPRD 也将 3060 秒轮询定义为 V1 推荐方案,将实时推送保留为实时性要求提高后的 V2 能力。现有实现能够在进入首页时立即加载、返回首页时主动刷新,并在离开页面后停止轮询,满足当前需求。
如果后续达到本文列出的升级触发条件,优先采用 SSEWebSocket 仅在出现双向实时交互需求时考虑。实时通道只发送“待办状态已变化”事件,任务数据仍通过现有 REST 接口查询。
## 当前实现与负载
- 首页创建和重新激活时立即调用 `/system/todo/unreadCount``/system/todo/homeList?limit=3`
- 首页激活期间每 60 秒刷新一次,并通过 `todoLoading` 避免同一页面内请求重叠。
- 页面失活或销毁时清除定时器,待办中心列表打开时自行查询。
- 每个停留在首页的活跃用户每分钟产生 2 个只读请求,理论请求速率为 `活跃首页用户数 / 30` QPS1000 个同时停留首页的用户约为 33.3 QPS。
- 首页查询已有按接收人、状态和排序设计的索引,当前验收目标为 P95 小于 500ms真实数据基准仍按安全与性能清单执行。
- 轮询的最坏可见延迟为 60 秒,平均约 30 秒。业务页面完成待办后已有主动刷新,不必等待下一轮轮询。
## 方案比较
| 维度 | 60 秒轮询 | SSE | WebSocket |
| --- | --- | --- | --- |
| 当前需求匹配 | 满足 PRD 的 3060 秒要求 | 超出当前 SLA | 超出当前 SLA |
| 通信方向 | 前端拉取 | 服务端单向通知 | 双向通信 |
| 实现成本 | 已完成 | 中 | 高 |
| 现有鉴权兼容 | 直接复用 Bearer 请求头 | 原生 `EventSource` 不能设置 Bearer 请求头,需要流式 fetch/polyfill 或鉴权改造 | 握手鉴权和令牌续期需要单独实现 |
| 多实例支持 | 无额外要求 | 需要跨实例事件分发 | 需要跨实例事件分发和连接管理 |
| 代理与运维 | 普通 HTTP | 需要关闭缓冲、配置超时和心跳 | 需要 Upgrade、超时、心跳和连接容量配置 |
| 降级 | REST 本身即主链路 | 必须回退轮询 | 必须回退轮询 |
待办只需要服务端向前端发送“数据有变化”的信号不需要客户端通过长连接提交命令因此若未来升级SSE 比 WebSocket 更贴合通信模型。
## 当前基础设施缺口
- 后端没有 `spring-boot-starter-websocket``SseEmitter` 端点或待办连接注册表。
- `TodoTaskService` 的创建、完成、取消、过期和归档操作没有统一发布提交后事件。
- 待办由 system、pile、Quartz 等多个模块触发,需要统一事件出口,不能在各业务入口分别维护推送逻辑。
- 项目虽已配置 Redis 和 RabbitMQ但没有面向待办用户事件的跨实例发布订阅通道仅使用进程内连接表会在多实例部署时漏推。
- 当前后台登录是无状态 Bearer 鉴权。令牌保存在前端 Cookie 中,但服务端只读取 `Authorization` 请求头,原生 `EventSource` 无法直接复用。
- 仓库没有可验证的 Nginx/网关长连接超时、SSE 缓冲或 WebSocket Upgrade 配置。
## 升级触发条件
满足任一条件后重新评估实时推送:
1. 产品明确要求待办创建或状态变化后 10 秒内可见。
2. 待办需要在首页之外显示全局角标、桌面提醒或声音提醒。
3. 测试或生产监控表明轮询接口的 QPS、数据库负载或 P95 已超过约定容量预算,且索引和接口合并仍不足以解决。
4. 系统已经具备可复用的认证长连接、跨实例事件总线和网关长连接运维能力,接入成本明显下降。
在触发实时化之前,优先考虑低成本优化:页面不可见时暂停轮询、恢复可见时立即刷新,以及在证据表明确有必要时合并首页数量和最近待办接口。
## SSE 目标方案
1. `TodoTaskService` 在数据库事务提交后发布只包含 `assigneeUserId`、事件类型和版本号的待办变更事件,不发送标题、内容或业务参数。
2. 使用 Redis Pub/Sub 或 RabbitMQ 将事件广播到所有应用实例;每个实例只向本机对应用户的连接发送刷新信号。
3. 增加受认证保护的 `/system/todo/events` SSE 端点。前端使用支持 `Authorization` 请求头的流式 fetch/polyfill避免把令牌放入 URL。
4. 服务端发送 2030 秒心跳并清理断开的连接;前端自动重连,对连续事件做短时间去抖。
5. 前端收到事件后仍调用现有 `/unreadCount``/homeList``/list` 接口获取权威数据。
6. SSE 断开、网关不支持或事件总线异常时自动回退到 60 秒轮询;重新连接后立即全量刷新。
该方案不要求事件可靠保存。SSE 事件只是刷新提示,断线期间遗漏的事件由重连后的 REST 查询和轮询兜底。
## 实时化验收项
- 任何用户只能收到自己的刷新事件,不能从事件内容推断其他用户或运营商的任务。
- 创建、完成、取消、过期和归档均在事务提交后触发事件,回滚事务不推送。
- 单实例和多实例部署均能送达,断线重连不会造成连接泄漏。
- 令牌过期后连接关闭,重新登录后建立新连接;日志和 URL 不记录令牌。
- 长连接不可用时自动回退轮询,待办最终展示结果与数据库一致。
- 在明确实时 SLA 后,以事件提交到页面完成 REST 刷新为口径记录 P95并在压测中验证连接数、线程数和网关超时配置。