SerpBase 多品类采集:并发要按 credits 限流

以前管道里全是 1-credit 的搜索端点,「控并发」基本等于「控 QPS」,一个令牌桶就完事。后来 catalog 扩到 39 个端点、6 个大类,价格从 1 到 3 credits 不等——地图详情一次 3 credits,是搜索的三倍。这时冒出一个问题:并发数控制得住请求数,控制不住成本。

问题:请求级限流的盲区

原来的令牌桶按「请求数」计量:每秒放 5 个请求,跑满就是 5 个。混合品类之后,同样 5 个并发:

  • 5 个搜索(1 credit)= 5 credits/秒
  • 5 个地图详情(3 credits)= 15 credits/秒

成本差三倍,限流器却毫无察觉。请求数是表象,credits 才是成本。

方案:令牌桶改成按 credits 计量

import threading, time

class CreditLimiter:
    """按 credits 计量的令牌桶"""
    def __init__(self, credits_per_sec: float, burst: int = 10):
        self.rate = credits_per_sec
        self.burst = burst
        self.tokens = float(burst)
        self.updated = time.monotonic()
        self.lock = threading.Lock()

    def acquire(self, cost: int) -> None:
        while True:
            with self.lock:
                now = time.monotonic()
                self.tokens = min(self.burst, self.tokens + (now - self.updated) * self.rate)
                self.updated = now
                if self.tokens >= cost:
                    self.tokens -= cost
                    return
                wait = (cost - self.tokens) / self.rate
            time.sleep(wait)

用法:请求之前先按「这个端点的价格」准入。

def fetch(endpoint: str, payload: dict) -> dict:
    limiter.acquire(PRICE_TABLE[endpoint])     # 按成本准入,不按请求数
    resp = session.post(f"{BASE}/{endpoint}", json=payload, timeout=(5, 30))
    return resp.json()

目录价表:价格是配置,不是常量

# catalog.yaml —— 端点一多,硬编码必过期
google/search: 1
google/image/search: 1
google/news/search: 1
google/maps/search: 2
google/maps/detail: 3
amazon/product: 2
web/scrape: 1

这份表从官方文档拉取、随文档更新。价格调整时改配置,不动代码——这是多端点管道能长期维护的前提。

和退避、429 的配合

credits 限流管「匀速消耗」,429 处理管「上游拒绝」,两层各司其职:

  • 收到 429:把 credits_per_sec 降半——注意降的是速率,不是并发数
  • 恢复后加性升回(每次 +10%),别一步拉满
  • 指数退避的重试请求同样走 limiter——重试也是真实成本

观测:按端点记账

响应里的 credits_charged 是实际扣费,落库后按端点聚合:

SELECT endpoint, SUM(credits_charged) AS credits, COUNT(*) AS calls
FROM request_log
GROUP BY endpoint ORDER BY credits DESC;

这张表回答一个常见问题:「预算到底被哪个端点吃掉了?」多品类管道里,答案经常出人意料——你可能以为搜索是大头,其实地图详情和电商查询悄悄占了一半。

另外注意:credits_charged 可能为 null(计费未确认),对账要按窗口做,把未知项当风险敞口,别强行按 0 记账。

踩坑记录

坑 1:按请求数限流。 混合品类后成本失控,限流器却显示「一切正常」——它只看请求数,不看价格。

坑 2:价格硬编码。 39 个端点写死在代码里,文档一改全线失真。

坑 3:burst 设太大。 令牌桶允许瞬时突发,一个 burst 50 打 3-credit 端点就是 150 credits 瞬间蒸发。burst 要按「最贵端点 × 覆盖秒数」来设。

坑 4:429 只降并发。 并发减半,但每个请求还是 3 credits,成本速率没降多少。要降的是 credits 速率。

坑 5:null 当 0 记账。 未确认计费强行按 0 处理,月底对账永远差一笔。

小结

多品类采集把「并发控制」从一维变成二维:请求数决定节奏,credits 决定成本。令牌桶按 credits 计量、目录价按配置维护、429 降速率——这套组合拳,是在 39 个端点、1 到 3 credits 的 catalog 下把管道开稳的前提。

端点目录价与计费字段(含 null 语义)见 SerpBase 官方文档。你们的多端点管道怎么控成本?评论区聊聊。

posted @ 2026-10-16 09:00  蜘蛛人  阅读(5)  评论(0)    收藏  举报