Mundarija (22)
- Bu darsda
- 1. Nega bu kerak?
- 2. RabbitMQ
- 2.1 Tushunchalar
- 2.2 Kanon tajribasi
- 2.3 Kutilmagan qoida: nack limitga hisoblanmaydi
- 3. NATS JetStream
- 4. pg-boss: Postgres'dagi navbat
- 5. Kafka — sinalmagan
- 5.1 G'oya
- 5.2 Nega sinalmagan va qachon kerak
- 6. SQS — sinalmagan
- 7. Qaysi birini tanlash
- 8. Loyiha qadami: kanon tajribasi, loyihaga kirmaydi
- 9. Ko'p uchraydigan xatolar
- 10. Mashqlar
- 1-mashq (oson): bo'shliqlarni to'ldiring
- 2-mashq (o'rta): kanon raqamlaridan tezlik
- 3-mashq (qiyin): RabbitMQ topic exchange
- 11. Real ishda
- Xulosa
- Manbalar
Boshqa xabar broker'lari: RabbitMQ, Kafka, SQS, NATS va pg-boss
Qisqacha: BullMQ — Node ilovasi ichidagi ish navbati. Tizimlar va tillar o'rtasida xabar almashish uchun alohida brokerlar bor. RabbitMQ — marshrutlash ustasi: exchange xabarni qoidalar bo'yicha navbatlarga tarqatadi, ack/nack, prefetch, dead letter. Kafka — xabarlarni o'chirmaydigan jurnal: partition, offset, qayta o'qish. SQS — AWS'ning boshqariladigan navbati, visibility timeout bilan. NATS JetStream — yengil va tez. pg-boss — Redis'siz, Postgres'ning o'zida. Kanonda 10 000 xabar: NATS 1.9 s, RabbitMQ 3.2 s, pg-boss 25.3 s. Kafka va SQS — sinalmagan.
Bu darsda
- RabbitMQ'ning exchange, queue, binding, ack/nack, prefetch va dead letter tushunchalarini kanon tajribasida ko'rasiz.
- RabbitMQ 4 dagi kutilmagan qoidani bilasiz:
nackqayta yetkazish limitiga hisoblanmaydi. - Kafka'ning topic, partition, offset va consumer group g'oyasini modelda tushunasiz.
- SQS'dagi visibility timeout'ni modellashtirasiz.
- NATS JetStream va pg-boss o'lchovlarini ko'rib, loyiha uchun broker tanlash mezonlarini bilasiz.
Oldin bilishingiz kerak: BullMQ ishonchlilik, Pub/Sub va Redis Streams, SKIP LOCKED bilan DB-asosli navbat, Nega navbat kerak.
Oldingi darsda vaqtga bog'langan ishlar bilan BullMQ bo'limini yakunladik. Endi BullMQ'dan tashqariga qaraymiz.
1. Nega bu kerak?
BullMQ bizning loyihaga yetadi: bitta Node ilovasi, Redis bor. Lekin kompaniya o'sadi:
- to'lov servisi Go'da, bildirishnoma servisi Node'da, analitika Python'da — ular bir-biriga hodisa yuborishi kerak;
- bitta hodisa ("vazifa bajarildi") uchta joyga borishi kerak: eslatma, audit, statistika;
- hodisalar tarixini qayta o'qish kerak: yangi analitika servisi o'tgan oy hodisalaridan hisob yasashi kerak.
BullMQ — Node kutubxonasi va ish navbati (bitta ish — bitta bajaruvchi). Bu ehtiyojlarga mo'ljallangan vositalar — xabar broker'lari (message broker): alohida server, ko'p tilli klientlar, standart protokollar. Ularni ishlatishdan oldin bilish kerak — qaysi muammoga qaysi biri.
2. RabbitMQ
2.1 Tushunchalar
RabbitMQ (AMQP 0-9-1 protokoli) da producer xabarni to'g'ridan navbatga emas, exchange ga yuboradi. Exchange binding qoidalariga qarab uni bir yoki bir nechta navbatga tarqatadi. Consumer navbatdan oladi va ack qiladi.
flowchart LR
P[Producer] -->|routing key eslatma| X{{vazifalar - direct exchange}}
X -->|binding eslatma| Q[(eslatmalar - quorum queue)]
Q -->|prefetch 100| C[Consumer]
C -->|ack| Q
C -->|nack requeue=false| Q
Q -->|dead letter| DLX{{vazifalar.dlx - fanout}}
DLX --> DLQ[(eslatmalar.dlq)]| Tushuncha | Ma'nosi |
|---|---|
| Exchange | xabarni qabul qilib, navbatlarga tarqatuvchi |
direct |
routing key aynan mos navbatga |
topic |
naqsh bo'yicha: vazifa.*.muddat, vazifa.# |
fanout |
bog'langan hamma navbatga |
| Binding | "exchange X dan kalit K bilan kelganini navbat Q ga" qoidasi |
| ack / nack | "bajardim" / "bajarolmadim" (requeue — navbatga qaytarilsinmi) |
| Prefetch | consumer'ga ack'siz bir vaqtda nechta xabar beriladi |
| DLX | dead letter exchange — rad etilgan yoki limitdan oshgan xabarlar boradigan joy |
| Quorum queue | bir nechta tugunga nusxalanadigan, ishonchli navbat turi |
Prefetch — BullMQ'dagi concurrency'ning o'xshashi: prefetch(100) — consumer'da bir vaqtda 100 tadan ortiq tasdiqlanmagan xabar bo'lmaydi. Prefetchsiz RabbitMQ navbatdagi hamma xabarni bitta consumer'ga to'kib yuborishi mumkin.
2.2 Kanon tajribasi
Kanon RabbitMQ 4.4.0 (rabbitmq:4-alpine, Docker) va amqplib 2.2.0 bilan direct exchange, quorum queue va DLX qurdi (formatlab, qisqartirib):
import amqp from "amqplib";
const c = await amqp.connect("amqp://guest:guest@127.0.0.1:4472");
const ch = await c.createChannel();
await ch.assertExchange("vazifalar", "direct", { durable: true });
await ch.assertExchange("vazifalar.dlx", "fanout", { durable: true });
await ch.assertQueue("eslatmalar.dlq", { durable: true });
await ch.bindQueue("eslatmalar.dlq", "vazifalar.dlx", "");
await ch.assertQueue("eslatmalar", {
durable: true,
arguments: {
"x-dead-letter-exchange": "vazifalar.dlx",
"x-queue-type": "quorum",
"x-delivery-limit": 2,
},
});
await ch.bindQueue("eslatmalar", "vazifalar", "eslatma");
for (let i = 0; i < N; i++) {
ch.publish("vazifalar", "eslatma",
Buffer.from(JSON.stringify({ i })), { persistent: true });
}
const ish = await c.createChannel();
await ish.prefetch(100);
await ish.consume("eslatmalar", (m) => {
// ... i === 7 bo'lsa nack, qolganlari ack
ish.ack(m);
});guest:guest — konteynerning sukut foydalanuvchisi, faqat lokal; production'da — o'z foydalanuvchingiz va parol muhit o'zgaruvchisidan. Natijada ikkinchi qatorga alohida qarang: 7-xabar necha marta qaytdi va qachon DLQ'ga tushdi:
10000 xabar (persistent, quorum queue): 3187 ms; ack 9999
i=7: nack(requeue=true) bilan 135 marta qaytdi — x-delivery-limit 2 ga QARAMAY DLQ'ga o'tmadi (RabbitMQ 4.4 kuzatuvi); nack(requeue=false) dan keyin DLQ'da: 12.3 Kutilmagan qoida: nack limitga hisoblanmaydi
Nima bo'ldi? 10 000 xabardan 9 999 tasi tasdiqlandi, bittasi (7-xabar) "issiq sikl"da aylanib qoldi. Kanon x-delivery-limit: 2 qo'ydi: "xabar 2 martadan ortiq qayta yetkazilsa — DLQ'ga". Keyin 7-xabarni nack(requeue=true) bilan 135 marta qaytardi. Xabar DLQ'ga o'tmadi — har safar yana navbatga keldi. Faqat nack(requeue=false) dan keyin DLQ'da paydo bo'ldi.
Kanon buni "RabbitMQ 4.4 kuzatuvi" deb yozdi. Biz rasmiy hujjatni tekshirdik (rabbitmq.com, Quorum Queues sahifasi, 2026-10-28): 4.3 versiyasidan boshlab limit delivery-count ga asoslanadi va u faqat haqiqiy muvaffaqiyatsizlikda oshadi. Hujjat jadvaliga ko'ra basic.nack delivery-count ni oshirmaydi va limitga hisoblanmaydi, basic.reject esa oshiradi. Ya'ni bu xato emas — hujjatlashtirilgan xatti-harakat. 4.0 dan boshlab quorum queue'larda sukut limit — 20.
Amaliy xulosa:
- "Zaharli" xabarni cheksiz qaytarmaslik uchun
nack(requeue=true)ga tayanmang. O'zingiz urinishlarni sanang (sarlavhada yoki bazada) va chegaradan keyinnack(m, false, false)— DLQ'ga. - Broker xatti-harakati versiyalar orasida o'zgaradi. Eski maqoladagi "delivery-limit nack'ni ham sanaydi" gapi bugun noto'g'ri bo'lishi mumkin — versiyangiz hujjatini o'qing va o'zingiz o'lchang.
Tekshirib ko'ring: Nega
nack(requeue=true)bilan 135 marta qaytgan xabar consumer uchun xavfli, hatto DLQ bo'lmasa ham?
Javob
Xabar darhol navbatga qaytadi va darhol yana keladi — consumer CPU'si va tarmoq shu bitta xabarga sarflanadi ("issiq sikl"). Boshqa xabarlar sekinlashadi, loglar to'ladi. BullMQ'dagi backoff kabi kutish yo'q. Shuning uchun qayta urinish kerak bo'lsa — kechiktiruvchi navbat (TTL + DLX) yoki urinishlarni sanash.
3. NATS JetStream
NATS — yengil xabar tizimi (Go'da yozilgan, bitta kichik binar). JetStream — uning saqlovchi qatlami: stream xabarlarni diskda saqlaydi, consumer ulardan o'qiydi, ack kutadi. Kanon NATS 2.15.1 va @nats-io/* 3.4.0 bilan sinadi (formatlab):
import { connect } from "@nats-io/transport-node";
import { jetstream, jetstreamManager, AckPolicy }
from "@nats-io/jetstream";
const nc = await connect({ servers: "127.0.0.1:4422" });
const jsm = await jetstreamManager(nc);
await jsm.streams.add({ name: "VAZIFA", subjects: ["vazifa.>"] });
await jsm.consumers.add("VAZIFA", {
durable_name: "eslatmachi",
ack_policy: AckPolicy.Explicit,
ack_wait: 1_000_000_000, // nanosoniya — 1 s
max_deliver: 3,
});Chiqishda vaqtdan tashqari i=7 yetkazish raqamlari ga qarang:
10000 xabar: publish 860 ms, jami 1875 ms; i=7 yetkazish raqamlari: [1,2] (ack_wait 1 s dan keyin qayta keldi)7-xabar birinchi marta ack qilinmadi (worker o'ldi taqlidi) — ack_wait (1 s) dan keyin u ikkinchi marta keldi, deliveryCount 2. Bu BullMQ'dagi stalled va SQS'dagi visibility timeout'ning o'xshashi: tasdiq kelmasa — qayta. E'tibor bering: ack_wait nanosoniyada — 1_000 yozsangiz, 1 mikrosoniya bo'ladi.
4. pg-boss: Postgres'dagi navbat
SKIP LOCKED darsida yozgan navbatimizning tayyor, to'liq versiyasi — pg-boss. Redis kerak emas: navbat — Postgres sxemasidagi jadvallar. Kanon pg-boss 12.38.0 bilan (formatlab):
import { PgBoss } from "pg-boss";
const boss = new PgBoss({ connectionString, schema: "pgboss_t49" });
await boss.start();
await boss.createQueue("eslatma");
await boss.insert("eslatma",
Array.from({ length: N }, (_x, i) => ({ data: { i } })));
await boss.work("eslatma",
{ batchSize: 200, pollingIntervalSeconds: 0.5 },
async (ishlar) => { olindi += ishlar.length; });
await boss.createQueue("bir-marta", { policy: "short" });
const a = await boss.send("bir-marta", { x: 1 },
{ singletonKey: "hisobot-2026-10-27" });
const b = await boss.send("bir-marta", { x: 1 },
{ singletonKey: "hisobot-2026-10-27" });10000 ish: insert 405 ms, ishlash jami 25338 ms (polling 0.5 s, batch 200)
policy short + singletonKey bilan ikki send: id / null (navbatda bor — rad)- Kiritish tez — 405 ms (bitta
INSERTbilan ko'p qator). - Ishlash sekin — 25.3 s. Sabab — polling: worker har 0.5 s da bir marta bazaga "ish bormi" deb so'raydi va bir martada 200 tadan oladi. 10 000 / 200 = 50 so'rov × 0.5 s ≈ 25 s. Bu pg-boss'ning sekinligi emas, sozlamaning natijasi — lekin Postgres navbati tabiatan "so'rab turadi" (BullMQ esa Redis'da bloklovchi kutish bilan ish kelishini darhol biladi).
singletonKey— ikkinchisendnullqaytardi: shu kalitli ish navbatda bor. BullMQ'dagijobIdning o'xshashi.
pg-boss'ning asosiy afzalligi — ish ma'lumot bilan bitta tranzaksiyada qo'shilishi mumkin (bitta baza). Bu Transactional outbox muammosini boshqa yo'l bilan hal qiladi.
Yana bir yangilik: kanon ishlatgan bullmq 6.3.19 paketi ichida PostgreSQL backend kodi bor (createPostgresBackend, pg — ixtiyoriy peer bog'liqlik) — biz paket fayllarida ko'rdik. Kanon uni sinamagan; ishlatishdan oldin BullMQ hujjatidagi holatini tekshiring.
5. Kafka — sinalmagan
5.1 G'oya
Kafka — navbat emas, taqsimlangan jurnal (log). Xabar o'qilgandan keyin o'chirilmaydi, sozlangan muddatgacha (masalan 7 kun) saqlanadi. Har consumer jurnalda qayerga kelganini — offset ni — o'zi eslab qoladi. Yangi servis jurnalni boshidan o'qishi mumkin (replay).
- Topic — jurnal nomi (
vazifa-hodisalari). - Partition — topic bo'laklari; parallellik birligi. Bir kalitli xabarlar doim bitta partition'ga tushadi — ular orasida tartib saqlanadi.
- Consumer group — bir guruhdagi consumer'lar partition'larni bo'lishib oladi; har partition — guruhdagi bitta consumer'ga.
// Kafka g'oyasi: topic → partition'lar;
// kalit bir xil — partition bir xil.
// (Kafka murmur2 xeshini ishlatadi; bu yerda o'quv uchun oddiy xesh.)
const xesh = (s) => [...s]
.reduce((h, c) => (h * 31 + c.charCodeAt(0)) >>> 0, 7);
const PARTITION = 3;
const log = Array.from({ length: PARTITION }, () => []);
const hodisalar = [
["vazifa-7", "yaratildi"], ["vazifa-8", "yaratildi"],
["vazifa-7", "tahrirlandi"], ["vazifa-9", "yaratildi"],
["vazifa-7", "bajarildi"], ["vazifa-8", "o'chirildi"],
];
for (const [kalit, tur] of hodisalar) {
const p = xesh(kalit) % PARTITION;
log[p].push(`${kalit}:${tur}`); // offset = massivdagi indeks
}
log.forEach((l, p) => console.log(`partition ${p}:`, l.join(", ")));
// Consumer group: har partition — guruhdagi bitta consumer'ga.
const guruh = ["c1", "c2"];
log.forEach((_, p) => {
console.log(`p${p} → ${guruh[p % guruh.length]}`);
});Konsolda:
partition 0: vazifa-9:yaratildi
partition 1: vazifa-7:yaratildi, vazifa-7:tahrirlandi, vazifa-7:bajarildi
partition 2: vazifa-8:yaratildi, vazifa-8:o'chirildi
p0 → c1
p1 → c2
p2 → c1vazifa-7 ning uchala hodisasi bitta partition'da, tartib bilan — consumer "bajarildi" ni "yaratildi" dan oldin ko'rmaydi. Lekin partition'lar orasida tartib yo'q. Guruhda 2 consumer, partition 3 ta: c1 ikkitasini oladi. Consumer 4 ta bo'lsa, bittasi bo'sh turadi — parallellik partition sonidan oshmaydi.
5.2 Nega sinalmagan va qachon kerak
Kanon Kafka'ni sinamadi: Kafka Java mashinasida (JVM) ishlaydi, uning image'i ~400+ MB disk va katta RAM talab qiladi, kanon mashinasida joy tor edi. Yuqoridagi model — g'oya, Kafka API emas. Kafka kerak bo'ladigan joylar: sekundiga yuz minglab hodisa, hodisalarni qayta o'qish (replay), bir hodisani ko'p mustaqil servis o'qishi, oqimli analitika. Kichik va o'rta Node loyiha uchun bu ortiqcha murakkablik. Node klientlari (KafkaJS, @confluentinc/kafka-javascript) holatini hujjatdan tekshiring. Nazariya — Event streaming nazariyasi darsida.
6. SQS — sinalmagan
Amazon SQS — AWS'ning boshqariladigan navbati: server o'rnatmaysiz, so'rov uchun to'laysiz. Asosiy tushuncha — visibility timeout: consumer xabarni olganda u o'chirilmaydi, faqat ma'lum vaqtga boshqalardan yashiriladi. Consumer DeleteMessage qilmasa, vaqt tugagach xabar yana ko'rinadi.
// SQS g'oyasi: xabar o'chirilmaydi, faqat "ko'rinmas" bo'ladi.
// O'chirish (DeleteMessage) bo'lmasa — timeout'dan keyin
// yana ko'rinadi.
const VISIBILITY = 30; // soniya
const xabar = { id: "m-1", korinmasGacha: 0, qabul: 0 };
function receive(soat) {
if (soat < xabar.korinmasGacha) return null;
xabar.korinmasGacha = soat + VISIBILITY;
xabar.qabul++;
const n = xabar.qabul;
return `t=${soat}s: ${xabar.id} olindi (ReceiveCount ${n})`;
}
for (const soat of [0, 10, 29, 30, 45, 61]) {
console.log(receive(soat) ?? `t=${soat}s: bo'sh (xabar ko'rinmas)`);
}Konsolda:
t=0s: m-1 olindi (ReceiveCount 1)
t=10s: bo'sh (xabar ko'rinmas)
t=29s: bo'sh (xabar ko'rinmas)
t=30s: m-1 olindi (ReceiveCount 2)
t=45s: bo'sh (xabar ko'rinmas)
t=61s: m-1 olindi (ReceiveCount 3)Ish 30 soniyadan uzoq davom etsa, xabar ikkinchi consumer'ga ham boradi — BullMQ'dagi lockDuration bilan aynan bir xil muammo. SQS'da DLQ "redrive policy" orqali: maxReceiveCount dan oshgan xabar DLQ'ga. Standard navbat at-least-once va tartibni kafolatlamaydi; FIFO navbat tartib va dedup (takroriy xabarni tashlab yuborish) beradi, lekin sekinroq (hujjat bo'yicha). Kanon sinamadi — AWS akkaunt kerak.
7. Qaysi birini tanlash
Kanon o'lchovlari (10 000 xabar, bitta mashina, Docker, bir marta):
| Broker | 10 000 xabar | Izoh |
|---|---|---|
| NATS JetStream 2.15 | 1 875 ms (publish 860) | eng tezi |
| RabbitMQ 4.4 (quorum, persistent) | 3 187 ms | persistent + quorum — ishonchlilik narxi |
| pg-boss 12.38 | 25 338 ms (insert 405) | polling 0.5 s × batch 200 |
| Kafka, SQS | — | sinalmagan |
Tezlik tanlovning yagona mezoni emas:
| Ehtiyoj | Tanlov |
|---|---|
| Node ilova, Redis bor, fon vazifalari | BullMQ |
| Redis yo'q, kichik/o'rta yuk, ish ma'lumot bilan bitta tranzaksiyada | pg-boss |
| Ko'p tilli servislar, murakkab marshrutlash | RabbitMQ |
| Yengil, tez, oqim va so'rov-javob | NATS JetStream |
| Juda katta hajm, replay, ko'p mustaqil o'quvchi | Kafka (sinalmagan) |
| AWS'da, server boshqarmasdan | SQS (sinalmagan) |
Kanon tanlovi o'zgarmadi: vazifalar uchun BullMQ — Redis 27-qismdan bor, bitta Node ilova. Yangi broker — yangi server, monitoring, zaxira nusxa va bilim. Uni ehtiyoj paydo bo'lganda qo'shing, "keyinroq kerak bo'lar" deb emas.
8. Loyiha qadami: kanon tajribasi, loyihaga kirmaydi
Bu darsda loyihaga kod qo'shilmaydi. Kanon uchta brokerni o'lchab, BullMQ tanlovini tasdiqladi va RabbitMQ'ning nack qoidasini topdi. O'zingiz sinab ko'ring: Docker o'rnatilgan bo'lsa, docker run -d --name rabbit -p 5672:5672 rabbitmq:4-alpine va npm i amqplib@2.2.0 bilan darsdagi direct exchange'ni quring. Keyin nack(m, false, true) o'rniga ch.reject(m, true) ni sinab, xabar x-delivery-limit dan keyin DLQ'ga o'tishini tekshiring (hujjatga ko'ra o'tishi kerak; kanon buni sinamagan).
9. Ko'p uchraydigan xatolar
| Belgi | Sabab | Davosi |
|---|---|---|
| Bitta xabar cheksiz qaytyapti | nack(requeue=true) limitga hisoblanmaydi (RabbitMQ ≥ 4.3) |
urinishlarni sanash, requeue=false → DLX |
| Consumer xotirasi to'ldi | prefetch yo'q | prefetch(N) |
| Broker qayta yonganda xabarlar yo'q | durable/persistent emas | durable: true, persistent: true |
| JetStream xabarni qayta yuboryapti | ack_wait juda kichik (nanosoniya!) |
birlikni tekshiring |
| pg-boss sekin | polling oralig'i | pollingIntervalSeconds, batchSize |
| Kafka'da tartib buzildi | turli kalit — turli partition | tartib kerak hodisalarga bir xil kalit |
| SQS'da xabar ikki marta | ish visibility timeout'dan uzoq | timeout'ni oshirish yoki uzaytirish, idempotentlik |
10. Mashqlar
Mashqlar kurs/mashqlar/29/49-brokerlar/ da.
1-mashq (oson): bo'shliqlarni to'ldiring
- RabbitMQ'da xabarni navbatlarga tarqatuvchi —
- Consumer'ga ack'siz bir vaqtda beriladigan xabarlar soni —
- Kafka'da consumer jurnalda qayerga kelgani —
- SQS'da olingan xabarni vaqtincha yashiradigan sozlama — visibility
- Redis'siz, Postgres'dagi Node navbat kutubxonasi —
Yechim
- exchange — direct, topic, fanout.
- prefetch — BullMQ concurrency'ning o'xshashi.
- offset — consumer o'zi saqlaydi, replay mumkin.
- timeout — o'chirilmasa, xabar qayta ko'rinadi.
- pg-boss — polling,
singletonKey.
2-mashq (o'rta): kanon raqamlaridan tezlik
Kanon o'lchovlari 10 000 xabar uchun: RabbitMQ 3187 ms, NATS 1875 ms, pg-boss 25338 ms. Har broker uchun soniyasiga xabar sonini hisoblang, kamayish tartibida chiqaring va eng tezi eng sekinidan necha baravar tez ekanini toping.
Yechim
const N = 10_000;
const olchov = [
{ nom: "RabbitMQ (quorum)", ms: 3187 },
{ nom: "NATS JetStream", ms: 1875 },
{ nom: "pg-boss", ms: 25338 },
];
const tezlik = olchov
.map((o) => ({ ...o, sekundiga: Math.round(N / (o.ms / 1000)) }))
.sort((a, b) => b.sekundiga - a.sekundiga);
for (const o of tezlik) {
const son = String(o.sekundiga).padStart(5);
console.log(`${o.nom.padEnd(18)} ${son} xabar/s`);
}
const engTez = tezlik[0].sekundiga;
const engSekin = tezlik.at(-1).sekundiga;
console.log("farq:", Math.round(engTez / engSekin), "baravar");Konsolda:
NATS JetStream 5333 xabar/s
RabbitMQ (quorum) 3138 xabar/s
pg-boss 395 xabar/s
farq: 14 baravar14 baravar — lekin bu "pg-boss yomon" degani emas. Kanon sozlamasida pg-boss 0.5 s da bir marta so'radi; kuniga 10 000 ta email yuboradigan ilova uchun 395 xabar/s ham ortiqcha. Bir o'lchov, bitta mashina, Docker ichida — production raqami emas. Tanlovda tezlikdan oldin: "Redis bormi?", "ish ma'lumot bilan bitta tranzaksiyada kerakmi?", "kim ishlatadi va kuzatadi?"
3-mashq (qiyin): RabbitMQ topic exchange
Topic exchange'da routing key nuqta bilan ajratilgan so'zlar (vazifa.7.muddat), binding naqshida * — aynan bitta so'z, # — nol yoki ko'p so'z. mos(pattern, kalit) funksiyasini yozing va uchta binding (eslatmalar: vazifa.*.muddat, audit: vazifa.#, email: #.email) uchun to'rt kalit qaysi navbatlarga borishini chiqaring.
Yechim
// "*" — aynan bitta so'z, "#" — nol yoki ko'p so'z
function mos(pattern, kalit) {
const p = pattern.split(".");
const k = kalit.split(".");
function yur(i, j) {
if (i === p.length) return j === k.length;
if (p[i] === "#") {
for (let n = j; n <= k.length; n++) {
if (yur(i + 1, n)) return true;
}
return false;
}
if (j === k.length) return false;
return (p[i] === "*" || p[i] === k[j]) && yur(i + 1, j + 1);
}
return yur(0, 0);
}
const bindinglar = {
eslatmalar: "vazifa.*.muddat",
audit: "vazifa.#",
email: "#.email",
};
const kalitlar = [
"vazifa.7.muddat", "vazifa.7.izoh.email", "foydalanuvchi.email",
"vazifa",
];
for (const k of kalitlar) {
const qayerga = Object.entries(bindinglar)
.filter(([, p]) => mos(p, k)).map(([q]) => q);
const matn = qayerga.join(", ") || "(hech qayer)";
console.log(k.padEnd(20), "→", matn);
}Konsolda:
vazifa.7.muddat → eslatmalar, audit
vazifa.7.izoh.email → audit, email
foydalanuvchi.email → email
vazifa → auditOxirgisiga e'tibor bering: vazifa.# yolg'iz vazifa ga ham mos, chunki # nol so'zni ham qabul qiladi. vazifa.*.muddat esa vazifa.7.izoh.muddat ga mos kelmaydi — * faqat bitta so'z. Bitta xabar bir nechta navbatga ketdi — bu fanout'siz ham "bir hodisa — ko'p iste'molchi". RabbitMQ'ning haqiqiy algoritmi trie (so'zlar daraxti) bilan ishlaydi; rekursiv versiya — tushunish uchun.
11. Real ishda
- RabbitMQ — bank, logistika, ko'p tilli mikroservislar; NestJS mikroservis transporti sifatida ham (Mikroservis asoslari NestJS'da).
- Kafka — katta kompaniyalarda hodisalar "magistrali", analitika va CDC (CDC va ma'lumot sinxronlash).
- SQS/SNS, Google Pub/Sub, Azure Service Bus — bulutdagi boshqariladigan muqobillar.
- pg-boss — "Redis qo'shmaymiz" degan kichik jamoalar va bitta Postgres'li loyihalar.
- Intervyuda: "RabbitMQ va Kafka farqi?", "Visibility timeout nima?", "Partition va consumer group qanday ishlaydi?", "Navbat va event stream farqi?"
Xulosa
- BullMQ — Node ichidagi ish navbati; broker'lar — tillar va servislar orasidagi xabar almashish.
- RabbitMQ: exchange → binding → queue, ack/nack, prefetch, DLX; 10 000 xabar 3.2 s (quorum, persistent).
- RabbitMQ ≥ 4.3:
nack(requeue=true)x-delivery-limitga hisoblanmaydi (kanonda 135 marta qaytdi; hujjat tasdiqlaydi);requeue=false→ DLQ. - NATS JetStream: 1.9 s,
ack_wait(nanosoniya) dan keyin qayta yetkazish. - pg-boss: Redis'siz,
singletonKey; polling tufayli 25.3 s. - Kafka — jurnal, partition, offset, replay; SQS — visibility timeout; ikkalasi sinalmagan.
Keyingi dars: Transactional outbox naqshi — bazaga yozish va navbatga xabar yuborish ikkalasi birga bo'lishi uchun: kanonda outbox'siz 100 tadan 10 ta hodisa yo'qoldi, outbox bilan — 0.
Manbalar
- RabbitMQ — AMQP 0-9-1 model (exchange, binding): https://www.rabbitmq.com/tutorials/amqp-concepts
- RabbitMQ — Quorum Queues (delivery-limit,
delivery-count, 4.0 sukut 20, 4.3 o'zgarishi): https://www.rabbitmq.com/docs/quorum-queues — 2026-10-28 da o'qildi - RabbitMQ — Consumer Prefetch, Dead Letter Exchanges: https://www.rabbitmq.com/docs/consumer-prefetch, https://www.rabbitmq.com/docs/dlx
- NATS JetStream — Consumers (
ack_wait,max_deliver): https://docs.nats.io/nats-concepts/jetstream/consumers - pg-boss: https://github.com/timgit/pg-boss
- Apache Kafka — Introduction (topic, partition, consumer group): https://kafka.apache.org/documentation/#introduction — sinalmagan
- Amazon SQS — Visibility timeout, DLQ: https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-visibility-timeout.html — sinalmagan
- Kanon tajribasi (IlmHamroh, 2026-10-27): RabbitMQ 4.4.0, NATS 2.15.1 (Docker), amqplib 2.2.0, @nats-io/* 3.4.0, pg-boss 12.38.0, PostgreSQL 18.6, Node 24.21.0. Modellar va mashq yechimlari — biz ishga tushirdik (2026-10-28).
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!