2013-10-09 58 views
29

我试图在我的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" 

但似乎这些得到的每个请求重置。

如何正确执行此操作?

编辑:瓶10.1

+0

你检出了[Flask-Cache](http://pythonhosted.org/Flask-Cache/)吗? –

+7

不,谢谢你的链接,但这是一个非常简单的任务,我想保持低依赖关系。 – Profpatsch

+0

您使用的是什么版本的Flask? – codegeek

回答

40

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

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

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

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

为了解决'flask.g'对象,这是一个全局对象,每个请求的基础上

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

它的“擦干净”的要求之间,不能用于它们之间共享状态。

+1

我理解数据库方法,但不熟悉缓存层。你能推荐一些缓存层库/框架吗? – max

+0

似乎在Flask 0.11'flask.g'中绑定了应用程序上下文。它会改变什么吗? – omikron

+0

我喜欢SQlite,但我不认为你想用它来并发使用,在需要写入的应用程序中。写入锁定数据库。我真的认为,有点像redis-server会做这份工作,特别是上市。另一种(也许是愚蠢的)方法可能是将数据单独写入文件夹。所以,每个线程都会写它自己的文件,但是当你检索数据时,你将读取所有文件。这样你总是可以获得所有数据,而不会阻止新的写入。 – erm3nda

7

此行

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

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

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

,直到调用以下,g.foo应提供

ctx.pop() 

我howver不知道,如果你想用这个缓存的目的。

+4

那我该用什么?我认为这是将全球价值存储在应用程序中的手段? – Profpatsch

+2

应用程序上下文根据需要创建并销毁。它永远不会在线程间移动,也不会在请求之间共享。 (http://flask.pocoo.org/docs/appcontext/) –

3

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

我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服务器环境中运行。

+2

这将适用于全球唯一的uwsgi工作者。您还必须担心对变量的多次访问,因此您可能必须根据使用情况实施锁定或类似操作。这是包里的另一个工具。 –

相关问题