OpenFeign重试组件Retryer原理

简介: 该文章主要讲解了OpenFeign中的重试组件Retryer的工作原理及其实现细节。

前言

前面三篇文章已经分析了Feign的通信Client组件,Client是Rpc框架中最核心关键的能力,通过Client发起远程通信。

# OpenFeign第一个可扩展组件通信Client详解
# OpenFeign最核心组件LoadBalancerFeignClient详解
# OpenFeign集成Ribbon负载均衡-过滤和选择服务核心实现

市面上的Rpc框架在核心通信能力的基础上,有很多服务治理的组件,OpenFeign也一样,今天主角是重试组件,我们看看OpenFeign是如何实现重试的。

重试组件入口

OpenFeign执行远程请求是在feign.SynchronousMethodHandler#invoke,重试组件也是在这里嵌入的,我们看看具体实现

//调用远程入口
public Object invoke(Object[] argv) throws Throwable {
   
   
        RequestTemplate template = this.buildTemplateFromArgs.create(argv);
        Request.Options options = this.findOptions(argv);
        Retryer retryer = this.retryer.clone();
        //死循环,如果成功或者重试结束就返回
        while(true) {
   
   
            try {
   
   
                //通过client发起通信
                return this.executeAndDecode(template, options);
            } catch (RetryableException var9) {
   
   
                //处理RetryableException异常
                RetryableException e = var9;

                try {
   
   
                    //重试组件生效,判断是否重试
                    retryer.continueOrPropagate(e);
                } catch (RetryableException var8) {
   
   
                    Throwable cause = var8.getCause();
                    if (this.propagationPolicy == ExceptionPropagationPolicy.UNWRAP && cause != null) {
   
   
                        throw cause;
                    }

                    throw var8;
                }
            }
        }
}

发起请求的地方捕获了RetryableException异常,并且执行了重试的逻辑。 Feign提供了两个重试实现

1、feign.Retryer.Default

2、feign.Retryer#NEVER_RETRY

image.png

OpenFeign默认使用不重试

从openfeign的装配类FeignClientsConfiguration我们可以看出 在OpenFeign中,默认使用的是NEVER_RETRY,不重试

@Configuration
public class FeignClientsConfiguration {
   
   
    @Bean
    @ConditionalOnMissingBean
    public Retryer feignRetryer() {
   
   
        return Retryer.NEVER_RETRY;
    }
}

OpenFeign默认重试组件分析

feign.Retryer.Default是OpenFeign实现了重试能力的重试组件,我们看下他是如何实现的

public static class Default implements Retryer {
   
   
        private final int maxAttempts;
        private final long period;
        private final long maxPeriod;
        int attempt;
        long sleptForMillis;

        public Default() {
   
   
            this(100L, TimeUnit.SECONDS.toMillis(1L), 5);
        }

        public Default(long period, long maxPeriod, int maxAttempts) {
   
   
            this.period = period;
            this.maxPeriod = maxPeriod;
            this.maxAttempts = maxAttempts;
            this.attempt = 1;
        }

        protected long currentTimeMillis() {
   
   
            return System.currentTimeMillis();
        }

        public void continueOrPropagate(RetryableException e) {
   
   
            //已经超过最大重试次数,不重试了,异常往上抛
            //attempt默认是1
            if (this.attempt++ >= this.maxAttempts) {
   
   
                throw e;
            } else {
   
   
                long interval;
                //根据上次重试时间计算间隔时间
                if (e.retryAfter() != null) {
   
   
                    interval = e.retryAfter().getTime() - this.currentTimeMillis();
                    //间隔时间是否超过最大重试间隔时间
                    if (interval > this.maxPeriod) {
   
   
                        interval = this.maxPeriod;
                    }

                    if (interval < 0L) {
   
   
                        return;
                    }
                } else {
   
   
                    //获取下次重试间隔时间
                    interval = this.nextMaxInterval();
                }

                try {
   
   
                    //睡眠重试间隔,再发起重试
                    Thread.sleep(interval);
                } catch (InterruptedException var5) {
   
   
                    Thread.currentThread().interrupt();
                    throw e;
                }

                this.sleptForMillis += interval;
            }
        }

        long nextMaxInterval() {
   
   
            //计算下次重试间隔,按照1.5为底的指数递增
            long interval = (long)((double)this.period * Math.pow(1.5, (double)(this.attempt - 1)));
            return interval > this.maxPeriod ? this.maxPeriod : interval;
        }

        public Retryer clone() {
   
   
            return new Default(this.period, this.maxPeriod, this.maxAttempts);
        }
    }

1、默认重试是捕获RetryableException异常,如果是RetryableException异常则执行重试逻辑。

2、每次重试间隔按照底数是1.5的指数级递增

自定义重试组件

我们可以自己实现重试组件feign.Retryer接口,也可以使用OpenFeign自带的重试组件feign.Retryer.Default,假如我们使用自带的重试组件,我们只要定义一个配置类,配置Retryer的Bean,覆盖默认的Retryer.NEVER_RETRY;

下方是配置每次重试间隔是100ms,最大重试间隔是1s, 重试次数是2次

@Component
public class CustomFeignConfig {
   
   


    public Integer retryPeriod = 100;
    public Integer retryMaxPeriod = 1000;
    public Integer maxRetryAttempts = 2;

    //自定义重试组件,覆盖OpenFeign默认的Retryer.NEVER_RETRY;
    @Bean
    public Retryer retryer() {
   
   
        return new Retryer.Default(retryPeriod, retryMaxPeriod, maxRetryAttempts);
    }
}

通过上面的配置类,OpenFeign就具备重试功能了,请注意虽然maxRetryAttempts配置是2,但是只会重试1次,2次包括本身调用那一次。也就是说,如果想除去本身调用那次外重试2次,则maxRetryAttempts需要配置为3。

总结

1、OpenFeign提供了重试组件,默认是不重试的

2、feign.Retryer.Default的实际重试次数是maxAttempts-1

OpenFeign重试组件分析完了,对你有没有帮助呢?

image.png

相关文章
|
6月前
Feign 调用超时,会自动重试吗?如何设置?
Feign默认不重试超时请求,但可通过Retryer配置重试策略。可设置最大重试次数、间隔等参数,并结合@FeignClient的configuration属性启用,提升服务调用的容错能力。
392 0
|
SQL API Apache
Dinky 和 Flink CDC 在实时整库同步的探索之路
本次分享围绕 Dinky 的整库同步技术演进,从传统数据集成方案的痛点出发,探讨了 Flink CDC Yaml 作业的探索历程。内容分为三个部分:起源、探索、未来。在起源部分,分析了传统数据集成方案中全量与增量割裂、时效性低等问题,引出 Flink CDC 的优势;探索部分详细对比了 Dinky CDC Source 和 Flink CDC Pipeline 的架构与能力,深入讲解了 YAML 作业的细节,如模式演变、数据转换等;未来部分则展望了 Dinky 对 Flink CDC 的支持与优化方向,包括 Pipeline 转换功能、Transform 扩展及实时湖仓治理等。
1625 12
Dinky 和 Flink CDC 在实时整库同步的探索之路
|
消息中间件 JSON Java
Spring Boot、Spring Cloud与Spring Cloud Alibaba版本对应关系
Spring Boot、Spring Cloud与Spring Cloud Alibaba版本对应关系
36538 1
|
负载均衡 Java 开发者
OpenFeign的工作原理
OpenFeign的工作原理
|
缓存 负载均衡 Java
OpenFeign第一个可扩展组件通信Client详解
这篇文章详细分析了OpenFeign框架中的第一个可扩展组件——通信Client,包括其默认实现`feign.Client.Default`,以及如何使用`LoadBalancerFeignClient`集成负载均衡能力,并探讨了如何替换默认的`HttpURLConnection`通信组件为`OkHttpClient`或`ApacheHttpClient`。
OpenFeign第一个可扩展组件通信Client详解
|
存储 监控 Java
OpenFeign请求拦截器组件RequestInterceptor原理与使用场景
该文章讲述了OpenFeign中的请求拦截器组件RequestInterceptor的原理及其常见使用场景。
OpenFeign请求拦截器组件RequestInterceptor原理与使用场景
|
Java 数据处理 Spring
Spring Cloud OpenFeign 超时与重试
今天给大家分享的是 `feign` 的超时与重试配置。
1354 0
Spring Cloud OpenFeign 超时与重试
|
负载均衡 Java Spring
Ribbon的超时配置会覆盖OpenFeign的超时配置吗
该文章详细分析了OpenFeign与Ribbon之间的超时配置关系,解释了Ribbon如何覆盖OpenFeign的默认超时配置,并探讨了OpenFeign超时配置的动态修改方案。
|
消息中间件 监控 搜索推荐
OpenFeign日志组件Logger原理与应用
该文章详细解释了如何在OpenFeign中配置并使用请求和响应的GZIP压缩功能。
|
负载均衡 Java 数据库连接
SpringCloud之OpenFeign简单使用
当远程服务调用失败时,会采用熔断降级策略,调用熔断降级的方法返回。
656 2

热门文章

最新文章