跳到主要内容

分布式任务调度设计

问题

如何设计一个分布式任务调度系统?

答案

方案对比

方案原理优缺点
Spring @Scheduled单机 Cron简单,但不支持集群、不可视化
Quartz数据库锁实现集群重量级,配置复杂
XXL-Job调度中心 + 执行器可视化、集群、推荐
ElasticJobZooKeeper 协调弹性扩容、分片

XXL-Job 架构

任务幂等设计

分布式任务幂等执行
@XxlJob("dailyStatisticsJob")
public void dailyStatistics() {
String date = LocalDate.now().minusDays(1).toString();
String lockKey = "job:daily_stats:" + date;

// 分布式锁保证只执行一次
Boolean locked = redisTemplate.opsForValue()
.setIfAbsent(lockKey, "1", 2, TimeUnit.HOURS);
if (!Boolean.TRUE.equals(locked)) {
log.info("任务已被其他节点执行, date={}", date);
return;
}

try {
// 执行统计逻辑
statisticsService.calculate(date);
} catch (Exception e) {
redisTemplate.delete(lockKey); // 执行失败释放锁,允许重试
throw e;
}
}

分片任务

大数据量分片处理
@XxlJob("batchProcessJob")
public void batchProcess() {
int shardIndex = XxlJobHelper.getShardIndex(); // 当前分片序号
int shardTotal = XxlJobHelper.getShardTotal(); // 总分片数

// 每个执行器处理自己分片的数据
List<Order> orders = orderMapper.selectByMod(shardIndex, shardTotal);
// SQL: SELECT * FROM orders WHERE id % #{shardTotal} = #{shardIndex}

for (Order order : orders) {
processOrder(order);
}
}

常见面试问题

Q1: 如何防止任务重复执行?

答案

  • 调度中心保证同一任务同一时间只调度一次
  • 执行器端通过分布式锁做幂等
  • 数据库唯一索引兜底

Q2: 任务执行失败怎么办?

答案

  • 重试策略:XXL-Job 支持配置重试次数
  • 告警通知:失败后邮件/钉钉告警
  • 补偿机制:人工触发重新执行

Q3: 分片任务的好处?

答案

将大任务拆分给多个执行器并行处理,提升吞吐量。执行器扩缩容时自动重新分配分片。

相关链接