Files
trumandu--myboot/docs/scheduler_refactor_analysis.md
2026-07-13 12:34:57 +08:00

11 KiB
Raw Permalink Blame History

调度器重构可行性分析:使用 APScheduler

使用说明:日常开发与配置 Cron/间隔任务请参阅 scheduler.md

1. 当前实现分析

1.1 现有功能

  • 任务类型支持

    • Cron 任务(6位表达式:秒 分 时 日 月 周)
    • 间隔任务(interval,支持秒/分/小时)
    • 一次性任务(date,指定日期时间执行)
  • API 接口

    • add_cron_job(func, cron, job_id, **kwargs)
    • add_interval_job(func, interval, job_id, **kwargs)
    • add_date_job(func, run_date, job_id, **kwargs)
    • remove_job(job_id)
    • get_job(job_id)
    • list_jobs()
    • start() / stop()
    • add_scheduled_job(job: ScheduledJob)
  • 集成点

    • Application 生命周期集成(启动/停止)
    • 自动配置系统自动注册装饰器标记的任务
    • 支持 ScheduledJob 类对象
    • 配置支持(时区、最大工作线程数、启用/禁用)

1.2 当前实现的问题

  • 自定义实现,维护成本高
  • Cron 表达式解析逻辑复杂,可能存在边界情况
  • 时区处理需要手动实现(依赖 pytz
  • 任务执行状态跟踪有限
  • 缺少任务持久化能力
  • 错误处理和重试机制简单

2. APScheduler 能力分析

2.1 APScheduler 核心特性

  • 触发器(Triggers

    • CronTrigger:完整的 Cron 表达式支持(5位或6位)
    • IntervalTrigger:固定间隔执行
    • DateTrigger:指定日期时间执行
  • 执行器(Executors

    • ThreadPoolExecutor:线程池执行
    • ProcessPoolExecutor:进程池执行
    • AsyncIOExecutor:异步执行
    • GeventExecutorGevent 协程执行
  • 任务存储(Job Stores

    • MemoryJobStore:内存存储(默认)
    • SQLAlchemyJobStore:数据库持久化
    • RedisJobStoreRedis 持久化
    • MongoDBJobStoreMongoDB 持久化
  • 调度器(Schedulers

    • BlockingScheduler:阻塞式调度器
    • BackgroundScheduler:后台线程调度器(推荐)
    • AsyncIOScheduler:异步调度器
    • GeventSchedulerGevent 调度器
    • TornadoSchedulerTornado 调度器
    • TwistedSchedulerTwisted 调度器

2.2 APScheduler 优势

  • 成熟稳定,社区活跃
  • 完整的 Cron 表达式支持(包括复杂表达式)
  • 内置时区支持(无需手动处理)
  • 任务持久化能力
  • 丰富的任务状态和事件监听
  • 更好的错误处理和日志
  • 支持任务暂停/恢复
  • 支持任务修改(修改触发器)

3. 功能映射关系

3.1 任务类型映射

当前实现 APScheduler 映射方式
add_cron_job(func, cron, ...) scheduler.add_job(func, CronTrigger.from_crontab(cron), ...) 直接映射
add_interval_job(func, interval, ...) scheduler.add_job(func, IntervalTrigger(seconds=interval), ...) 直接映射
add_date_job(func, run_date, ...) scheduler.add_job(func, DateTrigger(run_date=...), ...) 直接映射

3.2 API 接口映射

当前方法 APScheduler 对应方法 兼容性
add_cron_job() add_job(..., trigger=CronTrigger(...)) 可封装保持兼容
add_interval_job() add_job(..., trigger=IntervalTrigger(...)) 可封装保持兼容
add_date_job() add_job(..., trigger=DateTrigger(...)) 可封装保持兼容
remove_job(job_id) remove_job(job_id) 完全兼容
get_job(job_id) get_job(job_id) 完全兼容
list_jobs() get_jobs() 可封装保持兼容
start() start() 完全兼容
stop() shutdown() ⚠️ 需要封装(方法名不同)

3.3 配置映射

当前配置 APScheduler 配置 映射方式
scheduler.enabled 通过 start() 控制 逻辑控制
scheduler.timezone timezone 参数 直接映射
scheduler.max_workers executors['default']['max_workers'] 直接映射

4. 重构方案设计

4.1 方案一:完全封装(推荐)

设计思路:保持现有 API 接口不变,内部使用 APScheduler 实现。

优点

  • 完全向后兼容,现有代码无需修改
  • 可以逐步迁移,降低风险
  • 保留自定义扩展能力

实现要点

from apscheduler.schedulers.background import BackgroundScheduler
from apscheduler.triggers.cron import CronTrigger
from apscheduler.triggers.interval import IntervalTrigger
from apscheduler.triggers.date import DateTrigger

class Scheduler:
    def __init__(self, config):
        # 创建 APScheduler 实例
        self._scheduler = BackgroundScheduler(
            timezone=config.get('scheduler.timezone', 'UTC'),
            executors={
                'default': {
                    'type': 'threadpool',
                    'max_workers': config.get('scheduler.max_workers', 10)
                }
            }
        )
        # 保持兼容性的映射
        self._jobs = {}  # job_id -> APScheduler Job
        self._scheduled_jobs = {}  # ScheduledJob 对象
    
    def add_cron_job(self, func, cron, job_id=None, **kwargs):
        # 转换 cron 表达式格式(6位 -> APScheduler 格式)
        trigger = self._parse_cron(cron)
        job = self._scheduler.add_job(
            func,
            trigger=trigger,
            id=job_id,
            **kwargs
        )
        self._jobs[job_id] = job
        return job_id

4.2 方案二:直接使用 APScheduler

设计思路:直接暴露 APScheduler 的 API,简化封装层。

优点

  • 代码更简洁
  • 可以直接使用 APScheduler 的高级特性

缺点

  • 需要修改现有代码
  • 破坏向后兼容性

4.3 推荐方案:方案一(完全封装)

理由

  1. 保持 API 兼容性,现有代码无需修改
  2. 可以逐步增强功能(如添加持久化)
  3. 保留扩展空间

5. 实施步骤

5.1 阶段一:基础重构(保持兼容)

  1. 创建新的 Scheduler 类(基于 APScheduler

    • 实现所有现有 API 方法
    • 保持方法签名和返回值一致
    • 内部使用 APScheduler
  2. Cron 表达式转换

    • 当前格式:"秒 分 时 日 月 周"6位)
    • APScheduler 格式:"分 时 日 月 周"5位)或 CronTrigger 对象
    • 需要实现转换函数
  3. 测试验证

    • 单元测试覆盖所有 API
    • 集成测试验证现有功能
    • 确保装饰器和自动配置正常工作

5.2 阶段二:功能增强(可选)

  1. 添加任务持久化

    • 支持 SQLAlchemyJobStore
    • 支持 RedisJobStore
  2. 增强任务管理

    • 任务暂停/恢复
    • 任务修改触发器
    • 任务执行历史
  3. 事件监听

    • 任务执行成功/失败事件
    • 任务错过执行事件

5.3 阶段三:优化和文档

  1. 性能优化
  2. 文档更新
  3. 示例代码更新

6. 关键技术点

6.1 Cron 表达式转换

当前格式6位):

"秒 分 时 日 月 周"
例如:"0 0 * * * *"  # 每小时

APScheduler 格式5位或 CronTrigger):

# 方式1:5位字符串(标准 Cron)
"分 时 日 月 周"
"0 * * * *"  # 每小时

# 方式2CronTrigger 对象(推荐)
CronTrigger(second=0, minute=0, hour='*', day='*', month='*', day_of_week='*')

转换函数

def _parse_cron(self, cron_expr: str) -> CronTrigger:
    """将6位 Cron 表达式转换为 APScheduler CronTrigger"""
    parts = cron_expr.split()
    if len(parts) == 6:
        second, minute, hour, day, month, weekday = parts
        return CronTrigger(
            second=second,
            minute=minute,
            hour=hour,
            day=day,
            month=month,
            day_of_week=weekday
        )
    elif len(parts) == 5:
        # 标准5位格式,秒默认为0
        return CronTrigger.from_crontab(cron_expr)
    else:
        raise ValueError(f"无效的 Cron 表达式: {cron_expr}")

6.2 ScheduledJob 集成

需要将 ScheduledJob.execute() 方法适配到 APScheduler

def add_scheduled_job(self, job: ScheduledJob, job_id: Optional[str] = None):
    # 包装 execute 方法,保持状态跟踪
    def wrapped_execute():
        return job.execute()
    
    # 根据 trigger 类型添加任务
    if isinstance(job.trigger, str):
        return self.add_cron_job(wrapped_execute, job.trigger, job_id)
    # ... 其他类型

6.3 时区处理

APScheduler 内置时区支持,无需手动处理:

from pytz import timezone

scheduler = BackgroundScheduler(
    timezone=timezone('Asia/Shanghai')  # 直接支持时区对象
)

6.4 任务状态管理

APScheduler 提供任务状态:

  • pending:等待执行
  • running:正在执行
  • completed:已完成
  • failed:执行失败

可以通过 get_job(job_id).next_run_time 获取下次执行时间。

7. 风险评估

7.1 兼容性风险

  • 风险Cron 表达式格式差异
  • 缓解:实现转换函数,确保兼容

7.2 行为差异风险

  • 风险APScheduler 的执行时机可能与当前实现略有差异
  • 缓解:充分测试,特别是边界情况

7.3 性能风险

  • 风险APScheduler 可能有额外的开销
  • 缓解:性能测试,通常 APScheduler 性能更好

8. 测试策略

8.1 单元测试

  • 所有 API 方法的测试
  • Cron 表达式转换测试
  • 任务添加/删除测试

8.2 集成测试

  • 装饰器自动注册测试
  • ScheduledJob 集成测试
  • 应用生命周期集成测试

8.3 兼容性测试

  • 现有示例代码运行测试
  • 现有项目迁移测试

9. 结论

9.1 可行性评估

高度可行

理由

  1. APScheduler 功能完全覆盖现有需求
  2. API 可以完全兼容
  3. 项目已依赖 APScheduler
  4. 重构收益明显(稳定性、功能、维护性)

9.2 推荐方案

采用方案一(完全封装),分阶段实施:

  1. 第一阶段:基础重构,保持兼容
  2. 第二阶段:功能增强(可选)
  3. 第三阶段:优化和文档

9.3 预期收益

  • 减少代码量(约 200+ 行 → 100+ 行)
  • 提高稳定性(使用成熟库)
  • 增强功能(持久化、事件监听等)
  • 降低维护成本
  • 更好的错误处理

9.4 实施建议

  1. 先创建新实现,保留旧实现作为备份
  2. 充分测试后再替换
  3. 逐步迁移,可以先支持两种实现并存
  4. 文档更新,说明新特性

10. 参考资源