background_tasks.py 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. """
  2. 后台定时任务管理
  3. """
  4. import asyncio
  5. from typing import List, Optional
  6. from contextlib import asynccontextmanager
  7. from app.database import get_db
  8. from app.core.logger import logger
  9. from app.services.key_rotation_service import KeyRotationService
  10. from app.services.session_service import SessionService
  11. from app.services.heartbeat_monitor import HeartbeatMonitor
  12. from app.models.system_config import SystemConfig
  13. from app.config import settings
  14. class BackgroundTaskManager:
  15. """后台任务管理器"""
  16. def __init__(self):
  17. self.tasks: List[asyncio.Task] = []
  18. self.running = False
  19. async def start_all_tasks(self):
  20. """启动所有后台任务"""
  21. if self.running:
  22. logger.warning("后台任务已在运行")
  23. return
  24. self.running = True
  25. logger.info("启动后台定时任务...")
  26. # 启动各个任务
  27. self.tasks = [
  28. asyncio.create_task(self._key_rotation_task()),
  29. asyncio.create_task(self._session_cleanup_task()),
  30. asyncio.create_task(self._heartbeat_cleanup_task()),
  31. ]
  32. logger.info(f"已启动 {len(self.tasks)} 个后台任务")
  33. async def stop_all_tasks(self):
  34. """停止所有后台任务"""
  35. if not self.running:
  36. logger.warning("后台任务未运行")
  37. return
  38. logger.info("停止后台定时任务...")
  39. self.running = False
  40. # 取消所有任务
  41. for task in self.tasks:
  42. if not task.done():
  43. task.cancel()
  44. # 等待任务完成
  45. try:
  46. await asyncio.gather(*self.tasks, return_exceptions=True)
  47. except asyncio.CancelledError:
  48. pass
  49. self.tasks.clear()
  50. logger.info("后台定时任务已停止")
  51. async def _key_rotation_task(self):
  52. """密钥轮换任务 - 根据配置间隔执行"""
  53. # 获取检查间隔配置
  54. check_interval_minutes = SystemConfig.get_config_value('key_rotation_check_interval_minutes', 5)
  55. check_interval_seconds = check_interval_minutes * 60
  56. logger.info(f"启动密钥轮换定时任务 (间隔: {check_interval_minutes}分钟)")
  57. while self.running:
  58. try:
  59. logger.debug("执行密钥轮换检查...")
  60. db = next(get_db())
  61. try:
  62. # 检查是否需要轮换密钥
  63. if KeyRotationService.should_rotate_key(db):
  64. logger.info("检测到需要轮换密钥,开始执行...")
  65. success, public_key, error_msg = KeyRotationService.rotate_key(db)
  66. if success:
  67. logger.info("密钥轮换成功")
  68. else:
  69. logger.error(f"密钥轮换失败: {error_msg}")
  70. else:
  71. logger.debug("当前无需轮换密钥")
  72. # 清理过期密钥
  73. cleaned_count = KeyRotationService.cleanup_expired_keys(db)
  74. if cleaned_count > 0:
  75. logger.info(f"清理了 {cleaned_count} 个过期密钥")
  76. finally:
  77. db.close()
  78. except Exception as e:
  79. logger.exception("密钥轮换任务异常")
  80. # 根据配置等待指定时间
  81. await asyncio.sleep(check_interval_seconds)
  82. async def _session_cleanup_task(self):
  83. """Session清理任务 - 根据配置间隔执行"""
  84. # 获取清理间隔配置
  85. cleanup_interval_hours = SystemConfig.get_config_value('session_cleanup_interval_hours', 2)
  86. cleanup_interval_seconds = cleanup_interval_hours * 3600
  87. logger.info(f"启动Session清理定时任务 (间隔: {cleanup_interval_hours}小时)")
  88. while self.running:
  89. try:
  90. logger.debug("执行Session清理...")
  91. db = next(get_db())
  92. try:
  93. cleaned_count = SessionService.cleanup_expired_sessions(db)
  94. if cleaned_count > 0:
  95. logger.info(f"清理了 {cleaned_count} 个30天前的非活跃Session记录")
  96. else:
  97. logger.debug("没有需要清理的旧Session记录")
  98. finally:
  99. db.close()
  100. except Exception as e:
  101. logger.exception("Session清理任务异常")
  102. # 根据配置等待指定时间
  103. await asyncio.sleep(cleanup_interval_seconds)
  104. async def _heartbeat_cleanup_task(self):
  105. """心跳监控缓存清理任务 - 根据配置间隔执行"""
  106. # 获取清理间隔配置
  107. cleanup_interval_hours = SystemConfig.get_config_value('heartbeat_cleanup_interval_hours', 1)
  108. cleanup_interval_seconds = cleanup_interval_hours * 3600
  109. logger.info(f"启动心跳缓存清理定时任务 (间隔: {cleanup_interval_hours}小时)")
  110. while self.running:
  111. try:
  112. logger.debug("执行心跳缓存清理...")
  113. cleaned_count = HeartbeatMonitor.cleanup_cache()
  114. if cleaned_count > 0:
  115. logger.info(f"清理了 {cleaned_count} 个过期的缓存条目")
  116. else:
  117. logger.debug("心跳缓存状态正常,无需清理")
  118. # 输出监控统计信息
  119. stats = HeartbeatMonitor.get_monitoring_stats()
  120. logger.info(f"心跳监控统计: 监控用户={stats.get('total_monitored_users', 0)}, "
  121. f"受保护用户={stats.get('protected_users', 0)}, "
  122. f"失败用户={stats.get('failing_users', 0)}, "
  123. f"缓存大小={stats.get('cache_size', 0)}")
  124. except Exception as e:
  125. logger.exception("心跳缓存清理任务异常")
  126. # 根据配置等待指定时间
  127. await asyncio.sleep(cleanup_interval_seconds)
  128. # 全局后台任务管理器实例
  129. background_task_manager = BackgroundTaskManager()
  130. @asynccontextmanager
  131. async def lifespan_manager(app):
  132. """FastAPI lifespan 事件管理器"""
  133. # 启动时执行
  134. logger.info("应用启动,开始初始化后台任务...")
  135. await background_task_manager.start_all_tasks()
  136. yield
  137. # 关闭时执行
  138. logger.info("应用关闭,开始清理后台任务...")
  139. await background_task_manager.stop_all_tasks()
  140. # 便捷函数
  141. async def start_background_tasks():
  142. """启动后台任务(手动调用)"""
  143. await background_task_manager.start_all_tasks()
  144. async def stop_background_tasks():
  145. """停止后台任务(手动调用)"""
  146. await background_task_manager.stop_all_tasks()
  147. def get_task_manager() -> BackgroundTaskManager:
  148. """获取后台任务管理器实例"""
  149. return background_task_manager