在我的应用程序中,公共对象的状态通过发出请求而改变,响应取决于状态。
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的唯一数字。或者会发生类似的情况:
客户端1查询。 self.param
将增加1。
在执行return语句之前,线程将切换到客户机2。 self.param
再次递增。
线程切换回客户端1,客户端返回数字2,比如说。
现在,线程移动到客户机2,并将数字3返回给他/她。
因为只有两个客户端,所以预期的结果是1和2,而不是2和3。跳过了一个数字。
当我扩展我的应用程序时,这真的会发生吗?我应该考虑哪些全局变量的替代方案?
4条答案
按热度按时间q3qa4bjr1#
不能使用全局变量保存此类数据。它不仅不是线程安全的,也不是进程安全的,而且生产环境中的wsgi服务器会产生多个进程。如果您使用线程来处理请求,那么您的计数不仅会出错,而且还会因处理请求的进程而异。
使用 flask 外部的数据源保存全局数据。数据库、memcached或redis都是适当的单独存储区域,具体取决于您的需要。如果需要加载和访问Python数据,请考虑
multiprocessing.Manager
. 您还可以将会话用于每个用户的简单数据。开发服务器可以在单线程和进程中运行。您将看不到所描述的行为,因为每个请求都将被同步处理。启用线程或进程,您将看到它。
app.run(threaded=True)
或app.run(processes=10)
. (在1.0中,服务器默认为线程化。)一些wsgi服务器可能支持gevent或其他异步工作程序。全局变量仍然不是线程安全的,因为仍然没有针对大多数竞争条件的保护。您仍然可以有这样一个场景:一个工人获得一个值,产生,另一个工人修改它,产生,然后第一个工人也修改它。
如果在请求期间需要存储一些全局数据,可以使用flask的
g
对象另一种常见情况是管理数据库连接的顶级对象。这种类型的“全局”的区别在于它对每个请求都是唯一的,而不是在请求之间使用,并且有一些东西可以管理资源的设置和拆卸。6ie5vjzr2#
这并不是全局线程安全的真正答案。
但我认为在这里提到会议是很重要的。您正在寻找一种存储特定于客户端的数据的方法。每个连接都应该以线程安全的方式访问自己的数据池。
这在服务器端会话中是可能的,它们可以在一个非常整洁的flask插件中使用:https://pythonhosted.org/flask-session/
如果您设置了会话,则
session
变量在所有路由中都可用,其行为类似于字典。对于每个连接的客户端,此字典中存储的数据是单独的。下面是一个简短的演示:
之后
pip install Flask-Session
,您应该能够运行此。尝试从不同的浏览器访问它,您将看到计数器在它们之间不共享。vkc1a9a23#
请求外部数据源的另一个示例是缓存,例如flask缓存或其他扩展提供的内容。
创建一个文件
common.py
并在其中放置以下内容:在您的
flask app
创建后,使用以下代码注册缓存:现在,通过导入缓存并执行以下操作,在整个应用程序中使用:
eagi6jfj4#
虽然完全接受之前的投票结果,并且不鼓励使用全局变量进行生产和可扩展的flask存储,但为了原型或真正简单的服务器,在flask“开发服务器”下运行。。。
...
python内置的数据类型,我个人使用并测试了全局
dict
,根据python文档,是线程安全的。过程不安全。在开发服务器下运行的每个(可能并发的)flask会话中,插入、查找和读取这样一个(服务器全局)dict都是正常的。
当使用唯一的flask会话密钥为这种全局dict设置密钥时,它对于服务器端存储特定于会话的数据非常有用,否则就不适合cookie(最大大小为4KB)。
当然,这样一个服务器全局dict应该小心地加以保护,以免在内存中变得太大。在请求处理期间,可以对“旧”键/值对进行某种类型的过期编码。
同样,不建议将其用于生产部署或可扩展部署,但对于本地面向任务的服务器来说,这可能是可行的,因为对于给定的任务来说,单独的数据库太多了。
...