@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 字段)
| 位置 | 字段 | 取值范围 | 示例 |
|---|---|---|---|
| 1 | 秒 | 0–59 | 0, */5 |
| 2 | 分 | 0–59 | 30, * |
| 3 | 时 | 0–23 | 8, */2 |
| 4 | 日 | 1–31 | 1, * |
| 5 | 月 | 1–12 | *, 6 |
| 6 | 周 | 0–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"] | String | Cron 表达式,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}")
}
}
}