银行汇率查询并发怎么控?纯同步模型下的限流思路
约 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)





