Python、WSGI、多处理和共享数据

Python、WSGI、多处理和共享数据,python,multiprocessing,mod-wsgi,wsgi,Python,Multiprocessing,Mod Wsgi,Wsgi,我对mod_wsgi的多处理器特性和将在具有多处理器能力的wsgi服务器上执行的wsgi应用程序的一般设计感到有点困惑 考虑以下指令: WSGIDaemonProcess example processes=5 threads=1 如果我理解正确,mod_wsgi将产生5个Python(例如CPython)进程,这些进程中的任何一个都可以接收用户的请求 文件说: 其中,共享数据需要对所有应用程序实例可见,而不管它们在哪个子进程中执行,以及对其所做的更改 一个应用程序的数据立即可供另一个应用程序

我对mod_wsgi的多处理器特性和将在具有多处理器能力的wsgi服务器上执行的wsgi应用程序的一般设计感到有点困惑

考虑以下指令:

WSGIDaemonProcess example processes=5 threads=1
如果我理解正确,mod_wsgi将产生5个Python(例如CPython)进程,这些进程中的任何一个都可以接收用户的请求

文件说:

其中,共享数据需要对所有应用程序实例可见,而不管它们在哪个子进程中执行,以及对其所做的更改 一个应用程序的数据立即可供另一个应用程序使用, 包括在另一个子进程中执行的任何外部数据 必须使用数据库或共享内存等存储。全球的 普通Python模块中的变量不能用于此目的

但在这种情况下,当人们想要确保应用程序在任何WSGI条件下运行(包括多处理条件)时,它会变得非常沉重

例如,一个包含当前已连接用户数量的简单变量—它应该是进程安全的读/写自/写入memcached,还是一个DB或(如果标准库机制可用)共享内存

代码会像这样吗

counter = 0

@app.route('/login')
def login():
    ...
    counter += 1
    ...

@app.route('/logout')
def logout():
    ...
    counter -= 1
    ...

@app.route('/show_users_count')
def show_users_count():
    return counter
在多处理环境中行为不可预测


谢谢大家!

来自wsgi的进程和线程文档:

当Apache以有多个子进程的模式运行时,每个子进程将包含每个WSGI应用程序的子解释器

这意味着在您的配置中,有5个进程,每个进程有1个线程,将有5个解释器,并且没有共享数据。每个解释器的计数器对象都是唯一的。您需要构建一些自定义解决方案来计算会话数(一个您可以与之通信的常见流程,某种基于持久性的解决方案,等等),或者,这绝对是我的建议,使用预构建的解决方案(Google Analytics和Chartbeat是非常好的选择)


我倾向于认为使用globals共享数据是全球滥用的一大形式。在我做过并行处理的大多数环境中,这是一个bug和可移植性问题。如果您的应用程序突然要在多个虚拟机上运行,该怎么办?无论线程和进程的共享模式如何,这都会破坏您的代码。

如果您使用的是
多处理
,则在进程之间共享数据是不可能的。并且仅当进程具有父/子关系(它们通过继承共享)时才起作用。如果不是这样的话,使用A和Objor。

在你的问题中有几个方面需要考虑。 首先,apache MPM和mod_wsgi应用程序之间的交互。如果在嵌入式模式下运行mod_wsgi应用程序(不需要

WSGIDaemonProcess
needed,
WSGIProcessGroup%{GLOBAL}
),则从apache MPM继承多处理/多线程。这应该是最快的选择,最终会有多个进程和每个进程多个线程,这取决于您的MPM配置。相反,如果您在守护进程模式下运行mod_wsgi,使用
WSGIDaemonProcess[options]
WSGIProcessGroup
,您可以以较小的成本很好地控制多处理/多线程

在单个apache2服务器中,您可以定义零个、一个或多个命名的
WSGIDaemonProcess
es,每个应用程序都可以在其中一个进程(
WSGIProcessGroup
)中运行,或者使用
WSGIProcessGroup%{GLOBAL}
以嵌入式模式运行

您可以通过检查
wsgi.multithread
wsgi.multiprocess
变量来检查多处理/多线程

通过您的配置
WSGIDaemonProcess example processs=5 threads=1
您有5个独立的进程,每个进程都有一个执行线程:没有全局数据,没有共享内存,因为您无法控制生成子进程,但mod_wsgi正在为您做这件事。要共享全局状态,您已经列出了一些可能的选项:进程与之接口的DB、某种基于文件系统的持久性、守护进程(从apache外部启动)和基于套接字的IPC

正如Roland Smith所指出的,后者可以通过以下方式使用高级API实现:在apache之外创建并启动
BaseManager
服务器进程

m = multiprocessing.managers.BaseManager(address=('', 12345), authkey='secret')
m.get_server().serve_forever()
在您的应用程序中,您可以
连接

m = multiprocessing.managers.BaseManager(address=('', 12345), authkey='secret')
m.connect()
上面的示例是虚拟的,因为
m
没有注册有用的方法,但是(python文档)您将发现如何在进程中创建和代理对象(如示例中的
计数器


关于您的示例的最后一点评论,使用
进程=5线程=1
。我知道这只是一个例子,但在现实世界的应用程序中,我怀疑性能将与
进程=1线程=5
相当:只有在“单进程多线程”模型的预期性能提升显著时,才应该深入研究多处理中共享数据的复杂性

引用:“一个包含当前连接用户数量的简单变量”。这是HTTP,没有“已连接”用户的概念,因此这样的计数不能是“简单的”。(例如,用户可以通过忘记您给他们的任何令牌(例如,通过清除浏览器cookies)来注销)。这意味着应用程序视为“已连接”的用户,例如,通过上次HTTP会话时间戳+10分钟。Andre评论中的+1,但尽管我同意会话计数的固有困难,我认为这更多地与良好的网页设计有关,而不是手头的具体多处理/共享数据问题。另一个问题是,没有代码确保计数器以有序的方式读取、更新和写回(据我所知,+=1在python中不是原子操作…)。需要某种类型的锁定