Spring RestTemplate 超时设置 和 ResourceAccessException

概述

超级简单易用的 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);
    }

}

具体大小适情况而定. 连接总数配置过大还会影响性能.

猜你喜欢

转载自my.oschina.net/u/1169457/blog/1595337