# **🎯 系统级异步架构风险剖析与演进技术蓝图 (V2.0 定稿版)** **文档性质:** 架构决策与研发执行规范 **面向受众:** 架构师、技术负责人、中高级后端研发 **背景:** 随着 ASL 工具 3 等批量耗时任务的引入,系统正从“单体异步”转向“分布式扇出 (Fan-out)”架构。 **核心目标:** 解决多实例部署下的计数丢失、状态撕裂与事件孤岛风险,建立工业级的分布式任务处理标准。 ## **💡 一、 问题的本质:架构的分水岭** 在分布式系统设计中,异步任务的复杂度随业务颗粒度呈指数级跃迁。我们必须将任务划分为两个完全不同的等级: | 维度 | Level 1:单体任务 (现有标准) | Level 2:分布式扇出 (演进方向) | | :---- | :---- | :---- | | **典型案例** | 工具 C 解析 1 个 Excel | 工具 3 批量提取 100 篇文献 | | **工作流** | 1 个触发 \-\> 1 个 Worker \-\> 结束 | 1 个触发 \-\> 1 个 Manager \-\> N 个子 Worker | | **多实例安全性** | **高**。依靠 pg-boss 行锁。 | **低**。子任务跨机器,必须处理原子性聚合。 | | **容错代价** | **小**。失败重跑 40 秒。 | **极大**。若无扇出,第 99 篇失败会导致前 98 篇全废。 | | **系统影响** | 局部影响。 | 全系统级风险(API 熔断、数据库连接耗尽)。 | ## **🔍 二、 核心风险深度剖析 (The Risks)** 在多 SAE 实例(Multi-Pod)部署环境下,若不严格执行 V2.0 规范,将面临以下系统性崩溃风险: ### **1\. 统计数据的“幻影覆盖” (Race Condition)** * **现象:** 当 100 个子任务在不同 Pod 同时完成时,如果采用 count \= count \+ 1 的读写逻辑,多个进程会读到相同的旧值并覆盖写入。 * **后果:** 进度条卡死、统计金额错误、任务永远无法触发“完成”回调。 ### **2\. “最后一个人关灯”难题 (The Terminator Problem)** * **现象:** 缺乏全局协调逻辑。Manager 派发完任务就结束了,子任务各自为政。 * **后果:** 系统不知道“这组任务”什么时候算真正结束,无法自动触发后续的报告生成或通知发送。 ### **3\. SSE 实时日志的“物理隔绝” (Event Silos)** * **现象:** 用户的浏览器连接在 Pod A,但执行任务的 Worker 运行在 Pod B。Pod B 产生的日志在 Pod A 的内存里完全不存在。 * **后果:** 页面显示“处理中”但日志区一片空白,用户因感知不到进度而频繁刷新,造成更大的后端冲击。 ## **🛠️ 三、 全系统演进执行建议 (The Guidelines)** 为了消除上述风险,全平台所有异步模块必须强制对齐以下 4 项架构红线: ### **🚨 规范 1:强制执行数据库级原子操作** 禁止在异步代码中使用任何内存层面的数学运算来更新数据库。 * **错误写法:** data: { count: task.count \+ 1 } * **正确标准 (Prisma):** \`\`\`typescript await prisma.task.update({ where: { id: taskId }, data: { successCount: { increment: 1 } } }); ### **🚨 规范 2:引入“Last Child Wins”收口机制** 在分布式环境下,必须由最后一个完成任务的进程负责“关灯(翻转父任务状态)”。 * **执行逻辑:** 每个子任务在执行完【原子递增】后,必须同步读取更新后的结果。 * **判定公式:** if (updatedTask.successCount \+ updatedTask.failedCount \=== updatedTask.totalCount) * **后续:** 若条件成立,该实例负责将 Task.status 改为 completed 并触发 SSE 完成事件。 ### **🚨 规范 3:从 EventEmitter 转向跨实例消息总线** 彻底封杀多实例环境下的单机 EventEmitter 实时推送。 * **Postgres-Only 方案:** 充分利用 PostgreSQL 的 LISTEN/NOTIFY 机制。 * **工作流:** 1\. Worker 发送 NOTIFY channel\_name, payload。 2\. 所有 API 节点在启动时 LISTEN 该频道。 3\. 收到通知的 API 节点检查本地内存,若存在对应 taskId 的 SSE 客户端,则执行推送。 ### **🚨 规范 4:极端场景下的“背压限制”与超时阻断** * **全局限流:** 针对昂贵的外部 API(如 MinerU),必须使用 pg-boss 的 teamConcurrency 进行数据库级全局限流,严禁使用单机 P-Queue。 * **超时阻断:** 所有跨网络请求必须强制设置 timeout(建议 ≤ 90s),防止外部接口假死扣住 pg-boss 队列名额,导致系统死锁。 ## **📅 四、 路线图:如何平滑过渡?** 1. **实验场 (M1/M2):** 以“ASL 工具 3”作为首个 V2.0 规范试点,沉淀出通用的 FanOutHelper 和 ListenNotifyService。 2. **基建化:** 将上述成功代码抽离,封装入 common/jobs 和 common/streaming 能力层。 3. **全量覆盖:** 发布《Postgres-Only 异步任务处理指南 v2.0》,要求后续所有涉及“批量处理”的模块(如 IIT Agent、批量报告生成)严格照此执行。 ## **🏁 架构师寄语** 工具 3 的出现不是增加了复杂度,而是帮我们掀开了分布式环境下一直被掩盖的风险盖子。 **与其在上线后通过熬夜排查“幽灵 Bug”,不如现在多审核一次,在文档阶段就打好地基。** 请研发团队认真研读此蓝图,这套规范将让我们的系统从“能跑通”进化到“金身不坏”。