SerpBase 预算感知调度:让采集任务不再「跑一半没额度」
采集中途没额度,是最贵的一种失败:任务跑了一半,数据缺一块,重跑又要从头开始,告警在半夜响。后来我给管道加了「预算感知调度」——任务前查余额、按类型预留额度、余额不足时降级执行。这篇讲这三层的设计。
第一层:准入——不启动,比跑一半便宜
任务启动前,先估算本次需要多少 credits,和余额比:
# catalog.yaml —— 端点目录价(从官方文档同步)
PRICE = {"google/search": 1, "google/news/search": 1, "google/maps/search": 2, "google/maps/detail": 3}
def estimate_cost(tasks: list[dict]) -> int:
"""按目录价估算任务成本(上限,不是实际扣费)"""
return sum(PRICE[t["endpoint"]] for t in tasks)
def preflight(tasks: list[dict]) -> bool:
cost = estimate_cost(tasks)
balance = account_credits()["credits"]
if balance < cost:
alert(f"余额不足:需要 {cost},剩余 {balance},任务未启动")
return False
return True
预估按目录价算,不按实际扣费算——未确认的计费是 unknown,按上限估才安全。余额不够就不启动:任务没跑,数据是「没有」;任务跑一半,数据是「缺一块」,后者更难查。
第二层:预留——并发任务别互相抢
多个任务并发时,各查各的余额会同时通过准入、同时烧光。加一层预留:
import threading
class Budget:
def __init__(self):
self.lock = threading.Lock()
self.reserved = 0
def reserve(self, cost: int) -> bool:
with self.lock:
balance = account_credits()["credits"]
if balance - self.reserved >= cost:
self.reserved += cost
return True
return False
def settle(self, cost: int):
with self.lock:
self.reserved -= cost
任务开始前 reserve,结束后 settle。并发任务的准入判断基于「余额 - 已预留」,互不打架。
第三层:降级——余额低时砍什么
余额不足以跑全部时,按优先级砍,而不是整体失败:
| 优先级 | 内容 | 余额不足时 |
|---|---|---|
| P0 | 核心关键词日报 | 保留 |
| P1 | 竞品监控 | 保留,降频 |
| P2 | 非核心端点(图片、地图详情) | 跳过 |
| P3 | 探索性查询 | 跳过 |
def plan(tasks: list[dict], balance: int) -> list[dict]:
"""按预算裁剪任务,返回可执行部分"""
ordered = sorted(tasks, key=lambda t: t["priority"])
remaining, plan = balance, []
for t in ordered:
cost = PRICE[t["endpoint"]]
if cost <= remaining:
plan.append(t)
remaining -= cost
if skipped := len(tasks) - len(plan):
log.warning("预算不足,跳过 %d 个任务", skipped)
return plan
降级必须显式记录:跳过了什么、为什么。事后复盘时,「那天为什么少了一批数据」要有答案。
和限流的分工
这两层经常被混为一谈,其实管的东西不同:
- credits 限流管「速率」:每秒烧多少,防止触发 429(并发篇的内容)
- 预算管理管「总量」:这个月/今天总共能烧多少,防止中途断粮
一个管节奏,一个管总量,两层都要。
踩坑记录
坑 1:只看总余额。 限时额度到期后余额会跳水——permanent_credits 和 expiring_credits 要分开看,准入判断用能长期持有的那部分。
坑 2:预估按实际扣费算。 计费未确认时 credits_charged 是 null,按它预估会把预算估漏。按目录价上限估。
坑 3:并发不预留。 三个任务同时通过准入,把余额在一次并发里烧光。预留机制补上。
坑 4:降级不记录。 静默跳过的任务,事后没人知道数据为什么缺。跳过要进日志。
坑 5:告警阈值不留余量。 余额等于「预估成本」才告警,等告警响的时候已经来不及买额度了。阈值要留出采购和响应时间。
小结
预算感知调度把「额度管理」从「月底看账单」前移到「每次任务前」:准入防跑一半、预留防并发抢、降级防整体失败。三层都不复杂,但堵住的是采集中最贵的那类事故——不是没数据,是缺一半还不知道为什么。
余额查询端点(GET /account/credits)和计费字段说明见 SerpBase 官方文档。你们怎么管采集预算?评论区聊聊。

浙公网安备 33010602011771号