workers.py 3.8 KB

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