Timeout di Spring RestTemplate


125

Vorrei impostare i timeout di connessione per un servizio di riposo utilizzato dalla mia applicazione web. Sto usando Spring's RestTemplate per parlare con il mio servizio. Ho fatto alcune ricerche e ho trovato e utilizzato l'xml di seguito (nella mia applicazione xml) che credo abbia lo scopo di impostare il timeout. Sto usando Spring 3.0.

Ho anche visto lo stesso problema qui Configurazione del timeout per i servizi web primaverili con RestTemplate ma le soluzioni non sembrano così pulite , preferirei impostare i valori di timeout tramite 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>

Sembra che qualunque cosa io abbia impostato come readTimeout ottengo quanto segue:

Cavo di rete scollegato: attende circa 20 secondi e segnala la seguente eccezione:

org.springframework.web.client.ResourceAccessException: errore I / O: nessuna route all'host: connessione; l'eccezione annidata è java.net.NoRouteToHostException: nessuna route all'host: connect

Url errato, quindi 404 restituito dal servizio di riposo: attende circa 10 secondi e segnala la seguente eccezione:

org.springframework.web.client.HttpClientErrorException: 404 non trovato

I miei requisiti richiedono timeout più brevi, quindi devo essere in grado di modificarli. Qualche idea su cosa sto sbagliando?

Grazie molto.

Risposte:


164

Per Spring Boot> = 1.4

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

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

poi nel tuo application.properties

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

Questo funziona perché HttpComponentsClientHttpRequestFactoryha setter pubbliche connectionRequestTimeout, connectTimeoute readTimeoute @ConfigurationPropertiesli mette per voi.


Per Spring 4.1 o Spring 5 senza Spring Boot utilizzando @Configurationinvece diXML

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

        return new RestTemplate(httpRequestFactory);
    }
}

Bell'esempio! Per favore, rimuovi la newfrase dispari Spring Bootnell'esempio
StasKolodyuk

7
Nota che dopo questa configurazione, RestTemplate utilizzerà il client http apache (per impostare il timeout). I thread maxPerRoute predefiniti del pool di connessioni del client http Apache è 5 e il numero massimo di thread totali è 10 (httpClient-4.5.2). Dobbiamo impostarlo noi stessi in alcune situazioni (ad esempio, dobbiamo connetterci a molti host e abbiamo bisogno di più connessioni).
bluearrow

2
Si prega di notare che l' connectionRequestTimeoutattributo non è disponibile prima del 4.1.4 RILASCIO
Taoufik Mohdit

Ho provato la configurazione per Spring Boot> = 1.4 su Spring Boot> = 2.1.8 e non ho avuto successo. Ho seguito questo post ( zetcode.com/springboot/resttemplate ) per effettuare quella configurazione.
Ângelo Polotto

@ ÂngeloPolotto il link che hai postato dà lo stesso consiglio di questa soluzione. L'articolo dice: "In alternativa, possiamo usare RestTemplateBuilder per fare il lavoro."
dustin.schultz

76

Finalmente ho funzionato.

Penso che il fatto che il nostro progetto avesse due diverse versioni del jar commons-httpclient non fosse d'aiuto. Una volta risolto il problema, ho scoperto che puoi fare due cose ...

Nel codice puoi inserire quanto segue:

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

La prima volta che viene chiamato questo codice, verrà impostato il timeout per la HttpComponentsClientHttpRequestFactoryclasse utilizzata da RestTemplate. Pertanto, tutte le chiamate successive effettuate da RestTemplateutilizzeranno le impostazioni di timeout definite sopra.

O l'opzione migliore è fare questo:

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

Dove utilizzo l' RestOperationsinterfaccia nel mio codice e ottengo i valori di timeout da un file delle proprietà.


Quindi questo imposta i timeout per tutte le chiamate attraverso questo modello di riposo (che è un singleton). Sai se è possibile controllare i timeout per richiesta? (es: 10 sec per una chiamata post e 5 sec per una chiamata ecc.)
codesalsa

@ sardo. Dove utilizzo l'interfaccia RestOperations nel mio codice. abbiamo bisogno di creare un'interfaccia esplicita per questo?
deadend

Hai detto che stai usando Spring 3.0 - con cui sono anche bloccato - ma nella 3.0 non c'è HttpComponentsClientHttpRequestFactory! Hai aggiornato la primavera?
Kutzi

5
Il codice sopra non funziona nell'ultima primavera. Dà ClassCastExceptionjava.lang.ClassCastException: org.springframework.http.client.InterceptingClientHttpRequestFactory cannot be cast to org.springframework.http.client.HttpComponentsClientHttpRequestFactory
comiventor

40

Questa domanda è il primo link per una ricerca in Spring Boot, quindi sarebbe ottimo mettere qui la soluzione consigliata nella documentazione ufficiale . Spring Boot ha il suo comodo bean RestTemplateBuilder :

@Bean
public RestTemplate restTemplate(
        RestTemplateBuilder restTemplateBuilder) {

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

La creazione manuale di istanze RestTemplate è un approccio potenzialmente problematico perché altri bean configurati automaticamente non vengono iniettati in istanze create manualmente.


2
Una nota per i nuovi arrivati ​​primaverili come me: semplicemente inserirli in una @Configuration non farà nulla. Questo metodo richiede che tu abbia questo RestTemplate iniettato da qualche parte che lo utilizza come argomento per il costruttore di RestTemplateXhrTransport che a sua volta aggiungerai al tuo elenco di trasporti che passerai al tuo SocksJSClient.
Key Lay

setConnectTimeoute alcune implementazioni di setReadTimeoutsono deprecate
skryvets

17

Ecco i miei 2 centesimi. Niente di nuovo, ma alcune spiegazioni, miglioramenti e codice più recente.

Per impostazione predefinita, RestTemplateha un timeout infinito. Esistono due tipi di timeout: timeout di connessione e timeout di lettura. Ad esempio, potrei connettermi al server ma non leggere i dati. L'applicazione era in sospeso e non hai idea di cosa stia succedendo.

Userò le annotazioni, che oggigiorno sono preferite rispetto a XML.

@Configuration
public class AppConfig {

    @Bean
    public RestTemplate restTemplate() {

        var factory = new SimpleClientHttpRequestFactory();

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

        return new RestTemplate(factory);
    }
}

Qui usiamo SimpleClientHttpRequestFactoryper impostare la connessione e leggere i timeout. Viene quindi passato al costruttore di RestTemplate.

@Configuration
public class AppConfig {

    @Bean
    public RestTemplate restTemplate(RestTemplateBuilder builder) {

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

Nella seconda soluzione, usiamo il RestTemplateBuilder. Notare anche i parametri dei due metodi: prendono Duration. I metodi sovraccaricati che richiedono direttamente millisecondi sono ora deprecati.

Modifica testato con Spring Boot 2.1.0 e Java 11.


Quale versione primaverile e Java stai usando?
orirab

2
Spring Boot 2.1.0 e Java 11. Per un esempio funzionante, puoi dare un'occhiata al mio tutorial: zetcode.com/springboot/resttemplate
Jan Bodnar

Suggerisco di aggiungere questo alla risposta
orirab

Vedi github.com/spring-projects/spring-boot/blob/master/… . È stato aggiunto in Spring Boot 2.1.0.
Jan Bodnar

Grazie @JanBodnar, il tuo tutorial è l'unico che ha funzionato bene sul mio Spring Boot 5.x
Ângelo Polotto

15

Ecco un modo davvero semplice per impostare il timeout:

RestTemplate restTemplate = new RestTemplate(getClientHttpRequestFactory());

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

0

Avevo uno scenario simile, ma era anche necessario impostare un proxy. Il modo più semplice che ho potuto vedere per farlo era estendere il SimpleClientHttpRequestFactoryper la facilità di impostazione del proxy (proxy diversi per non-prod vs prod). Questo dovrebbe comunque funzionare anche se non hai bisogno del proxy. Quindi nella mia classe estesa sovrascrivo il openConnection(URL url, Proxy proxy)metodo, usando lo stesso del sorgente , ma impostando solo i timeout prima di tornare.

@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;
}

0

Per espandere la risposta di 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);
}

0
  1. RestTemplate timeout con SimpleClientHttpRequestFactory Per ignorare a livello di codice le proprietà di timeout, è possibile personalizzare la classe SimpleClientHttpRequestFactory come di seguito.

Eseguire l'override del timeout 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 aiuta a impostare il timeout ma è molto limitato nella funzionalità e potrebbe non essere sufficiente nelle applicazioni in tempo reale. Nel codice di produzione, potremmo voler utilizzare HttpComponentsClientHttpRequestFactory che supporta la libreria client HTTP insieme a resttemplate.

HTTPClient fornisce altre utili funzionalità come pool di connessioni, gestione delle connessioni inattive, ecc.

Ulteriori informazioni: Spring RestTemplate + esempio di configurazione HttpClient

Eseguire l'override del timeout 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;
}

riferimento: esempio di configurazione del timeout di Spring RestTemplate

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.