跳到内容

Scheduler Starter

受众:开发者 摘要:统一任务调度 Facade。db-scheduler / spring-scheduled / redis / memory 多种 provider,执行日志、暂停恢复、手动触发、Cron 校验、失败重试、后台运维入口。

何时使用

场景建议
一次性延迟任务(订单超时关闭、限时优惠)Scheduler.once(taskName, instanceId, fireAt)
固定间隔周期任务(每 5 分钟同步)Scheduler.fixedDelay(taskName, instanceId, interval)
Cron 表达式任务(每日 02:00)Scheduler.cron(taskName, instanceId, "0 0 2 * * *")
多实例可靠任务默认 db-scheduler provider
轻量延迟 / 补偿任务(已有 Redis)切到 redis provider
单元测试 / 单进程诊断 / lite 兼容模式切到 spring-scheduled provider

不要把高频毫秒级任务塞进来(用 Quartz 或专门方案);不要把业务事件同步处理塞进来(用 Event Facade)。

快速开始

yaml
spring:
  open:
    scheduler:
      default-provider: db-scheduler
java
@Component
public class OrderTimeoutCloseTask implements SchedulerTask {

    @Override
    public String name() {
        return "order.timeout.close";
    }

    @Override
    public String description() {
        return "订单超时未支付时自动关闭订单";
    }

    @Override
    public void execute(SchedulerTaskContext context) {
        // 业务 Service 调用
    }
}
java
Scheduler.once("order.timeout.close", "order-10001",
    Instant.now().plus(Duration.ofMinutes(30)));

SchedulerTask 注册为 Spring Bean 后自动进入任务注册表。description() 是可选说明,默认留空;它只用于后台任务列表帮助运维识别任务用途,不会写入 scheduled_tasks 这类 provider 执行状态表。

Facade API

java
// 一次性延迟任务
Scheduler.once(taskName, instanceId, fireAt);

// 固定间隔
Scheduler.fixedDelay(taskName, instanceId, interval);
Scheduler.fixedDelay(taskName, instanceId, initialDelay, interval);

// Cron 表达式
Scheduler.cron(taskName, instanceId, cron);

// 暂停 / 恢复(运行时控制)
Scheduler.pause();
Scheduler.resume();

// 手动触发 / 取消单个实例
Scheduler.trigger(taskName, instanceId);
Scheduler.cancel(taskName, instanceId);

taskName 必须与 SchedulerTask.name() 一致。instanceId 区分同一任务的不同实例(订单号、交易号、业务 ID)。

源码机制骨架

Scheduler starter 的运行链路按 Facade、Manager、Registry、Provider、Execution Log 分层:

  • Scheduler 是业务侧 Facade。静态方法只取当前 Spring 容器绑定的 SchedulerManager,不直接读取配置、不直接触碰 db-scheduler / Redis / TaskScheduler。
  • SchedulerManagerProvider 在自动配置完成后把 SchedulerManager 绑定到 Facade;测试或上下文销毁时负责清理绑定。
  • DefaultSchedulerManager 负责校验 SchedulerTask 是否已注册,并按 spring.open.scheduler.default-provider 选择 provider;单次调度不覆盖 provider,便于环境级统一切换调度后端。
  • DefaultSchedulerTaskRegistry 收集所有 Spring Bean 形式的 SchedulerTask,按 SchedulerTask.name() 建立索引;重复名称会在注册期失败。任务可选的 description() 会作为注册元数据提供给后台运维视图。
  • ScheduleRequest 是 provider 共享的标准请求模型,统一承载 once / fixedDelay / cron 三类调度请求,并补齐空 instanceId、默认时区和一次性任务默认触发时间。
  • ScheduledTask 是提交快照,只表示任务已交给 provider;是否执行成功以执行日志和 provider 状态为准。
  • SchedulerTaskContext 是业务任务执行时看到的调度事实,包含任务名、实例 ID、调度类型、计划时间、实际执行时间、provider 和 traceId。
  • SpringScheduledSchedulerProvider 基于 Spring TaskScheduler 执行动态调度任务,适合单实例和 lite 模式;它不是静态 @Scheduled 注解扫描器,也不提供任务持久化或多实例协调。
  • MemorySchedulerProvider 直接用 Spring TaskScheduler 触发任务,不持久化任务实例,主要用于本地测试和极简单实例场景。
  • DbSchedulerProvider 把请求转换为 db-scheduler 的 one-time / recurring task instance;DbSchedulerTaskDispatcher 在触发时再按 taskName 找到业务 SchedulerTask 并记录执行日志。
  • RedisSchedulerProvider 用 Hash 保存任务元数据、ZSet 保存到期索引、短期锁避免多实例重复执行;失败后按 retry 配置重排一次性任务,重复任务按下一次计划继续调度。
  • SchedulerExecutionRecorder 是所有 provider 执行任务前后的统一包裹层,负责 traceId 绑定、开始 / 完成日志和异常透传。
  • JdbcSchedulerExecutionLogStore 写入 scheduled_task_execution_log;表名只允许配置为数据库标识符,具体值使用参数绑定。
  • SchedulerExecutionLogCleanupRegistrar 把执行日志清理注册成一个普通 Cron 调度任务,因此清理逻辑同样走当前默认 provider。
  • spring-open-core-scheduler 汇总 provider 状态、任务指标、misfire 策略、Cron 校验、最近执行日志和运行时操作入口;Scheduler starter 本身只保留调度 contract。

配置项

yaml
spring:
  open:
    scheduler:
      enabled: true                       # 启用开关
      default-provider: db-scheduler       # 默认 provider
      providers:
        spring-scheduled:
          provider: spring-scheduled
          pool-size: 4
          thread-name-prefix: spring-open-scheduled-
        db-scheduler:
          provider: db-scheduler
          one-time-task-name: spring-open-scheduler-once
          recurring-task-name: spring-open-scheduler-recurring
          retry:
            max-retries: 0                # 0 = 不限次数
            interval: 5m
        redis:
          provider: redis
          enabled: true
          auto-startup: false
          key-prefix: scheduler:redis
          scan-interval: 1s
          batch-size: 50
          lock-ttl: 5m
        memory:
          provider: memory
          pool-size: 4
      execution-log:
        enabled: true
        table-name: scheduled_task_execution_log
        cleanup:
          enabled: true
          retention: 30d
          cron: "0 0 3 * * *"
配置默认值说明
enabledtrue关闭后整个 AutoConfiguration 跳过
default-providerdb-scheduler默认调度后端
providers.<name>.provider-真实 provider
execution-log.enabledtrue是否写执行日志
execution-log.cleanup.retention30d日志保留天数
execution-log.cleanup.cron0 0 3 * * *清理任务 Cron

开发期不保留旧配置兼容;Scheduler 统一使用 default-provider / providers.*

Provider 扩展

Provider 配置Provider说明适用
db-schedulerdb-scheduler基于数据库持久化调度默认,多实例、可靠任务
spring-scheduledspring-scheduled基于 Spring TaskScheduler 的动态本地调度单实例诊断、lite 兼容模式
redisredisRedis ZSet 轻量延迟调度轻量补偿、短延迟
memorymemory本机内存测试、极简单实例

lite 兼容模式自动切换为 spring-scheduled;普通开发不推荐用 lite 绕开默认调度基础设施。

scheduled_tasks.task_data 是 db-scheduler 内部序列化任务数据,MySQL/MariaDB 用 blob,PostgreSQL 用 bytea,不要改成 text

执行日志

调度执行日志默认写入 scheduled_task_execution_log,记录:

  • 任务开始时间、成功 / 失败、耗时
  • TraceId 链路追踪
  • 错误摘要

自动清理任务默认每天 03:00 清理 30 天前日志。

后台任务概览会展示最近执行日志入口和最近 7 天执行趋势。执行趋势直接从执行日志聚合出每日执行数、成功数、失败数、运行中数和平均耗时,便于先判断任务是否出现波动;执行日志仍是“任务执行事实”,不替代业务审计或业务状态表,业务补偿、账务、审批等长期事实应留在各自业务模块。

后台运维 API

spring-open-core-scheduler 提供调度运维入口(权限:scheduler:*):

API说明
GET /api/v1/admin/scheduler/status调度状态
GET /api/v1/admin/scheduler/operations调度运维快照、能力清单和 runbook 提示
GET /api/v1/admin/scheduler/tasks任务列表(关键字 / 状态 / 失败筛选 / 排序)
GET /api/v1/admin/scheduler/tasks/failures失败任务聚合
GET /api/v1/admin/scheduler/tasks/{taskName}/metrics单任务指标(成功率 / 失败率 / 平均耗时)
POST /api/v1/admin/scheduler/cron/validateCron 表达式校验并预览后续触发时间
POST /api/v1/admin/scheduler/pause暂停调度
POST /api/v1/admin/scheduler/resume恢复调度
POST /api/v1/admin/scheduler/tasks/{taskName}/instances/{instanceId}/trigger手动触发
POST /api/v1/admin/scheduler/tasks/{taskName}/instances/{instanceId}/stop停止任务实例(运维语义,底层复用取消能力)
DELETE /api/v1/admin/scheduler/tasks/{taskName}/instances/{instanceId}取消任务
GET /api/v1/admin/scheduler/execution-logs执行日志分页
GET /api/v1/admin/scheduler/execution-logs/trends执行日志每日趋势(默认最近 7 天,最长 90 天)
POST /api/v1/admin/scheduler/execution-logs/{id}/retry基于失败日志重试(仅 FAILED 状态)
POST /api/v1/admin/scheduler/execution-logs/cleanup清理旧日志

后台页面在有副作用的暂停、恢复、手动触发和停止操作前会做二次确认。任务列表会在任务名下展示可选 description,便于区分业务用途;列表里的 misfirePolicy 是 provider 暴露的执行策略摘要,当前 provider 未提供细分策略时显示 provider-default

GET /api/v1/admin/scheduler/operations 是只读运维快照,汇总默认 provider、暂停状态、注册任务数、失败任务数、运行中执行数、执行日志可用性、成功率、平均耗时、能力清单和 runbook 提示。它不会暂停、恢复、触发或停止任何任务,适合发布前 smoke、故障排查和确认业务任务是否已经被 Scheduler 注册表发现。若快照提示没有注册任务,应优先检查 SchedulerTask Bean 是否被扫描,以及业务 registrar 是否在运行期成功调用 SchedulerManager

扩展边界

  • 新增底层调度介质时实现 SchedulerProvider,并在自动配置中注册 provider Bean;业务代码仍只依赖 Scheduler / SchedulerTask / SchedulerManager
  • 新增业务任务时实现 SchedulerTask,任务名保持应用内唯一,业务参数通过 instanceId 指向业务对象,不在调度数据里塞 Entity、连接、文件句柄或临时上下文。
  • ScheduleRequest 只承载调度事实,不承载业务审批、订单状态、工作流节点或权限判断。
  • MemorySchedulerProvider 不做持久化,不能作为多实例可靠调度;生产多实例优先 db-scheduler
  • RedisSchedulerProvider 适合轻量延迟和补偿,不作为长期事实数据源;复杂死信、批量恢复、跨区域灾备仍应走专用队列 / 调度方案。
  • 执行日志只记录任务执行事实和错误摘要,不替代业务审计、账务流水、幂等表或操作日志。
  • spring-open-core-scheduler 负责后台运维 API 和执行日志持久化运维入口;Scheduler starter 只提供底层调度 contract,不反向依赖运行时底座模块。

注意事项

  • 多实例部署优先 db-scheduler
  • 生产修改 Cron 前先使用后台 Cron 校验预览触发时间,再保存业务配置或重新注册任务
  • Redis provider 适合轻量延迟任务,不作为长期事实数据源
  • 失败任务通过执行日志 + 手动重试入口处理,不在业务任务中重复实现
  • 应用启动期 db-scheduler 配置 always-persist-timestamp-in-utc=true 避免 MySQL TIMESTAMP 跨时区偏差
  • Scheduler starter 自身不依赖 MyBatis-Plus;只有 spring-open-core-scheduler 存在时才使用 MyBatis-Plus 执行日志 store

验证命令

bash
./mvnw -pl spring-open-starters/spring-open-starter-scheduler -am test -DskipITs

# 真实基础设施集成测试(MySQL + PostgreSQL + Redis Testcontainers)
./mvnw -pl spring-open-starters/spring-open-starter-scheduler -am verify -DskipITs=false

相关

Released under the Apache License 2.0.