Spring RestTemplate-Zeitlimit

125

Ich möchte die Verbindungszeitüberschreitungen für einen von meiner Webanwendung verwendeten Restdienst festlegen. Ich verwende Spring's RestTemplate, um mit meinem Dienst zu sprechen. Ich habe einige Nachforschungen angestellt und die folgende XML-Datei (in meiner Anwendungs-XML-Datei) gefunden und verwendet, von der ich glaube, dass sie das Zeitlimit festlegen soll. Ich benutze Spring 3.0.

Ich habe hier auch das gleiche Problem gesehen. Timeout-Konfiguration für Spring-Webservices mit RestTemplate, aber die Lösungen scheinen nicht so sauber zu sein . Ich würde es vorziehen, die Timeout-Werte über die Spring-Konfiguration festzulegen

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

Wie auch immer ich das readTimeout einstelle, ich erhalte Folgendes:

Netzwerkkabel getrennt: Wartet ca. 20 Sekunden und meldet folgende Ausnahme:

org.springframework.web.client.ResourceAccessExcep tion: E / A-Fehler: Keine Route zum Host: connect; verschachtelte Ausnahme ist java.net.NoRouteToHostException: Keine Route zum Host: Verbindung

URL falsch, so dass 404 vom Restdienst zurückgegeben wird: Wartet ca. 10 Sekunden und meldet folgende Ausnahme:

org.springframework.web.client.HttpClientErrorException: 404 Nicht gefunden

Meine Anforderungen erfordern kürzere Zeitüberschreitungen, daher muss ich diese ändern können. Irgendwelche Ideen, was ich falsch mache?

Danke vielmals.

Sardo
quelle

Antworten:

163

Für Spring Boot> = 1.4

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

Für 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());
    }
}

dann in deinem application.properties

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

Das funktioniert , weil HttpComponentsClientHttpRequestFactoryöffentlichen Setter hat connectionRequestTimeout, connectTimeoutund readTimeoutund @ConfigurationPropertiessetzt sie für Sie.


Für Frühling 4.1 oder Feder 5 ohne Federverschluß mit @ConfigurationstattXML

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

        return new RestTemplate(httpRequestFactory);
    }
}
Dustin.schultz
quelle
Schönes Beispiel! Bitte entfernen Sie ungerade newAussage im Spring BootBeispiel
StasKolodyuk
7
Beachten Sie, dass RestTemplate nach dieser Konfiguration den Apache-HTTP-Client verwendet (um das Zeitlimit festzulegen). Die standardmäßigen maxPerRoute-Threads des Apache http-Client-Verbindungspools sind 5 und die maximale Gesamtzahl der Threads beträgt 10 (httpClient-4.5.2). In einigen Situationen müssen wir dies selbst einstellen (z. B. müssen wir eine Verbindung zu vielen Hosts herstellen und benötigen mehr Verbindungen).
Bluearrow
2
Bitte beachten Sie, dass das connectionRequestTimeoutAttribut nicht vor dem 4.1.4.FREIGABE verfügbar ist
Taoufik Mohdit
Ich habe die Konfiguration für Spring Boot> = 1.4 bei Spring Boot> = 2.1.8 ausprobiert und hatte keinen Erfolg. Ich habe diesen Beitrag ( zetcode.com/springboot/resttemplate ) befolgt , um diese Konfiguration vorzunehmen.
Angelo Polotto
@ ÂngeloPolotto Der von Ihnen gepostete Link gibt den gleichen Rat wie diese Lösung. In dem Artikel heißt es: "Alternativ können wir den RestTemplateBuilder verwenden, um die Arbeit zu erledigen."
Dustin.schultz
76

Ich habe es endlich geschafft.

Ich denke, die Tatsache, dass unser Projekt zwei verschiedene Versionen des Commons-httpclient-JARs hatte, hat nicht geholfen. Nachdem ich das geklärt hatte, stellte ich fest, dass Sie zwei Dinge tun können ...

Im Code können Sie Folgendes eingeben:

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

Wenn dieser Code zum ersten Mal aufgerufen wird, wird das Zeitlimit für die HttpComponentsClientHttpRequestFactoryvon der Klasse verwendete Klasse festgelegt RestTemplate. Daher verwenden alle nachfolgenden Anrufe von RestTemplatedie oben definierten Timeout-Einstellungen.

Oder die bessere Option ist dies:

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

Wo ich die RestOperationsSchnittstelle in meinem Code verwende und die Timeout-Werte aus einer Eigenschaftendatei erhalte.

Sardo
quelle
Dadurch werden die Zeitüberschreitungen für alle Aufrufe über diese Restvorlage (die ein Singleton ist) festgelegt. Wissen Sie, ob es möglich ist, die Zeitüberschreitungen pro Anfrage zu steuern? (zB: 10 Sekunden für einen Post-Anruf und 5 Sekunden für einen Anruf usw.)
Codesalsa
@ Sardo. Wo ich die RestOperations-Schnittstelle in meinem Code verwende. Müssen wir dafür eine explizite Schnittstelle erstellen?
Deadend
Sie sagten, dass Sie Spring 3.0 verwenden - woran ich auch festhalte -, aber in 3.0 gibt es keine HttpComponentsClientHttpRequestFactory! Hast du Spring aktualisiert?
Kutzi
5
Der obige Code funktioniert im letzten Frühjahr nicht. Es gibt ClassCastExceptionjava.lang.ClassCastException: org.springframework.http.client.InterceptingClientHttpRequestFactory cannot be cast to org.springframework.http.client.HttpComponentsClientHttpRequestFactory
comiventor
40

Diese Frage ist der erste Link für eine Spring Boot-Suche. Daher wäre es großartig, hier die in der offiziellen Dokumentation empfohlene Lösung anzugeben . Spring Boot verfügt über eine eigene Convenience-Bean RestTemplateBuilder :

@Bean
public RestTemplate restTemplate(
        RestTemplateBuilder restTemplateBuilder) {

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

Die manuelle Erstellung von RestTemplate-Instanzen ist ein potenziell problematischer Ansatz, da andere automatisch konfigurierte Beans nicht in manuell erstellte Instanzen eingefügt werden.

heldev
quelle
2
Ein Hinweis für Spring-Neulinge wie mich: Nur dies in eine @Configuration zu stecken, bringt nichts. Diese Methode erfordert, dass Sie diese RestTemplate irgendwo injiziert haben, wo sie als Argument für den Konstruktor von RestTemplateXhrTransport verwendet wird, den Sie wiederum zu Ihrer Liste der Transporte hinzufügen, die Sie an Ihren SocksJSClient übergeben.
Key Lay
setConnectTimeoutund einige Implementierungen von setReadTimeoutsind veraltet
Skryvets
17

Hier sind meine 2 Cent. Nichts Neues, aber einige Erklärungen, Verbesserungen und neuerer Code.

Standardmäßig RestTemplateist das Zeitlimit unbegrenzt. Es gibt zwei Arten von Zeitüberschreitungen: Verbindungszeitüberschreitung und Lesezeitüberschreitung. Zum Beispiel konnte ich eine Verbindung zum Server herstellen, aber keine Daten lesen. Die Anwendung hing und Sie haben keine Ahnung, was los ist.

Ich werde Anmerkungen verwenden, die heutzutage XML vorgezogen werden.

@Configuration
public class AppConfig {

    @Bean
    public RestTemplate restTemplate() {

        var factory = new SimpleClientHttpRequestFactory();

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

        return new RestTemplate(factory);
    }
}

Hier stellen wir SimpleClientHttpRequestFactorydie Verbindung ein und lesen Timeouts. Es wird dann an den Konstruktor von übergeben RestTemplate.

@Configuration
public class AppConfig {

    @Bean
    public RestTemplate restTemplate(RestTemplateBuilder builder) {

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

In der zweiten Lösung verwenden wir die RestTemplateBuilder. Beachten Sie auch die Parameter der beiden Methoden: Sie nehmen Duration. Die überladenen Methoden, die direkt Millisekunden dauern, sind jetzt veraltet.

Bearbeiten Getestet mit Spring Boot 2.1.0 und Java 11.

Jan Bodnar
quelle
Welche Spring- und Java-Version verwenden Sie?
Orirab
2
Spring Boot 2.1.0 und Java 11. Ein funktionierendes Beispiel finden Sie in meinem Tutorial: zetcode.com/springboot/resttemplate
Jan Bodnar
Ich schlage vor, dies zur Antwort
hinzuzufügen
Siehe github.com/spring-projects/spring-boot/blob/master/… . Es wurde in Spring Boot 2.1.0 hinzugefügt.
Jan Bodnar
Vielen Dank @JanBodnar, Ihr Tutorial ist das einzige, das gut auf meinem Spring Boot 5.x
Angelo Polotto
15

Hier ist eine wirklich einfache Möglichkeit, das Timeout festzulegen:

RestTemplate restTemplate = new RestTemplate(getClientHttpRequestFactory());

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

Ich hatte ein ähnliches Szenario, musste aber auch einen Proxy festlegen. Der einfachste Weg, dies zu tun, bestand darin, das zu erweitern, SimpleClientHttpRequestFactoryum das Einstellen des Proxys zu vereinfachen (verschiedene Proxys für Nicht-Prod gegen Prod). Dies sollte auch dann funktionieren, wenn Sie den Proxy nicht benötigen. Dann überschreibe ich in meiner erweiterten Klasse die openConnection(URL url, Proxy proxy)Methode, indem ich dieselbe wie die Quelle verwende , aber nur die Zeitüberschreitungen festlege, bevor ich zurückkehre.

@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.
quelle
0

Um die Antwort von benscabbia zu erweitern :

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
quelle
0
  1. RestTemplate-Timeout mit SimpleClientHttpRequestFactory Um die Timeout-Eigenschaften programmgesteuert zu überschreiben, können Sie die SimpleClientHttpRequestFactory-Klasse wie folgt anpassen.

Zeitüberschreitung mit SimpleClientHttpRequestFactory überschreiben

//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-Zeitlimit mit HttpComponentsClientHttpRequestFactory SimpleClientHttpRequestFactory hilft beim Festlegen des Zeitlimits, ist jedoch in seiner Funktionalität sehr eingeschränkt und in Echtzeitanwendungen möglicherweise nicht ausreichend. Im Produktionscode möchten wir möglicherweise HttpComponentsClientHttpRequestFactory verwenden, die die HTTP-Client-Bibliothek zusammen mit resttemplate unterstützen.

HTTPClient bietet weitere nützliche Funktionen wie Verbindungspool, Verwaltung von Leerlaufverbindungen usw.

Lesen Sie mehr: Konfigurationsbeispiel für Spring RestTemplate + HttpClient

Zeitüberschreitung mit HttpComponentsClientHttpRequestFactory überschreiben

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

Referenz: Beispiel für eine Konfiguration des Spring RestTemplate-Timeouts

Zgpeace
quelle