FeignClient重试机制造成的接口幂等性
Feign源码分析,其实现类在 SynchronousMethodHandler,实现方法是public Object invoke(Object[] argv) ,它的代码分析如下:
1.构造请求数据,将对象转换为json:
RequestTemplate template = buildTemplateFromArgs.create(argv);
2.发送请求进行执行(执行成功会解码响应数据):
executeAndDecode(template, options);
3. 执行请求会有重试机制:
Retryer retryer = this.retryer.clone();
while (true) {
try {
return executeAndDecode(template, options);
} catch (RetryableException e) {
try {
retryer.continueOrPropagate(e);
} catch (RetryableException th) {
Throwable cause = th.getCause();
// 重试结束 或则 不允许重试,则通过抛异常的形式终止
if (propagationPolicy == UNWRAP && cause != null) {
throw cause;
} else {
throw th;
}
}
if (logLevel != Logger.Level.NONE) {
logger.logRetry(metadata.configKey(), logLevel);
}
continue;
}
}
4. Retryer是重试器,其实现方法有两种
第一种是系统默认实现方式,第二种是可以自定义重试器,一般少用,通过默认实现重试类Default可以看到其构造函数中的重试次数为5。
public Default() {
this(100, SECONDS.toMillis(1), 5);
}
public Default(long period, long maxPeriod, int maxAttempts) {
this.period = period;
this.maxPeriod = maxPeriod;
this.maxAttempts = maxAttempts;
this.attempt = 1;
}
因此解决Feign调用的幂等性问题最简单也就最常用的就是让Feign不重试。
为FeignClient增加请求重试机制
spring cloud通过feign client进行服务之间调用的时候,默认不会进行重试,这样会有一个问题,比如你的服务在滚动升级重启的时候,feign的调用将直接失败,但其实我是滚动重启,重启了一个服务实例,还有另外一个服务实例是可用的,应该允许自动均衡策略重试请求发送到另外一个可用的服务实例上去。
要启用重试机制,首先必须引入spring-retry依赖:
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
然后通过注册一个bean:
@Bean
public Retryer feignRetryer() {
return new Retryer.Default();
}
大功告成。
不过还有个前提就是,你的远程调用接口方法的必须是幂等的(比如GET方法认为是幂等的,调用多少次结果都一样,而POST方法有可能有重复提交问题),不然还是不会重试的,因为其他HttpMethod被认为是非幂等的,不能重复执行,因此不能被重试
以上为个人经验,希望能给大家一个参考,也希望大家多多支持编程网。