gunicorn uvicorn worker.py как соблюдать настройку limit_concurrency

Aug 19 2020

FastAPI использует gunicorn для запуска uvicorn worker, как описано в https://www.uvicorn.org/settings/

Однако Gunicorn не позволяет запускать uvicorn с пользовательскими настройками, как также упоминается в https://github.com/encode/uvicorn/issues/343

Проблема предлагала переопределить config_kwargs в исходном файле, например https://github.com/encode/uvicorn/blob/master/uvicorn/workers.py

Мы пробовали это, но uvicorn не соблюдает настройки limit_concurrencyв нескольких файлах uvicorn в источнике:

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

Как можно заставить uvicorn соблюдать эту настройку? Мы все еще получаем 503 ошибки от FastAPI

------- ОБНОВЛЕНИЕ ----------- настройка пулемета по- --worker-connections 1000прежнему вызывает 503 при выполнении 100 параллельных запросов, которые распределяются между множеством рабочих.

Однако я считаю, что это немного более сложная проблема: наша конечная точка API выполняет много тяжелой рабочей нагрузки, обычно на выполнение требуется 5 секунд.

Стресс-тест с 2 ядрами, 2 рабочими:

  • A. 100+ одновременных запросов, большая нагрузка на конечную точку - рабочие-соединения 1
  • Б. 100+ одновременных запросов, высокая нагрузка на конечную точку - рабочие-соединения 1000
  • C. 100+ одновременных запросов, низкая нагрузка на конечную точку - рабочие-соединения 1
  • D. Более 100 одновременных запросов, низкая нагрузка на конечную точку - рабочие-соединения 1000

Оба эксперимента A и B дали 503 ответа, поэтому, если предположить, что настройка рабочих соединений действительно работает, слишком много имитирующих соединений, похоже, не вызывают наших ошибок 503.

Мы озадачены таким поведением, потому что ожидаем, что gunicorn / uvicorn поставит работу в очередь и не выдаст 503 ошибки.

Ответы

2 JPG Aug 19 2020 at 11:57

Из документа Gunicorn

worker-connections

Максимальное количество одновременных клиентов.

и из uvicorn doc

limit-concurrency

Максимальное разрешенное количество одновременных подключений или задач до выдачи ответов HTTP 503.

Согласно этой информации, обе переменные настроек делают одно и то же. Так

uvicorn --limit-concurrency 100 application:demo_app

почти так же, как

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

Примечание: я не проводил никаких реальных испытаний по этому поводу, пожалуйста, поправьте меня, если я ошибаюсь.


Кроме того, вы можете установить limit-concurrency(или limit_concurrency) путем создания подкласса uvicorn.workers.UvicornWorkerкласса

from uvicorn.workers import UvicornWorker


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

и теперь используйте это CustomUvicornWorkerс gunicornкомандой как,

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

Примечание: вы можете проверить self.config.limit_concurrencyв CustomUvicornWorkerклассе, чтобы убедиться, что значение установлено правильно.