如何处理Python工作人员长时间运行的请求?

问题描述:

我有一个Python(嗯,它现在是PHP,但我们正在重写)函数,它需要一些参数(A和B)并计算一些结果(在图中找到从A到B的最佳路径,图是只读的) ,在典型情况下,一次呼叫需要0.1s到0.9s来完成。用户可以将此功能作为简单的REST Web服务访问(GET bestpath.php?from = A & = B)。当前的实现非常愚蠢 - 这是一个简单的php脚本+ apache + mod_php + APC,每个请求都需要加载所有数据(在php数组中超过12MB),创建所有结构,计算路径并退出。我想改变它。每个worker都是一个运行在循环中的python应用程序(获取请求 - >处理 - >发送应答 - >获取请求...)每个工人一次可以处理一个请求。我需要一些可以充当前端的东西:获取用户请求,管理请求队列(可配置超时),并为我的工作人员提供一次请求。如何处理Python工作人员长时间运行的请求?

如何处理这个?你能提出一些设置吗? nginx + fcgi或wsgi或其他东西? HAProxy的?正如你可以看到我是一个Python新手,反向代理等我只需要一个关于架构(和数据流)的起点

btw。工作人员正在使用只读数据,因此不需要保持它们之间的锁定和通信

在Python中使用线程处理这种排列的典型方法是使用标准库模块Queue。使用队列模块来管理工人的一个例子可以在这里找到:Queue Example

看起来像你需要“工人”是不同的过程(至少其中一些,因此也可以让他们所有独立的过程,而不是比成串的线程分成几个进程)。 Python 2.6和更高版本的标准库中的multiprocessing模块提供了良好的功能,可以生成一组进程并通过FIFO“队列”与它们进行通信;如果由于某种原因,你被Python 2.5或甚至更早版本所困住,那么PyPi存储库上的多处理版本可以下载并与那些较旧版本的Python一起使用。

“前端”可以也应该很容易与WSGI一起运行(使用Apache或Nginx),它可以通过multiprocessing处理与工作进程之间的所有通信,而无需使用HTTP代理等等,为系统的那部分;只有前端本身就是一个Web应用程序,工作人员只需接收,处理和响应前端请求的工作单元。这对我来说似乎是最健康,最简单的建筑。

Python的第三方包中还有其他的分布式处理方法,但多处理是相当体面的,并有作为标准库的一部分的优势,所以,如果没有其他特殊的限制或约束,多处理就是我想要的建议你去。

+0

多处理看起来不错,但坦率地说,我不需要所有的功能(通信,同步等),我的工作人员是独立的,在这种设置下,我需要在每台服务器上安装一个前端,对吧?我正在考虑在worker中实现基本的http解析(或者别的什么,我不知道其他协议可以在worker和revproxy之间使用),并让一些反向代理请求处理服务器池(工作人员在),但我不知道这种方法是否有意义,以及从哪里开始(哪个revproxy,如何实现工作人员 revproxy通信等) – winter 2009-11-04 16:26:08

+0

除通信目的外您不需要同步,但您需要通信(从前端到工作的任务,从工作人员到前端的结果 - 不,你绝对不需要每个服务器的前端,你为什么这么认为?),而多处理提供的队列对于,让你摆脱对协议,代理和其他任何问题的担忧 - 我认为你急于走向的方向可能会让你陷入一团糟的工作当中,而多处理更容易使用! – 2009-11-05 01:15:20

+0

好的,但与多处理我需要编写2个应用程序:app1 - 一个前端进程管理队列,app2 - 许多工作进程,对吧?那么:app1可以在不同的machnies上管理app2吗?是app1持久性?我的意思是 - 它的代码和数据保持在来自web服务器的请求之间?如果没有队列丢失,如果是 - 它必须是异步的才能处理同时发出的请求。所以app1基本上是模仿异步网络服务器 - 它唯一的目的是管理请求的队列。我想避免复制已经存在的功能,这使得复杂化一切 – winter 2009-11-05 22:47:19

我想你可以配置modwsgi /阿帕奇所以它有几个“热”的Python解释器在准备去在任何时候,还重用它们对新接入 单独的进程 (并生成一个新的,如果他们都忙)。 在这种情况下,您可以将所有预处理的数据加载为模块全局变量,并且它们只会在每个进程中加载​​一次,并且每个新访问都会重新使用它们。事实上,我不确定这不是modwsgi/Apache的默认配置 。

这里的主要问题是,你可能最终会消耗大量的“核心”内存(但这可能不是问题) 。 我认为你也可以配置modwsgi单进程/多 线程 - 但在这种情况下,你可能只能使用一个CPU,因为Python全局解释器锁(臭名昭着的GIL)的 ,我想。

不要害怕在modwsgi邮件列表中询问 - 它们非常友好,响应性很好。

+0

如果使用嵌入模式,只会在Apache/mod_wsgi中产生新进程,并且发生这种情况是Apache的一项功能,而不是mod_wsgi的功能。来自'http://code.google.com/p/modwsgi/wiki/ProcessesAndThreading'中描述的内存。 – 2009-11-04 22:54:28

+0

哦,还应该提到,使用嵌入模式来获得该功能在涉及大量数据/内存时确实存在问题。见'http://blog.dscpl.com.au/2009/03/load-spikes-and-excessive-memory-usage.html'。 – 2009-11-04 22:55:37

您可以使用nginx负载均衡器代理PythonPaste paster(它提供WSGI,例如Pylons),它将每个请求作为单独的线程启动。

有很多FastCGI模块都带有preforked模式和WSGI接口,用于python,最为人熟知的是flup。我个人对这种任务的偏好是带有nginx的superfcgi。两者都将启动多个进程并将向它们发送请求。 12Mb并不是在每个进程中分别加载它们,但是如果您想在工作者之间共享数据,则需要线程,而不是进程。请注意,在单进程和多线程的python中,由于GIL,不会有效地使用多个CPU /内核。可能最好的方法是使用多个进程(与内核一样多),每个进程运行多个线程(superfcgi中的默认模式)。

+0

感谢您的链接,我不知道superfcgi。我知道GIL和我的员工是'数学'密集型的,所以我希望他们每个人都有自己的过程。我不需要工人之间的沟通。为什么你认为设置进程_和_线程是最好的方法?我的意思是,当你想尽可能快地进行计算时,线程的意义何在? – winter 2009-11-05 21:49:50

+0

另外,哪一层(nginx?superfcgi?my app?)负责在这个设置中排队(worker = process,没有线程)?例如:我有4名工人,每人可以一次处理一个请求,现在我有10个来自用户的请求,其中4个直接进入工作人员,但剩下的6个呢?他们应该停留在FIFO队列中X秒。 – winter 2009-11-05 21:55:49

+0

侦听套接字就像一个队列。 'backlog'参数对应于等待接受的最大请求数。 'superfcgi'中的每个工人(进程或线程)只有在准备好处理请求时才接受。所以OS会为你排队。 – 2009-11-06 05:25:47

在这种情况下最简单的解决方案是使用网络服务器来完成所有繁重工作。当网络服务器为你做所有事情时,为什么你应该处理线程和/或进程?

在Python的部署标准的安排是:

  1. 的Web服务器上启动多个进程每运行一次完整的Python解释器和所有数据加载到内存中。
  2. HTTP请求进来,被分派关闭一些程序
  3. 过程并您计算并直接将结果返回给Web服务器和用户
  4. 当你需要改变你的代码或图形数据,重新启动Web服务器并返回步骤1.

这是架构使用Django和其他流行的web框架。

+0

好的,但我希望我的应用程序代码和数据保持请求之间,我不希望解释器reaload在每个请求。它是如何工作的? – winter 2009-11-05 22:02:32

+0

当没有空闲进程(worker)处理请求时会发生什么?是否有一些fifo队列王(超时)? (抱歉,我的新手问题来自PHP世界;) – winter 2009-11-05 22:04:52

+0

应用程序代码和数据存储在内存中,只要进程处于活动状态,即。永远或直到你重新启动它。这取决于Web服务器的配置,Apache将允许您为每个进程设置最大数量的请求。 一般来说,网络服务器将在必要时产生新的进程。这可以进行长度配置。请参阅Apache文档中的MinSpareServers和MaxSpareServers。 – knutin 2009-11-06 08:20:38

另一种选择是数据库中的队列表。
工作进程在循环中运行或关闭cron并轮询队列表以寻找新的作业。