docs: plan realtime events and gateway WebSocket migration
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
# 实时事件推送规划
|
||||
|
||||
## 目标与首期边界
|
||||
|
||||
用户已确认:复用 douyin-pc 已验证的网页实时通知信号;先实现事件推送,WS 与现有 HTTP 共用网关地址和端口;后续全部网关业务通信迁移到 WS。
|
||||
|
||||
首期链路:网页通知立即唤醒 → 网关暂存待确认投递 → WS 主动推送后台 → 数据库事务保存 → 页面 SSE 通知 → 按当前筛选与页码重新读取。
|
||||
|
||||
- 只处理点赞、评论、关注、转发;私信仍走独立收件箱,不自动互动。
|
||||
- 保留真实 UID 核验、Python 解码 64 位通知 ID、只读通知列表、来源和时间语义、监听开关代次及原去重规则。
|
||||
- 本期其他 HTTP 功能不迁移。已废弃的事件 GET 拉取路径删除,不增加轮询回退。
|
||||
- 保持现有表结构与检查点,不新增数据库更新脚本。未确认投递在网关保留;进程重启根据持久化检查点完整核对平台仍可返回的历史,不能承诺平台已删除通知也能恢复。
|
||||
|
||||
## 通道与消息
|
||||
|
||||
### 浏览器 → 网关
|
||||
|
||||
复用 douyin-pc 已验证的 NoticeFrontier 回调,在网页已建立的连接上读取通知帧,过滤 700/960/961 分组及对应互动类型,不另建抖音私有协议客户端。固定的已登录通知页面执行等待 Promise,有信号立即返回,空闲超时仅用于身份/存活检查,不表示定时调用通知 API。网关取得原始 JSON 后在 Python 解码、按通知 ID 读取详情并沿用 normalize_notice。
|
||||
|
||||
通知信号与初次/周期历史核对使用独立浏览器连接与执行线程,不让完整历史扫描占住实时通道。完整核对仍约每 5 分钟一次,且不截断历史。不能使用扫描头部页代替真实通知信号。
|
||||
|
||||
### 网关 → 后台
|
||||
|
||||
同地址新增 `/v1/channel`,每个网关复用一条 WS,多个监听账号共享连接。首期消息仅包括 subscribe/unsubscribe、投递批次、ack、错误。携带账号环境 alias、订阅标识、运行代次及每次启动独立的 session_id,拒绝旧订阅投递;同一开关代次重连后,旧停止请求也不能停止新会话。账号启动只持有各自生命周期锁,不占共享订阅注册表锁;订阅验证不等待浏览器操作锁;单账号错误不关闭其他账号通道。
|
||||
|
||||
使用成熟 WS 库处理握手、帧、心跳和关闭;不手写协议。接收等待不能持有浏览器 alias 锁;通道读取与账号保存分开,慢账号不阻塞其他账号。只在后台保存成功后确认,未确认批次重连后重送。过载必须显式报错,不静默丢弃事件。
|
||||
|
||||
启动/停止监听首期仍使用已有 HTTP 命令,事件拉取和确认迁移到 WS。连接失效明确记录,重连重新订阅并补送;失败不伪装连接正常。
|
||||
|
||||
### 后台 → 页面
|
||||
|
||||
事务保存成功后发布事件列表变更。复用现有 `/api/creator/updates` SSE 通道和前端 creatorSubscribe,页面收到通知立即刷新当前筛选分页,不直接拼接第一行、不改变排序。首次连接/重连也重新查询数据库,页面断开不影响后台收取。取消页面 5 秒轮询,保留手动刷新,断线明确展示。
|
||||
|
||||
SSE 是列表失效通知而非唯一数据存储;服务重启或页面重连以数据库查询恢复,所以无需另建消息中间件。
|
||||
|
||||
## 封面与慢任务
|
||||
|
||||
事件保存与确认不等待封面、历史补收或未来 AI 分析。封面队列满时,在事件原保存事务写入真实过载原因,不在确认路径额外同步写库;历史核对再尝试下载。封面下载独立处理,成功后清除同一事件的封面错误并发布列表变更;路径由现有作者 UID/作品 ID 本地文件约定解析,不新增路径字段;失败记录真实错误,不阻断事件、不回退远程图片。初次历史与实时投递允许交错;仅全部历史投递确认后推进对应历史检查点,不能要求队列中后来新增的实时投递也已确认。
|
||||
|
||||
## 生命周期与可观测性
|
||||
|
||||
- 开关代次、运行身份仍由现有事务与浏览器核验约束;关闭阻止旧代次写入。
|
||||
- 记录通知信号时间、网关接收/发送、后台保存与确认时间,包含网关、alias、通知 ID/投递 ID、批次数量及连接状态。实时与历史投递以 realtime_signal 区分;跨机时间戳差值标记为 clock_delta(受时钟同步影响),WS 发送至保存确认的往返耗时用网关单调时钟测量,不伪造精确跨机延迟。
|
||||
- 页面断线、WS 断线、通知页身份失败、补收异常、封面错误分别可见,不能把设置开启等同实时正常。
|
||||
- 建议人工验收目标:从网页信号到保存 P95 ≤ 1 秒,到页面可见 P95 ≤ 2 秒。抖音生成通知的耗时另算;这些是待测目标,不是实施后未经真机验证的保证。
|
||||
|
||||
## 测试与完成标准
|
||||
|
||||
先写失败测试再实现。Python 覆盖信号唤醒、实时/历史并行、ACK 边界、重送、错误/停止、真实本机 WS 握手与多订阅;Go 覆盖共享连接、断线/取消、持久化后确认、去重/代次与 SSE 变更;前端覆盖即时刷新、重连/断线提示、清理及原筛选/分页。受改业务单元覆盖率 ≥ 65%;运行现有相关回归、antd lint、web 构建及 diff 检查。不自动编写或运行 E2E。
|
||||
|
||||
没有平台真机通知证据时,只报告代码/单元验证通过,不声称已实现上述真实延迟指标。douyin-pc 只读参考,不改其代码或混入其未跟踪文档。
|
||||
|
||||
## 后续全量 WS 迁移
|
||||
|
||||
1. 在同一 `/v1/channel` 增加有请求 ID 的命令/结果,按功能迁移环境启停、身份/资料、采集、私信、状态等全部业务通信。
|
||||
2. 命令按请求 ID 去重,明确接收、执行完成和结果未确认;断线不自动重发可能产生副作用的操作。文件/截图等明确二进制传输、分块与独立处理,避免大消息阻塞实时事件;不把所有任务放在连接接收线程。
|
||||
3. 每项迁移完成即删除对应 HTTP 路由与客户端,不保留兼容回退。
|
||||
4. 最终删除网关全部 HTTP 业务接口,仅保留 WS 标准握手入口,不增加另一个端口;健康/状态也走 WS。
|
||||
|
||||
本期不提前实现通用 RPC 框架、未使用命令、配置项或新消息中间件。规划随真实实施结果更新。
|
||||
|
||||
## 首期实施与验证记录
|
||||
|
||||
- 已实现网页通知唤醒、同端口共享 WS、保存后确认、异步本地封面、复用 SSE 即时刷新;原事件 GET 拉取、前端 5 秒轮询已移除。
|
||||
- 首期仍保留监听启动/停止及其他网关 HTTP 命令,全量迁移尚未实施,按上节继续。
|
||||
- Python 网关完整单元回归:176 项通过;本期三个业务模块合计覆盖 92%,各模块 83%–97%。
|
||||
- Go 全项目测试通过;PostgreSQL 事件/监听相关测试含 race 检查通过。新增通道业务函数覆盖 65%–100%,封面工作线程 97.7%,监听会话 83.9%,封面结果更新 77.3%。这是受影响业务范围的覆盖,不是整个 Go 项目的覆盖率。
|
||||
- 前端及网页信号 Node 单元测试:19 项通过;事件页覆盖 98.83%;antd lint 无问题,web 构建通过;没有编写或运行 E2E。
|
||||
- 已检查同代次旧会话停止、共享通道最后订阅关闭与新订阅加入、单账号启动/错误隔离、历史与实时并行、封面过载不额外阻塞确认等边界。
|
||||
- 部署需同时更新后台与网关,旧事件协议不保留兼容。保留浏览器 profile、指纹与 Cookie,不通过重建环境或清 Cookie 迁移;暂停账号不会因此恢复运行。
|
||||
- 尚未进行真实抖音通知到数据库、页面的全链路延迟验收;前述 P95 目标仍待人工采样核对,不能以模拟测试、连接成功或通知列表读取成功替代。
|
||||
Reference in New Issue
Block a user