如果您的网络服务器的工作器类型与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()
您可以将数据分配给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])
但是,您应该注意以下情况:
- 用于
shared_lock
在覆盖shared_dict
. (请参阅下面的 Flask 示例。)
- 没有数据持久性。如果您重新启动应用程序,或者如果主(第一个)
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)
这在仅使用 1 个 worker 时有效gunicorn -w 1 server:app
。当使用多个工作进程gunicorn -w 4 server:app
时,很明显这number
不是共享状态,而是每个工作进程的独立状态。
相反,使用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"])
这适用于任意数量的工人,例如gunicorn -w 4 server:app
.