Neuigkeiten von trion.
Immer gut informiert.

Spring: Timeouts pro RestClient konfigurieren

In diesem kurzen Blog-Post beschreibe ich zwei Varianten, wie unterschiedliche Timeouts je RestClient konfiguriert werden können.

Springs RestClient (Spring Framework 6.1 bzw. Spring Boot 3.2) ist eine moderne Alternative zum in die Jahre gekommenen RestTemplate. Der RestClient bietet eine Fluent-API mit der man synchrone HTTP-Anfragen verschicken kann. Die API ist dabei ähnlich zum non-blocking und reaktiven WebClient aus dem Spring WebFlux Projekt.

Connect und Read Timeouts

Bei der Kommunikation zwischen Anwendungen spielen Timeouts eine wichtige Rolle.

Der Connect Timeout gibt an, wie lange ein Client darauf wartet, eine Verbindung zu einem Server aufzubauen, bevor der Versuch abgebrochen wird. Dieser Timeout sollte so gewählt werden, dass defekte Services oder falsche Firewalleinstellungen schnell erkannt werden. Ein möglicher Ansatz zur Wahl des Connect Timeouts könnte ein Vielfaches der Round-Trip-Time zwischen Sender und Empfänger sein.

Der Request Timeout legt hingegen fest, wie lange ein Client nach erfolgreichem Verbindungsaufbau auf eine Antwort des Servers wartet. Konkret wird dazu die Zeitspanne zwischen einzelnen Datenpaketen des Servers betrachtet. Der Request Timeout kann zum Beispiel auf der Grundlage von Service-Level-Agreements oder Latenz-Informationen des aufgerufenen Services festgelegt werden.

Timeouts pro Client

Eine Spring Boot Anwendung kommuniziert potenziell mit mehr als einem externen Service. Diese Services unterscheiden sich beispielsweise in URL, Authentifizierungsmethode und Antwortzeit.

Daher ist es sinnvoll je Service einen RestClient als Bean bereitzustellen und diese auch mit passenden Timeouts auszustatten.

Während man für das RestTemplate oder den WebClient recht einfach Timeouts pro Client konfigurieren kann, muss man aktuell beim RestClient ein wenig kreativ werden. Es folgen zwei Varianten, wie Timeouts beim RestClient umgesetzt werden können.

Variante 1: Konfiguration per ClientHttpRequestFactory

Timeouts für einen RestClient können über eine ClientHttpRequestFactory eingestellt werden. Seit Spring Boot 3.4 lässt sich diese Factory über ClientHttpRequestFactorySettings aus dem Package org.springframework.boot.http.client konfigurieren.

Die Factory für die HTTP-Request kann anschließend an den RestClient.Builder übergeben werden.

import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.boot.http.client.ClientHttpRequestFactoryBuilder;
import org.springframework.boot.http.client.ClientHttpRequestFactorySettings;
// ...

public static final String TRION_API_CLIENT = "trionApiClient";
@Bean(TRION_API_CLIENT)
public RestClient trionApiClient(RestClient.Builder builder) {
    return builder
            .baseUrl("https://trion.de/api")
            .requestFactory(customRequestFactory())
            .build();
}

ClientHttpRequestFactory customRequestFactory() {
    ClientHttpRequestFactorySettings settings = ClientHttpRequestFactorySettings
            .defaults()
            .withConnectTimeout(Duration.ofSeconds(2))
            .withReadTimeout(Duration.ofSeconds(4));

    return ClientHttpRequestFactoryBuilder.detect().build(settings);
}

Mittels ClientHttpRequestFactoryBuilder.detect() wird die tatsächliche Implementierung des HTTP-Clients per Classpath-Scan ermittelt. Alternativ kann aber auch direkt die gewünschte Implementierung angegeben werden. Hier zum Beispiel Apache HttpComponents.

ClientHttpRequestFactory customRequestFactory() {
    // ...
    return ClientHttpRequestFactoryBuilder.httpComponents().build(settings);
}

Variante 2: Konfiguration per RestTemplateBuilder

Eine weitere Möglichkeit, Timeouts je RestClient zu konfigurieren, ist die Nutzung des RestTemplateBuilder. Dieser erlaubt es, Read- und Connect-Timeouts zu setzen und anschließend ein RestTemplate zu erstellen, das wiederum zum Erstellen eines RestClient verwendet wird.

Ein Beispiel dazu:

public static final String TRION_API_CLIENT = "trionApiClient";
@Bean(TRION_API_CLIENT)
public RestClient trionApiClient(RestTemplateBuilder builder) {
    var restTemplate = builder
            .rootUri("https://trion.de/api")
            .connectTimeout(Duration.ofSeconds(2))
            .readTimeout(Duration.ofSeconds(4))
            .build();

    return RestClient.create(restTemplate);
}

// Weitere Clients mit anderen Timeouts

In diesem Ansatz wird der RestTemplateBuilder genutzt, um ein RestTemplate mit den gewünschten Timeouts zu konfigurieren. Danach dient dieses RestTemplate als Grundlage, um den RestClient zu erstellen.

Mit dem Weg über das RestTemplate wird der Code zum einen einfach und kompakt gehalten. Zudem ist dieser Ansatz auch praktisch, wenn bereits Konfigurationen oder Builder für das RestTemplate in der Anwendung vorhanden sind.




Zu den Themen Spring Boot und Spring Security bieten wir sowohl Beratung, Entwicklungsunterstützung als auch passende Schulungen an:

Auch für Ihren individuellen Bedarf können wir Workshops und Schulungen anbieten. Sprechen Sie uns gerne an.

Feedback oder Fragen zu einem Artikel - per Twitter @triondevelop oder E-Mail freuen wir uns auf eine Kontaktaufnahme!

Zur Desktop Version des Artikels