fin*_*oot 9 python concurrency web-applications shared-state flask
我想为与多个工作人员(即多个进程)一起运行的 Flask 应用程序提供共享状态。
从关于这个主题的类似问题中引用这个答案:
您不能使用全局变量来保存此类数据。[...] 使用 Flask 之外的数据源来保存全局数据。根据您的需要,数据库、memcached 或 redis 都是合适的独立存储区域。
(来源:flask 中的全局变量线程安全吗?如何在请求之间共享数据?)
我的问题是关于如何在 Flask 的“外部”提供数据的建议的最后一部分。目前,我的网络应用程序非常小,我想避免对其他程序的要求或依赖。如果我不想在后台运行 Redis 或其他任何东西,而是提供 Web 应用程序的 Python 代码的所有内容,我有哪些选择?
fin*_*oot 15
如果您的网络服务器的工作类型与multiprocessing模块兼容,您可以使用multiprocessing.managers.BaseManager为 Python 对象提供共享状态。一个简单的包装器可能如下所示:
from multiprocessing import Lock
from multiprocessing.managers import AcquirerProxy, BaseManager, DictProxy
def get_shared_state(host, port, key):
shared_dict = {}
shared_lock = Lock()
manager = BaseManager((host, port), key)
manager.register("get_dict", lambda: shared_dict, DictProxy)
manager.register("get_lock", lambda: shared_lock, AcquirerProxy)
try:
manager.get_server()
manager.start()
except OSError: # Address already in use
manager.connect()
return manager.get_dict(), manager.get_lock()
Run Code Online (Sandbox Code Playgroud)
您可以将数据分配给shared_dict以使其跨进程访问:
HOST = "127.0.0.1"
PORT = 35791
KEY = b"secret"
shared_dict, shared_lock = get_shared_state(HOST, PORT, KEY)
shared_dict["number"] = 0
shared_dict["text"] = "Hello World"
shared_dict["array"] = numpy.array([1, 2, 3])
Run Code Online (Sandbox Code Playgroud)
但是,您应该注意以下情况:
shared_lock在覆盖shared_dict. (请参阅下面的烧瓶示例。)BaseManager进程终止,则共享状态将消失。BaseManager,您无法直接编辑 中的嵌套值shared_dict。例如,shared_dict["array"][1] = 0没有效果。您必须编辑一个副本,然后将其重新分配给字典键。烧瓶示例:
以下 Flask 应用程序使用全局变量来存储计数器编号:
from flask import Flask
app = Flask(__name__)
number = 0
@app.route("/")
def counter():
global number
number += 1
return str(number)
Run Code Online (Sandbox Code Playgroud)
这在仅使用 1 个 worker 时有效gunicorn -w 1 server:app。当使用多个 workergunicorn -w 4 server:app时,很明显这number不是共享状态,而是每个 worker 进程的单独状态。
相反,使用shared_dict,应用程序看起来像这样:
from flask import Flask
app = Flask(__name__)
HOST = "127.0.0.1"
PORT = 35791
KEY = b"secret"
shared_dict, shared_lock = get_shared_state(HOST, PORT, KEY)
shared_dict["number"] = 0
@app.route("/")
def counter():
with shared_lock:
shared_dict["number"] += 1
return str(shared_dict["number"])
Run Code Online (Sandbox Code Playgroud)
这适用于任意数量的工人,例如gunicorn -w 4 server:app.