# 待办中心实时刷新评估 ## 结论 当前继续使用首页 60 秒轮询,不新增 WebSocket 或 SSE。 现阶段没有低于 60 秒的待办送达 SLA,PRD 也将 30~60 秒轮询定义为 V1 推荐方案,将实时推送保留为实时性要求提高后的 V2 能力。现有实现能够在进入首页时立即加载、返回首页时主动刷新,并在离开页面后停止轮询,满足当前需求。 如果后续达到本文列出的升级触发条件,优先采用 SSE;WebSocket 仅在出现双向实时交互需求时考虑。实时通道只发送“待办状态已变化”事件,任务数据仍通过现有 REST 接口查询。 ## 当前实现与负载 - 首页创建和重新激活时立即调用 `/system/todo/unreadCount` 与 `/system/todo/homeList?limit=3`。 - 首页激活期间每 60 秒刷新一次,并通过 `todoLoading` 避免同一页面内请求重叠。 - 页面失活或销毁时清除定时器,待办中心列表打开时自行查询。 - 每个停留在首页的活跃用户每分钟产生 2 个只读请求,理论请求速率为 `活跃首页用户数 / 30` QPS;1000 个同时停留首页的用户约为 33.3 QPS。 - 首页查询已有按接收人、状态和排序设计的索引,当前验收目标为 P95 小于 500ms;真实数据基准仍按安全与性能清单执行。 - 轮询的最坏可见延迟为 60 秒,平均约 30 秒。业务页面完成待办后已有主动刷新,不必等待下一轮轮询。 ## 方案比较 | 维度 | 60 秒轮询 | SSE | WebSocket | | --- | --- | --- | --- | | 当前需求匹配 | 满足 PRD 的 30~60 秒要求 | 超出当前 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. 服务端发送 20~30 秒心跳并清理断开的连接;前端自动重连,对连续事件做短时间去抖。 5. 前端收到事件后仍调用现有 `/unreadCount`、`/homeList` 或 `/list` 接口获取权威数据。 6. SSE 断开、网关不支持或事件总线异常时自动回退到 60 秒轮询;重新连接后立即全量刷新。 该方案不要求事件可靠保存。SSE 事件只是刷新提示,断线期间遗漏的事件由重连后的 REST 查询和轮询兜底。 ## 实时化验收项 - 任何用户只能收到自己的刷新事件,不能从事件内容推断其他用户或运营商的任务。 - 创建、完成、取消、过期和归档均在事务提交后触发事件,回滚事务不推送。 - 单实例和多实例部署均能送达,断线重连不会造成连接泄漏。 - 令牌过期后连接关闭,重新登录后建立新连接;日志和 URL 不记录令牌。 - 长连接不可用时自动回退轮询,待办最终展示结果与数据库一致。 - 在明确实时 SLA 后,以事件提交到页面完成 REST 刷新为口径记录 P95,并在压测中验证连接数、线程数和网关超时配置。