26

我对 mod_wsgi 的多处理功能以及将在具有多处理能力的 WSGI 服务器上执行的 WSGI 应用程序的一般设计有点困惑。

考虑以下指令:

WSGIDaemonProcess example processes=5 threads=1

如果我理解正确,mod_wsgi 将产生 5 个 Python(例如 CPython)进程,并且这些进程中的任何一个都可以接收来自用户的请求。

文档说:

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

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

例如,一个包含当前连接用户数量的简单变量——它应该是进程安全的从 memcached 读/写到 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

在多处理环境中表现不可预测?

谢谢!

4

3 回答 3

24

您的问题有几个方面需要考虑。

首先,apache MPM 和 mod_wsgi 应用程序之间的交互。如果您在嵌入式模式下运行 mod_wsgi 应用程序(WSGIDaemonProcess不需要WSGIProcessGroup %{GLOBAL}),您将从 apache MPM 继承多处理/多线程。这应该是最快的选项,并且您最终会拥有多个进程和每个进程的多个线程,具体取决于您的 MPM 配置。相反,如果您在守护程序模式下运行 mod_wsgi ,使用WSGIDaemonProcess <name> [options]and WSGIProcessGroup <name>,您可以很好地控制多处理/多线程,但开销很小

在单个 apache2 服务器中,您可以定义零个、一个或多个命名的WSGIDaemonProcesses,并且每个应用程序都可以在这些进程之一中运行 ( WSGIProcessGroup <name>) 或以嵌入模式运行WSGIProcessGroup %{GLOBAL}

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

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

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

m = multiprocessing.managers.BaseManager(address=('', 12345), authkey='secret')
m.get_server().serve_forever()

在您的应用程序中,您connect

m = multiprocessing.managers.BaseManager(address=('', 12345), authkey='secret')
m.connect()

上面的例子是假的,因为m没有注册有用的方法,但是在这里(python 文档)你会发现如何在你的进程中创建和代理一个对象(就像counter你的例子中的)。

对您的示例的最后评论,使用processes=5 threads=1. 我知道这只是一个例子,但在现实世界的应用程序中,我怀疑性能将与以下方面相当processes=1 threads=5:只有在“单进程多线程”的预期性能提升时,您才应该进入多处理中共享数据的复杂性模型很重要。

于 2012-10-08T13:28:03.113 回答
4

从有关 wsgi 的进程和线程的文档中:

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

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

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

于 2012-10-05T19:30:30.860 回答
2

如果您正在使用multiprocessing,则有多种方法可以在进程之间共享数据。数组仅在进程具有父/子关系(它们通过继承共享)时才有效。如果不是这种情况,请使用 aManagerProxy对象。

于 2012-10-07T14:03:06.087 回答