动态定时任务
Coco 调度(coco-scheduling)在 Spring TaskScheduler 之上提供两种能力:一是用 @CocoScheduled 声明方法级定时任务,二是通过 CocoTaskScheduler 编程式地在运行时动态注册、替换、取消任务。相比原生 @Scheduled,它额外提供稳定任务名、重叠执行策略、执行结果观测和统一的关闭生命周期管理。
调度绑定 coco.scheduling 命名空间。与限流、幂等、锁不同,调度默认开启(coco.scheduling.enabled 默认 true,matchIfMissing = true),不依赖 Web 运行时。
功能简介
- 注解式声明:
@CocoScheduled支持 Cron、固定延迟(fixedDelay)、固定频率(fixedRate)三种触发方式,三者必须且只能指定一个。 - 动态本地任务调度:通过
CocoTaskScheduler/CocoTaskRegistry在运行时register、replace、cancel任务,并查询状态。任务在本地调度器上执行(本地,不含跨实例分片)。 - 重叠执行策略:
CocoTaskOverlapPolicy控制上一次执行未结束时新触发的行为,默认SKIP(跳过)。 - 执行观测:
CocoTaskExecutionObserverSPI 接收任务开始、成功、失败、跳过等事件,事件只携带治理元数据,不含业务参数或异常文本。 - 稳定任务名与状态快照:每个任务有稳定名称,可查询最近一次执行结果、耗时、TraceId 等状态。
- 可替换组件:
TaskScheduler、CocoTaskExecutionGuard、CocoTaskScheduler均可由业务方提供 Bean 覆盖。
如何启用接入
调度默认开启,通常无需额外配置即可使用。如需关闭,可设置 coco.scheduling.enabled=false 或通过特性开关禁用 scheduling。
1. (可选)调整调度配置
coco:
scheduling:
enabled: true
pool-size: 4
thread-name-prefix: "coco-scheduling-"
shutdown:
await-termination: 30s
interrupt: false
2. 用注解声明任务
@Component
public class ReportJobs {
@CocoScheduled(name = "daily-report", cron = "0 0 2 * * *", zone = "Asia/Shanghai")
public void dailyReport() {
// 每天 02:00(上海时区)执行
}
@CocoScheduled(fixedDelay = "10s", initialDelay = "5s",
overlapPolicy = CocoTaskOverlapPolicy.SKIP)
public void pollQueue() {
// 上次执行结束后再等 10 秒触发;重叠则跳过
}
}
未显式指定 name 时使用 beanName#methodName 作为稳定名。fixedDelay、fixedRate、initialDelay 使用 Spring DurationStyle 文本,如 10s 或 PT10S。enabled = false 时任务不注册到底层调度器。
3. 编程式动态注册
@Service
public class TaskAdminService {
private final CocoTaskScheduler scheduler;
public TaskAdminService(CocoTaskScheduler scheduler) {
this.scheduler = scheduler;
}
public void scheduleCleanup() {
CocoTaskDefinition definition = CocoTaskDefinition
.builder("temp-cleanup", () -> cleanupTempFiles())
.fixedRate(Duration.ofMinutes(5))
.initialDelay(Duration.ofSeconds(30))
.overlapPolicy(CocoTaskOverlapPolicy.SKIP)
.build();
this.scheduler.register(definition); // 同名已存在则拒绝
}
public void reschedule(CocoTaskDefinition definition) {
this.scheduler.replace(definition); // 原子替换并取消旧 future
}
public boolean stop(String name) {
return this.scheduler.cancel(name); // 取消并移除
}
public List<CocoTaskStatus> overview() {
return this.scheduler.list(); // 全部任务状态快照
}
}
一个任务定义只能声明 Cron、固定延迟或固定频率中的一种,注册时严格校验。register 遇同名任务拒绝;replace 原子替换并取消旧任务的 future。
使用示例
观测任务执行
实现 CocoTaskExecutionObserver 并注册为 Bean 即可接收执行事件:
@Component
public class LoggingTaskObserver implements CocoTaskExecutionObserver {
@Override
public void onExecution(CocoTaskExecutionEvent event) {
// event.taskName() / outcome() / duration() / traceId() / failureType()
log.info("task={} outcome={} durationMs={} traceId={}",
event.taskName(), event.outcome(),
event.duration().toMillis(), event.traceId());
}
}
观察器抛出的异常会被调度器隔离,不会终止后续任务调度。CocoTaskExecutionOutcome 取值:NONE、STARTED、SUCCEEDED、FAILED、SKIPPED。
重叠策略
| 策略 | 行为 |
|---|---|
SKIP(默认) | 上一次执行尚未结束时,跳过新的触发,并产生 SKIPPED 事件。 |
ALLOW | 允许同名任务的多个执行并发重叠。 |
SKIP 由 CocoTaskExecutionGuard 保证。默认实现是进程内互斥,只在单实例内跳过重叠;如需跨实例的"同一时刻只有一个实例执行",业务方可将 guard 替换为基于分布式锁的适配实现。
关键配置项
前缀 coco.scheduling。
| 配置项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
enabled | boolean | true | 是否启用调度模块(matchIfMissing = true,默认开启)。 |
pool-size | int | 1 | 默认 ThreadPoolTaskScheduler 线程池大小。 |
thread-name-prefix | string | coco-scheduling- | 调度线程名前缀。 |
shutdown.await-termination | Duration | 30s | 关闭时等待在执行任务完成的最长时间。 |
shutdown.interrupt | boolean | false | 关闭时是否中断正在执行的任务(false 表示等待其完成)。 |
@CocoScheduled 注解属性:name、cron、fixedDelay、fixedRate、zone、initialDelay、overlapPolicy(默认 SKIP)、enabled(默认 true)。
边界注意事项
- 本地调度,非集群分片:任务在本地
TaskScheduler上运行。多实例部署时每个实例都会各自触发同名任务;需要"全局单次执行"时,请把CocoTaskExecutionGuard替换为分布式实现,或结合分布式锁自行控制。 - 三选一触发方式:Cron、固定延迟、固定频率必须且只能指定其一,无论注解还是
CocoTaskDefinition,违反约束会在注册/校验时报错。 - 默认 SKIP 只在进程内生效:默认 guard 是进程内互斥,无法阻止不同实例的重叠。
- 注册名唯一:
register遇同名任务拒绝;替换请用replace。 - 事件不含业务数据:执行事件与状态快照刻意不携带方法参数、Runnable、异常消息,仅暴露治理所需元数据,避免通过治理接口泄露业务数据。
- 关闭行为受配置影响:
shutdown.interrupt=false时会等待在执行任务至多await-termination;设置为true会尝试中断,长任务需自行处理中断响应。