gunicorn uvicorn worker.py cómo respetar la configuración de limit_concurrency

Aug 19 2020

FastAPI usa gunicorn para lanzar trabajadores de uvicorn como se describe en https://www.uvicorn.org/settings/

Sin embargo, gunicorn no permite iniciar uvicorn con configuraciones personalizadas como también se menciona en https://github.com/encode/uvicorn/issues/343

El problema sugirió anular config_kwargs en el archivo fuente como https://github.com/encode/uvicorn/blob/master/uvicorn/workers.py

Lo intentamos, pero uvicorn no respeta la configuración limit_concurrencyen varios archivos uvicorn en la fuente:

https://github.com/encode/uvicorn/blob/master/uvicorn/workers.py

# fail

        config_kwargs = {
            "app": None,
            "log_config": None,
            "timeout_keep_alive": self.cfg.keepalive,
            "timeout_notify": self.timeout,
            "callback_notify": self.callback_notify,
            "limit_max_requests": self.max_requests, "limit_concurrency": 10000,
            "forwarded_allow_ips": self.cfg.forwarded_allow_ips,
        }

https://github.com/encode/uvicorn/blob/master/uvicorn/main.py

# fail

    kwargs = {
        "app": app,
        "host": host,
        "port": port,
        "uds": uds,
        "fd": fd,
        "loop": loop,
        "http": http,
        "ws": ws,
        "lifespan": lifespan,
        "env_file": env_file,
        "log_config": LOGGING_CONFIG if log_config is None else log_config,
        "log_level": log_level,
        "access_log": access_log,
        "interface": interface,
        "debug": debug,
        "reload": reload,
        "reload_dirs": reload_dirs if reload_dirs else None,
        "workers": workers,
        "proxy_headers": proxy_headers,
        "forwarded_allow_ips": forwarded_allow_ips,
        "root_path": root_path,
        "limit_concurrency": 10000,
        "backlog": backlog,
        "limit_max_requests": limit_max_requests,
        "timeout_keep_alive": timeout_keep_alive,
        "ssl_keyfile": ssl_keyfile,
        "ssl_certfile": ssl_certfile,
        "ssl_version": ssl_version,
        "ssl_cert_reqs": ssl_cert_reqs,
        "ssl_ca_certs": ssl_ca_certs,
        "ssl_ciphers": ssl_ciphers,
        "headers": list([header.split(":") for header in headers]),
        "use_colors": use_colors,
    }

¿Cómo se puede obligar a uvicorn a respetar este entorno? Seguimos recibiendo errores 503 de FastAPI

------- ACTUALIZAR ----------- la configuración de gunicorn --worker-connections 1000todavía causa 503 al hacer 100 solicitudes paralelas que se distribuyen a muchos trabajadores.

Sin embargo, creo que es un tema un poco más complicado: nuestro punto final de API realiza una gran carga de trabajo, por lo general tarda 5 segundos en completarse.

Prueba de estrés con 2 núcleos, 2 trabajadores:

  • A. Más de 100 solicitudes simultáneas, carga pesada de punto final: conexiones de trabajador 1
  • B. Más de 100 solicitudes simultáneas, carga pesada de punto final: conexiones de trabajador 1000
  • C. Más de 100 solicitudes simultáneas, carga baja de punto final: conexiones de trabajador 1
  • D. Más de 100 solicitudes simultáneas, carga baja de punto final - conexiones de trabajador 1000

Ambos experimentos A y B arrojaron 503 respuestas, por lo que asumiendo que la configuración de conexiones de trabajador funciona, demasiadas conexiones simuladas parecen no causar nuestros errores 503.

Estamos desconcertados por este comportamiento, porque esperamos que gunicorn / uvicorn ponga en cola el trabajo y no arroje errores 503.

Respuestas

2 JPG Aug 19 2020 at 11:57

Del doctor gunicorn

worker-connections

El número máximo de clientes simultáneos.

y de uvicorn doc

limit-concurrency

Número máximo de conexiones o tareas simultáneas que se deben permitir, antes de emitir respuestas HTTP 503.

Según esta información, ambas configuraciones varían haciendo lo mismo. Entonces

uvicorn --limit-concurrency 100 application:demo_app

es casi igual que

gunicorn --worker-connections 100 -k uvicorn.workers.UvicornWorker application:demo_app

Nota: No he hecho ninguna prueba real sobre esto, corríjame si me equivoco.


Además, puede establecer limit-concurrency(o limit_concurrency) subclasificando la uvicorn.workers.UvicornWorkerclase

from uvicorn.workers import UvicornWorker


class CustomUvicornWorker(UvicornWorker):
    CONFIG_KWARGS = {
        "loop": "uvloop",
        "http": "httptools",
        "limit_concurrency": 100
    }

y ahora usa esto CustomUvicornWorkercon el gunicorncomando como,

gunicorn -k path.to.custom_worker.CustomUvicornWorker application:demo_app

Nota: puede inspeccionar self.config.limit_concurrencyen CustomUvicornWorkerclase para asegurarse de que el valor se haya establecido correctamente.