Tempo limite de descanso da mola

125

Gostaria de definir o tempo limite da conexão para um serviço de descanso usado pelo meu aplicativo da web. Estou usando o RestTemplate do Spring para conversar com meu serviço. Eu fiz algumas pesquisas e encontrei e usei o xml abaixo (no meu aplicativo xml), que acredito que deve definir o tempo limite. Estou usando o Spring 3.0.

Também vi o mesmo problema aqui Configuração de tempo limite para serviços da web do Spring com RestTemplate, mas as soluções não parecem tão limpas , eu prefiro definir os valores de tempo limite via 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>

Parece que, independentemente do que eu defina como readTimeout, recebo o seguinte:

Cabo de rede desconectado: aguarda cerca de 20 segundos e relata a seguinte exceção:

org.springframework.web.client.ResourceAccessException: Erro de E / S: Nenhuma rota para o host: conectar; exceção aninhada é java.net.NoRouteToHostException: nenhuma rota para o host: conectar

URL incorreto, para 404 retornado pelo serviço de repouso: Aguarda cerca de 10 segundos e relata a seguinte exceção:

org.springframework.web.client.HttpClientErrorException: 404 não encontrado

Meus requisitos exigem tempos limite mais curtos, portanto, preciso alterá-los. Alguma idéia do que estou fazendo de errado?

Muito Obrigado.

sardo
fonte

Respostas:

163

Para inicialização por mola> = 1.4

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

Para inicialização por mola <= 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());
    }
}

então no seu application.properties

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

Isso funciona porque HttpComponentsClientHttpRequestFactorytem setters públicos connectionRequestTimeout,connectTimeout e, readTimeoute @ConfigurationPropertiesdefine-los para você.


Para o Spring 4.1 ou Spring 5 sem Spring Boot usando em @Configurationvez 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
fonte
Belo exemplo! Por favor, remova newafirmações ímpares no Spring Bootexemplo
StasKolodyuk 21/10
7
Observe que, após essa configuração, o RestTemplate usará o cliente http apache (para definir o tempo limite). Os segmentos maxPerRoute padrão do pool de conexões do cliente http Apache são 5 e o total total de threads é 10 (httpClient-4.5.2). Precisamos definir isso sozinhos em algumas situações (como precisamos nos conectar a muitos hosts e precisar de mais conexões).
precisa
2
Observe que o connectionRequestTimeoutatributo não está disponível antes da versão 4.1.4
Taoufik Mohdit
Eu tentei a configuração do Spring Boot> = 1.4 no Spring Boot> = 2.1.8 e não tive sucesso. Eu segui este post ( zetcode.com/springboot/resttemplate ) para fazer essa configuração.
Ângelo Polotto
@ ÂngeloPolotto o link que você postou dá os mesmos conselhos que esta solução. O artigo diz: "Como alternativa, podemos usar o RestTemplateBuilder para fazer o trabalho".
dustin.schultz
76

Eu finalmente consegui isso funcionando.

Acho que o fato de nosso projeto ter duas versões diferentes do jar commons-httpclient não estava ajudando. Depois que resolvi isso, descobri que você pode fazer duas coisas ...

No código, você pode colocar o seguinte:

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

A primeira vez que esse código é chamado, ele definirá o tempo limite da HttpComponentsClientHttpRequestFactoryclasse usada pelo RestTemplate. Portanto, todas as chamadas subsequentes feitas por RestTemplateusarão as configurações de tempo limite definidas acima.

Ou a melhor opção é fazer isso:

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

Onde eu uso a RestOperationsinterface no meu código e obtenho os valores de tempo limite de um arquivo de propriedades.

sardo
fonte
Portanto, isso define o tempo limite para todas as chamadas através deste modelo de descanso (que é um singleton). Você sabe se é possível controlar os tempos limite por solicitação? (por exemplo: 10 seg para uma chamada posto e 5 segundos para uma chamada get etc)
codesalsa
@ sardo. Onde eu uso a interface RestOperations no meu código. precisamos criar alguma interface explícita para isso?
Deadend
Você disse que está usando o Spring 3.0 - com o qual também estou preso -, mas no 3.0 não há HttpComponentsClientHttpRequestFactory! Você atualizou o Spring?
Kutzi
5
O código acima não funciona na última primavera. Dá ClassCastExceptionjava.lang.ClassCastException: org.springframework.http.client.InterceptingClientHttpRequestFactory cannot be cast to org.springframework.http.client.HttpComponentsClientHttpRequestFactory
comiventor 22/02
40

Esta questão é o primeiro link para uma pesquisa Spring Boot, portanto, seria ótimo colocar aqui a solução recomendada na documentação oficial . O Spring Boot possui seu próprio bean de conveniência RestTemplateBuilder :

@Bean
public RestTemplate restTemplate(
        RestTemplateBuilder restTemplateBuilder) {

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

A criação manual de instâncias RestTemplate é uma abordagem potencialmente problemática, porque outros beans configurados automaticamente não estão sendo injetados em instâncias criadas manualmente.

holdev
fonte
2
Uma observação para os novatos do Spring como eu: apenas colocar isso em uma @Configuration não fará nada. Esse método requer que você tenha esse RestTemplate injetado em algum lugar que o use como argumento para o construtor de RestTemplateXhrTransport, que você adicionará à sua Lista de transportes que passa para o SocksJSClient.
Key Lay
setConnectTimeout e algumas implementações de setReadTimeout estão obsoletas
skryvets 10/07/19
17

Aqui estão os meus 2 centavos. Nada de novo, mas algumas explicações, melhorias e código mais recente.

Por padrão, RestTemplatetem tempo limite infinito. Existem dois tipos de tempos limite: tempo limite da conexão e tempo limite da leitura. Por exemplo, eu poderia me conectar ao servidor, mas não consegui ler os dados. O aplicativo estava suspenso e você não tem idéia do que está acontecendo.

Vou usar anotações, que hoje são preferidas ao XML.

@Configuration
public class AppConfig {

    @Bean
    public RestTemplate restTemplate() {

        var factory = new SimpleClientHttpRequestFactory();

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

        return new RestTemplate(factory);
    }
}

Aqui usamos SimpleClientHttpRequestFactorypara definir a conexão e ler tempos limite. É então passado para o construtor de RestTemplate.

@Configuration
public class AppConfig {

    @Bean
    public RestTemplate restTemplate(RestTemplateBuilder builder) {

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

Na segunda solução, usamos o RestTemplateBuilder. Observe também os parâmetros dos dois métodos: eles aceitam Duration. Os métodos sobrecarregados que levam diretamente milissegundos agora estão obsoletos.

Editar testado com Spring Boot 2.1.0 e Java 11.

Jan Bodnar
fonte
Qual versão de primavera e java você está usando?
orirab
2
Spring Boot 2.1.0 e Java 11. Para um exemplo de trabalho, você pode dar uma olhada no meu tutorial: zetcode.com/springboot/resttemplate
Jan Bodnar
Sugiro adicionando esta a resposta
orirab
Consulte github.com/spring-projects/spring-boot/blob/master/… . Foi adicionado no Spring Boot 2.1.0.
Jan Bodnar
Obrigado @JanBodnar, seu tutorial é o único que funcionou bem no meu Spring Boot 5.x #
Ângelo Polotto
15

Aqui está uma maneira realmente simples de definir o tempo limite:

RestTemplate restTemplate = new RestTemplate(getClientHttpRequestFactory());

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

Eu tive um cenário semelhante, mas também foi necessário definir um proxy. A maneira mais simples de ver isso foi estendendo a SimpleClientHttpRequestFactoryfacilidade de configuração do proxy (proxies diferentes para não-prod vs prod). Isso ainda deve funcionar, mesmo se você não precisar do proxy. Então, na minha classe estendida, substituo o openConnection(URL url, Proxy proxy)método, usando o mesmo da fonte , mas apenas definindo os tempos limite antes de retornar.

@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
fonte
0

Para expandir a resposta 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
fonte
0
  1. Tempo limite RestTemplate com SimpleClientHttpRequestFactory Para substituir programaticamente as propriedades de tempo limite, podemos personalizar a classe SimpleClientHttpRequestFactory como abaixo.

Substituir tempo limite com 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. Tempo limite do RestTemplate com HttpComponentsClientHttpRequestFactory SimpleClientHttpRequestFactory ajuda a definir o tempo limite, mas sua funcionalidade é muito limitada e pode não ser suficiente em aplicativos em tempo real. No código de produção, podemos querer usar HttpComponentsClientHttpRequestFactory, que suporta a biblioteca HTTP Client junto com resttemplate.

O HTTPClient fornece outros recursos úteis, como pool de conexões, gerenciamento de conexões inativas etc.

Leia mais: Exemplo de configuração Spring RestTemplate + HttpClient

Substituir tempo limite com 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;
}

Referência: Exemplo de Configuração de Tempo Limite do Spring RestTemplate

Zgpeace
fonte