gunicorn uvicorn worker.py come onorare l'impostazione limit_concurrency

Aug 19 2020

FastAPI utilizza gunicorn per avviare i lavoratori uvicorn come descritto in https://www.uvicorn.org/settings/

Tuttavia gunicorn non consente di avviare uvicorn con impostazioni personalizzate come menzionato anche in https://github.com/encode/uvicorn/issues/343

Il problema ha suggerito di sovrascrivere config_kwargs nel file sorgente come https://github.com/encode/uvicorn/blob/master/uvicorn/workers.py

L'abbiamo provato ma uvicorn non sta rispettando l'impostazione limit_concurrencyin più file uvicorn nel sorgente:

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,
    }

Come può l'uvicorno essere costretto a onorare questa impostazione? Stiamo ancora ricevendo 503 errori da FastAPI

------- UPDATE ----------- l'impostazione gunicorn --worker-connections 1000causa ancora 503 quando si effettuano 100 richieste parallele distribuite a molti worker.

Tuttavia, credo che sia un problema un po 'più complicato: il nostro endpoint API fa un carico di lavoro molto pesante, di solito richiede 5 secondi per essere completato.

Stress test con 2 core, 2 worker:

  • A. Oltre 100 richieste simultanee, carico pesante dell'endpoint - connessioni di lavoro 1
  • B. Più di 100 richieste simultanee, carico pesante dell'endpoint - connessioni di lavoro 1000
  • C. Oltre 100 richieste simultanee, endpoint a basso carico - connessioni di lavoro 1
  • D. 100+ richieste simultanee, endpoint basso carico - connessioni di lavoro 1000

Entrambi gli esperimenti A e B hanno prodotto 503 risposte, quindi supponendo che l'impostazione delle connessioni di lavoro funzioni, troppe connessioni simulate sembrano non causare i nostri errori 503.

Siamo perplessi su questo comportamento, perché ci aspettiamo che gunicorn / uvicorn metta in coda il lavoro e non lanci 503 errori.

Risposte

2 JPG Aug 19 2020 at 11:57

Dal gunicorn doc

worker-connections

Il numero massimo di client simultanei.

e da uvicorn doc

limit-concurrency

Numero massimo di connessioni o attività simultanee da consentire, prima di inviare risposte HTTP 503.

Secondo queste informazioni, entrambe le variabili di impostazione fanno la stessa cosa. Così

uvicorn --limit-concurrency 100 application:demo_app

è quasi uguale a

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

Nota: non ho fatto alcun vero test su questo, per favore correggimi se sbaglio.


Inoltre, puoi impostare limit-concurrency(o limit_concurrency) sottoclassando la uvicorn.workers.UvicornWorkerclasse

from uvicorn.workers import UvicornWorker


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

e ora usalo CustomUvicornWorkercon il gunicorncomando come,

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

Nota: puoi ispezionare self.config.limit_concurrencyin CustomUvicornWorkerclasse per assicurarti che il valore sia stato impostato correttamente.