This commit is contained in:
konjacpotato
2025-11-14 21:45:00 +08:00
parent fd2a3171ad
commit 52d5bbc216
3 changed files with 173 additions and 57 deletions

View File

@ -12,13 +12,13 @@ from database.tscheduler.crud import get_tasks_by_executor
from log.log_manager import log
"""
这是一个特殊的任务,负责管理任务,命名为管理者任务。
这是一个特殊的任务,负责管理任务,命名为任务管理者任务。
工作流程:
1 检索数据库任务数据表
1 检索数据库任务数据表t_scheduler
2 检查是否已经在任务队列中,如果不在则添加
任务执行时间间隔为600秒。
任务执行时间间隔为{config.scheduler_interval}秒。
"""
@ -26,20 +26,40 @@ from log.log_manager import log
def log_task_execution(task_name: str, start_time: float, end_time: float = None):
"""辅助函数,记录任务的开始和结束日志"""
start_time_str = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(start_time))
end_time_str = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(end_time))
# 如果没有提供 end_time只记录开始日志
if end_time is None:
log(f"{task_name} start execute at {start_time_str}")
else:
elapsed_time = end_time - start_time
log(f"{task_name} end execute at {end_time_str}, use time {elapsed_time:.2f} seconds")
return
# 否则格式化结束时间并记录耗时
try:
end_time_str = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(end_time))
except Exception:
end_time_str = str(end_time)
elapsed_time = end_time - start_time
log(f"{task_name} end execute at {end_time_str}, use time {elapsed_time:.2f} seconds")
def execute_task(task: callable):
"""执行任务并记录日志"""
start_time = time.time()
task_name = task.func.__name__ if isinstance(task, functools.partial) else task.__name__
# 获取可读的任务名称
try:
task_name = task.func.__name__ if isinstance(task, functools.partial) else task.__name__
except Exception:
task_name = getattr(task, '__name__', repr(task))
log_task_execution(task_name, start_time) # 先记录开始时间
task()
# 捕获任务执行中的异常,防止调度器崩溃
try:
task()
except Exception as e:
end_time = time.time()
log(f"{task_name} raised exception: {e}")
log_task_execution(task_name, start_time, end_time)
return
end_time = time.time()
log_task_execution(task_name, start_time, end_time) # 记录结束时间
@ -47,68 +67,89 @@ def execute_task(task: callable):
def load_tasks(scheduler: BlockingScheduler):
with get_session() as db:
tasks = get_tasks_by_executor(db, config.scheduler_name)
for task in tasks:
module_path = task.module_path
function_name = task.function_name
trigger = task.trigger
interval_seconds = task.interval_seconds
task_id = task.id
for db_task in tasks:
module_path = db_task.module_path
function_name = db_task.function_name
trigger = db_task.trigger
interval_seconds = db_task.interval_seconds
task_id = db_task.id
# 动态导入模块和函数
module = importlib.import_module(module_path)
task_function = partial(execute_task, partial(getattr(module, function_name), task))
# 动态导入模块和函数,失败则记录并跳过该任务
try:
module = importlib.import_module(module_path)
func = getattr(module, function_name)
except Exception as e:
log(f"Failed to import {module_path}.{function_name} for task id={task_id}: {e}")
continue
# 检查任务是否已存在
if not scheduler.get_job(str(task_id)):
# 将数据库任务对象作为参数传入任务函数
task_callable = partial(func, db_task)
task_function = partial(execute_task, task_callable)
try:
if trigger == "interval":
scheduler.add_job(
task_function,
"interval",
seconds=interval_seconds,
seconds=interval_seconds or 0,
id=str(task_id),
replace_existing=True
)
log(f"Task {task.task_name} added with interval {interval_seconds} seconds")
log(f"Task {db_task.task_name} added/updated with interval {interval_seconds} seconds")
elif trigger == "cron":
# 解析 cron 表达式的字段
fields = task.cron_expression.split()
# 确保字段长度符合七字段格式
fields = (db_task.cron_expression or "").split()
if len(fields) != 7:
raise ValueError("无效的 Quartz cron 表达式")
# 替换 Quartz 风格的 `?` 为 APScheduler 可接受的 `*`
log(f"Invalid cron expression for task id={task_id}: {db_task.cron_expression}")
continue
# 将 Quartz 的 `?` 替换为 APScheduler 可接受的 `*`
if fields[5] == '?':
fields[5] = '*' # 替换 `day_of_week` 字段中的 `?`
# 使用 cron 表达式的字段添加任务
fields[5] = '*'
scheduler.add_job(
task_function, # 要执行的任务
'cron', # 使用 cron 触发器
second=fields[0], # 秒
minute=fields[1], # 分钟
hour=fields[2], # 小时
day=fields[3], # 日期
month=fields[4], # 月份
day_of_week=fields[5], # 星期
year=fields[6], # 年份
task_function,
'cron',
second=fields[0],
minute=fields[1],
hour=fields[2],
day=fields[3],
month=fields[4],
day_of_week=fields[5],
year=fields[6],
id=str(task_id),
replace_existing=True
)
log(f"Task {task.task_name} added with cron {task.cron_expression}")
log(f"Task {db_task.task_name} added/updated with cron {db_task.cron_expression}")
elif trigger == "date":
# 如果task.execution_date为None则设置为当前时间10秒后
if task.execution_date is None :
task.execution_date = datetime.now() + timedelta(seconds=10)
run_date = db_task.execution_date
# 如果 execution_date 为 None 或不是 datetime尝试解析或回退为现在+10s
if run_date is None or not isinstance(run_date, datetime):
try:
# 尝试解析 ISO 格式字符串(如果传入的是字符串)
if isinstance(run_date, str):
run_date = datetime.fromisoformat(run_date)
else:
run_date = datetime.now() + timedelta(seconds=10)
except Exception:
run_date = datetime.now() + timedelta(seconds=10)
scheduler.add_job(
task_function,
"date",
run_date=task.execution_date,
run_date=run_date,
id=str(task_id),
replace_existing=True
)
log(f"Task {task.task_name} added with date {task.execution_date}")
log(f"Task {db_task.task_name} added/updated with date {run_date}")
else:
log(f"Invalid trigger type: {trigger}")
log(f"Invalid trigger type for task id={task_id}: {trigger}")
except Exception as e:
log(f"Failed to schedule task id={task_id}: {e}")
# 管理者任务
# 任务管理者任务
def manager_task(scheduler: BlockingScheduler):
load_tasks(scheduler)