Flask应用中如何用cachetools实现缓存过期返回旧值并后台更新
实现带过期返回+后台更新的TTL缓存方案
针对你在Flask应用中遇到的远程API请求超时问题,我们可以基于cachetools扩展实现缓存过期时返回旧值、后台异步更新的逻辑,核心思路是用线程在后台执行耗时任务,同时让请求快速响应。
核心实现步骤
1. 初始化缓存与线程控制工具
我们需要TTLCache存储缓存数据,用线程锁和更新标记避免重复触发后台任务:
from cachetools import TTLCache import threading from flask import Flask app = Flask(__name__) # 初始化TTL缓存:最多存1条数据,TTL设为3600秒(可根据需求调整) cache = TTLCache(maxsize=1, ttl=3600) # 线程锁,保证更新操作的线程安全 update_lock = threading.Lock() # 标记是否正在后台更新缓存 is_updating = False
2. 封装耗时的远程API调用函数
假设这是你的耗时任务(替换为实际远程API请求逻辑):
def fetch_remote_data(): # 模拟耗时操作:实际场景替换为远程API请求代码 import time time.sleep(60) # 模拟1分钟的耗时请求 return {"data": "远程API返回的最新数据", "timestamp": time.time()}
3. 实现带后台更新的缓存逻辑
封装函数处理缓存读取、过期判断和后台更新逻辑:
def get_cached_data(): global is_updating # 先尝试获取缓存值 cached_value = cache.get('remote_data') # 检查缓存是否过期或不存在 cache_expired_or_empty = (cached_value is None) or (cache.ttl() <= 0) # 如果需要更新且当前没有在更新,启动后台线程 if cache_expired_or_empty and not is_updating: with update_lock: # 双重检查,防止多线程并发触发重复更新 if not is_updating: is_updating = True # 启动后台线程执行更新,daemon=True保证Flask退出时线程自动终止 threading.Thread(target=update_cache, daemon=True).start() # 无论缓存是否过期,优先返回现有缓存(首次预加载前可能返回None,需处理) return cached_value def update_cache(): global is_updating try: # 执行耗时的远程API请求 new_data = fetch_remote_data() # 更新缓存 cache['remote_data'] = new_data finally: # 无论任务成功失败,都标记更新结束,保证后续请求能正常触发新的更新 with update_lock: is_updating = False
4. Flask路由集成
在路由中调用缓存函数,快速返回响应:
@app.route('/') def index(): data = get_cached_data() # 首次启动预加载前,data可能为None,这里返回提示或默认值 if not data: return "数据加载中,请稍后刷新", 202 return f"当前数据:{data['data']} | 生成时间:{data['timestamp']}"
5. 启动时预加载缓存
为避免首次请求等待,在Flask启动时提前加载一次缓存:
if __name__ == '__main__': # 启动前预加载缓存,后续请求直接使用缓存值 cache['remote_data'] = fetch_remote_data() app.run(debug=True)
关键细节说明
- 线程安全:通过
update_lock和双重检查机制,避免多请求并发触发重复的耗时任务,减少资源浪费。 - 异常容错:
update_cache中的try/finally块保证即使远程API请求失败,更新标记也会被重置,后续请求仍能触发新的更新尝试。 - 响应速度:无论缓存是否过期,请求都会立即返回现有缓存值,彻底解决页面超时问题。
内容的提问来源于stack exchange,提问作者Saita
相关产品推荐
相关产品推荐

