You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现带异常捕获的重试机制?解决定时任务成功后重复执行问题

解决方案

核心思路是通过当日执行状态标记来控制任务是否继续执行,结合缓存或数据库实现当日有效状态,避免重复执行。以下是具体实现方案:

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);
    }
}

关键修正说明

  1. 修复原代码逻辑错误:将valueRepository.save(value)移至try块内,避免失败时保存无效值(原代码的0)。
  2. 状态标记机制:通过缓存/数据库实现当日有效执行状态,成功后标记,后续任务直接跳过。
  3. 异常处理修正:补全catch块的语法错误,明确捕获Exception并正确打印日志。

内容的提问来源于stack exchange,提问作者exponents

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 10:20:28