16 KiB
需求文档:ETL 统一请求编排与线程模型改造
简介
对飞球 Connector ETL 系统(apps/etl/connectors/feiqiu/)的请求编排和线程模型进行全局统一化改造。当前系统所有 ODS 任务在 BaseOdsTask.execute() 中同步串行执行 API 请求、数据处理和数据库写入,无限流机制、无取消信号、无并行处理能力。本次改造建立统一的请求编排框架,所有 21 个 ODS 任务迁移到"串行请求 + 异步处理 + 单线程写库"架构,支持全局限流(5-20 秒随机间隔)、外部取消信号、可选的"列表→详情"二级拉取模式,并对 DWD 层加载进行多线程优化。
术语表
- ETL_System:飞球 Connector ETL 系统(
apps/etl/connectors/feiqiu/),负责从飞球 SaaS API 拉取数据并加载到 PostgreSQL 的 ODS → DWD → DWS 各层 - Unified_Pipeline:统一请求编排框架,所有 ODS 任务共用的"串行请求 + 异步处理 + 单线程写库"执行引擎
- Request_Scheduler:全局请求调度器,负责将所有 API 请求统一排队、串行发送、遵循限流规则
- Rate_Limiter:请求间隔控制器,控制相邻两次 API 请求之间的随机等待时间(默认 5-20 秒均匀分布),防止触发上游风控
- Processing_Pool:异步处理线程池,多个工作线程并行消费 API 响应数据,执行字段提取、数据清洗、content_hash 计算等 CPU 密集操作
- Write_Worker:单线程写入工作器,汇总所有处理完成的结果,统一执行数据库写入操作,保证写入串行化
- CancellationToken:取消令牌,外部组件(如 Admin-web、CLI)通过设置该令牌通知正在执行的任务中断
- ODS_Task:ODS 层数据拉取任务的统称,当前共 21 个,通过
OdsTaskSpec数据类定义、_build_task_class()动态生成任务类 - Detail_Mode:二级详情拉取模式,在列表接口拉取完成后逐条调用详情接口获取更丰富的数据,属于可选能力
- Pipeline_Config:管道配置,包含 worker 数、队列大小、批量写入阈值、限流间隔等参数,不同任务可独立配置
- BaseOdsTask:当前 ODS 任务基类(
tasks/ods/ods_tasks.py),封装时间窗口解析、API 分页拉取、结构感知写入、快照软删除等核心逻辑 - TaskExecutor:任务执行器(
orchestration/task_executor.py),封装单个任务的执行生命周期(游标管理、运行记录、数据源路由) - FlowRunner:流程编排器(
orchestration/flow_runner.py),编排多层任务(ODS → DWD → DWS → INDEX)的执行顺序 - DWD_Loader:DWD 层加载任务(
DwdLoadTask),通过_merge_dim_scd2()执行 SCD2 合并,将 ODS 原始数据转换为维度/事实表
需求
需求 1:统一请求调度器
用户故事: 作为 ETL 运维人员,我希望所有 API 请求通过统一的调度器串行发送并遵循限流规则,避免触发上游风控导致 IP 封禁或数据拉取失败。
验收标准
- THE Request_Scheduler SHALL 维护一个全局请求队列,所有 ODS 任务的 API 请求统一进入该队列排队等待发送
- THE Request_Scheduler SHALL 严格串行发送请求:等待上一个请求的 HTTP 响应完整返回后,再等待限流间隔,然后发送队列中的下一个请求
- THE Rate_Limiter SHALL 在每两个相邻请求之间插入 5 至 20 秒之间的随机等待时间(均匀分布)
- THE Rate_Limiter SHALL 支持通过 Pipeline_Config 调整最小间隔(默认 5 秒)和最大间隔(默认 20 秒),不同任务可配置不同的间隔范围
- IF Rate_Limiter 初始化时最小间隔大于最大间隔,THEN THE Rate_Limiter SHALL 抛出
ValueError并包含描述性错误信息 - WHEN 同一次 FlowRunner 执行中包含多个 ODS 任务时,THE Request_Scheduler SHALL 按任务注册顺序依次处理每个任务的请求,同一时刻仅有一个 HTTP 请求在途
- THE Request_Scheduler SHALL 在每个请求完成后记录请求耗时、响应状态码和目标 endpoint 到日志
需求 2:异步处理与单线程写库架构
用户故事: 作为 ETL 运维人员,我希望 API 响应数据的处理(字段提取、清洗、hash 计算)能与请求发送并行执行,同时保证数据库写入的串行化,在不增加 API 压力的前提下提升整体吞吐。
验收标准
- THE Unified_Pipeline SHALL 在每个 API 请求的响应返回后,立即将响应数据提交到 Processing_Pool 的任务队列,不阻塞 Request_Scheduler 的限流等待计时
- THE Processing_Pool SHALL 支持多个工作线程并行消费处理队列中的响应数据,执行字段提取、数据清洗、content_hash 计算、record 层合并等操作
- THE Processing_Pool 的工作线程数量 SHALL 通过 Pipeline_Config 配置(默认值 2),不同任务可独立配置
- THE Write_Worker SHALL 作为单独的线程运行,从处理完成队列中消费数据,统一执行数据库 INSERT/UPSERT 操作
- THE Write_Worker SHALL 支持批量写入:累积到配置的阈值(默认 100 条记录)或等待超时(默认 5 秒)后执行一次批量写入
- THE Write_Worker 的批量写入阈值和等待超时 SHALL 通过 Pipeline_Config 配置,不同任务可独立配置
- WHEN 所有请求发送完毕后,THE Unified_Pipeline SHALL 等待 Processing_Pool 和 Write_Worker 全部完成后再返回最终结果
- THE Unified_Pipeline SHALL 保证多线程读库操作的安全性:Processing_Pool 中的工作线程可并行读取数据库(如查询最新 content_hash),使用独立的只读数据库连接
需求 3:外部取消信号支持
用户故事: 作为 ETL 运维人员,我希望能通过 Admin-web 或 CLI 发送取消信号中断正在执行的 ODS 任务,避免长时间运行的任务无法停止。
验收标准
- THE CancellationToken SHALL 提供线程安全的
cancel()方法和is_cancelled属性,供外部组件触发取消 - WHEN CancellationToken 被触发时,THE Request_Scheduler SHALL 在当前请求的限流等待期或响应等待期中断,不再发起后续请求
- WHEN CancellationToken 被触发时,THE Processing_Pool SHALL 完成当前已提交到队列中的所有数据处理任务,不丢弃已入队的数据
- WHEN CancellationToken 被触发时,THE Write_Worker SHALL 将所有已处理完成的数据写入数据库后再退出,保证已处理数据的持久化
- WHEN 任务因取消信号中断时,THE Unified_Pipeline SHALL 返回部分完成的统计结果(已完成的请求数、已处理的记录数、已写入的记录数),任务状态标记为
CANCELLED - THE CancellationToken SHALL 支持超时自动取消:可在创建时指定最大执行时间(秒),超时后自动触发取消信号
- IF CancellationToken 在任务启动前已处于取消状态,THEN THE Unified_Pipeline SHALL 立即返回空结果,不发送任何请求
需求 4:Pipeline 配置体系
用户故事: 作为 ETL 运维人员,我希望线程模型的各项参数(worker 数、队列大小、批量写入阈值、限流间隔)足够灵活,不同接口可以有不同的配置,以适应不同 API 的特性。
验收标准
- THE Pipeline_Config SHALL 支持以下可配置参数:Processing_Pool 工作线程数(
workers,默认 2)、处理队列容量(queue_size,默认 100)、Write_Worker 批量写入阈值(batch_size,默认 100)、Write_Worker 等待超时秒数(batch_timeout,默认 5.0)、Rate_Limiter 最小间隔秒数(rate_min,默认 5.0)、Rate_Limiter 最大间隔秒数(rate_max,默认 20.0) - THE Pipeline_Config SHALL 遵循现有配置分层体系(根
.env<.env.local< 环境变量 < CLI 参数),通过AppConfig的pipeline.*命名空间读取 - THE Pipeline_Config SHALL 支持任务级覆盖:通过
pipeline.<task_code>.*命名空间为特定任务指定独立配置,未指定时回退到pipeline.*全局默认值 - IF Pipeline_Config 中
workers小于 1 或queue_size小于 1,THEN THE Unified_Pipeline SHALL 抛出ValueError并包含描述性错误信息 - IF Pipeline_Config 中
batch_size小于 1,THEN THE Unified_Pipeline SHALL 抛出ValueError并包含描述性错误信息 - THE Pipeline_Config SHALL 支持运行时通过 CLI 参数
--pipeline-workers、--pipeline-batch-size、--pipeline-rate-min、--pipeline-rate-max覆盖全局默认值
需求 5:现有 ODS 任务迁移
用户故事: 作为 ETL 开发者,我希望现有 21 个 ODS 任务全部迁移到统一管道框架上,保持功能完全等价,不丢失任何现有能力。
验收标准
- THE Unified_Pipeline SHALL 完整保留 BaseOdsTask 的所有现有功能:时间窗口解析(
_resolve_window)、窗口分段(build_window_segments)、API 分页拉取(iter_paginated)、结构感知写入(_insert_records_schema_aware)、快照软删除(_mark_missing_as_deleted)、content_hash 去重(skip_unchanged) - THE Unified_Pipeline SHALL 保留 OdsTaskSpec 数据类的所有现有字段定义,迁移后的任务通过相同的
OdsTaskSpec实例配置 - WHEN 迁移完成后,THE ETL_System 对每个 ODS 任务执行相同输入数据时 SHALL 产生与迁移前完全相同的数据库写入结果(相同的 inserted/updated/skipped 计数和相同的记录内容)
- THE Unified_Pipeline SHALL 保留现有的
endpoint_routing逻辑(recent/former 路由拆分),迁移后的请求路由行为与现有系统一致 - THE Unified_Pipeline SHALL 保留现有的
source_file、source_endpoint、fetched_at等元数据写入逻辑 - THE Unified_Pipeline SHALL 兼容现有的
TaskExecutor执行生命周期(游标管理、运行记录、数据源路由),迁移后 TaskExecutor 无需修改调用方式 - WHEN 迁移完成后,THE TaskRegistry 中所有 21 个 ODS 任务的注册代码和元数据 SHALL 保持不变
需求 6:二级详情拉取模式
用户故事: 作为 ETL 开发者,我希望统一管道框架支持"列表接口拉完后逐条调详情"的二级拉取模式,以便团购详情等需要二次请求的业务能在框架内实现。
验收标准
- THE Unified_Pipeline SHALL 支持可选的 Detail_Mode:在列表接口的所有分页数据拉取并写入 ODS 完成后,从已写入的记录中提取 ID 列表,逐条调用详情接口
- THE OdsTaskSpec SHALL 新增可选字段支持 Detail_Mode 配置:详情接口 endpoint、详情请求参数构造函数、详情数据目标表名、详情数据的 data_path 和 list_key
- WHEN OdsTaskSpec 未配置 Detail_Mode 相关字段时,THE Unified_Pipeline SHALL 跳过详情拉取阶段,行为与纯列表拉取模式完全一致
- THE Detail_Mode 的详情请求 SHALL 通过 Request_Scheduler 统一排队,遵循与列表请求相同的限流规则
- IF 详情接口对某个 ID 返回错误或超时,THEN THE Unified_Pipeline SHALL 记录错误日志(含 ID 和错误信息)并继续处理下一个 ID,不中断整体流程
- WHEN 详情拉取完成后,THE Unified_Pipeline SHALL 在任务执行结果中包含详情拉取的统计信息(详情成功数、详情失败数、详情跳过数),与列表拉取统计分开记录
需求 7:DWD 层多线程优化
用户故事: 作为 ETL 运维人员,我希望 DWD 层加载任务能利用多线程并行处理多张表的 SCD2 合并,缩短 DWD 层的整体执行时间。
验收标准
- THE DWD_Loader SHALL 支持多线程并行执行多张 DWD 表的 SCD2 合并操作,每张表的合并在独立线程中运行
- THE DWD_Loader 的并行线程数 SHALL 通过配置参数控制(默认值 4),通过
AppConfig的dwd.parallel_workers读取 - THE DWD_Loader SHALL 保证每张表的 SCD2 合并操作在独立的数据库事务中执行,单张表失败不影响其他表的处理
- WHEN 某张 DWD 表的 SCD2 合并失败时,THE DWD_Loader SHALL 记录错误日志(含表名和错误信息),将该表标记为失败,继续处理其他表
- THE DWD_Loader SHALL 在所有表处理完成后返回汇总结果:成功表数、失败表数、每张表的 inserted/updated 计数
- THE DWD_Loader 的现有
_merge_dim_scd2()方法 SHALL 保持不变,多线程优化仅在调度层面并行调用该方法
需求 8:可观测性与监控
用户故事: 作为 ETL 运维人员,我希望统一管道框架提供充分的运行时可观测性,便于监控执行状态和排查问题。
验收标准
- THE Unified_Pipeline SHALL 在任务执行过程中记录以下关键指标到日志:当前请求队列深度、Processing_Pool 活跃线程数、Write_Worker 待写入队列深度、已完成请求数/总请求数
- THE Unified_Pipeline SHALL 在任务完成后输出执行摘要:总耗时、请求阶段耗时、处理阶段耗时、写入阶段耗时、各阶段的记录数统计
- WHEN Processing_Pool 的任务队列达到容量上限时,THE Unified_Pipeline SHALL 记录警告日志,Request_Scheduler 暂停发送新请求直到队列有空位(背压机制)
- WHEN Write_Worker 的待写入队列积压超过
queue_size * 2时,THE Unified_Pipeline SHALL 记录警告日志 - THE Unified_Pipeline SHALL 与现有的
EtlTimer集成,在 FlowRunner 的计时报告中体现各 ODS 任务的请求/处理/写入阶段耗时
需求 9:错误处理与容错
用户故事: 作为 ETL 运维人员,我希望统一管道框架具备完善的错误处理机制,单个请求或记录的失败不影响整体任务的执行。
验收标准
- IF 单个 API 请求失败(HTTP 错误、超时、API 返回错误码),THEN THE Request_Scheduler SHALL 按现有
APIClient的重试策略(最多 3 次,指数退避)重试,重试耗尽后记录错误并继续处理下一个请求 - IF Processing_Pool 中某条记录的处理抛出异常,THEN THE Processing_Pool SHALL 记录错误日志(含记录标识和异常信息),将该记录标记为处理失败,继续处理队列中的其他记录
- IF Write_Worker 执行批量写入时发生数据库错误,THEN THE Write_Worker SHALL 回滚当前批次的事务,记录错误日志(含批次大小和错误信息),将该批次的记录标记为写入失败
- WHEN 任务执行完成后,THE Unified_Pipeline SHALL 在执行结果中汇总所有错误:请求失败数、处理失败数、写入失败数,以及每个失败项的错误摘要
- IF 任务执行过程中连续失败次数超过配置阈值(默认 10 次),THEN THE Unified_Pipeline SHALL 主动中断任务执行,将任务状态标记为
FAILED,避免无效重试浪费时间 - THE Unified_Pipeline SHALL 保留现有 BaseOdsTask 的事务管理语义:每个窗口分段(segment)的数据在该分段全部处理完成后统一 commit,分段失败时 rollback 该分段
需求 10:Admin-web 日志输出优化
用户故事: 作为 ETL 运维人员,我希望在 Admin-web 管理后台查看 ETL 执行日志时,各个任务的日志按任务分组、有序展示,避免多任务并行执行时日志行交叉混乱导致难以阅读和排查问题。
验收标准
- THE ETL_System SHALL 为每个 ODS 任务的执行日志添加任务标识前缀(任务代码),使日志行可按任务归属区分
- THE Admin-web SHALL 支持按任务代码过滤和分组展示 ETL 执行日志,用户可选择查看单个任务的日志或全部日志
- THE Unified_Pipeline SHALL 在多线程环境下保证日志写入的原子性:每条日志消息作为完整的一行输出,不会被其他线程的日志截断或插入
- THE ETL_System SHALL 为每个任务维护独立的日志缓冲区,任务完成后将该任务的完整日志按时间顺序一次性输出到 Admin-web,避免执行过程中不同任务的日志行交叉
- THE Admin-web SHALL 在 ETL 执行结果页面中按任务分段展示日志:每个任务的日志折叠为独立区块,展开后显示该任务的完整执行日志(含时间戳、日志级别、消息内容)
- WHEN 多个 ODS 任务在同一次 FlowRunner 执行中运行时,THE Admin-web SHALL 在顶部展示任务执行时间线概览(每个任务的开始时间、结束时间、状态),用户可点击跳转到对应任务的日志区块