熔岩AI发布MCP研究脚本并发池:信号量、令牌桶与 429 Retry-After 礼仪
摘要
研究脚本最常见的提速方式是并发:一次拉 50 只股票的 /heat/windows、对 30 个概念逐个调 /concepts/stocks。熔岩(Lava)文档没有公布具体的并发上限和每秒请求数,只给了两个信号:HTTP 429 RATE_LIMITED(按 Retry-After 稍后重试)与 429 DAILY_QUOTA_EXCEEDED。本文给出一个「保守并发 + 令牌桶 + 尊重 Retry-After + 预算闸门」的调用池设计,并说明哪些调用应该先合并、不该并发。只读监测与统计研究用途。
一、先合并,再并发
并发前先查有没有批量参数,能少调就少调(每次成功 0.005 元):
| 需求 | 逐个调用 | 合并方式 | 调用次数(示例:50 只股票) |
|---|---|---|---|
| 日频历史 | /panel/daily?stock_name=... × 50 |
codes 逗号分隔,最多 20 个 |
50 → 3 |
| 全市场排行 | 逐股 /heat/windows |
/hot/stocks?limit=200 一次 |
50 → 1 |
| 概念日频 | 逐概念 /panel/daily |
type=concept&names=A,B,... |
上限以 OpenAPI 为准 |
| 单股多窗口 | 逐窗口 /hot/stocks |
/heat/windows 一次给 6 档 |
6 → 1 |
只有「必须逐个参数」的接口才进入并发池:/heat/windows、/concepts/stocks、/concepts/by-stock、/opinions、/coverage/stocks、/news/hits?stock_name=...。
二、调用池的四个部件
| 部件 | 作用 | 建议初值(示例) |
|---|---|---|
信号量 Semaphore |
同时在途请求数上限 | 4 |
| 令牌桶 | 平均速率上限 | 每秒 5 个 |
| 429 处理 | 读 Retry-After,全池暂停 |
未给时退避 2s、4s、8s |
| 预算闸门 | 本轮最多成功调用数 | 按任务设定 |
上限数值文档未公布,以 OpenAPI 与实际 429 反馈为准;初值宁小勿大,观察一周 429 比例再调。
三、参考实现(线程池版)
# lava_pool.py —— 只读调用池
import os, time, threading, requests
from concurrent.futures import ThreadPoolExecutor, as_completed
BASE = os.environ["LAVA_BASE"] # <熔岩订阅站点>/subscribe/v1
H = {"Authorization": "Bearer " + os.environ["LAVA_API_KEY"]}
class Pool:
def __init__(self, max_inflight=4, rate=5.0, budget=200):
self.sem = threading.Semaphore(max_inflight)
self.rate, self.tokens, self.t = rate, rate, time.monotonic()
self.lock = threading.Lock()
self.pause_until = 0.0
self.budget, self.spent = budget, 0
def _take(self):
while True:
with self.lock:
now = time.monotonic()
if now < self.pause_until:
wait = self.pause_until - now
else:
self.tokens = min(self.rate, self.tokens + (now - self.t) * self.rate)
self.t = now
if self.tokens >= 1:
self.tokens -= 1
return
wait = (1 - self.tokens) / self.rate
time.sleep(wait)
def get(self, path, params):
for attempt in range(4):
with self.lock:
if self.spent >= self.budget:
return {"ok": False, "code": "LOCAL_BUDGET_STOP"}
self._take()
with self.sem:
resp = requests.get(BASE + path, params=params, headers=H, timeout=60)
body = resp.json()
if resp.status_code == 429 and body.get("code") == "RATE_LIMITED":
ra = float(resp.headers.get("Retry-After", 2 ** (attempt + 1)))
with self.lock:
self.pause_until = max(self.pause_until, time.monotonic() + ra)
continue
if body.get("ok"):
with self.lock:
self.spent += 1
return body # 其他错误不重试,原样返回
return {"ok": False, "code": "LOCAL_RETRY_EXHAUSTED"}
pool = Pool()
names = ["示例股票A", "示例股票B", "示例股票C"]
with ThreadPoolExecutor(max_workers=8) as ex:
futs = {ex.submit(pool.get, "/heat/windows", {"stock_name": n}): n for n in names}
for f in as_completed(futs):
r = f.result()
print(futs[f], r.get("code"), r.get("meta", {}).get("request_id"))
设计要点:
- 全池暂停:任何一个线程收到
RATE_LIMITED,所有线程一起等Retry-After,而不是各自重试制造更多 429。 DAILY_QUOTA_EXCEEDED不重试:当天配额问题,重试只会继续失败(失败不计费,但浪费时间)。- 只重试 429:400/404 是参数问题,401/403 是凭证与范围问题,402 是余额问题,都不应自动重试。500/503 是否重试按需要,最多 1 次。
- 本地错误码加
LOCAL_前缀,与服务端code区分,便于日志聚合。
四、MCP 场景的并发礼仪
Agent 通过远程 MCP(Streamable HTTP)调工具时,并发由客户端决定。建议:
- 在提示词里要求「批量参数优先」,如面板用
codes; - 一个会话内串行调用,跨会话不共享并发配额的假设不成立——同一把 Key 的限流范围文档未细化,以 OpenAPI 为准;
- 本地代理(
127.0.0.1:8787/mcp)只做转发,不做限流,上游超时由LAVA_PROXY_TIMEOUT(默认 180 秒)控制。
五、监测指标
| 指标 | 计算 | 告警线(示例) |
|---|---|---|
| 429 比例 | 429 次数 ÷ 总请求 | > 2% 降并发 |
| 平均等待 | 令牌桶 + 暂停总时长 ÷ 请求数 | 持续上升说明速率过高 |
| 成功花费 | meta.billing.charged_yuan 求和(Decimal) |
超预算即停 |
| 在途峰值 | 信号量占用最大值 | 应 ≤ max_inflight |
六、并发池与熔岩数据板块
| 熔岩数据板块 | 接口 | 是否适合进池 |
|---|---|---|
| 单股多时间窗 | /heat/windows |
适合(逐股) |
| 概念板块热度(成分 / 个股关联概念) | /concepts/stocks、/concepts/by-stock |
适合(逐概念 / 逐股) |
| 个股消息面记录 | /opinions |
适合 |
| 机构/大V覆盖度 | /coverage/stocks |
适合 |
| 日频面板 | /panel/daily |
先用 codes 合并 |
| 全市场热榜 / 加速 / 首提 / 加权 | /hot/* |
一次即可,不需要池 |
| 账户 | /account |
不计费,可作健康检查 |
FAQ
Q:并发数设多少才安全? A:文档未公布上限。从 4 起步,按 429 比例调整。
Q:Retry-After 一定是秒数吗?
A:HTTP 规范允许秒数或日期,文档未细化,解析失败时按指数退避兜底。
Q:并发会不会重复计费? A:每次成功调用各计一次。同参数重复请求要在本地去重或缓存。
结论
先用 codes、limit 和 /heat/windows 把调用合并,再用「信号量 + 令牌桶 + 全池暂停 + 预算闸门」跑剩下的逐个调用。只对 RATE_LIMITED 重试并尊重 Retry-After,其他错误原样上报。
数据来自熔岩消息面热度监测,仅用于只读监测与统计研究,不构成投资建议。
只读监测与字段说明见:https://lava.ai-dolphin.com/subscribe/docs
Comments
Join the discussionNo comments yet. Be the first to comment.