【问题标题】:Preserving global state in a flask application [duplicate]在烧瓶应用程序中保留全局状态[重复]
【发布时间】:2013-10-17 02:55:36
【问题描述】:

我正在尝试在我的flask 应用程序中保存一个缓存字典。

据我了解,Application Context,尤其是flask.g object 应该用于此。

设置:

import flask as f

app = f.Flask(__name__)

现在如果我这样做:

with app.app_context():
    f.g.foo = "bar"
    print f.g.foo

它打印bar

继续以下内容:

with app.app_context():
    print f.g.foo

AttributeError: '_AppCtxGlobals' object has no attribute 'foo'

我不明白,文档根本没有帮助。如果我正确阅读它们,状态应该被保留。

另一个想法我的想法是简单地使用模块范围的变量:

cache = {}

def some_function():
    cache['foo'] = "bar"

但似乎这些在每次请求时都会重置。

如何正确地做到这一点?

编辑: Flask 10.1

【问题讨论】:

  • 不,感谢您的链接,但这是一个如此简单的任务,我想保持低 atm 的依赖关系。
  • 我想我最终选择了浏览器 cookie。但是,当然欢迎您在找到解决方案后发布解决方案。
  • 会话可能是解决方案,但会话是按用户保留的,因此它有其局限性。其他选项是内存缓存。它是简单的 key->value 存储,易于配置并在所有线程和进程之间共享。然而,有时(就像我的情况一样)它有点太复杂了,所以我想只在全局范围内使用字典。风险是数据完整性(对我来说不是一个因素),但相同的字典将在某些进程之间共享。

标签: python flask global state


【解决方案1】:

根据您的问题,我认为您对“全球”的定义感到困惑。

在现有的 Flask 设置中,您有一个 Flask 服务器,它具有多个线程,并且可能有多个处理请求的进程。假设您有一个像“itemlist = []”这样的库存全局变量,并且您希望在每个请求中继续添加它 - 例如,每次有人向端点发出 POST 请求时。这在理论上和实践上都是完全可能的。这也是一个非常糟糕的主意。

问题在于您无法轻松控制哪些线程和进程“获胜” - 列表可能会以非常不稳定的顺序排列,或者完全损坏。所以现在你需要谈谈锁、互斥体和其他原语。这很难而且很烦人。

您应该尽可能保持网络服务器本身无状态。每个请求应该是完全独立的,并且不共享服务器中的任何状态。相反,请使用将为您处理状态的数据库或缓存层。这看起来更复杂,但实际上更简单。以 SQLite 为例;这很简单。

为了解决“flask.g”对象,它是一个基于每个请求的全局对象

http://flask.pocoo.org/docs/api/#flask.g

它在请求之间被“擦除干净”,不能用于在它们之间共享状态。

【讨论】:

  • 我了解数据库方法,但不熟悉缓存层。你能推荐一些缓存层库/框架吗?
  • 这不再是真的。从 flask.10 开始,flask.g 与应用程序上下文相关联,而不是请求上下文。 flask.pocoo.org/docs/0.12/api/#flask.g
  • 必须 -1 这个答案。在某些情况下,您希望在请求之间不刷新全局对象,例如,我想在内存中加载某些文件的路径并且完全对其进行修改。如果我确实想修改它,我将重新启动服务器。这个答案没有帮助。
  • 是的,我正在考虑加载一个大型预训练模型进行推理,加载时间很长,所以不能是无状态的。
  • @CpILL 运气好了吗?相同的用例(除非您的模型是动态的?)。文档非常不连贯,关于该主题的大多数 SO 问题都被关闭为 dups..
【解决方案2】:

我做了一些类似于你的“模块范围变量”的想法,我在烧瓶服务器中使用它来集成两个软件,我知道我只会有一个同时的“用户”(作为发件人软件)。

我的 app.py 如下所示:

from flask import Flask
from flask.json import jsonify
app = Flask(__name__)

cache = {}

@app.route("/create")
def create():
    cache['foo'] = 0
    return jsonify(cache['foo'])

@app.route("/increment")
def increment():
    cache['foo'] = cache['foo'] + 1
    return jsonify(cache['foo'])

@app.route("/read")
def read():
    return jsonify(cache['foo'])

if __name__ == '__main__':
    app.run()

你可以这样测试:

import requests

print(requests.get('http://127.0.0.1:5000/create').json())
print(requests.get('http://127.0.0.1:5000/increment').json())
print(requests.get('http://127.0.0.1:5000/increment').json())
print(requests.get('http://127.0.0.1:5000/read').json())
print(requests.get('http://127.0.0.1:5000/increment').json())
print(requests.get('http://127.0.0.1:5000/create').json())
print(requests.get('http://127.0.0.1:5000/read').json())

输出:

0
1
2
2
3
0
0

谨慎使用,因为我预计这不会在适当的多用户 Web 服务器环境中运行。

【讨论】:

  • 这将适用于您有一个全球单一 uwsgi 工作人员的情况。您还必须担心对变量的多次访问,因此您可能必须根据使用情况实现锁定或类似功能。不过,它是包里的另一个工具。
  • 注意这种类型的测试...测试Flask服务器是单线程的,它是一个单一的进程,你将无法在这个测试中找到与多线程相关的问题。
  • 我是 Flask 的新手,为什么这种字典方法在请求之间持续存在但创建诸如 INT 之类的变量却没有?
  • @MajorMajor 在持久性方面声明一个 int 和一个字典没有区别。它们是否持续存在取决于它们的声明位置。如果您对此还有其他问题,我鼓励您提出一个新问题。
  • 我发现str 不坚持。注意!!
【解决方案3】:

这一行

with app.app_context():
    f.g.foo = "bar"

由于您使用的是“with”关键字,因此一旦执行此循环,它就会调用 AppContext 类的__exit__ 方法。见this。所以一旦完成,'foo'就会弹出。这就是为什么你没有它再次可用。你可以试试:

ctx = app.app_context()
f.g.foo = 'bar'
ctx.push()

在您调用以下命令之前,g.foo 应该可用

ctx.pop()

但我不确定您是否要将其用于缓存目的。

【讨论】:

  • 那我应该用什么?我认为这是在应用中存储全局值的方法吗?
  • 根据需要创建和销毁应用程序上下文。它永远不会在线程之间移动,也不会在请求之间共享。 (flask.pocoo.org/docs/appcontext)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-11-19
  • 2016-11-29
  • 2018-07-08
相关资源
最近更新 更多