Spring RestTemplate超时时间

177

我希望为我的Web应用程序使用的REST服务设置连接超时时间。 我正在使用Spring的RestTemplate与我的服务进行通信。 我已经进行了一些研究,并且在我的应用程序xml中找到并使用了下面的xml(我认为是用来设置超时时间的)。 我正在使用Spring 3.0。

我也在这里看到了同样的问题Timeout configuration for spring webservices with RestTemplate,但是解决方案似乎不太简洁,我更喜欢通过Spring配置来设置超时值。

<bean id="RestOperations" class="org.springframework.web.client.RestTemplate">
    <constructor-arg>
    
      <bean class="org.springframework.http.client.CommonsClientHttpRequestFactory">
        <property name="readTimeout" value="${restURL.connectionTimeout}" />
      </bean>
    </constructor-arg>
</bean>

无论我将readTimeout设置为多少,我都会得到以下结果:

网络电缆已断开: 等待大约20秒钟并报告以下异常:

org.springframework.web.client.ResourceAccessException:I/O错误:无法连接到主机;嵌套异常为java.net.NoRouteToHostException:无法连接到主机

URL不正确,因此rest服务返回404: 等待大约10秒钟并报告以下异常:

org.springframework.web.client.HttpClientErrorException:404 Not Found

我的要求需要更短的超时时间,所以我需要能够更改这些超时时间。有什么想法吗?

非常感谢。

12个回答

234

对于Spring Boot >= 1.4

@Configuration
public class AppConfig
{
    @Bean
    public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder) 
    {
        return restTemplateBuilder
           .setConnectTimeout(...)
           .setReadTimeout(...)
           .build();
    }
}

对于 Spring Boot <= 1.3

@Configuration
public class AppConfig
{
    @Bean
    @ConfigurationProperties(prefix = "custom.rest.connection")
    public HttpComponentsClientHttpRequestFactory customHttpRequestFactory() 
    {
        return new HttpComponentsClientHttpRequestFactory();
    }

    @Bean
    public RestTemplate customRestTemplate()
    {
        return new RestTemplate(customHttpRequestFactory());
    }
}

然后在您的application.properties文件中进行设置。

custom.rest.connection.connection-request-timeout=...
custom.rest.connection.connect-timeout=...
custom.rest.connection.read-timeout=...

这是因为 HttpComponentsClientHttpRequestFactory 具有公共的 setter 方法 connectionRequestTimeout, connectTimeout, 和 readTimeout,而且 @ConfigurationProperties 会为您设置它们。


对于不使用 Spring Boot 的 Spring 4.1 或 Spring 5,请使用 @Configuration 代替 XML

@Configuration
public class AppConfig
{
    @Bean
    public RestTemplate customRestTemplate()
    {
        HttpComponentsClientHttpRequestFactory httpRequestFactory = new HttpComponentsClientHttpRequestFactory();
        httpRequestFactory.setConnectionRequestTimeout(...);
        httpRequestFactory.setConnectTimeout(...);
        httpRequestFactory.setReadTimeout(...);

        return new RestTemplate(httpRequestFactory);
    }
}

不错的例子!请在“Spring Boot”示例中删除奇怪的“new”语句。 - StasKolodyuk
14
请注意,在进行此配置后,RestTemplate将使用Apache Http Client(以设置超时)。 Apache Http Client连接池的默认maxPerRoute线程为5,最大总线程为10(httpClient-4.5.2)。在某些情况下,我们需要自己设置这些值(例如,我们需要连接许多主机并需要更多的连接)。 - bluearrow
2
请注意,在4.1.4.RELEASE之前,connectionRequestTimeout属性不可用。 - Taoufik Mohdit
我尝试在Spring Boot >= 2.1.8 上配置 Spring Boot >= 1.4,但没有成功。我按照这篇文章(http://zetcode.com/springboot/resttemplate/)进行了配置。 - Ângelo Polotto
@ÂngeloPolotto,您发布的链接给出了与此解决方案相同的建议。该文章指出:“或者,我们可以使用RestTemplateBuilder来完成这项工作。” - dustin.schultz
@dustin.schultz,默认值是什么? - Artanis Zeratul

82
我终于让它工作了。
我认为我们的项目有两个不同版本的commons-httpclient jar包并不是什么帮助。一旦我解决了这个问题,我发现你可以做两件事...
在代码中,你可以放置以下内容:
HttpComponentsClientHttpRequestFactory rf =
    (HttpComponentsClientHttpRequestFactory) restTemplate.getRequestFactory();
rf.setReadTimeout(1 * 1000);
rf.setConnectTimeout(1 * 1000);

第一次调用此代码时,它将设置由 RestTemplate 使用的 HttpComponentsClientHttpRequestFactory 的超时时间。因此, RestTemplate 进行的所有后续调用都将使用上述定义的超时设置。
或者更好的选择是这样做:
<bean id="RestOperations" class="org.springframework.web.client.RestTemplate">
    <constructor-arg>
        <bean class="org.springframework.http.client.HttpComponentsClientHttpRequestFactory">
            <property name="readTimeout" value="${application.urlReadTimeout}" />
            <property name="connectTimeout" value="${application.urlConnectionTimeout}" />
        </bean>
    </constructor-arg>
</bean>

我在代码中使用RestOperations接口,并从属性文件中获取超时值。


2
这将为通过此REST模板(单例)进行的所有调用设置超时时间。您知道是否可以针对每个请求控制超时时间吗?(例如:对于POST调用为10秒,对于GET调用为5秒等) - codesalsa
@sardo。当我在代码中使用RestOperations接口时,我们需要为此创建任何显式接口吗? - deadend
你说你正在使用Spring 3.0 - 我也卡在这里了 - 但是在3.0中没有HttpComponentsClientHttpRequestFactory!你更新了Spring吗? - Kutzi
6
以上代码在最新版本的Spring中无法正常工作。它会产生ClassCastException异常,提示java.lang.ClassCastException: org.springframework.http.client.InterceptingClientHttpRequestFactory无法转换为org.springframework.http.client.HttpComponentsClientHttpRequestFactory - comiventor
setReadTimeout 在 Spring 6+ 中已被弃用。 - Arun Gowda

67

这个问题是Spring Boot搜索的第一个链接,因此在此处放置官方文档推荐的解决方案会非常有帮助。Spring Boot有自己的便捷BeanRestTemplateBuilder

@Bean
public RestTemplate restTemplate(
        RestTemplateBuilder restTemplateBuilder) {

    return restTemplateBuilder
            .setConnectTimeout(Duration.ofSeconds(500))
            .setReadTimeout(Duration.ofSeconds(500))
            .build();
}

手动创建RestTemplate实例可能存在问题,因为自动配置的其他bean没有被注入到手动创建的实例中。


2
给像我这样的Spring新手的一条提示:仅将其放在@Configuration中是没有任何作用的。该方法要求您将此RestTemplate注入到某个地方,以将其用作RestTemplateXhrTransport构造函数的参数,然后将其添加到传递给SocksJSClient的Transports列表中。 - Key Lay
setConnectTimeout 和一些 setReadTimeout 的实现已经被弃用。 - skryvets
当您升级Spring Boot到更高版本(例如我的情况是v2.6.9)时,需要使用Duration.ofSeconds()。 - Abhilash Ramteke

33

这是我的个人见解。没有新内容,只有一些解释、改进和更新的代码。

RestTemplate 默认具有无限超时时间。它有两种超时:连接超时和读取超时。例如,我可以连接到服务器但无法读取数据。应用程序会挂起,你不知道发生了什么。

我将使用注释,这是现在比 XML 更受欢迎的方法。

@Configuration
public class AppConfig {

    @Bean
    public RestTemplate restTemplate() {

        var factory = new SimpleClientHttpRequestFactory();

        factory.setConnectTimeout(3000);
        factory.setReadTimeout(3000);

        return new RestTemplate(factory);
    }
}

在这里,我们使用 SimpleClientHttpRequestFactory 来设置连接和读取超时时间。然后将其传递给 RestTemplate 的构造函数。

@Configuration
public class AppConfig {

    @Bean
    public RestTemplate restTemplate(RestTemplateBuilder builder) {

        return builder
                .setConnectTimeout(Duration.ofMillis(3000))
                .setReadTimeout(Duration.ofMillis(3000))
                .build();
    }
}
在第二种解决方案中,我们使用RestTemplateBuilder。同时注意这两个方法的参数:它们使用Duration。直接使用毫秒的重载方法现在已经被弃用。 编辑 测试使用了Spring Boot 2.1.0和Java 11。

18

以下是一种非常简单的设置超时时间的方法:

RestTemplate restTemplate = new RestTemplate(getClientHttpRequestFactory());

private ClientHttpRequestFactory getClientHttpRequestFactory() {
    int timeout = 5000;
    HttpComponentsClientHttpRequestFactory clientHttpRequestFactory =
      new HttpComponentsClientHttpRequestFactory();
    clientHttpRequestFactory.setConnectTimeout(timeout);
    return clientHttpRequestFactory;
}

7
  1. 使用SimpleClientHttpRequestFactory设置RestTemplate的超时时间 为了在程序中覆盖超时属性,我们可以按照以下方式自定义SimpleClientHttpRequestFactory类。

使用SimpleClientHttpRequestFactory覆盖超时时间

//Create resttemplate
RestTemplate restTemplate = new RestTemplate(getClientHttpRequestFactory());

//Override timeouts in request factory
private SimpleClientHttpRequestFactory getClientHttpRequestFactory() 
{
    SimpleClientHttpRequestFactory clientHttpRequestFactory
                      = new SimpleClientHttpRequestFactory();
    //Connect timeout
    clientHttpRequestFactory.setConnectTimeout(10_000);

    //Read timeout
    clientHttpRequestFactory.setReadTimeout(10_000);
    return clientHttpRequestFactory;
}
  1. 使用HttpComponentsClientHttpRequestFactory设置RestTemplate超时时间 SimpleClientHttpRequestFactory可以帮助设置超时时间,但其功能非常有限,在实时应用中可能不足以满足需求。在生产代码中,我们可能需要使用支持HTTP Client库的HttpComponentsClientHttpRequestFactory。

HTTPClient提供其他有用的功能,如连接池、空闲连接管理等。

阅读更多:Spring RestTemplate + HttpClient配置示例

使用HttpComponentsClientHttpRequestFactory覆盖超时时间

//Create resttemplate
RestTemplate restTemplate = new RestTemplate(getClientHttpRequestFactory());

//Override timeouts in request factory
private SimpleClientHttpRequestFactory getClientHttpRequestFactory() 
{
    HttpComponentsClientHttpRequestFactory clientHttpRequestFactory
                      = new HttpComponentsClientHttpRequestFactory();
    //Connect timeout
    clientHttpRequestFactory.setConnectTimeout(10_000);

    //Read timeout
    clientHttpRequestFactory.setReadTimeout(10_000);
    return clientHttpRequestFactory;
}

参考链接:Spring RestTemplate超时配置示例


5

SpringBoot版本>2

为restTemplate设置简单的超时时间。我已将读取和写入超时时间设置为3秒。

@Bean
public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder){
        RestTemplate restTemplate= restTemplateBuilder.setConnectTimeout(Duration.ofMillis(3000)).setReadTimeout(Duration.ofMillis(3000)).build();
        return restTemplate;

}

如果您想设置动态超时值,请参考以下内容。

    //basic imports
    @SpringBootApplication
    public class DemoApplication{
    //the below values are injected from application.properties file
    @Value($"{connection.timeout.value}")
    private String connectionTimeoutValue;
    @Value("${read.timeout.value}")
    private String readTimeOutValue;
    
    public static void main(String args[]){
    
    SpringApplication.run(DemoApplication.class, args)
    }
    
        @Bean
        public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder){
                RestTemplate restTemplate= restTemplateBuilder.setConnectTimeout(Duration.ofMillis(connectionTimeoutValue)).setReadTimeout(Duration.ofMillis(readTimeOutValue)).build();
                return restTemplate;
        
        }
    
    }

在application.properties文件中:

#change the numerical values according to your need.
connection.timeout.value=3000
read.timeout.value=3000

注意:超时值以毫秒为单位。

对于SpringBoot版本<2: 请删除Duration.ofMillis并直接提供以下值...

RestTemplate restTemplate= restTemplateBuilder.setConnectTimeout(3000).setReadTimeout(3000).build();

在这种情况下如何动态设置每个调用的时长?@Santh - Bilgehan
@Bilgehan已更新了答案,请参考...... - Santh

0

在使用Apache的httpcomponents时,仅在RestTemplateBuilder中设置超时时间对我没有起作用。我还必须在RequestFactory中设置超时时间。

以下是整个代码:

public RestTemplate templateBuilder() {
    RestTemplate restTemplate = this.restTemplateBuilder
            .setConnectTimeout(Duration.ofSeconds(connectTimeout))
            .setReadTimeout(Duration.ofSeconds(readTimeout))
            .build();
    HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
    requestFactory.setConnectTimeout((int) connectTimeout * 1000);
    requestFactory.setReadTimeout((int) readTimeout * 1000);
    requestFactory.setConnectionRequestTimeout((int) connectTimeout * 1000);
    restTemplate.setRequestFactory(new BufferingClientHttpRequestFactory(requestFactory));
    return restTemplate;
}

0
private static RestTemplate restTemplate;

static {
    HttpComponentsClientHttpRequestFactory rf = new HttpComponentsClientHttpRequestFactory();
    rf.setReadTimeout(3 * 1000);
    rf.setConnectTimeout(2 * 1000);

    restTemplate = new RestTemplate(rf);
    restTemplate.getMessageConverters()
        .add(0, new StringHttpMessageConverter(StandardCharsets.UTF_8));
}

0
我曾经遇到过类似的情况,但还需要设置代理。我能想到最简单的方法是扩展 SimpleClientHttpRequestFactory,以方便设置代理(非生产环境和生产环境使用不同的代理)。即使您不需要代理,这种方法仍然有效。然后在我的扩展类中,我重写了 openConnection(URL url, Proxy proxy) 方法,使用与 source 相同的内容,只是在返回之前设置超时时间。
@Override
protected HttpURLConnection openConnection(URL url, Proxy proxy) throws IOException {
    URLConnection urlConnection = proxy != null ? url.openConnection(proxy) : url.openConnection();
    Assert.isInstanceOf(HttpURLConnection.class, urlConnection);
    urlConnection.setConnectTimeout(5000);
    urlConnection.setReadTimeout(5000);
    return (HttpURLConnection) urlConnection;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接