Manejo eficiente de las conexiones HTTP de larga ejecución en una arquitectura web nginx / gunicorn / django

Estoy trabajando en un servicio web implementado encima denginx+Gunicorn+Django. Los clientes son aplicaciones de teléfonos inteligentes. La aplicación debe realizar llamadas de larga duración a API externas (Facebook, Amazon S3 ...), por lo que el servidor simplemente pone en cola el trabajo en un servidor de trabajo (usandoApio terminadoRedis).

Siempre que sea posible, una vez que el servidor haya puesto en cola el trabajo, regresará de inmediato y la conexión HTTP se cerrará. Esto funciona bien y permite que el servidor sostenga una carga muy alta.

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

Pero en algunos casos, el cliente necesita obtener el resultado tan pronto como finalice el trabajo. Desafortunadamente, no hay forma de que el servidor pueda contactar al cliente una vez que se cierre la conexión HTTP. Una solución sería confiar en que la aplicación cliente sondea el servidor cada pocos segundos hasta que se complete el trabajo. Me gustaría evitar esta solución, si es posible, principalmente porque obstaculizaría la capacidad de respuesta del servicio y también porque cargaría el servidor con muchas solicitudes de sondeo innecesarias.

En resumen, me gustaría mantener la conexión HTTP en funcionamiento, sin hacer nada (excepto tal vez enviar un espacio en blanco de vez en cuando para mantener la conexión TCP activa, solocomo hace Amazon S3), hasta que el trabajo haya finalizado y el servidor devuelva el resultado.

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

¿Cómo puedo implementar conexiones HTTP de larga ejecución de manera eficiente, asumiendo que el servidor tiene una carga muy alta (no es el caso todavía, pero el objetivo es poder sostener la carga más alta posible, con cientos o miles de solicitudes por segundo)? )?

La descarga de los trabajos reales a otros servidores debería garantizar un bajo uso de la CPU en el servidor, pero ¿cómo puedo evitar que los procesos se acumulen y utilicen toda la RAM del servidor, o que se eliminen las solicitudes entrantes debido a demasiadas conexiones abiertas?

Es probable que esto sea principalmente una cuestión de configurar nginx y gunicorn correctamente. He leído un poco sobreTrabajadores asíncronos basados ​​en greenlets en Gunicorn.: la documentación dice que los trabajadores asíncronos son utilizados por "Aplicaciones que hacen largas llamadas de bloqueo (es decir, servicios web externos)", esto suena perfecto. También dice"En general, una aplicación debe poder hacer uso de estas clases de trabajadores sin cambios". Esto suena bien. ¿Algún comentario sobre esto?

Gracias por tus consejos.

Respuestas a la pregunta(1)

Su respuesta a la pregunta