محتويات الدورة

Node.js / المتقدم: خدمات Node.js موثوقة / الدرس 16

الموثوقية والإغلاق المنضبط

ضع سياسات فشل محدودة وأوقف الخدمة مع مراعاة العمل المقبول والموارد المملوكة.

وقت القراءة التقريبي: 4 دقيقةتاريخ مراجعة المحتوى
في هذه الصفحة

أهداف الدرس

  • اشرح المفاهيم والمفاضلات المتعلقة بموضوع «الموثوقية والإغلاق المنضبط».
  • توقع المثالين وتحقق من مخرجاتهما وسلوك تنظيف الموارد فيهما.
  • شخّص الإخفاقات الثلاثة الشائعة وطبق الحلول المشروحة على مدخلات مشابهة.

تحديد ميزانية العملية كاملة

يحتاج الطلب إلى موعد نهائي شامل لا مهلة كبيرة مستقلة لكل اعتمادية ومحاولة. وزع الميزانية على الحصول على الاتصال والشبكة والمعالجة. ميّز مهلة الخمول عن المهلة الكلية؛ فقد يتجاوز طرف يرسل بايتا دوريا سياسة الخمول وحدها. مرر الإلغاء حيث يُدعم وحرر الموارد بانتهاء الطلب. تعمل حدود السعة والمهل والطوابير معا؛ فقد يستهلك طابور غير محدود المهلة قبل بدء العمل ويحتفظ بذاكرة لمستدعين غادروا.

إعادة المحاولة تحتاج حدودا ومعاني

يعيد مثال المحاولة فشلا مؤقتا مصنفا وينجح في الثانية. تأخيره صغير وثابت للتعليم؛ وتستخدم المحاولات المنشورة عادة تأخيرا أسيا محدودا مع عشوائية وموعد شامل. لا تكرر كل الأخطاء. قد تزيد المحاولات المتزامنة من كل النسخ سوء الاعتمادية المتعطلة. قد يرفض قاطع الدائرة الطلبات مؤقتا بعد فشل مستمر، لكنه يحتاج سياسة اختبار تعاف. تكمل هذه الأدوات منع تكرار الأثر ولا تستبدله في العمليات المغيرة للحالة البعيدة.

الإغلاق انتقال حالة

تنتقل الخدمة من البدء إلى الجاهزية ثم التصريف فالتوقف. أثناء التصريف أوقف الجاهزية كي تتوقف طبقة التوجيه عن إرسال طلبات، ثم أوقف قبول الاتصالات ودع المقبول يكتمل ضمن مهلة. أغلق المستهلكين الخلفيين والمجمعات والعمال ومصدّرات القياس بترتيب مقصود. ينفذ مثال HTTP طلبا محليا ويستدعي إغلاقا آمنا للتكرار مرتين وينتظر وعد الإغلاق نفسه. ينسق النشر الفعلي الإشارات وفترة سماح المنصة؛ وتوقيت الانتقالات عقد تشغيلي.

javascript مثال كامل: الإغلاق انتقال حالة
import { setTimeout as delay } from 'node:timers/promises';
let attempts = 0;
async function dependency() {
  attempts++;
  if (attempts === 1) { const error = new Error('Temporary'); error.code = 'TRANSIENT'; throw error; }
  return 'ok';
}
async function run() {
  for (let attempt = 1; attempt <= 3; attempt++) {
    try { return await dependency(); }
    catch (error) {
      if (error.code !== 'TRANSIENT' || attempt === 3) throw error;
      await delay(1);
    }
  }
}
console.log(await run(), attempts);
الناتج المتوقع
ok 2

الرمز TRANSIENT وحده قابل للتكرار وعدد المحاولات محدود. تقبل الدالة الفعلية أيضا موعدا نهائيا وإشارة إلغاء.

تصريف الاتصالات بوعي

توقف server.close الاستماع الجديد وتنتظر الاتصالات وفق سلوك HTTP في الإصدار المختار. تحتاج الاتصالات المرقاة الطويلة إلى تتبع وبروتوكول إغلاق مستقلين. حدد مهلة نهائية ووثّق معنى الإنهاء القسري للعمل غير المكتمل. لا تستدع process.exit مباشرة عند SIGTERM فقد تقطع الاستجابات والسجلات والتنظيف. يمكن لإشارة ثانية أو انقضاء المهلة تفعيل سياسة القوة الموثقة. أزل مؤقت الإغلاق عند انتهاء التصريف كي لا يبقي العملية حية.

javascript مثال كامل: تصريف الاتصالات بوعي
import { createServer } from 'node:http';
import { once } from 'node:events';
let ready = true;
let closing;
const server = createServer((req, res) => { res.end('ok'); });
function shutdown() {
  if (!closing) {
    ready = false;
    closing = new Promise((resolve, reject) => server.close(e => e ? reject(e) : resolve()));
  }
  return closing;
}
server.listen(0, '127.0.0.1'); await once(server, 'listening');
try {
  const response = await fetch(`http://127.0.0.1:${server.address().port}`);
  console.log(await response.text());
} finally {
  const a = shutdown(); const b = shutdown();
  console.log(ready, a === b); await a;
}
console.log('stopped');
الناتج المتوقع
ok
false true
stopped

يغير التصريف الجاهزية ويعيد وعد إغلاق واحدا. تُستهلك الاستجابة المحلية قبل إغلاق المستمع.

اختبار الفشل دون حركة حقيقية

اختبر منعزلا اعتمادية بطيئة وعاملا فاشلا وإغلاقا مكررا وطلبا قُبل قبل التصريف. تحقق من عدم قبول عمل جديد بعد الانتقال ومن إغلاق الموارد في النجاح والخطأ. لا تجر تجارب سعة إلا في بيئة مصرح بها ومتحكم بها ومدخلات محدودة. تشمل الاستعادة إعادة التشغيل الخارجية وتسوية البيانات والرجوع، لا التقاط الاستثناء فقط. وضّح معاني الجاهزية والحياة كي لا تعيد المنصة تشغيل عملية مشغولة باستمرار أو توجه الطلبات إلى عملية عاجزة.

الأخطاء الشائعة وإصلاحها

تقطع SIGTERM الاستجابات

لماذا يحدث؟
خرج معالج الإشارة قبل تصريف الموارد.
كيف تصلحه؟
غيّر الجاهزية وأوقف القبول وصفّ العمل ثم اخرج.
كيف تتجنب تكراره؟
اختبر الإغلاق مع طلب قيد التنفيذ.

تفاقم المحاولات الانقطاع

لماذا يحدث؟
تضاعف محاولات متزامنة غير محدودة الحمل.
كيف تصلحه؟
حدد المحاولات والتأخير والعشوائية والموعد الشامل.
كيف تتجنب تكراره؟
راقب المحاولات لكل طلب أصلي.

تبقى العملية حية بعد إغلاق HTTP

لماذا يحدث؟
بقي مؤقت أو عامل أو مجمع أو اتصال مرقى.
كيف تصلحه؟
تتبع كل مورد طويل العمر وأغلقه.
كيف تتجنب تكراره؟
احتفظ بحصر واضح لدورات حياة الموارد.

اختبر فهمك

1. تمهل المنصة30 ثانية قبل الإنهاء القسري. هل تعطى كل محاولة مهلة30 ثانية؟

اعرض الحل المشروح

لا. احجز زمنا للتصريف والتنظيف وضع كل المحاولات ضمن ميزانية كلية أصغر. لا تتجاوز حدود المحاولة الموعد الشامل. أوقف جدولة المحاولات الجديدة عند بدء الإغلاق واحترم إلغاء المستدعي.

2. يطلب مكونان الإغلاق في وقت متقارب. ما القيد المطلوب؟

اعرض الحل المشروح

يجب أن يتولى تسلسل واحد إغلاق الموارد ويرى المستدعيان نتيجة الاكتمال نفسها. خزّن وعد الإغلاق وأعده كما في المثال. يمنع ذلك إغلاق المجمعات مرتين وانتقالات جاهزية متعارضة مع إمكان سياسة قوة مستقلة موثقة.

أهم ما تعلمته

  • يحتاج الطلب إلى موعد نهائي شامل لا مهلة كبيرة مستقلة لكل اعتمادية ومحاولة.
  • يعيد مثال المحاولة فشلا مؤقتا مصنفا وينجح في الثانية.
  • اختبر منعزلا اعتمادية بطيئة وعاملا فاشلا وإغلاقا مكررا وطلبا قُبل قبل التصريف.

المراجع الرسمية