FastAPI 接入 FastAPI-Limiter 以及使用 Redis 进行限流指南

fastapi-limiter 更新至 v0.2.0 后与之前不太不一样,这里以接入 Redis 进行示例说明

安装:

bash 复制代码
pip install redis[hiredis]==7.4.0
pip install fastapi-limiter

创建 Redis 管理器

python 复制代码
def _get_redis_pool(use_cache_db: bool = True):
    uri = settings.REDIS_CACHE_URI if use_cache_db else settings.REDIS_URI

    return redis.ConnectionPool.from_url(uri.encoded_string(), decode_responses=True)


redis_client = redis.Redis(connection_pool=_get_redis_pool(False))
redis_cache_client = redis.Redis(connection_pool=_get_redis_pool())

注意这里我使用了一个参数来区别 Redis 的 DB,实际上这里的 URI 来自 pydantic,示例如下:

python 复制代码
@computed_field
@property
def REDIS_URI(self) -> RedisDsn:
    return RedisDsn.build(
        scheme="redis",
        host=self.REDIS_HOST,
        port=self.REDIS_PORT,
        password=self.REDIS_PASSWORD or None,
        path=str(self.REDIS_DB)
    )

@computed_field
@property
def REDIS_CACHE_URI(self) -> RedisDsn:
    return RedisDsn.build(
        scheme="redis",
        host=self.REDIS_HOST,
        port=self.REDIS_PORT,
        password=self.REDIS_PASSWORD or None,
        path=str(self.REDIS_CACHE_DB)
    )

创建 pyrate_limiter 限制器并使用 Redis

因为 fastapi-limiter 基于 pyrate-limiter 实现,这里我们需要先创建限制器,然后提供给 fastapi_limiter

python 复制代码
from pyrate_limiter import Rate, Duration, Limiter, RedisBucket

# 用于区分全局和业务的不同限制
_GLOBAL_BUCKET_KEY = "global-rate-limit"
_BIZ_BUCKET_KEY = "biz-rate-limit"

# 自定义默认限流
_DEFAULT_RATES = [
    Rate(60, Duration.MINUTE),          # 60 次每分钟
    Rate(60 * 5, Duration.MINUTE * 10), # 300 次每十分钟
    Rate(1000, Duration.HOUR)           # 1000 次每小时
]


def get_redis_limiter(rates: list[Rate] | None = None, is_global: bool = False) -> Limiter:
    if is_global:
        bucket_key = _GLOBAL_BUCKET_KEY

        if not rates:
            rates = _DEFAULT_RATES
    else:
        if not rates:
            raise ValueError("Biz limit rate rules cannot be None")

        bucket_key = _BIZ_BUCKET_KEY
    
    # 使用我们已有 redis 管理器中的客户端
    bucket = RedisBucket.init(rates, redis_client, bucket_key)

    return Limiter(bucket)

依赖注入方法

这里可在前置路由依赖中先校验 jwt 后将用户编号或者 ID 放到 request.state 中,随后依赖该 get_rate_limiter 方法

须实现用户唯一标识才可用于判断是否登录并用来实现精确流控

python 复制代码
from fastapi import Request
from fastapi_limiter.depends import RateLimiter
from fastapi_limiter.identifier import default_identifier
from pyrate_limiter import Rate


async def get_identifier(request: Request) -> str:
    # 自定义实现逻辑
    user_no = getattr(request.state, "user_no", None)

    if user_no:
        return f"user_{user_no}:{request.scope["path"]}"

    return await default_identifier(request)


def get_rate_limiter(rules: list[Rate] | None = None, is_global: bool = False) -> RateLimiter:
    return RateLimiter(get_redis_limiter(rules, is_global), get_identifier)

路由全局限流

此处示例,假设 get_current_user 方法中实现了对 request.state 的用户唯一身份设置,就能轻松区分是否已登录用户并分别限流

get_current_user 中应当实现对 jwt 的解码,拿到用户唯一编号后进行数据库查询,确认无误后设置到 request.state(参考逻辑)

python 复制代码
from fastapi import APIRouter, Depends
from app.api.deps import get_current_user
from app.infrastructure.limiter.api_rate_limiter import get_rate_limiter

api = APIRouter(dependencies=[Depends(get_current_user), Depends(get_rate_limiter(is_global=True))])

路由单独限流

由于 FastAPIdependencies 是叠加的,所以上方 RedisBucketkey 需要区分

否则在例如发送短信等接口上会因为全局限流已经写入了一条 Redis 记录,导致设置 1 分钟 1 次 的限流不可用,会直接触发 429 请求频繁

这里例如我们有一个发送短信接口:

python 复制代码
_SEND_CODE_RATES = [
    Rate(1, Duration.MINUTE), # 1 次每分钟
    Rate(5, Duration.HOUR),   # 5 次每小时
    Rate(10, Duration.DAY)    # 10 次每天
]

@router.post("/send-code", dependencies=[Depends(get_rate_limiter(_SEND_CODE_RATES))])
async def send_code():
    pass

在触发一次短信发送后,会往 Redis 写入两条记录如下:

复制代码
ZSET Key: global-rate-limit
Value: user_123456:/api/send-code:8:1:adee7ca88b:1

ZSET Key: biz-rate-limit
Value: user_123456:/api/send-code:8:2:7d2ecd023e:1
相关推荐
真实的菜1 小时前
Redis 从入门到精通(三):持久化机制 —— RDB 与 AOF 深度解析
数据库·redis·缓存
财经资讯数据_灵砚智能1 小时前
基于全球经济类多源新闻的NLP情感分析与数据可视化(夜间-次晨)2026年6月4日
人工智能·python·ai·信息可视化·自然语言处理·ai编程·灵砚智能
装不满的克莱因瓶1 小时前
深度学习优化:使用深层神经网络来解决复杂任务
人工智能·python·深度学习·神经网络·机器学习·ai
橙子圆1231 小时前
Redis知识10之缓存
数据库·redis·缓存
Super Scraper2 小时前
如何使用 cURL 发送 JSON:-d、--json 及常见错误的完整指南
人工智能·爬虫·python·自动化·json·mcp
半壶清水2 小时前
用python脚本加html自建的书法字典
开发语言·python·html
The moon forgets2 小时前
DreamVLA:世界知识驱动的视觉-语言-动作新范式
人工智能·pytorch·python·深度学习·具身智能·vla
myenjoy_12 小时前
Python + Snap7 实现西门子 S7-1200/1500 数据采集
开发语言·python