Ein Reactive REST Client in Spring WebFlux ist ein HTTP-Client, der nicht blockierend arbeitet.

Das bedeutet:

  • Der Thread wartet nicht auf die Antwort.
  • Während die Antwort kommt, kann der Thread andere Requests bearbeiten.
  • Gut für:
    • viele parallele Requests
    • Microservices
    • Streaming
    • hohe Skalierung

In Spring Boot WebFlux verwendet man meistens:

  • WebClient statt RestTemplate

RestTemplate = klassisch/blockierend
WebClient = reactive/non-blocking


Grundkonzept einfach erklärt

Klassisch/blockierend

String result = restTemplate.getForObject(url, String.class);

Der Thread wartet hier: Bei vielen Requests braucht man viele Threads.


Reactive/non-blocking

Mono<String> result = webClient.get().uri(„/users“).retrieve()

    .bodyToMono(String.class);

Hier wartet der Thread nicht aktiv.

Stattdessen:

Request senden, Thread freigeben, Antwort kommt später und Weiterverarbeitung starten


Wichtige Reactive Typen

Mono

0 oder 1 Ergebnis.

Mono<User>

Flux

0 bis N Ergebnisse.

Flux<User>


Minimales Beispiel

Maven Dependency

<dependency>

    <groupId>org.springframework.boot</groupId>

    <artifactId>spring-boot-starter-webflux</artifactId>

</dependency>


Einfaches DTO

public class Todo {

    private Long id;

    private String title;

    public Todo() {

    }

    public Todo(Long id, String title) {

        this.id = id;

        this.title = title;

    }

    public Long getId() {

        return id;

    }

    public String getTitle() {

        return title;

    }

    public void setId(Long id) {

        this.id = id;

    }

    public void setTitle(String title) {

        this.title = title;

    }

}


Reactive REST Client

Service mit WebClient

@Service

public class TodoClient {

    private final WebClient webClient;

    public TodoClient(WebClient.Builder builder) {

        this.webClient = builder

                .baseUrl(„https://example.com&#8220;)

                .build();

    }

    public Mono<Todo> getTodo(Long id) {

        return webClient.get()

                .uri(„/todos/{id}“, id)

                .retrieve()

                .onStatus(

                        status -> status.is4xxClientError(),

                        response -> Mono.error(

                                new RuntimeException(„4xx Fehler“))

                )

                .onStatus(

                        status -> status.is5xxServerError(),

                        response -> Mono.error(

                                new RuntimeException(„5xx Fehler“))

                )

                .bodyToMono(Todo.class)

                .timeout(Duration.ofSeconds(3))

                .doOnSuccess(todo ->

                        System.out.println(„Success: “ + todo.getTitle()))

                .doOnError(error ->

                        System.out.println(„Error: “ + error.getMessage()))

                .onErrorResume(error -> {

                    System.out.println(„Fallback aktiviert“);

                    return Mono.just(

                            new Todo(-1L, „Fallback Todo“));

                });

    }

}


Controller Beispiel

@RestController

@RequestMapping(„/api“)

public class TodoController {

    private final TodoClient todoClient;

    public TodoController(TodoClient todoClient) {

        this.todoClient = todoClient;

    }

    @GetMapping(„/todo/{id}“)

    public Mono<Todo> getTodo(@PathVariable Long id) {

        return todoClient.getTodo(id);

    }

}


Warum gibt Controller ein Mono zurück?

Weil WebFlux komplett reactive arbeitet.

Spring verarbeitet das asynchron.


Minimaler Test

Hier testet man den Client.


Test Dependency

<dependency>

    <groupId>io.projectreactor</groupId>

    <artifactId>reactor-test</artifactId>

    <scope>test</scope>

</dependency>


Einfacher Test

@SpringBootTest

class TodoClientTest {

    @Autowired

    private TodoClient todoClient;

    @Test

    void shouldReturnTodo() {

        Mono<Todo> mono = todoClient.getTodo(1L);

        StepVerifier.create(mono)

                .expectNextMatches(todo ->

                        todo.getId() != null)

                .verifyComplete();

    }

}


Klassisch

Mach Request

Warte

Verarbeite Antwort


Reactive

Definiere Pipeline

Wenn Antwort kommt:

   → verarbeite Daten

   → handle Fehler

   → sende weiter

Man beschreibt eher einen Datenfluss.


Wichtige Operatoren

map

Objekt transformieren.

mono.map(todo -> todo.getTitle())


flatMap

Asynchrone Verkettung.

mono.flatMap(todo -> anotherCall())


filter

.filter(todo -> todo.getId() > 0)


onErrorResume

Fallback.


Wenn kein Thread wartet:

Wie kommt die Antwort später zurück?

Event Loop + Callback Mechanismus + Betriebssystem Netzwerk-Events sorgt dafür dass die Antwort vervollständigt wird.

In Spring WebFlux passiert im Hintergrund sehr viel automatisch.


Klassisches Blocking nochmal

Normaler RestTemplate

String result = restTemplate.getForObject(…);

Intern:

Thread sendet HTTP Request

Thread wartet/blockiert

Socket wartet

Antwort kommt

Thread läuft weiter

Der Thread ist die ganze Zeit beschäftigt.


Reactive Ansatz

Mit WebClient:

Mono<Todo> mono = webClient.get()…

Hier passiert etwas komplett anderes.


Reactive Modell

Thread:

Request senden

„Informiere mich später“

Thread frei

(Event Loop überwacht Netzwerk)

Antwort kommt

Event ausgelöst

Pipeline weiter


Warum ist das skalierbarer?

Weil Threads teuer sind.

Ein blockierter Thread braucht:

  • RAM
  • Context Switches
  • CPU Verwaltung

Mit Reactive:

Wenige Threads

Viele Requests

Beispiel:

ModellRequests
Blocking1000 Threads für 1000 langsame Requests
Reactivevielleicht 8-20 Threads

Wichtiger Denkwechsel

Reactive bedeutet:

Nicht:

„warte auf Ergebnis“

Sondern:

„registriere was passieren soll,

wenn Ergebnis kommt“


Warum Mono zuerst „nichts enthält“

Das ist extrem wichtig.

Mono<Todo>

ist NICHT das Ergebnis selbst.

Es ist eher:

Ein Versprechen auf zukünftige Daten

Ähnlich wie:

  • Future
  • Promise in JavaScript

Beispiel

Mono<Todo> mono = webClient.get()…

Hier existiert das Todo vielleicht noch gar nicht.

Du definierst nur:

Was passieren soll,

wenn es irgendwann kommt.


map() passiert später

mono.map(todo -> todo.getTitle())

Nicht sofort.

Sondern erst:

wenn Antwort angekommen ist


Subscriber startet alles

Sehr wichtig:

Reactive Streams sind oft „lazy“.

Erst ein Subscriber startet die Pipeline.

Zum Beispiel:

mono.subscribe(…)

Oder Spring WebFlux subscribt automatisch im Controller.


Warum flatMap wichtig ist

Wenn weiterer async Call kommt:

getUser()

   .flatMap(user -> getOrders(user))

Bedeutet:

Wenn User da:

   starte neuen async Call

Hinterlasse einen Kommentar

I’m Iman

Mein Name ist Iman Dabbaghi. Ich arbeite als Senior Software Engineer in der Schweiz. Außerdem interessiere ich mich sehr für gewaltfreie Kommunikation, Bachata-Tanz und Musik sowie fürs die Persönlichkeitsentwicklung.

Ich habe einen Masterabschluss in Informatik von der Universität Freiburg in Deutschland, bin Spring/Java Certified Professional (OCP), Certified Professional for Software Architecture (CPSA-F) und ein lebenslanger Lernender 🎓.

EN:

My name is Iman Dabbaghi. I work as a Senior Software Engineer in Switzerland. I am also very interessted in nonviolent communication, Bachata dance and music and also for personal development.

I hold a masters degree in computer science from the university of Freiburg in Germany, am a Spring / Java Certified Professional (OCP), Certified Software Architecture (CPSA-F) and Life Long Learner🎓

Let’s connect