Reaktive Programmierung in Java: Die Reactor-Bibliothek

Online Python-Trainer für Anfänger

Lernen Sie Python einfach ohne theoretische Überlastung. Lösen Sie praktische Aufgaben mit automatischer Überprüfung und schreiben Sie Code direkt im Browser.

Kurs starten

Reaktive Programmierung in Java mit Project Reactor

\n\n

Reaktive Programmierung ist ein Entwicklungsparadigma, das auf asynchrone Datenströme und die Weitergabe von Änderungen ausgerichtet ist. In Java wird dieses Konzept durch die Spezifikation Reactive Streams und ihre bekannteste Implementierung, die Bibliothek Project Reactor (Teil des Spring WebFlux-Ökosystems), umgesetzt.

\n\n

Im Gegensatz zu traditionellem imperativem Code, bei dem ein Programm Befehle sequenziell ausführt, ermöglicht der reaktive Ansatz die Verarbeitung von Daten, sobald sie eintreffen, ohne dabei Ausführungsthreads zu blockieren. Dies ist besonders wichtig für hochlastige Systeme, Microservices und Anwendungen mit einer großen Anzahl gleichzeitiger Anfragen.

\n\n

Grundlegende Komponenten von Reactor

\n\n

Die Bibliothek bietet zwei Haupttypen von reaktiven Sequenzen:

\n\n
    \n
  • Mono<T> – stellt einen Strom dar, der 0 oder 1 Element enthalten kann. Wird für einzelne asynchrone Operationen verwendet (z. B. HTTP-Anfrage, Abrufen eines Datensatzes aus der Datenbank).
  • \n
  • Flux<T> – stellt einen Strom dar, der von 0 bis N Elemente enthalten kann. Geeignet für Sammlungen, Ereignisströme oder Daten, die in Teilen eintreffen.
  • \n
\n\n

Beide Typen implementieren das Interface Publisher aus der Reactive-Streams-Spezifikation und unterstützen den Mechanismus des Backpressure (Gegendruck) – die Möglichkeit des Abonnenten, die Geschwindigkeit des Datenempfangs zu steuern.

\n\n

Beispiel: Erstellen und Verarbeiten eines Flux

\n\n

Betrachten wir ein einfaches Beispiel zum Erstellen eines reaktiven Zahlenstroms und dessen Verarbeitung:

\n\n
import reactor.core.publisher.Flux;\n\npublic class ReactorExample {\n    public static void main(String[] args) {\n        Flux<Integer> numbers = Flux.just(1, 2, 3, 4, 5);\n\n        numbers\n            .map(n -> n * 2)          // Transformation\n            .filter(n -> n % 3 == 0)   // Filterung\n            .subscribe(\n                data -> System.out.println("Empfangen: " + data),\n                error -> System.err.println("Fehler: " + error),\n                () -> System.out.println("Abgeschlossen!")\n            );\n    }\n}\n
\n\n

In diesem Beispiel:

\n
    \n
  • Flux.just() erstellt einen Strom aus fünf Zahlen.
  • \n
  • map() multipliziert jedes Element mit 2.
  • \n
  • filter() behält nur Zahlen, die durch 3 teilbar sind.
  • \n
  • subscribe() startet die Verarbeitung – gibt das Ergebnis aus, behandelt Fehler und signalisiert den Abschluss.
  • \n
\n\n

Asynchronität und Arbeiten mit Mono

\n\n

Reactor ermöglicht die Ausführung asynchroner Operationen ohne Blockierung von Threads. Zum Beispiel die Simulation einer Anfrage an einen entfernten Dienst:

\n\n
import reactor.core.publisher.Mono;\nimport java.time.Duration;\n\npublic class AsyncExample {\n    public static void main(String[] args) throws InterruptedException {\n        Mono<String> asyncResult = Mono.fromSupplier(() -> {\n            try {\n                Thread.sleep(1000); // Verzögerung simulieren\n            } catch (InterruptedException e) {\n                e.printStackTrace();\n            }\n            return "Daten empfangen";\n        }).subscribeOn(Schedulers.boundedElastic()); // in einem anderen Thread ausführen\n\n        System.out.println("Anfrage gesendet...");\n        \n        asyncResult.subscribe(System.out::println);\n        \n        Thread.sleep(2000); // auf Abschluss warten\n    }\n}\n
\n\n

Der entscheidende Punkt: Der Aufruf von .subscribeOn() verlagert die Ausführung der teuren Operation in einen separaten Thread, ohne den Hauptthread zu blockieren. Der Abonnent erhält das Ergebnis asynchron.

\n\n

Fehlerbehandlung in reaktiven Strömen

\n\n

Im Gegensatz zu try-catch werden Fehler in der reaktiven Programmierung über Operatoren behandelt:

\n\n
Flux<String> fluxWithError = Flux.just("A", "B", "C")\n    .map(s -> {\n        if ("B".equals(s)) t                    

Empfehlungen