一、springcloud2020中openfeign超时时间遇到的坑
在springcloud2020以前的版本中,springcloud自带ribbon实现负载均衡。而springcloud2020版本整合的是LoadBalancer做负载均衡策略。而ribbon默认的超时时间为1秒钟,经过测试发现springcloud2020没有默认的超时时间了。虽然请求时间很长,依然会收到应答。
我们现在来思考一个问题,如果这个请求长达 10 ~ 20 秒,一旦并发量突然增高,就会引发雪崩,为了对应这样的问题,我们可以通过配置超时时间来进行限制。
多说一句,一般我们实际业务也是不可能这样的,10 ~ 20 秒的请求太久了,业务一般会要求响应时间不超过几秒。
二、payment服务端模拟长时间应答
为简单起见,新增一个请求sleep(5000),休眠5秒钟
三、订单消费端order80新增service接口
@Component
@FeignClient(value = "CLOUD-PAYMENT-SERVICE")
public interface OpenFeignPaymentServices {
@GetMapping("/payment/get/{id}")
CommonResult<Payment> getPaymentById(@PathVariable("id") Long id);
@GetMapping("/payment/openfeign/timeout")
String getFeignTimeout();
}
四、订单消费端order80新增controller接口调用service的接口
@GetMapping("/consumer/payment/openfeign/timeout")
public String getFeignTimeout() {
return openFeignPaymentServices.getFeignTimeout();
}
五、配置 OpenFeign 超时机制
我们在订单模块order80中进行配置超时机制
yml
feign:
client:
config:
default: # 这里就是指的所有被加载的默认FeignClient实现的服务配置都生效
connectTimeout: 1000
readTimeout: 2000
connectTimeout 是指连接超时时间,如果超过 1 秒,就表示超时了。(无默认值)
readTimeout 是指从连接建立时开始计算,返回的时间超过 2 秒就表示超时了。(无默认值)
六、测试
配置好之后,我们重新启动订单服务,然后再来请求一下
前端浏览器报500错误,通过看后台日志可以看到是超时了
可以看到,这里是直接异常,因为我们设置的是 2 秒的返回时间,但是因为我们设置了 5 秒的线程休眠,导致我们数据返回不回来,最后直接异常。
七、超时时间设置的源码路径
org.springframework.cloud:spring-cloud-openfeign-core:3.04 -> spring-cloud-openfeign-core:3.04.jar -> org.springframework.cloud.openfeign -> FeignClientFactoryBean.class
下面截取了一小段代码
protected void configureUsingProperties(FeignClientConfiguration config, Builder builder) {
if (config != null) {
if (config.getLoggerLevel() != null) {
builder.logLevel(config.getLoggerLevel());
}
if (!this.refreshableClient) {
this.connectTimeoutMillis = config.getConnectTimeout() != null ? config.getConnectTimeout() : this.connectTimeoutMillis;
this.readTimeoutMillis = config.getReadTimeout() != null ? config.getReadTimeout() : this.readTimeoutMillis;
this.followRedirects = config.isFollowRedirects() != null ? config.isFollowRedirects() : this.followRedirects;
builder.options(new Options((long)this.connectTimeoutMillis, TimeUnit.MILLISECONDS, (long)this.readTimeoutMillis, TimeUnit.MILLISECONDS, this.followRedirects));
}
}