用 Flask 来写个轻博客 (27) — 使用 Flask-Cache 实现网页缓存加速

简介: 目录目录前文列表扩展阅读Flask-Cache应用 Flask-Cache 实现视图函数缓存缓存无参数的普通函数缓存带参数的普通函数缓存无动态参数的视图函数缓存带动态参数的视图函数前文列表用 Flask 来写个轻博客 (1)...

目录

前文列表

用 Flask 来写个轻博客 (1) — 创建项目
用 Flask 来写个轻博客 (2) — Hello World!
用 Flask 来写个轻博客 (3) — (M)VC_连接 MySQL 和 SQLAlchemy
用 Flask 来写个轻博客 (4) — (M)VC_创建数据模型和表
用 Flask 来写个轻博客 (5) — (M)VC_SQLAlchemy 的 CRUD 详解
用 Flask 来写个轻博客 (6) — (M)VC_models 的关系(one to many)
用 Flask 来写个轻博客 (7) — (M)VC_models 的关系(many to many)
用 Flask 来写个轻博客 (8) — (M)VC_Alembic 管理数据库结构的升级和降级
用 Flask 来写个轻博客 (9) — M(V)C_Jinja 语法基础快速概览
用 Flask 来写个轻博客 (10) — M(V)C_Jinja 常用过滤器与 Flask 特殊变量及方法
用 Flask 来写个轻博客 (11) — M(V)C_创建视图函数
用 Flask 来写个轻博客 (12) — M(V)C_编写和继承 Jinja 模板
用 Flask 来写个轻博客 (13) — M(V)C_WTForms 服务端表单检验
用 Flask 来写个轻博客 (14) — M(V)C_实现项目首页的模板
用 Flask 来写个轻博客 (15) — M(V)C_实现博文页面评论表单
用 Flask 来写个轻博客 (16) — MV(C)_Flask Blueprint 蓝图
用 Flask 来写个轻博客 (17) — MV(C)_应用蓝图来重构项目
用 Flask 来写个轻博客 (18) — 使用工厂模式来生成应用对象
用 Flask 来写个轻博客 (19) — 以 Bcrypt 密文存储账户信息与实现用户登陆表单
用 Flask 来写个轻博客 (20) — 实现注册表单与应用 reCAPTCHA 来实现验证码
用 Flask 来写个轻博客 (21) — 结合 reCAPTCHA 验证码实现用户注册与登录
用 Flask 来写个轻博客 (22) — 实现博客文章的添加和编辑页面
用 Flask 来写个轻博客 (23) — 应用 OAuth 来实现 Facebook 第三方登录
用 Flask 来写个轻博客 (24) — 使用 Flask-Login 来保护应用安全
用 Flask 来写个轻博客 (25) — 使用 Flask-Principal 实现角色权限功能
用 Flask 来写个轻博客 (26) — 使用 Flask-Celery-Helper 实现异步任务

扩展阅读

Flask-Cache — Flask-Cache 0.13 documentation
缓存— Flask 0.10.1 documentation

Flask-Cache

网页的加载时间决定了你的网站是否能够受到用户的欢迎, 现在为止我们 blog 应用模板的每次渲染都会查询数据库, 这是不合理的. 对一些数据不被经常改变的页面我们应该引入缓存的机制来减少对数据库的访问.

Flask-Cache 能够让我们把视图函数返回的结果缓存下来, 以后再次渲染时, 只需要从缓存中取出这些结果就可以了, 无须重复的渲染.

  • 安装
pip install Flask-Cache
pip freeze > requirement.txt

应用 Flask-Cache 实现视图函数缓存

  • 初始化
    vim jmilkfansblog/extensions.py
from flask.ext.cache import Cache


...

#### Create the Flask-Cache's instance
cache = Cache()
  • 将 cache 对象注册到 app 对象中
    vim jmilkfansblog/__init__.py
from jmilkfansblog.extensions import cache

...

def create_app(object_name):
    """Create the app instance via `Factory Method`"""
...

    #### Init the Flask-Cache via app object
    cache.init_app(app)
  • 配置 Cache 类型
class DevConfig(Config):
    """Development config class."""
...

    #### Flask-Cache's config
    CACHE_TYPE = 'simple'

Flask-Cache 定义缓存的方式就是为你需要缓存的函数加上缓存装饰器, 这样的装饰器由多种类型, 对应装饰在不同的函数或视图函数中.

缓存无参数的普通函数

vim jmilkfansblog/controllers/blog.py

@cache.cached(timeout=7200, key_prefix='sidebar_data')
def sidebar_data():
    """Set the sidebar function."""

    # Get post of recent
    recent = db.session.query(Post).order_by(
            Post.publish_date.desc()
        ).limit(5).all()

    # Get the tags and sort by count of posts.
    top_tags = db.session.query(
            Tag, func.count(posts_tags.c.post_id).label('total')
        ).join(
            posts_tags
        ).group_by(Tag).order_by('total DESC').limit(5).all()
    return recent, top_tags

NOTE 1: 装饰器 @ cache.cached() 的形参数 key_prefix 是必须的, 并且传入的值是唯一的, 这样 Flask-Cache 才能够正确的返回该函数缓存的值.

NOTE 2: 该函数被缓存的时间设定为 2 小时, 这是因为该函数返回的值不被经常改变, 而且就算是被改变了也不会对整体页面的显示有大的影响.

缓存带参数的普通函数

装饰器 cache.cached() 不会考虑到参数的问题, 如果函数在接收到不同的实参后还返回相同结果的话, 这明显是不科学的. 为了解决这个问题, 我们使用 @cache.memoize() 装饰器来定义.
vim jmilkfansblog/models.py

    @staticmethod
    @cache.memoize(60)
    def verify_auth_token(token):
        """Validate the token whether is night."""

        serializer = Serializer(
            current_app.config['SECRET_KEY'])
        try:
            # serializer object already has tokens in itself and wait for 
            # compare with token from HTTP Request /api/posts Method `POST`.
            data = serializer.loads(token)
        except SignatureExpired:
            return None
        except BadSignature:
            return None

        user = User.query.filter_by(id=data['id']).first()
        return user

NOTE 1: 装饰器 @cache.memoize() 不仅仅会缓存运行的结果, 还缓存调用时的参数, 所以在函数接受到相同的参数时, 就会将缓存中该参数对应的结果返回.

NOTE 2: @cache.momoize() 非常不适用于返回结果依赖全局变量或依赖数据库查询的函数, 因为在这很容易导致数据竞态, 即输入了相同的参数, 可能会返回不同的结果. 最适合被缓存的函数是纯函数(在传入的参数相同的情况下会输出相同的结果)

缓存无动态参数的视图函数

# Use the Blueprint object to set the Route URL
# Register the view function into blueprint
@blog_blueprint.route('/')
@blog_blueprint.route('/<int:page>')
@cache.cached(timeout=60)
def home(page=1):
    """View function for home page"""

    posts = Post.query.order_by(
        Post.publish_date.desc()
    ).paginate(page, 10)

    recent, top_tags = sidebar_data()

    return render_template('home.html',
                           posts=posts,
                           recent=recent,
                           top_tags=top_tags)

NOTE 1: 这里仍然是使用装饰器 @cache.cached(), 但却不必需要指定 key_prefix 形参.
NOTE 2: timeout 表示该视图函数返回的结果将会被缓存的时长.

缓存带动态参数的视图函数

因为该类型视图函数的参数 post_id 是被动态解析成为 URL(/post/<string:post_id>) 的, 所以并不是每一次调用都会返回相同的结果, 这样就需要我们进一步的确认, 只有在确定两次不同请求的都是同一个目标(即 URL)时, 才会将缓存返回, 否则创建一个新的缓存.

def make_cache_key(*args, **kwargs):
    """Dynamic creation the request url."""

    path = request.path
    args = str(hash(frozenset(request.args.items())))
    return (path + args).encode('utf-8')
...

@blog_blueprint.route('/post/<string:post_id>', methods=('GET', 'POST'))
@cache.cached(timeout=60, key_prefix=make_cache_key)
def post(post_id):
    """View function for post page"""

    # Form object: `Comment`
    form = CommentForm()

...

NOTE 1: 这里我们仍然使用了 key_prefix 参数来进行定位, 而且该形参除了可以接受 String 类型对象之后, 也可以接收一个函数.

NOTE 2: 函数 make_cache_key() 会动态的生成一个唯一的缓存键, 用于确定这一次请求的 URL 是否存在缓存, 如果存在则返回, 如果不存在则新建. 所以在该函数中我们使用了请求 path + hash 字符串结合而成的唯一字符串作为缓存键.

相关文章
|
缓存 数据格式
实现LRU缓存的三种方式(建议收藏)
LRU全称为Least Recently Used,即最近使用的。针对的是在有限的内存空间内,只缓存最近使用的数据(即get和set的数据),超过有限内存空间的数据将会被删除。这个在面试题中也是常会被问到的内容,接下来就看看怎么来实现。
964 0
实现LRU缓存的三种方式(建议收藏)
|
1月前
|
存储 缓存 运维
LAMP架构调优(五)——网页缓存设置
LAMP架构调优(五)——网页缓存设置
10 1
|
7月前
|
机器人 UED Python
基于Python+Flask实现一个简易网页验证码登录系统案例
基于Python+Flask实现一个简易网页验证码登录系统案例
106 0
基于Python+Flask实现一个简易网页验证码登录系统案例
|
机器学习/深度学习 缓存 Oracle
【数据库设计与实现】第7章:缓存与检查点
缓存与检查点设计原则数据缓冲区与检查点是相辅相成的,所以放在同一个章节介绍。由于CPU与持久化设备之间存在巨大的速度差距,所以在内存中引入缓冲区缩小这个差距。从读的角度来看,将热点数据或预判用户可能读取的数据提前加载到内存中,从而将持久化设备的读时延和带宽提升至内存的时延和带宽。从写的角度来看,直接修改缓冲区中的数据而不是磁盘中的数据,可以带来两方面的优势。其一,将持久化设备的写时延和带宽提升至内
【数据库设计与实现】第7章:缓存与检查点
|
缓存 JSON Java
java 实现读取txt文件,反射创建对象,android 手机缓存文件目录
java 实现读取txt文件,反射创建对象,android 手机缓存文件目录
339 1
java 实现读取txt文件,反射创建对象,android 手机缓存文件目录
|
存储 缓存 算法
基于LinkedHashMap实现LRU缓存
基于LinkedHashMap实现LRU缓存
167 0
基于LinkedHashMap实现LRU缓存
|
存储 人工智能 缓存
2022云栖精选—云原生驱动数据抽象 与缓存加速开源技术发展
顾 荣 南京大学 计算机科学与技术系 计算机软件新技术国家重点实验室(南京大学
432 0
2022云栖精选—云原生驱动数据抽象 与缓存加速开源技术发展
|
存储 缓存 JSON
python flask-caching模块缓存详解
python flask-caching模块缓存详解
|
缓存 算法 安全
如何使用 LinkedHashMap 实现 LRU 缓存?
在上一篇文章里,我们聊到了 HashMap 的实现原理和源码分析,在源码分析的过程中,我们发现一些 LinkedHashMap 相关的源码,当时没有展开,现在它来了。 那么,LinkedHashMap 与 HashMap 有什么区别呢?其实,LinkedHashMap 的使用场景非常明确 —— LRU 缓存。今天,我们就来讨论 LinkedHashMap 是如何实现 LRU 缓存的。
135 0

热门文章

最新文章