背景
项目中ServiceA 调用 ServiceB中接口时,
ServiceB处理超时长时间未返回数据,结果显示最终ServiceB中接收到ServiceA的两次请求,导致数据重复写入。
初步分析是Feign或者Ribbon的重试机制导致的,但当前项目中并未特别设置重试相关的配置,配置文件配置为
ribbon:
ConnectTimeout: 10000 # 连接超时时间(ms)
ReadTimeout: 10000 # 通信超时时间(ms)
场景模拟/分析
ServiceA Controller
@GetMapping(value = "/hi")
public String sayHi(@RequestParam String name) {
return serviceHi.sayHiFromClientServiceHi(name);
}
@PostMapping(value = "/hipost")
public String sayHiPost(@RequestBody JSONObject paramJsonObject) {
return serviceHi.sayHiPostFromClientServiceHi(paramJsonObject);
}
ServiceB Controller
@GetMapping("/hi")
public String testGet(@RequestParam(value = "name", defaultValue = "forezp") String name) throws InterruptedException {
log.info("be called, param is {}",name);
Thread.sleep(11000L);
return "hi " + name + " ,i am from port:" + port;
}
@PostMapping("/hipost")
public String testPost(@RequestBody JSONObject paramJsonObject) throws InterruptedException {
log.info("be called, param is {}",paramJsonObject.getStr("name"));
Thread.sleep(11000L);
return paramJsonObject.getStr("name");
}
调用Hi接口后 返回ReadTimeOut ServiceB 被调用两次
调用HiPost接口后 返回ReadTimeOut ServiceB 被调用一次跟踪源码发现,getRequestSpecificRetryHandler 方法中做了重试相关策略的配置, 当okToRetryOnAllOperations为false且方法不为Get时, 不会进行重试,否则都会默认进行重试调用
public RequestSpecificRetryHandler getRequestSpecificRetryHandler(final S request, final IClientConfig requestConfig) {
if (this.okToRetryOnAllOperations) {
return new RequestSpecificRetryHandler(true, true, this.getRetryHandler(), requestConfig);
} else {
return !request.getContext().getMethod().equals("GET") ? new RequestSpecificRetryHandler(true, false, this.getRetryHandler(), requestConfig) : new RequestSpecificRetryHandler(true, true, this.getRetryHandler(), requestConfig);
}
}
解决方案
通过为以下字段进行配置修改来关闭重试
ribbon:
ConnectTimeout: 10000 # 连接超时时间(ms)
ReadTimeout: 10000 # 通信超时时间(ms)
OkToRetryOnAllOperations: false # 是否对所有操作重试 默认为false
MaxAutoRetriesNextServer: 0 # 同一服务不同实例的重试次数 默认为1
MaxAutoRetries: 0 # 同一实例的重试次数 默认为0
ConnectTimeOut: 建立连接所用的时间
ReadTimeOut: 连接建立后从服务器读取到资源的时间
最大重试次数 = (1+maxAutoRetries)*(1+maxAutoRetriesNextServer)次