स्वीकृतियाँ, डेड-लेटर कतारें और पुनःप्रयास
मैन्युअल स्वीकृतियों से डिलीवरी सुनिश्चित कीजिए, दूषित संदेश संभालिए और पुनःप्रयास व डेड-लेटर प्रवाह लागू कीजिए।
स्वीकृतियाँ, डेड-लेटर कतारें और पुनःप्रयास, CoddyKit पर Node.js बैकएंड विकास बूटकैंप का एक निःशुल्क पाठ है। यह 4 में से 3वाँ पाठ है। आप नीचे पूरा पाठ निःशुल्क पढ़ सकते हैं—फिर अंतर्निहित कोड संपादक और 24/7 एआई ट्यूटर के साथ ब्राउज़र में इसका व्यावहारिक अभ्यास कर सकते हैं। यह Node.js बैकएंड विकास बूटकैंप सीखने के मार्ग का हिस्सा है और आपकी प्रगति वेब तथा CoddyKit ऐप पर सिंक होती रहती है। Node.js बैकएंड विकास बूटकैंप पाठ्यक्रम में कुल 4 पाठ शामिल हैं।
Acknowledgements क्यों महत्वपूर्ण हैं
डिफ़ॉल्ट रूप से, जब कोई consumer RabbitMQ से message प्राप्त करता है, तो उसे broker को बताना होता है कि message सफलतापूर्वक process हुआ या नहीं। इस संकेत को acknowledgement (ack) कहा जाता है।
यदि आप auto-ack सक्षम करते हैं, तो RabbitMQ message deliver होते ही उसे हटा देता है। यदि आपका worker process के बीच में crash हो जाए, तो message हमेशा के लिए खो जाता है। manual acks के साथ broker message को तब तक रखता है जब तक आप उसकी पुष्टि नहीं करते, और consumer के बंद हो जाने पर उसे फिर से deliver करता है।
- auto-ack: तेज़, लेकिन अधिकतम-एक-बार delivery (messages खो सकते हैं)
- manual ack: सुरक्षित, कम-से-कम-एक-बार delivery देता है
किसी भी ऐसे job के लिए जिसमें वास्तविक काम होता है, जैसे card से शुल्क लेना, email भेजना या DB में लिखना, आप लगभग हमेशा manual acks का उपयोग करना चाहेंगे।
Manual Acks के साथ Consuming
amqplib library का उपयोग करते हुए, manual acknowledgement सक्षम करने के लिए आप { noAck: false } को channel.consume में भेजते हैं। आपका handler सफलतापूर्वक पूरा होने के बाद आप channel.ack(msg) call करते हैं।
जब तक आप ack नहीं करते, RabbitMQ message को unacked मानता है और channel या connection बंद होने पर उसे फिर से deliver करेगा।
const amqp = require('amqplib');
async function start() {
const conn = await amqp.connect('amqp://localhost');
const channel = await conn.createChannel();
const queue = 'orders';
await channel.assertQueue(queue, { durable: true });
channel.consume(queue, async (msg) => {
if (msg === null) return;
const order = JSON.parse(msg.content.toString());
console.log('Processing order', order.id);
// ... do real work here ...
channel.ack(msg); // confirm success
}, { noAck: false });
}
start();ack, nack और reject
RabbitMQ आपको deliver किए गए message का उत्तर देने के तीन तरीके देता है:
channel.ack(msg)— सफलता, message हटाएँchannel.nack(msg, false, requeue)— विफलता; यदिrequeue=trueहै, तो message queue में वापस जाता है; यदिfalseहै, तो उसे हटा दिया जाता है या dead-letter कर दिया जाता हैchannel.reject(msg, requeue)— nack जैसा, लेकिन केवल एक message के लिए
nack का दूसरा argument allUpTo है। केवल वर्तमान message पर कार्य करने के लिए इसे false रखें; true रखने पर इस message तक के सभी unacked messages पर nack लागू होगा।
मुख्य निर्णय: requeue=true तुरंत retry करता है और poison messages के लिए अनंत hot-loops पैदा कर सकता है। इसके बजाय requeue=false और dead-letter strategy को प्राथमिकता दें।
// Failure with no requeue -> message is dropped or dead-lettered
channel.nack(msg, false, false);
// Failure with requeue -> message returns to the front of the queue
channel.nack(msg, false, true);
// reject only ever affects this one message
channel.reject(msg, false);Prefetch: In-Flight Work को नियंत्रित करना
सीमाओं के बिना, RabbitMQ किसी उपभोक्ता को जितने संदेश भेज सकता है, उतने भेज देता है और वे सभी मेमोरी में बिना ack के पड़े रहते हैं। इससे धीमा कार्यकर्ता अत्यधिक दबाव में आ सकता है।
channel.prefetch(n) उन बिना ack वाले संदेशों की अधिकतम संख्या तय करता है, जिन्हें कोई उपभोक्ता एक समय में अपने पास रख सकता है। मैन्युअल ack के साथ, prefetch(1) वाला कार्यकर्ता एक संदेश संसाधित करता है, उसे ack करता है, फिर अगला संदेश प्राप्त करता है — इससे निष्पक्ष और बैक-प्रेशर वाला वितरण होता है।
const channel = await conn.createChannel();
await channel.assertQueue('orders', { durable: true });
// Only one unacked message at a time per consumer
await channel.prefetch(1);
channel.consume('orders', async (msg) => {
await handle(msg);
channel.ack(msg);
}, { noAck: false });पॉइज़न संदेश क्या है
पॉइज़न संदेश वह संदेश है जिसका संसाधन हमेशा विफल होता है — जैसे खराब JSON, हटाए गए रिकॉर्ड का संदर्भ या ऐसा सत्यापन त्रुटि जिसे ठीक नहीं किया जा सकता। यदि आप उसे सीधे nack(msg, false, true) करते हैं, तो संदेश फिर से कतार में चला जाता है, दोबारा भेजा जाता है, फिर विफल होता है और हमेशा इसी चक्र में घूमता रहता है, जिससे आपका CPU व्यस्त रहता है।
आपको कुछ प्रयासों के बाद हार मानने और संदेश को निरीक्षण के लिए किसी सुरक्षित स्थान पर भेजने का तरीका चाहिए। वह स्थान डेड-लेटर कतार (DLQ) है।
- N प्रयासों के बाद पुनः प्रयास करना बंद करें
- संदेश को मुख्य प्रवाह से बाहर ले जाएँ
- इसे डीबग करने या मैन्युअल रूप से फिर से चलाने के लिए रखें
डेड-लेटर एक्सचेंज
RabbitMQ किसी संदेश को डेड-लेटर में भेजे जाने पर उसे स्वचालित रूप से किसी दूसरे एक्सचेंज में भेज सकता है। किसी संदेश को डेड-लेटर में तब भेजा जाता है जब:
- उसे
requeue=falseके साथ nack या अस्वीकार किया जाता है, या - उसका TTL समाप्त हो जाता है, या
- कतार अपनी अधिकतम लंबाई से अधिक हो जाती है
आप इसे कतार के आर्ग्युमेंट x-dead-letter-exchange (आवश्यक) और वैकल्पिक रूप से x-dead-letter-routing-key के साथ कॉन्फ़िगर करते हैं। उस एक्सचेंज से किसी कतार को bindQueue करें और असफल संदेश वहाँ पहुँच जाएँगे।
// Main queue routes failures to the 'dlx' exchange
await channel.assertExchange('dlx', 'direct', { durable: true });
await channel.assertQueue('orders.dlq', { durable: true });
await channel.bindQueue('orders.dlq', 'dlx', 'orders.dead');
await channel.assertQueue('orders', {
durable: true,
arguments: {
'x-dead-letter-exchange': 'dlx',
'x-dead-letter-routing-key': 'orders.dead'
}
});विफल संदेश को DLQ में भेजना
जब orders कतार में डेड-लेटर एक्सचेंज कॉन्फ़िगर हो जाता है, तो किसी संदेश को वहाँ भेजने के लिए बस उसे दोबारा कतार में डाले बिना nack करना होता है।
ब्रोकर आपके लिए रूटिंग संभालता है — आपको कभी भी संदेशों को मैन्युअल रूप से DLQ में प्रकाशित नहीं करना पड़ता। इससे आपके उपभोक्ता का तर्क साफ़ रहता है: संसाधित करें और ऐसी विफलता होने पर, जिसे ठीक नहीं किया जा सकता, RabbitMQ को उसे डेड-लेटर में भेजने दें।
channel.consume('orders', async (msg) => {
try {
const order = JSON.parse(msg.content.toString());
await processOrder(order);
channel.ack(msg);
} catch (err) {
console.error('Unrecoverable failure:', err.message);
// requeue=false -> RabbitMQ dead-letters to orders.dlq
channel.nack(msg, false, false);
}
}, { noAck: false });हेडर के साथ पुनः प्रयासों की गिनती
अक्सर आप हार मानने से पहले कुछ बार पुनः प्रयास करना चाहते हैं — क्षणिक DB timeout दूसरे प्रयास में सफल हो सकता है। RabbitMQ में पुनः प्रयासों की कोई अंतर्निहित गिनती नहीं होती, इसलिए आपको attempts की गिनती स्वयं रखनी होती है।
जब कोई संदेश डेड-लेटर में भेजा जाता है, तो RabbitMQ प्रत्येक डेड-लेटर घटना का विवरण देने वाला x-death हेडर array जोड़ता है, जिसमें count भी शामिल होता है। आप इसे पढ़कर तय कर सकते हैं कि पुनः प्रयास करना है या संदेश को अंतिम DLQ में भेजना है।
function deathCount(msg) {
const xDeath = msg.properties.headers && msg.properties.headers['x-death'];
if (!Array.isArray(xDeath) || xDeath.length === 0) return 0;
// sum counts across dead-letter events for this reason
return xDeath.reduce((sum, d) => sum + (d.count || 0), 0);
}
const attempts = deathCount(msg);
if (attempts >= 3) {
channel.nack(msg, false, false); // give up -> parking DLQ
} else {
// route back for another attempt
}TTL प्रतीक्षा कतार के साथ विलंबित पुनः प्रयास
तुरंत दोबारा कतार में डालने पर पुनः प्रयास तुरंत होता है, जो उन क्षणिक त्रुटियों के लिए खराब है जिन्हें ठीक होने में समय लगता है। एक सामान्य तरीका TTL वाली पुनः प्रयास कतार का उपयोग करना है, जो संदेशों को डेड-लेटर के रूप में मुख्य कतार में वापस भेजती है।
प्रवाह: मुख्य कतार में विफलता → संदेश orders.retry में जाता है, जिसमें x-message-ttl (जैसे 5s) होता है और डेड-लेटर एक्सचेंज orders की ओर वापस संकेत करता है। TTL समाप्त होने के बाद RabbitMQ इसे स्वचालित रूप से वापस भेज देता है — इससे प्रयासों के बीच अंतर्निहित विलंब मिल जाता है।
// Retry queue: holds messages for 5s, then dead-letters back to main
await channel.assertQueue('orders.retry', {
durable: true,
arguments: {
'x-message-ttl': 5000,
'x-dead-letter-exchange': '', // default exchange
'x-dead-letter-routing-key': 'orders' // back to main queue
}
});
// On a retryable failure, publish into the wait queue instead of nacking
channel.sendToQueue('orders.retry', msg.content, {
persistent: true,
headers: msg.properties.headers
});
channel.ack(msg); // ack the original; the copy is now waitingपुनः प्रयास के तर्क को एक साथ रखना
विफलता होने पर एक मजबूत उपभोक्ता यह निर्णय-प्रवाह अपनाता है:
- Parse या validate स्थायी रूप से विफल हो → तुरंत डेड-लेटर के रूप में पार्किंग DLQ में भेजें
- क्षणिक त्रुटि और attempts < max → विलंबित पुनः प्रयास के लिए पुनः प्रयास (TTL) कतार में भेजें
- क्षणिक त्रुटि, लेकिन attempts ≥ max → हार मानें और पार्किंग DLQ में भेजें
इससे कुल कार्य सीमित रहता है, लगातार तेज़ चक्रों से बचाव होता है और विफल संदेश निरीक्षण या मैन्युअल रूप से फिर चलाने के लिए सुरक्षित रहते हैं।
const MAX_RETRIES = 3;
channel.consume('orders', async (msg) => {
let order;
try {
order = JSON.parse(msg.content.toString());
} catch (e) {
return channel.nack(msg, false, false); // poison -> parking DLQ
}
try {
await processOrder(order);
channel.ack(msg);
} catch (err) {
const attempts = retryHeader(msg);
if (attempts >= MAX_RETRIES) {
channel.nack(msg, false, false); // exhausted -> parking DLQ
} else {
channel.sendToQueue('orders.retry', msg.content, {
persistent: true,
headers: { ...msg.properties.headers, 'x-retries': attempts + 1 }
});
channel.ack(msg); // ack original; retry is queued
}
}
}, { noAck: false });चलने योग्य पुनः प्रयास सिमुलेशन
RabbitMQ के व्यवहार के लिए ब्रोकर आवश्यक है, लेकिन पुनः प्रयास का निर्णय तर्क सामान्य JavaScript है, जिसे आप अलग से समझ और test कर सकते हैं। नीचे किसी order उपभोक्ता द्वारा किए जाने वाले ack / पुनः प्रयास / डेड-लेटर निर्णय का स्वतंत्र सिमुलेशन दिया गया है।
यह मुख्य विचार दिखाता है: सफल होने पर ack करें, सीमा से कम attempts होने तक पुनः प्रयास करें और पुनः प्रयास समाप्त होने पर संदेश को DLQ में रख दें।
const MAX_RETRIES = 3;
function decide(message) {
// simulate processing: 'good' succeeds, 'bad-json' is poison, else transient
if (message.kind === 'good') return { action: 'ack' };
if (message.kind === 'bad-json') return { action: 'dlq', reason: 'poison' };
const attempts = message.retries || 0;
if (attempts >= MAX_RETRIES) return { action: 'dlq', reason: 'exhausted' };
return { action: 'retry', retries: attempts + 1 };
}
const inbox = [
{ id: 1, kind: 'good' },
{ id: 2, kind: 'bad-json' },
{ id: 3, kind: 'transient', retries: 0 },
{ id: 4, kind: 'transient', retries: 3 }
];
for (const msg of inbox) {
const result = decide(msg);
console.log('msg ' + msg.id + ' ->', JSON.stringify(result));
}त्वरित जाँच
आपके पास मैन्युअल ack का उपयोग करने वाला उपभोक्ता है। किसी संदेश में ऐसा malformed JSON है जिसे कभी parse नहीं किया जा सकता। आप उसे हमेशा चक्र में घूमने से रोकना चाहते हैं और बाद में निरीक्षण के लिए सुरक्षित भी रखना चाहते हैं।
पुनरावलोकन
आपने सीखा कि RabbitMQ से संदेशों का विश्वसनीय और लचीला वितरण कैसे किया जाता है:
- मैन्युअल ack (
noAck: false+channel.ack) कम-से-कम-एक-बार वितरण देते हैं, इसलिए क्रैश होने पर संदेश कभी खोता नहीं है। requeue=falseके साथ nack या reject करने पर विफल संदेश हमेशा चक्र में घूमने के बजाय डेड-लेटर एक्सचेंज को सौंप दिया जाता है।- prefetch(n) बिना ack वाले संदेशों की संख्या सीमित करके धीमे कार्यकर्ताओं पर बैक-प्रेशर डालता है।
- डेड-लेटर एक्सचेंज (
x-dead-letter-exchange) विफल संदेशों को निरीक्षण और फिर से चलाने के लिए स्वचालित रूप से DLQ में भेजते हैं। - पुनः प्रयास TTL वाली प्रतीक्षा कतार का उपयोग करते हैं, जो संदेशों को डेड-लेटर के रूप में मुख्य कतार में वापस भेजती है। attempts की गिनती (
x-deathया कस्टम हेडर के माध्यम से) संदेश को पार्क करने से पहले पुनः प्रयासों की संख्या सीमित करती है।
मिलकर ये तरीके सीमित, दिखाई देने योग्य और बिना संदेश खोए संदेश संसाधन उपलब्ध कराते हैं।
एआई शिक्षक के साथ JavaScript सीखें — निःशुल्क
अपने ब्राउज़र में वास्तविक कोड लिखें और चलाएँ, चौबीसों घंटे एआई शिक्षक से तुरंत सहायता पाएँ, और वेब या ऐप पर वहीं से शुरू करें जहाँ आपने छोड़ा था।
- पाठ्यक्रम
- 22
- पाठ
- 92
अक्सर पूछे जाने वाले प्रश्न
क्या “स्वीकृतियाँ, डेड-लेटर कतारें और पुनःप्रयास” पाठ निःशुल्क है?
हाँ—“स्वीकृतियाँ, डेड-लेटर कतारें और पुनःप्रयास” का पूरा पाठ यहाँ वेब पर निःशुल्क पढ़ा जा सकता है। इंटरैक्टिव अभ्यास (अंतर्निहित कोड संपादक और 24/7 एआई ट्यूटर) करने और Node.js बैकएंड विकास बूटकैंप पाठ्यक्रम का बाकी हिस्सा अनलॉक करने के लिए CoddyKit PRO लें। Node.js बैकएंड विकास बूटकैंप पाठ्यक्रम में कुल 4 पाठ शामिल हैं।
“स्वीकृतियाँ, डेड-लेटर कतारें और पुनःप्रयास” में मैं क्या सीखूँगा?
मैन्युअल स्वीकृतियों से डिलीवरी सुनिश्चित कीजिए, दूषित संदेश संभालिए और पुनःप्रयास व डेड-लेटर प्रवाह लागू कीजिए। आप ब्राउज़र में सीधे चलाए जाने वाले व्यावहारिक कोड के साथ Node.js बैकएंड विकास बूटकैंप का अभ्यास करते हैं, और पाठ पूरा करते समय 24/7 एआई ट्यूटर आपके प्रश्नों के उत्तर देता है।
क्या Node.js बैकएंड विकास बूटकैंप शुरू करने के लिए मुझे किसी अनुभव की आवश्यकता है?
पहले के अनुभव की आवश्यकता नहीं है। CoddyKit पर Node.js बैकएंड विकास बूटकैंप शुरुआती से लेकर उन्नत शिक्षार्थियों तक सभी के लिए व्यवस्थित किया गया है, इसलिए आप यहीं से या शुरुआत से सीखना शुरू कर सकते हैं और अपनी गति से आगे बढ़ सकते हैं। यह 4 में से 3वाँ पाठ है।
“स्वीकृतियाँ, डेड-लेटर कतारें और पुनःप्रयास” पाठ पूरा करने में कितना समय लगता है?
CoddyKit का अधिकांश पाठ लगभग 5–10 मिनट में पूरा हो जाता है। हर पाठ छोटा और संवादात्मक है, इसलिए आप लगातार प्रगति करते हैं और वेब या ऐप पर वहीं से सीखना जारी रख सकते हैं जहाँ आपने छोड़ा था।
क्या मैं इस Node.js बैकएंड विकास बूटकैंप पाठ में कोड लिख और चला सकता हूँ?
हाँ। हर Node.js बैकएंड विकास बूटकैंप पाठ में एक अंतर्निर्मित कोड संपादक शामिल है, जिससे आप सीधे अपने ब्राउज़र में वास्तविक कोड लिख और चला सकते हैं और तुरंत एआई प्रतिक्रिया पा सकते हैं—स्थानीय सेटअप की आवश्यकता नहीं है।
इस पाठ्यक्रम के सभी पाठ
- उत्पादक, उपभोक्ता और AMQP मॉडल
- एक्सचेंज प्रकार: Direct, Topic, Fanout और Headers
- स्वीकृतियाँ, डेड-लेटर कतारें और पुनःप्रयास
- कार्य कतारें, प्रीफ़ेच और प्रतिस्पर्धी उपभोक्ता