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.