在我的应用程序中,通过发出请求来更改公共对象的状态,响应取决于状态。
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个不同的客户提出请求,可能出现问题吗?预期的结果是100个不同的客户每个都看到1到100之间的唯一数字。或者会发生这样的事情:
self.param
增加1。self.param
再次递增。由于只有两个客户端,预期结果为1和2,而不是2和3.跳过了一个数字。
这会在我扩展应用程序时实际发生吗?我应该看一下全局变量的替代方案?
答案 0 :(得分:51)
您无法使用全局变量来保存此类数据。它不仅不是线程安全的,它不是进程安全,并且生产中的WSGI服务器产生多个进程。如果您使用线程来处理请求,那么您的计数不仅会出错,它们也会因处理请求的进程而异。
使用Flask外部的数据源来保存全局数据。数据库,memcached或redis都是适当的独立存储区域,具体取决于您的需求。如果您需要加载和访问Python数据,请考虑multiprocessing.Manager
。您还可以将会话用于每个用户的简单数据。
开发服务器可以在单线程和进程中运行。您将看不到您描述的行为,因为将同步处理每个请求。启用线程或进程,您将看到它。 app.run(threaded=True)
或app.run(processes=10)
。 (在1.0中,服务器默认是线程化的。)
某些WSGI服务器可能支持gevent或其他异步工作程序。全局变量仍然不是线程安全的,因为仍然没有针对大多数竞争条件的保护。您仍然可以有一个方案,其中一个工人获得一个值,产量,另一个工人获得它,产量,然后第一个工人也修改它。
如果您需要在请求期间存储一些全局数据,您可以使用Flask的g
object。另一种常见情况是一些管理数据库连接的顶级对象。这种“全局”的区别在于它对每个请求都是唯一的,而不是在请求之间使用,而且还有管理资源设置和拆除的东西。
答案 1 :(得分:6)
这并不是对全局变量线程安全的真正答案。
但是我认为在这里提到会议很重要。 您正在寻找一种存储客户端特定数据的方法。每个连接都应该以线程安全的方式访问自己的数据池。
这在服务器端会话中是可能的,并且它们可以在一个非常简洁的烧瓶插件中使用: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
之后,您应该可以运行它。尝试从不同的浏览器访问它,您会发现计数器未在它们之间共享。
答案 2 :(得分:3)
虽然完全接受先前提出的答案,并且不鼓励将全局变量用于生产和可扩展的Flask存储,但出于原型或非常简单的服务器的目的,它们在烧瓶“开发服务器”下运行...
...
Python内置数据类型以及我个人使用和测试的全局dict
,as per Python documentation是线程安全的。 处理不安全。
在开发服务器下运行的每个Flask会话中,从(服务器全局)字典进行的插入,查找和读取都可以。
用唯一的Flask会话密钥键入这样的全局字典时,对于服务器端存储特定于会话的数据非常有用,否则该数据将不适合Cookie(最大大小为4 kB)。
当然,应该谨慎地保护这样的服务器全局指令,使其不会太大而成为内存。可以在请求处理期间对某种过期的“旧”键/值对进行编码。
同样,不建议将其用于生产或可伸缩部署,但对于本地任务导向型服务器来说可能是可以的,因为对于给定任务而言,单独的数据库太多了。
...
答案 3 :(得分:1)
请求外部数据源的另一个示例是缓存,例如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")