我刚遇到了这个问题,搜索并没有找到任何解决方案。下面是我的解决方案和思考过程。
使用HttpComponentsClientHttpRequestFactory为RestTemplate设置超时时间。每次发出请求时,它会在requestFactory上调用createRequest函数。在此函数中设置了RequestConfig,其中包含超时时间和一些特定于请求的属性。然后将该RequestConfig设置在HttpContext上。以下是尝试构建此RequestConfig和HttpContext的步骤(按顺序):
1. 在HttpComponentsClientHttpRequestFactory内部调用createHttpContext函数,默认情况下不执行任何操作并返回null。
2. 从HttpUriRequest获取RequestConfig(如果存在),并将其添加到HttpContext中。
3. 在HttpComponentsClientHttpRequestFactory内部调用createRequestConfig函数,该函数从HttpClient获取RequestConfig,将其与requestFactory内部构建的RequestConfig合并,并将其添加到HttpContext中。(默认情况下会执行此操作)
我认为这三个步骤都可以有针对性的解决方案。我相信最简单且最可靠的解决方案是围绕第一个步骤构建解决方案。我最终创建了自己的HttpComponentsRequestFactory,并覆盖了createHttpContext函数,该函数内部逻辑检查请求URI的路径是否与我提供的pathPattern匹配,并为该pathPattern指定超时时间。
public class PathTimeoutHttpComponentsClientHttpRequestFactory extends HttpComponentsClientHttpRequestFactory {
private List<PathPatternTimeoutConfig> pathPatternTimeoutConfigs = new ArrayList<>();
protected HttpContext createHttpContext(HttpMethod httpMethod, URI uri) {
for (PathPatternTimeoutConfig config : pathPatternTimeoutConfigs) {
if (httpMethod.equals(config.getHttpMethod())) {
final Matcher matcher = config.getPattern().matcher(uri.getPath());
if (matcher.matches()) {
HttpClientContext context = HttpClientContext.create();
RequestConfig requestConfig = createRequestConfig(getHttpClient());
requestConfig = RequestConfig.copy(requestConfig)
.setSocketTimeout(config.getReadTimeout())
.setConnectTimeout(config.getConnectionTimeout())
.setConnectionRequestTimeout(config.getConnectionRequestTimeout())
.build();
context.setAttribute(HttpClientContext.REQUEST_CONFIG, requestConfig);
return context;
}
}
}
return null;
}
public void addPathTimeout(HttpMethod httpMethod, String pathPattern, int connectionTimeout, int connectionRequestTimeout, int readTimeout) {
Assert.hasText(pathPattern, "pathPattern must not be null, empty, or blank");
final PathPatternTimeoutConfig pathPatternTimeoutConfig = new PathPatternTimeoutConfig(httpMethod, pathPattern, connectionTimeout, connectionRequestTimeout, readTimeout);
pathPatternTimeoutConfigs.add(pathPatternTimeoutConfig);
}
private class PathPatternTimeoutConfig {
private HttpMethod httpMethod;
private String pathPattern;
private int connectionTimeout;
private int connectionRequestTimeout;
private int readTimeout;
private Pattern pattern;
public PathPatternTimeoutConfig(HttpMethod httpMethod, String pathPattern, int connectionTimeout, int connectionRequestTimeout, int readTimeout) {
this.httpMethod = httpMethod;
this.pathPattern = pathPattern;
this.connectionTimeout = connectionTimeout;
this.connectionRequestTimeout = connectionRequestTimeout;
this.readTimeout = readTimeout;
this.pattern = Pattern.compile(pathPattern);
}
public HttpMethod getHttpMethod() {
return httpMethod;
}
public String getPathPattern() {
return pathPattern;
}
public int getConnectionTimeout() {
return connectionTimeout;
}
public int getConnectionRequestTimeout() { return connectionRequestTimeout; }
public int getReadTimeout() {
return readTimeout;
}
public Pattern getPattern() {
return pattern;
}
}
}
如果您愿意的话,您可以创建一个具有默认超时时间的请求工厂,并像这样为特定路径指定自定义超时时间:
@Bean
public PathTimeoutHttpComponentsClientHttpRequestFactory requestFactory() {
final PathTimeoutHttpComponentsClientHttpRequestFactory factory = new PathTimeoutHttpComponentsClientHttpRequestFactory();
factory.addPathTimeout(HttpMethod.POST, "\\/api\\/groups\\/\\d+\\/users\\/\\d+", 1000, 1000, 30000);
factory.setConnectionRequestTimeout(1000);
factory.setConnectTimeout(1000);
factory.setReadTimeout(5000);
return factory;
}
@Bean
public RestTemplate restTemplate() {
final RestTemplate restTemplate = new RestTemplate();
restTemplate.setRequestFactory(requestFactory());
...
return restTemplate;
}
这种方法非常可重用,不需要为每个独特的超时创建单独的RestTemplate,据我所知,它是线程安全的。
ServiceARestTemplateHighTimeout``ServiceARestTemplateMidTimeout``ServiceARestTemplateLowTimeout
..并且这应该复制到每个6个服务..这是18个RestTemplates,如果需要新的唯一超时,我将不得不创建更多的RestTemplates。对此有何想法? - prettyvoid