feign和ribbon的重试机制

前言

首先声明一点,这里的重试并不是报错以后的重试,而是负载均衡客户端发现远程请求实例不可到达后,去重试其他实例。

feign重试

feign的重试机制默认是关闭的,源码如下

    //FeignClientsConfiguration.java
    @Bean
    @ConditionalOnMissingBean
    public Retryer feignRetryer() {
        return Retryer.NEVER_RETRY;
    }

当没有spring容器中不存在retryer这个实例的时候,会初始化这个bean, NEVER_RETRY

如何开启

    @Bean
    public Retryer feignRetryer() {
        return new Retryer.Default();
    }

在你的配置类中,添加如上代码,当然你也可以自定义Retryer。 默认重试5次

源码实现

代码入口: feign.SynchronousMethodHandler

  @Override
  public Object invoke(Object[] argv) throws Throwable {
    // 获取请求模板
    RequestTemplate template = buildTemplateFromArgs.create(argv);
    // 克隆当前的重试对象
    Retryer retryer = this.retryer.clone();
    while (true) { // 一个死循环
      try {
        // 执行请求
        return executeAndDecode(template);
      } catch (RetryableException e) {
        // 出现异常,retryer.continueOrPropagate进行重试
        retryer.continueOrPropagate(e);
        if (logLevel != Logger.Level.NONE) {
          logger.logRetry(metadata.configKey(), logLevel);
        }
        continue;
      }
    }
  }

步骤说明:

1.获取请求模板

2.获取重试对象

3.开启一个死循环,执行请求,如果出现异常,则被retryer处理。这个时候就要看retryer的具体实例了,如果是Retryer.NEVER_RETRY 可以看一下他的实现

Retryer NEVER_RETRY = new Retryer() {

    @Override
    public void continueOrPropagate(RetryableException e) {
      // 直接抛了个异常
      throw e;
    }

    @Override
    public Retryer clone() {
      return this;
    }
  };

看一下Retryer.Default的实现

public void continueOrPropagate(RetryableException e) {
        // maxAttempts =5 , 如果重试次数大于5了,则直接抛个 异常出去
      if (attempt++ >= maxAttempts) {
        throw e;
      }

      long interval;
      if (e.retryAfter() != null) {
        interval = e.retryAfter().getTime() - currentTimeMillis();
        if (interval > maxPeriod) {
          interval = maxPeriod;
        }
        if (interval < 0) {
          return;
        }
      } else {
        interval = nextMaxInterval();
      }
      try {
        Thread.sleep(interval);
      } catch (InterruptedException ignored) {
        Thread.currentThread().interrupt();
      }
      sleptForMillis += interval;
    }

综上所述,上面那个while循环什么情况下会直接退出?

1.重试对象里面抛了个异常出来,while循环退出。

2.执行请求成功,直接return出去

3.开启了hystrix,hystrix超时,则直接熔断

ribbon的重试机制

ribbon的重试机制是默认重试一次。

属性    备注
ribbon.MaxAutoRetrues    重试相同的服务,默认次数为1
ribbon.MaxAutoRetruesNextServer    重试下一台服务,默认为1
ribbon.connectTimeout    连接超时时间
ribbon.readTimeout    读取数据超时
ribbon.okToRetryOnAllOperations    无论是超时还是connet异常,统统重试,默认为false,
源码实现

代码入口:AbstractLoadBalancerAwareClient

public T executeWithLoadBalancer(final S request, final IClientConfig requestConfig) throws ClientException {
          // 获取请求重试handler
        RequestSpecificRetryHandler handler = getRequestSpecificRetryHandler(request, requestConfig);
          // 构建ribbon的负载均衡请求
        LoadBalancerCommand<T> command = LoadBalancerCommand.<T>builder()
                .withLoadBalancerContext(this)
                .withRetryHandler(handler)
                .withLoadBalancerURI(request.getUri())
                .build();

        try {
            // 执行请求
            return command.submit(
                new ServerOperation<T>() {
                    @Override
                    public Observable<T> call(Server server) {
                        URI finalUri = reconstructURIWithServer(server, request.getUri());
                        S requestForServer = (S) request.replaceUri(finalUri);
                        try {
                            return Observable.just(AbstractLoadBalancerAwareClient.this.execute(requestForServer, requestConfig));
                        } 
                        catch (Exception e) {
                            return Observable.error(e);
                        }
                    }
                })
                .toBlocking()
                .single();
        } catch (Exception e) {
            Throwable t = e.getCause();
            if (t instanceof ClientException) {
                throw (ClientException) t;
            } else {
                throw new ClientException(e);
            }
        }
        
    }

上面那么多行,值需要注意其中一行就好了

 RequestSpecificRetryHandler handler = getRequestSpecificRetryHandler(request, requestConfig);
1
由于这个ribbon是结合feign使用的,所以这个获取重试机制的方法是实现在下面这个类里面:

//org.springframework.cloud.netflix.feign.ribbon.FeignLoadBalancer.java
@Override
    public RequestSpecificRetryHandler getRequestSpecificRetryHandler(
            RibbonRequest request, IClientConfig requestConfig) {
          // 1. 从ribbon的配置里面获取OkToRetryOnAllOperations的配置,默认为false,如果配置为true
        if (this.clientConfig.get(CommonClientConfigKey.OkToRetryOnAllOperations,
                false)) {
              // 
            return new RequestSpecificRetryHandler(true, true, this.getRetryHandler(),
                    requestConfig);
        }
          // 2. 判断请求方式是否是POST请求
        if (!request.toRequest().method().equals("GET")) {
            return new RequestSpecificRetryHandler(true, false, this.getRetryHandler(),
                    requestConfig);
        }
        else {
              // 3. 默认的重试handler
            return new RequestSpecificRetryHandler(true, true, this.getRetryHandler(),
                    requestConfig);
        }
    }

从上面的代码可以看到,根据不同的条件,构建不一样的RequestSpecificRetryHandler,在这之前,有必要了解一下RequestSpecificRetryHandler的四个参数的

含义。

public RequestSpecificRetryHandler(boolean okToRetryOnConnectErrors, boolean okToRetryOnAllErrors, RetryHandler baseRetryHandler,  IClientConfig requestConfig);
1
okToRetryOnConnectErrors : 在出现connectErrors的时候,进行重试

okToRetryOnAllErrors : 只要发现实例不可用时,都进行重试

baseRetryHandler :负载均衡重试的handler,使用的是默认的DefaultLoadBalancerRetryHandler

requestConfig : 配置

了解到这些参数的作用是,咱们再来看一下,ribbon的重试

步骤说明:

1.从ribbon的配置里面获取OkToRetryOnAllOperations的配置,默认为false,如果配置为true,那么设置okToRetryOnConnectErrors = true , okToRetryOnAllErrors = true , 也就是说 配置了这个,只要发现实例不可达,那么都会进行重试

2.判断请求方式是否是POST请求,如果是POST 请求,则设置okToRetryOnConnectErrors = true , okToRetryOnAllErrors = false , 这个设置的意思,只要在

出现connectErrors的时候才会进行重试。

3.第三步,基本上表示该请求为GET请求了,这个时候是设置okToRetryOnConnectErrors = true , okToRetryOnAllErrors = true , 跟第一步的效果一样。

总结:

默认OkToRetryOnAllOperations为false, 所以上面的第一步一般是不会执行, 也就是说,POST请求会走上面的第二步,GET请求是走上面的第三步。

默认ribbon重试一次,POST请求默认是在connect异常的时候会重试,GET请求是都会进行重试,包括请求超时之类的。所以尽量不要用GET请求做增加,修改,删除的操作,

     欢迎大家关注我的微信公众号 您的关注就是我不懈的动力 

 

OpenFeign 是一个在 Spring Cloud 中使用的声明式 HTTP 客户端,它提供了一些默认重试机制来处理请求失败的情况。在默认情况下,OpenFeign 使用了 Ribbon 作为负载均衡器,并且具备了一些默认重试策略。 OpenFeign默认重试机制是基于 Ribbon 的,它会在发生请求失败时进行重试。具体的重试策略可以通过配置文件或者代码进行调整。默认情况下,OpenFeign 会进行 5 次最大重试默认的最大重试次数可以通过配置项 `feign.client.config.default.retryMaxAttempts` 进行修改。 除了最大重试次数,OpenFeign 还提供了其他的重试相关配置项,例如 `feign.client.config.default.retryPeriod` 可以设置每次重试之间的时间间隔,默认为 100 毫秒。此外,还可以通过 `feign.client.config.default.retryableStatusCodes` 配置项来设置哪些 HTTP 状态码会触发重试默认情况下,OpenFeign 会在遇到网络异常(如连接超时、连接拒绝等)或者 5xx 级别的状态码时进行重试。 如果默认重试机制无法满足需求,也可以通过自定义 Feign 的配置类来实现更加灵活的重试策略。可以通过实现 `Retryer` 接口来定义自己的重试策略,并将其注入到 Feign 的配置类中。 总结起来,OpenFeign重试机制可以通过配置文件或者代码进行调整,默认情况下会在发生请求失败时进行最多 5 次的重试。如果需要更加定制化的重试策略,可以通过自定义 Feign 的配置类来实现。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值