Module 07 · Executable
Project Reactor
Reactive composition với Mono/Flux, operators, scheduler, backpressure, testing và debugging.
1. Mono và Flux
| Type | Cardinality | Ví dụ |
|---|---|---|
Mono<T> | 0..1 | Find user by id. |
Flux<T> | 0..N | Rows, events, stream. |
2. Creation
Mono.just("hello");
Mono.empty();
Mono.error(new IllegalStateException());
Flux.just(1, 2, 3);
Flux.range(1, 10);
Flux.fromIterable(items);
Mono.fromCallable(this::blockingCall);
Mono.defer(() -> computePublisherAtSubscriptionTime());
defer tạo publisher tại thời điểm subscribe, hữu ích khi cần state mới cho mỗi subscription.
3. map / flatMap / concatMap
Mono<String> name = userMono.map(User::name);
Mono<Order> order =
userMono.flatMap(user -> orderRepository.findLatest(user.id()));
Bridge: thenCompose ≈ flatMap ≈ Vert.x compose.
flatMap: concurrency cao, completion order có thể khác source order.concatMap: tuần tự, giữ thứ tự.flatMapSequential: inner có thể concurrent nhưng output giữ source order.
4. zip / merge / concat
Mono<Profile> profile =
Mono.zip(userMono, ordersMono)
.map(tuple -> new Profile(tuple.getT1(), tuple.getT2()));
merge interleave theo arrival time; concat chờ source trước hoàn tất.
5. Error handling
serviceCall()
.timeout(Duration.ofSeconds(2))
.retryWhen(Retry.backoff(2, Duration.ofMillis(100)))
.onErrorResume(TimeoutException.class, ex -> fallback());
Retry side effect phải xét idempotency.
6. Scheduler
| Scheduler | Dùng khi |
|---|---|
parallel() | CPU work ngắn. |
boundedElastic() | Bridge blocking APIs có giới hạn. |
single() | Serial execution. |
source
.subscribeOn(Schedulers.boundedElastic())
.map(this::stepA)
.publishOn(Schedulers.parallel())
.map(this::cpuStep);
subscribeOn tác động subscription/upstream; publishOn tạo execution boundary cho downstream.
7. Backpressure
flux.limitRate(100);
flux.onBackpressureBuffer(1000);
flux.onBackpressureDrop();
flux.onBackpressureLatest();8. Cold vs Hot
Cold: mỗi subscriber có sequence riêng. Hot: subscribers quan sát nguồn đang phát theo thời gian.
9. StepVerifier
StepVerifier.create(service.findUser(42))
.expectNextMatches(user -> user.id() == 42)
.verifyComplete();10. Debugging
pipeline
.checkpoint("load-order")
.doOnNext(v -> log.debug("value={}", v))
.doOnError(e -> log.error("pipeline failed", e));11. Lab — Aggregator
findUser(id) ┐
├─ zip ─► profile
findOrders(id) ┘
↓
timeout + selective retry + fallback orders=[]
Không dùng block(); test timeout bằng StepVerifier.
12. Bài tập
G1. Khi nào map tạo nested publisher?
Khi function trả Publisher; dùng flatMap.
G2. Chọn flatMap hay concatMap nếu bắt buộc đúng thứ tự?
G3. Vì sao JDBC trong map trên event-loop thread là anti-pattern?
Tài liệu và code thực hành
Đọc chapter chi tiết [legacy source: modules/07-project-reactor.md] · Mở foundation labs [legacy source: labs/foundation/README.md] · Xem Reactor source [legacy source: examples/README.md]