web-dev-qa-db-fra.com

Délai d'attente Spring RestTemplate

Je souhaite définir les délais de connexion pour un service de repos utilisé par mon application Web. J'utilise Spring's RestTemplate pour parler à mon service. J'ai fait des recherches et j'ai trouvé et utilisé le XML ci-dessous (dans mon application XML) qui, je crois, est destiné à définir le délai d'attente. J'utilise Spring 3.0.

J'ai également vu le même problème ici Configuration du délai d'attente pour les services Web Spring avec RestTemplate mais les solutions ne semblent pas être que clean, je préférerais définir les valeurs de délai d'attente 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>

Il semble que quoi que je définisse le readTimeout, j'obtienne ce qui suit:

Câble réseau déconnecté: Attend environ 20 secondes et signale l'exception suivante:

org.springframework.web.client.ResourceAccessException: erreur d'E/S: aucune route vers l'hôte: connect; L'exception imbriquée est Java.net.NoRouteToHostException: aucune route vers l'hôte: connect

Url incorrect alors 404 renvoyé par le service de repos: Attend environ 10 secondes et signale l'exception suivante:

org.springframework.web.client.HttpClientErrorException: 404 Introuvable

Mes besoins nécessitent des délais d'attente plus courts et je dois donc pouvoir les modifier. Des idées sur ce que je fais mal?

Merci beaucoup. 

79
sardo

J'ai finalement eu ce travail.

Je pense que le fait que notre projet ait deux versions différentes du fichier jar commons-httpclient n'aidait pas. Une fois que j'ai résolu ce problème, j'ai découvert que vous pouviez faire deux choses ...

Dans le code, vous pouvez mettre ce qui suit:

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

La première fois que ce code est appelé, il définit le délai d'expiration de la classe HttpComponentsClientHttpRequestFactory utilisée par la variable RestTemplate. Par conséquent, tous les appels ultérieurs passés par RestTemplate utiliseront les paramètres de délai d'attente définis ci-dessus.

Ou la meilleure option est de faire ceci:

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

Où j'utilise l'interface RestOperations dans mon code et récupère les valeurs de délai d'attente à partir d'un fichier de propriétés.

65
sardo

Pour Spring Boot> = 1.4

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

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

puis dans votre application.properties

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

Cela fonctionne parce que HttpComponentsClientHttpRequestFactory a des paramètres publics connectionRequestTimeout, connectTimeout et readTimeout et @ConfigurationProperties les définit pour vous.


Pour Spring 4.1 ou Spring 5 sans Spring Boot utilisez @Configuration au lieu de XML

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

        return new RestTemplate(httpRequestFactory);
    }
}
111
dustin.schultz

Cette question est le premier lien pour une recherche dans Spring Boot. Par conséquent, il serait bon de mettre ici la solution recommandée dans la documentation officielle . Spring Boot a son propre bean pratique RestTemplateBuilder :

@Bean
public RestTemplate restTemplate(
        RestTemplateBuilder restTemplateBuilder) {

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

La création manuelle d'instances RestTemplate est une approche potentiellement gênante, car d'autres beans configurés automatiquement ne sont pas injectés dans des instances créées manuellement. 

16
heldev

Voici un moyen très simple de définir le délai d'attente: 

RestTemplate restTemplate = new RestTemplate(getClientHttpRequestFactory());

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

Voici mes 2 cents. Rien de nouveau, mais quelques explications, améliorations et code plus récent. 

Par défaut, RestTemplate a un délai d'expiration infini . Il existe deux types de délais d'expiration: le délai de connexion et le délai de lecture. Par exemple, je pouvais me connecter au serveur mais je ne pouvais pas lire les données. L'application était en suspens et vous n'avez aucune idée de ce qui se passe.

Je vais utiliser des annotations, qui sont préférables de nos jours à XML.

@Configuration
public class AppConfig {

    @Bean
    public RestTemplate restTemplate() {

        var factory = new SimpleClientHttpRequestFactory();

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

        return new RestTemplate(factory);
    }
}

Ici, nous utilisons SimpleClientHttpRequestFactory pour définir la connexion et le délai de lecture .. Il est ensuite transmis au constructeur de RestTemplate.

@Configuration
public class AppConfig {

    @Bean
    public RestTemplate restTemplate(RestTemplateBuilder builder) {

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

Dans la deuxième solution, nous utilisons la variable RestTemplateBuilder. Notez également les paramètres des deux méthodes: elles prennent Duration. Les méthodes surchargées prenant directement des millisecondes sont maintenant obsolètes.

Edit Testé avec Spring Boot 2.1.0 et Java 11.

4
Jan Bodnar

J'ai eu un scénario similaire, mais il était également nécessaire de définir un proxy. La façon la plus simple de procéder était d’étendre la variable SimpleClientHttpRequestFactory pour faciliter le paramétrage du proxy (différents proxy pour les non-prod vs prod). Cela devrait quand même fonctionner même si vous n’avez pas besoin du proxy. Ensuite, dans ma classe étendue, je substitue la méthode openConnection(URL url, Proxy proxy), en utilisant la même chose que source , mais en définissant simplement les délais d'attente avant de retourner.

@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
Ryan D