背景
我们知道任何的客户端与服务端发起接口请求,都需要等待服务端处理完之后响应,
那么等待时间不能确定什么时候可以响应,但是客户端不可能一直等待中,所以客户端
就要设置一个超时时间,不管是否处理完,过了设置的指定时间就等于是超时。
那么在微服务中如何处理超时的呢?
微服务客户端
在微服务中有两个调用客户端
- 网关路由客户端 (Netty Client)
- 服务调用客户端 (Feign Client)
Feign Client
可以是Apache HttpClient
或OK HttpClient
或JDK HttpClient
或其他
很多人认为 网关的客户端 用的是 Feign
,其实不是的。
- 网关路由是使用的
org.springframework.cloud.gateway.filter.GlobalFilter
实现的 - 服务调用是使用
feign.Client
实现的
原理差不多都是使用责任链设计模式
设置超时时间
网关路由
应用超时代码行
-
org.springframework.cloud.gateway.filter.NettyRoutingFilter
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { . . . Duration responseTimeout = getResponseTimeout(route); if (responseTimeout != null) { responseFlux = responseFlux .timeout(responseTimeout, Mono.error(new TimeoutException( "Response took longer than timeout: " + responseTimeout))) .onErrorMap(TimeoutException.class, th -> new ResponseStatusException(HttpStatus.GATEWAY_TIMEOUT, th.getMessage(), th)); } return responseFlux.then(chain.filter(exchange)); } private Duration getResponseTimeout(Route route) { Object responseTimeoutAttr = route.getMetadata().get(RESPONSE_TIMEOUT_ATTR); Long responseTimeout = null; if (responseTimeoutAttr != null) { if (responseTimeoutAttr instanceof Number) { responseTimeout = ((Number) responseTimeoutAttr).longValue(); } else { responseTimeout = Long.valueOf(responseTimeoutAttr.toString()); } } return responseTimeout != null ? Duration.ofMillis(responseTimeout) : properties.getResponseTimeout(); }
配置超时时间
-
org.springframework.cloud.gateway.config.HttpClientProperties
connectTimeout
连接超时时间responseTimeout
响应超时时间
-
配置示例
spring: cloud: gateway: enabled: true httpclient: connectTimeout: 1000 responseTimeout: 5000
服务调用
应用超时代码行
-
org.springframework.cloud.openfeign.FeignClientFactoryBean
protected void configureUsingProperties( FeignClientProperties.FeignClientConfiguration config, Feign.Builder builder) { . . . connectTimeoutMillis = config.getConnectTimeout() != null ? config.getConnectTimeout() : connectTimeoutMillis; readTimeoutMillis = config.getReadTimeout() != null ? config.getReadTimeout() : readTimeoutMillis; followRedirects = config.isFollowRedirects() != null ? config.isFollowRedirects() : followRedirects; builder.options(new Request.Options(connectTimeoutMillis, TimeUnit.MILLISECONDS, readTimeoutMillis, TimeUnit.MILLISECONDS, followRedirects)); . . . }
FeignName 在初始化的时候已经设置
-
feign.okhttp.OkHttpClient
OKhttp 作示例... public feign.Response execute(feign.Request input, feign.Request.Options options) throws IOException { okhttp3.OkHttpClient requestScoped; if (delegate.connectTimeoutMillis() != options.connectTimeoutMillis() || delegate.readTimeoutMillis() != options.readTimeoutMillis() || delegate.followRedirects() != options.isFollowRedirects()) { requestScoped = delegate.newBuilder() .connectTimeout(options.connectTimeoutMillis(), TimeUnit.MILLISECONDS) .readTimeout(options.readTimeoutMillis(), TimeUnit.MILLISECONDS) .followRedirects(options.isFollowRedirects()) .build(); } else { requestScoped = delegate; } Request request = toOkHttpRequest(input); Response response = requestScoped.newCall(request).execute(); return toFeignResponse(response, input).toBuilder().request(input).build(); }
配置超时时间
-
配置类
org.springframework.cloud.openfeign.FeignClientProperties.FeignClientConfiguration
connectTimeout
连接超时时间readTimeout
响应超时时间
-
配置示例
feign: client: config: default: connectTimeout: 500 readTimeout: 3000
default
是默认应用值,此处可以替代feign name
这样的话指定该feign
下面的所有方法都是使用该超时时间
-
如果需要特定方法不同的超时时间的话,需要在
feign
方法中添加feign.Request.Options
参数,或者是使用拦截器feign.RequestInterceptor
设置@PostMapping("/e1") ResponseEntity<String> e1(Options options);