[python]使用flask-caching缓存数据

简介

Flask-Caching 是 Flask 的一个扩展,为任何 Flask 应用程序添加了对各种后端的缓存支持。它基于 cachelib 运行,并通过统一的 API 支持 werkzeug 的所有原始缓存后端。开发者还可以通过继承 flask_caching.backends.base.BaseCache 类来开发自己的缓存后端。​​​​​​​

安装

pip install Flask-Caching

设置

缓存通过缓存实例来管理

from flask import Flask
from flask_caching import Cacheconfig = {"DEBUG": True,          # some Flask specific configs"CACHE_TYPE": "SimpleCache",  # Flask-Caching related configs"CACHE_DEFAULT_TIMEOUT": 300
}
app = Flask(__name__)
# tell Flask to use the above defined config
app.config.from_mapping(config)
cache = Cache(app)

也可以使用init_app来延后配置缓存实例

cache = Cache(config={'CACHE_TYPE': 'SimpleCache'})app = Flask(__name__)
cache.init_app(app)

还可以提供一个备用的配置字典,如果有多个Cache缓存实例,每个实例使用不同的后端,这将非常有用。

#: Method A: During instantiation of class
cache = Cache(config={'CACHE_TYPE': 'SimpleCache'})
#: Method B: During init_app call
cache.init_app(app, config={'CACHE_TYPE': 'SimpleCache'})

缓存视图函数

使用cached()装饰器缓存视图函数,默认使用path作为缓存的key

@app.route("/")
@cache.cached(timeout=50)
def index():return render_template('index.html')

cached 装饰器还有另一个可选参数叫做 unless。这个参数接受一个可调用对象,它返回 True 或 False。如果 unless 返回 True,那么将完全跳过缓存机制。

为了在视图中动态确定超时时间,可以返回 CachedResponse,这是 flask.Response 的子类。

@app.route("/")
@cache.cached()
def index():return CachedResponse(response=make_response(render_template('index.html')),timeout=50,)

缓存插拔式视图类

from flask.views import Viewclass MyView(View):@cache.cached(timeout=50)def dispatch_request(self):return 'Cached for 50s'

缓存其它函数

使用相同的 @cached 装饰器,还可以缓存其他非视图相关的函数的结果。需要注意替换 key_prefix,否则它将使用 request.path 作为 cache_key。键控制从缓存中获取什么内容。例如,如果一个键在缓存中不存在,将会在缓存中创建一个新的键值对条目。否则,将会返回该键的值(即缓存的结果)。

@cache.cached(timeout=50, key_prefix='all_comments')
def get_all_comments():comments = do_serious_dbio()return [x.author for x in comments]cached_comments = get_all_comments()

自定义缓存键

有时您希望为每个路由定义自己的缓存键。使用 @cached 装饰器,您可以指定如何生成这个键。当缓存键不应仅仅是默认的 key_prefix,而是必须从请求中的其他参数派生时,这可能会非常有用。例如,在缓存 POST 路由时,缓存键应该根据请求中的数据而不仅仅是路由或视图本身来确定,这时就可以使用这个功能。

def make_key():"""A function which is called to derive the key for a computed value.The key in this case is the concat value of all the json requestparameters. Other strategy could to use any hashing function.:returns: unique string for which the value should be cached."""user_data = request.get_json()return ",".join([f"{key}={value}" for key, value in user_data.items()])@app.route("/hello", methods=["POST"])
@cache.cached(timeout=60, make_cache_key=make_key)
def some_func():....

记忆化

在记忆化中,函数参数也会包含在cache_key

注意:对于不接收参数的函数来说,cached() 和 memoize() 实际上是相同的。

Memoize 也适用于方法,因为它会将 self或 cls 参数的身份作为缓存键的一部分。

记忆化背后的理论是,如果你有一个函数需要在一次请求中多次调用,那么它只会在第一次使用这些参数调用该函数时进行计算。例如,一个 sqlalchemy 对象用来确定一个用户是否具有某个角色。在一次请求中,你可能需要多次调用这个函数。为了避免每次需要这些信息时都访问数据库,你可能会做如下操作:

class Person(db.Model):@cache.memoize(50)def has_membership(self, role_id):return Group.query.filter_by(user=self, role_id=role_id).count() >= 1

将可变对象(类等)作为缓存键的一部分可能会变得棘手。建议不要将对象实例传递给记忆化函数。然而,memoize 会对传入的参数执行 repr(),因此如果对象有一个返回唯一标识字符串的 __repr__ 函数,该字符串将被用作缓存键的一部分。

例如,一个 sqlalchemy 的 person 对象,返回数据库 ID 作为唯一标识符的一部分:

class Person(db.Model):def __repr__(self):return "%s(%s)" % (self.__class__.__name__, self.id)

删除记忆化缓存

您可能需要按函数删除缓存。使用上述示例,假设您更改了用户的权限并将其分配给某个角色,但现在您需要重新计算他们是否拥有某些成员资格。您可以使用 delete_memoized() 函数来实现这一点:

cache.delete_memoized(user_has_membership)

如果仅将函数名称作为参数提供,那么该函数的所有记忆化版本都将失效。然而,您可以通过提供与缓存时相同的参数值来删除特定的缓存。在下面的示例中,只有用户角色的缓存被删除:

user_has_membership('demo', 'admin')
user_has_membership('demo', 'user')cache.delete_memoized(user_has_membership, 'demo', 'user')

如果一个类方法被记忆化,您必须将类作为第一个 *args 参数提供。

class Foobar(object):@classmethod@cache.memoize(5)def big_foo(cls, a, b):return a + b + random.randrange(0, 100000)cache.delete_memoized(Foobar.big_foo, Foobar, 5, 2)

缓存Jinja2模板

基本使用

{% cache [timeout [,[key1, [key2, ...]]]] %}
...
{% endcache %}

默认情况下,“模板文件路径” + “块开始行”的值被用作缓存键。此外,键名也可以手动设置。键会连接成一个字符串,这样可以避免在不同模板中评估相同的块。

将超时设置为 None 以表示没有超时,但可以使用自定义键。

{% cache None, "key" %}
...
{% endcache %}

设置timeoutdel来删除缓存值

{% cache 'del', key1 %}
...
{% endcache %}

如果提供了键,您可以轻松生成模板片段的键,并在模板上下文外部删除它。

from flask_caching import make_template_fragment_key
key = make_template_fragment_key("key1", vary_on=["key2", "key3"])
cache.delete(key)

考虑使用render_form_fieldrender_submit

{% cache 60*5 %}
<div><form>{% render_form_field(form.username) %}{% render_submit() %}</form>
</div>
{% endcache %}

清空缓存

清空应用缓存的简单示例

from flask_caching import Cachefrom yourapp import app, your_cache_configcache = Cache()def main():cache.init_app(app, config=your_cache_config)with app.app_context():cache.clear()if __name__ == '__main__':main()

某些后端实现不支持完全清除缓存。此外,如果您不使用键前缀,一些实现(例如 Redis)会清空整个数据库。请确保您没有在缓存数据库中存储任何其他数据。

显式缓存数据

数据可以通过直接使用代理方法如 Cache.set() 和 Cache.get() 来显式缓存。通过 Cache 类还有许多其他可用的代理方法。

@app.route("/html")
@app.route("/html/<foo>")
def html(foo=None):if foo is not None:cache.set("foo", foo)bar = cache.get("foo")return render_template_string("<html><body>foo cache: {{bar}}</body></html>", bar=bar)

基本使用示例

from flask import Flask
from flask_caching import Cache
import timeflask_cache = Cache(config={'CACHE_TYPE': 'SimpleCache'})app = Flask(__name__)fake_db = {"zhangsan": "qwerty"
}def do_io(username: str):time.sleep(0.01)return fake_db.get(username, "")@app.get("/user/<username>")
def get_user(username):if data := flask_cache.get(username):print(f"getting data from cache, username: {username}")return dataelse:print("data not found in cache")db_data = do_io(username)flask_cache.set(username, db_data, timeout=10)return db_dataif __name__ == "__main__":flask_cache.init_app(app)app.run("127.0.0.1", 8000)
  • 测试
wrk -t1 -c10 -d30s http://127.0.0.1:8000/user/zhangsan

SimpleCache在gunicorn中的问题

gunicorn会创建多个子进程,子进程之间是否共享simplecache?

先写一个普通的service,暴露两个api

  • GET /cache/<key_name>: 根据key name获取缓存值
  • POST /cache: 添加缓存
from flask import Flask, request
from flask_caching import Cache
from typing import Optionalflask_config = {"CACHE_TYPE": "SimpleCache","CACHE_DEFAULT_TIMEOUT": 300
}app = Flask(__name__)
app.config.from_mapping(flask_config)
cache = Cache(app)@app.get("/cache/<foo>")
def get_cached_data(foo: Optional[str]):if not foo:return "foo is None\n"cache_rst = cache.get(foo)if not cache_rst:return f"key {foo} is not in cache\n"return f"find key {foo} in cache, value is {cache_rst}\n"@app.post("/cache")
def set_cached_data():try:req_body = request.get_json()except Exception as e:raise Exception(f"request body is not json format, error: {e}\n") from ekey = req_body.get("key", None)value = req_body.get("value", None)if not key or not value:return "key or value is None\n"if cached_data := cache.get(key):return f"key {key} is already in cache, value is {cached_data}\n"cache.set(key, value)return f"set key {key} in cache, value is {value}\n"if __name__ == "__main__":app.run(host="0.0.0.0", port=5000)

先用flask默认运行方式运行,测试接口是否正常

# 添加键值对缓存
curl -X POST http://127.0.0.1:5000/cache -H 'Content-Type: application/json' -d '{"key": "k1", "value": "v1"}'# 获取缓存
curl http://127.0.0.1:5000/cache/k1

如果响应正常的话,再用gunicorn启动。如下命令将启动4个工作子进程

gunicorn demo:app -b 0.0.0.0:5000 -w 4 -k gevent --worker-connections 2000

请求测试。第一个请求设置缓存,后面四个获取缓存,可见工作进程之间并不共享flask_cache。如果用gunicorn或多个flask service实例,最好换其他cache type,比如RedisCache。

$ curl -X POST http://127.0.0.1:5000/cache -H 'Content-Type: application/json' -d '{"key": "k1", "value": "v1"}'
set key k1 in cache, value is v1$ curl http://127.0.0.1:5000/cache/k1
key k1 is not in cache$ curl http://127.0.0.1:5000/cache/k1
key k1 is not in cache$ curl http://127.0.0.1:5000/cache/k1
find key k1 in cache, value is v1$ curl http://127.0.0.1:5000/cache/k1
key k1 is not in cache

关注灵活就业新业态,关注公账号:贤才宝(贤才宝https://www.51xcbw.com)   

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/64760.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于UNITY3D的照片墙演示项目技术分享

unity实现超大图片墙演示,由于拥有海量图片&#xff0c;使用了CPU 多线程&#xff0c;unity dots技术&#xff0c;图片组成文字部分&#xff0c;使用了点阵图技术&#xff0c;提取文字像素。 &#xff08;关于点阵介绍&#xff09; 点阵字体是把每一个字符都分成1616或2424个点…

设计模式-访问者设计模式

介绍 访问者模式&#xff08;Visitor&#xff09;&#xff0c;表示一个作用于某对象结构中的各元素的操作&#xff0c;它使你可以在不改变个元素的类的前提下定义作用于这些元素的新操作。 问题&#xff1a;在一个机构里面有两种员工&#xff0c;1.Teacher 2.Engineer 员…

ISO/IEC 25010:2023 系统和软件的质量模型(产品质量模型)

本文是对ISO/IEC 25010 2023年底第2版&#xff1a;系统和软件工程-系统和软件质量要求与评估 (SQuaRE)-产品质量模型一文的转载&#xff0c;此外还参考了ISO/IEC 25019:2023(en) Systems and software engineering — Systems and software Quality Requirements and Evaluatio…

【零基础保姆级教程】制作自己的数据集——Labelimg的安装与使用及常见的报错解决方法

1.是什么 LabelImg 是一个图形化的图像标注工具&#xff0c;主要用于机器学习和计算机视觉领域中的数据预处理。它是由 GitHub 用户 tzutalin 开发的开源项目&#xff0c;基于 Python 和 Qt 框架编写。LabelImg 允许用户手动为图像添加边界框&#xff08;bounding boxes&#…

【java基础系列】实现数字的首位交换算法

在java中&#xff0c;手写实现一个数字的首位交换算法实现 实现效果 实现代码 核心业务代码 public static void main(String[] args) {int[] arr {1,2,3,4,5};int temp arr[0];for (int i 0; i < arr.length; i) {System.out.print(arr[i]);}System.out.println(&quo…

【java面向对象编程】第七弹----Object类、类变量与类方法

笔上得来终觉浅,绝知此事要躬行 &#x1f525; 个人主页&#xff1a;星云爱编程 &#x1f525; 所属专栏&#xff1a;javase &#x1f337;追光的人&#xff0c;终会万丈光芒 &#x1f389;欢迎大家点赞&#x1f44d;评论&#x1f4dd;收藏⭐文章 目录 一、Object类 1.1equa…

GO--堆(have TODO)

堆 堆&#xff08;Heap&#xff09;是一种特殊的数据结构。它是一棵完全二叉树&#xff08;完全二叉树是指除了最后一层外&#xff0c;每一层上的节点数都是满的&#xff0c;并且最后一层的节点都集中在左边&#xff09;&#xff0c;结放在数组&#xff08;切片&#xff09;中…

java开发入门学习五-流程控制

流程控制语句 if&#xff0c; if...else&#xff0c; if..else if..else 与前端相同 略 switch case 与前端不同的是case不能使用表达式&#xff0c;使用表达式会报错 class TestSwitch {public static void main(String[] args) {// switch 表达式只能是特定的数据类型…

豆包MarsCode测评:编程效率再提升

豆包MarsCode测评&#xff1a;编程效率再提升 本文正在参与豆包MarsCode AI 编程体验家活动 随着人工智能技术的发展&#xff0c;编程的方式也在悄然发生变化。最近&#xff0c;豆包推出的 AI 编程工具 MarsCode 在开发者社区引发了不小的关注。这是一款支持多种主流编程语言…

FFmpeg 框架简介和文件解复用

文章目录 ffmpeg框架简介libavformat库libavcodec库libavdevice库 复用&#xff08;muxers&#xff09;和解复用&#xff08;demuxers&#xff09;容器格式FLVScript Tag Data结构&#xff08;脚本类型、帧类型&#xff09;Audio Tag Data结构&#xff08;音频Tag&#xff09;V…

Unity开发哪里下载安卓Android-NDK-r21d,外加Android Studio打包实验

NDK下载方法&#xff08;是r21d,不是r21e, 不是abc, 是d版本呢) google的东西&#xff0c;居然是完全开源的 真的不是很多公司能做到&#xff0c;和那种伪搜索引擎是不同的 到底什么时候google才会开始造车 不过风险很多&#xff0c;最好不要合资&#xff0c;风险更大 Andr…

leetcode-128.最长连续序列-day14

为什么我感觉上述代码时间复杂度接近O(2n), 虽然有while循环&#xff0c;但是前面有个if判断&#xff0c;能进入while循环的也不多&#xff0c;while循环就相当于两个for循环&#xff0c;但不是嵌套类型的&#xff1a; 变量作用域问题&#xff1a;

人工智能入门是先看西瓜书还是先看花书?

在人工智能入门时&#xff0c;关于先看《机器学习》&#xff08;西瓜书&#xff09;还是先看《深度学习》&#xff08;花书&#xff09;的问题&#xff0c;实际上取决于个人的学习目标和背景。 《机器学习》&#xff08;西瓜书&#xff09;由周志华教授撰写&#xff0c;是一本…

B 站数据库负责人赵月顺:助力海内外业务增长,百套 TiDB 的选型与运维实战

导读 B 站对 TiDB 的应用已相当广泛&#xff0c;被应用在了 包括视频观看、一键三连、发送弹幕、撰写评论、阅读漫画以及视频后端的存储等场景&#xff0c; 目前拥有近 100 套集群。 本文由 B 站数据库负责人赵月顺撰写&#xff0c; 详细介绍了 B 站面临业务增长选择 TiDB 的…

二九(vue2-05)、父子通信v-model、sync、ref、¥nextTick、自定义指令、具名插槽、作用域插槽、综合案例 - 商品列表

1. 进阶语法 1.1 v-model 简化代码 App.vue <template><!-- 11-src-下拉封装 --><div class"app"><!-- <BaseSelect :cityId"selectId" changeId"handleChangeId"></BaseSelect> --><!-- v-model 简化…

flask-admin+Flask-WTF 实现实现增删改查

背景&#xff1a; flask-adminflask-wtf在网上可以搜索到很多资料&#xff0c;但有价值的很少&#xff0c;或许是太简单&#xff0c;或者是很少人这么用&#xff0c;或者。。。&#xff0c;本文将作者近礼拜摸索到的一点经验分享出来&#xff0c;给自己做个记录。 材料&#…

Linux下基于最新稳定版ESP-IDF5.3.2开发esp32s3入门任务间的通讯-消息队列【入门四】

继续上一篇任务创建 【Linux下基于最新稳定版ESP-IDF5.3.2开发esp32s3入门任务间的通讯-信号量【入门三】-CSDN博客】 今天要实现消息队列进行任务的通讯 一、从上一篇信号量通讯demo拷贝一份重命名&#xff0c;还是之前的两个任务&#xff0c;重命名了。 xTaskCreatePinned…

workman服务端开发模式-应用开发-后端api推送修改二

需要修改两个地方&#xff0c;第一个是总控制里面的续token延时&#xff0c;第二个是操作日志记录 一、总控续token延时方法 在根目录下app文件夹下controller文件夹下Base.php中修改isLoginAuth方法&#xff0c;具体代码如下&#xff1a; <?php /*** 总控制* User: 龙哥…

ReactPress 1.6.0:重塑博客体验,引领内容创新

ReactPress 是一个基于Next.js的博客&CMS系统&#xff0c; Github项目地址&#xff1a;https://github.com/fecommunity/reactpress 欢迎Star。 体验地址&#xff1a;http://blog.gaoredu.com/ 今天&#xff0c;我们自豪地宣布ReactPress 1.6.0版本的正式发布&#xff0c;…

重拾设计模式--外观模式

文章目录 外观模式&#xff08;Facade Pattern&#xff09;概述定义 外观模式UML图作用 外观模式的结构C 代码示例1C代码示例2总结 外观模式&#xff08;Facade Pattern&#xff09;概述 定义 外观模式是一种结构型设计模式&#xff0c;它为子系统中的一组接口提供了一个统一…