Spring RestTemplate tiempo de espera

125

Me gustaría establecer los tiempos de espera de conexión para un servicio de descanso utilizado por mi aplicación web. Estoy usando RestTemplate de Spring para hablar con mi servicio. Investigué un poco y encontré y usé el xml a continuación (en mi aplicación xml) que creo que está destinado a establecer el tiempo de espera. Estoy usando Spring 3.0.

También he visto el mismo problema aquí Configuración de tiempo de espera para servicios web de primavera con RestTemplate, pero las soluciones no parecen tan limpias , preferiría establecer los valores de tiempo de espera a través de la configuración de 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>

Parece que sea lo que sea que establezca readTimeout, obtengo lo siguiente:

Cable de red desconectado: espera unos 20 segundos e informa la siguiente excepción:

org.springframework.web.client.ResourceAccessExcepción: Error de E / S: No hay ruta al host: conectar; La excepción anidada es java.net.NoRouteToHostException: No hay ruta al host: conectar

Url incorrecta, por lo que 404 devuelto por el servicio de descanso: espera unos 10 segundos e informa la siguiente excepción:

org.springframework.web.client.HttpClientErrorException: 404 no encontrado

Mis requisitos requieren tiempos de espera más cortos, por lo que debo poder cambiarlos. ¿Alguna idea de lo que estoy haciendo mal?

Muchas gracias.

sardo
fuente

Respuestas:

164

Para Spring Boot> = 1.4

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

Para 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());
    }
}

entonces en tu application.properties

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

Esto funciona porque HttpComponentsClientHttpRequestFactorytiene los emisores públicos connectionRequestTimeout, connectTimeouty readTimeouty @ConfigurationPropertiesconjuntos para usted.


Para Spring 4.1 o Spring 5 sin Spring Boot usando en @Configurationlugar deXML

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

        return new RestTemplate(httpRequestFactory);
    }
}
dustin.schultz
fuente
Buen ejemplo! Por favor, elimine la newdeclaración extraña en el Spring Bootejemplo
StasKolodyuk
7
Tenga en cuenta que después de esta configuración, RestTemplate utilizará el cliente http de Apache (para establecer el tiempo de espera). Los subprocesos maxPerRoute predeterminados del grupo de conexiones del cliente http de Apache es 5, y el número máximo de subprocesos es 10 (httpClient-4.5.2). Necesitamos configurar esto nosotros mismos en algunas situaciones (como cuando necesitamos conectarnos a muchos hosts y necesitamos más conexiones).
bluearrow
2
Tenga en cuenta que el connectionRequestTimeoutatributo no está disponible antes del 4.1.4
Taoufik Mohdit
Probé la configuración para Spring Boot> = 1.4 en Spring Boot> = 2.1.8 y no tuve éxito. Seguí esta publicación ( zetcode.com/springboot/resttemplate ) para hacer esa configuración.
Ângelo Polotto
@ ÂngeloPolotto, el enlace que publicó ofrece el mismo consejo que esta solución. El artículo dice: "Alternativamente, podemos usar RestTemplateBuilder para hacer el trabajo".
dustin.schultz
76

Finalmente conseguí que esto funcionara.

Creo que el hecho de que nuestro proyecto tuviera dos versiones diferentes del jar commons-httpclient no estaba ayudando. Una vez que resolví eso, descubrí que puedes hacer dos cosas ...

En el código puedes poner lo siguiente:

HttpComponentsClientHttpRequestFactory rf =
    (HttpComponentsClientHttpRequestFactory) restTemplate.getRequestFactory();
rf.setReadTimeout(1 * 1000);
rf.setConnectTimeout(1 * 1000);

La primera vez que se llama a este código, se establecerá el tiempo de espera para la HttpComponentsClientHttpRequestFactoryclase utilizada por RestTemplate. Por lo tanto, todas las llamadas posteriores realizadas por RestTemplateutilizarán la configuración de tiempo de espera definida anteriormente.

O la mejor opción es hacer esto:

<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>

Donde uso la RestOperationsinterfaz en mi código y obtengo los valores de tiempo de espera de un archivo de propiedades.

sardo
fuente
Entonces, esto establece los tiempos de espera para todas las llamadas a través de esta plantilla de descanso (que es un singleton). ¿Sabes si es posible controlar los tiempos de espera por solicitud? (por ejemplo: 10 segundos para una llamada posterior y 5 segundos para una llamada de
recepción,
@ sardo. Donde uso la interfaz RestOperations en mi código. ¿Necesitamos crear alguna interfaz explícita para esto?
muerto
Dijiste que estás usando Spring 3.0, con lo que también estoy atrapado, ¡pero en 3.0 no hay HttpComponentsClientHttpRequestFactory! ¿Actualizaste Spring?
Kutzi
5
El código anterior no funciona en la última versión de Spring. Da ClassCastExceptionjava.lang.ClassCastException: org.springframework.http.client.InterceptingClientHttpRequestFactory cannot be cast to org.springframework.http.client.HttpComponentsClientHttpRequestFactory
comiventor
40

Esta pregunta es el primer enlace para una búsqueda de Spring Boot, por lo tanto, sería genial poner aquí la solución recomendada en la documentación oficial . Spring Boot tiene su propio bean RestTemplateBuilder de conveniencia :

@Bean
public RestTemplate restTemplate(
        RestTemplateBuilder restTemplateBuilder) {

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

La creación manual de instancias de RestTemplate es un enfoque potencialmente problemático porque otros beans configurados automáticamente no se inyectan en instancias creadas manualmente.

Heldev
fuente
2
Una nota para los recién llegados de Spring como yo: simplemente pegar esto en una @Configuration no hará nada. Este método requiere que tenga esta RestTemplate inyectada en algún lugar que la use como argumento para el constructor de RestTemplateXhrTransport, que a su vez agregará a su Lista de transportes que pasará a su SocksJSClient.
Key Lay
setConnectTimeouty algunas implementaciones de setReadTimeoutestán en desuso
skryvets
17

Aquí están mis 2 centavos. Nada nuevo, pero algunas explicaciones, mejoras y código más nuevo.

De forma predeterminada, RestTemplatetiene un tiempo de espera infinito. Hay dos tipos de tiempos de espera: tiempo de espera de conexión y tiempo de espera de lectura. Por ejemplo, podía conectarme al servidor pero no podía leer los datos. La aplicación se colgó y no tienes ni idea de lo que está pasando.

Voy a usar anotaciones, que en estos días se prefieren a XML.

@Configuration
public class AppConfig {

    @Bean
    public RestTemplate restTemplate() {

        var factory = new SimpleClientHttpRequestFactory();

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

        return new RestTemplate(factory);
    }
}

Aquí usamos SimpleClientHttpRequestFactorypara configurar la conexión y leer los tiempos de espera. Luego se pasa al constructor de RestTemplate.

@Configuration
public class AppConfig {

    @Bean
    public RestTemplate restTemplate(RestTemplateBuilder builder) {

        return builder
                .setConnectTimeout(Duration.ofMillis(3000))
                .setReadTimeout(Duration.ofMillis(3000))
                .build();
    }
}

En la segunda solución, usamos RestTemplateBuilder. También observe los parámetros de los dos métodos: toman Duration. Los métodos sobrecargados que toman directamente milisegundos ahora están en desuso.

Edición Probado con Spring Boot 2.1.0 y Java 11.

Jan Bodnar
fuente
¿Qué versión de Spring y Java estás usando?
orirab
2
Spring Boot 2.1.0 y Java 11. Para ver un ejemplo de trabajo, puede echar un vistazo a mi tutorial: zetcode.com/springboot/resttemplate
Jan Bodnar
Sugiero agregar esto a la respuesta
orirab
Consulte github.com/spring-projects/spring-boot/blob/master/… . Se agregó en Spring Boot 2.1.0.
Jan Bodnar
Gracias @JanBodnar, tu tutorial es el único que funcionó bien en mi Spring Boot 5.x
Ângelo Polotto
15

Aquí hay una forma realmente sencilla de establecer el tiempo de espera:

RestTemplate restTemplate = new RestTemplate(getClientHttpRequestFactory());

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

Tuve un escenario similar, pero también tuve que configurar un Proxy. La forma más sencilla que pude ver para hacer esto fue extender el SimpleClientHttpRequestFactorypara facilitar la configuración del proxy (diferentes proxies para non-prod vs prod). Sin embargo, esto debería funcionar incluso si no necesita el proxy. Luego, en mi clase extendida, anulo el openConnection(URL url, Proxy proxy)método, usando lo mismo que la fuente , pero solo estableciendo los tiempos de espera antes de regresar.

@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;
}
Ryan D
fuente
0

Para ampliar la respuesta de benscabbia :

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);
}
Tasos Zervos
fuente
0
  1. RestTemplate timeout con SimpleClientHttpRequestFactory Para anular programáticamente las propiedades de tiempo de espera, podemos personalizar la clase SimpleClientHttpRequestFactory como se muestra a continuación.

Anular el tiempo de espera con 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. RestTemplate timeout con HttpComponentsClientHttpRequestFactory SimpleClientHttpRequestFactory ayuda a configurar el tiempo de espera, pero tiene una funcionalidad muy limitada y puede que no sea suficiente en aplicaciones en tiempo real. En el código de producción, es posible que queramos utilizar HttpComponentsClientHttpRequestFactory que admite la biblioteca de cliente HTTP junto con resttemplate.

HTTPClient proporciona otras funciones útiles como grupo de conexiones, gestión de conexiones inactivas, etc.

Leer más: Ejemplo de configuración Spring RestTemplate + HttpClient

Anular el tiempo de espera con 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;
}

referencia: Ejemplo de configuración de tiempo de espera de Spring RestTemplate

Zgpeace
fuente