银行汇率查询并发怎么控?纯同步模型下的限流思路

约 9 分钟 高级银行汇率查询并发控制限流令牌桶
# 银行汇率查询并发怎么控?纯同步模型下的限流思路 接口:银行汇率查询(apiCode=105)· 免费接口(L0 基础版 100 次/天、1 QPS)· 请求方式:POST / GET(同步)· 返回格式:JSON · 适用人群:中高级开发者、架构师 · 阅读时间:约 6 分钟 · 最后实测核对:2026-09-18 ## 核心要点 - 银行汇率查询的 4 个接入点都是同步请求-响应模型:没有订阅推送、没有回调、没有批量接入点。 - 并发额度与每日额度按档位计算,L0 基础版是 1 QPS + 100 次/天。 - 控制并发的手段有三层:客户端令牌桶限速、缓存优先、失败时指数退避。 ## 先认清调用模型 做汇率看板或者换算服务时,先确认这个接口能给什么。银行汇率查询的四个接入点——币种列表(105-35)、汇率查询(105-30)、汇率转换(105-31)、查询历史汇率(105-34)——全部是同步请求-响应。你要数据,就主动发一次请求;不存在「提交任务等回调」这条路径。 这个模型决定了并发工作的重心:控制自己发出去的请求数量。每秒发多少、超时多长、失败重试几次,这三件事决定了系统的稳定性。 ## 接口速览 | 项目 | 说明 | |------|------| | 调用模型 | 同步 POST / GET 请求-响应 | | 异步能力 | 无订阅、无回调、无批量接入点 | | 并发额度 | 按档位,L0 为 1 QPS;L4 为 50 QPS | | 每日额度 | 按档位,L0 为 100 次/天;L4 为 50,000 次/天 | | 超时设置 | 105-30 / 105-31 / 105-34 为 15 秒;105-35 为 30 秒 | | 客户端手段 | 令牌桶限速 + 缓存优先 + 指数退避 | ## 令牌桶限流实现 ```python import threading import time import requests API = "https://route.showapi.com/105-30" APPKEY = "YOUR_APPKEY" class TokenBucket: """按档位设置 QPS:L0 用 1,L4 用 50。留一点余量,避免贴边。""" def __init__(self, rate: float, capacity: float = None): self.rate = rate self.capacity = capacity if capacity is not None else max(rate, 1) self.tokens = self.capacity self.ts = time.monotonic() self._lock = threading.Lock() def acquire(self, timeout: float = 5.0) -> bool: deadline = time.monotonic() + timeout while True: with self._lock: now = time.monotonic() self.tokens = min(self.capacity, self.tokens + (now - self.ts) * self.rate) self.ts = now if self.tokens >= 1: self.tokens -= 1 return True if time.monotonic() >= deadline: return False time.sleep(0.02) bucket = TokenBucket(rate=1) # L0 档位 def query_rate(code: str = "USD", retries: int = 3): for attempt in range(retries): if not bucket.acquire(): print("本地限流:等待超时,跳过本次请求") return None try: js = requests.post( API, params={"appKey": APPKEY}, data={"code": code}, timeout=15 ).json() except requests.RequestException as exc: wait = 2 ** attempt # 1s / 2s / 4s print(f"第 {attempt + 1} 次请求异常,{wait}s 后重试:{exc}") time.sleep(wait) continue if js.get("showapi_res_code") == 0 and js["showapi_res_body"].get("ret_code") == 0: return js["showapi_res_body"] print("业务返回异常,停止重试:", js.get("showapi_res_body")) return None print("重试次数用尽") return None ``` 三个要点:令牌桶把对外速率压在档位的 QPS 以内;重试等待时间按 1、2、4 秒递增,避免一组失败请求同时重发;重试要有次数上限,连续失败应降级到缓存或静态兜底。 ## 缓存优先,让限流器闲着 限流是刹车,缓存是少开车。牌价是延迟数据,30 到 60 秒的缓存对展示没有实际影响,却能把调用量降下来。把缓存放在限流器前面判断,命中缓存时连令牌都不用取。 ```python def get_rate(code: str = "USD"): cached = read_cache(f"105-30:{code}") # 你自己的缓存读写 if cached: return cached["data"] body = query_rate(code) if body: write_cache(f"105-30:{code}", body, ttl=30) return body ``` 多实例部署时,令牌桶和缓存都要放在共享层(Redis),否则每个实例各自限速、各自回源,总速率会超出档位。 ## 一次并发实测的结果 2026-09-18 实测:在浏览器脚本上下文里近乎同时发出 5 个 105-30 的请求,全部在 305 毫秒内返回,5 个响应的 `showapi_res_code` 与 `ret_code` 都是 0,没有出现被拒绝的请求。 这说明短时突发在这个量级上可以正常拿到响应。并发与每日额度按档位计算,L0 是 1 QPS,客户端侧仍建议按档位设置限速,把余量留给重试和缓存回源。 ## FAQ **Q1:接口支持批量查询或订阅推送吗?** 不支持。4 个接入点都是同步请求-响应,没有批量接入点,也没有订阅与回调。需要批量数据时自行循环调用,并在客户端加限速。 **Q2:超出档位并发会怎样?** 并发额度按档位计算,L0 是 1 QPS。超出当前档位的请求按官方档位说明处理,客户端侧应该用令牌桶把速率压在档位以内。 **Q3:能用 WebSocket 接收推送吗?** 不能。接口没有推送通道,实时性靠主动请求配合本地缓存实现。 **Q4:重试应该重几次?** 建议 3 次封顶,等待时间 1 秒、2 秒、4 秒递增。重试次数不设上限会把一次故障放大成一轮流量高峰。 **Q5:怎么知道该用哪个档位的 QPS?** 先量自己的实际峰值:展示型页面配合 30 秒缓存,多数场景每天几十次调用就够。确有更高需求再用积分升级档位。 ## 下一步阅读 - [银行汇率查询:L0 档位每天 100 次,缓存该缓存什么](https://www.showapi.com/guides/exchange-rate-cache-105):份额度怎么省下来。 - [银行汇率查询:企业汇率看板的落库与走势实现](https://www.showapi.com/guides/exchange-rate-finance-dashboard-105):定时拉取 + 落库 + 前端只读的架构。 - [银行汇率查询:数据延迟与「仅供参考」的四个适用范围](https://www.showapi.com/guides/exchange-rate-delay-caveat-105):数据性质决定了缓存与降级策略。 - **本系列共 13 篇**:查看[银行汇率查询指南总目录](https://www.showapi.com/guides/exchange-rate-guides-105)
加载中...