基于 AWS 的无服务器数据管道
定价研究需要稳定地从市场获取二手零件的商品信息。我在 AWS 上设计、构建并运维了一个事件驱动的数据管道。它在测试中达到了每天 500 万条商品信息的目标,而且是在最小规格的数据库实例上。
- 我的角色
- 我在一位资深工程师离职后接手了 AWS 基础设施,然后独自设计、构建并运维了这个管道。
- 成果
- 测试中每天 500 万条商品信息。在每日运行的生产环境中,数据源的访问频率限制使它每天只能采集约 5000 条。
- 技术栈
- AWS Lambda(Docker 镜像)、SQS、EventBridge、RDS PostgreSQL、RDS Proxy、CloudWatch、SNS、ECR、Python
- 范围
- 公司经营的 7 个汽车品牌在交易平台上的商品信息,每天采集
问题
公司的价格预测工作需要广泛且新鲜的市场数据。目标是针对公司经营的 7 个汽车品牌,每天采集约 500 万条商品信息。
这个数据管道必须稳定可靠、控制在预算之内,并能应对一个限制读取速度的数据源。
架构
- 调度 EventBridge 启动每一轮采集。一个小型 Lambda 函数重置当天的目标。
- 队列 SQS 存放待处理的任务,取代了早期把数据库当作队列的设计。失败的消息会进入死信队列。
- 工作函数 打包成 Docker 镜像的 Lambda 函数并行抓取和解析页面。
- 存储 商品信息写入 RDS PostgreSQL,并通过 RDS Proxy 实现连接池。写入是增量且原子的。
- 告警 CloudWatch 指标和告警,通过 SNS 发送通知。
- 重试与恢复安全地认领目标、重试、恢复卡住的任务,以及重新投递 DLQ 消息
- 监控 Lambda只读任务,把数据库健康状况上报到 CloudWatch
- 健康检查可在任何操作系统上运行的 AWS CLI 脚本
宁可减速,也不宕机
限制请求频率的数据源会进行反制。这个管道就是为此而设计的:
- 熔断机制。当页面开始被拦截时,工作函数会暂停,而不是更猛烈地重试。共享的冷却状态保存在 PostgreSQL 中。
- 自适应限流。请求时间带有随机抖动;当数据源开始反制时,管道会自动退避;冷却结束后,工作函数会逐步恢复负载。
- 安全并发。工作函数能安全地认领目标、重试并恢复卡住的任务,因此许多工作函数可以并行运行而互不冲突。
可观测性与运维
- 只对重要的事告警。当编排器停止、工作函数失败、Lambda 对它们限流、队列积压或消息滞留过久、页面被拦截、数据库写入失败,或者每天的商品信息数量下降时,CloudWatch 会提醒我。
- 健康检查。AWS CLI 脚本可以在任何机器上检查这条管道,另有一个小型只读 Lambda 函数把数据库健康状况上报到 CloudWatch。
- 书面操作规程。它们说明如何限制或停止 Lambda 函数、如何清空队列,这样失控的任务就无法推高成本。
资深评审后的加固
一位资深工程师评审了这条管道,我根据评审意见做了加固:
- 每条数据库查询都设有超时,每次写入都是原子的。
- 工作函数只抓取白名单上的 URL,并拒绝过大的响应。
- 返回可疑空页面的请求会快速失败,每次失败都连同上下文一起记录。
- 我把开销很大的
COUNT(*)从热点路径中移除,并添加了索引维护工具。 - ECR 镜像和 CloudWatch 日志的保留策略控制了存储成本。
结果
- 500 万/天目标值,测试中在最小规格的 RDS 实例上达到
- 约 5000/天生产环境中数据源的限制所允许的速率
- 每日在生产环境中采集、存储和告警
- 7 个品牌覆盖公司的定价研究
经验总结:这条管道每天能处理 500 万条商品信息,但数据源承受不了。数据源收紧访问频率限制后,可持续的速率降到了每天约 5000 条商品信息,这成了常态运行速率。在这种规模下,设定上限的是数据源,而不是基础设施,因此退避机制必须从第一天起就纳入设计。
本页面略去了雇主代码、内部数据和私有网址,也不透露数据来源。