Spring RestTemplate 超时

Posted

技术标签:

【中文标题】Spring RestTemplate 超时【英文标题】:Spring RestTemplate timeout 【发布时间】:2012-11-29 23:52:49 【问题描述】:

我想为我的 Web 应用程序使用的休息服务设置连接超时。我正在使用 Spring 的 RestTemplate 与我的服务交谈。我做了一些研究,发现并使用了下面的 xml(在我的应用程序 xml 中),我相信它是为了设置超时。我正在使用 Spring 3.0。

我在这里Timeout configuration for spring webservices with RestTemplate 也看到了同样的问题,但解决方案似乎并不干净,我更愿意通过 Spring config 设置超时值

<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 error: No route to host: connect;嵌套异常是 java.net.NoRouteToHostException: No route to host: connect

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

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

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

非常感谢。

【问题讨论】:

【参考方案1】:
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));

【讨论】:

【参考方案2】:
    使用 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;

    使用 HttpComponentsClientHttpRequestFactory 的 RestTemplate 超时 SimpleClientHttpRequestFactory 有助于设置超时,但它的功能非常有限,在实时应用程序中可能不够用。在生产代码中,我们可能希望使用支持 HTTP 客户端库的 HttpComponentsClientHttpRequestFactory 以及 resttemplate。

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

阅读更多:Spring RestTemplate + HttpClient configuration example

使用 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 timeout configuration example

【讨论】:

【参考方案3】:

扩展benscabbia's答案:

private RestTemplate restCaller = new RestTemplate(getClientHttpRequestFactory());

private ClientHttpRequestFactory getClientHttpRequestFactory() 
    int connectionTimeout = 5000; // milliseconds
    int socketTimeout = 10000; // milliseconds
    RequestConfig config = RequestConfig.custom()
      .setConnectTimeout(connectionTimeout)
      .setConnectionRequestTimeout(connectionTimeout)
      .setSocketTimeout(socketTimeout)
      .build();
    CloseableHttpClient client = HttpClientBuilder
      .create()
      .setDefaultRequestConfig(config)
      .build();
    return new HttpComponentsClientHttpRequestFactory(client);

【讨论】:

【参考方案4】:

这个问题是 Spring Boot 搜索的第一个链接,因此,最好将solution recommended in the official documentation 放在这里。 Spring Boot有自己的便利豆RestTemplateBuilder:

@Bean
public RestTemplate restTemplate(
        RestTemplateBuilder restTemplateBuilder) 

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

手动创建 RestTemplate 实例是一种潜在的麻烦方法,因为其他自动配置的 bean 没有被注入到手动创建的实例中。

【讨论】:

给像我这样的 Spring 新手的注意事项:仅将其粘贴在 @Configuration 中不会做任何事情。此方法要求您将这个 RestTemplate 注入 somwhere,将其用作 RestTemplateXhrTransport 的构造函数的参数,然后将其添加到传递给 SocksJSClient 的传输列表中。 setConnectTimeoutsetReadTimeout 的一些实现已弃用【参考方案5】:

这是我的 2 美分。没什么新东西,但有一些解释、改进和更新的代码。

默认情况下,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 测试。

【讨论】:

【参考方案6】:

对于 Spring Boot >= 1.4

@Configuration
public class AppConfig

    @Bean
    public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder) 
    
        return restTemplateBuilder
           .setConnectTimeout(...)
           .setReadTimeout(...)
           .build();
    


对于 Spring Boot

@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 有公共设置器 connectionRequestTimeoutconnectTimeoutreadTimeout@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 语句 请注意,在此配置后,RestTemplate 将使用 apache http 客户端(设置超时)。 Apache http 客户端连接池的默认 maxPerRoute 线程数为 5,最大总线程数为 10 (httpClient-4.5.2)。在某些情况下我们需要自己设置(比如我们需要连接很多主机,需要更多的连接)。 请注意 connectionRequestTimeout 属性在 4.1.4.RELEASE 之前不可用 我在 Spring Boot >= 2.1.8 上尝试了 Spring Boot >= 1.4 的配置,但没有成功。我按照这篇文章 (zetcode.com/springboot/resttemplate) 进行了配置。 @ÂngeloPolotto 您发布的链接提供了与此解决方案相同的建议。文章说:“或者,我们可以使用 RestTemplateBuilder 来完成这项工作。”【参考方案7】:

我有类似的情况,但也需要设置代理。我能看到的最简单的方法是扩展 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;

【讨论】:

【参考方案8】:

这是设置超时的一个非常简单的方法:

RestTemplate restTemplate = new RestTemplate(getClientHttpRequestFactory());

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

【讨论】:

【参考方案9】:

我终于搞定了。

我认为我们的项目有两个不同版本的 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 接口并从属性文件中获取超时值。

【讨论】:

所以这设置了通过这个休息模板(这是一个单例)的所有调用的超时时间。您知道是否可以控制每个请求的超时时间? (例如:post call 10 秒,get call 等 5 秒) @sardo。我在代码中使用 RestOperations 接口的地方。我们需要为此创建任何显式接口吗? 您说您使用的是 Spring 3.0——我也坚持使用它——但在 3.0 中没有 HttpComponentsClientHttpRequestFactory!你更新 Spring 了吗? 以上代码在最新的 Spring 中不起作用。它给 ClassCastException java.lang.ClassCastException: org.springframework.http.client.InterceptingClientHttpRequestFactory cannot be cast to org.springframework.http.client.HttpComponentsClientHttpRequestFactory

以上是关于Spring RestTemplate 超时的主要内容,如果未能解决你的问题,请参考以下文章

Spring RestTemplate 连接超时不起作用

Spring Cloud常用组件超时总结

设置RestTemplate的读取超时

spring RestTemplate提交json格式数据

(030)Spring Boot之RestTemplate访问web服务案例

由于未绑定的 RestTemplate,Spring-Boot RestClientTest 无法正确自动配置 MockRestServiceServer