workers.py 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. from __future__ import annotations
  2. import asyncio
  3. import logging
  4. import signal
  5. import sys
  6. import warnings
  7. from typing import Any
  8. from gunicorn.arbiter import Arbiter
  9. from gunicorn.workers.base import Worker
  10. from uvicorn.config import Config
  11. from uvicorn.server import Server
  12. warnings.warn(
  13. "The `uvicorn.workers` module is deprecated. Please use `uvicorn-worker` package instead.\n"
  14. "For more details, see https://github.com/Kludex/uvicorn-worker.",
  15. DeprecationWarning,
  16. )
  17. class UvicornWorker(Worker):
  18. """
  19. A worker class for Gunicorn that interfaces with an ASGI consumer callable,
  20. rather than a WSGI callable.
  21. """
  22. CONFIG_KWARGS: dict[str, Any] = {"loop": "auto", "http": "auto"}
  23. def __init__(self, *args: Any, **kwargs: Any) -> None:
  24. super().__init__(*args, **kwargs)
  25. logger = logging.getLogger("uvicorn.error")
  26. logger.handlers = self.log.error_log.handlers
  27. logger.setLevel(self.log.error_log.level)
  28. logger.propagate = False
  29. logger = logging.getLogger("uvicorn.access")
  30. logger.handlers = self.log.access_log.handlers
  31. logger.setLevel(self.log.access_log.level)
  32. logger.propagate = False
  33. config_kwargs: dict = {
  34. "app": None,
  35. "log_config": None,
  36. "timeout_keep_alive": self.cfg.keepalive,
  37. "timeout_notify": self.timeout,
  38. "callback_notify": self.callback_notify,
  39. "limit_max_requests": self.max_requests,
  40. "forwarded_allow_ips": self.cfg.forwarded_allow_ips,
  41. }
  42. if self.cfg.is_ssl:
  43. ssl_kwargs = {
  44. "ssl_keyfile": self.cfg.ssl_options.get("keyfile"),
  45. "ssl_certfile": self.cfg.ssl_options.get("certfile"),
  46. "ssl_keyfile_password": self.cfg.ssl_options.get("password"),
  47. "ssl_version": self.cfg.ssl_options.get("ssl_version"),
  48. "ssl_cert_reqs": self.cfg.ssl_options.get("cert_reqs"),
  49. "ssl_ca_certs": self.cfg.ssl_options.get("ca_certs"),
  50. "ssl_ciphers": self.cfg.ssl_options.get("ciphers"),
  51. }
  52. config_kwargs.update(ssl_kwargs)
  53. if self.cfg.settings["backlog"].value:
  54. config_kwargs["backlog"] = self.cfg.settings["backlog"].value
  55. config_kwargs.update(self.CONFIG_KWARGS)
  56. self.config = Config(**config_kwargs)
  57. def init_process(self) -> None:
  58. self.config.setup_event_loop()
  59. super().init_process()
  60. def init_signals(self) -> None:
  61. # Reset signals so Gunicorn doesn't swallow subprocess return codes
  62. # other signals are set up by Server.install_signal_handlers()
  63. # See: https://github.com/encode/uvicorn/issues/894
  64. for s in self.SIGNALS:
  65. signal.signal(s, signal.SIG_DFL)
  66. signal.signal(signal.SIGUSR1, self.handle_usr1)
  67. # Don't let SIGUSR1 disturb active requests by interrupting system calls
  68. signal.siginterrupt(signal.SIGUSR1, False)
  69. def _install_sigquit_handler(self) -> None:
  70. """Install a SIGQUIT handler on workers.
  71. - https://github.com/encode/uvicorn/issues/1116
  72. - https://github.com/benoitc/gunicorn/issues/2604
  73. """
  74. loop = asyncio.get_running_loop()
  75. loop.add_signal_handler(signal.SIGQUIT, self.handle_exit, signal.SIGQUIT, None)
  76. async def _serve(self) -> None:
  77. self.config.app = self.wsgi
  78. server = Server(config=self.config)
  79. self._install_sigquit_handler()
  80. await server.serve(sockets=self.sockets)
  81. if not server.started:
  82. sys.exit(Arbiter.WORKER_BOOT_ERROR)
  83. def run(self) -> None:
  84. return asyncio.run(self._serve())
  85. async def callback_notify(self) -> None:
  86. self.notify()
  87. class UvicornH11Worker(UvicornWorker):
  88. CONFIG_KWARGS = {"loop": "asyncio", "http": "h11"}