如何实现带异常捕获的重试机制?解决定时任务成功后重复执行问题
解决方案
核心思路是通过当日执行状态标记来控制任务是否继续执行,结合缓存或数据库实现当日有效状态,避免重复执行。以下是具体实现方案:
1. 单实例场景:本地缓存实现(轻量高效)
使用带过期时间的本地缓存存储当日成功状态,午夜自动重置,适合单服务实例场景。
import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import java.time.LocalDate; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; @Component public class ComputeScheduler { // 本地缓存:键为当日日期,值为是否成功执行,1天后自动过期 private final LoadingCache<LocalDate, Boolean> dailySuccessCache = CacheBuilder.newBuilder() .expireAfterWrite(1, TimeUnit.DAYS) .build(new CacheLoader<>() { @Override public Boolean load(LocalDate key) { return false; // 默认当日未成功执行 } }); @Scheduled(cron = "${scheduler: */10 7-8 * * MON-FRI}") public void dailyComputeValue() { LocalDate today = LocalDate.now(); // 先检查当日是否已成功执行,避免重复操作 try { if (dailySuccessCache.get(today)) { log.info("今日已完成有效计算,跳过本次任务"); return; } } catch (ExecutionException e) { log.error("检查当日执行状态失败", e); return; } try { // 执行计算逻辑 double value = computeValue("your-product-id"); // 仅成功时保存结果 valueRepository.save(value); // 标记当日已成功,后续任务不再执行 dailySuccessCache.put(today, true); log.info("计算成功并保存,当日任务结束"); } catch (Exception ex) { log.warn("记录暂不可用,10分钟后重试", ex); } } public Double computeValue(String product) throws Exception { List<Pair<ZonedDateTime, Double>> valueRecords = new ArrayList<>(externalService.getRecord()); if (valueRecords.isEmpty()) { throw new Exception("Record not available yet"); } // 替换为实际计算逻辑 return valueRecords.stream() .map(Pair::getValue) .reduce(0.0, Double::sum); } }
2. 集群场景:数据库状态标记
如果是多实例部署,本地缓存无法共享状态,可通过数据库存储当日执行状态:
@Scheduled(cron = "${scheduler: */10 7-8 * * MON-FRI}") public void dailyComputeValue() { LocalDate today = LocalDate.now(); // 查询数据库是否已有当日成功记录 boolean hasSucceeded = dailyExecutionRecordRepository.existsByDateAndStatus(today, ExecutionStatus.SUCCESS); if (hasSucceeded) { log.info("今日已完成有效计算,跳过本次任务"); return; } try { double value = computeValue("your-product-id"); valueRepository.save(value); // 写入数据库成功记录 dailyExecutionRecordRepository.save(new DailyExecutionRecord(today, ExecutionStatus.SUCCESS)); log.info("计算成功并保存,当日任务结束"); } catch (Exception ex) { log.warn("记录暂不可用,10分钟后重试", ex); } }
关键修正说明
- 修复原代码逻辑错误:将
valueRepository.save(value)移至try块内,避免失败时保存无效值(原代码的0)。 - 状态标记机制:通过缓存/数据库实现当日有效执行状态,成功后标记,后续任务直接跳过。
- 异常处理修正:补全
catch块的语法错误,明确捕获Exception并正确打印日志。
内容的提问来源于stack exchange,提问作者exponents
相关产品推荐
相关产品推荐

