Feign 的重试调用,这样封装真香!

👉 这是一个或许对你有用的社群

🐱 一对一交流/面试小册/简历优化/求职解惑,欢迎加入「芋道快速开发平台」知识星球。下面是星球提供的部分资料: 

38582f28550209df80b2d4e8e974bef8.gif

👉这是一个或许对你有用的开源项目

国产 Star 破 10w+ 的开源项目,前端包括管理后台 + 微信小程序,后端支持单体和微服务架构。

功能涵盖 RBAC 权限、SaaS 多租户、数据权限、商城、支付、工作流、大屏报表、微信公众号、CRM 等等功能:

  • Boot 仓库:https://gitee.com/zhijiantianya/ruoyi-vue-pro

  • Cloud 仓库:https://gitee.com/zhijiantianya/yudao-cloud

  • 视频教程:https://doc.iocoder.cn

【国内首批】支持 JDK 21 + SpringBoot 3.2.2、JDK 8 + Spring Boot 2.7.18 双版本 

来源:JAVA旭阳


在我们公司里,不同的服务之间通过Feign进行远程调用,但是,我们在尝试使调用可重试时遇到了一个小问题,Feign框架本身可以配置的自己的重试机制,但是它是一刀切的方式,所有的调用都是同样的机制,没有办法像我们希望的那样在每个方法的基础上配置。不过我在项目中探索除了一种新的写法,通过spring-retry框架集合Feign去实现重试机制,可以为每个调用实现不同的重试机制,那究竟是如何做到的呢,继续往下看呀。

自定义注解@FeignRetry

为了解决上面提到的问题,让Feign调用的每个接口单独配置不同的重试机制。我们使用了面向切面编程并编写了一个自定义注解:@FeignRetry。此注释的工作方式类似于@Retryable的包装器,并与其共享相同的规范以避免混淆。

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface FeignRetry {

    Backoff backoff() default @Backoff();
    int maxAttempt() default 3;
    Class<? extends Throwable>[] include() default {};
}

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Backoff {
    long delay() default 1000L;;
    long maxDelay() default 0L;
    double multiplier() default 0.0D;;
}

FeignRetryAspect切面处理@FeignRetry注解。

Slf4j
@Aspect
@Component
public class FeignRetryAspect {

    @Around("@annotation(FeignRetry)")
    public Object retry(ProceedingJoinPoint joinPoint) throws Throwable {
        Method method = getCurrentMethod(joinPoint);
        FeignRetry feignRetry = method.getAnnotation(FeignRetry.class);

        RetryTemplate retryTemplate = new RetryTemplate();
        retryTemplate.setBackOffPolicy(prepareBackOffPolicy(feignRetry));
        retryTemplate.setRetryPolicy(prepareSimpleRetryPolicy(feignRetry));

        // 重试
        return retryTemplate.execute(arg0 -> {
            int retryCount = arg0.getRetryCount();
            log.info("Sending request method: {}, max attempt: {}, delay: {}, retryCount: {}",
                    method.getName(),
                    feignRetry.maxAttempt(),
                    feignRetry.backoff().delay(),
                    retryCount
            );
            return joinPoint.proceed(joinPoint.getArgs());
        });
    }

    private BackOffPolicy prepareBackOffPolicy(FeignRetry feignRetry) {
        if (feignRetry.backoff().multiplier() != 0) {
            ExponentialBackOffPolicy backOffPolicy = new ExponentialBackOffPolicy();
            backOffPolicy.setInitialInterval(feignRetry.backoff().delay());
            backOffPolicy.setMaxInterval(feignRetry.backoff().maxDelay());
            backOffPolicy.setMultiplier(feignRetry.backoff().multiplier());
            return backOffPolicy;
        } else {
            FixedBackOffPolicy fixedBackOffPolicy = new FixedBackOffPolicy();
            fixedBackOffPolicy.setBackOffPeriod(feignRetry.backoff().delay());
            return fixedBackOffPolicy;
        }
    }


    private SimpleRetryPolicy prepareSimpleRetryPolicy(FeignRetry feignRetry) {
        Map<Class<? extends Throwable>, Boolean> policyMap = new HashMap<>();
        policyMap.put(RetryableException.class, true);  // Connection refused or time out
        policyMap.put(ClientException.class, true);     // Load balance does not available (cause of RunTimeException)
        if (feignRetry.include().length != 0) {
            for (Class<? extends Throwable> t : feignRetry.include()) {
                policyMap.put(t, true);
            }
        }
        return new SimpleRetryPolicy(feignRetry.maxAttempt(), policyMap, true);
    }

    private Method getCurrentMethod(JoinPoint joinPoint) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        return signature.getMethod();
    }
}

捕获FeignRetry注解的方法,将配置传递给Spring RetryTemplate,根据配置调用服务。

基于 Spring Boot + MyBatis Plus + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

  • 项目地址:https://github.com/YunaiV/ruoyi-vue-pro

  • 视频教程:https://doc.iocoder.cn/video/

@FeignRetry 的使用

用法很简单,只需将注释放在我们希望重试机制处于活动状态的 Feign Client方法上即可。自定义切面的用法类似于Spring自带的@Retryable注解。

@GetMapping
@FeignRetry(maxAttempt = 3, backoff = @Backoff(delay = 500L))
ResponseEntity<String> retrieve1();

@GetMapping
@FeignRetry(maxAttempt = 6, backoff = @Backoff(delay = 500L, maxDelay = 20000L, multiplier = 4))
ResponseEntity<String> retrieve2();

另外还需要在应用程序类中使用 @EnableRetry 注释来启动重试,比如可以加载SpringBoot的启动类中。

基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

  • 项目地址:https://github.com/YunaiV/yudao-cloud

  • 视频教程:https://doc.iocoder.cn/video/

总结

Feign重试其实是一个很常见的场景,我们本文通过了自定义了一个@FeignRetry注解来实现可重试的机制,针对不同的Feign接口还可以使用不同的重试策略,是不是很方便,快在你的项目中用起来吧。

如果你有更好的方案,欢迎在评论区讨论....


欢迎加入我的知识星球,全面提升技术能力。

👉 加入方式,长按”或“扫描”下方二维码噢

042a6ab9c8535aa191ac21d109b8c021.png

星球的内容包括:项目实战、面试招聘、源码解析、学习路线。

dafef490a7fd115079ff389b65613489.png

186747f50027df14220595f7e53e1064.png7ba1cfc5949cf24aadc27def962d3549.png29c20813df19aa79eb7907adefd9b260.pngaaea231e07ac3b89a34ea6b82f63e9e2.png

文章有帮助的话,在看,转发吧。
谢谢支持哟 (*^__^*)
### Feign远程调用重试机制配置 Feign 提供了一种灵活的方式来处理远程调用中的失败情况,通过内置的 `Retryer` 接口实现自动重试功能。以下是关于如何在 Feign 中配置远程调用重试机制的具体说明。 #### 1. 使用默认重试机制 Feign 自带了一个默认的重试器类 `feign.Retryer.Default`,它会在网络连接失败或其他特定情况下尝试重新发起请求,默认最多会重试五次[^1]。可以通过如下方式启用全局默认重试: ```yaml feign: client: config: default: # 全局配置 retryer: feign.Retryer.Default ``` 上述配置表示对于所有的 Feign 客户端都会应用默认的重试策略。 --- #### 2. 自定义重试逻辑 如果需要更复杂的重试行为,可以自定义一个实现了 `feign.Retryer` 接口的类,并将其注入到 Feign 的配置中。下面是一个简单的例子展示如何创建并设置自定义重试器: ##### 创建自定义 Retryer 类 ```java import feign.RetryableException; import feign.Retryer; public class CustomRetryer implements Retryer { private int attempt = 0; // 当前尝试次数 private final int maxAttempts; // 最大尝试次数 private final long backoffMillis; // 每次等待的时间间隔(毫秒) public CustomRetryer(int maxAttempts, long backoffMillis) { this.maxAttempts = maxAttempts; this.backoffMillis = backoffMillis; } @Override public void continueOrPropagate(RetryableException e) throws Exception { if (attempt++ >= maxAttempts) { // 如果达到最大尝试次数,则抛出异常终止流程 throw e; } Thread.sleep(backoffMillis); // 否则休眠一段时间再重试 } @Override public Retryer clone() { return new CustomRetryer(maxAttempts, backoffMillis); } } ``` 此代码片段展示了如何构建一个具有指定最大重试次数和固定时间间隔的自定义重试器。 ##### 将自定义 Retryer 注入到 Feign 配置中 为了使自定义的重试器生效,需通过 Spring Boot 的 Bean 或者其他方式注册该组件。例如,在 Java 配置文件中完成这一操作: ```java import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class FeignConfig { @Bean public Retryer customRetryer() { return new CustomRetryer(3, 1000L); // 设置最大重试三次,每次间隔一秒 } } ``` 随后,在启动类或者任意地方加载这个配置即可让其影响整个应用程序内的所有 Feign 调用。 --- #### 3. 结合拦截器使用 除了基本的重试外,还可以利用 Feign 的拦截器来增强调试能力或记录日志等功能。这有助于更好地监控每一次请求及其对应的响应状态码等信息。 --- #### 总结 以上介绍了两种主要的方式用于配置 Feign 的远程调用重试机制——一种是直接采用框架自带的简单方案;另一种则是根据实际需求设计更加精细控制流的解决方案。无论是哪种途径都能有效提升系统的健壮性和用户体验满意度。 ```java // 示例:完整的自定义重试器集成过程 @Bean public Retryer myCustomizedRetryLogic() { return new CustomRetryer(7, 500L); // 假设允许七次重试机会,每半秒钟一次 } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值