Limit czasu Spring RestTemplate

125

Chciałbym ustawić limity czasu połączenia dla usługi odpoczynku używanej przez moją aplikację internetową. Używam Spring's RestTemplate do rozmowy z moją usługą. Zrobiłem kilka badań i znalazłem i użyłem XML poniżej (w mojej aplikacji xml), który moim zdaniem ma na celu ustawienie limitu czasu. Używam Spring 3.0.

Widziałem również ten sam problem tutaj Konfiguracja limitu czasu dla sprężynowych usług sieciowych z RestTemplate, ale rozwiązania nie wydają się tak czyste , wolałbym ustawić wartości limitu czasu za pomocą konfiguracji Springa

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

Wygląda na to, że niezależnie od ustawienia readTimeout na otrzymuję następujące informacje:

Odłączony kabel sieciowy: czeka około 20 sekund i zgłasza następujący wyjątek:

org.springframework.web.client.ResourceAccessExcepcja: Błąd we / wy: brak trasy do hosta: połącz; zagnieżdżony wyjątek to java.net.NoRouteToHostException: Brak trasy do hosta: connect

Adres URL jest niepoprawny, więc 404 zwrócony przez usługę odpoczynku: czeka około 10 sekund i zgłasza następujący wyjątek:

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

Moje wymagania wymagają krótszych limitów czasu, więc muszę mieć możliwość ich zmiany. Jakieś pomysły, co robię źle?

Wielkie dzięki.

sardo
źródło

Odpowiedzi:

164

W przypadku rozruchu wiosennego> = 1,4

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

W przypadku rozruchu wiosennego <= 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());
    }
}

następnie w twoim application.properties

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

To działa, ponieważ HttpComponentsClientHttpRequestFactoryma ustawiające publicznych connectionRequestTimeout, connectTimeoutoraz readTimeouti @ConfigurationPropertiesustawia je dla Ciebie.


Dla Spring 4.1 lub Spring 5 bez Spring Boot użyj @ConfigurationzamiastXML

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

        return new RestTemplate(httpRequestFactory);
    }
}
dustin.schultz
źródło
Niezły przykład! Proszę usunąć dziwne newstwierdzenie w Spring Bootprzykładzie
StasKolodyuk
7
Zauważ, że po tej konfiguracji RestTemplate użyje klienta apache http (aby ustawić limit czasu). Domyślna liczba wątków maxPerRoute w puli połączeń klienta HTTP Apache to 5, a maksymalna łączna liczba wątków to 10 (httpClient-4.5.2). W niektórych sytuacjach musimy to ustawić samodzielnie (np. Potrzebujemy połączenia z wieloma hostami i więcej połączeń).
bluearrow
2
Uwaga: connectionRequestTimeoutatrybut nie jest dostępny przed 4.1.4. WYDANIE
Taoufik Mohdit
Wypróbowałem konfigurację dla Spring Boot> = 1.4 na Spring Boot> = 2.1.8 i nie powiodło się. Śledziłem ten post ( zetcode.com/springboot/resttemplate ), aby dokonać tej konfiguracji.
Ângelo Polotto
@ ÂngeloPolotto łącze, które zamieściłeś, zawiera te same rady, co to rozwiązanie. Artykuł mówi: „Alternatywnie możemy użyć RestTemplateBuilder do wykonania tej pracy”.
dustin.schultz
76

W końcu udało mi się to.

Myślę, że fakt, że nasz projekt miał dwie różne wersje pliku commons-httpclient jar, nie pomagał. Kiedy to załatwiłem, odkryłem, że możesz zrobić dwie rzeczy ...

W kodzie możesz umieścić:

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

Pierwsze wywołanie tego kodu spowoduje ustawienie limitu czasu dla HttpComponentsClientHttpRequestFactoryklasy używanej przez RestTemplate. Dlatego wszystkie kolejne połączenia wykonywane przez użytkownika RestTemplatebędą korzystać z ustawień limitu czasu zdefiniowanych powyżej.

Lub lepszą opcją jest zrobienie tego:

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

Gdzie używam RestOperationsinterfejsu w moim kodzie i pobieram wartości limitu czasu z pliku właściwości.

sardo
źródło
Więc to ustawia limity czasu dla wszystkich wywołań przez ten szablon odpoczynku (który jest singletonem). Czy wiesz, czy można kontrolować limity czasu na żądanie? (np .: 10 sekund dla połączenia pocztowego i 5 sekund dla
odebrania
@ sardo. Gdzie używam interfejsu RestOperations w moim kodzie. musimy stworzyć do tego jakiś wyraźny interfejs?
koniec
Powiedziałeś, że używasz Spring 3.0 - z którym też utknąłem - ale w 3.0 nie ma HttpComponentsClientHttpRequestFactory! Czy zaktualizowałeś Spring?
Kutzi
5
Powyższy kod nie działa w najnowszej wersji Spring. Daje ClassCastExceptionjava.lang.ClassCastException: org.springframework.http.client.InterceptingClientHttpRequestFactory cannot be cast to org.springframework.http.client.HttpComponentsClientHttpRequestFactory
comiventor
41

To pytanie jest pierwszym linkiem do wyszukiwania Spring Boot, dlatego wspaniale byłoby umieścić tutaj rozwiązanie zalecane w oficjalnej dokumentacji . Spring Boot ma własną wygodną fasolkę RestTemplateBuilder :

@Bean
public RestTemplate restTemplate(
        RestTemplateBuilder restTemplateBuilder) {

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

Ręczne tworzenie instancji RestTemplate jest potencjalnie kłopotliwym podejściem, ponieważ inne automatycznie skonfigurowane komponenty bean nie są wprowadzane w ręcznie utworzonych instancjach.

holdev
źródło
2
Uwaga dla nowych użytkowników Spring, takich jak ja: samo umieszczenie tego w @Configuration nic nie da. Ta metoda wymaga, abyś miał ten RestTemplate wstrzyknięty gdzieś, gdzie używa go jako argumentu do konstruktora RestTemplateXhrTransport, który z kolei dodasz do swojej listy transportów, którą przekażesz do swojego SocksJSClient.
Key Lay
setConnectTimeouta niektóre implementacje setReadTimeoutsą przestarzałe
skryvets,
17

Oto moje 2 centy. Nic nowego, ale kilka wyjaśnień, ulepszeń i nowszy kod.

Domyślnie RestTemplatema nieskończony limit czasu. Istnieją dwa rodzaje limitów czasu: limit czasu połączenia i limit czasu odczytu. Na przykład mogłem połączyć się z serwerem, ale nie mogłem odczytać danych. Aplikacja się zawiesiła i nie masz pojęcia, co się dzieje.

Zamierzam użyć adnotacji, które obecnie są preferowane nad XML.

@Configuration
public class AppConfig {

    @Bean
    public RestTemplate restTemplate() {

        var factory = new SimpleClientHttpRequestFactory();

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

        return new RestTemplate(factory);
    }
}

Tutaj używamy SimpleClientHttpRequestFactorydo ustawiania połączenia i odczytywania limitów czasu. Następnie jest przekazywany do konstruktora RestTemplate.

@Configuration
public class AppConfig {

    @Bean
    public RestTemplate restTemplate(RestTemplateBuilder builder) {

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

W drugim rozwiązaniu używamy RestTemplateBuilder. Zwróć także uwagę na parametry dwóch metod: przyjmują Duration. Przeciążone metody, które zajmują bezpośrednio milisekundy, są teraz przestarzałe.

Edycja testowana za pomocą Spring Boot 2.1.0 i Java 11.

Jan Bodnar
źródło
Jakiej wersji Spring i Java używasz?
orirab
2
Spring Boot 2.1.0 i Java 11. Działający przykład można znaleźć w moim samouczku: zetcode.com/springboot/resttemplate
Jan Bodnar,
Proponuję dodać to do odpowiedzi
orirab
Zobacz github.com/spring-projects/spring-boot/blob/master/… . Został dodany w Spring Boot 2.1.0.
Jan Bodnar
Dziękuję @JanBodnar, Twój samouczek jest jedynym, który działał dobrze na moim Spring Boot 5.x
Ângelo Polotto
15

Oto naprawdę prosty sposób na ustawienie limitu czasu:

RestTemplate restTemplate = new RestTemplate(getClientHttpRequestFactory());

private ClientHttpRequestFactory getClientHttpRequestFactory() {
    int timeout = 5000;
    HttpComponentsClientHttpRequestFactory clientHttpRequestFactory =
      new HttpComponentsClientHttpRequestFactory();
    clientHttpRequestFactory.setConnectTimeout(timeout);
    return clientHttpRequestFactory;
}
benscabbia
źródło
0

Miałem podobny scenariusz, ale musiałem też ustawić proxy. Najprostszym sposobem, w jaki mogłem to zrobić, było rozszerzenie SimpleClientHttpRequestFactorydla ułatwienia ustawiania proxy (różne proxy dla non-prod vs prod). To powinno nadal działać, nawet jeśli nie potrzebujesz serwera proxy. Następnie w mojej rozszerzonej klasie nadpisuję openConnection(URL url, Proxy proxy)metodę, używając tego samego co źródło , ale tylko ustawiając limity czasu przed powrotem.

@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.
źródło
0

Aby rozwinąć odpowiedź 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
źródło
0
  1. Limit czasu RestTemplate z SimpleClientHttpRequestFactory Aby programowo zastąpić właściwości limitu czasu, możemy dostosować klasę SimpleClientHttpRequestFactory, jak poniżej.

Zastąp limit czasu za pomocą 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. Limit czasu RestTemplate z HttpComponentsClientHttpRequestFactory SimpleClientHttpRequestFactory pomaga w ustawieniu limitu czasu, ale ma bardzo ograniczoną funkcjonalność i może okazać się niewystarczający w aplikacjach czasu rzeczywistego. W kodzie produkcyjnym możemy chcieć użyć HttpComponentsClientHttpRequestFactory, które obsługują bibliotekę klienta HTTP wraz z resttemplate.

HTTPClient zapewnia inne przydatne funkcje, takie jak pula połączeń, zarządzanie bezczynnymi połączeniami itp.

Przeczytaj więcej: Przykład konfiguracji Spring RestTemplate + HttpClient

Zastąp limit czasu za pomocą 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;
}

Reference: Przykład konfiguracji limitu czasu Spring RestTemplate

Zgpeace
źródło