Messaging & streaming ichiga quyidagilar kiradi: Apache Kafka, RabbitMQ, Spring Kafka, Spring AMQP, Event-driven architecture patterns, Outbox pattern.
1. Messaging nima?
Messaging — servislar bir-biri bilan to‘g‘ridan-to‘g‘ri chaqiriq qilmasdan, message broker orqali gaplashishi.
Oddiy REST chaqiriq:
Order Service → Payment ServiceMessaging bilan:
Order Service → Message Broker → Payment ServiceBu yerda Message Broker — Kafka, RabbitMQ, ActiveMQ, Pulsar kabi vosita.
2. Nega messaging kerak?
Microservice’da hamma narsani REST orqali qilish xavfli bo‘lishi mumkin.
Masalan checkout jarayoni:
Client
↓
Order Service
↓
Payment Service
↓
Inventory Service
↓
Notification ServiceAgar Notification Service vaqtincha ishlamay qolsa, order yaratish ham yiqilishi kerakmi?
Ko‘pincha yo‘q.
Shuning uchun order yaratilgandan keyin event yuboriladi:
Order Service → order.created event → Notification ServiceNotification Service keyinroq ishlab olsa ham bo‘ladi.
3. Messaging asosiy foydalari
3.1. Decoupling
Servislar bir-biriga qattiq bog‘lanmaydi.
Yomon:
Order Service Payment Service’ni aniq URL orqali chaqiradiYaxshiroq:
Order Service order.created event chiqaradi
Payment Service o‘sha eventni eshitadiOrder Service Payment Service ichki ishlashini bilmaydi.
3.2. Async processing
Hamma ishni request ichida qilish shart emas.
Masalan:
POST /ordersRequest ichida faqat:
Order yaratishKeyin background’da:
Email yuborish
SMS yuborish
Analytics yangilash
Search index yangilashBu API latency’ni kamaytiradi.
3.3. Retry va buffering
Agar consumer vaqtincha ishlamasa, message broker message’ni saqlab turadi.
Producer → Broker → Consumer down
↓
message saqlanadiConsumer qayta ishlaganda message’larni o‘qib oladi.
3.4. Scalability
Consumerlarni ko‘paytirish mumkin.
Kafka topic: order.created
↓
Consumer 1
Consumer 2
Consumer 3Workload bo‘linadi.
4. Messaging bilan bog‘liq xavflar
Messaging kuchli, lekin bepul emas.
Kamchiliklari:
debugging qiyinlashadi;
event yo‘qolishi mumkin;
duplicate message kelishi mumkin;
ordering muammosi bo‘lishi mumkin;
schema evolution kerak bo‘ladi;
monitoring majburiy;
eventual consistency paydo bo‘ladi.
Senior developer messaging qo‘shishdan oldin so‘raydi:
Bu joyda async processing haqiqatan kerakmi yoki oddiy REST yetarlimi?
5. Queue va Topic farqi
Queue
Queue’da message odatda bitta consumer tomonidan ishlanadi.
Message Queue
↓
Consumer AAgar bir nechta consumer bo‘lsa, message ulardan bittasiga tushadi:
Queue
├── Consumer A
├── Consumer B
└── Consumer CBu worker model uchun yaxshi.
Masalan:
email yuborish;
image resize;
file processing;
payment job;
background task.
Topic
Topic’da message bir nechta subscriberga tarqatilishi mumkin.
Topic: order.created
├── Payment Service
├── Inventory Service
├── Notification Service
└── Analytics ServiceBu event-driven architecture uchun yaxshi.
6. Apache Kafka
Apache Kafka — distributed event streaming platform.
Kafka ko‘proq quyidagi holatlarda ishlatiladi:
katta throughput;
event streaming;
analytics pipeline;
microservice event bus;
log/event saqlash;
real-time processing;
audit trail.
Kafka’da asosiy tushunchalar:
Producer → Topic → Partition → Consumer Group → Consumer6.1. Kafka topic
Topic — message’lar yoziladigan nomlangan kanal.
Masalan:
order.created
payment.completed
inventory.reserved
user.registeredProducer topic’ga yozadi:
Order Service → order.createdConsumer topic’dan o‘qiydi:
Payment Service ← order.created6.2. Kafka partition
Topic bir nechta partitionga bo‘linadi.
order.created
├── partition 0
├── partition 1
└── partition 2Partition nima beradi?
parallel processing;
scalability;
ordering control.
Muhim qoida:
Kafka’da ordering faqat bitta partition ichida kafolatlanadi.
Agar bir userning eventlari tartib bilan ishlanishi kerak bo‘lsa, key sifatida userId beriladi.
key = userIdShunda shu userga tegishli eventlar bitta partitionga tushadi.
6.3. Kafka offset
Offset — partition ichidagi message’ning tartib raqami.
partition 0:
offset 0 → event A
offset 1 → event B
offset 2 → event CConsumer qayergacha o‘qiganini offset orqali biladi.
Agar consumer o‘chib qayta yonsa, oxirgi commit qilingan offsetdan davom etadi.
6.4. Consumer group
Consumer group — bitta servisning parallel consumerlari.
Topic: order.created, 3 partition
Consumer Group: payment-service
├── consumer 1 → partition 0
├── consumer 2 → partition 1
└── consumer 3 → partition 2Agar yana bitta alohida servis shu topic’ni o‘qisa, u o‘z consumer group’iga ega bo‘ladi:
Consumer Group: notification-service
Consumer Group: analytics-serviceHar bir group message’larni mustaqil o‘qiydi.
7. Kafka’da delivery semantics
Kafka’da uchta asosiy delivery model bor:
Model | Ma’nosi | Xavf |
|---|---|---|
At-most-once | Message 0 yoki 1 marta ishlanadi | Yo‘qolishi mumkin |
At-least-once | Message kamida 1 marta ishlanadi | Duplicate bo‘lishi mumkin |
Exactly-once | Kafka ichida aniq bir marta processing | Murakkab, hamma joyda emas |
Backend amaliyotida eng ko‘p:
At-least-once + idempotent consumerYa’ni message qayta kelishi mumkin deb design qilinadi.
7.1. Kafka consumer yomon misol
@KafkaListener(topics = "order.created")
public void handle(OrderCreatedEvent event) {
emailService.send(event.email());
}Agar message qayta kelsa, email ikki marta ketishi mumkin.
7.2. Idempotent consumer
@KafkaListener(topics = "order.created")
public void handle(OrderCreatedEvent event) {
if (processedEventRepository.existsByEventId(event.eventId())) {
return;
}
emailService.send(event.email());
processedEventRepository.save(new ProcessedEvent(event.eventId()));
}Bu yerda eventId unique bo‘lishi kerak.
8. Spring Kafka
Java/Spring Boot’da Kafka bilan ishlash uchun ko‘p ishlatiladigan kutubxona — Spring Kafka.
8.1. Dependency
<dependency>
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka</artifactId>
</dependency>8.2. Kafka producer
@Service
@RequiredArgsConstructor
public class OrderEventProducer {
private final KafkaTemplate<String, OrderCreatedEvent> kafkaTemplate;
public void send(OrderCreatedEvent event) {
kafkaTemplate.send("order.created", event.orderId().toString(), event);
}
}Bu yerda:
kafkaTemplate.send(topic, key, value);topic— qaysi kanalga yuboriladi;key— partition tanlashga yordam beradi;value— event body.
8.3. Kafka consumer
@Component
@RequiredArgsConstructor
public class OrderCreatedConsumer {
private final EmailService emailService;
@KafkaListener(
topics = "order.created",
groupId = "notification-service"
)
public void consume(OrderCreatedEvent event) {
emailService.sendOrderCreatedEmail(event.email(), event.orderId());
}
}8.4. Event DTO
public record OrderCreatedEvent(
UUID eventId,
Long orderId,
Long userId,
String email,
Instant createdAt
) {}Event’da kamida shular bo‘lishi yaxshi:
eventId;eventType;aggregateId;occurredAt;kerakli biznes data.
9. Kafka config
Oddiy application.yml:
spring:
kafka:
bootstrap-servers: localhost:9092
producer:
key-serializer: org.apache.kafka.common.serialization.StringSerializer
value-serializer: org.springframework.kafka.support.serializer.JsonSerializer
consumer:
group-id: notification-service
auto-offset-reset: earliest
key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
value-deserializer: org.springframework.kafka.support.serializer.JsonDeserializer
properties:
spring.json.trusted.packages: "*"Productionda trusted.packages: "*" ehtiyotkorlik bilan ishlatiladi. Aniq package ko‘rsatish yaxshiroq.
10. Kafka’da xatolarni boshqarish
Consumer message’ni ishlay olmasa nima bo‘ladi?
Masalan:
email service down
DB timeout
invalid eventVariantlar:
retry;
dead letter topic;
skip;
alert;
manual intervention.
10.1. Dead Letter Topic
Agar message bir necha retry’dan keyin ham ishlanmasa, alohida topic’ga tashlanadi.
order.created
↓
consumer retry failed
↓
order.created.DLTDLT kerak, chunki bitta yomon message butun consumerni to‘xtatib qo‘ymasligi kerak.
11. RabbitMQ
RabbitMQ — message broker. U routing, queue va delivery patternlari kuchli bo‘lgan tizim.
RabbitMQ ko‘proq quyidagilarga mos:
background job;
task queue;
complex routing;
request/reply messaging;
delayed job;
priority queue;
command-based messaging.
RabbitMQ asosiy tushunchalari:
Producer → Exchange → Queue → Consumer12. RabbitMQ exchange turlari
RabbitMQ’da producer message’ni to‘g‘ridan-to‘g‘ri queue’ga emas, exchangega yuboradi.
Exchange message’ni qaysi queue’ga borishini hal qiladi.
12.1. Direct exchange
Routing key aniq mos tushsa, message queue’ga boradi.
routing key = payment.createdExchange
└── Queue: payment.createdMos joy:
aniq command;
aniq event type;
simple routing.
12.2. Fanout exchange
Message barcha bog‘langan queue’larga yuboriladi.
Exchange
├── email-queue
├── sms-queue
└── analytics-queueMos joy:
broadcast;
notification;
event tarqatish.
12.3. Topic exchange
Pattern asosida routing qiladi.
order.created
order.cancelled
payment.completedBinding:
order.*Bu order.created va order.cancelledni ushlaydi.
Binding:
*.completedBu payment.completed kabi eventlarni ushlaydi.
12.4. Headers exchange
Routing key emas, header qiymatlariga qarab routing qiladi.
Kamroq ishlatiladi, lekin murakkab routingda foydali.
13. Spring AMQP
Spring Boot’da RabbitMQ bilan ishlash uchun ko‘p ishlatiladigan kutubxona — Spring AMQP.
13.1. Dependency
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>13.2. RabbitMQ config
@Configuration
public class RabbitConfig {
public static final String EXCHANGE = "order.exchange";
public static final String QUEUE = "notification.queue";
public static final String ROUTING_KEY = "order.created";
@Bean
public TopicExchange orderExchange() {
return new TopicExchange(EXCHANGE);
}
@Bean
public Queue notificationQueue() {
return QueueBuilder.durable(QUEUE).build();
}
@Bean
public Binding binding() {
return BindingBuilder
.bind(notificationQueue())
.to(orderExchange())
.with(ROUTING_KEY);
}
}13.3. RabbitMQ producer
@Service
@RequiredArgsConstructor
public class OrderRabbitProducer {
private final RabbitTemplate rabbitTemplate;
public void send(OrderCreatedEvent event) {
rabbitTemplate.convertAndSend(
RabbitConfig.EXCHANGE,
RabbitConfig.ROUTING_KEY,
event
);
}
}13.4. RabbitMQ consumer
@Component
@RequiredArgsConstructor
public class OrderRabbitConsumer {
private final EmailService emailService;
@RabbitListener(queues = RabbitConfig.QUEUE)
public void consume(OrderCreatedEvent event) {
emailService.sendOrderCreatedEmail(event.email(), event.orderId());
}
}14. Kafka vs RabbitMQ
Mezon | Kafka | RabbitMQ |
|---|---|---|
Asosiy model | Event streaming/log | Message broker/queue |
Message saqlash | Topic’da retention bilan saqlaydi | Consumer olgach queue’dan o‘chadi |
Throughput | Juda yuqori | O‘rtacha/yaxshi |
Ordering | Partition ichida | Queue ichida |
Replay | Juda kuchli | Tabiiy emas |
Routing | Oddiyroq | Juda kuchli |
Use case | Event stream, analytics, event sourcing | Task queue, routing, command messaging |
Scaling | Partition orqali | Queue/consumer orqali |
Consumer model | Pull | Push/prefetch |
Murakkablik | Yuqoriroq | Osonroq boshlanadi |
Qachon Kafka?
Kafka tanlang, agar:
eventlarni saqlash va qayta o‘qish kerak bo‘lsa;
katta throughput kerak bo‘lsa;
bir eventni ko‘p servislar mustaqil o‘qisa;
real-time analytics kerak bo‘lsa;
event-driven architecture markazida event log kerak bo‘lsa.
Misollar:
order.created
payment.completed
user.activity
clickstream
audit events
inventory changesQachon RabbitMQ?
RabbitMQ tanlang, agar:
background task queue kerak bo‘lsa;
murakkab routing kerak bo‘lsa;
command yuborish kerak bo‘lsa;
request/reply pattern kerak bo‘lsa;
delayed yoki priority queue kerak bo‘lsa.
Misollar:
send-email
resize-image
generate-report
process-payment-command
send-sms15. Event-driven architecture
Event-driven architecture — servislar bir-biri bilan eventlar orqali gaplashadigan arxitektura.
Event — tizimda bo‘lib o‘tgan fakt.
OrderCreated
PaymentCompleted
InventoryReserved
UserRegisteredEvent nomi odatda o‘tgan zamonda bo‘ladi, chunki bu buyruq emas, fakt.
Yaxshi:
OrderCreated
PaymentFailed
UserRegisteredYomon:
CreateOrder
SendEmail
DoPaymentBular commandga o‘xshaydi.
16. Event va Command farqi
Tushuncha | Ma’nosi | Misol |
|---|---|---|
Event | Narsa bo‘lib bo‘lgan | OrderCreated |
Command | Narsa bajarilsin | CreateOrder |
Query | Ma’lumot so‘rash | GetOrderById |
Event:
OrderCreatedMa’nosi:
Order yaratildiCommand:
CreateOrderMa’nosi:
Order yaratSenior developer event va commandni aralashtirmaydi.
17. Event schema design
Event yomon design qilinsa, keyin butun tizim qiynaladi.
Yaxshi event:
public record OrderCreatedEvent(
UUID eventId,
String eventType,
Long orderId,
Long userId,
BigDecimal totalAmount,
String currency,
Instant occurredAt,
Integer schemaVersion
) {}Nega kerak?
eventId— duplicate message’ni aniqlash uchun;eventType— event turini bilish uchun;orderId— aggregate ID;occurredAt— qachon bo‘lgan;schemaVersion— kelajakda event o‘zgarsa;biznes fieldlar — consumerga kerakli data.
18. Event schema evolution
Bugun event shunday:
{
"orderId": 1,
"amount": 100
}Ertaga currency qo‘shildi:
{
"orderId": 1,
"amount": 100,
"currency": "USD"
}Consumer eski fieldlarni kutayotgan bo‘lsa, buzilmasligi kerak.
Qoida:
yangi field qo‘shish odatda xavfsiz;
mavjud fieldni o‘chirish xavfli;
field nomini o‘zgartirish xavfli;
type o‘zgartirish xavfli;
schema version foydali;
Avro/Protobuf + Schema Registry katta tizimlarda foydali.
19. Outbox pattern
Messagingdagi eng muhim production patternlardan biri — Outbox pattern.
Yomon kod:
@Transactional
public void createOrder(CreateOrderRequest request) {
Order order = orderRepository.save(new Order(request));
kafkaTemplate.send("order.created", new OrderCreatedEvent(order.getId()));
}Bu xavfli.
Nega?
Holat 1
DB save bo‘ldi
Kafka send failedOrder bor, lekin event yo‘q.
Holat 2
Kafka send bo‘ldi
DB transaction rollback bo‘ldiEvent bor, lekin order yo‘q.
19.1. To‘g‘ri yechim
Order va event bitta DB transaction ichida saqlanadi:
@Transactional
public void createOrder(CreateOrderRequest request) {
Order order = orderRepository.save(new Order(request));
OutboxEvent event = new OutboxEvent(
UUID.randomUUID(),
"OrderCreated",
order.getId().toString(),
toJson(new OrderCreatedEvent(
UUID.randomUUID(),
order.getId(),
order.getUserId(),
order.getTotalAmount(),
Instant.now()
)),
Instant.now()
);
outboxRepository.save(event);
}Keyin alohida publisher:
outbox table → publisher → Kafka/RabbitMQPublisher event yuborilgach statusni o‘zgartiradi:
NEW → SENTYoki sent_at yozadi.
19.2. Outbox table
CREATE TABLE outbox_events (
id UUID PRIMARY KEY,
event_type VARCHAR(100) NOT NULL,
aggregate_id VARCHAR(100) NOT NULL,
payload JSONB NOT NULL,
status VARCHAR(20) NOT NULL,
created_at TIMESTAMP NOT NULL,
sent_at TIMESTAMP
);19.3. Publisher misol
@Component
@RequiredArgsConstructor
public class OutboxPublisher {
private final OutboxEventRepository repository;
private final KafkaTemplate<String, String> kafkaTemplate;
@Scheduled(fixedDelay = 1000)
public void publish() {
List<OutboxEvent> events = repository.findTop100ByStatusOrderByCreatedAt("NEW");
for (OutboxEvent event : events) {
kafkaTemplate.send("order.events", event.getAggregateId(), event.getPayload());
event.markAsSent();
}
repository.saveAll(events);
}
}Bu oddiy ko‘rinish. Productionda:
locking;
retry count;
error message;
batch;
transaction boundary;
duplicate publish ehtimoli;
idempotent consumer;
monitoring kerak.
20. Inbox pattern
Outbox producer tomonni himoya qiladi.
Consumer tomonda esa Inbox pattern ishlatiladi.
Maqsad:
Bir message ikki marta kelsa ham, faqat bir marta ishlansin.
CREATE TABLE inbox_events (
event_id UUID PRIMARY KEY,
processed_at TIMESTAMP NOT NULL
);Consumer:
@Transactional
public void handle(OrderCreatedEvent event) {
if (inboxRepository.existsById(event.eventId())) {
return;
}
processBusinessLogic(event);
inboxRepository.save(new InboxEvent(event.eventId(), Instant.now()));
}Bu payment, inventory, notification kabi consumerlarda juda foydali.
21. Ordering muammosi
Messagingda eventlar noto‘g‘ri tartibda kelishi mumkin.
Masalan:
OrderCancelled
OrderCreatedBu yomon.
Kafka’da buni kamaytirish uchun bir aggregate uchun bir xil key beriladi:
kafkaTemplate.send("order.events", orderId.toString(), event);Shunda bir orderga tegishli eventlar bitta partitionga tushadi va tartib saqlanadi.
Lekin bu butun topic bo‘yicha global ordering degani emas.
22. Poison message
Poison message — consumer doim ishlay olmaydigan yomon message.
Masalan:
{
"orderId": null
}Consumer har safar xato beradi.
Agar DLT bo‘lmasa:
consumer message’da tiqilib qoladi
qolgan message’lar ishlanmaydiShuning uchun kerak:
validation;
retry limit;
DLT;
alert;
manual fix/replay.
23. Retry strategiyasi
Messagingda retry ikki xil bo‘ladi:
23.1. Immediate retry
Darhol qayta urinish.
Mos:
vaqtinchalik kichik xato;
network glitch.
Lekin xavfli:
Down service’ni yana ham ezadi23.2. Delayed retry
Ma’lum vaqt kutib qayta ishlash.
Masalan:
retry after 10s
retry after 1m
retry after 5mYaxshi production yondashuv:
main topic
↓ failed
retry-10s topic
↓ failed
retry-1m topic
↓ failed
DLT24. Backpressure messagingda
Agar producer juda tez, consumer sekin bo‘lsa:
Producer: 10 000 msg/sec
Consumer: 1 000 msg/secQueue yoki lag oshadi.
Kafka’da bu consumer lag deyiladi.
Senior developer lagni monitoring qiladi:
consumer lag = oxirgi offset - consumer o‘qigan offsetAgar lag oshib borsa:
consumer sekin;
partition kam;
DB sekin;
external service sekin;
consumer ichida blocking bor;
batch size noto‘g‘ri.
25. Monitoring va alerting
Messaging productionda monitoringiz bo‘lmasa, ko‘r holda yurasiz.
Kafka uchun:
consumer lag;
producer error rate;
broker disk usage;
under-replicated partitions;
request latency;
topic throughput;
rebalance count.
RabbitMQ uchun:
queue depth;
unacked messages;
publish rate;
consume rate;
dead letter count;
memory usage;
connection/channel count.
Application darajasida:
event processing latency;
failed messages;
retry count;
DLT count;
duplicate message count.
26. Real ecommerce flow
POST /ordersOrder Service:
1. Order DBga yoziladi
2. Outbox event yoziladi
3. Response qaytadiOutbox Publisher:
4. OrderCreated event Kafka’ga yuboriladiPayment Service:
5. OrderCreated eventni o‘qiydi
6. Payment qiladi
7. PaymentCompleted event chiqaradiInventory Service:
8. PaymentCompleted eventni o‘qiydi
9. Stock reserve qiladi
10. InventoryReserved event chiqaradiNotification Service:
11. InventoryReserved eventni o‘qiydi
12. Userga email/SMS yuboradiBu yerda kerak bo‘ladi:
eventId;
idempotent consumer;
outbox;
retry;
DLT;
monitoring;
tracing;
schema version;
ordering uchun key.
27. Eng ko‘p uchraydigan xatolar
Xato 1: Event’ni command sifatida ishlatish
Yomon:
SendEmailEventBu event emas, command.
Yaxshiroq:
OrderCreatedNotification Service o‘zi qaror qiladi: email yuboradimi, SMS yuboradimi.
Xato 2: Consumer idempotent emas
Message ikki marta kelsa, biznes xato bo‘ladi.
Masalan:
ikki marta payment;
ikki marta SMS;
ikki marta bonus;
ikki marta inventory decrement.
Xato 3: DLT yo‘q
Bitta yomon message butun processingni to‘xtatadi.
Xato 4: Kafka’ni oddiy queue deb ishlatish
Kafka event log sifatida kuchli. Agar sizga faqat oddiy background job kerak bo‘lsa, RabbitMQ yoki boshqa queue yetarli bo‘lishi mumkin.
Xato 5: RabbitMQ’da routingni noto‘g‘ri loyihalash
Exchange, routing key va queue’lar nomlanishi chalkash bo‘lsa, keyin debugging og‘ir bo‘ladi.
Xato 6: Event schema’ni nazoratsiz o‘zgartirish
Producer fieldni o‘zgartirdi, consumer productionda yiqildi.
Schema evolution qoidalari bo‘lishi kerak.
28. Senior uchun amaliy checklist
Messaging qo‘shishdan oldin savollar:
Bu sync bo‘lishi shartmi yoki async bo‘lsa bo‘ladimi?
Message yo‘qolsa nima bo‘ladi?
Message ikki marta kelsa nima bo‘ladi?
Message tartibi muhimmi?
Consumer down bo‘lsa nima bo‘ladi?
Retry nechta bo‘ladi?
DLT bormi?
Event schema qanday versionlanadi?
Monitoring qayerda?
Correlation ID bormi?
Outbox kerakmi?
Consumer idempotentmi?29. Interview savollari
Senior Java uchun bilish kerak:
Kafka
Topic, partition, offset nima?
Consumer group qanday ishlaydi?
Kafka’da ordering qayerda kafolatlanadi?
Consumer lag nima?
At-least-once va exactly-once farqi?
Kafka replay nima?
Partition key noto‘g‘ri tanlansa nima bo‘ladi?
RabbitMQ
Exchange va queue farqi?
Direct, fanout, topic exchange farqi?
Binding nima?
Prefetch nima?
Ack/nack nima?
Dead letter exchange nima?
Architecture
Event va command farqi?
Outbox pattern nima uchun kerak?
Inbox pattern nima?
Idempotent consumer qanday qilinadi?
DLT nima uchun kerak?
Retry storm nima?
Kafka vs RabbitMQ qachon tanlanadi?
30. Qisqa xulosa
Messaging & streaming Senior Java developer uchun juda muhim mavzu.
Asosiy fikrlar:
Messaging servislarni bo‘sh bog‘laydi
Async processing latency’ni kamaytiradi
Kafka event streaming uchun kuchli
RabbitMQ routing va task queue uchun qulay
Duplicate message normal holat
Consumer idempotent bo‘lishi shart
Outbox event yo‘qolishini kamaytiradi
DLT productionda majburiy
Monitoring bo‘lmasa messaging xavfliEng muhim qoida:
Messaging qo‘shish — shunchaki Kafka yoki RabbitMQ ulash emas. Bu failure, duplicate, retry, ordering, schema va monitoring muammolarini ham design qilish degani.