在nginx / gunicorn / django Web架构中高效处理长时间运行的HTTP连接

时间:2012-08-09 12:14:43

标签: django performance web-services nginx gunicorn

我正在开发一个在nginx + gunicorn + django之上实施的网络服务。客户是智能手机应用程序。应用程序需要对外部API(Facebook,Amazon S3 ...)进行一些长时间运行的调用,因此服务器只需将作业排队到作业服务器(使用Celery而不是Redis)。

只要有可能,一旦服务器对作业进行了排队,它就会立即返回,并且HTTP连接已关闭。这很好用,可以让服务器承受很高的负载。

client                   server                 job server
  .                        |                        |
  .                        |                        |
  |------HTTP request----->|                        |
  |                        |--------queue job------>|
  |<--------close----------|                        |
  .                        |                        |
  .                        |                        |

但在某些情况下,客户需要在作业完成后立即获得结果。不幸的是,一旦HTTP连接关闭,服务器就无法联系客户端。一种解决方案是依赖客户端应用程序每隔几秒轮询一次服务器,直到作业完成。如果可能的话,我想避免这种解决方案,主要是因为它会阻碍服务的反应性,还因为它会在服务器上加载许多不必要的轮询请求。

简而言之,我想保持HTTP连接正常运行,什么也不做(除了可能每隔一段时间发送一个空格以保持TCP连接存活,只有like Amazon S3 does),直到工作完成完成后,服务器返回结果。

client                   server                 job server
  .                        |                        |
  .                        |                        |
  |------HTTP request----->|                        |
  |                        |--------queue job------>|
  |<------keep-alive-------|                        |
  |         [...]          |                        |
  |<------keep-alive-------|                        |
  |                        |<--------result---------|
  |<----result + close-----|                        |
  .                        |                        |
  .                        |                        |

如果服务器处于非常高的负载下,我怎样才能以有效的方式实现长时间运行的HTTP连接(目前情况并非如此,但目标是能够承受最高负载,数百个或每秒数千个请求)?

将实际作业卸载到其他服务器应该确保服务器上的CPU使用率低,但是如何避免堆积和使用所有服务器RAM的进程,或者由于打开的连接太多而丢弃传入请求?

这可能主要是正确配置nginx和gunicorn的问题。我已经阅读了一些关于async workers based on greenlets in gunicorn的文章:文档说“应用程序进行长时间阻止调用(即外部Web服务)”使用异步工作者,这听起来很完美。它还说“通常,应用程序应该能够使用这些工作类而无需更改”。听起来不错。对此有何反馈?

感谢您的建议。

1 个答案:

答案 0 :(得分:30)

answering my own question,也许某人有更好的解决方案。

进一步阅读gunicorn's documentation,并阅读更多关于eventletgevent的信息,我认为枪炮完美地回答了我的问题。 Gunicorn有一个管理工人池的主过程。每个worker可以是同步的(单线程,一次处理一个请求)或异步(每个worker几乎同时处理多个请求)。

同步工作者很容易理解和调试,如果工作者失败,只会丢失一个请求。但是如果一个工人陷入长时间运行的外部API调用,它基本上就是在睡觉。因此,在高负载的情况下,所有工作人员可能在等待结果时最终都在睡觉,并且请求最终会被丢弃。

因此解决方案是将默认工作类型从同步更改为异步(选择eventlet或gevent,here's a comparison)。现在每个工作人员都运行多个green threads,每个人都非常轻量级。每当一个线程必须等待某个I / O时,另一个绿色线程将继续执行。这称为cooperative multitasking。它非常快,而且非常轻量级(如果他们正在等待I / O,则单个工作人员可以处理数千个并发请求)。正是我需要的。

我想知道我应该如何更改现有代码,但显然标准的python模块在启动时由gunicorn monkey-patched(实际上是由eventlet或gevent),因此所有现有代码都可以无变化地运行,并且仍然可以很好地运行其他线程。

有很多参数可以在gunicorn中调整,例如使用gunicorn worker_connections参数的最大并发客户端数,使用backlog的最大挂起连接数参数等。

这很棒,我马上开始测试!