概述
超级简单易用的 Spring RestTemplate, 还可以多线程共享一个实例, 有一个小缺憾, 就是需要记得设置默认超时时间, 否则默认不超时. 而设置超时时间以后, 又会引入新的 Connection Pool 大小的问题.
设置超时
这个链接讲了两种方式:spring-resttemplate-timeout, 这里就直接贴过来了
方式1
@Configuration
public class AppConfig{
@Bean
public RestTemplate customRestTemplate(){
HttpComponentsClientHttpRequestFactory httpRequestFactory = new HttpComponentsClientHttpRequestFactory();
httpRequestFactory.setConnectionRequestTimeout(...);
httpRequestFactory.setConnectTimeout(...);
httpRequestFactory.setReadTimeout(...);
return new RestTemplate(httpRequestFactory);
}
}
方式2
@Bean
public RestTemplate restTemplate(
RestTemplateBuilder restTemplateBuilder) {
return restTemplateBuilder
.setConnectTimeout(500)
.setReadTimeout(500)
.build();
}
方式3
(不建议) 以前见过, 通过 property 设置全局 java.net 里 URL 的 timeout 来影响 RestTemplate 的超时时间(默认使用java.net包里面组件).
设置超时后问题和解决
调用 RestTemplate 的默认构造函数,RestTemplate 对象在底层通过使用 java.net 包下的实现创建 HTTP 请求. 这种情况相对简单, 一个请求一个连接, 也没有限制.
而上面所说的前两种设置超时方式, 都会导致 RestTemplate 引入 Apache HttpClient 去设置超时时间. 在默认 HttpClientBuilder
(httpClient-4.5.2)里面有一段代码:
if (systemProperties) {
String s = System.getProperty("http.keepAlive", "true");
if ("true".equalsIgnoreCase(s)) {
s = System.getProperty("http.maxConnections", "5");
final int max = Integer.parseInt(s);
poolingmgr.setDefaultMaxPerRoute(max);
poolingmgr.setMaxTotal(2 * max);
}
}
在 keepAlive(默认) 情况下, 默认设置了对单个 host 的最大 connection 数量是 5. 所有connection数量不能超过 10~ 如果说单个 host 设置长连接数设置为5问题不大的话, 那么对于需要访问多个不同网站的情况下, 连接总数一共10个就有点坑了. 在长时间得不到Connection的情况下, 会抛出异常: org.springframework.web.client.ResourceAccessException Timeout waiting for connection from pool
具体解决方法是手工配置一下连接池:
@Configuration
public class RestTemplateConfig {
@Value("${application.http.resttemplate.timeoutSeconds:30}")
private Integer defaultTimeOutSeconds;
@Value("${application.http.resttemplate.maxConnPerRoute:50}")
private Integer defaultMaxPerRoute;
@Value("${application.http.resttemplate.maxConnTotal:200}")
private Integer maxTotal;
@Bean
public RestTemplate customRestTemplate() {
int timeoutMills = defaultTimeOutSeconds * 1000;
RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(timeoutMills)
.setConnectionRequestTimeout(timeoutMills).setSocketTimeout(timeoutMills).build();
HttpClient httpClient = HttpClientBuilder.create().setMaxConnTotal(maxTotal)
.setMaxConnPerRoute(defaultMaxPerRoute).setDefaultRequestConfig(requestConfig).build();
HttpComponentsClientHttpRequestFactory httpRequestFactory = new HttpComponentsClientHttpRequestFactory(
httpClient);
return new RestTemplate(httpRequestFactory);
}
}
具体大小适情况而定. 连接总数配置过大还会影响性能.