简介
在 Flask 开发中,我们经常会有数据库操作、模板渲染等,这些操作单次可能速度感觉挺快,压力不大,但是当次数一上来,那就不行了,各种问题就来了,所以为了缓解这些问题,我们经常会采用缓存的方法以空间换时间。
但是,我们要怎么缓存呢?在 Flask 的官方文档中有一个简单的解决方法,那就是利用 Werkzeug(Flask非常依赖它) 的 SimpleCache 来缓存,如果有兴趣可以查看一下这页文档:Caching。虽然这是官网的一个介绍,但是并非是很好的实践,因为我们需要参与的事情太多了,所以为了解决一些繁杂的手续,有人开发了一个插件 Flask-Cache,它能帮助我们解决很多不需要的代码,简化到只需要一个装饰器。
下面,我就以我实践过的项目介绍一下 Flask-Cache 的基本使用和一些比较好的实践。
安装
这个就是老生常谈了,大多数的依赖都可以通过 pip 解决,这个也不例外:
pip install Flask-Cache
项目应用
一开始就先来个简单的应用,展示一下如何使用 Flask-Cache 吧,至于一些实践之类的就先介绍完基本用法之后在后面介绍。下面就举一个缓存 view 结果的示例:
#!/usr/bin/env python
# encoding: utf-8
from flask import Flask
from flask.ext.cache import Cache
app = Flask(__name__)
# Check Configuring Flask-Cache section for more details
cache = Cache(app,config={'CACHE_TYPE': 'simple'})
@app.route('/index')
@cache.cached()
def index():
print "index called"
return "Hello World"
if __name__ == '__main__':
app.run(host='0.0.0.0', port=8080)
这里是一个简单的 Flask 应用,在 Line:12 我们指定了缓存 view index,所以我们想把服务器跑起来,然后访问一下这个 URL:
http://localhost:8080
当第一次访问的时候我们可以看到控制台的输出是:
* Running on http://0.0.0.0:8080/ (Press CTRL+C to quit)
index called
192.168.1.158 - - [18/Mar/2019 18:03:05] "GET /index HTTP/1.1" 200 -
然后我们再刷新一遍浏览器,这个时候看一下控制台的输出:
* Running on http://0.0.0.0:8080/ (Press CTRL+C to quit)
index called
192.168.1.158 - - [18/Mar/2019 18:03:15] "GET /index HTTP/1.1" 200 -
192.168.1.158 - - [18/Mar/2019 18:03:18] "GET /index HTTP/1.1" 200 -
对比一下可以发现,当我们第二次访问的时候,其实是没有再执行一遍 index 函数了,而是直接将缓存的结果返回了,这就达到了我们缓存的目的了。
清除缓存
可能有的时候我们修改过数据或者更换过模板之类的情况,需要清理缓存,这有主动和被动两种办法:
- 主动:通过 cache.delete 删除缓存
- 被动:设置 cache 的缓存时间让缓存自动超时
这里就针对两种方式分别介绍一下如何使用:
被动清除
被动清除就是我们在设置缓存装饰器的时候指定缓存生效时间,如果不想设置也行,那就在 Flask 的设置中加上配置项:CACHE_DEFAULT_TIMEOUT 这个配置,推荐使用变量 CACHE_DEFAULT_TIMEOUT 的配置方式,但是这里就介绍装饰器的方式,还是以之前的示例为例进行介绍:
@app.route('/index')
@cache.cached(timeout=50)
def index():
print "index called"
return "Hello World"
这里可以看到和之前的代码有所区别,区别就在于 cache.cached 是带参数了,参数就是 timeout,然后值是 50,其实就是说每次缓存的有效期是 50 秒,50 秒过期之后需要重新获取一遍,然后再缓存 50 秒。这样我们就可以在一定程度上保证缓存的新鲜度。
主动清除
其实,要保持更高的新鲜度,主动清除才是比较合适的方式,主动清除就是我们调用 cache 的 delete 方法来删除指定的缓存,首先想看下 delete 的函数原型:
delete(*args, **kwargs)
好像看不出什么东西,那这里直说吧,一个调用的示例是这样的:
cache.delete(key='view//')
这是删除首页的示例,这里可能你会有点疑问了,这个 key 是什么,我要删除一个缓存我怎么知道它的 key 是什么?
对于这个问题,可能文档不会告诉你,但是我稍看了下源码,发现这个 key 的函数很简单:
def make_cache_key(*args, **kwargs):
if callable(key_prefix):
cache_key = key_prefix()
elif '%s' in key_prefix:
cache_key = key_prefix % request.path
else:
cache_key = key_prefix
return cache_key
在第 5 行 可以看到 key 就是 key_prefix % request.path,和请求的路径是相关的,那么 key_prefix 是什么呢,稍作查找就会发现是: key_prefix=‘view/%s’,这样的话我们就可以很快得知道哪个 view 的 key 是什么,也能很方便得清除缓存了。
其他缓存
前面介绍的都是缓存 view 的,那么假设我要缓存数据库查询怎么办,其实也很方便,和 view 的缓存差不多,但是有一点点不一样的就是我们需要制定缓存的 key。因为我们前面可以看到,如果我们不指定 key 的话,默认的缓存 key 是和请求路径相关的,那么就会和 view 的缓存 key 冲突,这样的话就悲剧了,混淆了缓存。下面是一个缓存数据库查询的示例:
@cache.cached(key='all_post')
def get_all_post():
return Post.query.all()
删除缓存
既然我们都明确指定 key 了,那么删除缓存也就水到渠成的事情了,直接调用 cache.delete(‘all_post’) 就删除掉这个缓存了,简单明了。
缓存位置
前面我们都对这个问题避而不谈,现在是时候来聊聊了,那就是缓存缓存,我们的缓存到底缓存在哪个地方?
我们往前面的例子看一下,可以看到初始化 Cache 的代码:
cache = Cache(app,config={'CACHE_TYPE': 'simple'})
有个参数是 CACHE_TYPE,这里设置的值是 simple,那么代表着什么?根据官方文档的介绍,这个 simple 的意思就是说使用本地Python字典作为缓存的位置,也就是说我们的缓存都是放到内存中 python 的字典上,当获取缓存的时候直接从字典中根据 key 获取。
那么,除了 simple,还有那些可选的位置?根据官方文档的描述,目前支持的存储位置有:
- null: 不缓存
- simple: 使用本地Python字典缓存。这不是真正的线程安全。
- memcached: 使用memcached服务器作为后端。支持pylibmc或memcache或谷歌应用程序引擎的memcache库。
- gaememcached: MemcachedCache一个不同的名称
- redis: 使用 Redis 作为后端存储缓存值
- filesystem: 使用文件系统来存储缓存值
- saslmemcached:使用memcached服务器作为后端。使用SASL建立与memcached服务器的连接。pylibmc是必须的,libmemcached必须支持SASL。
Flask 模块化中的实践
前面举的例子是在一个简单的 Flask 应用中的,但是,我们正常在使用的时候并不会就这么简单得使用 Flask,我们会划分为各个模块,例如 routes.py models.py 以及 templates 等等,那么,在这种情况下,我们该如何在 Blueprint 上缓存 view 呢?
首先先举个 bad example:
// views.py
cache = Cache()
@bp.route('test')
@cache.cached()
def test()
...
// persistens.py
cache = Cache()
@cache.cached()
def get_all_post()
...
这里是每个地方都实例化了一个 Cache ,这明显是不合理的,这里我给的一个比较合理的建议。
缓存初始化实践
首先先说下目录结构:
application/
route.py
service.py
extensions.py
然后将初始化放到 extensions.py 里面去:
// extension.spy
...
cache = Cache()
然后再在需要使用的地方 import 进来就好了:
from application.extensions import cache
@cache.cached()
def test()
...
更新 Cache
之前说过了,当我们需要更新数据的时候需要刷新 Cache,有两种方式:主动和被动。主动刷新我们需要删除缓存,那么假设对于一个博客系统,我们再多个地方都缓存了 Cache,那么在更新文章的时候难道需要在更新函数里面刷新?像这样:
def update_post(post):
update(post)
cache.delete('view//post')
cache.delete('get_all_post')
...
这样好像很难看,而且当我们在哪个添加了一个新的缓存的时候,还有可能要在这里添加东西,那么,有什么好的办法可以处理这个?我给的建议是使用 信号,例如:
// service.py
def update_post(post):
update(post)
update_post_signal.send(app, post.id)
// signal_process.py
@update_post_signal.connect
def post_updated(sender, post_id):
cache.delete('view//post')
cache.delete('get_post_with_id_{}'.format(post_id))
通过 信号 我们将 cache 操作都统一起来了,这样的话我们以后修改/新增/删除都直接找这个文件就好了,不用到处找更新的地方了。
总结
ok,关于 Flask-Cache 缓存的就讲这么多先,如果想了解更多的内容,可以参照一下官方文档,内容不会很多,比较简单直接,而且内容也还可以。