fastapi 依赖注入里如何让依赖只执行一次(缓存)
技术百科
舞夢輝影
发布时间:2026-01-24
浏览: 次 FastAPI依赖中lru_cache不生效是因为每次请求会重新调用依赖函数;真正有效的缓存是框架内置的“同一请求内相同依赖自动复用”,无需手动加装饰器。
FastAPI 依赖中用 lru_cache 缓存函数结果不生效?
因为 FastAPI 的依赖注入每次请求都会重新调用依赖函数,lru_cache 默认作用于函数调用层面,但如果你缓存的是无参函数(比如 get_db()),它确实能起作用;可一旦依赖函数带请求上下文(如接收 Request 或 Depends 嵌套),缓存就失效——参数不同,缓存 key 就不同。
真正需要“每请求只执行一次”的场景,其实是避免同一请求内多次调用同一个依赖(比如多个路由参数或嵌套依赖都用了 get_current_user),这时应靠 FastAPI 自身的依赖缓存机制,而不是手动加 lru_cache。
- FastAPI 默认对「同一请求生命周期内」的相同依赖(相同类型、相同参数)自动复用返回值
- 这个行为不依赖装饰器,是框架内置的,只要你不主动传入新参数(比如每次都传不同
token字符串),它就会命中缓存 - 验证方式:在依赖函数里加
print("called"),同一请求中多次声明该依赖,只会输出一次
如何强制让依赖在请求内只运行一次(即使参数动态变化)
当你的依赖函数签名里包含动态值(比如 def get_config(env: str = Query(...))),FastAPI 会认为每次 env 不同就是不同依赖实例,无法复用。此时需手动管理生命周期:
- 把依赖逻辑拆成两层:外层接收动态参数并生成唯一 key,内层用
lru_cache(maxsize=128)缓存结果 - 确保 key 能准确反映输入差异,例如
f"{env}_{region}" - 注意:缓存对象若含非可哈希类型(如
dict、list),要先转成tuple或json.dumps后再哈希
示例:
@lru_cache(maxsize=128)
def _load_config_cached(env: str, region: str) -> dict:
return {"env": env, "region": region, "ts": time.time()}
def get_config(env: str = Query(...), region: str = Query("us-east-1")) -> dict:
return _load_config_cached(env, region)
全局单例依赖(整个应用生命周期只初始化一次)
适合数据库连接池、Redis 客户端、配置加载器这类无需请求上下文的资源。关键不是“缓存”,而是“只创建一次”:
- 用模块级变量 +
lambda或普通函数包装,配合Depends注入 - 不要在依赖函数体内做耗时初始化(如连接 DB),而应在应用启动时完成(用
@app.on_event("startup")) - 推荐写法:定义一个类,用
__init__做初始化,再通过依赖函数返回其实例
示例:
class RedisClient:
def __init__(self):
self.client = redis.Redis(...)
redis_client = RedisClient() # 全局单例
def get_redis() -> RedisClient:
return redis_client
然后在路由中:def route(redis: RedisClient = Depends(get_redis)) —— 每次都是同一个实例。
容易被忽略的坑:异步依赖和缓存不兼容
如果依赖函数是 async def,你不能直接给它加 @lru_cache(协程不可哈希),也不能在 async 函数里同步调用缓存函数后 await —— 这会导致阻塞事件循环。
- 正确做法:缓存纯同步逻辑,异步部分(如 D
B 查询)仍走 async,但可对查询结果做内存缓存(如用
functools.lru_cache缓存 SQL 字符串 → 查询结果映射) - 更稳妥的方案是用
aiocache或async_lru这类支持协程的缓存库 - 切记:FastAPI 的依赖缓存机制本身不区分 sync/async,它只管“是否复用上一次返回的对象”,所以 async 依赖同样享受请求内复用
复杂点在于,很多人想缓存的是「await 后的结果」,但又没意识到 async 函数返回的是协程对象,不是结果——必须先 await 才能缓存值,而 await 只能在 async 上下文中做。
# ai
# 的是
# 就会
# 这类
# 都是
# 也不
# 如果你
# 是因为
# 里加
# app
# 复用
# redis
# js
# json
# 路由
# 循环
# 对象
# 字符串
# 数据库
# 异步
# 事件
# red
# Token
# sql
# Lambda
# print
# 查询结果
# fastapi
相关栏目:
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
AI推广<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
SEO优化<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
技术百科<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
谷歌推广<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
百度推广<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
网络营销<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
案例网站<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
精选文章<?muma echo $count; ?>
】
相关推荐
- 如何使用Golang实现跨域请求支持_Golang
- Python大型项目拆分策略_模块化解析【教程】
- win11 OneDrive怎么彻底关闭 Win1
- Win11笔记本怎么看电池健康度_Win11电池报
- Win11怎么关闭通知中心_Windows11系统
- Win11视频默认播放器怎么改_Win11关联第三
- Win11怎么设置鼠标宏_Win11鼠标按键自定义
- 如何在Golang中使用闭包_封装变量与函数作用域
- Win11怎么设置虚拟键盘_打开Win11屏幕键盘
- Win11怎么设置ipv4地址_Windows 1
- Windows蓝屏错误0x0000001E怎么修复
- Win11怎么设置麦克风权限_允许应用访问Win1
- Win11怎么关闭触摸屏_禁用Win11笔记本触摸
- Go 中的 := 运算符:类型推导机制与使用边界详
- c# 在高并发下使用反射发射(Reflection
- Windows10怎么用“讲述人”读屏辅助 Win
- Dapper的Execute方法的返回值是什么意思
- 如何使用Golang sync.Map实现并发安全
- 如何在Golang中定义接口_抽象方法和多态实现
- 如何在Golang中实现文件下载_Golang文件
- 短链接怎么用php递归还原_多层加密链接的处理法【
- Win10如何关闭安全中心所有通知 Win10禁用
- Win11怎么把图标拖到任务栏_Win11固定应用
- Win10怎么查看内存时序参数_Win10CPU-
- Linux怎么查找死循环进程_Linux系统负载分
- 如何使用正则表达式提取以编号开头、后接多个注解的逻
- Python字符串操作教程_切片拼接与格式化详解
- 如何使用Golang encoding/json解
- Win11怎么关闭系统推荐内容_Windows11
- Win10怎样安装PPT模板_Win10安装PPT
- php怎么捕获异常_trycatch结构处理运行时
- 如何使用Golang实现负载均衡_分发请求到多个服
- c++ std::atomic如何保证原子性 c+
- windows如何修改文件默认打开方式_windo
- 如何使用Golang template生成文本模板
- Win11怎么设置组合键快捷方式_Windows1
- Windows10如何彻底关闭自动更新_Win10
- Win11关机界面怎么改_Win11自定义关机画面
- Windows10系统怎么查看硬盘健康_Win10
- Win11怎么关闭专注助手 Win11关闭免打扰模
- PhpStorm怎么调试PHP代码_PhpStor
- Golang如何避免指针逃逸_Golang逃逸分析
- 使用类变量定义字符串常量时的类型安全最佳实践
- Windows怎样关闭桌面弹窗广告_Windows
- Python异步编程高级项目教程_asyncio协
- Windows10如何更改系统字体大小_Win10
- c++中的std::conjunction和std
- Windows服务无法启动错误1067是什么_进程
- c++如何获取map中所有的键_C++遍历键值对提
- php订单日志怎么记录评价_php记录订单评价日志


QQ客服