Diese Seite bleibt auch ohne JavaScript lesbar. Suche und Buttons sind Zusatzkomfort.
Spring WebFlux und Reactive Streams
Nicht-blockierende Architektur verstehen, ohne jede Anwendung unnötig reactive zu machen.
1. Wann Reactive sinnvoll ist
Reactive lohnt sich, wenn viele parallele Requests überwiegend auf IO warten: externe APIs, Streaming, WebSockets, nicht-blockierende Datenbanktreiber oder lange laufende Datenflüsse. Für klassische CRUD-Systeme mit blockierendem JDBC/JPA ist WebFlux oft zusätzlicher Denkaufwand ohne klaren Gewinn.
Das größte Risiko ist Mischbetrieb: Ein blockierender Aufruf in einer Event-Loop kann die Skalierung zerstören. Deshalb muss bei WebFlux die gesamte Kette betrachtet werden: Controller, Service, HTTP-Clients, Datenbanktreiber, Messaging und Threading.
@RestController
@RequestMapping("/api/customers")
class CustomerReactiveController {
private final CustomerClient customerClient;
CustomerReactiveController(CustomerClient customerClient) {
this.customerClient = customerClient;
}
@GetMapping("/{id}/risk")
Mono<RiskResponse> risk(@PathVariable String id) {
return customerClient.fetchCustomer(id)
.flatMap(customer -> customerClient.fetchRisk(customer.riskProfileId()))
.map(risk -> new RiskResponse(id, risk.score()));
}
}
@Component
class CustomerClient {
private final WebClient webClient;
CustomerClient(WebClient.Builder builder) {
this.webClient = builder.baseUrl("https://customer.internal").build();
}
Mono<CustomerDto> fetchCustomer(String id) {
return webClient.get()
.uri("/customers/{id}", id)
.retrieve()
.onStatus(HttpStatusCode::is5xxServerError,
response -> Mono.error(new RemoteSystemUnavailableException("customer")))
.bodyToMono(CustomerDto.class)
.timeout(Duration.ofSeconds(2));
}
}
2. Backpressure und fachliche Realität
Backpressure heißt: ein Consumer kann signalisieren, wie viel er verarbeiten kann. Das schützt Systeme vor Überlast. Fachlich ersetzt es aber keine Idempotenz, keine fachliche Reihenfolge und keine Retry-Strategie.
3. Debugging Reactive
Reactive-Code ist schwerer linear zu lesen. Daher sind StepVerifier-Tests, strukturierte Operator-Ketten, klare Namen und gezieltes Logging wichtig. Nicht jede Business-Regel muss in einer einzigen langen Chain stehen.
@Test
void calculatesRisk() {
Mono<RiskResponse> result = service.calculateRisk("C-42");
StepVerifier.create(result)
.expectNextMatches(r -> r.customerId().equals("C-42") && r.score() >= 0)
.verifyComplete();
}
Enterprise-Prüffragen
- Gibt es blockierende Aufrufe in der reactive Pipeline?
- Ist Timeout/Retry begrenzt?
- Sind Mono/Flux-Ketten testbar?
- Ist Reactive fachlich begründet oder nur Mode?