一、问题产生
自定义重试次数,实现如下
@ConditionalOnProperty(prefix = "feign.client", name = "enable", havingValue = "true")
@Configuration
public class FeignConfig {@Beanpublic FeignInterceptor feignInterceptor() {return new FeignInterceptor();}@Beanpublic ErrorDecoder errorDecoder() {return new FeignErrorDecoder();}@Beanpublic Retryer feignRetryer() {return new Retryer.Default(500, TimeUnit.SECONDS.toMillis(1L), 3);}
}
Application.yml配置如下:
feign:client:enable: trueconfig:default:connectTimeout: 30000readTimeout: 30000#retryer: feign.Retryer.Defaultfile-service: # 为个别服务设置不同的超时时间connectTimeout: 6 # 为了方便测试超时重试readTimeout: 6 # 为了方便测试超时重试
上述调用完后,发现不对,配置了3次,实际重试了6次,注册中心有2个被调服务实例
二、问题根源
您的重试次数异常(配置3次实际触发6次)是由于 Feign 与 Ribbon 的重试策略叠加,且 Eureka 注册的多个服务实例触发 Ribbon 的实例切换重试 导致的。具体原因如下:
-
Ribbon 默认重试机制
Ribbon 的MaxAutoRetriesNextServer
默认值为1
,当服务注册多个实例时,Ribbon 会在首次请求失败后 切换实例重试 1 次。此时总重试次数计算公式为:总请求次数 = (1 + MaxAutoRetries) * (1 + MaxAutoRetriesNextServer) * Feign重试次数
例如:若
MaxAutoRetries=0
(默认)、MaxAutoRetriesNextServer=1
(默认)、FeignmaxAttempts=3
,则总次数为:(1+0) * (1+1) * 3 = 6次
-
Eureka 多实例触发切换
当两个服务实例注册到 Eureka 时,Ribbon 的MaxAutoRetriesNextServer=1
会尝试 切换实例重试,导致每个实例被调用Feign重试次数 × (1 + MaxAutoRetriesNextServer)
次。
三、解决方案
关闭 Ribbon 实例切换重试
在 application.yml
中显式禁用 Ribbon 的实例切换重试:
ribbon:MaxAutoRetriesNextServer: 0 # 关闭切换实例重试MaxAutoRetries: 0 # 关闭同一实例重试(默认已为0,可省略)OkToRetryOnAllOperations: false # 禁止非GET请求重试
修改完成后,再次执行程序,控制台输出了超时
可以看到文件服务重试了3次,如下图所示