أنظمة إنترنت الأشياء (IoT) هشة بطبيعتها. مستشعر غير مستقر واحد، أو خلل لحظي في الشبكة، أو انتهاء مهلة لواجهة برمجة تطبيقات (API) مسؤولة يمكن أن تتسبب في فشل شامل للنظام. بينما تتعامل لغات البرمجة التقليدية مع هذه الاستثناءات باستخدام كتل `try-catch`، فإن أدوات سير العمل المرئية مثل Node-RED تتطلب نهجًا مختلفًا لتحمل الأعطال. ببساطة، ترك الرسائل تفشل بصمت ليس خيارًا للبنية التحتية الجاهزة للإنتاج. لبناء نظام ذاتي الشفاء حقًا، يجب علينا تنفيذ مفاتيح الدائرة (Circuit Breakers) لمنع الفشل المتسلسل وطرود الرسائل الميتة (DLQs) لالتقاط وفحص الرسائل غير القابلة للتسليم.
لماذا تحتاج سير العمل المرئية إلى معالجة أخطاء صريحة
في تدفق Node-RED، إذا فشل عقدة (على سبيل المثال، انتهاء مهلة طلب HTTP)، يتم عادةً إسقاط الرسالة أو إرسالها إلى عقدة خطأ. بدون استراتيجية، سيؤدي فشل الخدمة المسؤولة (مثل قاعدة بيانات سحابية) إلى احتقان التدفق بأكمله، مما يستهلك الذاكرة ويؤخر الإنذارات الحرجة للأجهزة الصحية. الهدف من النظام الذاتي الشفاء هو اكتشاف هذه الأعطال تلقائيًا، وإيقاف إرسال الحركة إلى المكوّن المعطوب (مفتاح الدائرة)، وتخزين البيانات الفاشلة بأمان لاستعادتها لاحقًا (DLQ) دون إيقاف بقية خط الأنابيب.
تنفيذ نمط مفتاح الدائرة (Circuit Breaker)
يراقب مفتاح الدائرة معدل نجاح الاستدعاءات لخدمة تابعة. إذا تجاوز معدل الفشل حدًا معينًا، فإنه "يفتح" الدائرة، مما يؤدي إلى فشل الطلبات اللاحقة فورًا لمدة محددة لمنح الخدمة وقتًا للتعافي.
بينما يحتوي Node-RED على معالجة أخطاء مدمجة، يمكننا تنفيذ هذه المنطق باستخدام مجموعة من عقد switch، وfunction، وchange. فيما يلي تنفيذ مفاهيمي باستخدام عقدة function تتتبع عدد مرات الفشل في تخزين السياق.
// عقدة Function: منطق مفتاح الدائرة
// يجب وضع هذه العقدة قبل عقدة طلب HTTP غير المستقرة.
let failures = context.get('failures') || 0;
let circuitState = context.get('circuitState') || 'CLOSED'; // CLOSED, OPEN, HALF_OPEN
const FAILURE_THRESHOLD = 3;
const RESET_TIMEOUT = 30000; // 30 ثانية
// إذا كانت الدائرة مفتوحة (OPEN)، تحقق مما إذا كان الوقت قد حان للمحاولة مرة أخرى (HALF_OPEN)
if (circuitState === 'OPEN') {
const now = new Date().getTime();
const lastOpenTime = context.get('lastOpenTime') || 0;
if (now - lastOpenTime > RESET_TIMEOUT) {
circuitState = 'HALF_OPEN';
context.set('circuitState', circuitState);
// السماح بمرور طلب واحد لاختبار الخدمة
return msg;
} else {
// الدائرة لا تزال مفتوحة، إسقاط الرسالة وتفعيل الإنذار
msg.payload = { error: "Service unavailable", circuit: "OPEN" };
msg.topic = "circuit_breaker_trip";
return [msg, null]; // توجيه إلى DLQ/إنذار الخطأ
}
}
// تمرير الرسالة إلى عقدة الخدمة
msg._circuitContext = { failures: failures };
return msg;
// **هام:** يجب عليك معالجة الاستجابة.
// أرفق عقدة "معالج مفتاح الدائرة" بعد استدعاء الخدمة.
// إذا فشل استدعاء الخدمة (عقدة الخطأ)، زد عدد مرات الفشل.
// إذا نجح، أعد تعيين عدد مرات الفشل.
معالجة الاستجابة: يجب عليك التقاط مساري النجاح والفشل. قم بتوصيل المخرج من عقدة الخدمة بعقدة function تعيد تعيين عدد مرات الفشل عند النجاح. قم بتوصيل مخرج الخطأ من عقدة الخدمة بعقدة function منفصلة تزيد عدد مرات الفشل، وإذا تم تحقيق الحد، تحدّث circuitState إلى OPEN.
تنفيذ طرد الرسائل الميتة (DLQ)
عندما يتم تفعيل مفتاح الدائرة أو تفشل رسالة في التحقق، لا نتخلص من البيانات. نرسلها إلى طرد الرسائل الميتة. في Node-RED، يتم تنفيذ DLQ عادةً كمخزن دائم، مثل عقدة file، أو موضوع mqtt باسم dlq/alerts، أو مجموعة database.
إليك كيفية هيكلة عقدة DLQ لضمان سلامة البيانات:
// عقدة Function: تجهيز الرسالة لـ DLQ
// يتم وضع هذه العقدة على مخرج الخطأ لمفتاح الدائرة
// أو عقدة الخطأ الرئيسية للتدفق.
const dlqMessage = {
originalPayload: msg.payload,
originalTopic: msg.topic,
deviceId: msg.deviceId,
timestamp: new Date().toISOString(),
errorReason: msg.error.message || "Unknown Error",
retryCount: context.get('retryCount') || 0
};
// اختياريًا، زد عداد إعادة المحاولة لإعادة المعالجة المجدولة
const currentRetries = dlqMessage.retryCount + 1;
dlqMessage.retryCount = currentRetries;
return {
payload: dlqMessage,
topic: "iot/dlq/alerts" // استخدم موضوع MQTT مخصص
};
الاستعادة وإعادة المحاولة التلقائية
النظام الذاتي الشفاء الحقيقي لا يخزن الرسائل الفاشلة فحسب، بل يحاول استعادتها. أضف عقدة inject تُطلق كل 5 دقائق. يجب أن تقرأ هذه العقدة من مخزن DLQ الخاص بك (على سبيل المثال، اشتراك MQTT في iot/dlq/alerts/# مع سياسة احتفاظ، أو استعلام قاعدة بيانات).
لكل رسالة مسترجعة من DLQ، تحقق من circuitState. إذا كانت الدائرة CLOSED أو HALF_OPEN، أعد نشر الحمولة الأصلية إلى تدفق المعالجة الرئيسي. إذا كانت الدائرة OPEN، اتركها في DLQ للدورة التالية. هذا يخلق آلية ضغط خلفية طبيعية حيث تُعاد محاولة الرسائل الفاشلة فقط عندما تكون الخدمة المسؤولة صحية.
الخاتمة
من خلال دمج مفاتيح الدائرة وطرود الرسائل الميتة في تدفقات Node-RED الخاصة بك، تحول نظام IoT هش إلى بنية تحتية مرنة ذاتية الشفاء. يمنع مفتاح الدائرة الحمل الزائد ويسمح للخدمات بالتعافي، بينما يضمن DLQ عدم فقدان أي بيانات. هذا النمط حاسم لأي نشر IoT للإنتاج حيث تكون سلامة البيانات وتوافر النظام أمراً بالغ الأهمية. ابدأ صغيراً: نفذ منطق المفتاح لأكثر خدمة مسؤولة غير مستقرة لديك وراقب الأثر على استقرار النظام.