signals.py 2.0 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758
  1. """
  2. 操作系统信号和 Windows 控制台事件处理。
  3. """
  4. import sys
  5. import threading
  6. import signal
  7. from foundation.observability.logger.loggering import server_logger
  8. _shutdown_lock = threading.Lock()
  9. _shutdown_called = False
  10. def _run_shutdown_once(shutdown_callback):
  11. """确保 shutdown 回调在整个进程生命周期内只执行一次。"""
  12. global _shutdown_called
  13. with _shutdown_lock:
  14. if _shutdown_called:
  15. return
  16. _shutdown_called = True
  17. shutdown_callback()
  18. def setup_signal_handlers(shutdown_callback):
  19. """注册信号处理器,shutdown_callback 会在收到终止信号时调用(通过 _run_shutdown_once 确保只执行一次)。"""
  20. def handler(signum, frame):
  21. server_logger.info(f"收到信号 {signum},正在停止服务...")
  22. _run_shutdown_once(shutdown_callback)
  23. try:
  24. signal.signal(signal.SIGINT, handler)
  25. signal.signal(signal.SIGTERM, handler)
  26. except AttributeError:
  27. pass
  28. if sys.platform == 'win32':
  29. _setup_windows_handler(shutdown_callback)
  30. def _setup_windows_handler(shutdown_callback):
  31. try:
  32. import win32api
  33. def win32_handler(dwCtrlType):
  34. CTRL_C_EVENT = 0
  35. CTRL_BREAK_EVENT = 1
  36. CTRL_CLOSE_EVENT = 2
  37. CTRL_SHUTDOWN_EVENT = 6
  38. if dwCtrlType in (CTRL_C_EVENT, CTRL_BREAK_EVENT, CTRL_CLOSE_EVENT, CTRL_SHUTDOWN_EVENT):
  39. server_logger.info(f"收到Windows控制台事件 {dwCtrlType},正在停止服务...")
  40. # 将耗时 shutdown 操作放到后台线程,避免阻塞 console handler 超时
  41. threading.Thread(
  42. target=lambda: _run_shutdown_once(shutdown_callback),
  43. daemon=True,
  44. ).start()
  45. return False
  46. win32api.SetConsoleCtrlHandler(win32_handler, True)
  47. except (ImportError, AttributeError) as e:
  48. server_logger.debug(f"Windows控制台事件处理不可用: {e}")