You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在FastAPI中实现每分钟Token速率限制

多Worker环境下OpenAI每分钟Token限制的处理方案

由于FastAPI多Worker是独立进程,进程内全局变量无法共享,直接用全局变量会导致竞态条件和计数不准。以下是几种无需数据库的可行方案:

1. 基于multiprocessing.Manager的跨进程共享状态

利用Python标准库的multiprocessing.Manager创建跨进程共享的状态字典,配合进程锁避免竞态。适合不想引入外部依赖的场景。

实现示例

from fastapi import FastAPI
from multiprocessing import Manager, Lock
import time
import asyncio
from concurrent.futures import ThreadPoolExecutor

# 初始化跨进程共享状态和锁
manager = Manager()
token_stats = manager.dict({
    "used": 0,
    "cycle_start": time.time()
})
token_lock = Lock()
executor = ThreadPoolExecutor()
app = FastAPI()

def _sync_check_and_update(request_tokens: int, max_per_minute: int) -> bool:
    """同步函数:处理Token计数和周期重置,必须加锁"""
    with token_lock:
        now = time.time()
        # 进入新的分钟周期,重置计数
        if now - token_stats["cycle_start"] >= 60:
            token_stats["used"] = 0
            token_stats["cycle_start"] = now
        
        # 检查是否超过限制
        if token_stats["used"] + request_tokens <= max_per_minute:
            token_stats["used"] += request_tokens
            return True
        else:
            # 等待到下一个周期再处理
            wait_time = 60 - (now - token_stats["cycle_start"])
            time.sleep(wait_time)
            token_stats["used"] = request_tokens
            token_stats["cycle_start"] = time.time()
            return True

async def check_token_quota(request_tokens: int, max_per_minute: int) -> bool:
    """异步包装:避免同步锁阻塞事件循环"""
    loop = asyncio.get_running_loop()
    return await loop.run_in_executor(executor, _sync_check_and_update, request_tokens, max_per_minute)

# 接口示例
@app.post("/chat-completion")
async def chat_completion():
    # 用tiktoken预估本次请求的Token消耗(输入+输出)
    estimated_tokens = 150
    max_token_limit = 1000

    if not await check_token_quota(estimated_tokens, max_token_limit):
        return {"status": "error", "message": "Token quota exceeded"}
    
    # 发起OpenAI并发请求(asyncio.gather)
    # ...
    return {"status": "success"}

2. 基于Redis的原子计数

Redis的内存键值存储支持原子操作,天然避免竞态,性能更优,适合高并发场景(Redis属于轻量服务,不算传统数据库)。

实现示例

from fastapi import FastAPI
import redis
import asyncio

redis_client = redis.Redis(host="localhost", port=6379, db=0, decode_responses=True)
app = FastAPI()

async def check_token_quota(request_tokens: int, max_per_minute: int) -> bool:
    # 原子增加Token计数
    current_used = redis_client.incrby("openai_token_counter", request_tokens)
    # 首次设置时添加60秒过期时间(自动重置周期)
    if current_used == request_tokens:
        redis_client.expire("openai_token_counter", 60)
    
    if current_used > max_per_minute:
        # 超过限制,回滚计数
        redis_client.decrby("openai_token_counter", request_tokens)
        return False
    return True

# 接口示例
@app.post("/chat-completion")
async def chat_completion():
    estimated_tokens = 150
    max_token_limit = 1000

    if not await check_token_quota(estimated_tokens, max_token_limit):
        return {"status": "error", "message": "Token quota exceeded"}
    
    # 执行OpenAI并发请求
    # ...
    return {"status": "success"}

3. 本地文件+文件锁(轻量小众场景)

用本地文件存储Token计数和周期时间,配合文件锁实现跨进程同步。适合极低流量、不想依赖外部服务的场景,需注意跨平台兼容性。

核心逻辑

  • 用fcntl(Linux)或msvcrt(Windows)实现文件独占锁
  • 每次读写文件前加锁,更新计数后解锁
  • 定期检查周期时间,超过60秒则重置计数

关键注意事项

  • Token预估准确性:必须用OpenAI官方的tiktoken库计算输入输出的实际Token数,避免预估不足导致实际超限制
  • 异步兼容:同步锁或文件操作需放在线程池执行,避免阻塞FastAPI的事件循环
  • 降级处理:当Token耗尽时,可返回429 Too Many Requests状态码,或自动等待到下一个周期再处理

内容的提问来源于stack exchange,提问作者monopoly

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 16:46:14