Spring Retry调用重试机制

Spring Retry 是 Spring 框架的一个模块,旨在为应用程序提供自动重试机制,以增强系统的容错性和稳定性。​它特别适用于处理由于网络波动、服务暂时不可用等原因导致的瞬时性故障。

  • 自动重试:​在指定的异常发生时,自动重新执行失败的操作。

  • 重试策略:​支持多种重试策略,如固定次数、超时限制、表达式判断等。

  • 退避策略:​提供固定延迟、指数退避、随机退避等策略,控制重试间隔。

  • 兜底处理:​通过 @Recover 注解定义在重试失败后的回调方法。

 重试AOP:FeignRetry 

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface FeignRetry {
    /**
     * 最大重试次数,默认3次
     */
    int maxAttempts() default 3;

    /**
     * 初始重试间隔时间(毫秒),默认1000ms
     */
    long delay() default 1000L;

    /**
     * 最大重试间隔时间(毫秒),默认0,表示不限制
     */
    long maxDelay() default 0L;

    /**
     * 重试间隔乘数,默认1.0(固定间隔)
     */
    double multiplier() default 1.0;

    /**
     * 需要触发重试的异常类型,默认所有异常
     */
    Class<? extends Throwable>[] include() default {Exception.class};
}

AOP 切面实现重试:FeignRetryAspect 

@Aspect
@Component
public class FeignRetryAspect {

    @Autowired
    private MongoTemplate mongoTemplate;

    private final ObjectMapper objectMapper = new ObjectMapper();


    @Around("@annotation(feignRetry)")
    public Object around(ProceedingJoinPoint pjp, FeignRetry feignRetry) throws Throwable {
        RetryTemplate retryTemplate = new RetryTemplate();

        // 配置重试策略
        Map<Class<? extends Throwable>, Boolean> retryableExceptions = Arrays.stream(feignRetry.include())
                .collect(Collectors.toMap(ex -> ex, ex -> true));
        SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy(feignRetry.maxAttempts(), retryableExceptions);
        retryTemplate.setRetryPolicy(retryPolicy);

        // 配置退避策略
        BackOffPolicy backOffPolicy;
        if (feignRetry.multiplier() > 1.0) {
            ExponentialBackOffPolicy exponentialBackOffPolicy = new ExponentialBackOffPolicy();
            exponentialBackOffPolicy.setInitialInterval(feignRetry.delay());
            exponentialBackOffPolicy.setMultiplier(feignRetry.multiplier());
            exponentialBackOffPolicy.setMaxInterval(feignRetry.maxDelay() > 0 ? feignRetry.maxDelay() : Long.MAX_VALUE);
            backOffPolicy = exponentialBackOffPolicy;
        } else {
            FixedBackOffPolicy fixedBackOffPolicy = new FixedBackOffPolicy();
            fixedBackOffPolicy.setBackOffPeriod(feignRetry.delay());
            backOffPolicy = fixedBackOffPolicy;
        }
        retryTemplate.setBackOffPolicy(backOffPolicy);

        // 执行带有重试的逻辑,并记录日志
        return retryTemplate.execute(context -> {
            try {
                return pjp.proceed();
            } catch (Throwable throwable) {
                // 记录重试日志
                RetryLog log = new RetryLog();
                log.setClassName(pjp.getTarget().getClass().getName());
                log.setMethodName(pjp.getSignature().getName());
                log.setExceptionMessage(throwable.getMessage());
                log.setAttempt(context.getRetryCount() + 1);
                log.setTimestamp(new Date());

                // 序列化方法参数为 JSON 字符串
                try {
                    String paramsJson = objectMapper.writeValueAsString(pjp.getArgs());
                    log.setParameters(paramsJson);
                } catch (Exception e) {
                    log.setParameters("参数序列化失败: " + e.getMessage());
                }

                mongoTemplate.save(log);
                throw throwable;
            }
        });
    }
}

日志实体类:RetryLog

/**
 * 重试日志
 */
@Data
@Document(collection = "retry_logs")
public class RetryLog {

    /**
     * MongoDB 文档的唯一标识,对应 MongoDB 的 "_id" 字段。
     */
    @Id
    private String id;

    /**
     * 发生重试的类的全限定名(包括包名)。
     * 例如:com.example.service.MyFeignClient
     */
    private String className;

    /**
     * 发生重试的方法名。
     * 例如:fetchData
     */
    private String methodName;

    /**
     * 异常的详细信息,用于记录触发重试的异常原因。
     */
    private String exceptionMessage;

    /**
     * 当前的重试次数,从 1 开始计数。
     * 例如:第一次重试为 1,第二次为 2,以此类推。
     */
    private int attempt;

    /**
     * 记录重试发生的时间戳。
     */
    private Date timestamp;

    /**
     * 方法参数的 JSON 字符串表示。
     */
    private String parameters;
}

 

posted @ 2025-04-17 09:16  ~落辰~  阅读(197)  评论(0)    收藏  举报