From 758e5dc0a211615745060021cbfafc1c5d694e5b Mon Sep 17 00:00:00 2001 From: YorkSu Date: Sun, 26 Jan 2025 09:41:17 +0800 Subject: [PATCH 01/11] docs: gunicorn with uvicorn workers --- docs/topics/deployment/gunicorn.rst | 208 ++++++++++++++++++++++++++++ docs/topics/deployment/index.rst | 1 + 2 files changed, 209 insertions(+) create mode 100644 docs/topics/deployment/gunicorn.rst diff --git a/docs/topics/deployment/gunicorn.rst b/docs/topics/deployment/gunicorn.rst new file mode 100644 index 0000000000..d9efaed074 --- /dev/null +++ b/docs/topics/deployment/gunicorn.rst @@ -0,0 +1,208 @@ +Gunicorn with Uvicorn workers +============================= + +Gunicorn (Green Unicorn) is a Python WSGI HTTP Server that can be used with uvicorn workers to deploy ASGI applications. + +Use When +-------- + +Gunicorn with Uvicorn workers is commonly used in scenarios where you need: + +- **Process Management**: When you need a robust process manager to handle multiple worker processes, automatic worker restarts, and graceful reloads. +- **Load Distribution**: When your application needs to utilize multiple CPU cores by running multiple worker processes to handle concurrent requests. +- **Production Deployments**: When deploying applications that require process monitoring, health checks, and automatic recovery from failures. +- **Resource Control**: When you need fine-grained control over worker lifecycle, timeouts, and resource limits. +- **Zero-downtime Deployments**: When you need to perform rolling restarts of worker processes without dropping requests. + +These features make it particularly suitable for production environments where reliability and process management are critical requirements. + +.. important:: **Deprecation Notice** + + The Gunicorn+Uvicorn pattern is considered legacy for ASGI deployments since `Uvicorn 0.30.0+ `_ includes native worker management. + + - Uvicorn added a new multiprocess manager, that is meant to replace Gunicorn entirely. + - The main goal is to be able to have a proper process manager that can handle multiple workers and restart them when needed. + - You can see more details on the the pull request `#2183 `_. + + For new deployments, use Uvicorn directly: + + .. code-block:: shell + + uvicorn app:app --workers 4 + +Alternatives +~~~~~~~~~~~~ + +For different deployment scenarios, consider these alternatives: + +- :doc:`NGINX Unit `: + A dynamic web and application server, suitable for running and managing multiple applications. +- `systemd `_: + A system and service manager, integrated into many Linux distributions for managing system processes. + + .. note:: Official documentation coming soon +- :doc:`Manually with an ASGI server `: + Direct control by running the application with an ASGI server like Uvicorn, Hypercorn, Daphne, etc. +- `systemd `_: + A system and service manager, integrated into many Linux distributions for managing system processes. + + .. note:: Official documentation coming soon +- :doc:`Supervisor `: + A process control system that can be used to automatically start, stop and restart processes; includes a web UI. +- :doc:`Docker `: + Ideal for containerized environments, offering isolation and scalability. + +Installation +------------ + +.. code-block:: shell + + pip install gunicorn uvicorn + +Create a ``app.py`` file containing the reference of your Litestar app + +.. literalinclude:: /examples/todo_app/hello_world.py + :language: python + :caption: app.py + +Command Line Usage +------------------ + +Basic startup command: + +.. tab-set:: + + .. tab-item:: Basic + + .. code-block:: shell + :caption: Start with 4 worker processes + + gunicorn app:app -w 4 -k uvicorn.workers.UvicornWorker + + .. tab-item:: With Environment Variables + + .. code-block:: shell + :caption: Production-ready configuration with Docker + + gunicorn app:app \ + --bind 127.0.0.1:8000 \ + --worker-class uvicorn.workers.UvicornWorker \ + --workers ${WORKERS_PER_CORE:-4} \ + --max-workers ${MAX_WORKERS:-8} \ + --timeout 120 \ + --keep-alive 5 + +.. code-block:: console + :caption: Console Output + + [2025-01-24 23:51:22 +0800] [35955] [INFO] Starting gunicorn 23.0.0 + [2025-01-24 23:51:22 +0800] [35955] [INFO] Listening at: http://127.0.0.1:8000 (35955) + [2025-01-24 23:51:22 +0800] [35955] [INFO] Using worker: uvicorn.workers.UvicornWorker + [2025-01-24 23:51:22 +0800] [35962] [INFO] Booting worker with pid: 35962 + [2025-01-24 23:51:22 +0800] [35963] [INFO] Booting worker with pid: 35963 + [2025-01-24 23:51:22 +0800] [35964] [INFO] Booting worker with pid: 35964 + [2025-01-24 23:51:22 +0800] [35965] [INFO] Booting worker with pid: 35965 + [2025-01-24 23:51:23 +0800] [35962] [INFO] Started server process [35962] + [2025-01-24 23:51:23 +0800] [35962] [INFO] Waiting for application startup. + [2025-01-24 23:51:23 +0800] [35962] [INFO] Application startup complete. + [2025-01-24 23:51:23 +0800] [35963] [INFO] Started server process [35963] + [2025-01-24 23:51:23 +0800] [35963] [INFO] Waiting for application startup. + [2025-01-24 23:51:23 +0800] [35963] [INFO] Application startup complete. + [2025-01-24 23:51:23 +0800] [35964] [INFO] Started server process [35964] + [2025-01-24 23:51:23 +0800] [35964] [INFO] Waiting for application startup. + [2025-01-24 23:51:23 +0800] [35964] [INFO] Application startup complete. + [2025-01-24 23:51:23 +0800] [35965] [INFO] Started server process [35965] + [2025-01-24 23:51:23 +0800] [35965] [INFO] Waiting for application startup. + [2025-01-24 23:51:23 +0800] [35965] [INFO] Application startup complete. + +Integration with Project Code +----------------------------- + +Create a management script in your project (e.g. ``gunicorn_runner.py``): + +.. tab-set:: + + .. tab-item:: subprocess + + .. code-block:: python + :caption: gunicorn_runner.py + + import subprocess + import sys + import os + + + def main() -> None: + proc = subprocess.Popen( + [ + sys.executable, + "-m", + "gunicorn", + "app:app", + "--workers", + os.environ.get("WORKERS", "1"), + "--bind", + f"{os.environ.get('HOST', '127.0.0.1')}:{os.environ.get('PORT', '8000')}", + "--worker-class", + "uvicorn.workers.UvicornWorker", + ] + ) + + try: + proc.wait() + + except KeyboardInterrupt: + proc.terminate() + proc.wait(timeout=5) + + + if __name__ == "__main__": + main() + + .. tab-item:: BaseApplication + + For advanced usage, you can use the ``BaseApplication`` class directly. It will allow us to override Gunicorn's own logging configuration. + + .. code-block:: python + :caption: gunicorn_runner.py + + from __future__ import annotations + + import os + from typing import TYPE_CHECKING + + from gunicorn.app.base import BaseApplication + + from app import app + + if TYPE_CHECKING: + from litestar import Litestar + + + class StandaloneApplication(BaseApplication): + """Our Gunicorn application.""" + + def __init__(self, app: Litestar, options: dict[str, str] | None = None): + self.options = options or {} + self.application = app + super().__init__() + + def load_config(self): + if self.cfg is None: + raise AssertionError("StandaloneApplication must be loaded by a parent class") + + config = {key: value for key, value in self.options.items() if key in self.cfg.settings and value is not None} + for key, value in config.items(): + self.cfg.set(key.lower(), value) + + def load(self): + return self.application + + + if __name__ == "__main__": + options = { + "bind": f"{os.environ.get('HOST', '127.0.0.1')}:{os.environ.get('PORT', '8000')}", + "workers": int(os.environ.get("WORKERS", "1")), + "worker_class": "uvicorn.workers.UvicornWorker", + } + StandaloneApplication(app, options).run() diff --git a/docs/topics/deployment/index.rst b/docs/topics/deployment/index.rst index f7a7ff8f75..e4eeaa5590 100644 --- a/docs/topics/deployment/index.rst +++ b/docs/topics/deployment/index.rst @@ -12,5 +12,6 @@ Contents nginx-unit manually-with-asgi-server + gunicorn docker supervisor From ace54683fb3a44c7eef353b79a666ee017a25cf7 Mon Sep 17 00:00:00 2001 From: YorkSu Date: Sun, 26 Jan 2025 09:41:17 +0800 Subject: [PATCH 02/11] docs: update description --- docs/topics/deployment/gunicorn.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/topics/deployment/gunicorn.rst b/docs/topics/deployment/gunicorn.rst index d9efaed074..7cb77d568d 100644 --- a/docs/topics/deployment/gunicorn.rst +++ b/docs/topics/deployment/gunicorn.rst @@ -1,7 +1,9 @@ Gunicorn with Uvicorn workers ============================= -Gunicorn (Green Unicorn) is a Python WSGI HTTP Server that can be used with uvicorn workers to deploy ASGI applications. +Gunicorn (Green Unicorn) is mainly an application server using the WSGI standard. That means that Gunicorn can serve applications like Flask and Django. Gunicorn by itself is not compatible with Litestar, as Litestar uses the newest ASGI standard. + +But Gunicorn supports working as a process manager and allowing users to tell it which specific worker process class to use. Then Gunicorn would start one or more worker processes using that class. And Uvicorn has a Gunicorn-compatible worker class, so you can use Uvicorn workers with Gunicorn to deploy ASGI applications. Use When -------- From b6c37a42d694cd3079801b05cd91fd9d7f92c10e Mon Sep 17 00:00:00 2001 From: YorkSu Date: Sun, 26 Jan 2025 09:41:17 +0800 Subject: [PATCH 03/11] fix: remove duplicated systemd alternatives --- docs/topics/deployment/gunicorn.rst | 4 ---- 1 file changed, 4 deletions(-) diff --git a/docs/topics/deployment/gunicorn.rst b/docs/topics/deployment/gunicorn.rst index 7cb77d568d..8f8705dedc 100644 --- a/docs/topics/deployment/gunicorn.rst +++ b/docs/topics/deployment/gunicorn.rst @@ -45,10 +45,6 @@ For different deployment scenarios, consider these alternatives: .. note:: Official documentation coming soon - :doc:`Manually with an ASGI server `: Direct control by running the application with an ASGI server like Uvicorn, Hypercorn, Daphne, etc. -- `systemd `_: - A system and service manager, integrated into many Linux distributions for managing system processes. - - .. note:: Official documentation coming soon - :doc:`Supervisor `: A process control system that can be used to automatically start, stop and restart processes; includes a web UI. - :doc:`Docker `: From 9f86e171b11ec701d165ee0af920ac6cb8cfa318 Mon Sep 17 00:00:00 2001 From: YorkSu Date: Sun, 26 Jan 2025 09:41:17 +0800 Subject: [PATCH 04/11] fix: type annotation --- docs/topics/deployment/gunicorn.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/topics/deployment/gunicorn.rst b/docs/topics/deployment/gunicorn.rst index 8f8705dedc..4bc33a773d 100644 --- a/docs/topics/deployment/gunicorn.rst +++ b/docs/topics/deployment/gunicorn.rst @@ -180,12 +180,12 @@ Create a management script in your project (e.g. ``gunicorn_runner.py``): class StandaloneApplication(BaseApplication): """Our Gunicorn application.""" - def __init__(self, app: Litestar, options: dict[str, str] | None = None): + def __init__(self, app: Litestar, options: dict[str, str] | None = None) -> None: self.options = options or {} self.application = app super().__init__() - def load_config(self): + def load_config(self) -> None: if self.cfg is None: raise AssertionError("StandaloneApplication must be loaded by a parent class") @@ -193,7 +193,7 @@ Create a management script in your project (e.g. ``gunicorn_runner.py``): for key, value in config.items(): self.cfg.set(key.lower(), value) - def load(self): + def load(self) -> None: return self.application From 1fc5671ff58601f0de9504c008036d91cb28b418 Mon Sep 17 00:00:00 2001 From: YorkSu Date: Sun, 26 Jan 2025 09:41:17 +0800 Subject: [PATCH 05/11] docs: move integration into tab-set --- docs/topics/deployment/gunicorn.rst | 108 ++++++++-------------------- 1 file changed, 31 insertions(+), 77 deletions(-) diff --git a/docs/topics/deployment/gunicorn.rst b/docs/topics/deployment/gunicorn.rst index 4bc33a773d..be950e56c7 100644 --- a/docs/topics/deployment/gunicorn.rst +++ b/docs/topics/deployment/gunicorn.rst @@ -63,10 +63,8 @@ Create a ``app.py`` file containing the reference of your Litestar app :language: python :caption: app.py -Command Line Usage ------------------- - -Basic startup command: +Run Gunicorn with Uvicorn Workers +--------------------------------- .. tab-set:: @@ -83,83 +81,15 @@ Basic startup command: :caption: Production-ready configuration with Docker gunicorn app:app \ - --bind 127.0.0.1:8000 \ + --bind ${HOST:-127.0.0.1}:${PORT:-8000} \ --worker-class uvicorn.workers.UvicornWorker \ - --workers ${WORKERS_PER_CORE:-4} \ - --max-workers ${MAX_WORKERS:-8} \ - --timeout 120 \ - --keep-alive 5 - -.. code-block:: console - :caption: Console Output - - [2025-01-24 23:51:22 +0800] [35955] [INFO] Starting gunicorn 23.0.0 - [2025-01-24 23:51:22 +0800] [35955] [INFO] Listening at: http://127.0.0.1:8000 (35955) - [2025-01-24 23:51:22 +0800] [35955] [INFO] Using worker: uvicorn.workers.UvicornWorker - [2025-01-24 23:51:22 +0800] [35962] [INFO] Booting worker with pid: 35962 - [2025-01-24 23:51:22 +0800] [35963] [INFO] Booting worker with pid: 35963 - [2025-01-24 23:51:22 +0800] [35964] [INFO] Booting worker with pid: 35964 - [2025-01-24 23:51:22 +0800] [35965] [INFO] Booting worker with pid: 35965 - [2025-01-24 23:51:23 +0800] [35962] [INFO] Started server process [35962] - [2025-01-24 23:51:23 +0800] [35962] [INFO] Waiting for application startup. - [2025-01-24 23:51:23 +0800] [35962] [INFO] Application startup complete. - [2025-01-24 23:51:23 +0800] [35963] [INFO] Started server process [35963] - [2025-01-24 23:51:23 +0800] [35963] [INFO] Waiting for application startup. - [2025-01-24 23:51:23 +0800] [35963] [INFO] Application startup complete. - [2025-01-24 23:51:23 +0800] [35964] [INFO] Started server process [35964] - [2025-01-24 23:51:23 +0800] [35964] [INFO] Waiting for application startup. - [2025-01-24 23:51:23 +0800] [35964] [INFO] Application startup complete. - [2025-01-24 23:51:23 +0800] [35965] [INFO] Started server process [35965] - [2025-01-24 23:51:23 +0800] [35965] [INFO] Waiting for application startup. - [2025-01-24 23:51:23 +0800] [35965] [INFO] Application startup complete. - -Integration with Project Code ------------------------------ - -Create a management script in your project (e.g. ``gunicorn_runner.py``): - -.. tab-set:: - - .. tab-item:: subprocess - - .. code-block:: python - :caption: gunicorn_runner.py - - import subprocess - import sys - import os - + --workers ${WORKERS:-4} - def main() -> None: - proc = subprocess.Popen( - [ - sys.executable, - "-m", - "gunicorn", - "app:app", - "--workers", - os.environ.get("WORKERS", "1"), - "--bind", - f"{os.environ.get('HOST', '127.0.0.1')}:{os.environ.get('PORT', '8000')}", - "--worker-class", - "uvicorn.workers.UvicornWorker", - ] - ) - - try: - proc.wait() - - except KeyboardInterrupt: - proc.terminate() - proc.wait(timeout=5) + .. tab-item:: Integration + For advanced usage, you can use the ``BaseApplication`` class directly. It will allow us to to do some additional work, such as overriding Gunicorn's own logging configuration. - if __name__ == "__main__": - main() - - .. tab-item:: BaseApplication - - For advanced usage, you can use the ``BaseApplication`` class directly. It will allow us to override Gunicorn's own logging configuration. + Create a management script in your project (e.g. ``gunicorn_runner.py``). .. code-block:: python :caption: gunicorn_runner.py @@ -204,3 +134,27 @@ Create a management script in your project (e.g. ``gunicorn_runner.py``): "worker_class": "uvicorn.workers.UvicornWorker", } StandaloneApplication(app, options).run() + + +.. code-block:: console + :caption: Console Output + + [2025-01-24 23:51:22 +0800] [35955] [INFO] Starting gunicorn 23.0.0 + [2025-01-24 23:51:22 +0800] [35955] [INFO] Listening at: http://127.0.0.1:8000 (35955) + [2025-01-24 23:51:22 +0800] [35955] [INFO] Using worker: uvicorn.workers.UvicornWorker + [2025-01-24 23:51:22 +0800] [35962] [INFO] Booting worker with pid: 35962 + [2025-01-24 23:51:22 +0800] [35963] [INFO] Booting worker with pid: 35963 + [2025-01-24 23:51:22 +0800] [35964] [INFO] Booting worker with pid: 35964 + [2025-01-24 23:51:22 +0800] [35965] [INFO] Booting worker with pid: 35965 + [2025-01-24 23:51:23 +0800] [35962] [INFO] Started server process [35962] + [2025-01-24 23:51:23 +0800] [35962] [INFO] Waiting for application startup. + [2025-01-24 23:51:23 +0800] [35962] [INFO] Application startup complete. + [2025-01-24 23:51:23 +0800] [35963] [INFO] Started server process [35963] + [2025-01-24 23:51:23 +0800] [35963] [INFO] Waiting for application startup. + [2025-01-24 23:51:23 +0800] [35963] [INFO] Application startup complete. + [2025-01-24 23:51:23 +0800] [35964] [INFO] Started server process [35964] + [2025-01-24 23:51:23 +0800] [35964] [INFO] Waiting for application startup. + [2025-01-24 23:51:23 +0800] [35964] [INFO] Application startup complete. + [2025-01-24 23:51:23 +0800] [35965] [INFO] Started server process [35965] + [2025-01-24 23:51:23 +0800] [35965] [INFO] Waiting for application startup. + [2025-01-24 23:51:23 +0800] [35965] [INFO] Application startup complete. From 5a06d31cc9c270004715e92849e050a03555755e Mon Sep 17 00:00:00 2001 From: YorkSu Date: Sun, 26 Jan 2025 09:41:17 +0800 Subject: [PATCH 06/11] docs: improve integration part --- docs/topics/deployment/gunicorn.rst | 34 ++++++++++++++++++----------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/docs/topics/deployment/gunicorn.rst b/docs/topics/deployment/gunicorn.rst index be950e56c7..f9c7332a21 100644 --- a/docs/topics/deployment/gunicorn.rst +++ b/docs/topics/deployment/gunicorn.rst @@ -24,7 +24,7 @@ These features make it particularly suitable for production environments where r - Uvicorn added a new multiprocess manager, that is meant to replace Gunicorn entirely. - The main goal is to be able to have a proper process manager that can handle multiple workers and restart them when needed. - - You can see more details on the the pull request `#2183 `_. + - Refer to the pull request `#2183 `_ for implementation details. For new deployments, use Uvicorn directly: @@ -87,9 +87,9 @@ Run Gunicorn with Uvicorn Workers .. tab-item:: Integration - For advanced usage, you can use the ``BaseApplication`` class directly. It will allow us to to do some additional work, such as overriding Gunicorn's own logging configuration. + For advanced deployment configurations, developers can leverage the ``BaseApplication`` class to customize Gunicorn's operational parameters. This approach provides enhanced flexibility, particularly for implementing custom logging configurations and fine-tuned application initialization. - Create a management script in your project (e.g. ``gunicorn_runner.py``). + Create a dedicated management script within your project (e.g., ``gunicorn_runner.py``): .. code-block:: python :caption: gunicorn_runner.py @@ -97,20 +97,17 @@ Run Gunicorn with Uvicorn Workers from __future__ import annotations import os - from typing import TYPE_CHECKING + from typing import Callable from gunicorn.app.base import BaseApplication from app import app - - if TYPE_CHECKING: - from litestar import Litestar class StandaloneApplication(BaseApplication): """Our Gunicorn application.""" - def __init__(self, app: Litestar, options: dict[str, str] | None = None) -> None: + def __init__(self, app: Callable, options: dict[str, str] | None = None) -> None: self.options = options or {} self.application = app super().__init__() @@ -119,15 +116,15 @@ Run Gunicorn with Uvicorn Workers if self.cfg is None: raise AssertionError("StandaloneApplication must be loaded by a parent class") - config = {key: value for key, value in self.options.items() if key in self.cfg.settings and value is not None} - for key, value in config.items(): - self.cfg.set(key.lower(), value) + for key, value in self.options.items(): + if key in self.cfg.settings and value is not None: + self.cfg.set(key.lower(), value) - def load(self) -> None: + def load(self) -> Callable: return self.application - if __name__ == "__main__": + def main() -> None: options = { "bind": f"{os.environ.get('HOST', '127.0.0.1')}:{os.environ.get('PORT', '8000')}", "workers": int(os.environ.get("WORKERS", "1")), @@ -135,6 +132,17 @@ Run Gunicorn with Uvicorn Workers } StandaloneApplication(app, options).run() + if __name__ == "__main__": + main() + + Optionally, you can register this script in your ``pyproject.toml`` for streamlined project management: + + .. code-block:: toml + :caption: pyproject.toml + + [project.scripts] + gunicorn_runner = "gunicorn_runner:main" + .. code-block:: console :caption: Console Output From fb6e76b49c97216cec3dff6f5ccba8fe7f1ea73b Mon Sep 17 00:00:00 2001 From: YorkSu Date: Sun, 26 Jan 2025 09:41:17 +0800 Subject: [PATCH 07/11] docs: links to manually-with-asgi-server --- docs/topics/deployment/gunicorn.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/topics/deployment/gunicorn.rst b/docs/topics/deployment/gunicorn.rst index f9c7332a21..9331d415d1 100644 --- a/docs/topics/deployment/gunicorn.rst +++ b/docs/topics/deployment/gunicorn.rst @@ -26,7 +26,7 @@ These features make it particularly suitable for production environments where r - The main goal is to be able to have a proper process manager that can handle multiple workers and restart them when needed. - Refer to the pull request `#2183 `_ for implementation details. - For new deployments, use Uvicorn directly: + For new deployments, use :doc:`Uvicorn ` directly. .. code-block:: shell From 0bd4244814b5a44f06ce7a246c432076766b3918 Mon Sep 17 00:00:00 2001 From: YorkSu Date: Sun, 26 Jan 2025 09:41:17 +0800 Subject: [PATCH 08/11] fix: trim trailing whitespace --- docs/topics/deployment/gunicorn.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/topics/deployment/gunicorn.rst b/docs/topics/deployment/gunicorn.rst index 9331d415d1..1069b0abcd 100644 --- a/docs/topics/deployment/gunicorn.rst +++ b/docs/topics/deployment/gunicorn.rst @@ -20,7 +20,7 @@ These features make it particularly suitable for production environments where r .. important:: **Deprecation Notice** - The Gunicorn+Uvicorn pattern is considered legacy for ASGI deployments since `Uvicorn 0.30.0+ `_ includes native worker management. + The Gunicorn+Uvicorn pattern is considered legacy for ASGI deployments since `Uvicorn 0.30.0+ `_ includes native worker management. - Uvicorn added a new multiprocess manager, that is meant to replace Gunicorn entirely. - The main goal is to be able to have a proper process manager that can handle multiple workers and restart them when needed. From 199491260716cc2bb10a32e0847b2bf588ca7816 Mon Sep 17 00:00:00 2001 From: YorkSu Date: Sun, 26 Jan 2025 09:41:17 +0800 Subject: [PATCH 09/11] docs: move a brief description to manually with asgi server --- docs/topics/deployment/gunicorn.rst | 168 ------------------ docs/topics/deployment/index.rst | 1 - .../deployment/manually-with-asgi-server.rst | 43 +++++ 3 files changed, 43 insertions(+), 169 deletions(-) delete mode 100644 docs/topics/deployment/gunicorn.rst diff --git a/docs/topics/deployment/gunicorn.rst b/docs/topics/deployment/gunicorn.rst deleted file mode 100644 index 1069b0abcd..0000000000 --- a/docs/topics/deployment/gunicorn.rst +++ /dev/null @@ -1,168 +0,0 @@ -Gunicorn with Uvicorn workers -============================= - -Gunicorn (Green Unicorn) is mainly an application server using the WSGI standard. That means that Gunicorn can serve applications like Flask and Django. Gunicorn by itself is not compatible with Litestar, as Litestar uses the newest ASGI standard. - -But Gunicorn supports working as a process manager and allowing users to tell it which specific worker process class to use. Then Gunicorn would start one or more worker processes using that class. And Uvicorn has a Gunicorn-compatible worker class, so you can use Uvicorn workers with Gunicorn to deploy ASGI applications. - -Use When --------- - -Gunicorn with Uvicorn workers is commonly used in scenarios where you need: - -- **Process Management**: When you need a robust process manager to handle multiple worker processes, automatic worker restarts, and graceful reloads. -- **Load Distribution**: When your application needs to utilize multiple CPU cores by running multiple worker processes to handle concurrent requests. -- **Production Deployments**: When deploying applications that require process monitoring, health checks, and automatic recovery from failures. -- **Resource Control**: When you need fine-grained control over worker lifecycle, timeouts, and resource limits. -- **Zero-downtime Deployments**: When you need to perform rolling restarts of worker processes without dropping requests. - -These features make it particularly suitable for production environments where reliability and process management are critical requirements. - -.. important:: **Deprecation Notice** - - The Gunicorn+Uvicorn pattern is considered legacy for ASGI deployments since `Uvicorn 0.30.0+ `_ includes native worker management. - - - Uvicorn added a new multiprocess manager, that is meant to replace Gunicorn entirely. - - The main goal is to be able to have a proper process manager that can handle multiple workers and restart them when needed. - - Refer to the pull request `#2183 `_ for implementation details. - - For new deployments, use :doc:`Uvicorn ` directly. - - .. code-block:: shell - - uvicorn app:app --workers 4 - -Alternatives -~~~~~~~~~~~~ - -For different deployment scenarios, consider these alternatives: - -- :doc:`NGINX Unit `: - A dynamic web and application server, suitable for running and managing multiple applications. -- `systemd `_: - A system and service manager, integrated into many Linux distributions for managing system processes. - - .. note:: Official documentation coming soon -- :doc:`Manually with an ASGI server `: - Direct control by running the application with an ASGI server like Uvicorn, Hypercorn, Daphne, etc. -- :doc:`Supervisor `: - A process control system that can be used to automatically start, stop and restart processes; includes a web UI. -- :doc:`Docker `: - Ideal for containerized environments, offering isolation and scalability. - -Installation ------------- - -.. code-block:: shell - - pip install gunicorn uvicorn - -Create a ``app.py`` file containing the reference of your Litestar app - -.. literalinclude:: /examples/todo_app/hello_world.py - :language: python - :caption: app.py - -Run Gunicorn with Uvicorn Workers ---------------------------------- - -.. tab-set:: - - .. tab-item:: Basic - - .. code-block:: shell - :caption: Start with 4 worker processes - - gunicorn app:app -w 4 -k uvicorn.workers.UvicornWorker - - .. tab-item:: With Environment Variables - - .. code-block:: shell - :caption: Production-ready configuration with Docker - - gunicorn app:app \ - --bind ${HOST:-127.0.0.1}:${PORT:-8000} \ - --worker-class uvicorn.workers.UvicornWorker \ - --workers ${WORKERS:-4} - - .. tab-item:: Integration - - For advanced deployment configurations, developers can leverage the ``BaseApplication`` class to customize Gunicorn's operational parameters. This approach provides enhanced flexibility, particularly for implementing custom logging configurations and fine-tuned application initialization. - - Create a dedicated management script within your project (e.g., ``gunicorn_runner.py``): - - .. code-block:: python - :caption: gunicorn_runner.py - - from __future__ import annotations - - import os - from typing import Callable - - from gunicorn.app.base import BaseApplication - - from app import app - - - class StandaloneApplication(BaseApplication): - """Our Gunicorn application.""" - - def __init__(self, app: Callable, options: dict[str, str] | None = None) -> None: - self.options = options or {} - self.application = app - super().__init__() - - def load_config(self) -> None: - if self.cfg is None: - raise AssertionError("StandaloneApplication must be loaded by a parent class") - - for key, value in self.options.items(): - if key in self.cfg.settings and value is not None: - self.cfg.set(key.lower(), value) - - def load(self) -> Callable: - return self.application - - - def main() -> None: - options = { - "bind": f"{os.environ.get('HOST', '127.0.0.1')}:{os.environ.get('PORT', '8000')}", - "workers": int(os.environ.get("WORKERS", "1")), - "worker_class": "uvicorn.workers.UvicornWorker", - } - StandaloneApplication(app, options).run() - - if __name__ == "__main__": - main() - - Optionally, you can register this script in your ``pyproject.toml`` for streamlined project management: - - .. code-block:: toml - :caption: pyproject.toml - - [project.scripts] - gunicorn_runner = "gunicorn_runner:main" - - -.. code-block:: console - :caption: Console Output - - [2025-01-24 23:51:22 +0800] [35955] [INFO] Starting gunicorn 23.0.0 - [2025-01-24 23:51:22 +0800] [35955] [INFO] Listening at: http://127.0.0.1:8000 (35955) - [2025-01-24 23:51:22 +0800] [35955] [INFO] Using worker: uvicorn.workers.UvicornWorker - [2025-01-24 23:51:22 +0800] [35962] [INFO] Booting worker with pid: 35962 - [2025-01-24 23:51:22 +0800] [35963] [INFO] Booting worker with pid: 35963 - [2025-01-24 23:51:22 +0800] [35964] [INFO] Booting worker with pid: 35964 - [2025-01-24 23:51:22 +0800] [35965] [INFO] Booting worker with pid: 35965 - [2025-01-24 23:51:23 +0800] [35962] [INFO] Started server process [35962] - [2025-01-24 23:51:23 +0800] [35962] [INFO] Waiting for application startup. - [2025-01-24 23:51:23 +0800] [35962] [INFO] Application startup complete. - [2025-01-24 23:51:23 +0800] [35963] [INFO] Started server process [35963] - [2025-01-24 23:51:23 +0800] [35963] [INFO] Waiting for application startup. - [2025-01-24 23:51:23 +0800] [35963] [INFO] Application startup complete. - [2025-01-24 23:51:23 +0800] [35964] [INFO] Started server process [35964] - [2025-01-24 23:51:23 +0800] [35964] [INFO] Waiting for application startup. - [2025-01-24 23:51:23 +0800] [35964] [INFO] Application startup complete. - [2025-01-24 23:51:23 +0800] [35965] [INFO] Started server process [35965] - [2025-01-24 23:51:23 +0800] [35965] [INFO] Waiting for application startup. - [2025-01-24 23:51:23 +0800] [35965] [INFO] Application startup complete. diff --git a/docs/topics/deployment/index.rst b/docs/topics/deployment/index.rst index e4eeaa5590..f7a7ff8f75 100644 --- a/docs/topics/deployment/index.rst +++ b/docs/topics/deployment/index.rst @@ -12,6 +12,5 @@ Contents nginx-unit manually-with-asgi-server - gunicorn docker supervisor diff --git a/docs/topics/deployment/manually-with-asgi-server.rst b/docs/topics/deployment/manually-with-asgi-server.rst index 61436016d1..f44723c7c7 100644 --- a/docs/topics/deployment/manually-with-asgi-server.rst +++ b/docs/topics/deployment/manually-with-asgi-server.rst @@ -160,3 +160,46 @@ ASGI server with the following command: [INFO] Starting granian [INFO] Listening at: 127.0.0.1:8000 + +Gunicorn with Uvicorn workers +----------------------------- + +.. important:: **Deprecation Notice** + + The Gunicorn+Uvicorn pattern is considered legacy for ASGI deployments since `Uvicorn 0.30.0+ `_ includes native worker management. + + Uvicorn added a new multiprocess manager, that is meant to replace Gunicorn entirely. Refer to the pull request `#2183 `_ for implementation details. + + For new deployments, use `Uvicorn <#run-the-asgi-server>`_ directly. + +Gunicorn (Green Unicorn) is WSGI server which can serve applications like Flask and Django. Gunicorn by itself is not compatible with Litestar, as Litestar uses the newest ASGI standard. + +But Gunicorn supports working as a process manager and allowing users to tell it which specific worker process class to use. Then Gunicorn would start one or more worker processes using that class. And Uvicorn has a Gunicorn-compatible worker class, so you can use Uvicorn workers with Gunicorn to deploy ASGI applications. + +.. code-block:: shell + :caption: Start with 4 worker processes + + gunicorn app:app -w 4 -k uvicorn.workers.UvicornWorker + +.. code-block:: console + :caption: Console Output + + [2025-01-24 23:51:22 +0800] [35955] [INFO] Starting gunicorn 23.0.0 + [2025-01-24 23:51:22 +0800] [35955] [INFO] Listening at: http://127.0.0.1:8000 (35955) + [2025-01-24 23:51:22 +0800] [35955] [INFO] Using worker: uvicorn.workers.UvicornWorker + [2025-01-24 23:51:22 +0800] [35962] [INFO] Booting worker with pid: 35962 + [2025-01-24 23:51:22 +0800] [35963] [INFO] Booting worker with pid: 35963 + [2025-01-24 23:51:22 +0800] [35964] [INFO] Booting worker with pid: 35964 + [2025-01-24 23:51:22 +0800] [35965] [INFO] Booting worker with pid: 35965 + [2025-01-24 23:51:23 +0800] [35962] [INFO] Started server process [35962] + [2025-01-24 23:51:23 +0800] [35962] [INFO] Waiting for application startup. + [2025-01-24 23:51:23 +0800] [35962] [INFO] Application startup complete. + [2025-01-24 23:51:23 +0800] [35963] [INFO] Started server process [35963] + [2025-01-24 23:51:23 +0800] [35963] [INFO] Waiting for application startup. + [2025-01-24 23:51:23 +0800] [35963] [INFO] Application startup complete. + [2025-01-24 23:51:23 +0800] [35964] [INFO] Started server process [35964] + [2025-01-24 23:51:23 +0800] [35964] [INFO] Waiting for application startup. + [2025-01-24 23:51:23 +0800] [35964] [INFO] Application startup complete. + [2025-01-24 23:51:23 +0800] [35965] [INFO] Started server process [35965] + [2025-01-24 23:51:23 +0800] [35965] [INFO] Waiting for application startup. + [2025-01-24 23:51:23 +0800] [35965] [INFO] Application startup complete. From 94888512f5d0ad75030e855b3d91a4d457c93b93 Mon Sep 17 00:00:00 2001 From: YorkSu Date: Sun, 26 Jan 2025 09:41:17 +0800 Subject: [PATCH 10/11] fix: duplicate explicit target name --- docs/topics/deployment/manually-with-asgi-server.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/topics/deployment/manually-with-asgi-server.rst b/docs/topics/deployment/manually-with-asgi-server.rst index f44723c7c7..1df02a87fe 100644 --- a/docs/topics/deployment/manually-with-asgi-server.rst +++ b/docs/topics/deployment/manually-with-asgi-server.rst @@ -170,7 +170,7 @@ Gunicorn with Uvicorn workers Uvicorn added a new multiprocess manager, that is meant to replace Gunicorn entirely. Refer to the pull request `#2183 `_ for implementation details. - For new deployments, use `Uvicorn <#run-the-asgi-server>`_ directly. + For new deployments, use Uvicorn directly. Gunicorn (Green Unicorn) is WSGI server which can serve applications like Flask and Django. Gunicorn by itself is not compatible with Litestar, as Litestar uses the newest ASGI standard. From 5ec0f1bc5011e647927f401f06e6d716cc1b145b Mon Sep 17 00:00:00 2001 From: YorkSu Date: Fri, 31 Jan 2025 00:10:00 +0800 Subject: [PATCH 11/11] docs: only keep the notice --- .../deployment/manually-with-asgi-server.rst | 32 ------------------- 1 file changed, 32 deletions(-) diff --git a/docs/topics/deployment/manually-with-asgi-server.rst b/docs/topics/deployment/manually-with-asgi-server.rst index 1df02a87fe..f2bc614769 100644 --- a/docs/topics/deployment/manually-with-asgi-server.rst +++ b/docs/topics/deployment/manually-with-asgi-server.rst @@ -171,35 +171,3 @@ Gunicorn with Uvicorn workers Uvicorn added a new multiprocess manager, that is meant to replace Gunicorn entirely. Refer to the pull request `#2183 `_ for implementation details. For new deployments, use Uvicorn directly. - -Gunicorn (Green Unicorn) is WSGI server which can serve applications like Flask and Django. Gunicorn by itself is not compatible with Litestar, as Litestar uses the newest ASGI standard. - -But Gunicorn supports working as a process manager and allowing users to tell it which specific worker process class to use. Then Gunicorn would start one or more worker processes using that class. And Uvicorn has a Gunicorn-compatible worker class, so you can use Uvicorn workers with Gunicorn to deploy ASGI applications. - -.. code-block:: shell - :caption: Start with 4 worker processes - - gunicorn app:app -w 4 -k uvicorn.workers.UvicornWorker - -.. code-block:: console - :caption: Console Output - - [2025-01-24 23:51:22 +0800] [35955] [INFO] Starting gunicorn 23.0.0 - [2025-01-24 23:51:22 +0800] [35955] [INFO] Listening at: http://127.0.0.1:8000 (35955) - [2025-01-24 23:51:22 +0800] [35955] [INFO] Using worker: uvicorn.workers.UvicornWorker - [2025-01-24 23:51:22 +0800] [35962] [INFO] Booting worker with pid: 35962 - [2025-01-24 23:51:22 +0800] [35963] [INFO] Booting worker with pid: 35963 - [2025-01-24 23:51:22 +0800] [35964] [INFO] Booting worker with pid: 35964 - [2025-01-24 23:51:22 +0800] [35965] [INFO] Booting worker with pid: 35965 - [2025-01-24 23:51:23 +0800] [35962] [INFO] Started server process [35962] - [2025-01-24 23:51:23 +0800] [35962] [INFO] Waiting for application startup. - [2025-01-24 23:51:23 +0800] [35962] [INFO] Application startup complete. - [2025-01-24 23:51:23 +0800] [35963] [INFO] Started server process [35963] - [2025-01-24 23:51:23 +0800] [35963] [INFO] Waiting for application startup. - [2025-01-24 23:51:23 +0800] [35963] [INFO] Application startup complete. - [2025-01-24 23:51:23 +0800] [35964] [INFO] Started server process [35964] - [2025-01-24 23:51:23 +0800] [35964] [INFO] Waiting for application startup. - [2025-01-24 23:51:23 +0800] [35964] [INFO] Application startup complete. - [2025-01-24 23:51:23 +0800] [35965] [INFO] Started server process [35965] - [2025-01-24 23:51:23 +0800] [35965] [INFO] Waiting for application startup. - [2025-01-24 23:51:23 +0800] [35965] [INFO] Application startup complete.