虽然这是一个老问题,但在SO或通过谷歌搜索似乎没有其他答案,我花了一段时间才解决了这个问题,因此也许我的答案可以帮助某些人。
首先,您需要一些缓存后端,我使用flask-caching 和redis 以及来自pypi的Python redis
库sudo pip install redis
。
接下来,执行from flask_caching import Cache
,然后执行cache = Cache()
,我在另一个名为extensions.py
的文件中执行此操作。如果您正在使用应用程序工厂模式,这非常重要,因为您稍后需要导入cache
,并且这有助于避免较大的flask应用程序出现循环引用问题。
之后,您需要在flask应用程序上注册flask-caching扩展,我在单独的app.py
文件中完成了此操作:
from flask import Flask
from extensions import cache
def create_app(config_obj=None):
"""An application factory"""
app = Flask(__name__)
app.config.from_object(config_obj)
cache.init_app(app, config={'CACHE_TYPE': 'redis',
'CACHE_REDIS_HOST': '127.0.0.1',
'CACHE_REDIS_PORT': '6379',
'CACHE_REDIS_URL': 'redis://127.0.0.1:6379'})
return app
现在,Flask 中的 cache
已经被注册了,它可以从 extensions.py
导入并在整个应用程序中使用,而不会出现循环引用问题。继续你正在使用 user_loader
的任何文件:
import pickle
from flask import current_user
from extensions import cache
from models.user_models import User
@login_manager.user_loader
def load_user(user_id):
"""Load user by ID from cache, if not in cache, then cache it."""
user = 'user_{}'.format(user_id)
user_obj = pickle.loads(cache.get(user)) if cache.get(user) else None
if user_obj is None:
query = User.query.get(int(user_id))
user_obj = pickle.dumps(query)
cache.set(user, user_obj, timeout=3600)
return query
return user_obj
最后,当你注销用户时,可以将其从缓存中删除:
@blueprint.route('/logout/')
@login_required
def logout():
"""Logout."""
user = 'user_{}'.format(current_user.id)
cache.delete(user)
logout_user()
for key in ('identity.name', 'identity.auth_type'):
session.pop(key, None)
flash('You are logged out.', 'info')
return redirect(url_for('public.home')
这个方法看起来非常有效,每个用户每页的SQLAlchemy查询次数减少了3次,对于我的应用程序中的一些部分,页面加载速度提高了200ms,同时消除了一个到达SQLAlchemy连接池限制的严重问题。
关于这种解决方案的最后一个重要点是,如果出于任何原因更改用户对象,例如为用户分配新角色或能力,则必须清除缓存中的用户对象。例如像下面这样:
# set the user_id from current_user.id CACHE object
user_id = current_user.id
# remove the old USER object from cache since you will change it
# first set the cache key to user_{id}
cache_user = 'user_{}'.format(user_id)
# now delete the cache key
cache.delete(cache_user)
背景:
我需要考虑缓存flask-login user_loader,这是因为我通过扩展flask-login类的UserMixin和AnonymousUserMixin实现了访问控制列表管理,并添加了一些类方法,例如get_roles和get_abilities。我还使用了flask-sqlalchemy和postgresql后端,其中有一个角色表和一个能力表与用户对象相关联。这些用户角色和能力主要在模板中进行检查,以基于用户角色和能力呈现各种视图。
某些时候,当我在我的应用程序中打开多个浏览器选项卡或仅重新加载页面时,我开始收到错误消息:TimeoutError: QueuePool limit of size 5 overflow 10 reached, connection timed out, timeout 30。Flask-sqlalchemy具有SQLALCHEMY_POOL_SIZE和SQLALCHEMY_MAX_OVERFLOW设置,但是增加这些值只是掩盖了问题,对于我来说,错误仍然会在加载更多选项卡或执行更多页面重新加载时发生。
深入挖掘以找出根本原因,我使用SELECT * FROM pg_stat_activity查询了我的postgresql DB,并发现在每个请求上,我都累积了多个连接,状态为“空闲事务”,其中SQL查询明显与用户、角色、能力访问检查相关联。这些“空闲事务”连接导致我的DB连接池达到容量极限。
进一步测试发现,缓存flask-login user_loader User对象消除了“空闲事务”连接,即使我将SQLALCHEMY_POOL_SIZE和SQLALCHEMY_MAX_OVERFLOW保留为默认值,我也不再遇到TimeoutError: QueuePool limit的问题。问题解决了!
skip_static
设置为True
呢?这样就不会影响静态路由的问题了,例如。 - Martijn Pieters