记录一次生产上的 Spring Cloud Feign 的重试问题

在一个项目中(数据产品),需要对接企业微信中第三方应用。在使用 Feign 的去调用微服务的用户模块用微信的 code 获取 access_token 以及用户工厂信息时出现 Feign 重试超时报错的情况,通过此篇文章记录问题解决的过程。

一、问题重现

1. Spring Cloud 部分依赖如下

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.5.3.RELEASE</version>
</parent>
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>Dalston.SR1</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-eureka</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-feign</artifactId>
    </dependency>

2.微信相关的接口文档

前端通过企业 id,配置好回调域名之后,调用微信的 API 去获取 code。见文档:

https://work.weixin.qq.com/api/doc/90000/90135/91022

注意:code只能用一次,见文档,因此获取到的 access_token 需要缓存起来,项目中是缓存到redis中的,用于后续的消息推送等等功能。

     99cf45683fd0ac32206240266089e4eb.png

3.请求流程图

      2f9a2a34af3fef4b0962bd37249cf63a.png

二、原因分析

首先,整个请求的链路中,阶段 2 是 feign 请求的位置,但是 yml 配置文件中并没有配置 feign。因此可以断定,feign 使用了默认的配置。问题发生时,查看 feign 的文档发现,feign 重试默认超时时间是 1s。

因此现在重新配置 feign 的超时时间,现有 feign 的配置如下:

feign:
  client:
    config:
      organization:
        connectTimeout: 5000
        readTimeout: 5000

其中,

  • organization 表示的就是 feign 所调用的服务名称。

  • connectTimeout表示建立请求连接的连接的时间(这里面包括获取请求 Eureka 中保存的服务列表——推测)

  • readTimeout表示连接建立以后请求调用的时间。

其次,在上述配置中,通过查看 organization 和 data 服务的请求日志,发现请求都能顺利建立。但是,当阶段三去请求微信的接口一旦延迟,则会触发 feign 的重试进行第二次调用。

由于阶段三请求微信的接口并不是没有调用,而是由于网络或者其他原因导致的微信没有响应,但是 code 又已经被消费了,当阶段二携带同样的 code 去调用微信的接口,这时就会出现 code 已经被消费。

最后,此时有另外一个问题就是,项目中的服务都是单实例部署,Spring Cloud 组件中 Feign 和 Ribbon 都有重试的功能。 Spring Cloud 中 Feign 整合了 Ribbon,但 Feign 和 Ribbon 都有重试功能。Spring Cloud为了统一两者的行为,在 C 版本以后,将 Feign 的重试策略默认设置为 feign.Retryer#NEVER_RETRY(即永不重试)。因此 Feign 的调用本质还是通过 Ribbon 去实现。

三、相关配置测试

经过测试后,发现 Feign 和 Ribbon 的配置都能实现超时熔断。

版本号:SpringCloud  Greenwich.SR1

配置一、只配置 Feign 相关配置,Feign 覆盖了 Ribbon 默认的超时时间配置

但是注意,该配置会触发 Ribbon 重试。

feign:
  client:
    config:
      eureka-client:
        connectTimeout: 1000
        readTimeout: 1000

配置二、只配置  Ribbon

注意:这里有个坑,MaxAutoRetriesNextServer 这个参数如果不配置为 0,即使在单实例部署的情况下,仍然会发生重试 1 次。因此如果不想发生重试,则需要手动配置 MaxAutoRetriesNextServer=0和MaxAutoRetries=0。

ribbon:
  ReadTimeout: 4000
  ConnectionTimeout: 4000
  OkToRetryOnAllOperations: true
  MaxAutoRetriesNextServer: 0 # 当前实例全部失败后可以换1个实例再重试,
  MaxAutoRetries: 1 # 在当前实例只重试2次

配置三、Feign和 Ribbon 都不配置

注意:经过测试后发现这里使用的是ribbon默认的超时配置,配置如下。

MaxAutoRetriesNextServer=1
MaxAutoRetries=0
public LoadBalancerContext(ILoadBalancer lb) {
    this.clientName = "default";
    this.maxAutoRetriesNextServer = 1;
    this.maxAutoRetries = 0;
    this.defaultRetryHandler = new DefaultLoadBalancerRetryHandler();
    this.okToRetryOnAllOperations =
        DefaultClientConfigImpl.DEFAULT_OK_TO_RETRY_ON_ALL_OPERATIONS;
    this.lb = lb;
}
 
 
版本号:SpringCloud Dalston.SR1 与 Greenwich.SR1 的测试结论一致

注意:Dalston.SR1 ribbon 组件默认的超时时间

 
 
public static final int DEFAULT_READ_TIMEOUT = 5000;
public static final int DEFAULT_CONNECT_TIMEOUT = 2000;

Greenwich.SR1 Ribbon 组件默认的超时间

 
 
 
 
public static final int DEFAULT_CONNECT_TIMEOUT = 1000;
public static final int DEFAULT_READ_TIMEOUT = 1000;

20ca6231d2b61b27d3e097ae42e69511.jpeg

SaaS模式下,多租户系统数据隔离实现方案


400b9b0e081eb750d24fe5e1429d5841.jpeg

MySQL数据库 5.7 vs 8.0性能测评


376cc0bf3b49ea665d1218c1fe493344.jpeg

SpringBoot项目限流就该这么设计(万能通用),稳!


240d929a0c8cb4438bae99baea9722dd.jpeg

用阿里的Arthas做Java内存诊断,真香


151dd611c53d6b84d228d0663dde5075.jpeg

简单几行代码,优雅的实现 SpringBoot 鉴权


071795e9259315ba0920d4c3508b3914.jpeg

Redis持久化,不注意这些容易出事!


a41f244e734cf73b0eb383ccb08c32d3.jpeg

SpringBoot 中快速优雅进行字段校验


点赞和在看就是最大的支持❤️

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值