在我的应用程序中,公共对象的状态通过发出请求而改变,响应取决于状态。
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和2,而不是2和3。一个数字被跳过。
当我扩展我的应用程序时,这真的会发生吗?我应该考虑哪些全局变量的替代方案?
不能使用全局变量保存此类数据。它不仅不是线程安全的,也不是进程安全的,而且生产环境中的WSGI服务器会产生多个进程。如果您使用线程来处理请求,那么您的计数不仅会出错,而且还会因处理请求的进程而异。
使用烧瓶外部的数据源保存全局数据。数据库、memcached或redis都是适当的单独存储区域,具体取决于您的需要。如果需要加载和访问Python数据,请考虑<代码>多重处理。管理器。您还可以将会话用于每个用户的简单数据。
开发服务器可以在单线程和进程中运行。您将看不到所描述的行为,因为每个请求都将被同步处理。启用线程或进程,您将看到它<代码>应用程序。运行(threaded=True)或应用程序。运行(进程=10)
。(在1.0中,服务器默认为线程化。)
某些WSGI服务器可能支持gevent或其他异步辅助角色。全局变量仍然不是线程安全的,因为仍然没有针对大多数竞争条件的保护。你仍然可以有这样一个场景,一个工作人员得到一个值,产生,另一个修改它,产生,然后第一个工作人员也修改它。
如果在请求期间需要存储一些全局数据,可以使用Flask的g
对象。另一种常见情况是管理数据库连接的顶级对象。这种类型的“全局”的区别在于它对每个请求都是唯一的,而不是在请求之间使用,并且有一些东西可以管理资源的设置和拆卸。
这并不是全局线程安全的真正答案。
但是我认为在这里提到会议是很重要的。您正在寻找一种存储客户端特定数据的方法。每个连接都应该以线程安全的方式访问自己的数据池。
这在服务器端会话中是可能的,它们可以在一个非常简洁的烧瓶插件中获得: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安装Flask会话之后,您应该能够运行这个。尝试从不同的浏览器访问它,您将看到计数器在它们之间不共享。
请求外部数据源的另一个示例是缓存,例如Flask缓存或其他扩展提供的内容。
common。py
并在其中放置以下内容:from flask_caching import Cache
# Instantiate the cache
cache = Cache()
# 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")