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