[英]Are global variables thread-safe in Flask? How do I share data between requests?
在我的应用中,一个普通的object的state是通过请求改变的,响应依赖于state。
class SomeObj():
def __init__(self, param):
self.param = param
def query(self):
self.param += 1
return self.param
global_obj = SomeObj(0)
@app.route('/')
def home():
flash(global_obj.query())
render_template('index.html')
如果我在我的开发服务器上运行它,我希望得到 1、2、3 等等。 如果同时从 100 个不同的客户端发出请求,那么 go 会不会出错? 预期的结果是 100 个不同的客户端每个看到一个从 1 到 100 的唯一数字。或者会发生这样的事情:
self.param
增加 1。self.param
再次递增。由于只有两个客户端,预期结果是 1 和 2,而不是 2 和 3。跳过了一个数字。
当我扩展我的应用程序时,这真的会发生吗? 我应该查看全局变量的哪些替代方案?
您不能使用全局变量来保存此类数据。 它不仅不是线程安全的,也不是进程安全的,而且生产环境中的 WSGI 服务器会产生多个进程。 如果您使用线程来处理请求,您的计数不仅会出错,而且还会根据处理请求的进程而有所不同。
使用 Flask 之外的数据源来保存全局数据。 数据库、memcached 或 redis 都是合适的单独存储区域,具体取决于您的需要。 如果您需要加载和访问 Python 数据,请考虑multiprocessing.Manager
。 您还可以将会话用于每个用户的简单数据。
开发服务器可以在单线程和进程中运行。 您不会看到您描述的行为,因为每个请求都将被同步处理。 启用线程或进程,您将看到它。 app.run(threaded=True)
或app.run(processes=10)
。 (在 1.0 中,服务器默认是线程化的。)
一些 WSGI 服务器可能支持 gevent 或其他异步工作者。 全局变量仍然不是线程安全的,因为仍然没有针对大多数竞争条件的保护。 您仍然可以有一个场景,一个工人获得一个价值,产出,另一个修改它,产出,然后第一个工人也修改它。
如果您需要在请求期间存储一些全局数据,您可以使用 Flask 的g
object 。 另一个常见的情况是一些管理数据库连接的顶级对象。 这种“全局”类型的区别在于它对每个请求都是唯一的,而不是在请求之间使用,并且有一些东西可以管理资源的设置和拆卸。
这并不是对全局线程安全的真正答案。
但我认为在这里提及会议很重要。 您正在寻找一种存储客户特定数据的方法。 每个连接都应该以线程安全的方式访问自己的数据池。
这在服务器端会话中是可能的,它们可以在一个非常简洁的烧瓶插件中使用: https ://pythonhosted.org/Flask-Session/
如果您设置会话,则session
变量在您的所有路由中都可用,它的行为类似于字典。 存储在此字典中的数据对于每个连接的客户端都是单独的。
这是一个简短的演示:
from flask import Flask, session
from flask_session import Session
app = Flask(__name__)
# Check Configuration section for more details
SESSION_TYPE = 'filesystem'
app.config.from_object(__name__)
Session(app)
@app.route('/')
def reset():
session["counter"]=0
return "counter was reset"
@app.route('/inc')
def routeA():
if not "counter" in session:
session["counter"]=0
session["counter"]+=1
return "counter is {}".format(session["counter"])
@app.route('/dec')
def routeB():
if not "counter" in session:
session["counter"] = 0
session["counter"] -= 1
return "counter is {}".format(session["counter"])
if __name__ == '__main__':
app.run()
在pip install Flask-Session
之后,你应该可以运行它了。 尝试从不同的浏览器访问它,您会发现它们之间没有共享计数器。
请求外部数据源的另一个示例是缓存,例如Flask-Caching或其他扩展提供的缓存。
common.py
并在其中放置以下内容:from flask_caching import Cache
# Instantiate the cache
cache = Cache()
flask app
的文件中,使用以下代码注册缓存:# Import cache
from common import cache
# ...
app = Flask(__name__)
cache.init_app(app=app, config={"CACHE_TYPE": "filesystem",'CACHE_DIR': Path('/tmp')})
# Import cache
from common import cache
# store a value
cache.set("my_value", 1_000_000)
# Get a value
my_value = cache.get("my_value")
虽然完全接受以前的赞成答案,并且不鼓励将全局变量用于生产和可扩展的 Flask 存储,但为了原型或非常简单的服务器,在 Flask '开发服务器'下运行......
...
Python 内置数据类型,我个人使用并测试了全局dict
, 根据 Python 文档是线程安全的。 过程不安全。
在开发服务器下运行的每个(可能是并发的)Flask 会话中,从这样的(服务器全局)dict 中插入、查找和读取都可以。
当这样的全局 dict 使用唯一的 Flask 会话密钥作为密钥时,它对于会话特定数据的服务器端存储非常有用,否则不适合 cookie(最大大小 4 kB)。
当然,应该小心保护这样的服务器全局字典,以免变得太大,在内存中。 可以在请求处理期间对某种过期的“旧”键/值对进行编码。
同样,不建议将其用于生产或可扩展部署,但对于本地面向任务的服务器来说可能没问题,因为单独的数据库对于给定任务来说太多了。
...
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.