scheduled-jobs
SKILL.md
定时任务开发指南
通用模板。如果项目有专属技能(如
leniu-java-task),优先使用。
设计原则
- 幂等性:任务重复执行不会产生副作用。考虑任务被重试、多次调度的情况。
- 可观测:任务执行结果必须可追踪(日志、监控、告警)。
- 故障隔离:单个任务失败不应影响其他任务的调度。
- 超时控制:设置合理的执行超时时间,避免任务无限阻塞。
方案对比
| 维度 | @Scheduled | Quartz | XXL-Job | 其他(SnailJob 等) |
|---|---|---|---|---|
| 学习成本 | 极低 | 中等 | 低 | 低-中 |
| 分布式支持 | 无 | 支持(数据库锁) | 原生支持 | 原生支持 |
| 可视化管理 | 无 | 无(需自建) | Web 控制台 | Web 控制台 |
| 失败重试 | 无 | 支持 | 支持 | 支持 |
| 任务分片 | 不支持 | 不支持 | 支持 | 支持 |
| 广播模式 | 不支持 | 不支持 | 支持 | 支持 |
| 工作流编排 | 不支持 | 不支持 | 子任务依赖 | 部分支持 |
| 依赖 | Spring 内置 | quartz jar | 独立服务 | 独立服务 |
| 适用场景 | 简单周期任务 | 中等复杂度 | 生产级分布式 | 生产级分布式 |
选型决策树
需要分布式调度?
├── 否 → @Scheduled(简单定时)
└── 是 → 需要可视化管理?
├── 否 → Quartz + 数据库(中等规模)
└── 是 → XXL-Job / SnailJob / PowerJob(生产推荐)
实现模式
方案一:@Scheduled(Spring 内置)
@Component
@EnableScheduling
public class SimpleScheduledTasks {
// 固定频率:每30秒执行一次
@Scheduled(fixedRate = 30000)
public void heartbeat() {
log.info("心跳检测");
}
// 固定延迟:上次执行完成后等10秒再执行
@Scheduled(fixedDelay = 10000)
public void cleanTempFiles() {
log.info("清理临时文件");
}
// Cron 表达式:每天凌晨2点执行
@Scheduled(cron = "0 0 2 * * ?")
public void dailyReport() {
log.info("生成日报");
}
// 使用配置文件中的 Cron
@Scheduled(cron = "${task.cleanup.cron:0 0 3 * * ?}")
public void configuredTask() {
log.info("可配置的定时任务");
}
}
注意事项:
- 默认单线程执行,需配置线程池避免任务阻塞
- 多实例部署时每个实例都会执行,需分布式锁控制
@Configuration
public class SchedulingConfig implements SchedulingConfigurer {
@Override
public void configureTasks(ScheduledTaskRegistrar registrar) {
registrar.setScheduler(Executors.newScheduledThreadPool(5));
}
}
方案二:Quartz
// 1. 定义 Job
public class OrderCleanupJob implements Job {
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
JobDataMap dataMap = context.getMergedJobDataMap();
int days = dataMap.getInt("retentionDays");
// 业务逻辑...
}
}
// 2. 配置调度
@Configuration
public class QuartzConfig {
@Bean
public JobDetail orderCleanupJobDetail() {
return JobBuilder.newJob(OrderCleanupJob.class)
.withIdentity("orderCleanup", "maintenance")
.usingJobData("retentionDays", 30)
.storeDurably()
.build();
}
@Bean
public Trigger orderCleanupTrigger() {
return TriggerBuilder.newTrigger()
.forJob(orderCleanupJobDetail())
.withIdentity("orderCleanupTrigger", "maintenance")
.withSchedule(CronScheduleBuilder.cronSchedule("0 0 2 * * ?"))
.build();
}
}
方案三:分布式任务调度平台(XXL-Job 等)
// 通用模式:注解标记任务处理器
@Component
public class OrderCleanupJobHandler {
@Autowired
private OrderService orderService;
// 具体注解取决于所选平台
// XXL-Job: @XxlJob("orderCleanupHandler")
// SnailJob: @JobExecutor(name = "orderCleanupJob")
public void execute(String params) {
log.info("开始清理过期订单");
try {
int days = StringUtils.isBlank(params) ? 30 : Integer.parseInt(params);
int count = orderService.cleanupExpiredOrders(days);
log.info("清理完成,删除 {} 条", count);
} catch (Exception e) {
log.error("清理失败: {}", e.getMessage());
throw e; // 抛出异常触发平台重试
}
}
}
执行模式
| 模式 | 特点 | 适用场景 |
|---|---|---|
| 集群/随机 | 多节点竞争,只有一个执行 | 订单处理、数据汇总 |
| 广播 | 所有节点都执行 | 清理本地缓存、刷新配置 |
| 分片 | 按规则分片,每节点处理部分数据 | 大数据量批处理 |
| MapReduce | 动态分片 + 结果汇总 | 分布式计算、统计 |
最佳实践
标准任务模板
@Component
public class [你的任务类] {
private static final Logger log = LoggerFactory.getLogger([你的任务类].class);
@Autowired
private [你的业务Service] bizService;
public void execute(String params) {
long startTime = System.currentTimeMillis();
log.info("[任务开始] 参数: {}", params);
try {
// 1. 解析参数
int days = parseParams(params);
// 2. 幂等检查
if (bizService.isAlreadyProcessed(today())) {
log.info("[任务跳过] 今日已执行");
return;
}
// 3. 执行业务逻辑
int count = bizService.process(days);
// 4. 记录结果
long cost = System.currentTimeMillis() - startTime;
log.info("[任务完成] 处理 {} 条,耗时 {} ms", count, cost);
} catch (Exception e) {
log.error("[任务失败] {}", e.getMessage(), e);
throw e; // 抛出异常触发重试
}
}
}
重试策略对比
| 策略 | 说明 | 适用场景 |
|---|---|---|
| 固定间隔 | 每次间隔相同(如 30s) | 网络抖动、临时故障 |
| 指数退避 | 间隔逐倍增加(1s, 2s, 4s, 8s...) | 服务恢复中 |
| Cron 重试 | 按 Cron 表达式重试 | 定点重试 |
| 最大次数 | 限制最大重试次数 | 防止无限重试 |
Cron 表达式速查
| 表达式 | 含义 |
|---|---|
0 0 2 * * ? |
每天凌晨 2:00 |
0 0/30 * * * ? |
每 30 分钟 |
0 0 9-18 * * MON-FRI |
工作日 9-18 点每小时 |
0 0 0 1 * ? |
每月 1 号零点 |
0 0 0 L * ? |
每月最后一天零点 |
常见错误
// 1. @Scheduled 任务中抛出异常但不处理
@Scheduled(fixedRate = 60000)
public void task() {
service.process(); // 异常后任务停止调度!
}
// 应捕获异常
@Scheduled(fixedRate = 60000)
public void task() {
try {
service.process();
} catch (Exception e) {
log.error("任务执行失败", e);
}
}
// 2. @Scheduled 默认单线程,长任务阻塞其他任务
// 应配置线程池(见上文 SchedulingConfig)
// 3. 多实例部署未做互斥
// @Scheduled 每个实例都执行 -> 数据重复处理
// 应使用分布式锁(Redis / 数据库)或分布式调度平台
// 4. 任务非幂等
public void syncData() {
insertAll(fetchData()); // 重复执行会插入重复数据
}
// 应先检查是否已处理
// 5. 任务内开启大事务
@Transactional
public void processAllOrders() {
// 处理百万条数据在一个事务中 -> 内存溢出、锁超时
}
// 应分批处理,每批独立事务
Weekly Installs
7
Repository
xu-cell/ai-engi…ing-initGitHub Stars
9
First Seen
9 days ago
Security Audits
Installed on
github-copilot7
codex7
kimi-cli7
gemini-cli7
cursor7
amp7