对接媒体平台、支付平台或 AI 服务时,经常要批量调用第三方 API。最容易犯的错,是只想着把并发拉满。真正稳定的并发调用,要把外部系统当成不可靠依赖。
我的判断: 并发调用的目标不是压榨最大 QPS,而是在媒体限流、网络抖动和局部失败同时存在时,仍然能按业务维度完成、记录和补跑。
并发数要可配置
不同平台限流不同,接口耗时也不同。线程池大小、批量大小、超时时间、重试次数,都应该配置化,而不是写死在代码里。
超时要覆盖整体任务
单次请求要有超时,整批任务也要有超时。否则某几个请求卡住,就会拖垮整个定时任务。
限流重试要退避
遇到限流不要立刻重试。可以用线性退避或指数退避,比如 1 秒、4 秒、9 秒。重试次数到达上限后,要记录失败并继续处理下一批,不要让一个账号拖死整批。
局部失败要可见
批量任务里,某个广告主、某条创意失败,不应该让所有结果都不可用。要记录失败对象、错误原因、请求批次和后续重提入口。
结果写入要幂等
第三方接口可能重复返回,任务也可能补跑。入库时用业务唯一键 upsert,才能让重复执行变成安全动作。
实际场景:媒体账号维度的并发拉取
广告平台里经常要按媒体账号拉取计划、创意、素材、花费等数据。单个媒体账号数据量可能不大,但账号数量一多,如果串行执行,定时任务会拖得很长;如果简单把所有请求并发打出去,又很容易触发媒体限流,甚至把整个任务拖死。更合理的方式是按媒体、账号和接口类型拆分并发边界。
以 B 站、广点通、快手这类媒体接口为例,后端通常会先拿到广告主列表,再按广告主拉取下层数据。并发层级应该控制在广告主维度,而不是每条创意一个线程。这样做的好处是日志、失败重试和任务补偿都能围绕广告主展开;某个广告主失败时,不影响其他广告主的数据入库,也方便后续单独补跑。
并发任务一定要有整体超时。单次 HTTP 请求配置超时只能防止一个请求卡住,但整批任务还可能因为某些 future 没返回而一直等待。可以给整批 CompletableFuture.allOf 加超时,或者在任务调度层设置最大执行时间。超时后不要简单丢弃结果,而要记录哪些广告主成功、哪些广告主失败、失败原因是什么,下次补跑只处理失败部分。
限流重试不能无限重试。媒体接口常见的错误包括 token 过期、请求频率过高、参数非法、媒体侧服务异常。token 过期应该先刷新授权;限流可以退避后重试;参数非法应该直接记录失败;媒体侧异常可以进入补偿。把这些错误都当成“重试一下”会让任务更慢,也会掩盖真正的字段映射问题。
入库阶段要用 upsert 或唯一键兜底。花费数据可以按日期、广告主、计划、创意等维度建立业务唯一键;创意素材关系可以按媒体创意 ID 和素材 ID 去重;账号、计划、单元等基础数据也应该允许重复拉取覆盖。这样并发拉取和历史补跑就不会互相冲突。
最后,批量并发不是为了跑得越快越好,而是为了在有限时间窗口里稳定完成任务。一个成熟的第三方 API 调用链路,需要能回答这些问题:这次任务处理了多少账号,失败了哪些账号,是否触发限流,平均耗时多少,写入了多少条,是否可以安全补跑。能回答这些问题,才算真正把并发调用做成了生产能力。