Adds response timeout using Mono.timeout

fixes gh-371
This commit is contained in:
Spencer Gibb
2018-06-18 22:49:00 -04:00
parent fdef92b3b7
commit ef173a64c6
6 changed files with 69 additions and 11 deletions

View File

@@ -217,8 +217,9 @@ public class GatewayAutoConfiguration {
@Bean
public NettyRoutingFilter routingFilter(HttpClient httpClient,
ObjectProvider<List<HttpHeadersFilter>> headersFilters) {
return new NettyRoutingFilter(httpClient, headersFilters);
ObjectProvider<List<HttpHeadersFilter>> headersFilters,
HttpClientProperties properties) {
return new NettyRoutingFilter(httpClient, headersFilters, properties);
}
@Bean

View File

@@ -20,6 +20,8 @@ package org.springframework.cloud.gateway.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
import reactor.ipc.netty.resources.PoolResources;
import java.time.Duration;
/**
* Configuration properties for the Netty {@link reactor.ipc.netty.http.client.HttpClient}
*/
@@ -29,6 +31,9 @@ public class HttpClientProperties {
/** The connect timeout in millis, the default is 45s. */
private Integer connectTimeout;
/** The response timeout. */
private Duration responseTimeout;
/** Pool configuration for Netty HttpClient */
private Pool pool = new Pool();
@@ -42,6 +47,14 @@ public class HttpClientProperties {
return connectTimeout;
}
public Duration getResponseTimeout() {
return responseTimeout;
}
public void setResponseTimeout(Duration responseTimeout) {
this.responseTimeout = responseTimeout;
}
public void setConnectTimeout(Integer connectTimeout) {
this.connectTimeout = connectTimeout;
}
@@ -220,6 +233,7 @@ public class HttpClientProperties {
public String toString() {
return "HttpClientProperties{" +
"connectTimeout=" + connectTimeout +
", responseTimeout=" + responseTimeout +
", pool=" + pool +
", proxy=" + proxy +
", ssl=" + ssl +

View File

@@ -26,10 +26,13 @@ import reactor.core.publisher.Mono;
import reactor.ipc.netty.NettyPipeline;
import reactor.ipc.netty.http.client.HttpClient;
import reactor.ipc.netty.http.client.HttpClientRequest;
import reactor.ipc.netty.http.client.HttpClientResponse;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.cloud.gateway.config.HttpClientProperties;
import org.springframework.cloud.gateway.filter.headers.HttpHeadersFilter;
import org.springframework.cloud.gateway.filter.headers.HttpHeadersFilter.Type;
import org.springframework.cloud.gateway.support.TimeoutException;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.NettyDataBuffer;
import org.springframework.http.HttpHeaders;
@@ -55,11 +58,14 @@ public class NettyRoutingFilter implements GlobalFilter, Ordered {
private final HttpClient httpClient;
private final ObjectProvider<List<HttpHeadersFilter>> headersFilters;
private final HttpClientProperties properties;
public NettyRoutingFilter(HttpClient httpClient,
ObjectProvider<List<HttpHeadersFilter>> headersFilters) {
ObjectProvider<List<HttpHeadersFilter>> headersFilters,
HttpClientProperties properties) {
this.httpClient = httpClient;
this.headersFilters = headersFilters;
this.properties = properties;
}
@Override
@@ -93,7 +99,7 @@ public class NettyRoutingFilter implements GlobalFilter, Ordered {
boolean preserveHost = exchange.getAttributeOrDefault(PRESERVE_HOST_HEADER_ATTRIBUTE, false);
return this.httpClient.request(method, url, req -> {
Mono<HttpClientResponse> responseMono = this.httpClient.request(method, url, req -> {
final HttpClientRequest proxyRequest = req.options(NettyPipeline.SendOptions::flushOnEach)
.headers(httpHeaders)
.chunkedTransfer(chunkedTransfer)
@@ -107,8 +113,16 @@ public class NettyRoutingFilter implements GlobalFilter, Ordered {
return proxyRequest.sendHeaders() //I shouldn't need this
.send(request.getBody().map(dataBuffer ->
((NettyDataBuffer)dataBuffer).getNativeBuffer()));
}).doOnNext(res -> {
((NettyDataBuffer) dataBuffer).getNativeBuffer()));
});
if (properties.getResponseTimeout() != null) {
responseMono.timeout(properties.getResponseTimeout(),
Mono.error(new TimeoutException("Response took longer than timeout: " +
properties.getResponseTimeout())));
}
return responseMono.doOnNext(res -> {
ServerHttpResponse response = exchange.getResponse();
// put headers and status so filters can modify the response
HttpHeaders headers = new HttpHeaders();

View File

@@ -26,6 +26,7 @@ import java.util.function.Predicate;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.reactivestreams.Publisher;
import org.springframework.cloud.gateway.support.TimeoutException;
import reactor.core.publisher.Mono;
import reactor.retry.Repeat;
import reactor.retry.RepeatContext;
@@ -146,12 +147,11 @@ public class RetryGatewayFilterFactory extends AbstractGatewayFilterFactory<Retr
};
}
private static <T> List<T> toList(T item) {
ArrayList<T> list = new ArrayList<>();
list.add(item);
return list;
private static <T> List<T> toList(T... items) {
return new ArrayList<>(Arrays.asList(items));
}
@SuppressWarnings("unchecked")
public static class RetryConfig {
private int retries = 3;
@@ -161,7 +161,7 @@ public class RetryGatewayFilterFactory extends AbstractGatewayFilterFactory<Retr
private List<HttpMethod> methods = toList(HttpMethod.GET);
private List<Class<? extends Throwable>> exceptions = toList(IOException.class);
private List<Class<? extends Throwable>> exceptions = toList(IOException.class, TimeoutException.class);
public RetryConfig setRetries(int retries) {
this.retries = retries;

View File

@@ -0,0 +1,28 @@
/*
* Copyright 2013-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.springframework.cloud.gateway.support;
public class TimeoutException extends Exception {
public TimeoutException() {
}
public TimeoutException(String message) {
super(message);
}
}

View File

@@ -62,6 +62,7 @@ public class GatewayAutoConfigurationTests {
GatewayAutoConfiguration.class))
.withPropertyValues("spring.cloud.gateway.httpclient.ssl.use-insecure-trust-manager=true",
"spring.cloud.gateway.httpclient.connect-timeout=10",
"spring.cloud.gateway.httpclient.response-timeout=10s",
"spring.cloud.gateway.httpclient.pool.type=fixed",
"spring.cloud.gateway.httpclient.proxy.host=myhost")
.run(context -> {