294 lines
10 KiB
Python
294 lines
10 KiB
Python
#!/usr/bin/env python
|
||
# -*- coding: utf-8 -*-
|
||
|
||
"""
|
||
定时任务管理模块
|
||
负责管理周期性任务的调度
|
||
"""
|
||
|
||
import asyncio
|
||
import logging
|
||
import json
|
||
from typing import Dict, List, Any, Optional, Set, Tuple, Callable, Coroutine
|
||
from datetime import datetime, timedelta
|
||
from sqlalchemy import select
|
||
|
||
from config.settings import settings
|
||
from data.models.taskdef import VWEDTaskDef
|
||
from data.session import get_async_session
|
||
from utils.logger import get_logger
|
||
from data.enum.task_def_enum import EnableStatus, PeriodicTaskStatus
|
||
|
||
# 获取日志记录器
|
||
logger = get_logger("services.enhanced_scheduler.periodic_task_manager")
|
||
|
||
class PeriodicTaskManager:
|
||
"""
|
||
定时任务管理器
|
||
负责管理和调度周期性任务
|
||
"""
|
||
|
||
def __init__(self, check_interval: int = 5):
|
||
"""
|
||
初始化定时任务管理器
|
||
|
||
Args:
|
||
check_interval: 任务检查间隔(秒)
|
||
"""
|
||
self.check_interval = check_interval
|
||
self.periodic_tasks = {} # 定时任务字典 {task_def_id: task_info}
|
||
self.is_running = False
|
||
self.task_runner = None
|
||
|
||
# 任务执行回调函数
|
||
self.run_task_callback = None
|
||
|
||
logger.info(f"初始化定时任务管理器: 检查间隔={check_interval}秒")
|
||
|
||
def set_run_task_callback(self, callback: Callable[[str], Coroutine]):
|
||
"""
|
||
设置任务执行回调函数
|
||
|
||
Args:
|
||
callback: 任务执行回调函数,接受task_def_id参数,返回协程对象
|
||
"""
|
||
self.run_task_callback = callback
|
||
|
||
async def start(self) -> None:
|
||
"""
|
||
启动定时任务管理器
|
||
"""
|
||
if self.is_running:
|
||
logger.warning("定时任务管理器已经在运行中")
|
||
return
|
||
|
||
if not self.run_task_callback:
|
||
raise ValueError("未设置任务执行回调函数")
|
||
|
||
self.is_running = True
|
||
|
||
# 加载已配置的定时任务
|
||
await self._load_periodic_tasks()
|
||
|
||
# 启动任务执行器
|
||
self.task_runner = asyncio.create_task(self._task_runner())
|
||
|
||
logger.info("定时任务管理器启动成功")
|
||
|
||
async def stop(self) -> None:
|
||
"""
|
||
停止定时任务管理器
|
||
"""
|
||
if not self.is_running:
|
||
logger.warning("定时任务管理器未在运行")
|
||
return
|
||
|
||
self.is_running = False
|
||
|
||
# 取消任务执行器
|
||
if self.task_runner:
|
||
self.task_runner.cancel()
|
||
try:
|
||
await self.task_runner
|
||
except asyncio.CancelledError:
|
||
pass
|
||
self.task_runner = None
|
||
|
||
logger.info("定时任务管理器已停止")
|
||
|
||
async def update_task(self, task_def_id: str, enable: bool = None) -> Dict[str, Any]:
|
||
"""
|
||
更新定时任务
|
||
|
||
Args:
|
||
task_def_id: 任务定义ID
|
||
enable: 是否启用
|
||
|
||
Returns:
|
||
Dict[str, Any]: 更新结果
|
||
"""
|
||
async with get_async_session() as session:
|
||
# 查询任务定义
|
||
result = await session.execute(
|
||
select(VWEDTaskDef).where(VWEDTaskDef.id == task_def_id)
|
||
)
|
||
task_def = result.scalars().first()
|
||
if not task_def:
|
||
return {
|
||
"success": False,
|
||
"message": "任务定义不存在",
|
||
"taskDefId": task_def_id
|
||
}
|
||
|
||
# 检查是否是定时任务
|
||
if task_def.periodic_task != PeriodicTaskStatus.PERIODIC:
|
||
return {
|
||
"success": False,
|
||
"message": "该任务不是定时任务",
|
||
"taskDefId": task_def_id
|
||
}
|
||
|
||
# 更新任务定义
|
||
updated = False
|
||
|
||
if enable is not None:
|
||
task_def.if_enable = EnableStatus.ENABLED if enable else EnableStatus.DISABLED
|
||
updated = True
|
||
|
||
# 如果有更新,提交到数据库
|
||
if updated:
|
||
await session.commit()
|
||
|
||
# 更新定时任务列表
|
||
if enable == True or task_def.if_enable == EnableStatus.ENABLED:
|
||
# 重新构建任务信息
|
||
period = task_def.period or 3600000 # 默认1小时
|
||
delay = task_def.delay or 3000 # 默认延迟3秒
|
||
|
||
# 计算下次运行时间
|
||
next_run = self._calculate_next_run_time(period, delay)
|
||
|
||
self.periodic_tasks[task_def_id] = {
|
||
"id": task_def.id,
|
||
"label": task_def.label,
|
||
"period": period, # 周期(毫秒)
|
||
"delay": delay, # 延迟(毫秒)
|
||
"last_run": None, # 上次运行时间
|
||
"next_run": next_run # 下次运行时间
|
||
}
|
||
elif enable == False and task_def_id in self.periodic_tasks:
|
||
# 从定时任务列表中移除
|
||
self.periodic_tasks.pop(task_def_id, None)
|
||
|
||
return {
|
||
"success": True,
|
||
"message": f"定时任务已{'更新' if updated else '保持不变'}",
|
||
"taskDefId": task_def_id,
|
||
"enabled": task_def.if_enable == EnableStatus.ENABLED
|
||
}
|
||
|
||
async def _load_periodic_tasks(self) -> None:
|
||
"""
|
||
加载所有定时任务
|
||
"""
|
||
try:
|
||
async with get_async_session() as session:
|
||
# 查询所有启用的定时任务
|
||
result = await session.execute(
|
||
select(VWEDTaskDef).where(
|
||
VWEDTaskDef.periodic_task == PeriodicTaskStatus.PERIODIC, # 周期任务
|
||
VWEDTaskDef.if_enable == EnableStatus.ENABLED # 启用状态
|
||
)
|
||
)
|
||
tasks = result.scalars().all()
|
||
|
||
# 记录定时任务
|
||
for task in tasks:
|
||
# 计算下次运行时间
|
||
period = task.period or 3600000 # 默认1小时
|
||
delay = task.delay or 3000 # 默认延迟3秒
|
||
next_run = self._calculate_next_run_time(period, delay)
|
||
|
||
self.periodic_tasks[task.id] = {
|
||
"id": task.id,
|
||
"label": task.label,
|
||
"period": period, # 周期(毫秒)
|
||
"delay": delay, # 延迟(毫秒)
|
||
"last_run": None, # 上次运行时间
|
||
"next_run": next_run # 下次运行时间
|
||
}
|
||
|
||
logger.info(f"已加载 {len(self.periodic_tasks)} 个定时任务")
|
||
except Exception as e:
|
||
logger.error(f"加载定时任务失败: {str(e)}")
|
||
|
||
async def _task_runner(self) -> None:
|
||
"""
|
||
任务执行器
|
||
定期检查并运行周期任务
|
||
"""
|
||
logger.info("定时任务执行器启动")
|
||
|
||
while self.is_running:
|
||
try:
|
||
# 检查是否有需要执行的定时任务
|
||
now = datetime.now()
|
||
tasks_to_run = []
|
||
|
||
for task_id, task_info in list(self.periodic_tasks.items()):
|
||
if task_info["next_run"] <= now:
|
||
tasks_to_run.append(task_id)
|
||
|
||
# 更新下次运行时间
|
||
task_info["last_run"] = now
|
||
task_info["next_run"] = self._calculate_next_run_time(
|
||
task_info["period"],
|
||
task_info["delay"],
|
||
now
|
||
)
|
||
|
||
# 执行需要运行的任务
|
||
for task_id in tasks_to_run:
|
||
try:
|
||
await self.run_task_callback(task_id)
|
||
logger.info(f"定时任务 {task_id} 已提交执行")
|
||
except Exception as e:
|
||
logger.error(f"提交定时任务 {task_id} 失败: {str(e)}")
|
||
|
||
# 休眠一段时间
|
||
await asyncio.sleep(self.check_interval)
|
||
|
||
except asyncio.CancelledError:
|
||
# 取消异常,退出循环
|
||
logger.info("定时任务执行器被取消")
|
||
break
|
||
except Exception as e:
|
||
logger.error(f"定时任务执行器异常: {str(e)}")
|
||
# 出现异常时短暂休眠,避免频繁错误
|
||
await asyncio.sleep(5.0)
|
||
|
||
logger.info("定时任务执行器结束")
|
||
|
||
def _calculate_next_run_time(self, period_ms: int, delay_ms: int = 0, base_time: datetime = None) -> datetime:
|
||
"""
|
||
计算下次运行时间
|
||
|
||
Args:
|
||
period_ms: 周期(毫秒)
|
||
delay_ms: 延迟时间(毫秒),初次运行时使用
|
||
base_time: 基准时间,默认为当前时间
|
||
|
||
Returns:
|
||
datetime: 下次运行时间
|
||
"""
|
||
now = base_time or datetime.now()
|
||
|
||
# 使用简单的周期计算
|
||
total_delay_seconds = (period_ms + delay_ms) / 1000 # 转换为秒
|
||
if base_time is None:
|
||
# 首次计算时考虑延迟
|
||
total_delay_seconds = delay_ms / 1000
|
||
|
||
return now + timedelta(seconds=total_delay_seconds)
|
||
|
||
def get_task_status(self) -> Dict[str, Any]:
|
||
"""
|
||
获取任务状态信息
|
||
|
||
Returns:
|
||
Dict[str, Any]: 任务状态
|
||
"""
|
||
return {
|
||
"task_count": len(self.periodic_tasks),
|
||
"tasks": {
|
||
task_id: {
|
||
"id": task_info["id"],
|
||
"label": task_info["label"],
|
||
"last_run": task_info["last_run"].strftime("%Y-%m-%d %H:%M:%S") if task_info["last_run"] else None,
|
||
"next_run": task_info["next_run"].strftime("%Y-%m-%d %H:%M:%S") if task_info["next_run"] else None,
|
||
"period": task_info["period"],
|
||
"delay": task_info.get("delay", 0)
|
||
}
|
||
for task_id, task_info in self.periodic_tasks.items()
|
||
}
|
||
} |