अपनी सेवा को ऑब्ज़र्वेबल और साफ़-सुथरे ढंग से रोकने योग्य बनाएँ: JSON लॉग, liveness/readiness प्रोब, और SIGTERM का प्रबंधन।
इस पाठ को Kodokon में खोलेंप्रोडक्शन में, एक लॉग किसी मशीन (Loki, Datadog, CloudWatch) के लिए होता है, इंसान के लिए नहीं: प्रति लाइन एक JSON ऑब्जेक्ट उत्सर्जित करें (एक लाइन = एक इवेंट) जिसमें एक level, एक ISO टाइमस्टैम्प, और संरचित फ़ील्ड हों - कभी भी ऐसे मुक्त-रूप इंटरपोलेशन नहीं जिन्हें क्वेरी करना असंभव हो। एक कम-ज्ञात बारीकी: stdout में लिखना किसी फ़ाइल या टर्मिनल के लिए सिंक्रोनस होता है, लेकिन किसी pipe के लिए एसिंक्रोनस। इसलिए एक बातूनी लॉगर गंतव्य के आधार पर इवेंट लूप को अवरुद्ध कर सकता है - यही pino जैसी लाइब्रेरी का पूरा मकसद है, जो तेज़ी से सीरियलाइज़ करती हैं और लिखने के काम को अलग कर सकती हैं।
function log(level, message, fields = {}) {
const entry = {
level,
message,
time: new Date().toISOString(),
pid: process.pid,
...fields,
};
process.stdout.write(JSON.stringify(entry) + "\n");
}
log("info", "server started", { port: 3000 });
log("error", "db unreachable", { retryInMs: 5000 });दो प्रोब में अंतर करें। Liveness इस प्रश्न का उत्तर देता है "क्या प्रोसेस को दोबारा शुरू किया जाना चाहिए?": इसे मामूली बने रहना चाहिए, बिना किसी बाहरी निर्भरता के। Readiness इस प्रश्न का उत्तर देता है "क्या मैं ट्रैफ़िक प्राप्त कर सकता हूँ?": यह डेटाबेस की जाँच कर सकता है, और सबसे बढ़कर शटडाउन के दौरान 503 पर स्विच कर सकता है, ताकि लोड बैलेंसर इंस्टेंस के बंद होने से पहले उसे हटा दे। इन दोनों को गड्डमड्ड कर देने से स्वस्थ प्रोसेस उसी क्षण दोबारा शुरू हो जाती हैं जब कोई निर्भरता ज़रा-सी लड़खड़ाती है।
import { createServer } from "node:http";
let ready = true;
const server = createServer((req, res) => {
if (req.url === "/healthz") {
res.writeHead(ready ? 200 : 503);
res.end(ready ? "ok" : "draining");
return;
}
res.end("hello");
});
server.listen(3000);
function shutdown() {
ready = false;
server.closeIdleConnections();
server.close(() => process.exit(0));
setTimeout(() => process.exit(1), 10_000).unref();
}
process.on("SIGTERM", shutdown);
process.on("SIGINT", shutdown);किसी ग्रेसफुल शटडाउन का क्रम: ऑर्केस्ट्रेटर SIGTERM भेजता है; आप readiness को 503 पर पलट देते हैं; server.close() नई कनेक्शन को अस्वीकार करता है और चल रही रिक्वेस्ट के पूरा होने का इंतज़ार करता है; closeIdleConnections() निष्क्रिय keep-alive कनेक्शन को बंद कर देता है जो अन्यथा close() को अनिश्चित काल तक रोके रखतीं। unref()-चिह्नित टाइमर एक सुरक्षा जाल के रूप में काम करता है: यह 10 सेकंड बाद प्रोसेस को समाप्त कर देता है, बिना खुद प्रोसेस को उससे पहले बाहर निकलने से रोके।
process.on("uncaughtException", (err) => {
const entry = {
level: "fatal",
message: err.message,
stack: err.stack,
time: new Date().toISOString(),
};
process.stderr.write(JSON.stringify(entry) + "\n");
process.exit(1);
});
process.on("unhandledRejection", (reason) => {
throw reason;
});