利用Flask-Cache缓存flask中带表单数据的GET请求,及FileSystemCache示例
网上大多数介绍都是对于flask中不带表单数据的GET请求的缓存,即一般的GET请求;而有时候,对于带有表单数据的GET请求进行缓存也是很有必要的,如对于特定的表单数据,GET请求返回的结果完全一样,这样加上缓存后效果会好很多。
但flask,即使是马上要用到的Flask-Cache扩展,也没有原生支持带表单数据的GET请求的缓存,需要自定义函数来实现。
Flask-Cache
Flask-Cache是比较常用的flask缓存扩展,这个扩展当然没有重写flask的cache部分,还是用到的werkzeug的Cache
模块。
Flask-Cache安装很容易:
pip install Flask-Cache
其简单应用如下:
from flask import Flask
import time
from flask.ext.cache import Cache
app = Flask(__name__)
cache = Cache(app, config={'CACHE_TYPE': 'simple'})
@app.route('/')
@cache.cached()
def root():
t = time.time()
return str(t)
if __name__ == '__main__':
app.debug = True
app.run()
带表单数据的GET请求遇到的问题
使用默认cache选项:
from flask import Flask, request
from flask.ext.cache import Cache
app = Flask(__name__)
cache = Cache(app, config={'CACHE_TYPE': 'simple'})
@app.route('/')
@cache.cached()
def root():
t = request.args['s']
return t
if __name__ == '__main__':
app.debug = True
app.run()
这样当访问此app时:
http://127.0.0.1:5000/?s=aaa
http://127.0.0.1:5000/?s=bbb
http://127.0.0.1:5000/?s=ccc
3次访问获得的返回值将全都是aaa
原因在于使用cached()
缓存时,默认情况下使用请求路径(request.path
)作为cache_key,即缓存时忽略了GET请求后面带的表单数据,所以上述三次访问对于cached而言是完全一样的访问;所以只会将第一次访问结果缓存,而后面两次访问都直接返回缓存结果。
使用自定义函数解决
cached()
提供了参数key_prefix
来自定义cache_key,所以只需要自定义request.path
加上了表单数据的值,作为cache_key就好了。
自定义函数:
def cache_key():
args = request.args
key = request.path + '?' + urllib.parse.urlencode([
(k, v) for k in sorted(args) for v in sorted(args.getlist(k))
])
return key
这样写比较复杂,但好处是即使表单数据顺序打乱,在转换为cache_key时也sorted()
过的,不会出现冗余cache的情况。
结合起来就是:
from flask import Flask, request
import time
from flask.ext.cache import Cache
import urllib.parse
app = Flask(__name__)
cache = Cache(app, config={'CACHE_TYPE': 'simple'})
def cache_key():
args = request.args
key = request.path + '?' + urllib.parse.urlencode([
(k, v) for k in sorted(args) for v in sorted(args.getlist(k))
])
return key
@app.route('/')
@cache.cached(key_prefix=cache_key)
def root():
t = request.args['s']
return t + ' ' + str(time.time())
if __name__ == '__main__':
app.debug = True
app.run()
这样就实现了带表单数据GET请求的缓存,至于timeout之类的自己再去调整咯。
另一种简单一些但可能产生冗余的自定义函数
如果能够控制GET表单数据的顺序,可以使用下面这个:
def cache_key():
key = request.path + request.url.rsplit('/', 1)[-1]
return key
甚至,如果能够确定请求不会产生干扰,可以考虑:
def cache_key():
key = request.url.rsplit('/', 1)[-1]
return key
使用文件系统缓存
注意到上述'CACHE_TYPE': 'simple'
,一般是作为测试环境下的缓存,实际生产环境下不要使用。
werkzeug支持的缓存类别有NullCache
、SimpleCache
、MemcachedCache
、GAEMemcachedCache
、RedisCache
和FileSystemCache
其中FileSystemCache
就是将每个cache作为文件缓存到磁盘,如果应对查询类app,且大多数查询速度明显不及访问磁盘,且服务器内存较小,不足以支持内存类缓存的话,可以考虑FileSystemCache
。
示例:
from flask import Flask, request
import time
from flask.ext.cache import Cache
import urllib.parse
app = Flask(__name__)
filesystem = {
'CACHE_TYPE': 'filesystem',
'CACHE_DIR': './flask_cache',
'CACHE_DEFAULT_TIMEOUT': 922337203685477580,
'CACHE_THRESHOLD': 922337203685477580
}
cache = Cache(app, config=filesystem)
def cache_key():
args = request.args
key = request.path + '?' + urllib.parse.urlencode([
(k, v) for k in sorted(args) for v in sorted(args.getlist(k))
])
return key
@app.route('/')
@cache.cached(key_prefix=cache_key)
def root():
t = request.args['s']
return t + ' ' + str(time.time())
if __name__ == '__main__':
app.debug = True
app.run()
关键点在于config
中的配置,其中CACHE_DEFAULT_TIMEOUT
和CACHE_THRESHOLD
设置的相当于无穷大,是因为每次GET请求查询返回的数据都是确定的,而且磁盘空间相对cache而言是无穷大,所以可以这样配置作为高速缓存。
参考:
Flask-Cache Flask-Cache 0.12 documentation
Cache Werkzeug Documentation (0.10)
Incoming Request Data API Flask Documentation (0.10)