Python并发请求限制QPS的三种实现方案
本文介绍在Python中使用GRequests进行高并发API请求时,如何通过三种方式限制每秒查询率(QPS)。内容涵盖基础的time.sleep阻塞法、精确的令牌桶算法实现,以及封装好的GRequests-Throttle库。针对低并发和高并发场景提供了具体的代码示例与性能对比,帮助开发者避免服务器拒绝响应并优化资源利用率。
背景与问题描述
此前遇到一个需求,需访问某API服务器获取数据。该服务器对QPS(每秒查询率)限制为2。由于限制较低,最初采用阻塞式请求即可满足。后续服务升级,QPS上限提升至20,原有的阻塞方式已无法充分利用带宽。因此改用GRequests进行并发请求,但发现并发速度过快导致服务器频繁返回错误码,造成大量资源浪费。为此,本文记录几种有效的节流(Throttle)方案:
基础限流方案:time.sleep阻塞法
以下示例均基于相同的前提条件,包含必要的包导入及数据初始化:
import grequests
urls = [
"https://www.baidu.com",
"https://www.google.com"
]
requests = [
grequests.get(url)
for url in urls
] * 1000
rate = 20 # 表示 20 请求/秒
time.sleep(1)
这是最直观的方法,通过time.sleep(1)强制阻塞进程,从而控制每秒发出的请求数量。其耗时逻辑可表示为:Time = 请求准备时延 + 请求发送时延 + time.sleep(1)。然而,该方法存在精度不足的问题:随着数据量增大,实际QPS的方差也会随之增大,导致限流效果不够稳定。
from time import sleep
req_groups = [
requests[i: i+rate]
for i in range(0, len(requests), rate)
]
ret = []
for req_group in req_groups:
ret += grequests.map(req_group)
sleep(1)
print(ret)
精确限流方案:令牌桶算法
令牌桶(token bucket)方法
该方法精度更高,在服务器和客户端能承受高并发的前提下,可将误差控制在±1以内。其耗时公式为:Time = 请求准备时延 + 请求发送时延 + 令牌桶阻塞时延。其中,令牌桶阻塞时延 ≈ 1 - (请求准备时延 + 请求发送时延)。尽管精度提升,但该方法会导致CPU占用率看似较高(主要源于while pass循环),尽管实际有效计算负载很低。
from time import time
class Throttle:
def __init__(self, rate):
self.rate = rate
self.tokens = 0
self.last = 0
def consume(self, amount=1):
now = time()
if self.last == 0:
self.last = now
elapsed = now - self.last
if int(elapsed * self.rate):
self.tokens += int(elapsed * self.rate)
self.last = now
self.tokens = (
self.rate
if self.tokens > self.rate
else self.tokens
)
if self.tokens >= amount:
self.tokens -= amount
else:
amount = 0
return amount
throttle = Throttle(rate)
req_groups = [
requests[i: i+rate]
for i in range(0, len(requests), rate)
]
ret = []
for req_group in req_groups:
ret += grequests.map(req_group)
while throttle.consume():
pass # 阻塞
print(ret)
封装库方案:GRequests-Throttle
GRequests-Throttle
这是一个基于令牌桶算法封装的GRequests修改版,使用方式极为简便。首先需安装grequests-throttle库(注:清华镜像源更新可能较慢,建议使用阿里镜像源):
pip install grequests-throttle
import grequests_throttle as gt
ret = gt.map(requests, rate=rate)
print(ret)
方案总结与建议
总结
若并发请求量较小,可直接使用time.sleep(1),实现简单快捷;若并发量较大,令牌桶算法能更充分地利用每一秒的带宽;若希望减少代码编写量,推荐使用GRequests-Throttle库进行流量控制。
关于Python并发请求下限制QPS的实现介绍至此。如需了解更多相关技术细节,可查阅本站往期文章或继续浏览后续内容。感谢支持!
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













