throttled-py
GitHub: github.com/ZhuoZhuoCra…
简介:🔧 支持多种算法(固定窗口,滑动窗口,令牌桶,漏桶 & GCRA)及存储(Redis、内存)的高性能 Python 限流库。
✨ 功能
- 支持同步和异步(
async / await
)。 - 提供线程安全的存储后端:Redis、内存(支持 Key 过期淘汰)。
- 支持多种限流算法:固定窗口、滑动窗口、令牌桶、漏桶 & 通用信元速率算法(Generic Cell Rate Algorithm, GCRA)。
- 支持配置限流算法,提供灵活的配额设置。
- 支持即刻返回及等待重试,提供函数调用、装饰器、上下文管理器。
- 支持集成到 MCP Python SDK,为模型对话流程提供限流支持。
- 良好的性能,单次限流 API 执行耗时换算如下(详见 Benchmarks):
- 内存:约为 2.5 ~ 4.5 次
dict[key] += 1
操作。 - Redis:约为 1.06 ~ 1.37 次
INCRBY key increment
操作。
- 内存:约为 2.5 ~ 4.5 次
🔰 安装
$ pip install throttled-py
如需使用扩展功能,可通过以下方式安装可选依赖项(多个依赖项用逗号分隔):
$ pip install "throttled-py[redis]"
$ pip install "throttled-py[redis,in-memory]"
🎨 快速开始
1)通用 API
limit
:消耗请求,返回 RateLimitResult。peek
:获取指定 Key 的限流器状态,返回 RateLimitState。
2)样例
from throttled import RateLimiterType, Throttled, rate_limiter, store, utils
throttle = Throttled(
# 📈 使用令牌桶作为限流算法。
using=RateLimiterType.TOKEN_BUCKET.value,
# 🪣 设置配额:每秒填充 1,000 个 Token(limit),桶大小为 1,000(burst)。
quota=rate_limiter.per_sec(1_000, burst=1_000),
# 📁 使用内存作为存储
store=store.MemoryStore(),
)
def call_api() -> bool:
# 💧消耗 Key=/ping 的一个 Token。
result = throttle.limit("/ping", cost=1)
return result.limited
if __name__ == "__main__":
# 💻 Python 3.12.10, Linux 5.4.119-1-tlinux4-0009.1, Arch: x86_64, Specs: 2C4G.
# ✅ Total: 100000, 🕒 Latency: 0.0068 ms/op, 🚀 Throughput: 122513 req/s (--)
# ❌ Denied: 98000 requests
benchmark: utils.Benchmark = utils.Benchmark()
denied_num: int = sum(benchmark.serial(call_api, 100_000))
print(f"❌ Denied: {denied_num} requests")
3)异步
同步和异步拥有一致的功能和标准 API,只需将导入语句从 from throttled import ...
替换为 from throttled.asyncio import ..
即可。
例如将 2)样例
改写为异步:
import asyncio
from throttled.asyncio import RateLimiterType, Throttled, rate_limiter, store, utils
throttle = Throttled(
using=RateLimiterType.TOKEN_BUCKET.value,
quota=rate_limiter.per_sec(1_000, burst=1_000),
store=store.MemoryStore(),
)
async def call_api() -> bool:
result = await throttle.limit("/ping", cost=1)
return result.limited
async def main():
benchmark: utils.Benchmark = utils.Benchmark()
denied_num: int = sum(await benchmark.async_serial(call_api, 100_000))
print(f"❌ Denied: {denied_num} requests")
if __name__ == "__main__":
asyncio.run(main())
4)作为装饰器
from throttled import Throttled, exceptions, rate_limiter
# 创建一个每分钟允许通过 1 次的限流器。
@Throttled(key="/ping", quota=rate_limiter.per_min(1))
def ping() -> str:
return "ping"
ping()
try:
ping() # 当触发限流时,抛出 LimitedError。
except exceptions.LimitedError as exc:
print(exc) # Rate limit exceeded: remaining=0, reset_after=60, retry_after=60
5)上下文管理器
你可以使用「上下文管理器」对代码块进行限流,允许通过时,返回 RateLimitResult。
触发限流或重试超时,抛出 LimitedError。
from throttled import Throttled, exceptions, rate_limiter
def call_api():
print("doing something...")
throttle: Throttled = Throttled(key="/api/v1/users/", quota=rate_limiter.per_min(1))
with throttle as rate_limit_result:
print(f"limited: {rate_limit_result.limited}")
call_api()
try:
with throttle:
call_api()
except exceptions.LimitedError as exc:
print(exc) # Rate limit exceeded: remaining=0, reset_after=60, retry_after=60