Messaging with Spring quyidagilarni qamrab oladi: Spring Events, @EventListener, ApplicationEventPublisher, @Async bilan eventlar, Spring AMQP / RabbitMQ, Spring Kafka, @KafkaListener, KafkaTemplate, Dead Letter Queue, va retry policies.
1. Messaging nima?
Messaging - application ichida yoki service’lar orasida ma’lumotni xabar/event ko‘rinishida uzatish.
Oddiy chaqiruv:
OrderService -> EmailService.send()Messaging bilan:
OrderService -> OrderCreatedEvent -> Email listenerYa’ni OrderService email yuborishni o‘zi bilmaydi. U faqat:
"Order yaratildi"
deb event chiqaradi. Kim eshitadi, nima qiladi - bu boshqa komponentlarning vazifasi.
2. Nega messaging kerak?
Masalan, order yaratildi:
public void createOrder() {
orderRepository.save(order);
emailService.sendEmail(order);
smsService.sendSms(order);
analyticsService.track(order);
bonusService.addBonus(order);
}Bu yomonlashib boradi:
OrderServicejuda ko‘p narsani bilib qoladi;email xato bersa order creationga ta’sir qilishi mumkin;
kod tightly coupled bo‘ladi;
keyin yangi action qo‘shish qiyinlashadi.
Messaging bilan:
public void createOrder() {
Order order = orderRepository.save(order);
eventPublisher.publishEvent(new OrderCreatedEvent(order.getId()));
}Keyin alohida listenerlar:
OrderCreatedEvent
├── Email listener
├── SMS listener
├── Analytics listener
└── Bonus listenerBu cleanroq.
3. Messaging turlari
Spring’da messagingni 3 darajada ko‘rish mumkin:
Tur | Qayerda ishlaydi? | Misol |
|---|---|---|
In-process event | Bitta application ichida | Spring Events |
Message broker | App va broker orasida | RabbitMQ |
Distributed event streaming | Microservice/event stream | Kafka |
4. Spring Events
Spring Events - bitta Spring Boot application ichida event chiqarish va eshitish mexanizmi.
Bu hali RabbitMQ yoki Kafka emas. Bu JVM ichida ishlaydi.
Oqim:
Service -> publishEvent() -> @EventListener5. Oddiy Spring Event misol
Event class:
public record OrderCreatedEvent(
Long orderId
) {
}Service:
@Service
public class OrderService {
private final OrderRepository orderRepository;
private final ApplicationEventPublisher eventPublisher;
public OrderService(
OrderRepository orderRepository,
ApplicationEventPublisher eventPublisher
) {
this.orderRepository = orderRepository;
this.eventPublisher = eventPublisher;
}
@Transactional
public Long createOrder(CreateOrderRequest request) {
Order order = new Order();
order.setUserId(request.userId());
Order saved = orderRepository.save(order);
eventPublisher.publishEvent(
new OrderCreatedEvent(saved.getId())
);
return saved.getId();
}
}Listener:
@Component
public class OrderCreatedListener {
@EventListener
public void handle(OrderCreatedEvent event) {
System.out.println("Order created: " + event.orderId());
}
}Natija:
Order created: 156. Spring Events synchronous ishlaydi
Default holatda @EventListener synchronous.
Ya’ni:
OrderService.createOrder()
-> publishEvent()
-> listener ishlaydi
-> keyin method davom etadiAgar listener ichida exception bo‘lsa, asosiy flowga ta’sir qilishi mumkin.
Masalan:
@EventListener
public void handle(OrderCreatedEvent event) {
throw new RuntimeException("Email failed");
}Agar bu transaction ichida bo‘lsa, order transaction ham rollback bo‘lishi mumkin.
Shuning uchun eventni qachon va qanday ishlatishni tushunish kerak.
7. @TransactionalEventListener
Transaction bilan ishlaganda bu juda muhim.
Oddiy @EventListener event publish qilingan zahoti ishlaydi. Lekin transaction hali commit bo‘lmagan bo‘lishi mumkin.
@Transactional
public void createOrder() {
Order order = orderRepository.save(new Order());
eventPublisher.publishEvent(new OrderCreatedEvent(order.getId()));
// transaction hali commit bo‘lmagan
}Agar listener email yuborsa, lekin keyin transaction rollback bo‘lsa:
Email ketdi: order yaratildi
Lekin DB rollback bo‘ldi: order yo‘qBu noto‘g‘ri.
Yaxshiroq:
@Component
public class OrderCreatedListener {
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void handle(OrderCreatedEvent event) {
System.out.println("Transaction commit bo‘lgandan keyin ishlaydi");
}
}8. Transaction event phase’lari
Phase | Qachon ishlaydi? |
|---|---|
| Commitdan oldin |
| Commitdan keyin |
| Rollbackdan keyin |
| Commit yoki rollbackdan keyin |
Ko‘p real holatda eng foydali variant:
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)Masalan:
email yuborish;
SMS yuborish;
cache invalidation;
Kafka/RabbitMQ event yuborish;
audit notification.
9. @Async bilan Spring Events
Agar listener asosiy requestni ushlab turmasin desak, async qilish mumkin.
Avval async yoqiladi:
@EnableAsync
@SpringBootApplication
public class App {
}Listener:
@Component
public class OrderNotificationListener {
@Async
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void sendNotification(OrderCreatedEvent event) {
System.out.println("Send notification for order: " + event.orderId());
}
}Endi listener boshqa threadda ishlaydi.
10. @Asyncda ehtiyot bo‘lish kerak
Async listener boshqa threadga o‘tadi. Shuning uchun:
transaction context o‘tmaydi;
lazy entity bilan ishlamang;
SecurityContextham avtomatik o‘tmasligi mumkin;event ichida entity emas, ID yuborish yaxshi.
Yomon:
public record OrderCreatedEvent(Order order) {
}Yaxshi:
public record OrderCreatedEvent(Long orderId) {
}Keyin listener ichida kerak bo‘lsa DBdan qayta o‘qiladi:
@Async
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void handle(OrderCreatedEvent event) {
Order order = orderRepository.findById(event.orderId()).orElseThrow();
}11. Spring Events qachon yetarli?
Spring Events yaxshi:
Holat | Sabab |
|---|---|
Bitta monolith ichida | Broker kerak emas |
Domain eventlar | Kodni loosely coupled qiladi |
Cache invalidation | Commitdan keyin tozalash mumkin |
Email/SMS trigger | Main flowdan ajratadi |
Audit/event log | Service kodini tozalaydi |
Lekin Spring Events yetarli emas:
Holat | Sabab |
|---|---|
Microservice orasida event kerak | JVM ichida qolib ketadi |
Retry/DLQ kerak | Spring Eventda built-in broker yo‘q |
Event yo‘qolmasligi kerak | App crash bo‘lsa event yo‘qolishi mumkin |
High throughput streaming | Kafka kerak bo‘lishi mumkin |
12. RabbitMQ nima?
RabbitMQ - message broker.
Application message yuboradi, RabbitMQ uni qabul qiladi va consumerlarga yetkazadi.
Producer -> RabbitMQ -> ConsumerRabbitMQ odatda quyidagilar uchun yaxshi:
task queue;
email/SMS job;
background processing;
routing;
retry;
dead letter queue;
command/message delivery.
13. RabbitMQ asosiy tushunchalari
Tushuncha | Ma’nosi |
|---|---|
Producer | Message yuboruvchi |
Consumer | Message o‘quvchi |
Queue | Message saqlanadigan navbat |
Exchange | Message qaysi queuega borishini hal qiladi |
Routing key | Message yo‘nalishi |
Binding | Exchange va queue bog‘lanishi |
DLQ | Failed message tushadigan queue |
14. RabbitMQ oqimi
Producer
↓
Exchange
↓ routing key
Queue
↓
ConsumerMasalan:
order.created exchange
routing key: email
queue: email.queue15. Spring AMQP dependency
Maven:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>Config:
spring:
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest16. RabbitMQ queue/exchange config
@Configuration
public class RabbitConfig {
public static final String ORDER_EXCHANGE = "order.exchange";
public static final String ORDER_CREATED_QUEUE = "order.created.queue";
public static final String ORDER_CREATED_ROUTING_KEY = "order.created";
@Bean
public TopicExchange orderExchange() {
return new TopicExchange(ORDER_EXCHANGE);
}
@Bean
public Queue orderCreatedQueue() {
return QueueBuilder
.durable(ORDER_CREATED_QUEUE)
.build();
}
@Bean
public Binding orderCreatedBinding() {
return BindingBuilder
.bind(orderCreatedQueue())
.to(orderExchange())
.with(ORDER_CREATED_ROUTING_KEY);
}
}Bu yerda:
order.exchange -> order.created -> order.created.queue17. RabbitMQ message yuborish
DTO:
public record OrderCreatedMessage(
Long orderId,
Long userId
) {
}Producer:
@Service
public class OrderMessageProducer {
private final RabbitTemplate rabbitTemplate;
public OrderMessageProducer(RabbitTemplate rabbitTemplate) {
this.rabbitTemplate = rabbitTemplate;
}
public void sendOrderCreated(OrderCreatedMessage message) {
rabbitTemplate.convertAndSend(
RabbitConfig.ORDER_EXCHANGE,
RabbitConfig.ORDER_CREATED_ROUTING_KEY,
message
);
}
}Service:
@Transactional
public Long createOrder(CreateOrderRequest request) {
Order order = orderRepository.save(new Order(request.userId()));
eventPublisher.publishEvent(
new OrderCreatedEvent(order.getId(), order.getUserId())
);
return order.getId();
}Commitdan keyin RabbitMQga yuborish:
@Component
public class OrderEventListener {
private final OrderMessageProducer producer;
public OrderEventListener(OrderMessageProducer producer) {
this.producer = producer;
}
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void onOrderCreated(OrderCreatedEvent event) {
producer.sendOrderCreated(
new OrderCreatedMessage(event.orderId(), event.userId())
);
}
}18. RabbitMQ message qabul qilish
@Component
public class OrderCreatedConsumer {
@RabbitListener(queues = RabbitConfig.ORDER_CREATED_QUEUE)
public void consume(OrderCreatedMessage message) {
System.out.println("Received order: " + message.orderId());
}
}Bu consumer order.created.queuedan message oladi.
19. RabbitMQ JSON converter
Default serialization bilan muammo bo‘lmasligi uchun JSON converter berish yaxshi:
@Configuration
public class RabbitJsonConfig {
@Bean
public MessageConverter jsonMessageConverter() {
return new Jackson2JsonMessageConverter();
}
}Endi RabbitTemplate va listener JSON bilan ishlaydi.
20. RabbitMQ retry
Agar consumer xato bersa, message qayta ishlanishi kerak bo‘lishi mumkin.
Config:
spring:
rabbitmq:
listener:
simple:
retry:
enabled: true
initial-interval: 2s
max-attempts: 3
multiplier: 2
max-interval: 10sMa’nosi:
1-urinish
2 sekund kutish
2-urinish
4 sekund kutish
3-urinish
xato bo‘lsa -> reject/DLQ21. Dead Letter Queue nima?
DLQ - Dead Letter Queue.
Agar message bir necha marta qayta ishlansa ham xato bersa, uni yo‘qotib yubormaslik kerak. DLQga yuboramiz.
order.created.queue
↓ error after retries
order.created.dlqDLQ kerak:
failed message’ni keyin tekshirish;
bug fixdan keyin qayta ishlash;
monitoring/alert qilish;
data yo‘qolmasligi uchun.
22. RabbitMQ DLQ config
@Configuration
public class RabbitDlqConfig {
public static final String ORDER_EXCHANGE = "order.exchange";
public static final String ORDER_CREATED_QUEUE = "order.created.queue";
public static final String ORDER_CREATED_DLQ = "order.created.dlq";
public static final String DLX = "order.dlx";
@Bean
public DirectExchange deadLetterExchange() {
return new DirectExchange(DLX);
}
@Bean
public Queue orderCreatedQueue() {
return QueueBuilder
.durable(ORDER_CREATED_QUEUE)
.deadLetterExchange(DLX)
.deadLetterRoutingKey("order.created.failed")
.build();
}
@Bean
public Queue orderCreatedDlq() {
return QueueBuilder
.durable(ORDER_CREATED_DLQ)
.build();
}
@Bean
public Binding orderCreatedDlqBinding() {
return BindingBuilder
.bind(orderCreatedDlq())
.to(deadLetterExchange())
.with("order.created.failed");
}
}Agar message reject qilinsa, u order.created.dlqga tushadi.
23. Kafka nima?
Kafka - distributed event streaming platform.
RabbitMQ ko‘proq queue/message broker sifatida ishlatiladi. Kafka esa eventlarni log ko‘rinishida saqlaydi.
Kafka oqimi:
Producer -> Topic partition -> Consumer groupKafka odatda yaxshi:
event streaming;
analytics;
audit eventlar;
microservice integration;
high throughput;
event replay;
real-time data pipeline.
24. Kafka asosiy tushunchalari
Tushuncha | Ma’nosi |
|---|---|
Topic | Eventlar nomlangan oqimi |
Partition | Topic ichidagi bo‘lim |
Offset | Consumer qayergacha o‘qiganini bildiradi |
Producer | Event yuboruvchi |
Consumer | Event o‘quvchi |
Consumer group | Birga ishlaydigan consumerlar guruhi |
Broker | Kafka server |
Retention | Eventlar qancha vaqt saqlanishi |
25. Kafka va RabbitMQ farqi qisqa
Mezон | RabbitMQ | Kafka |
|---|---|---|
Model | Queue/message broker | Distributed log/event stream |
Message saqlash | Odatda consume bo‘lgach yo‘qoladi | Retention bo‘yicha saqlanadi |
Replay | Qiyinroq | Kuchli |
Routing | Juda kuchli | Oddiyroq |
Throughput | Yaxshi | Juda yuqori |
Use case | Task queue, routing, job | Event stream, analytics, microservices |
Consumer | Queue’dan message oladi | Offset bo‘yicha o‘qiydi |
Oddiy qoida:
Background job / murakkab routing -> RabbitMQ
Event stream / replay / high throughput -> Kafka26. Spring Kafka dependency
Maven:
<dependency>
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka</artifactId>
</dependency>Spring Boot bilan odatda config:
spring:
kafka:
bootstrap-servers: localhost:9092
consumer:
group-id: order-service
auto-offset-reset: earliest
producer:
acks: all27. Kafka message DTO
public record OrderCreatedEventMessage(
Long orderId,
Long userId,
Instant createdAt
) {
}JSON serializer config:
spring:
kafka:
producer:
value-serializer: org.springframework.kafka.support.serializer.JsonSerializer
key-serializer: org.apache.kafka.common.serialization.StringSerializer
consumer:
value-deserializer: org.springframework.kafka.support.serializer.JsonDeserializer
key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
properties:
spring.json.trusted.packages: "com.example"28. KafkaTemplate bilan event yuborish
@Service
public class OrderKafkaProducer {
private final KafkaTemplate<String, OrderCreatedEventMessage> kafkaTemplate;
public OrderKafkaProducer(
KafkaTemplate<String, OrderCreatedEventMessage> kafkaTemplate
) {
this.kafkaTemplate = kafkaTemplate;
}
public void send(OrderCreatedEventMessage message) {
kafkaTemplate.send(
"order.created",
message.orderId().toString(),
message
);
}
}Bu yerda key sifatida orderId ishlatildi.
Nega key muhim?
Bir xil key -> odatda bir xil partitionBu order bo‘yicha event tartibini saqlashga yordam beradi.
29. @KafkaListener
@Component
public class OrderCreatedKafkaConsumer {
@KafkaListener(
topics = "order.created",
groupId = "notification-service"
)
public void consume(OrderCreatedEventMessage message) {
System.out.println("Kafka event: " + message.orderId());
}
}Agar consumer group bir xil bo‘lsa:
groupId = notification-servicemessage group ichidagi bitta consumer instancega boradi.
Agar boshqa service boshqa group bilan o‘qisa:
groupId = analytics-serviceu ham o‘sha eventni alohida oladi.
30. Kafka consumer group tushunchasi
Topic:
order.createdConsumer groups:
notification-service
analytics-service
bonus-serviceHar bir group eventlarni mustaqil o‘qiydi.
order.created event
├── notification-service oladi
├── analytics-service oladi
└── bonus-service oladiLekin bitta group ichida bir event odatda bitta consumer instancega beriladi.
31. Kafka retry va DLT
Kafka’da failed message uchun retry va DLT ishlatiladi.
Spring Kafka’da:
@RetryableTopic(
attempts = "3",
backoff = @Backoff(delay = 2000, multiplier = 2),
dltTopicSuffix = ".dlt"
)
@KafkaListener(topics = "order.created", groupId = "notification-service")
public void consume(OrderCreatedEventMessage message) {
if (message.orderId() == null) {
throw new IllegalArgumentException("orderId required");
}
System.out.println("Process order: " + message.orderId());
}Agar 3 marta xato bo‘lsa:
order.created.dlttopicga tushadi.
DLT listener:
@DltHandler
public void handleDlt(OrderCreatedEventMessage message) {
System.out.println("DLT message: " + message);
}32. Idempotency nima?
Messagingda eng muhim tushunchalardan biri - idempotency.
Message bir martadan ko‘proq kelishi mumkin.
OrderCreatedEvent(orderId=10)
OrderCreatedEvent(orderId=10)Consumer ikki marta ishlasa, muammo chiqishi mumkin:
bonus ikki marta berildi
email ikki marta ketdi
payment ikki marta qilindiShuning uchun consumer idempotent bo‘lishi kerak.
33. Idempotent consumer misol
Eventda unique eventId bo‘lsin:
public record OrderCreatedMessage(
UUID eventId,
Long orderId,
Long userId
) {
}Processed event table:
@Entity
public class ProcessedEvent {
@Id
private UUID eventId;
private LocalDateTime processedAt;
}Consumer:
@Transactional
public void consume(OrderCreatedMessage message) {
if (processedEventRepository.existsById(message.eventId())) {
return;
}
bonusService.addBonus(message.userId());
processedEventRepository.save(
new ProcessedEvent(message.eventId(), LocalDateTime.now())
);
}Endi bir xil event qayta kelsa, ikkinchi marta ishlamaydi.
34. Messaging va transaction muammosi
Eng katta muammo:
DBga order saqlandi
Kafka/RabbitMQga event yuborish kerakAgar:
DB commit bo‘ldi
Kafka send xato bo‘ldievent yo‘qoladi.
Aksincha:
Kafka event ketdi
DB rollback bo‘ldinoto‘g‘ri event ketdi.
Buni oddiy @Transactional bilan to‘liq hal qilish qiyin.
35. Transactional Outbox Pattern
Production microservice’da eng yaxshi patternlardan biri - Transactional Outbox.
Oqim:
Bitta DB transaction ichida:
1. Order save
2. OutboxEvent save
Alohida worker:
3. OutboxEvent o‘qiydi
4. Kafka/RabbitMQga yuboradi
5. SENT deb belgilaydiService:
@Transactional
public Long createOrder(CreateOrderRequest request) {
Order order = orderRepository.save(new Order(request.userId()));
OutboxEvent event = new OutboxEvent(
UUID.randomUUID(),
"ORDER_CREATED",
order.getId().toString(),
"""
{"orderId": %d, "userId": %d}
""".formatted(order.getId(), order.getUserId())
);
outboxRepository.save(event);
return order.getId();
}Bu kafolatlaydi:
Order va event record birga commit bo‘ladiKeyin worker eventni brokerga yuboradi.
36. Outbox entity
@Entity
public class OutboxEvent {
@Id
private UUID id;
private String type;
private String aggregateId;
@Column(columnDefinition = "TEXT")
private String payload;
@Enumerated(EnumType.STRING)
private OutboxStatus status;
private LocalDateTime createdAt;
private LocalDateTime sentAt;
}Status:
public enum OutboxStatus {
NEW,
SENT,
FAILED
}Worker:
@Scheduled(fixedDelay = 5000)
@Transactional
public void publishOutboxEvents() {
List<OutboxEvent> events =
outboxRepository.findTop100ByStatusOrderByCreatedAt(
OutboxStatus.NEW
);
for (OutboxEvent event : events) {
kafkaTemplate.send("order.events", event.getAggregateId(), event.getPayload());
event.markAsSent();
}
}Bu sodda variant. Katta sistemalarda Debezium CDC bilan outbox ishlatiladi.
37. Message ordering
Messagingda tartib muhim bo‘lishi mumkin.
Masalan:
ORDER_CREATED
ORDER_PAID
ORDER_CANCELLEDAgar tartib buzilsa, consumer noto‘g‘ri ishlaydi.
Kafka’da ordering odatda partition ichida kafolatlanadi.
Shuning uchun key muhim:
kafkaTemplate.send("order.events", orderId.toString(), message);Bir xil orderId bir xil partitionga tushadi va tartib saqlanishi osonroq bo‘ladi.
RabbitMQ’da ham queue ichida tartib bor, lekin retry, requeue, multiple consumer bo‘lsa tartib murakkablashadi.
38. At-least-once delivery
Ko‘p messaging systemlarda default amaliy kafolat:
At-least-once deliveryYa’ni message kamida bir marta yetkaziladi. Lekin ba’zan ikki marta ham kelishi mumkin.
Shuning uchun:
Consumer idempotent bo‘lishi shartBu juda muhim production qoidasi.
39. Poison message
Poison message - doim xato beradigan message.
Masalan:
{
"orderId": null
}Consumer har safar xato beradi.
Agar retry cheksiz bo‘lsa:
message -> error -> retry -> error -> retry -> ...Queue/topic tiqilib qolishi mumkin.
Yechim:
max retry;
DLQ/DLT;
validation;
alert;
manual replay.
40. Messagingda monitoring
Productionda quyidagilar kuzatiladi:
Metric | Nima uchun kerak |
|---|---|
Queue depth | Queue’da qancha message kutyapti |
Consumer lag | Kafka consumer qanchalik ortda |
Retry count | Xatolar ko‘payyaptimi |
DLQ size | Failed message bor-yo‘qligi |
Processing time | Consumer sekinmi |
Error rate | Xato foizi |
Broker availability | RabbitMQ/Kafka ishlayaptimi |
Kafka’da ayniqsa consumer lag muhim:
Producer tez yozmoqda
Consumer sekin o‘qimoqda
Lag oshib boradi41. Messagingda keng tarqalgan xatolar
Xato | Oqibat |
|---|---|
Entityni messagega solish | Lazy/loading/schema muammo |
Idempotency yo‘q | Duplicate processing |
Retry cheksiz | Poison message systemani tiqadi |
DLQ yo‘q | Failed message yo‘qoladi yoki loop qiladi |
Transactiondan oldin event yuborish | DB rollback bo‘lsa noto‘g‘ri event |
Broker sendni DB transaction ichida noto‘g‘ri boshqarish | Inconsistency |
Message versioning yo‘q | Consumer eski/yangi formatni tushunmaydi |
Monitoring yo‘q | Queue tiqilib qolganini kech bilasiz |
42. Message versioning
Vaqt o‘tishi bilan message formati o‘zgaradi.
Oldin:
{
"orderId": 10,
"userId": 5
}Keyin:
{
"eventVersion": 2,
"orderId": 10,
"userId": 5,
"totalAmount": 150000
}Yaxshi amaliyot:
public record OrderCreatedMessage(
int eventVersion,
UUID eventId,
Long orderId,
Long userId,
BigDecimal totalAmount,
Instant occurredAt
) {
}Consumer eski fieldlar yo‘q bo‘lsa ham yiqilib tushmasligi kerak.
43. Real architecture misol
Order service:
POST /orders
↓
OrderService.create()
↓
DB: orders insert
DB: outbox_events insert
↓
commitOutbox publisher:
outbox_events NEW
↓
Kafka: order.created
↓
outbox_events SENTConsumers:
notification-service -> email/sms
analytics-service -> reporting
bonus-service -> user bonus
warehouse-service -> stock reservationBu architecture’da OrderService boshqa servicelarni bevosita bilmaydi.
44. Spring Events vs RabbitMQ vs Kafka
Mezon | Spring Events | RabbitMQ | Kafka |
|---|---|---|---|
Scope | Bitta app ichida | Service/app orasida | Distributed event stream |
Persistence | Yo‘q | Queue’da saqlaydi | Log retention |
Retry/DLQ | Qo‘lda | Kuchli | Kuchli |
Throughput | App ichida | Yaxshi | Juda yuqori |
Replay | Yo‘q | Cheklangan | Kuchli |
Routing | Oddiy | Juda kuchli | Topic/partition |
Use case | Monolith ichki event | Background job, routing | Event streaming, microservices |
45. Qaysi birini tanlash kerak?
Spring Events tanlang
Agar:
Bitta Spring Boot app ichida decoupling kerakMasalan:
order created -> cache clear;
user registered -> welcome email trigger;
product updated -> local listener.
RabbitMQ tanlang
Agar:
Task queue, retry, DLQ, routing kerakMasalan:
email queue;
SMS queue;
image processing;
invoice generation;
background jobs.
Kafka tanlang
Agar:
Event stream, replay, ko‘p consumer group, high throughput kerakMasalan:
order events;
payment events;
analytics pipeline;
audit log;
microservice integration;
CDC/outbox events.
46. Best practice
1. Message ichiga entity solmang
Yomon:
public record OrderCreatedMessage(Order order) {
}Yaxshi:
public record OrderCreatedMessage(
UUID eventId,
Long orderId,
Long userId,
Instant occurredAt
) {
}2. Har message’da eventId bo‘lsin
Duplicate processingdan himoya qiladi.
UUID eventId3. Consumer idempotent bo‘lsin
Message ikki marta kelsa ham, natija buzilmasin.
4. Retry limit va DLQ shart
Cheksiz retry productionda xavfli.
5. Transaction commitdan keyin event yuboring
Monolith ichida:
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)Microservice’da:
Transactional Outbox6. Message schema/versioningni oldindan o‘ylang
Consumerlar har doim bir vaqtda deploy bo‘lmaydi.
47. Interview savollar
Savol 1: Spring Events nima?
Javob:
Spring Events bitta Spring application ichida event publish qilish va listenerlar orqali qabul qilish mexanizmi.
ApplicationEventPublisherevent chiqaradi,@EventListeneryoki@TransactionalEventListeneruni eshitadi.
Savol 2: @EventListener va @TransactionalEventListener farqi nima?
Javob:
@EventListenerevent publish qilingan zahoti ishlaydi.@TransactionalEventListeneresa transaction phase bilan bog‘lanadi, masalanAFTER_COMMITorqali faqat DB commit bo‘lgandan keyin ishlaydi.
Savol 3: RabbitMQ qachon ishlatiladi?
Javob:
RabbitMQ background job, task queue, murakkab routing, retry va DLQ kerak bo‘lgan joylarda yaxshi. Masalan email, SMS, invoice generation yoki image processing queue.
Savol 4: Kafka qachon ishlatiladi?
Javob:
Kafka high throughput event streaming, event replay, ko‘p consumer group, analytics pipeline va microservice integration uchun yaxshi. Kafka eventlarni retention bo‘yicha saqlaydi va consumerlar offset orqali o‘qiydi.
Savol 5: DLQ/DLT nima?
Javob:
Dead Letter Queue yoki Dead Letter Topic - retrylardan keyin ham ishlanmagan failed message tushadigan joy. U message yo‘qolmasligi, keyin tahlil qilish va qayta ishlash uchun kerak.
Savol 6: Idempotent consumer nima?
Javob:
Idempotent consumer bir xil message ikki yoki undan ko‘p marta kelsa ham natijani bir marta bajarilgandek saqlaydi. Masalan
eventIdni processed table’da saqlab, duplicate eventlarni o‘tkazib yuboradi.
Savol 7: Transactional Outbox nima?
Javob:
Transactional Outbox - DB update va event yaratishni bitta transaction ichida bajarish patterni. Service asosiy entity bilan birga outbox tablega event yozadi, keyin alohida worker yoki CDC uni Kafka/RabbitMQga yuboradi. Bu DB commit va event publish orasidagi inconsistency muammosini kamaytiradi.
48. Qisqa xulosa
Mavzu | Asosiy fikr |
|---|---|
Spring Events | Bitta app ichida event-based decoupling |
| Event chiqaradi |
| Eventni darhol eshitadi |
| Transaction phase bo‘yicha ishlaydi |
| Listenerni boshqa threadda ishlatadi |
RabbitMQ | Queue, routing, retry, DLQ uchun kuchli |
Kafka | Event stream, replay, consumer group, high throughput |
DLQ/DLT | Failed message’larni saqlash |
Idempotency | Duplicate message’dan himoya |
Outbox | DB transaction va event publish consistency uchun |
Eng muhim gap:
Messaging - service’larni bo‘sh bog‘lash uchun kuchli vosita. Lekin retry, DLQ, idempotency va transaction consistency bo‘lmasa, productionda katta muammo chiqaradi.