30 վայրկյանում
RabbitMQ-ն բաց կոդով message broker է։ Producer-ները հաղորդագրությունները հրապարակում են exchange-ներում, exchange-ները դրանք ըստ կանոնների ուղղորդում են հերթեր (queue), իսկ consumer-ները վերցնում են հաղորդագրությունները հերթերից և հաստատում (ack) դրանք, երբ աշխատանքն ավարտված է։ Այն անջատում է ծառայությունները միմյանցից, հարթեցնում է տրաֆիկի գագաթները և կրկնում է ձախողված աշխատանքը։ Ընթացիկ 4.x գիծն աջակցում է AMQP 0-9-1, AMQP 1.0, MQTT և streams, իսկ ռեպլիկացվող quorum queue-ները անվտանգ լռելյայն ընտրությունն են։
Իմ ամենօրյա stack-ում չէ․ գիտեմ հիմնական գաղափարները և մանրամասները կսովորեմ, երբ նախագիծը դա պահանջի։
Ինչու է սա կարևոր
- Ոչ ամեն առաջադրանք պետք է կատարվի HTTP հարցման ներսում։ Broker-ը թույլ է տալիս API-ին ակնթարթորեն ընդունել աշխատանքը և email-ները, պատկերների մշակումը կամ ինտեգրացիաները փոխանցել ֆոնային consumer-ներին։
- Ուղղորդումն է նրա ուժը․ մեկ իրադարձությունը կարող է ըստ տեսակի կամ pattern-ի հասնել բազմաթիվ հերթերի, ուստի նոր consumer-ներ կարելի է ավելացնել առանց producer-ը փոխելու։
- Այն կախված չէ լեզվից․ PHP ծառայությունը կարող է հրապարակել, իսկ Go, Python կամ Node.js worker-ները՝ սպառել, ինչը կարևոր է բազմալեզու համակարգերում։
- Այն գտնվում է Redis-ի վրա հիմնված պարզ job queue-ների (օրինակ՝ BullMQ կամ Laravel queues) և Kafka-ի նման log պլատֆորմի միջև․ ավելի հարուստ ուղղորդում և առաքման երաշխիքներ, քան առաջինները, և ավելի պարզ շահագործում, քան վերջինը։
Ինչպես է աշխատում
01Գաղափարը՝ փոստատուն հաղորդագրությունների համար
Պատկերացրեք RabbitMQ-ն որպես փոստատուն։ Producer-ը նամակը (message) թողնում է պատուհանում (exchange)՝ հասցեի պիտակով (routing key)։ Փոստատունը այն ըստ կանոնների (bindings) տեսակավորում է փոստարկղերի (queues) մեջ։ Consumer-ը դատարկում է իր փոստարկղը և ստորագրում յուրաքանչյուր նամակի համար (acknowledgement)։ Եթե consumer-ը երբեք չի ստորագրում, նամակը վերադառնում է՝ նորից առաքվելու։ Այս բաժանումը նշանակում է, որ producer-ը չգիտի և չի մտածում, թե ով է մշակում հաղորդագրությունը, քանի worker կա կամ արդյոք նրանք հիմա առցանց են․ հաղորդագրությունները ապահով սպասում են հերթում։
- Message՝ payload (բայթեր, սովորաբար JSON) և հատկություններ, ինչպիսիք են content type-ը, message ID-ն և header-ները։
- Queue՝ կարգավորված բուֆեր, որը պահում է հաղորդագրությունները, մինչև consumer-ը դրանք վերցնի։
- Connection և channel՝ մեկ TCP connection մեկ պրոցեսի համար, իսկ դրա ներսում թեթև channel-ներ յուրաքանչյուր thread-ի կամ առաջադրանքի համար։
02Exchange-ներ և ուղղորդում
Producer-ները երբեք ուղղակիորեն հերթում չեն հրապարակում․ նրանք հրապարակում են exchange-ում։ Exchange-ի տեսակն է որոշում ուղղորդումը։ Direct exchange-ը առաքում է այն հերթերին, որոնց binding key-ը ճշգրիտ համընկնում է routing key-ի հետ։ Topic exchange-ը համեմատում է կետերով pattern-ներ․ «order.*»-ը համընկնում է «order.created»-ի հետ, իսկ «order.#»-ը՝ ցանկացած խորության։ Fanout exchange-ը յուրաքանչյուր հաղորդագրություն պատճենում է բոլոր կապված հերթերին՝ անտեսելով key-երը։ Headers exchange-ը ուղղորդում է ըստ header-ների արժեքների։ Իրադարձությունների համար տարածված դիզայնն է՝ մեկ topic exchange յուրաքանչյուր դոմենի համար (orders, users) և մեկ հերթ consumer-ի յուրաքանչյուր նպատակի համար (email.order-created, analytics.orders), որպեսզի յուրաքանչյուր consumer ստանա իր պատճենը և աշխատի իր տեմպով։
- Default exchange՝ անանուն direct exchange, որը ուղղորդում է այն հերթին, որի անունը համընկնում է routing key-ի հետ։
- Չուղղորդվող հաղորդագրությունները կորչում են, եթե չեք հրապարակում mandatory դրոշով կամ չեք կարգավորել alternate exchange։
03Հերթերի տեսակները՝ quorum, classic և streams
Quorum queue-ները տվյալները ռեպլիկացնում են կենտ թվով node-երի վրա՝ օգտագործելով Raft կոնսենսուսի ալգորիթմը․ հաղորդագրությունը հաստատվում է միայն այն բանից հետո, երբ մեծամասնությունն այն ունի, ուստի այն վերապրում է node-ի խափանումը։ Դրանք առաջարկվող ընտրությունն են ամեն կարևոր բանի համար։ Classic queue-ները մեկ node-ի վրա են և չեն ռեպլիկացվում, քանի որ RabbitMQ 4.0-ը հեռացրեց classic mirrored queue-ները․ դրանք լավ են ժամանակավոր կամ վերարտադրելի աշխատանքի համար։ Streams-ը append-only log է․ consumer-ները կարդում են offset-ից, և հաղորդագրությունները սպառելիս չեն ջնջվում, ինչը հնարավոր է դարձնում replay և շատ մեծ fan-out՝ Kafka-ի topic-ների նման։ Quorum queue-ները աջակցում են նաև delivery limit, որը պաշտպանում է poison message-ներից, որոնք կրկին ու կրկին կործանում են consumer-ներին։
04Հուսալի հրապարակում
Durable հերթերը և persistent հաղորդագրությունները վերապրում են broker-ի վերագործարկումը, բայց producer-ին չեն ասում, որ broker-ը ստացել է հաղորդագրությունը։ Դրա համար օգտագործեք publisher confirms․ broker-ը հաստատում է յուրաքանչյուր հաղորդագրություն, երբ այն ապահով պահված է (quorum queue-ների դեպքում՝ ռեպլիկացված մեծամասնությանը)։ Եթե confirm-ը չի գալիս կամ վերադառնում է որպես nack, նորից հրապարակեք։ Յուրաքանչյուր հաղորդագրության տվեք եզակի messageId, որպեսզի consumer-ները կարողանան հայտնաբերել կրկնօրինակները, քանի որ կրկնումները առաքումը դարձնում են at-least-once։ Exchange-ներն ու հերթերը հայտարարեք կոդում կամ definitions ֆայլում, որպեսզի միջավայրերը վերարտադրելի լինեն։
import amqp from "amqplib";
const conn = await amqp.connect("amqp://your_user:your_password@localhost:5672");
const ch = await conn.createConfirmChannel();
await ch.assertExchange("orders", "topic", { durable: true });
await ch.assertQueue("email.order-created", {
durable: true, arguments: { "x-queue-type": "quorum" },
});
await ch.bindQueue("email.order-created", "orders", "order.created");
ch.publish("orders", "order.created", Buffer.from(JSON.stringify({ orderId: 42 })), {
persistent: true, contentType: "application/json", messageId: "order-42-created",
});
await ch.waitForConfirms(); // throws if the broker rejected the message
05Սպառում՝ ack-եր, prefetch և կրկնումներ
Consumer-ները պետք է օգտագործեն ձեռքով acknowledgement․ ack արեք միայն այն բանից հետո, երբ աշխատանքը հաջողվել է։ Եթե consumer-ը կործանվում է մինչև ack-ը, RabbitMQ-ն հաղորդագրությունը նորից առաքում է մեկ այլ consumer-ի։ Prefetch-ը (basic.qos) սահմանափակում է, թե քանի չհաստատված հաղորդագրություն կարող է պահել մեկ consumer-ը․ առանց դրա broker-ը ամեն ինչ մղում է առաջին consumer-ին, իսկ մյուսները պարապ են մնում։ Սկսեք 10–50 prefetch-ից և կարգավորեք։ Ձախողման դեպքում որոշեք․ requeue արեք անցողիկ սխալի դեպքում, կամ reject արեք առանց requeue-ի, որպեսզի հաղորդագրությունը գնա dead letter exchange (DLX)՝ ուսումնասիրության կամ ուշացված կրկնման համար։ Երբեք ack մի արեք մինչև մշակումը և handler-ները դարձրեք idempotent։
await ch.prefetch(20);
await ch.consume("email.order-created", async (msg) => {
if (!msg) return;
try {
const event = JSON.parse(msg.content.toString());
await sendOrderEmail(event.orderId); // must be idempotent
ch.ack(msg);
} catch (err) {
// false, false = do not requeue: route to the dead letter exchange
ch.nack(msg, false, false);
}
}, { noAck: false });
06Dead letter-ներ, policy-ներ և շահագործում
Dead letter exchange-ը ստանում է այն հաղորդագրությունները, որոնք մերժվել են, TTL-ով ժամկետանց են դարձել կամ գերազանցել են delivery limit-ը։ Կարգավորեք այն policy-ով, ոչ թե կոդում կոշտ գրված queue arguments-ով, քանի որ policy-ները կարելի է փոխել առանց հերթերը վերահայտարարելու։ Կրկնման դասական pattern-ը ձախողված հաղորդագրություններն ուղղորդում է TTL-ով retry հերթ, որի սեփական DLX-ը ցույց է տալիս հետ դեպի աշխատանքային exchange-ը՝ տալով ուշացված կրկնումներ։ Quorum queue-ների համար աշխատեցրեք RabbitMQ-ն որպես երեք node-անոց cluster, միացրեք management plugin-ը UI-ի և HTTP API-ի համար, մետրիկաները բացեք Prometheus plugin-ով և օգտագործեք TLS ու յուրաքանչյուր հավելվածի համար առանձին user՝ vhost-ի սահմանափակ թույլտվություններով։ Ջնջեք լռելյայն guest user-ը, որը, ամեն դեպքում, աշխատում է միայն localhost-ից։
# Dead-letter failed messages and cap redeliveries for all email.* queues
rabbitmqctl set_policy email-dlx "^email\." \
'{"dead-letter-exchange":"dlx","delivery-limit":5}' --apply-to quorum_queues
rabbitmqctl add_vhost shop
rabbitmqctl add_user orders_app 'use-a-strong-password'
rabbitmqctl set_permissions -p shop orders_app '^orders.*' '^orders.*' '^(orders|email).*'
Օգտագործեք, երբ
- Ֆոնային job-եր և առաջադրանքների բաշխում տարբեր լեզուներով գրված worker-ների միջև։
- Իրադարձությունների ծանուցում, երբ մեկ իրադարձությունը պետք է ճկուն ուղղորդմամբ հասնի մի քանի անկախ consumer-ի։
- Տրաֆիկի գագաթների հարթեցում, որպեսզի դանդաղ downstream համակարգերը (ERP, email, վճարային պրովայդերներ) չծանրաբեռնվեն։
- Request/reply և աշխատանքային հերթեր, որոնց պետք են յուրաքանչյուր հաղորդագրության acknowledgement, կրկնումներ և dead lettering։
Մի՛ օգտագործեք, երբ
- Իրադարձությունների երկարաժամկետ պահպանում և replay բազմաթիվ consumer-ների կողմից շատ բարձր throughput-ով․ Kafka-ն կամ RabbitMQ Streams-ն ավելի հարմար են։
- Մեկ մոնոլիտ՝ պարզ ֆոնային job-երով, որտեղ Redis-ի վրա հիմնված հերթը (BullMQ, Laravel queues) ավելի պարզ է շահագործել։
- Սինխրոն հարցումների մշակում, երբ կանչողին անմիջական պատասխան է պետք․ օգտագործեք ուղիղ API կանչ։
Այլընտրանքներ
- Redis-ի վրա հիմնված հերթեր (BullMQ, Laravel queues)
Շատ պարզ են մեկ հավելվածի stack-ի համար․ ավելի քիչ ուղղորդման հնարավորություններ և ավելի թույլ durability երաշխիքներ։
- Apache Kafka
Բաշխված log բարձր throughput-ով event streaming-ի համար՝ retention-ով և replay-ով․ ավելի ծանր է շահագործել։
- Amazon SQS / SNS
Լիովին կառավարվող հերթեր և fan-out AWS-ում․ առանց սերվերների, at-least-once առաքում, սահմանափակ ուղղորդում։
- NATS (JetStream)
Թեթև, շատ արագ messaging՝ ըստ ցանկության persistence-ով․ շատ դեպքերում ավելի պարզ, քան Kafka-ն։
Լավագույն պրակտիկաներ
- Կարևոր աշխատանքի համար օգտագործեք quorum queue-ներ և հրապարակեք publisher confirms-ով։
- Օգտագործեք ձեռքով ack, ack արեք միայն հաջողությունից հետո և յուրաքանչյուր consumer-ի համար սահմանեք prefetch limit։
- Consumer-ները դարձրեք idempotent եզակի messageId-ով, քանի որ առաքումը at-least-once է։
- Dead letter exchange-ները և delivery limit-ները կարգավորեք policy-ներով և հետևեք dead letter հերթերին։
- Մեկ պրոցեսում օգտագործեք մեկ երկարակյաց connection՝ առաջադրանքների համար channel-ներով․ մի բացեք connection յուրաքանչյուր հաղորդագրության համար։
- Հաղորդագրությունները պահեք փոքր (ID-ներ և էականը), իսկ մեծ payload-ները պահեք այլ տեղում։
- Հետևեք հերթի խորությանը, consumer-ների քանակին, չհաստատված հաղորդագրություններին և memory կամ disk alarm-ներին Prometheus-ով և Grafana-ով։
Տարածված սխալներ
- Auto-ack-ի օգտագործում, որի պատճառով կործանվող consumer-ը լուռ կորցնում է հաղորդագրությունները։
- Prefetch limit-ի բացակայություն, որի դեպքում մեկ consumer-ը կուտակում է հազարավոր հաղորդագրություններ, իսկ մյուսները պարապ են։
- Ձախողված հաղորդագրությունների անվերջ requeue, որը poison message-ի համար ստեղծում է տաք ցիկլ։
- Հերթերին թույլ տալ աճել մինչև միլիոնավոր հաղորդագրություններ առանց alert-ի, մինչև broker-ը հասնի memory կամ disk alarm-ի և արգելափակի publisher-ներին։
- Յուրաքանչյուր publish-ի համար նոր connection բացելը, ինչը բեռի տակ սպառում է ռեսուրսները։
Խորը վերլուծություն
07Quorum queue-ները ներսից
Quorum queue-ն Raft խումբ է․ մեկ leader և follower-ներ այլ node-երի վրա, սովորաբար երեք կամ հինգ անդամ։ Publish-ները գնում են leader-ին, որն ավելացնում է դրանք իր log-ին և ռեպլիկացնում․ երբ մեծամասնությունը գրառումը գրել է սկավառակին, leader-ը հաստատում է այն publisher-ին։ Եթե leader-ի node-ը խափանվում է, մնացած մեծամասնությունը մի քանի վայրկյանում ընտրում է նոր leader, և consumer-ները շարունակում են։ Քանի որ յուրաքանչյուր հաղորդագրություն ռեպլիկացվում և fsync է արվում, quorum queue-ները ավելի շատ disk I/O են օգտագործում և լավագույնն են այն հերթերի համար, որոնք պարբերաբար դատարկվում են, ոչ թե օրերով կայանված միլիոնավոր հաղորդագրությունների համար։ Դրանք հետևում են վերաառաքումների քանակին, կիրառում են delivery limit (4.0-ից ի վեր կա լռելյայն արժեք) և 4.0-ից աջակցում են հաղորդագրությունների առաջնահերթություններ։ Շատ երկար հերթերը պետք է դառնան streams։
- Հերթերի մեծ մասի համար օգտագործեք երեք անդամ․ հինգը հանդուրժում են երկու խափանում՝ ավելի մեծ գնով։
- Անդամները տեղաբաշխվում են cluster-ի node-երի վրա․ node-երը պահեք առանձին availability zone-երում։
08Flow control, alarm-ներ և back-pressure
RabbitMQ-ն պաշտպանում է ինքն իրեն։ Երբ node-ի memory-ի օգտագործումը անցնում է high watermark-ը (լռելյայն՝ RAM-ի մի մասը) կամ ազատ սկավառակը իջնում է disk limit-ից ցածր, այն alarm է բարձրացնում և ամբողջ cluster-ում արգելափակում է հրապարակող բոլոր connection-ները, մինչև ռեսուրսները վերականգնվեն։ Consumer-ները շարունակում են աշխատել, և այդպես համակարգը դատարկվում է։ Credit-based flow control-ը նաև դանդաղեցնում է արագ publisher-ներին, որոնք առաջ են անցնում հերթի պրոցեսներից։ Գործնականում alarm-ները ախտանիշ են այն բանի, որ հերթը աճում է ավելի արագ, քան սպառվում է։ Անսահմանափակ աճող հերթերի վրա սահմանեք max-length կամ TTL policy-ներ, alert դրեք հերթի խորության աճի տեմպի վրա, մասշտաբավորեք consumer-ները և broker-ին տվեք արագ սկավառակներ՝ պաշարով։ Client-ները պետք է մշակեն connection.blocked ծանուցումը և դադարեցնեն կամ բուֆերացնեն հրապարակումը։
09Կրկնումներ backoff-ով և poison message-ներ
Անմիջական requeue-ը կրկնում է չափազանց արագ և կարող է poison message-ը ցիկլի մեջ գցել։ Հուսալի pattern-ը ուշացումների համար օգտագործում է dead lettering․ ձախողման դեպքում consumer-ը մերժում է հաղորդագրությունը, որը DLX-ի միջոցով գնում է retry հերթ՝ message TTL-ով (օրինակ 30 վայրկյան) և առանց consumer-ների․ երբ TTL-ը լրանում է, այդ հերթը այն dead-letter է անում հետ դեպի հիմնական exchange-ը։ Էքսպոնենցիալ քայլերի համար (30 վ, 5 րոպե, 30 րոպե) օգտագործեք մի քանի retry հերթ և փորձերը հաշվեք header-ում կամ հենվեք quorum queue-ների x-delivery-count header-ի վրա։ Վերջին փորձից հետո ուղղորդեք parking հերթ, որը մարդն է ուսումնասիրում։ Կա նաև delayed message exchange plugin, բայց այն չի ռեպլիկացվում, ուստի կրիտիկական աշխատանքի համար TTL հերթերն ավելի անվտանգ են։
# Retry queue: holds failed messages for 30 s, then sends them back
rabbitmqctl set_policy retry-30s "^retry\.30s$" \
'{"message-ttl":30000,"dead-letter-exchange":"orders"}' --apply-to queues
# Main work queues dead-letter into the retry exchange
rabbitmqctl set_policy work-dlx "^email\." \
'{"dead-letter-exchange":"retry","delivery-limit":10}' --apply-to quorum_queues
10Exactly-once-ը առասպել է՝ idempotency և outbox
Ցանցերը ձախողվում են «մշակված»-ի և «ack արված»-ի միջև, ինչպես նաև «հրապարակված»-ի և «հաստատված»-ի միջև, ուստի ցանկացած հուսալի համակարգ at-least-once է։ Երկու pattern դա դարձնում է անվտանգ։ Transactional outbox-ը producer-ի կողմում․ բիզնես փոփոխությունը և outbox տողը գրեք նույն database transaction-ում, և թող relay-ը outbox տողերը հրապարակի confirms-ով ու նշի որպես ուղարկված։ Սա կանխում է «պատվերը պահվեց, բայց իրադարձությունը կորավ» և «իրադարձությունն ուղարկվեց, բայց պատվերը rollback եղավ» իրավիճակները։ Idempotent consumer-ը ստացող կողմում․ մշակված message ID-ները պահեք (կամ օգտագործեք բնական key-եր և upsert-ներ) նույն transaction-ում, ինչ side effect-ը, և բաց թողեք կրկնօրինակները։ Երկուսի դեպքում համակարգը վարվում է այնպես, կարծես առաքումը exactly once լիներ։
Իրական իրավիճակներ
Հերթը շարունակում է աճել ակցիայի ժամանակ
Խնդիր
Ակցիայի ժամանակ order-email հերթը աճում է մինչև 400,000 հաղորդագրություն, email-ները հասնում են ժամերով ուշ, և broker-ը բարձրացնում է memory alarm, որը արգելափակում է publisher-ներին, ներառյալ checkout-ը։
Ինչպես լուծել
Կարճաժամկետ՝ մասշտաբավորեք email consumer-ները և ստուգեք դրանց prefetch-ը․ prefetch 1-ի և դանդաղ SMTP կանչի դեպքում բարձրացրեք այն մինչև 20–50 և ուղարկեք զուգահեռ՝ պրովայդերի սահմաններում։ Management UI-ում ստուգեք consumer utilisation մետրիկան։ Առանձնացրեք կրիտիկական և ոչ կրիտիկական տրաֆիկը․ checkout իրադարձությունները չպետք է կիսեն vhost կամ node-ի ճնշումը զանգվածային email-ների հետ, ուստի email-ը տեղափոխեք իր սեփական հերթ՝ max-length կամ TTL policy-ով։ Երկարաժամկետ՝ alert դրեք հերթի խորության աճի վրա, կամպանիաներից առաջ load test արեք pipeline-ը և checkout publisher-ում մշակեք connection.blocked-ը տեղային outbox-ով, որպեսզի broker-ի alarm-ը երբեք չձախողի վճարումը։
Poison message-ը կործանում է բոլոր consumer-ներին
Խնդիր
Մեկ սխալ ձևաչափված հաղորդագրություն ստիպում է consumer-ին նետել չմշակված սխալ։ Այն requeue է արվում, վերցվում է հաջորդ consumer-ի կողմից, որը նույնպես կործանվում է, և հերթը կանգ է առնում։
Ինչպես լուծել
Սխալները բռնեք յուրաքանչյուր հաղորդագրության մակարդակում և nack արեք առանց requeue այն սխալների դեպքում, որոնք երբեք չեն հաջողվի, օրինակ՝ validation-ի ձախողումները, որպեսզի հաղորդագրությունը գնա dead letter հերթ։ Quorum queue-ի վրա սահմանեք delivery-limit policy (օրինակ 5), որպեսզի նույնիսկ մինչև nack-ը տեղի ունեցող կործանումները դադարեն մի քանի փորձից հետո։ Ավելացրեք parking հերթ և alert դրա խորության վրա։ Գրանցեք messageId-ն և սխալը, շտկեք producer-ը կամ parser-ը և շտկումից հետո replay արեք parked հաղորդագրությունները shovel-ով կամ փոքր script-ով։ Ավելացրեք contract test, որը ստուգում է հաղորդագրության schema-ն թե՛ producer-ի, թե՛ consumer-ի կողմում։
Միգրացիա classic mirrored queue-ներից
Խնդիր
Թիմը աշխատեցնում է հին RabbitMQ 3.x cluster՝ classic mirrored queue-ներով, և ուզում է թարմացնել 4.x-ի, որտեղ mirroring-ն այլևս գոյություն չունի։
Ինչպես լուծել
Նախ գույքագրեք ha-mode սահմանող հերթերն ու policy-ները։ Թարմացրեք 3.13-ի շրջանակում մինչև վերջին patch-ը, միացրեք բոլոր կայուն feature flag-երը, ապա կարևոր հերթերը տեղափոխեք quorum queue-ների։ Քանի որ հերթի տեսակը տեղում փոխել հնարավոր չէ, օգտագործեք blue-green մոտեցում․ հայտարարեք նոր quorum queue-ներ (կամ նոր vhost), publisher-ներին ուղղեք նոր exchange binding-ների վրա, թող consumer-ները դատարկեն հին հերթերը, ապա հեռացրեք դրանք։ Ստուգեք client-ի հնարավորությունները․ global QoS-ը և որոշ transient կամ exclusive կարգավորումներ այլ կերպ են վարվում։ Հեռացրեք ha-* policy-ները, ապա թարմացրեք 4.x-ի և ստուգեք, որ quorum queue-ների leader-ները հավասարաչափ բաշխված են node-երի միջև։
Արագ ուղեցույց
- Աշխատեցնել տեղում management UI-ով
docker run -d -p 5672:5672 -p 15672:15672 rabbitmq:4-management- Հերթերի ցանկ՝ խորությամբ և consumer-ներով
rabbitmqctl list_queues name type messages consumers- Հայտարարել quorum queue (client argument)
{ "x-queue-type": "quorum" }- Սահմանել prefetch (Node.js amqplib)
await channel.prefetch(20);- Մերժել դեպի dead letter
channel.nack(msg, false, false);- Cluster-ի առողջության ստուգում
rabbitmq-diagnostics check_running && rabbitmq-diagnostics check_local_alarms- Արտահանել definitions
rabbitmqctl export_definitions /tmp/definitions.json- Միացնել Prometheus մետրիկաները
rabbitmq-plugins enable rabbitmq_prometheus # scrape :15692/metrics- Ջնջել լռելյայն guest user-ը
rabbitmqctl delete_user guest
Հարցեր և պատասխաններ
Q1Ո՞րն է տարբերությունը exchange-ի և queue-ի միջև։
Queue-ն պահում է հաղորդագրությունները, մինչև consumer-ը դրանք վերցնի․ դա փոստարկղն է։ Exchange-ը ոչինչ չի պահում․ դա տեսակավորման գրասենյակն է, որը ստանում է հաղորդագրությունները producer-ներից և, ելնելով իր տեսակից և binding-ներից, որոշում, թե որ հերթերը պատճեն կստանան։ Direct exchange-ները ճշգրիտ համեմատում են routing key-ը, topic exchange-ները համեմատում են pattern-ներ, fanout-ը հեռարձակում է բոլոր կապված հերթերին, իսկ headers exchange-ները համեմատում են header-ների արժեքները։ Հենց այս բաժանումն է RabbitMQ-ն դարձնում ճկուն․ producer-ները հրապարակում են exchange-ում և երբեք կարիք չունեն իմանալու, թե քանի հերթ կամ consumer կա, ուստի նոր consumer կարող եք ավելացնել՝ պարզապես նոր հերթ կապելով։
Q2Ինչպե՞ս համոզվել, որ հաղորդագրությունները չեն կորչում։
Մի քանի շերտ պետք է միաժամանակ տեղում լինի։ Broker-ի վրա օգտագործեք durable quorum queue-ներ, որոնք ռեպլիկացվում են node-երի մեծամասնությանը, և persistent հաղորդագրություններ։ Producer-ի կողմում միացրեք publisher confirms և նորից հրապարակեք այն ամենը, ինչը չի հաստատվել։ Օգտագործեք mandatory հրապարակում կամ alternate exchange, որպեսզի չուղղորդվող հաղորդագրությունները լուռ չկորչեն։ Consumer-ի կողմում օգտագործեք ձեռքով acknowledgement և ack արեք միայն աշխատանքն ավարտելուց հետո, որպեսզի կործանումը բերի վերաառաքման, ոչ թե կորստի։ Վերջապես, հետևեք dead letter հերթերին։ Արդյունքը at-least-once առաքումն է, ինչը նշանակում է, որ consumer-ները պետք է idempotent լինեն՝ պատահական կրկնօրինակները մշակելու համար։
Q3Ի՞նչ է prefetch-ը և ինչպե՞ս այն սահմանել։
Prefetch-ը, որը սահմանվում է basic.qos-ով, չհաստատված հաղորդագրությունների առավելագույն քանակն է, որը broker-ը կմղի մեկ consumer-ի կամ channel-ի։ Առանց սահմանափակման broker-ը հնարավորինս շատ հաղորդագրություն է ուղարկում միացված ցանկացած consumer-ի, ուստի մեկ worker-ը կարող է պահել հազարավորներ, մինչ մյուսը պարապ է, իսկ կործանման դեպքում բոլորը միանգամից վերադառնում են։ Ցածր prefetch-ը, օրինակ 1, արդար բաշխում է տալիս դանդաղ, ծանր առաջադրանքների համար․ ավելի բարձր արժեքը, օրինակ 20-ից 100, մեծացնում է throughput-ը արագ առաջադրանքների համար՝ թաքցնելով ցանցի ուշացումը։ Սկսեք 10-ից 50-ի շուրջ, չափեք throughput-ը և consumer-ների ծանրաբեռնվածությունը և կարգավորեք։
Q4RabbitMQ, թե՞ Kafka։
Դրանք լուծում են տարբեր խնդիրներ։ RabbitMQ-ն broker է, որը կենտրոնացած է ուղղորդման և յուրաքանչյուր հաղորդագրության առաքման վրա․ հաղորդագրությունները գնում են հերթեր, consumer-ները հաստատում են դրանք, և դրանք ջնջվում են՝ ճկուն ուղղորդմամբ, առաջնահերթություններով, TTL-ներով և dead lettering-ով։ Kafka-ն բաշխված append-only log է․ հաղորդագրությունները պահվում են օրեր կամ ընդմիշտ, consumer-ները հետևում են offset-ներին և կարող են replay անել, իսկ partitioning-ը տալիս է շատ բարձր throughput՝ յուրաքանչյուր key-ի համար կարգով։ Ընտրեք RabbitMQ-ն task queue-ների, workflow-ների և բարդ ուղղորդմամբ ինտեգրացիաների համար։ Ընտրեք Kafka-ն event streaming-ի, analytics pipeline-ների և event sourcing-ի համար, որտեղ replay-ը կարևոր է։ RabbitMQ Streams-ը նեղացնում է այս տարբերությունը replay-ի չափավոր կարիքների համար։
Q5Ի՞նչ է dead letter exchange-ը։
Dead letter exchange-ը սովորական exchange է, որը ստանում է այն հաղորդագրությունները, որոնք RabbitMQ-ն չկարողացավ մշակել իրենց սկզբնական հերթում․ consumer-ի կողմից առանց requeue մերժված հաղորդագրություններ, հաղորդագրություններ, որոնց TTL-ը լրացել է, հաղորդագրություններ, որոնք դուրս են մնացել, քանի որ հերթը գերազանցել է երկարության սահմանը, իսկ quorum queue-ների դեպքում՝ նաև delivery limit-ը գերազանցած հաղորդագրություններ։ Դրան կապում եք dead letter հերթ, alert եք դնում դրա խորության վրա և bug-ը շտկելուց հետո ուսումնասիրում կամ replay եք անում հաղորդագրությունները։ TTL հերթի հետ համատեղ այն իրականացնում է նաև ուշացված կրկնումներ։ Դա այն անվտանգության ցանցն է, որը poison message-ը անվերջ ցիկլից դարձնում է տեսանելի, վերականգնելի խնդիր։
Q6Արդյո՞ք RabbitMQ-ն երաշխավորում է հաղորդագրությունների կարգը։
Մեկ հերթի ներսում հաղորդագրություններն առաքվում են այն կարգով, ինչով հրապարակվել են, քանի դեռ կա մեկ consumer և requeue-ներ չկան։ Մի քանի consumer-ի դեպքում հաղորդագրությունները մշակվում են զուգահեռ, ուստի ավարտի կարգը երաշխավորված չէ, իսկ requeue արված կամ վերաառաքված հաղորդագրությունը կարող է վերադառնալ ավելի ուշ եկածներից հետո։ Եթե խիստ կարգը կարևոր է յուրաքանչյուր entity-ի համար, օրինակ մեկ պատվերի բոլոր իրադարձությունները, ուղղորդեք ըստ key-ի դեպի մեկ հերթ յուրաքանչյուր partition-ի համար consistent hash exchange-ով կամ հերթի վրա օգտագործեք single active consumer։ Հաճախ ավելի լավ պատասխանն է consumer-ները նախագծել այնպես, որ հանդուրժեն կարգի խախտումը, օրինակ՝ տարբերակների համարներով։
Սենիոր մակարդակի հարցեր
S1Ինչպե՞ս կնախագծեիք event-driven ճարտարապետություն RabbitMQ-ի վրա մի քանի ծառայության համար։
Ես կստեղծեի մեկ durable topic exchange յուրաքանչյուր bounded context-ի համար (orders, payments, users), որը պատկանում է հրապարակող ծառայությանը, order.created.v1-ի նման routing key-երով, որոնք ներառում են տարբերակը։ Յուրաքանչյուր սպառող ծառայություն յուրաքանչյուր նպատակի համար հայտարարում է իր սեփական quorum queue-ն և կապում իրեն անհրաժեշտ key-երը, ուստի ծառայությունները զարգանում են անկախ։ Producer-ները հրապարակում են transactional outbox-ի միջոցով confirms-ով․ consumer-ները idempotent են և օգտագործում են ձեռքով ack-եր, prefetch, DLX՝ ուշացված կրկնումներով և parking հերթ։ Հաղորդագրությունների schema-ները գտնվում են ընդհանուր contract repository-ում՝ համատեղելիության թեստերով։ Յուրաքանչյուր ծառայություն ստանում է իր user-ը՝ թույլտվություններով, որոնք սահմանափակված են իր exchange-ներով և հերթերով։ Observability-ի համար կավելացնեի tracing header-ներ։ Kafka-ի համեմատ փոխզիջումն այն է, որ չկա երկար retention կամ replay, ուստի եթե consumer-ներին պատմություն է պետք, կավելացնեի Streams կամ առանձին log։
S2Ինչպե՞ս երաշխավորել կարգը յուրաքանչյուր entity-ի համար՝ consumer-ները մասշտաբավորելիս։
Մեկ հերթը շատ consumer-ներով մշակում է զուգահեռ, ուստի կարգը խախտվում է։ Տարբերակներ․ հերթի վրա օգտագործել single active consumer, որը պահպանում է կարգը, բայց հրաժարվում է զուգահեռությունից և աշխատում է որպես hot standby։ Մասշտաբավորելու համար partition արեք․ consistent hash exchange-ը ըստ պատվերի ID-ի hash-ի ուղղորդում է N հերթի, որոնցից յուրաքանչյուրն ունի single active consumer, ուստի յուրաքանչյուր entity միշտ ընկնում է նույն հերթը և մշակվում կարգով, իսկ throughput-ը մասշտաբավորվում է N-ով։ Streams-ը super streams-ով տալիս է նմանատիպ partitioned մոդել։ Վերաառաքումները դեռ կարող են խախտել կարգը partition-ի ներսում, եթե requeue եք անում, ուստի նախընտրեք կրկնման pattern-ներ, որոնք կարճ ժամանակով արգելափակում են այդ partition-ը, կամ consumer-ները նախագծեք տարբերակների ստուգումներով, որպեսզի հնացած իրադարձությունն անտեսվի։ Կարգը միշտ արժենում է throughput կամ բարդություն։
S3Ե՞րբ կընտրեիք RabbitMQ Streams-ը quorum queue-ների փոխարեն։
Quorum queue-ները աշխատանքի բաշխման համար են․ յուրաքանչյուր հաղորդագրություն մշակվում է մեկ consumer-ի կողմից և ջնջվում ack-ից հետո՝ կրկնումներով և dead lettering-ով։ Streams-ը append-only ռեպլիկացվող log է․ հաղորդագրությունները մնում են, մինչև retention-ը դրանք հեռացնի, consumer-ները կարդում են ցանկացած offset-ից, և շատ consumer-ներ կարող են կարդալ նույն տվյալները՝ առանց յուրաքանչյուր հերթի համար պատճենների։ Ընտրեք streams մեծ fan-out-ի համար (նույն իրադարձությունների հարյուրավոր consumer-ներ), bug-ից հետո կամ նոր consumer-ի համար replay-ի, շատ երկար backlog-ների կամ binary stream protocol-ով բարձր throughput-ի համար։ Մնացեք quorum queue-ների վրա, երբ ձեզ պետք են յուրաքանչյուր հաղորդագրության ack-եր, delivery limit-ներ, առաջնահերթություններ և DLX workflow-ներ։ Streams-ը չի աջակցում dead lettering, և consumer-ները պետք է իրենք հետևեն իրենց offset-ներին, ինչը պատասխանատվությունը տեղափոխում է հավելվածի վրա։
Բառարան
- Broker
- Սերվեր, որը ստանում, պահում և առաքում է հաղորդագրություններ հավելվածների միջև։
- Exchange
- Ուղղորդման բաղադրիչ, որը ստանում է հրապարակված հաղորդագրությունները և պատճենում դրանք կապված հերթերին։
- Binding
- Կանոն, որը կապում է exchange-ը հերթի հետ, հաճախ routing key-ով կամ pattern-ով։
- Routing key
- Հաղորդագրության պիտակ, որով exchange-ները որոշում են, թե ուր այն ուղարկել։
- Quorum queue
- Raft-ի վրա հիմնված ռեպլիկացվող հերթի տեսակ, որը վերապրում է node-երի խափանումները։
- Acknowledgement
- Consumer-ի ազդանշանը, որ հաղորդագրությունը մշակվել է և կարող է ջնջվել։
- Prefetch
- Չհաստատված հաղորդագրությունների առավելագույն քանակը, որը միաժամանակ առաքվում է մեկ consumer-ի։
- Dead letter exchange
- Exchange, որը ստանում է մերժված, ժամկետանց կամ չափից շատ կրկնված հաղորդագրությունները։
- Publisher confirm
- Broker-ի հաստատումը producer-ին, որ հաղորդագրությունը ապահով պահվել է։
- Vhost
- Վիրտուալ host՝ exchange-ների, հերթերի և թույլտվությունների մեկուսացված namespace։
Շարունակեք սովորել
Պրոդուկտի գաղափար ունե՞ք։Եկեք ստեղծենք այն։
Պատմեք ձեր գաղափարի, պրոդուկտի կամ թիմի մասին։ Սովորաբար պատասխանում եմ մեկ աշխատանքային օրվա ընթացքում։