Délai d'expiration du modèle de repos de printemps

125

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

J'ai également vu le même problème ici Configuration du délai d'expiration pour les services Web de printemps avec RestTemplate, mais les solutions ne semblent pas aussi claires , je préférerais définir les valeurs de délai d'expiration 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 tout ce que j'ai défini pour readTimeout, j'obtiens ce qui suit:

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

org.springframework.web.client.ResourceAccessExcep tion: erreur d'E / S: aucune route vers l'hôte: connexion; l'exception imbriquée est java.net.NoRouteToHostException: Aucune route vers l'hôte: connect

URL incorrecte donc 404 renvoyée par le service de repos: attend environ 10 secondes et signale l'exception suivante:

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

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

Merci beaucoup.

sardo
la source

Réponses:

164

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 HttpComponentsClientHttpRequestFactorya setters publics connectionRequestTimeout, connectTimeoutet readTimeoutet @ConfigurationPropertiesles met pour vous.


Pour Spring 4.1 ou Spring 5 sans Spring Boot en utilisant @Configurationau lieu 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
la source
Bel exemple! S'il vous plaît, supprimez la newdéclaration étrange dans l' Spring Bootexemple
StasKolodyuk
7
Notez qu'après cette configuration, RestTemplate utilisera le client http apache (pour définir le délai d'expiration). Les threads maxPerRoute par défaut du pool de connexions client http Apache sont 5 et le nombre total maximum de threads est de 10 (httpClient-4.5.2). Nous devons définir cela nous-mêmes dans certaines situations (par exemple, nous devons nous connecter à de nombreux hôtes et avoir besoin de plus de connexions).
bluearrow
2
Veuillez noter que l' connectionRequestTimeoutattribut n'est pas disponible avant la version 4.1.4.RELEASE
Taoufik Mohdit
J'ai essayé la configuration pour Spring Boot> = 1.4 sur Spring Boot> = 2.1.8 et je n'ai pas réussi. J'ai suivi ce post ( zetcode.com/springboot/resttemplate ) pour faire cette configuration.
Ângelo Polotto
@ ÂngeloPolotto le lien que vous avez posté donne le même conseil que cette solution. L'article dit: "Alternativement, nous pouvons utiliser RestTemplateBuilder pour faire le travail."
dustin.schultz
76

J'ai finalement réussi à faire fonctionner ça.

Je pense que le fait que notre projet ait eu deux versions différentes du jar commons-httpclient n'aidait pas. Une fois que j'ai réglé cela, 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éfinira le délai d'expiration de la HttpComponentsClientHttpRequestFactoryclasse utilisée par le RestTemplate. Par conséquent, tous les appels ultérieurs effectués par RestTemplateutiliseront les paramètres de délai d'expiration 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' RestOperationsinterface dans mon code et récupère les valeurs de délai d'expiration à partir d'un fichier de propriétés.

sardo
la source
Cela définit donc les délais d'expiration pour tous les appels via ce modèle de repos (qui est un singleton). Savez-vous s'il est possible de contrôler les délais d'expiration par requête? (par exemple: 10 sec pour un post-appel et 5 sec pour un get call, etc.)
codesalsa
@ sardo. Où j'utilise l'interface RestOperations dans mon code. nous devons créer une interface explicite pour cela?
impasse
Vous avez dit que vous utilisez Spring 3.0 - avec lequel je suis également coincé - mais dans la version 3.0, il n'y a pas de HttpComponentsClientHttpRequestFactory! Avez-vous mis à jour Spring?
Kutzi
5
Le code ci-dessus ne fonctionne pas dans le dernier printemps. Il donne ClassCastExceptionjava.lang.ClassCastException: org.springframework.http.client.InterceptingClientHttpRequestFactory cannot be cast to org.springframework.http.client.HttpComponentsClientHttpRequestFactory
comiventor
40

Cette question est le premier lien pour une recherche Spring Boot, ce serait donc bien 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(Duration.ofSeconds(500))
            .setReadTimeout(Duration.ofSeconds(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.

Heldev
la source
2
Une note aux nouveaux arrivants de Spring comme moi: le simple fait de coller ceci dans une @Configuration ne fera rien. Cette méthode nécessite que vous ayez ce RestTemplate injecté quelque part qui l'utilise comme argument du constructeur de RestTemplateXhrTransport que vous ajouterez à votre tour à votre liste de transports que vous passerez à votre SocksJSClient.
Key Lay
setConnectTimeoutet certaines implémentations de setReadTimeoutsont obsolètes
skryvets
17

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

Par défaut, RestTemplatea 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 pourrais me connecter au serveur mais je ne pouvais pas lire les données. L'application était suspendue et vous n'avez aucune idée de ce qui se passe.

Je vais utiliser des annotations, qui de nos jours sont préférées à 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 SimpleClientHttpRequestFactorypour définir la connexion et lire les délais d'attente. 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 le RestTemplateBuilder. Notez également les paramètres des deux méthodes: ils prennent Duration. Les méthodes surchargées qui prennent directement des millisecondes sont désormais obsolètes.

Edit Testé avec Spring Boot 2.1.0 et Java 11.

Jan Bodnar
la source
Quelle version Spring et Java utilisez-vous?
orirab
2
Spring Boot 2.1.0 et Java 11. Pour un exemple de travail, vous pouvez consulter mon tutoriel: zetcode.com/springboot/resttemplate
Jan Bodnar
Je suggère d'ajouter ceci à la réponse
orirab
Voir github.com/spring-projects/spring-boot/blob/master/… . Il a été ajouté dans Spring Boot 2.1.0.
Jan Bodnar
Merci @JanBodnar, votre tutoriel est le seul qui a bien fonctionné sur mon Spring Boot 5.x
Ângelo Polotto
15

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

RestTemplate restTemplate = new RestTemplate(getClientHttpRequestFactory());

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

J'ai eu un scénario similaire, mais je devais également définir un proxy. La façon la plus simple que j'ai pu voir pour faire cela était d'étendre le SimpleClientHttpRequestFactorypour la facilité de configuration du proxy (différents proxys pour non-prod vs prod). Cela devrait toujours fonctionner même si vous n'avez pas besoin du proxy. Ensuite, dans ma classe étendue, je remplace la openConnection(URL url, Proxy proxy)méthode, en utilisant la même chose que la source , mais en définissant simplement les délais d'attente avant de revenir.

@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
la source
0

Pour développer la réponse 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
la source
0
  1. Délai d'expiration de RestTemplate avec SimpleClientHttpRequestFactory Pour remplacer par programme les propriétés de délai d'expiration, nous pouvons personnaliser la classe SimpleClientHttpRequestFactory comme ci-dessous.

Remplacer le délai d'expiration avec 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. Délai d'expiration de RestTemplate avec HttpComponentsClientHttpRequestFactory SimpleClientHttpRequestFactory aide à définir le délai d'expiration, mais ses fonctionnalités sont très limitées et peuvent ne pas s'avérer suffisantes dans les applications en temps réel. Dans le code de production, nous souhaitons peut-être utiliser HttpComponentsClientHttpRequestFactory qui prend en charge la bibliothèque client HTTP avec resttemplate.

HTTPClient fournit d'autres fonctionnalités utiles telles que le pool de connexions, la gestion des connexions inactives, etc.

Lire la suite: Exemple de configuration Spring RestTemplate + HttpClient

Remplacer le délai d'expiration avec 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: Exemple de configuration du délai d'expiration Spring RestTemplate

Zgpeace
la source