gunicorn uvicorn worker.py come onorare l'impostazione limit_concurrency
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_concurrency
in 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 1000
causa 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
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.UvicornWorker
classe
from uvicorn.workers import UvicornWorker
class CustomUvicornWorker(UvicornWorker):
CONFIG_KWARGS = {
"loop": "uvloop",
"http": "httptools",
"limit_concurrency": 100
}
e ora usalo CustomUvicornWorker
con il gunicorn
comando come,
gunicorn -k path.to.custom_worker.CustomUvicornWorker application:demo_app
Nota: puoi ispezionare self.config.limit_concurrency
in CustomUvicornWorker
classe per assicurarti che il valore sia stato impostato correttamente.