Skip to content

@Scheduled 定时任务

@Scheduled 宏将 @Service 方法注册为定时任务。框架提供两种触发模式:固定延迟(毫秒数)Cron 表达式AceApplication.start() 时自动启动所有调度器,应用关闭时自动停止。

固定周期模式

@Scheduled[ms] 采用 fixedDelay 语义:上一次执行结束后等待指定毫秒再触发下一次,而非固定速率(fixedRate)。这意味着若任务本身耗时,实际间隔 = 执行耗时 + 等待时间。

cangjie
package demo.service

import ace.framework.*

@Service
class CleanupService {

    // 每 30 秒清理一次过期数据
    @Scheduled[30000]
    func cleanExpiredSessions(): Unit {
        println("[Scheduler] 清理过期 Session ...")
        // sessionStore.deleteExpired()
    }

    // 每 5 分钟刷新一次本地缓存
    @Scheduled[300000]
    func refreshLocalCache(): Unit {
        println("[Scheduler] 刷新本地缓存 ...")
        // cacheManager.refresh()
    }
}

Cron 表达式模式

@Scheduled["cron"] 支持 6 字段(含秒)和标准 5 字段两种格式。

字段说明(6 字段)

位置字段取值范围示例
10–590, */5
20–5930, *
30–238, */2
41–311, *
51–12*, 6
60–7(0 和 7 均为周日)1-5, *

常用 Cron 表达式

表达式含义
"0 * * * *"每分钟整点(5 字段)
"*/5 * * * * *"每 5 秒(6 字段)
"0 0 0 * * *"每天凌晨 00:00:00
"0 30 8 * * 1-5"工作日早 8:30
"0 0 12 1 * *"每月 1 日中午 12 点
cangjie
package demo.service

import ace.framework.*

@Service
class ReportService {

    // 每天凌晨 2 点生成日报
    @Scheduled["0 0 2 * * *"]
    func generateDailyReport(): Unit {
        println("[Scheduler] 生成日报 ...")
        // reportGenerator.run()
    }

    // 工作日早 9 点发送提醒
    @Scheduled["0 0 9 * * 1-5"]
    func sendMorningReminder(): Unit {
        println("[Scheduler] 发送晨间提醒 ...")
    }

    // 每 30 秒健康检查
    @Scheduled["*/30 * * * * *"]
    func healthCheck(): Unit {
        // upstreamChecker.ping()
    }
}

手动控制调度器

大多数情况下无需手动操作,AceApplication 会自动管理生命周期。若需精细控制:

cangjie
import ace.framework.scheduler.*

// 启动所有已注册的定时任务
startScheduler()

// 停止所有定时任务(优雅等待当前执行完成)
stopScheduler()

API 说明

宏参数类型语义
@Scheduled[ms]Int64(毫秒)fixedDelay:上次结束后等待 ms 毫秒再执行
@Scheduled["cron"]StringCron 表达式,5 字段或 6 字段均可

注意事项

方法必须无参

@Scheduled 标注的方法不能有任何参数。若需要依赖外部数据,通过 @Inject 注入其他 Service 或从配置读取。

fixedDelay vs fixedRate

ACE 当前只支持 fixedDelay(上次结束后计时),不支持 fixedRate(从上次开始计时)。这样可以避免任务堆积——若单次耗时超过间隔,下一次任务不会抢跑。如需更精确的速率控制,请使用 Cron 表达式。

集群环境

多实例部署时,所有节点都会独立触发定时任务,可能造成重复执行。若需分布式单次触发,请配合分布式锁(如 Redis 锁)在方法体内加保护,或仅在单节点启用调度。

配合 @Log 记录执行日志

cangjie
@Service
class DataSyncService {
    @Log
    var logger!: Logger

    @Scheduled["0 */10 * * * *"]  // 每 10 分钟
    func syncData(): Unit {
        logger.info("开始数据同步")
        try {
            // syncEngine.run()
            logger.info("数据同步完成")
        } catch (e: Exception) {
            logger.error("数据同步失败: ${e.message}")
        }
    }
}

基于 Apache-2.0 许可证发布