Spring WebFlux - Spring’ning reactive, non-blocking web framework’i. Bu mavzu quyidagilarni o‘z ichiga oladi: Mono & Flux, reactive controllers, RouterFunction, Reactive Spring Security, R2DBC, SSE/WebSocket, WebFlux vs Virtual Threads.
Spring rasmiy hujjatlarida WebFlux Spring Framework 5.0'da qo‘shilgani, u fully non-blocking, Reactive Streams back pressure qo‘llashi va Netty kabi serverlarda ishlashi aytiladi. (Home)
1. Reactive programming nima?
Oddiy Spring MVC modeli:
Request keladi
↓
Thread olinadi
↓
DB yoki external API javobini kutadi
↓
Response qaytaradi
↓
Thread bo‘shaydiMuammo:
Thread kutib turibdi.
Ish qilmayapti, lekin band.Reactive modelda esa:
Request keladi
↓
Operation boshlanadi
↓
Thread kutib turmaydi
↓
Javob tayyor bo‘lganda callback/event orqali davom etadiYa’ni WebFlux’ning asosiy g‘oyasi:
Kamroq thread bilan ko‘proq concurrent request ishlatishLekin bu hamma joyda tezroq degani emas. WebFlux faqat chain to‘liq non-blocking bo‘lsa foyda beradi.
2. Spring MVC vs Spring WebFlux
Mezoni | Spring MVC | Spring WebFlux |
|---|---|---|
Model | Blocking | Non-blocking |
Server | Tomcat/Servlet | Netty, Undertow, Servlet 3.1+ |
Thread modeli | Request per thread | Event-loop |
Return type | DTO, ResponseEntity | Mono, Flux |
DB access | JDBC/JPA | R2DBC, Reactive Mongo |
O‘rganish | Osonroq | Murakkabroq |
Debug qilish | Osonroq | Qiyinroq |
Best use-case | CRUD, enterprise app | High concurrency, streaming, async I/O |
3. Mono va Flux
Spring WebFlux Project Reactor ustida qurilgan. Spring hujjatlarida Reactor WebFlux uchun asosiy reactive library ekani, Mono 0..1 element, Flux esa 0..N element bilan ishlashi aytiladi. (Home)
Mono nima?
Mono<T> - bitta yoki nol natija.
Mono<User> = bitta user yoki emptyMisol:
@GetMapping("/{id}")
public Mono<UserDto> getUser(@PathVariable Long id) {
return userService.findById(id);
}Bu degani:
Hozir UserDto qaytmayapti.
Kelajakda UserDto yoki empty natija keladi.Flux nima?
Flux<T> - ko‘p natija yoki nol natija.
Project Reactor API hujjatida Flux 0 dan N gacha element chiqaradigan Reactive Streams Publisher sifatida ta’riflanadi. (projectreactor.io)
@GetMapping
public Flux<UserDto> getUsers() {
return userService.findAll();
}Bu:
0, 1, 10, 1000 yoki undan ko‘p UserDto oqimi4. Oddiy reactive controller
@RestController
@RequestMapping("/api/products")
@RequiredArgsConstructor
public class ProductController {
private final ProductService productService;
@GetMapping("/{id}")
public Mono<ProductResponse> getById(@PathVariable Long id) {
return productService.getById(id);
}
@GetMapping
public Flux<ProductResponse> getAll() {
return productService.getAll();
}
@PostMapping
public Mono<ProductResponse> create(@RequestBody Mono<CreateProductRequest> request) {
return request.flatMap(productService::create);
}
}E’tibor bering:
@RequestBody Mono<CreateProductRequest> requestBu request body ham reactive oqim sifatida olinayotganini bildiradi.
Oddiyroq yozish ham mumkin:
@PostMapping
public Mono<ProductResponse> create(@RequestBody CreateProductRequest request) {
return productService.create(request);
}Lekin to‘liq reactive style’da body’ni ham Mono qilish mumkin.
5. Reactive service layer
@Service
@RequiredArgsConstructor
public class ProductService {
private final ProductRepository productRepository;
public Mono<ProductResponse> getById(Long id) {
return productRepository.findById(id)
.map(this::toResponse)
.switchIfEmpty(Mono.error(new ProductNotFoundException(id)));
}
public Flux<ProductResponse> getAll() {
return productRepository.findAll()
.map(this::toResponse);
}
public Mono<ProductResponse> create(CreateProductRequest request) {
Product product = new Product();
product.setName(request.name());
product.setPrice(request.price());
return productRepository.save(product)
.map(this::toResponse);
}
private ProductResponse toResponse(Product product) {
return new ProductResponse(
product.getId(),
product.getName(),
product.getPrice()
);
}
}Bu yerda muhim joy:
.map(this::toResponse)Bu oddiy transformatsiya.
.flatMap(...)esa ichida yana Mono yoki Flux qaytadigan async operation bo‘lsa ishlatiladi.
6. map vs flatMap
map
Oddiy qiymatni boshqa qiymatga aylantiradi.
Mono<User> userMono = userRepository.findById(1L);
Mono<UserDto> dtoMono = userMono.map(user -> new UserDto(user.getName()));User → UserDtoflatMap
Ichida yana reactive operation bo‘lsa ishlatiladi.
Mono<OrderResponse> result = userRepository.findById(userId)
.flatMap(user -> orderRepository.findByUserId(user.getId()))
.map(order -> new OrderResponse(order.getId()));User → Mono<Order> → OrderResponseAgar map ishlatilsa, natija noto‘g‘ri bo‘ladi:
Mono<Mono<Order>> // yomonflatMap uni tekislaydi:
Mono<Order> // yaxshi7. Back pressure
Back pressure - consumer qancha data qabul qila olsa, producer shuncha yuborishi.
Masalan:
Producer: sekundiga 10 000 event chiqaradi
Consumer: sekundiga 1 000 event ishlay oladiAgar back pressure bo‘lmasa:
Memory to‘lib ketadi
Queue kattalashadi
App sekinlashadi yoki yiqiladiReactive Streams’da consumer producer’ga shunday signal beradi:
Menga hozir 100 ta element yubor.
Keyin yana so‘rayman.Spring WebFlux’ning muhim kuchi shunda: u non-blocking back pressure modelini qo‘llaydi. (Home)
8. RouterFunction va functional endpoints
WebFlux’da endpoint yozishning 2 usuli bor:
1. Annotation-based: @RestController
2. Functional style: RouterFunction + HandlerFunctionSpring hujjatlarida RouterFunction @RequestMappingga o‘xshashi, lekin faqat mapping emas, behavior ham berishi aytiladi. (Home)
RouterFunction misol
@Configuration
public class ProductRouter {
@Bean
public RouterFunction<ServerResponse> productRoutes(ProductHandler handler) {
return RouterFunctions.route()
.GET("/api/products/{id}", handler::getById)
.GET("/api/products", handler::getAll)
.POST("/api/products", handler::create)
.build();
}
}Handler:
@Component
@RequiredArgsConstructor
public class ProductHandler {
private final ProductService productService;
public Mono<ServerResponse> getById(ServerRequest request) {
Long id = Long.valueOf(request.pathVariable("id"));
return productService.getById(id)
.flatMap(product ->
ServerResponse.ok().bodyValue(product)
);
}
public Mono<ServerResponse> getAll(ServerRequest request) {
return ServerResponse.ok()
.body(productService.getAll(), ProductResponse.class);
}
public Mono<ServerResponse> create(ServerRequest request) {
return request.bodyToMono(CreateProductRequest.class)
.flatMap(productService::create)
.flatMap(product ->
ServerResponse.ok().bodyValue(product)
);
}
}Qachon RouterFunction yaxshi?
Holat | Mosligi |
|---|---|
Functional style yoqsa | Yaxshi |
Lightweight routing kerak bo‘lsa | Yaxshi |
Gateway/proxy style endpointlar | Yaxshi |
Oddiy CRUD team uchun |
|
Katta team, standard Spring style |
|
9. R2DBC: reactive database access
WebFlux ishlatib, lekin ichida JPA/JDBC ishlatsangiz, chain blocking bo‘lib qoladi.
Yomon kombinatsiya:
WebFlux + JPA/JDBCSabab:
JPA/JDBC blocking API.
Event-loop thread bloklanadi.To‘g‘riroq kombinatsiya:
WebFlux + R2DBC
WebFlux + Reactive MongoDB
WebFlux + Reactive Redis
WebFlux + WebClientSpring Data R2DBC relational database bilan reactive programming’ni birlashtirish uchun ishlatiladi va DatabaseClient kabi yuqori darajali abstraction beradi. (Home)
R2DBC repository misol
@Table("products")
public class Product {
@Id
private Long id;
private String name;
private BigDecimal price;
// getters/setters
}public interface ProductRepository extends ReactiveCrudRepository<Product, Long> {
Flux<Product> findByNameContainingIgnoreCase(String name);
}Controller:
@GetMapping("/search")
public Flux<ProductResponse> search(@RequestParam String name) {
return productRepository.findByNameContainingIgnoreCase(name)
.map(product -> new ProductResponse(
product.getId(),
product.getName(),
product.getPrice()
));
}10. Reactive transaction
R2DBC’da transaction ham reactive bo‘lishi kerak.
@Service
@RequiredArgsConstructor
public class OrderService {
private final TransactionalOperator transactionalOperator;
private final OrderRepository orderRepository;
private final PaymentRepository paymentRepository;
public Mono<OrderResponse> createOrder(CreateOrderRequest request) {
Mono<OrderResponse> flow = orderRepository.save(new Order(request.userId()))
.flatMap(order ->
paymentRepository.save(new Payment(order.getId(), request.amount()))
.map(payment -> new OrderResponse(order.getId(), payment.getStatus()))
);
return transactionalOperator.transactional(flow);
}
}Oddiy @Transactional har doim kutilganidek ishlamaydi, chunki reactive flow keyinroq execute bo‘ladi. Senior darajada TransactionalOperatorni bilish foydali.
11. WebClient
WebFlux’da boshqa service chaqirish uchun WebClient ishlatiladi.
RestTemplate blocking.WebClient non-blocking.
@Component
@RequiredArgsConstructor
public class PaymentClient {
private final WebClient webClient;
public Mono<PaymentResponse> createPayment(PaymentRequest request) {
return webClient.post()
.uri("/api/payments")
.bodyValue(request)
.retrieve()
.bodyToMono(PaymentResponse.class);
}
}Config:
@Configuration
public class WebClientConfig {
@Bean
public WebClient paymentWebClient(WebClient.Builder builder) {
return builder
.baseUrl("http://payment-service")
.build();
}
}12. Reactive error handling
Reactive chain’da error tashlash boshqacha ko‘rinadi.
public Mono<ProductResponse> getById(Long id) {
return productRepository.findById(id)
.switchIfEmpty(Mono.error(new ProductNotFoundException(id)))
.map(this::toResponse);
}Fallback:
public Mono<ProductResponse> getCachedProduct(Long id) {
return productClient.getProduct(id)
.onErrorResume(ex -> cacheService.getProductFromCache(id));
}Default qiymat:
public Mono<BigDecimal> getRating(Long productId) {
return ratingClient.getRating(productId)
.onErrorReturn(BigDecimal.ZERO);
}Retry:
public Mono<PaymentResponse> pay(PaymentRequest request) {
return paymentClient.createPayment(request)
.timeout(Duration.ofSeconds(2))
.retryWhen(Retry.backoff(2, Duration.ofMillis(300)))
.onErrorResume(ex ->
Mono.just(new PaymentResponse("PENDING"))
);
}Muhim: payment kabi operatsiyalarda retry faqat idempotency bilan xavfsiz.
13. Reactive Spring Security
WebFlux’da security ham reactive stack bilan ishlaydi.
Servlet stack’dagi asosiy class:
SecurityFilterChainReactive stack’da:
SecurityWebFilterChainMisol:
@Configuration
@EnableWebFluxSecurity
public class SecurityConfig {
@Bean
SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
return http
.csrf(ServerHttpSecurity.CsrfSpec::disable)
.authorizeExchange(exchange -> exchange
.pathMatchers("/api/public/**").permitAll()
.pathMatchers("/api/admin/**").hasRole("ADMIN")
.anyExchange().authenticated()
)
.oauth2ResourceServer(oauth2 ->
oauth2.jwt(Customizer.withDefaults())
)
.build();
}
}MVC’dagi security code’ni to‘g‘ridan-to‘g‘ri WebFlux’ga ko‘chirib bo‘lmaydi. Stack boshqacha.
14. SSE bilan streaming
SSE - Server-Sent Events. Server browser’ga doimiy event oqimi yuboradi.
Masalan:
Order status update
Notification stream
Live dashboard
Crypto price stream
Delivery trackingWebFlux’da SSE uchun Flux juda mos.
@GetMapping(value = "/events", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<OrderEvent> streamOrderEvents() {
return Flux.interval(Duration.ofSeconds(1))
.map(i -> new OrderEvent("ORDER_UPDATED", Instant.now()));
}Response bitta JSON emas, oqim:
event 1
event 2
event 3
...15. WebSocket bilan WebFlux
SSE bir tomonlama:
Server → ClientWebSocket ikki tomonlama:
Client ↔ ServerWebSocket real-time chat, trading, multiplayer, live collaboration kabi joylarda ishlatiladi.
Soddalashtirilgan WebSocket handler:
@Component
public class ChatWebSocketHandler implements WebSocketHandler {
@Override
public Mono<Void> handle(WebSocketSession session) {
Flux<WebSocketMessage> messages = session.receive()
.map(WebSocketMessage::getPayloadAsText)
.map(text -> "Echo: " + text)
.map(session::textMessage);
return session.send(messages);
}
}Config:
@Configuration
public class WebSocketConfig {
@Bean
public HandlerMapping webSocketMapping(ChatWebSocketHandler handler) {
Map<String, WebSocketHandler> map = new HashMap<>();
map.put("/ws/chat", handler);
SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping();
mapping.setUrlMap(map);
mapping.setOrder(10);
return mapping;
}
}16. WebFlux’da eng katta xato: blocking code
WebFlux ichida bular xavfli:
Thread.sleep(1000);userRepository.findById(id); // JPA blockingrestTemplate.getForObject(...); // blockingfile.readAllBytes(); // blocking I/OBunday code event-loop thread’ni bloklaydi.
Blocking code’ni alohida scheduler’ga chiqarish
Ba’zida blocking API’dan qochib bo‘lmaydi. Masalan eski SDK.
public Mono<LegacyResponse> callLegacySdk() {
return Mono.fromCallable(() -> legacySdk.call())
.subscribeOn(Schedulers.boundedElastic());
}Bu event-loop’ni himoya qiladi.
Lekin bu WebFlux’ni "to‘liq reactive" qilmaydi. Faqat blocking qismni alohida thread pool’ga chiqaradi.
17. WebFlux vs Virtual Threads
Java 21'dan keyin savol ko‘p beriladi:
WebFlux kerakmi yoki virtual threads yetadimi?Virtual Threads modeli
Virtual threads blocking code’ni arzonlashtiradi.
Spring MVC + virtual threads + JDBCko‘p CRUD applicationlar uchun juda yaxshi variant bo‘lishi mumkin.
WebFlux modeli
WebFlux esa non-blocking I/O model.
WebFlux + R2DBC + WebClient + Reactive Redisyuqori concurrency va streaming uchun kuchli.
Taqqoslash
Holat | Yaxshi tanlov |
|---|---|
Oddiy CRUD + PostgreSQL/JPA | Spring MVC + Virtual Threads |
Existing JPA project | Spring MVC |
Ko‘p external API chaqirish | WebFlux yoki MVC + virtual threads |
Streaming response | WebFlux |
SSE/WebSocket heavy workload | WebFlux |
Reactive Mongo/R2DBC ishlatilsa | WebFlux |
Team reactive bilmasa | MVC |
Debuggability muhim bo‘lsa | MVC |
Event-loop va back pressure kerak bo‘lsa | WebFlux |
18. Qachon WebFlux ishlatish kerak?
WebFlux yaxshi tanlov:
[+] Ko‘p concurrent connection bor
[+] External API call ko‘p
[+] Streaming kerak
[+] SSE/WebSocket bor
[+] Reactive DB driver ishlatyapsiz
[+] Back pressure kerak
[+] Thread sonini kam tutish muhim19. Qachon WebFlux ishlatmaslik kerak?
WebFlux shart emas:
[-] Oddiy CRUD application
[-] JPA/Hibernate asosiy persistence bo‘lsa
[-] Team reactive programming bilmasa
[-] Debuggability muhim bo‘lsa
[-] Blocking library’lar ko‘p bo‘lsa
[-] Performance muammo DB query yoki index’da bo‘lsaEng katta qoida:
WebFlux performance muammolarni sehrli hal qilmaydi.
Agar bottleneck DB index, yomon query, N+1 yoki noto‘g‘ri architecture bo‘lsa,
WebFlux yordam bermaydi.20. Real project example
Masalan, notification system:
Client
↓
Gateway
↓
Notification Service
├── WebFlux endpoint
├── SSE stream
├── Reactive Redis Pub/Sub
└── MongoDB reactive repositoryEndpoint:
@GetMapping(value = "/notifications/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<NotificationEvent> stream(@AuthenticationPrincipal Jwt jwt) {
String userId = jwt.getSubject();
return notificationService.streamForUser(userId);
}Service:
public Flux<NotificationEvent> streamForUser(String userId) {
return redisNotificationReceiver.receive(userId)
.map(message -> new NotificationEvent(
message.title(),
message.body(),
Instant.now()
));
}Bu WebFlux uchun mos use-case:
Ko‘p user ochiq connection ushlab turadi.
Server har bir user uchun alohida blocking thread ushlab turmaydi.21. Minimal project dependencies
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-r2dbc</artifactId>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>r2dbc-postgresql</artifactId>
</dependency>
</dependencies>Config:
spring:
r2dbc:
url: r2dbc:postgresql://localhost:5432/shop
username: postgres
password: postgres22. Senior interview javob
Agar suhbatda so‘rasa:
Spring WebFlux nima?
Javob:
Spring WebFlux - Spring’ning reactive, non-blocking web framework’i.
U Project Reactor ustida ishlaydi va Mono/Flux tiplari orqali async data oqimlarini boshqaradi.
Spring MVC request-per-thread modeliga yaqin bo‘lsa, WebFlux event-loop va non-blocking I/O modeliga yaqin.
WebFlux ko‘p concurrent connection, streaming, SSE/WebSocket, ko‘p external API call va reactive database driverlar bilan foydali.
Lekin JPA/JDBC kabi blocking stack bilan ishlatilsa, foydasi kamayadi yoki event-loop bloklanadi.
Java 21 virtual threads’dan keyin oddiy CRUD/JPA loyihalarda MVC + virtual threads ko‘pincha sodda va yaxshi tanlov.
WebFlux esa haqiqiy non-blocking chain, back pressure va streaming kerak bo‘lganda kuchli.23. Qisqa xulosa
Spring WebFlux’ni bitta gap bilan:
WebFlux - ko‘p request’ni kam thread bilan, non-blocking usulda boshqarish uchun.Eng muhim tushunchalar:
Tushuncha | Ma’nosi |
|---|---|
| 0 yoki 1 natija |
| 0 dan N gacha natija |
| Oddiy transformatsiya |
| Async/reactive transformatsiya |
Back pressure | Consumer producer’ni nazorat qiladi |
R2DBC | Reactive relational DB access |
WebClient | Non-blocking HTTP client |
SSE | Serverdan clientga event stream |
WebSocket | Ikki tomonlama real-time aloqa |
Senior darajada asosiy qaror:
WebFlux ishlatishdan oldin savol:
Butun chain non-blockingmi?
Agar yo‘q bo‘lsa, WebFlux shart emas.