Kodokon kodokon.com

ऑब्ज़र्वेबिलिटी: लॉग, healthcheck, ग्रेसफुल शटडाउन

अपनी सेवा को ऑब्ज़र्वेबल और साफ़-सुथरे ढंग से रोकने योग्य बनाएँ: JSON लॉग, liveness/readiness प्रोब, और SIGTERM का प्रबंधन।

9 मिनट · 3 प्रश्न

इस पाठ को Kodokon में खोलें

प्रोडक्शन में, एक लॉग किसी मशीन (Loki, Datadog, CloudWatch) के लिए होता है, इंसान के लिए नहीं: प्रति लाइन एक JSON ऑब्जेक्ट उत्सर्जित करें (एक लाइन = एक इवेंट) जिसमें एक level, एक ISO टाइमस्टैम्प, और संरचित फ़ील्ड हों - कभी भी ऐसे मुक्त-रूप इंटरपोलेशन नहीं जिन्हें क्वेरी करना असंभव हो। एक कम-ज्ञात बारीकी: stdout में लिखना किसी फ़ाइल या टर्मिनल के लिए सिंक्रोनस होता है, लेकिन किसी pipe के लिए एसिंक्रोनस। इसलिए एक बातूनी लॉगर गंतव्य के आधार पर इवेंट लूप को अवरुद्ध कर सकता है - यही pino जैसी लाइब्रेरी का पूरा मकसद है, जो तेज़ी से सीरियलाइज़ करती हैं और लिखने के काम को अलग कर सकती हैं।

JAVASCRIPT
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 पर स्विच कर सकता है, ताकि लोड बैलेंसर इंस्टेंस के बंद होने से पहले उसे हटा दे। इन दोनों को गड्डमड्ड कर देने से स्वस्थ प्रोसेस उसी क्षण दोबारा शुरू हो जाती हैं जब कोई निर्भरता ज़रा-सी लड़खड़ाती है।

JAVASCRIPT
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);
गतिशील readiness और SIGTERM पर ग्रेसफुल शटडाउन।

किसी ग्रेसफुल शटडाउन का क्रम: ऑर्केस्ट्रेटर SIGTERM भेजता है; आप readiness को 503 पर पलट देते हैं; server.close() नई कनेक्शन को अस्वीकार करता है और चल रही रिक्वेस्ट के पूरा होने का इंतज़ार करता है; closeIdleConnections() निष्क्रिय keep-alive कनेक्शन को बंद कर देता है जो अन्यथा close() को अनिश्चित काल तक रोके रखतीं। unref()-चिह्नित टाइमर एक सुरक्षा जाल के रूप में काम करता है: यह 10 सेकंड बाद प्रोसेस को समाप्त कर देता है, बिना खुद प्रोसेस को उससे पहले बाहर निकलने से रोके।

JAVASCRIPT
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;
});
अंतिम उपाय: लॉग करें, फिर बाहर निकलें।

ज्ञान जांच

सुनिश्चित करें कि आपको इस पाठ के मुख्य बिंदु याद हैं।

  1. liveness और readiness प्रोब में क्या अंतर है?
    • कोई नहीं: ये एक ही टेस्ट के दो नाम हैं
    • Liveness जाँचता है कि प्रोसेस चल रही है; readiness कि वह ट्रैफ़िक स्वीकार कर सकती है, निर्भरताओं समेत
    • Readiness कंटेनर को दोबारा शुरू करता है, liveness उसे लोड बैलेंसर से हटाता है
  2. server.close() ठीक-ठीक क्या करता है?
    • यह सभी खुली कनेक्शन तुरंत काट देता है
    • यह एक निश्चित 30 सेकंड की देरी के बाद प्रोसेस को समाप्त कर देता है
    • यह नई कनेक्शन को अस्वीकार करता है और मौजूदा के पूरा होने का इंतज़ार करता है
  3. किसी uncaughtException हैंडलर को क्या करना चाहिए?
    • त्रुटि लॉग करें, फिर प्रोसेस को समाप्त कर दें
    • त्रुटि को अनदेखा करें: Node खुद-ब-खुद उबर जाता है
    • विफल ऑपरेशन को स्वतः दोबारा आज़माएँ