## 3. 队列分层架构:系统分层、每层职责、层间接口定义 ### 3.1 分层设计原则 队列系统若不分层,会导致职责耦合、扩展困难、故障扩散。DownloadManager 采用**四层垂直切割**,每层只关注一个正交领域: - **隔离**:存储与调度互不依赖 - **独立演进**:替换队列后端不影响上层逻辑 - **可观测**:每层只暴露必要接口,内部细节不泄露 - **信任边界**:控制流严格单向,数据流经约定契约 | 层名 | 核心职责 | 典型实现 | |------|----------|----------| | **应用接入层 (API Layer)** | 接收任务提交、鉴权、参数校验、返回任务ID | REST / gRPC | | **队列管理层 (Queue Manager)** | 优先级排序、去重、延迟投递、状态变更 | 多级优先级队列 + 延迟桶 | | **调度执行层 (Scheduler)** | 按策略分发任务给 Worker、限流、重试 | 令牌桶 + 轮询 + 回退 | | **持久化层 (Persistence)** | 任务存储、恢复、归档 | MySQL / LevelDB | --- ### 3.2 分层架构图 (内联 SVG)
DownloadManager 队列四层架构 应用接入层 (API Layer) 接收任务 | 鉴权校验 | 参数标准化 | 返回 Ticket 接口:SubmitTask / QueryStatus / CancelTask 队列管理层 (Queue Manager) 优先级插入 | 唯一性去重 | 延迟投递桶 状态机:Pending → Ready → Delayed → Running 接口:Enqueue / Dequeue / Peek / UpdateStatus 调度执行层 (Scheduler) Worker 分配 | 限流控制 (令牌桶) | 重试队列 接口:Dispatch(worker_id) / Retry / Heartbeat 持久化层 (Persistence) 任务落盘 | 崩溃恢复 | 过期归档 接口:Save / Load / Delete / Archive 数据流(双向:写入/读取) 控制流 数据流
--- ### 3.3 层间接口定义 每层对外暴露的接口应满足**最少信息、幂等性、可重入**原则。 | 接口 | 源层 | 目标层 | 输入示例 | 输出示例 | 备注 | |------|------|--------|----------|----------|------| | `Enqueue(task)` | API | Queue | `{id, url, priority, maxRetry}` | `{ticket, queuePos}` | 去重由 Queue 层回查 Persistence | | `Dequeue(workerId, capacity)` | Scheduler | Queue | `{workerId, maxBatch}` | `[]task` | 返回 Ready 状态任务,返回后状态变 Running | | `Save(task)` | Queue | Persistence | `{task, state}` | `ok` | 每次状态变更时同步落盘 | | `Load(ticket)` | Queue | Persistence | `{ticket}` | `taskOrNull` | 崩溃恢复时批量加载 | >
**接口契约重点**:Queue 层不直接调用 Worker,Scheduler 层不接触数据库。所有跨层调用必须通过显式接口,禁止私自绕过。
--- ### 3.4 数据流与控制流 **控制流方向**(单向信任链): API → Queue (校验通过后) → Scheduler (可调度时) → Worker (实际执行) **数据流方向**(双向但有限权): - 写入:Queue 接收到任务后,先写入 Persistence 再返回成功(写后日志)。 - 读取:Scheduler 从 Queue 拉取任务时,Queue 先读取 Persistence 中的数据到内存缓存,再返回给 Scheduler。 **信任链路**:每层只信任上一层发出的接口数据。例如 Scheduler 不信任 Worker 自行上报的完成时间,必须由 Queue 层根据状态机回写最终状态。 --- ### 3.5 选型权衡 | 决策点 | 方案A | 方案B | 推荐及理由 | |--------|-------|-------|-----------| | Queue 内核 | 纯内存 (Redis List) | 自建优先级堆 (RBTree) | 推荐方案B:规则灵活、去重可控,Redis 适合简单 FIFO 但难以处理优先级变更 | | 持久化策略 | 每次操作全量刷盘 | 批量异步刷盘 + WAL | 推荐异步批量:性能与可靠性的折中,延迟控制在 10ms 内 | | 调度策略 | 简单轮询 | 加权公平队列 (WFQ) | 推荐 WFQ:避免低优先级任务被饿死,且可支持权重自定义 | --- ### 3.6 风险点 1. **队列堆积阻塞**:当 Worker 处理速度低于入队速度时,内存中 Ready 队列可能占满。 - 缓解:设置入队限流,队列长度超阈值时返回 429;启用背压机制通知 API 层减速。 2. **持久化写入延迟导致丢失**:异步刷盘在崩溃时可能丢失最近一批任务。 - 缓解:关键任务(高优先级)可配置同步写入,牺牲部分性能。 3. **层间依赖环**:若 Scheduler 依赖 Queue,Queue 又依赖 Scheduler 的状态回查,则形成环形依赖。 - 缓解:严格约定状态回查只能由 Scheduler → Queue → Persistence,禁止反向调用。 4. **死锁**:多个 Scheduler 同时 Dequeue 时若锁粒度不当,可能死锁。 - 缓解:使用乐观锁 + 重试机制,避免长事务。 --- ### 3.7 落地步骤 | 阶段 | 任务 | 输出 | |------|------|------| | 1 | 定义每层接口规范(Proto 或 JSON Schema) | 接口文档 + mock 测试桩 | | 2 | 实现 Persistence 层:选型数据库 + 表结构 | 可独立运行的存储模块 | | 3 | 实现 Queue 层:内存优先级堆 + 延迟桶 + 持久化桥接 | 单机可通过单元测试的队列核心 | | 4 | 实现 Scheduler 层:Worker 管理 + 限流 + 重试 | 可调度 3 个 Worker 的调度器 | | 5 | 集成 API 层:提供 HTTP/gRPC 入口 | 返回有效 Ticket 的全链路闭环 | | 6 | 压测 + 故障注入(模拟崩溃、网络分区) | 恢复 RTO ≤ 30s,RPO ≤ 1 条任务 | --- ### 3.8 验收标准 >
**通过条件**:所有验收项必须在分布式部署下(至少 2 节点)通过。
1. **分层隔离测试**:替换 Persistence 层为内存模拟实现后,Queue 功能应正常运行,无需修改上层代码。 2. **接口契约测试**:每层接口的输入输出严格符合定义,非法输入返回明确定义的错误码。 3. **状态机正确性**:任务状态流转(Pending→Ready→Delayed→Running→Done/Failed)不出现非法跳转。 4. **崩溃恢复**:随机 Kill 一个节点后,已提交的任务在 30 秒内被重新调度,不丢失任务(RPO=0)。 5. **优先级保序**:高优先级任务(priority=1)始终比低优先级(priority=10)先被调度,延迟不超过 100ms。