Cualquiera que trabaje en marcos de aplicaciones web WSGI como Flask sabrá que, como práctica recomendada, es muy importante usar un servidor HTTP WSGI como Gunicorn para implementar la aplicación fuera de sus servidores de desarrollo.
Gunicorn es un servidor WSGI muy popular y su popularidad se debe a que es liviano, rápido y simple, pero puede admitir la mayoría de los requisitos que tendría para alojar una aplicación en producción. No entraría en detalles aquí sobre "Por qué Gunicorn/O por qué no". Lo que discutiré aquí es qué tipo de trabajador debe seleccionar y por qué.
Gunicorn se basa en el modelo de trabajador anterior a la bifurcación. Esto significa que hay un proceso maestro central que gestiona un conjunto de procesos de trabajo. El maestro nunca sabe nada acerca de los clientes individuales. Todas las solicitudes y respuestas son manejadas completamente por procesos de trabajo.
Tipos de trabajadores
El trabajador gunicorn puede ser de 2 tipos amplios: Sync y Async .
El tipo de trabajador predeterminado es Sync y lo defenderé. Como sugiere el nombre, los trabajadores de sincronización ejecutan una solicitud tras otra. Puede tener varios trabajadores de este tipo, y el número de solicitudes concurrentes atendidas es igual al número de dichos trabajadores.
En contraste con esto, Gunicorn ofrece un tipo de trabajador asíncrono. Hablaré del tipo de trabajador Gevent que es el más popular entre todos. Los trabajadores asincrónicos como Gevent crean nuevos greenlets (pseudo subprocesos ligeros). Cada vez que llega una nueva solicitud, los greenlets generados por los subprocesos de trabajo los manejan. La idea central es que cuando hay una acción vinculada a IO en su código, el greenlet cederá y le dará un identificador a otro greenlet para usar la CPU. Entonces, cuando llegan muchas solicitudes a su aplicación, puede manejarlas simultáneamente, las llamadas vinculadas a IO no bloquearán la CPU y el rendimiento mejorará. Al mismo tiempo, los recursos necesarios para atender las solicitudes serán menores.
Si bien es tentador usar un tipo de trabajador asíncrono como Gevent y generar miles de greenlets, tiene un costo que debe conocer.
La documentación de Gunicorn define claramente cuándo debe usar un tipo de trabajador asíncrono
cualquier aplicación web que realice solicitudes salientes a las API que pueden tardar una cantidad de tiempo indefinida en regresar se beneficiará de un trabajador asíncrono
Podemos inferir de esto que cualquier operación vinculada a IO donde IO es relativamente más larga puede beneficiarse de los trabajadores asíncronos.
Advertencias que no se especifican en los ejemplos
Hay muchos recursos/ejemplos en Internet donde encontrará personas que defienden a Gevent. Pero la mayoría de estos ejemplos no hablan sobre los problemas que enfrentaría al usarlo en la aplicación de grado de producción real.
Si todo esto suena como algo que puede pagar a largo plazo, hágalo.
Los trabajadores de sincronización se simplifican en gran medida y funcionan bien en la mayoría de los casos. Mi consejo es que comience con los trabajadores de sincronización e intente arreglar el código de su aplicación tanto como sea posible si eso está bajo su control, en lugar de esperar que suceda magia cuando active 'Gevent'.
Si todos sus servicios de back-end están en la misma red, la latencia será muy baja y no debería preocuparse mucho por bloquear IO. Puedo decir por experiencia que con 3 o 4 trabajadores puede manejar miles de usuarios simultáneos fácilmente con un caché de aplicación adecuado. En caso de que necesite más rendimiento, puede tener más subprocesos por trabajador, lo que hará que el trabajador escriba gthread, pero aún así, un trabajador de sincronización y la huella de memoria por subproceso serán menores.
Si tiene una gran cantidad de memoria por trabajador, tal vez porque está cargando un modelo grande, intente usar preload True, lo que garantizaría que está cargando y luego bifurcando a los trabajadores. Hay otras opciones tambien. Me encantaría escribir algo sobre eso más tarde.
Si su aplicación tiene varias llamadas salientes a las API fuera de su red, debe proteger su aplicación estableciendo tiempos de espera razonables en su grupo de conexiones de urllib/requests. Además, asegúrese de que su aplicación tenga tiempos de espera de trabajo razonables que garanticen que una o más solicitudes incorrectas no bloqueen sus otras solicitudes durante mucho tiempo.
Finalmente, si todo esto falla y no puede escalar y usar sus recursos de manera eficiente, considere usar trabajadores asincrónicos.
NOTA: En algunos casos específicos, como la configuración de un socket web, las solicitudes de transmisión y las respuestas, siempre deberá usar trabajadores asincrónicos.
Referencias:
Comprender el bloqueo de llamadas