Reactive Programming

30.06.2026 | Muallif: Jaxongir a.k.a | Kategoriya: Java tili | 27 daqiqa o'qish

Reactive programming ichiga quyidagilar kiradi: Project Reactor, Mono, Flux, Spring WebFlux, Reactive Streams spec, R2DBC, Virtual threads vs reactive trade-offs.


1. Reactive programming nima?

Reactive programming - data oqimi va async eventlar bilan ishlash usuli.

Oddiy qilib:

Request keldi, lekin thread kutib o‘tirmaydi. DB, HTTP yoki file javobini kutish paytida thread bo‘shab, boshqa ishni qiladi.

Traditional blocking model:

Request
 ↓
Thread ajratiladi
 ↓
DB javobini kutadi
 ↓
HTTP javobini kutadi
 ↓
Response qaytaradi

Reactive model:

Request
 ↓
Thread ishni boshlaydi
 ↓
DB/HTTP kutish paytida thread band bo‘lmaydi
 ↓
Javob kelganda callback/pipeline davom etadi
 ↓
Response qaytariladi

Reactive programming ayniqsa I/O-heavy tizimlarda foydali:

  • ko‘p HTTP call;

  • ko‘p DB call;

  • messaging;

  • streaming;

  • websocket;

  • real-time notification;

  • gateway;

  • chat;

  • live dashboard.

Lekin reactive programming har doim ham kerak emas.


2. Blocking vs Non-blocking

Blocking

Blocking degani: thread javob kelguncha kutib turadi.

User user = userRepository.findById(id);
Order order = orderClient.getOrder(user.getId());
return order;

Bu kod oddiy va tushunarli. Lekin findById yoki getOrder vaqtida thread band bo‘lib turadi.

Masalan:

Thread 1 → DB javobini kutyapti
Thread 2 → Payment API kutyapti
Thread 3 → Redis kutyapti

Thread ishlamayapti, lekin band.


Non-blocking

Non-blocking degani: thread kutib turmaydi. Javob kelganda pipeline davom etadi.

return userRepository.findById(id)
        .flatMap(user -> orderClient.getOrder(user.getId()));

Bu yerda method darhol Mono<Order> qaytaradi. Real natija keyinroq keladi.


3. Reactive programming qachon kerak?

Reactive yaxshi tanlov bo‘lishi mumkin, agar:

Ko‘p concurrent connection bor
Ko‘p tashqi API chaqiriladi
Thread kutib qolishi ko‘p
Streaming kerak
WebSocket kerak
Gateway yoki proxy yozilyapti
Latency va resource efficiency muhim

Masalan API Gateway:

Client → Gateway → 10 ta backend service

Gateway ko‘p requestni ushlab turadi. Har request uchun platform thread band bo‘lsa, threadlar tez tugaydi. Reactive gateway bu yerda foydali.


4. Reactive qachon kerak emas?

Reactive har joyga qo‘yiladigan "zamonaviy bezak" emas.

Kerak bo‘lmasligi mumkin, agar:

Oddiy CRUD app
Team reactive’ni yaxshi bilmaydi
DB driver blocking bo‘lsa
Business logic CPU-heavy bo‘lsa
Debugging soddaligi muhim bo‘lsa
Virtual threads yetarli bo‘lsa

Agar app asosan PostgreSQL bilan oddiy CRUD qilsa, Spring MVC + virtual threads yoki oddiy thread pool ko‘pincha yetarli.


5. Reactive Streams spec

Reactive Streams - JVM’da async stream processing uchun standart.

U 4 ta asosiy interface beradi:

Publisher<T>
Subscriber<T>
Subscription
Processor<T, R>

Publisher

Data chiqaruvchi.

Men data yuboraman.

Subscriber

Data qabul qiluvchi.

Men data olaman.

Subscription

Subscriber qancha data xohlashini bildiradi.

Menga 10 ta item yubor.

Processor

Ham subscriber, ham publisher.

Data olaman, o‘zgartiraman, keyin chiqaraman.

Reactive Streams’ning eng muhim g‘oyasi:

Consumer qancha ko‘tara olsa, shuncha data oladi.

Bu backpressure deyiladi.


6. Backpressure

Backpressure - consumer producerga: "sekinroq yubor, men ulgurmayapman" deb signal berishi.

Masalan:

Producer: 10 000 item/sec
Consumer: 1 000 item/sec

Agar backpressure bo‘lmasa:

Queue to‘ladi
Memory oshadi
Latency oshadi
App yiqiladi

Reactive Streams’da subscriber qancha item olishni so‘raydi:

request(10)

Ya’ni producer 10 ta yuboradi, keyin yana ruxsat kutadi.


7. Project Reactor

Project Reactor - Spring ekotizimida reactive programming uchun asosiy library.

Spring WebFlux, Spring Cloud Gateway, reactive Redis, reactive MongoDB ko‘pincha Reactor ustida ishlaydi.

Reactor’da ikkita asosiy type bor:

Mono<T>
Flux<T>

8. Mono nima?

Mono - 0 yoki 1 ta qiymat qaytaradigan reactive type.

Mono<User> → user bo‘lishi mumkin yoki bo‘lmasligi mumkin
Mono<Void> → qiymat yo‘q, faqat operation tugashi muhim

Misol:

Mono<User> userMono = userService.findById(1L);

Bu darhol userni bermaydi. Bu kelajakda user kelishini ifodalovchi pipeline.


8.1. Mono oddiy misol

Mono.just("Hello")
        .map(text -> text.toUpperCase())
        .subscribe(System.out::println);

Natija:

HELLO

Tushuntirish:

Mono.just("Hello") → bitta qiymat yaratadi
map(...) → qiymatni o‘zgartiradi
subscribe(...) → pipeline’ni ishga tushiradi

Muhim:

Reactive pipeline subscribe bo‘lmaguncha ko‘pincha ishlamaydi.

Spring WebFlux controller’da esa siz odatda subscribe() chaqirmaysiz. Spring o‘zi subscribe qiladi.


9. Flux nima?

Flux - 0 dan N tagacha qiymat qaytaradigan reactive type.

Flux<User> → ko‘p user
Flux<Event> → eventlar oqimi
Flux<String> → stringlar oqimi

Misol:

Flux.just("A", "B", "C")
        .map(String::toLowerCase)
        .subscribe(System.out::println);

Natija:

a
b
c

10. Mono vs Flux

Type

Nechta qiymat

Misol

Mono<T>

0 yoki 1

user by id

Flux<T>

0 dan N gacha

users list, event stream

Misollar:

Mono<User> findUserById(Long id);
Flux<User> findAllUsers();
Mono<Void> deleteUser(Long id);

11. map va flatMap farqi

Reactive’da eng ko‘p chalkashadigan joylardan biri: map va flatMap.

map

map oddiy qiymatni boshqa qiymatga o‘zgartiradi.

Mono<User> userMono = userRepository.findById(id);

Mono<UserDto> dtoMono = userMono
        .map(user -> new UserDto(user.getId(), user.getName()));

Bu yerda:

User → UserDto

flatMap

flatMap ichida yana Mono yoki Flux qaytsa ishlatiladi.

Mono<Order> orderMono = userRepository.findById(userId)
        .flatMap(user -> orderRepository.findLatestByUserId(user.getId()));

Bu yerda:

User → Mono<Order>

Agar map ishlatsangiz:

Mono<Mono<Order>>

bo‘lib qoladi. Shuning uchun flatMap kerak.


12. Reactive chain misol

Traditional blocking:

public OrderDto getLatestOrder(Long userId) {
    User user = userRepository.findById(userId);
    Order order = orderRepository.findLatestByUserId(user.getId());
    return mapper.toDto(order);
}

Reactive:

public Mono<OrderDto> getLatestOrder(Long userId) {
    return userRepository.findById(userId)
            .flatMap(user -> orderRepository.findLatestByUserId(user.getId()))
            .map(order -> mapper.toDto(order));
}

Bu yerda thread DB javobini kutib turmaydi.


13. Spring WebFlux

Spring WebFlux - Spring’ning reactive web frameworki.

Spring MVC blocking modelda ishlaydi:

Servlet stack
Tomcat thread per request

Spring WebFlux reactive modelda ishlaydi:

Non-blocking stack
Event loop
Netty odatda default

WebFlux ikki xil uslubni qo‘llaydi:

Annotation-based controller
Functional routing

14. WebFlux Controller

@RestController
@RequiredArgsConstructor
@RequestMapping("/users")
public class UserController {

    private final UserService userService;

    @GetMapping("/{id}")
    public Mono<UserDto> getUser(@PathVariable Long id) {
        return userService.findById(id);
    }

    @GetMapping
    public Flux<UserDto> getUsers() {
        return userService.findAll();
    }
}

Bu yerda controller UserDto emas, Mono<UserDto> yoki Flux<UserDto> qaytaradi.


15. WebClient

Reactive HTTP client - WebClient.

RestTemplate blocking. WebClient reactive va non-blocking.

Misol:

@Service
@RequiredArgsConstructor
public class PaymentClient {

    private final WebClient webClient;

    public Mono<PaymentResponse> charge(PaymentRequest request) {
        return webClient.post()
                .uri("/payments")
                .bodyValue(request)
                .retrieve()
                .bodyToMono(PaymentResponse.class);
    }
}

Config:

@Bean
WebClient paymentWebClient(WebClient.Builder builder) {
    return builder
            .baseUrl("https://payment-service")
            .build();
}

16. Parallel call misol

Reactive’da bir nechta tashqi servisni parallel chaqirish qulay.

Masalan checkout summary:

public Mono<CheckoutSummary> getSummary(Long userId) {
    Mono<UserDto> userMono = userClient.getUser(userId);
    Mono<CartDto> cartMono = cartClient.getCart(userId);
    Mono<BonusDto> bonusMono = bonusClient.getBonus(userId);

    return Mono.zip(userMono, cartMono, bonusMono)
            .map(tuple -> new CheckoutSummary(
                    tuple.getT1(),
                    tuple.getT2(),
                    tuple.getT3()
            ));
}

Bu uchta call ketma-ket emas, parallel bajarilishi mumkin.

Blocking modelda yomon variant:

UserDto user = userClient.getUser(userId);
CartDto cart = cartClient.getCart(userId);
BonusDto bonus = bonusClient.getBonus(userId);

Agar har biri 300 ms bo‘lsa, umumiy 900 ms bo‘lishi mumkin. Reactive parallel chaqiriqda taxminan 300 ms atrofida tugashi mumkin.


17. Error handling

Reactive’da exceptionni try/catch bilan emas, operatorlar bilan boshqarasiz.

onErrorReturn

Xato bo‘lsa default qiymat qaytaradi.

return paymentClient.charge(request)
        .onErrorReturn(new PaymentResponse("FAILED"));

onErrorResume

Xato bo‘lsa boshqa pipeline’ga o‘tadi.

return paymentClient.charge(request)
        .onErrorResume(ex -> {
            log.warn("Payment failed, fallback used", ex);
            return Mono.just(new PaymentResponse("PAYMENT_UNAVAILABLE"));
        });

doOnError

Faqat log yoki side effect uchun.

return paymentClient.charge(request)
        .doOnError(ex -> log.error("Payment error", ex));

Muhim:

doOnError xatoni yutmaydi. Faqat kuzatadi. Xato pipeline’da davom etadi.


18. Timeout

Reactive’da timeout juda muhim.

return paymentClient.charge(request)
        .timeout(Duration.ofSeconds(2))
        .onErrorResume(TimeoutException.class, ex ->
                Mono.just(new PaymentResponse("TIMEOUT"))
        );

Agar timeout qo‘yilmasa, request uzoq osilib qolishi mumkin.


19. Retry

Retry ehtiyotkorlik bilan ishlatiladi.

return paymentClient.charge(request)
        .retryWhen(
                Retry.backoff(3, Duration.ofMillis(200))
                        .maxBackoff(Duration.ofSeconds(2))
        );

Lekin payment kabi non-idempotent operationlarda retry xavfli.

Yaxshi qoida:

Retry faqat idempotent yoki xavfsiz operationlarda.
Payment bo‘lsa idempotency key shart.

20. Blocking call muammosi

Reactive app ichida blocking call qilish eng katta xatolardan biri.

Yomon:

public Mono<UserDto> getUser(Long id) {
    User user = blockingJpaRepository.findById(id).orElseThrow();
    return Mono.just(mapper.toDto(user));
}

Bu WebFlux event loop threadini block qiladi.

Yana yomon:

return userReactiveRepository.findById(id)
        .map(user -> {
            Thread.sleep(1000);
            return mapper.toDto(user);
        });

Event loopni block qilish butun app latency’sini buzadi.


21. Blocking kodni qanday ajratish mumkin?

Agar majburan blocking library ishlatish kerak bo‘lsa, uni alohida schedulerga o‘tkazish mumkin:

public Mono<UserDto> getUser(Long id) {
    return Mono.fromCallable(() -> blockingJpaRepository.findById(id).orElseThrow())
            .subscribeOn(Schedulers.boundedElastic())
            .map(mapper::toDto);
}

boundedElastic blocking I/O uchun mo‘ljallangan.

Lekin bu reactive’ning toza foydasini kamaytiradi. Agar app ko‘p joyda blocking bo‘lsa, WebFlux tanlash noto‘g‘ri bo‘lishi mumkin.


22. R2DBC

R2DBC - relational database bilan reactive non-blocking ishlash uchun API.

JDBC blocking. R2DBC reactive.

JDBC:

Thread DB javobini kutadi

R2DBC:

Thread DB javobini kutib band bo‘lmaydi

Spring Data R2DBC repository:

public interface UserRepository extends ReactiveCrudRepository<User, Long> {
    Flux<User> findByStatus(String status);
}

Service:

public Mono<UserDto> findById(Long id) {
    return userRepository.findById(id)
            .map(mapper::toDto);
}

23. R2DBC cheklovlari

R2DBC har doim JPA/Hibernate o‘rnini to‘liq bosmaydi.

JPA/Hibernate’da bor:

Entity relationship
Lazy loading
First-level cache
Dirty checking
JPQL
Criteria API

R2DBC’da bular ancha cheklangan.

R2DBC ko‘proq:

simple query
high concurrency I/O
reactive pipeline
manual mapping

uchun mos.

Murakkab domain model va ORM qulayliklari kerak bo‘lsa, JPA hali ham kuchli.


24. Reactive transaction

Reactive transaction ham bor, lekin blocking transactiondan farq qiladi.

@Transactional
public Mono<Order> createOrder(CreateOrderRequest request) {
    return orderRepository.save(new Order(request))
            .flatMap(order -> outboxRepository.save(toEvent(order)).thenReturn(order));
}

Spring reactive transaction contextni thread-local orqali emas, Reactor context orqali olib yuradi.

Shuning uchun oddiy blocking mental model har doim ishlamaydi.


25. Reactor Context

Reactive’da bitta request har xil threadlarda davom etishi mumkin. Shuning uchun ThreadLocal har doim ishonchli emas.

Reactor’da Context bor.

Misol:

return Mono.deferContextual(ctx -> {
    String correlationId = ctx.get("correlationId");
    return Mono.just("correlationId=" + correlationId);
});

Context yozish:

return service.call()
        .contextWrite(ctx -> ctx.put("correlationId", "abc-123"));

Security, tracing, transaction kabi narsalarda context muhim.


26. ThreadLocal va MDC muammosi

Traditional Spring MVC’da MDC ko‘pincha thread-localga tayanadi.

Reactive’da request bir threaddan boshqasiga o‘tishi mumkin:

request start: thread-1
db response: thread-3
http response: thread-5

Shuning uchun MDC/correlation ID ni reactive context bilan to‘g‘ri integratsiya qilish kerak.

OpenTelemetry va zamonaviy Spring stack bu masalada ancha yordam beradi.


27. Schedulers

Reactor’da scheduler - ish qaysi thread poolda bajarilishini belgilaydi.

Ko‘p ishlatiladiganlar:

Scheduler

Qachon

parallel()

CPU-bound qisqa ishlar

boundedElastic()

Blocking I/O

single()

Bitta thread kerak bo‘lsa

immediate()

Hozirgi thread

Misol:

return Mono.fromCallable(() -> heavyCalculation())
        .subscribeOn(Schedulers.parallel());

Blocking call:

return Mono.fromCallable(() -> blockingFileRead())
        .subscribeOn(Schedulers.boundedElastic());

28. publishOn vs subscribeOn

Bu senior darajada bilish kerak bo‘lgan mavzu.

subscribeOn

Pipeline boshlanishi qaysi scheduler’da bo‘lishini belgilaydi.

Mono.fromCallable(() -> blockingCall())
        .subscribeOn(Schedulers.boundedElastic());

publishOn

Undan keyingi operatorlar qaysi scheduler’da bajarilishini belgilaydi.

return userRepository.findById(id)
        .publishOn(Schedulers.parallel())
        .map(this::heavyMapping);

Oddiy qoida:

subscribeOn → source qayerda ishlaydi
publishOn → keyingi operatorlar qayerda ishlaydi

29. Hot vs Cold Publisher

Cold publisher

Har bir subscriber uchun ish qaytadan boshlanadi.

Flux<Integer> numbers = Flux.range(1, 3);

numbers.subscribe(System.out::println);
numbers.subscribe(System.out::println);

Har subscriber 1,2,3 ni alohida oladi.

Hot publisher

Data subscriber bor-yo‘qligidan qat’i nazar chiqaveradi.

Misol:

live stock price
chat messages
sensor events
Kafka stream

Agar subscriber kech ulansa, oldingi eventlarni o‘tkazib yuborishi mumkin.


30. WebFlux streaming

WebFlux streaming response qaytara oladi.

@GetMapping(value = "/events", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<ServerSentEvent<String>> events() {
    return Flux.interval(Duration.ofSeconds(1))
            .map(i -> ServerSentEvent.builder("event-" + i).build());
}

Bu har sekundda event yuboradi.

Mos joylar:

  • live notification;

  • monitoring dashboard;

  • real-time status;

  • chat;

  • progress update.


31. Functional routing

WebFlux’da controller o‘rniga functional endpoint ishlatish mumkin.

@Configuration
public class UserRoutes {

    @Bean
    RouterFunction<ServerResponse> routes(UserHandler handler) {
        return RouterFunctions.route()
                .GET("/users/{id}", handler::getUser)
                .GET("/users", handler::getUsers)
                .build();
    }
}

Handler:

@Component
@RequiredArgsConstructor
public class UserHandler {

    private final UserService userService;

    public Mono<ServerResponse> getUser(ServerRequest request) {
        Long id = Long.valueOf(request.pathVariable("id"));

        return userService.findById(id)
                .flatMap(user -> ServerResponse.ok().bodyValue(user))
                .switchIfEmpty(ServerResponse.notFound().build());
    }

    public Mono<ServerResponse> getUsers(ServerRequest request) {
        return ServerResponse.ok().body(userService.findAll(), UserDto.class);
    }
}

Bu uslub kamroq ishlatiladi, lekin gateway/highly functional style yoqtiradiganlarda uchraydi.


32. Testing: StepVerifier

Reactive pipeline’ni test qilish uchun StepVerifier ishlatiladi.

@Test
void shouldReturnUppercase() {
    Mono<String> result = Mono.just("hello")
            .map(String::toUpperCase);

    StepVerifier.create(result)
            .expectNext("HELLO")
            .verifyComplete();
}

Error test:

@Test
void shouldReturnError() {
    Mono<String> result = Mono.error(new IllegalArgumentException("bad"));

    StepVerifier.create(result)
            .expectError(IllegalArgumentException.class)
            .verify();
}

33. WebTestClient

WebFlux endpoint test qilish uchun:

@WebFluxTest(UserController.class)
class UserControllerTest {

    @Autowired
    private WebTestClient webTestClient;

    @Test
    void shouldReturnUser() {
        webTestClient.get()
                .uri("/users/1")
                .exchange()
                .expectStatus().isOk()
                .expectBody()
                .jsonPath("$.id").isEqualTo(1);
    }
}

WebTestClient Spring MVC testlarida ham ishlatilishi mumkin, lekin WebFlux’da juda tabiiy.


34. Reactive va database pool

Reactive app’da ham DB resurs cheksiz emas.

Agar R2DBC pool kichik bo‘lsa:

Request ko‘p
DB connection kam
Pending request oshadi
Latency oshadi

Shuning uchun monitoring kerak:

R2DBC pool usage
pending acquire
query latency
timeout

Reactive thread kam ishlatadi, lekin DB connection, CPU, network baribir cheklangan.


35. Reactive performance xatolari

Xato 1: Event loopni block qilish

Thread.sleep(1000)
blockingRepository.findById()
file.readAllBytes()
RestTemplate call

Bular WebFlux event loopda ishlasa, tizim sekinlashadi.


Xato 2: .block() ishlatish

Yomon:

User user = userRepository.findById(id).block();

Agar reactive pipeline ichida .block() ishlatilsa, non-blocking model buziladi.

Controller’da ayniqsa yomon:

@GetMapping("/{id}")
public UserDto getUser(@PathVariable Long id) {
    return userService.findById(id).block();
}

To‘g‘ri:

@GetMapping("/{id}")
public Mono<UserDto> getUser(@PathVariable Long id) {
    return userService.findById(id);
}

Xato 3: Reactive + JPA aralashtirish

WebFlux controller, lekin ichida JPA blocking repository:

return Mono.just(jpaRepository.findById(id));

Bu reactive emas. Bu blocking kodni Mono ichiga o‘rash xolos.


Xato 4: Business logicni haddan tashqari chain qilish

Reactive kod o‘qilishi qiyinlashishi mumkin.

Yomon:

return a.flatMap(x -> b(x).flatMap(y -> c(y).flatMap(z -> d(z))));

Yaxshiroq:

  • methodlarga bo‘lish;

  • nomlangan private methodlar ishlatish;

  • pipeline’ni o‘qiladigan qilish.


Xato 5: Error handling yo‘q

Reactive’da exception pipeline ichida oqadi. Har doim timeout, retry, fallback strategiyasi bo‘lishi kerak.


36. Virtual threads vs Reactive

Java 21 bilan virtual threads production darajada kuchli variant bo‘ldi.

Virtual thread bilan blocking kod yozasiz, lekin har request uchun arzon virtual thread ishlaydi.

Spring MVC + virtual threads:

spring:
  threads:
    virtual:
      enabled: true

Kod oddiy blocking ko‘rinishda qoladi:

@GetMapping("/{id}")
public UserDto getUser(@PathVariable Long id) {
    User user = userRepository.findById(id).orElseThrow();
    return mapper.toDto(user);
}

Lekin thread resursi platform threaddan ancha arzon.


36.1. Reactive afzalligi

Reactive yaxshi:

Streaming
Backpressure
High concurrency non-blocking I/O
Gateway
WebSocket
Event stream
End-to-end reactive stack

36.2. Virtual thread afzalligi

Virtual thread yaxshi:

Oddiy blocking kod
Debugging osonroq
JPA/JDBC bilan mos
Teamga tushunarli
Migration osonroq

36.3. Qaysi birini tanlash kerak?

Holat

Yaxshi tanlov

Oddiy CRUD + JDBC/JPA

Spring MVC + virtual threads

Ko‘p tashqi HTTP call

WebFlux yoki virtual threads

Streaming/backpressure kerak

WebFlux/Reactor

WebSocket/live events

WebFlux

Team reactive bilmaydi

Virtual threads

End-to-end reactive DB/client bor

WebFlux

Legacy blocking library ko‘p

Virtual threads

Muhim xulosa:

Virtual threads reactive’ni butunlay yo‘q qilmaydi. Lekin oddiy backend CRUD va blocking I/O uchun reactive’ga ehtiyojni kamaytiradi.


37. Real production tanlov misollari

Ecommerce order service

Spring MVC + JPA + virtual threads

Ko‘pincha yetarli. Chunki business logic va transactionlar oddiyroq.

API Gateway

Spring Cloud Gateway / WebFlux

Chunki ko‘p concurrent connection va non-blocking proxy muhim.

Notification streaming

WebFlux + Server-Sent Events yoki WebSocket

Chunki clientlarga real-time event kerak.

Analytics event processing

Kafka Streams / Reactor / async pipeline

Chunki oqim bilan ishlash kerak.


38. Reactive observability

Reactive app’da observability yanada muhim.

Kuzatish kerak:

event loop blocked time
request latency
operator error rate
WebClient latency
R2DBC query latency
connection pool pending
retry count
timeout count
backpressure/drop count

Log va tracingda context propagation muhim, chunki bitta request bir nechta threadlarda davom etishi mumkin.


39. Reactive security

Spring Security WebFlux’da ham bor, lekin classlar farq qiladi.

MVC:

SecurityFilterChain

WebFlux:

SecurityWebFilterChain

Misol:

@Configuration
@EnableWebFluxSecurity
public class SecurityConfig {

    @Bean
    SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
        return http
                .csrf(ServerHttpSecurity.CsrfSpec::disable)
                .authorizeExchange(exchange -> exchange
                        .pathMatchers("/public/**").permitAll()
                        .pathMatchers("/admin/**").hasRole("ADMIN")
                        .anyExchange().authenticated()
                )
                .oauth2ResourceServer(oauth2 -> oauth2.jwt(Customizer.withDefaults()))
                .build();
    }
}

Reactive security context ham thread-localga emas, reactive contextga bog‘liq.


40. Senior checklist

Reactive ishlatishdan oldin tekshir:

Haqiqatan non-blocking kerakmi?
Team Reactor/WebFlux’ni tushunadimi?
DB layer R2DBCmi yoki blocking JPAmi?
Tashqi clientlar WebClientmi?
Pipeline ichida .block() yo‘qmi?
Thread.sleep yoki blocking call yo‘qmi?
Timeout/retry/fallback bor-mi?
Backpressure kerakmi?
Observability sozlanganmi?
MDC/context propagation to‘g‘rimi?
Virtual threads oddiyroq yechim bo‘la oladimi?

41. Interview savollari

Asosiy tushunchalar

  • Reactive programming nima?

  • Blocking va non-blocking farqi nima?

  • Reactive Streams spec nima?

  • Backpressure nima?

  • Mono va Flux farqi nima?

Reactor

  • map va flatMap farqi?

  • subscribeOn va publishOn farqi?

  • Hot va cold publisher nima?

  • doOnError, onErrorResume, onErrorReturn farqi?

  • .block() nima uchun xavfli?

Spring WebFlux

  • Spring MVC va WebFlux farqi?

  • WebClient va RestTemplate farqi?

  • WebFlux ichida JPA ishlatish xavflimi?

  • R2DBC nima?

  • WebFlux security MVC securitydan nimasi bilan farq qiladi?

Architecture

  • Reactive qachon kerak?

  • Virtual threads reactive o‘rnini bosadimi?

  • Gateway uchun qaysi model yaxshi?

  • Oddiy CRUD uchun reactive kerakmi?

  • Reactive debugging qiyinchiliklari nima?


42. Amaliy topshiriqlar

Topshiriq 1: Mono va Flux

Quyidagilarni yozing:

Mono<String> → uppercase
Flux<Integer> → faqat juft sonlar
Flux<User> → UserDto ga map

Topshiriq 2: WebFlux CRUD

Endpointlar:

GET /users/{id} → Mono<UserDto>
GET /users → Flux<UserDto>
POST /users → Mono<UserDto>

Repository sifatida vaqtincha in-memory map yoki reactive repository ishlating.


Topshiriq 3: WebClient

user-service, cart-service, bonus-service ni parallel chaqirib:

GET /checkout-summary/{userId}

qaytaring.

Talab:

Mono.zip ishlatilsin
Har clientda timeout bo‘lsin
Fallback bo‘lsin

Topshiriq 4: Blocking xatoni topish

WebFlux app ichida ataylab:

Thread.sleep(1000);

qo‘ying.

Keyin load test qilib latency qanday buzilishini kuzating. So‘ng uni:

Mono.delay(...)

yoki boundedElastic bilan almashtiring.


Topshiriq 5: StepVerifier

Service methodlarni test qiling:

success case
empty case
error case
timeout case

43. Qisqa xulosa

Reactive programming Senior Java developer uchun kuchli, lekin ehtiyotkorlik bilan ishlatiladigan skill.

Asosiy fikrlar:

Reactive - async data oqimi bilan ishlash
Mono - 0 yoki 1 qiymat
Flux - 0 dan N gacha qiymat
WebFlux - Spring reactive web framework
WebClient - non-blocking HTTP client
R2DBC - reactive relational DB access
Backpressure - consumer ulguradigan tezlikda data olish
.block() - reactive pipeline ichida xavfli
Virtual threads - oddiy blocking kod uchun kuchli alternativ

Eng muhim qoida:

Reactive programming’ni "zamonaviy" bo‘lgani uchun emas, aniq muammo uchun tanlang: high concurrency, non-blocking I/O, streaming yoki backpressure kerak bo‘lsa. Oddiy CRUD backendda virtual threads ko‘pincha soddaroq va arzonroq yechim bo‘ladi.