Node.js अपनी असाधारण थ्रूपुट और कॉनकरेंसी का श्रेय एक सरल दिखने वाले डिज़ाइन को देता है: गैर-ब्लॉक करने वाले अतुल्यकालिक (asynchronous) ऑपरेशनों को व्यवस्थित करने वाला एक एकल निष्पादन थ्रेड। इस रनटाइम के मूल में इवेंट लूप—C++ में libuv के माध्यम से कार्यान्वित एक सतत शेड्यूलिंग लूप है जो टाइमर, सिस्टम I/O इवेंट्स, माइक्रोट्सक्स और थ्रेड पूल हैंडऑफ़ का मध्यस्थता करता है। कम विलंबता (low-latency), उच्च-स्तरीय Node.js सेवाओं के निर्माण के लिए यह समझना मौलिक है कि इवेंट लूप प्रत्येक निष्पादन चरण का समन्वय कैसे करता है।
आर्किटेक्चर: V8, libuv, और सिंगल-थ्रेड प्रतिमान
जब कोई डेवलपर कहता है कि Node.js "सिंगल-थ्रेडेड" है, तो वे विशेष रूप से JavaScript कॉल स्टैक का संदर्भ देते हैं। आपका एप्लिकेशन कोड एक सिंगल मुख्य थ्रेड पर निष्पादित होता है, जिससे डेटा रेस, जटिल म्यूटेक्स लॉक और थ्रेड सिंक्रोनाइज़ेशन खतरों समाप्त हो जाते हैं। हालाँकि, अंतर्निहित रनटाइम अत्यधिक मल्टी-थ्रेडेड है:
- V8 इंजन: Google का ओपन-सोर्स JavaScript इंजन कोड को पार्स करता है, फ़ंक्शंस को मशीन निर्देशों में संकलित (compile) करता है, हीप मेमोरी को प्रबंधित करता है, और गारबेज कलेक्शन को व्यवस्थित करता है।
- libuv: C में लिखी गई एक उच्च-प्रदर्शन बहु-प्लेटफ़ॉर्म अतुल्यकालिक (asynchronous) I/O लाइब्रेरी। यह इवेंट लूप, क्रॉस-प्लेटफ़ॉर्म पोलिंग तंत्र (Linux पर
epoll, macOS/BSD परkqueue, Windows परIOCP), और सिंक्रोनस OS ऑपरेशनों के लिए एक वर्कर थ्रेड पूल प्रदान करता है। - ऑपरेटिंग सिस्टम कर्नेल: आधुनिक ���र्नेल गैर-ब्लॉक करने वाले नेटवर्क सॉकेट्स और इवेंट नोटिफिकेशन इंटरफेस का समर्थन करते हैं जो समर्पित कर्नेल थ्रेड्स के बिना हजारों खुले कनेक्शनों को समवर्ती रूप से संभालते हैं।
नेटवर्क I/O ऑपरेशन (HTTP अनुरोध, TCP स्ट्रीम, WebSockets) वर्कर थ्रेड्स का उपयोग नहीं करते हैं। वे सीधे ऑपरेटिंग सिस्टम कर्नेल की गैर-ब्लॉक करने वाली डिमल्टीप्लेक्सिंग परत (जैसे epoll) के साथ पंजीकृत होते हैं और सॉक��ट बफ़र्स में आने वाले डेटा होने पर ही इवेंट लूप को जगाते हैं।
इवेंट लूप जीवनचक्र के 6 चरण
इवेंट लूप के प्रत्येक पूर्ण पुनरावृत्ति (iteration) को टिककहा जाता है। प्रत्येक टिक में, libuv छह अलग-अलग चरणों में विशिष्ट FIFO कॉलबैक कतारों को क्रमिक रूप से संसाधित करता है:
| चरण | कतार और जिम्मेदारियाँ | सामान्य संचालन |
|---|---|---|
| 1. टाइमर | बीते हुए थ्रेशोल्�� टाइमर द्वारा निर्धारित कॉलबैक निष्पादित करता है। | setTimeout(), setInterval() |
| 2. लंबित कॉलबैक (Pending Callbacks) | पिछले चक्र से स्थगित I/O कॉलबैक निष्पादित करता है। | TCP सॉकेट त्रुटि हैंडलर (ECONNREFUSED) |
| 3. आइडल, प्रिपेयर | आंतरिक सबसिस्टम हाउस-कीपिंग। | libuv आंतरिक स्थिति सिंक्रोनाइज़ेशन |
| 4. पोल | नए I/O इवेंट्स को पुनर्प्राप्त करता है, I/O कॉलबैक निष्पादित करता है, और स्लीप टाइमआउट की गणना करता है। | आने वाले HTTP अनुरोध, डेटाबेस रीड, फ़ाइल बफ़र्स |
| 5. चेक | I/O पोलिंग के ठीक बाद चलने के लिए विशेष रूप से पंजीकृत कॉलबैक निष्पादित करता है। | setImmediate() |
| 6. क्लोज़ कॉलबैक | संसाधन सफ़ाई और अंतिम टियर-डाउन हुक निष्पादित करता है। | socket.on('close'), process.on('exit') |
पोल चरण और ब्लॉकिंग स्लीप गणना
The पोल चरण वह जगह है जहाँ Node.js अपने परिचालन जीवन का अधिकांश समय बिताता है। पोल चरण में प्रवेश करते समय:
- यदि पोल कतार में संसाधित करने के लिए स्क्रिप्ट शामिल हैं, तो इवेंट लूप उन्हें तब तक सिंक्रोनस रूप से निष्पादित करता है जब तक कि कतार समाप्त नहीं हो जाती या सिस्टम सीमा तक नहीं पहुँच जाती।
- यदि पोल कतार खाली है, तो लूप जांचता है कि क्या
setImmediate()कॉलबैक प्रतीक्षा कर रहे हैं। यदि हां, तो यह तुरंत चेक चरण. - पर перехода करता है यदि कोई
setImmediate()स्क्रिप्ट मौजूद नहीं है, तो लूप टाइमर मिन-हीप की जांच करता है। यदि कोई टाइमर बीत चुका है, ��ो यह टाइमर चरण में वापस आ जाता है। अन्यथा, यह निकटतम टाइमर समाप्त होने तक शेष सटीक अवधि के लिए OS प्रतीक्षा कॉल (जैसेepoll_wait) में थ्रेड को स्लीप मोड में रखता है।
माइक्रोट्सक्स: process.nextTick बनाम Promise.then()
माइक्रोट्सस्क कतारें मानक libuv छह-चरण चक्र के बाहर बैठती हैं और उच्च निष्पादन प्राथमिकता रखती हैं। जब भी कोई JavaScript कॉल स्टैक किसी ऑपरेशन को निष्पादित करना समाप्त कर देता है—या इवेंट लूप चरणों के बीच संक्रमण करते समय—Node.js आगे बढ़ने से पहले अपनी माइक्रोट्सस्क कतारों को पूरी तरह से खाली कर देता है।
| माइक्रोट्सस्क प्रकार | प्राथमिकता स्तर | निष्पादन गारंटी |
|---|---|---|
process.nextTick() |
सर्वोच्च (टिक कतार) | वर्तमान ऑपरेशन समाप्त होने के तुरंत बाद, किसी अन्य माइक्रोट्सस्क या चरण संक्रमण से पहले निष्पादित होता है। |
Promise.then() / async await |
उच्च (प्रॉमिस कतार) | सभी लंबित के तुरंत बाद निष्पादित होता है process.nextTick() कॉलबैक पूरी तरह से खाली हो चुके हैं। |
setImmediate() |
मानक (चेक चरण) | I/O पोलिंग के बाद चेक चरण के दौरान चलता है, जिससे I/O इवेंट्स को पहले प्रोसेस किया जा सके। |
इवेंट लूप स्टारवेशन पर चेतावनी: क्योंकि पुनरावर्ती process.nextTick() कॉल्स इवेंट लूप के आगे बढ़ने से पहले पूरी तरह से खाली हो जाते हैं, इसलिए अनंत nextTick पुनरावर्ती I/O पोलिंग को फ़्रीज़ कर देगी और पूरे सर्वर को स्टार्व कर देगी।
libuv वर्कर थ्रेड पूल
कुछ OS ऑपरेशन सभी ऑपरेटिंग सिस्टम पर नॉन-ब्लॉकिंग एसिंक्रोनस इंटरफेस का समर्थन नहीं करते हैं। इन ऑपरेशनों के दौरान मुख्य JavaScript थ्रेड को ब्लॉक होने से रोकने के लिए, libuv एक पृष्ठभूमि C++ वर्कर थ्रेड पूल बनाए रखता है।
libuv थ्रेड पूल को सौंपे गए ऑपरेशनों में शामिल हैं:
- फ़ाइल सिस्टम मॉड्यूल (
fs):fs.readFile(),fs.writeFile(),fs.stat()और निर्देशिका संचालन। - क्रिप्टोग्राफी मॉड्यूल (
crypto): CPU-हैवी हैशिंग, कुंजी निर्माण, और सि cifras (crypto.pbkdf2(),crypto.scrypt(),crypto.randomBytes()). - संपीड़न मॉड्यूल (
zlib): Gzip, Brotli, और Deflate स्ट्रीम ट्रांसफॉर्मेशन। - DNS रिज़ॉल्यूशन:
dns.lookup()(जो सिंक्रोनस C लाइब्रेरी का उपयोग करता हैgetaddrinfo(3)).
UV_THREADPOOL_SIZE को ट्यून करना
डिफ़ॉल्ट रूप से, libuv इतने आकार का थ्रेड पूल आवंटित करता है: 4 वर्कर थ्रेड्स. समवर्ती डिस्क I/O, संपीड़न, या पासवर्ड हैशिंग करने वाले हाई-थ्रूपुट सर्वर पर, चार थ्रेड आसानी से संतृप्त हो सकते हैं, जिससे ऑपरेशन कतार में लग जाते हैं और कृत्रिम विलंबता स्पाइक्स आ जाती हैं।
Node प्रोसेस लॉन्च करने से पहले आप थ्रेड पूल का आकार 1024 थ्रेड्स तक बढ़ा सकते हैं:
export UV_THREADPOOL_SIZE=16 && node server.js
उत्पादन में इवेंट लूप लैग की निगरानी करना
जब मुख्य थ्रेड पर सिंक्रोनस निष्पादन में बहुत अधिक समय लगता है (उदा. 50MB JSON स्ट्रिंग को पार्स करना या बिना अ��ुक्रमित RegEx को निष्पादित करना), तो इवेंट लूप को आगे बढ़ने से रोक दिया जाता है। इस देरी को इस प्रकार मापा जाता है इवेंट लूप लैग.
Node.js 12+ में, आप इसका उपयोग करके मूल रूप से उच्च-रिज़ॉल्यूशन इवेंट लूप देरी को माप सकते हैं perf_hooks:
const { monitorEventLoopDelay } = require('perf_hooks');
const histogram = monitorEventLoopDelay({ resolution: 10 });
histogram.enable();
setInterval(() => {
const p99 = (histogram.percentile(99) / 1e6).toFixed(2);
const max = (histogram.max / 1e6).toFixed(2);
console.log(`Event Loop Lag — p99: ${p99}ms | max: ${max}ms`);
histogram.reset();
}, 5000);
इवेंट लूप ब्लॉकिंग को रोकने के लिए व्यावहारिक रणनीतियाँ
- भारी CPU को वर्कर थ्रेड्स पर ऑफ़लोड करें: छवि प्रसंस्करण, PDF संकलन, या भारी डेटा ट्रांसफ़ॉर्म जैसे CPU-गहन कार्यों के लिए Node के मूल
worker_threads���ॉड्यूल या अलग माइक्रोसर्विस प्रक्रियाओं का उपयोग करें। - बड़े पेलोड को स्ट्रीम करें: इसके साथ भारी JSON फ़ाइलों को मेमोरी में बफर करने से बचें
fs.readFileSync(). इसके बजाय, इसके साथ डेटा चंक्स को स्ट्रीम करेंfs.createReadStream()और JSON स्ट्रीमिंग पार्सर्स। - setImmediate के साथ लंबे लूप तोड़ें: यदि वस्तुओं के एक बड़े संग्रह को संसाधित कर रहे हैं, तो सरणी (array) को छोटे टुकड़ों में विभाजित करें और इसका उपयोग करके बाद के पुनरावृत्तियों को शेड्यूल करें
setImmediate()ताकि चंक्स के बीच आने वाले I/O इवेंट्स को प्रोसेस किया जा सके। - सुरक्षित रेगुलर एक्सप्रेसर्स: नेस्टेड क्वांटिफ़ायर्स में विनाशकारी बैकट्रैकिंग से बचकर रेगुलर एक्सप्रेसर डेनियल ऑफ़ सर्विस (ReDoS) से बचाव करें।
PM2 प्रोसेस क्लस्टर, Nginx रिवर्स प्रॉक्सी एज कैशिंग, और स्वचालित SSL के साथ कॉन्फ़िगर किए गए अनुकूलित VPS बुनियादी ढांचे पर अपने Node.js एप्लिकेशन और माइक्रोसेर्विसेज को तैनात करें।
- Node.js VPS होस्टिंग योजनाएं देखें — बेयर-मेटल गति और समर्पित vCPUs
- multiDomainCMS प्लेटफ़ॉर्म आर्किटेक्चर — 35ms से कम SSR प्रतिक्रिया समय
- एक कस्टम बुनियादी ढांचा उद्धरण का अनुरोध करें — मुफ़्त साइज़िंग और माइग्रेशन सहायता