需求说明
使用Flask-Caching扩展为Flask web程序页面做缓存,用以缩短同一页面的加载时间。缓存后端使用Redis服务。
当使用缓存时,用户第一次访问某一页面服务器会执行视图函数中的数据库查询、函数计算、模板渲染等工作,并将渲染后的页面数据以键值对的形式存储在Redis中,对于视图函数默认的键格式为view/<request.path> 。当用户再次访问同一个页面时,服务器将会直接将Redis中保存的页面数据值返回给客户端,而不必再次执行视图函数中的代码,从而缩短页面加载时间,提升用户体验。
页面缓存适用于用户需要经常访问、包含大量数据库查询或函数计算,但是数据却不经常变动的页面。因为数据即使变动了,在不清除就缓存且缓存未到期的情况下,用户只能看到旧的页面数据。
环境配置
windows开发环境:
- python3.7
- Flask 2.1.1
- Flask-Caching 1.10.1
- Flask-DebugToolbar 0.13.1
- redis 4.2.2(redis-py包)
Redis服务部署在Ubuntu18.04虚拟机中,版本为6.2.6。服务器IP为192.168.73.130
Flask-Caching
Flask-Caching扩展为Flask web程序提供了便捷的缓存支持,开发者只需要实例化Cache类 后,为视图函数附加对应的装饰器,就也可以方便的为视图函数添加缓存支持。常用的装饰器有:
- cached():
常用参数有:
- timeout:缓存的过期时间,单位为秒
- key_prefix:缓存保存的键,默认为
view/request.path ,可以自行指定 - unless:接收一个callable对象,当对象返回值为True是,不进行缓存
- query_string:对于附带查询参数的url,将该参数设置True,可以为查询参数计算md5值并加入键中,防止在不同查询参数下都返回同一个页面
- memoize()
用法和cached() 基本类似,主要区别是,memoize()可以对传入被装饰方法的参数进行区分,为不同参数创建不同的缓存,相同的参数调用时直接返回缓存值。
代码示例
Flask-Caching连接Redis配置
Flask-Caching连接Redis服务,需要借助python的redis-py包,所以使用前先要安装pip install redis . 在环境变量中设置redis服务器对应的信息:
CACHE_REDIS_HOST = '192.168.73.130'
CACHE_REDIS_PORT = '6379'
CACHE_REDIS_PASSWORD = 'your password'
CACHE_REDIS_DB = '0'
为了方便,也可以直接使用redis连接的URL:
CACHE_REDIS_URL = 'redis://:<your password>@192.168.73.130:6379/0'
最后的0表示使用redis的0号数据库保存数据(redis默认有16个数据库0-15)
Flask程序代码
import os
import time
from flask import Flask, render_template, url_for, redirect, request
from flask_caching import Cache
from flask_debugtoolbar import DebugToolbarExtension
app = Flask(__name__)
app.jinja_env.trim_blocks = True
app.jinja_env.lstrip_blocks = True
app.config['SECRET_KEY'] = os.getenv('SECRET_KEY', 'dev#secret_key')
app.config['DEBUG_TB_INTERCEPT_REDIRECTS'] = False
cache = Cache(app, config={'CACHE_TYPE': 'RedisCache', 'CACHE_REDIS_URL': os.getenv('CACHE_REDIS_URL')})
toolbar = DebugToolbarExtension(app)
@app.route('/')
def index():
return render_template('index.html')
@app.route('/foo')
def foo():
"""不使用缓存"""
time.sleep(1)
return render_template('foo.html')
@app.route('/bar')
@cache.cached(timeout=10 * 60)
def bar():
time.sleep(1)
return render_template('bar.html')
@app.route('/baz')
@cache.cached(timeout=60 * 60)
def baz():
time.sleep(1)
return render_template('baz.html')
@app.route('/qux')
@cache.cached(query_string=True)
def qux():
time.sleep(1)
page = request.args.get('page', 1, type=int)
return render_template('qux.html', page=page)
启动程序,访问使用缓存的页面,在重载后通过DebugToolbar查看加载时间可以发现时间明显缩短。
清除缓存
- cache.delete():传入对应的键删除cached类型缓存
- cache.delete_memoize():传入对应键删除memoize类型缓存
- cache.clear():清空所有缓存,一般不使用
@app.route('/update/bar')
def update_bar():
"""为bar端点清除缓存"""
cache.delete('view/%s' % url_for('bar'))
flash('Cached data for bar have been deleted.')
return redirect(url_for('index'))
@app.route('/update/baz')
def update_baz():
cache.delete('view/%s' % url_for('baz'))
flash('Cached data for baz have been deleted.')
return redirect(url_for('index'))
@app.route('/update/all')
def update_all():
"""清除所有缓存"""
cache.clear()
flash('All cached data deleted.')
return redirect(url_for('index'))
问题记录
连接redis数据库时,程序报错Redis运行在保护模式。 Redis is running in protected mode because protected mode is enabled, no bind address was specified, no authentication password is requested to clients. In this mode connections are only accepted from the lookback interface. If you want to connect from external computers to Redis you may adopt one of the following solutions: 1) Just disable protected mode sending the command 'CONFIG SET protected-mode no' from the loopback interface by connecting to Redis from the same host the server is running, however MAKE SURE Redis is not publicly accessible from internet if you do so. Use CONFIG REWRITE to make this change permanent. 2) Alternatively you can just disable the protected mode by editing the Redis configuration file, and setting the protected mode option to 'no', and then restarting the server. 3) If you started the server manually just for testing, restart it with the --portected-mode no option. 4) Setup a bind address or an authentication password. NOTE: You only need to do one of the above things in order for the server to start accepting connections from the outside.
在提示信息中,已经告诉了我们如何解决这个问题,如果是单纯地进行测试,可以直接将保护模式关闭,然后重启服务即可。
如果想更安全一点,可以先使用redis-cli客户端进入服务,使用config set requirepass <password> 命令为redis设置密码;然后将配置文件/etc/redis/redis.conf 中的protected-mode 设为yes,同时将bind 选项配置为redis服务器的网卡地址(这里是192.168.73.130),然后重启服务systemctl restart redis-server 。
之后使用redis-cli时先使用auth <password> 命令登录;使用url连接时,使用redis://:<password>@ip:port/<database> 进行连接。
参考
《Python Web开发实战:入门、进阶与原理解析》 李辉
|