كان فشل خدمة واحدة يُسقط نظامنا بالكامل: كيف أنقذنا نمط ‘قاطع الدائرة’ (Circuit Breaker) من جحيم الانهيارات المتتالية؟

يا جماعة الخير، السلام عليكم.

اسمحوا لي آخذكم في رحلة قصيرة بالزمن، ليلة خميس ما بنساها بحياتي. كنت قاعد في مكتبي، الساعة كانت حوالي 11 بالليل، وفنجان القهوة الثالث بين إيديّ. فجأة، التلفون بلش يرن بدون توقف، ورسائل “سلاك” بتضوي زي شجرة عيد الميلاد. فتحت لوحة المراقبة (Dashboard) لألاقي الكارثة: النظام واقع! كل المؤشرات باللون الأحمر، والمستخدمين مش قادرين يعملوا أي إشي.

بعد شوية حفر وتحليل مع الفريق، اكتشفنا إن المشكلة كلها بلشت من خدمة صغيرة ومسكينة، خدمة الإشعارات (Notifications Service). هالخدمة صار فيها بطء شديد بسبب مشكلة في قاعدة البيانات تبعتها. لكن المصيبة إنه كل خدماتنا الثانية، من تسجيل الدخول للدفع للملف الشخصي، كانت بتحاول تحكي مع خدمة الإشعارات هاي. ولأنها بطيئة وما بترد، كل طلب كان يضل معلق، يسحب معه “ثريد” (thread) وموارد من السيرفر. شوي شوي، كل خدماتنا استنفدت مواردها ووقعت زي أحجار الدومينو. فشل واحد صغير، عمل انهيار كامل.

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

ما هو الجحيم الذي كنا نعيشه؟ مشكلة الانهيارات المتتالية (Cascading Failures)

اللي صار معنا هذيك الليلة إله اسم تقني رنان: الانهيارات المتتالية (Cascading Failures). في عالم الخدمات المصغرة (Microservices)، خدماتك بتعتمد على بعضها البعض. خدمة المستخدمين بتطلب بيانات من خدمة الطلبات، وخدمة الطلبات بتطلب تأكيد من خدمة الدفع، وهلم جرا.

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

  • الخدمة (أ) تستدعي الخدمة (ب).
  • الخدمة (ب) لديها مشكلة (بطيئة أو لا تستجيب).
  • الخدمة (أ) تظل منتظرة الرد، وتستهلك مواردها (اتصالات، ذاكرة، وحدات معالجة).
  • تأتي طلبات جديدة إلى الخدمة (أ)، وكلها تنتظر الخدمة (ب).
  • خلال دقائق، تستنفد الخدمة (أ) كل مواردها وتتوقف عن الاستجابة لأي طلب، حتى الطلبات التي لا علاقة لها بالخدمة (ب).
  • الآن، إذا كانت الخدمة (ج) تعتمد على الخدمة (أ)، فإنها ستبدأ بالفشل أيضًا. وهكذا ينتشر الفشل كالنار في الهشيم.

المشكلة الأساسية ليست أن الخدمات تفشل، فالفشل حتمي في الأنظمة الموزعة. المشكلة هي كيف يتعامل نظامك مع هذا الفشل.

المنقذ: نمط قاطع الدائرة (Circuit Breaker)

هنا يأتي دور البطل، نمط قاطع الدائرة. الفكرة مستوحاة مباشرة من قاطع الكهرباء الموجود في بيتك. لما يصير في حمل كهربائي زائد أو تماس، القاطع “بيفصل” أو “بيطق” ليحمي أجهزة البيت من الاحتراق والبيت كله من الحريق. هو لا يصلح المشكلة الكهربائية، ولكنه يعزلها ويمنع وقوع كارثة أكبر.

نمط قاطع الدائرة البرمجي يعمل بنفس المبدأ. هو يغلف (wraps) استدعاءات الشبكة لخدمة أخرى ويراقب حالات الفشل. وله ثلاث حالات رئيسية:

الحالة المغلقة (Closed)

هذا هو الوضع الطبيعي. الطلبات تمر من خلال قاطع الدائرة إلى الخدمة المستهدفة بدون أي تدخل. في هذه الأثناء، يقوم القاطع بعدّ حالات الفشل (مثل انتهاء مهلة الاستدعاء، أو أخطاء السيرفر). إذا وصل عدد حالات الفشل إلى حد معين خلال فترة زمنية محددة (مثلاً، 5 أخطاء في 30 ثانية)، “يقرر” القاطع أن هناك مشكلة جدية ويفتح الدائرة.

الحالة المفتوحة (Open)

عندما تفتح الدائرة، يتوقف القاطع عن تمرير أي طلبات إلى الخدمة المتعثرة. بدلًا من ذلك، يفشل فورًا (fail fast) ويرجع خطأ مباشرًا للخدمة المستدعية. هذا هو السحر بعينه! لأنه بذلك:

  1. يحمي الخدمة المستدعية: لا تستهلك مواردها في انتظار رد لن يأتي أبدًا.
  2. يعطي الخدمة المتعثرة فرصة للتعافي: بدلًا من إغراقها بمزيد من الطلبات التي لا تستطيع معالجتها، نمنحها وقتًا لتتنفس وتصلح نفسها (ربما يتم إعادة تشغيلها تلقائيًا).

بعد فترة زمنية محددة مسبقًا (مثلاً، 60 ثانية)، ينتقل القاطع إلى الحالة التالية.

الحالة شبه المفتوحة (Half-Open)

بعد انتهاء فترة الانتظار، لا يعود القاطع مباشرة إلى الحالة المغلقة. بدلاً من ذلك، يدخل في حالة اختبار حذرة. يسمح بمرور طلب واحد فقط (أو عدد قليل من الطلبات) إلى الخدمة.

  • إذا نجح هذا الطلب: يعتبر القاطع أن الخدمة قد تعافت، فيغلق الدائرة ويعود إلى الحالة الطبيعية (Closed).
  • إذا فشل هذا الطلب: يعتبر القاطع أن المشكلة لا تزال قائمة، فيعود فورًا إلى الحالة المفتوحة (Open) ويبدأ فترة انتظار جديدة.

كيف طبقنا قاطع الدائرة؟ مثال عملي

الكلام النظري جميل، لكن “ورجيني الشغل”. في عالم الدوت نت (.NET)، واحدة من أروع المكتبات لتطبيق هذا النمط وغيره هي Polly. دعونا نرى مثالًا مبسطًا بلغة C# لكيفية تغليف استدعاء API باستخدام قاطع الدائرة.


// أولاً، قم بتعريف سياسة قاطع الدائرة
// يمكن تعريفها مرة واحدة عند بدء تشغيل التطبيق
var circuitBreakerPolicy = Policy
    .Handle<HttpRequestException>() // حدد نوع الأخطاء التي ستؤثر على القاطع
    .Or<TimeoutRejectedException>() // يمكن إضافة أنواع أخرى من الأخطاء
    .CircuitBreakerAsync(
        exceptionsAllowedBeforeBreaking: 5, // عدد الأخطاء المتتالية المسموح بها قبل فتح الدائرة
        durationOfBreak: TimeSpan.FromSeconds(60), // مدة فتح الدائرة (60 ثانية)
        onBreak: (ex, breakDelay) => 
        {
            // كود يتم تنفيذه عند فتح الدائرة (للتسجيل أو التنبيه)
            Console.WriteLine($".قاطع الدائرة فتح! سيبقى مفتوحًا لمدة {breakDelay.TotalSeconds} ثانية");
        },
        onReset: () => 
        {
            // كود يتم تنفيذه عند إغلاق الدائرة مجددًا
            Console.WriteLine("قاطع الدائرة أغلق. عادت الأمور إلى طبيعتها.");
        },
        onHalfOpen: () => 
        {
            // كود يتم تنفيذه عند الدخول في حالة شبه مفتوحة
            Console.WriteLine("قاطع الدائرة في حالة شبه مفتوحة، سيتم إرسال طلب تجريبي.");
        }
    );

// ثانيًا، استخدم السياسة عند استدعاء الخدمة
try
{
    var response = await circuitBreakerPolicy.ExecuteAsync(async () =>
    {
        // هذا الكود هو المحمي بواسطة قاطع الدائرة
        Console.WriteLine("محاولة استدعاء الخدمة الخارجية...");
        var client = new HttpClient();
        return await client.GetStringAsync("http://api.example.com/data");
    });

    // في حال النجاح، تعامل مع الاستجابة
    Console.WriteLine("تم استدعاء الخدمة بنجاح.");
}
catch (BrokenCircuitException)
{
    // هذا الخطأ يظهر فورًا إذا كانت الدائرة مفتوحة
    // لا يتم محاولة استدعاء الخدمة الخارجية أصلًا
    Console.WriteLine("فشل سريع: الدائرة مفتوحة. لن يتم استدعاء الخدمة.");
    // هنا يمكنك تطبيق منطق الـ Fallback (الخطة ب)
}
catch (Exception ex)
{
    // أي أخطاء أخرى قد تحدث أثناء الاستدعاء
    Console.WriteLine($"حدث خطأ أثناء استدعاء الخدمة: {ex.GetType().Name}");
}

كما ترى، الكود واضح جدًا. نحن نعرّف “سياسة” تحدد شروط فتح وإغلاق الدائرة، ثم نستخدمها لتنفيذ الكود الحساس. هذا يفصل منطق المرونة (resilience logic) عن منطق العمل (business logic) بشكل أنيق.

نصائح من مطبخ أبو عمر: ما بعد قاطع الدائرة

تطبيق النمط هو مجرد البداية. من خلال تجربتنا، تعلمت بعض الدروس التي أود مشاركتها معكم:

1. قاطع الدائرة لا يعمل وحيدًا

قاطع الدائرة هو جزء من فريق. لكي يكون فعالًا، يجب أن تدمجه مع أنماط أخرى:

  • نمط إعادة المحاولة (Retry): قبل أن يعتبر القاطع أن المحاولة فاشلة، قد يكون من المفيد إعادة المحاولة مرة أو مرتين، خاصة مع الأخطاء المؤقتة (transient faults). مكتبة Polly تجعل من السهل دمج السياستين معًا.
  • نمط المهلة الزمنية (Timeout): لا تجعل خدمتك تنتظر إلى الأبد. حدد مهلة زمنية قصيرة لكل استدعاء خارجي. إذا لم يأتِ الرد خلال هذه المهلة، اعتبر المحاولة فاشلة.
  • نمط الخطة البديلة (Fallback): ماذا يحدث عندما تفتح الدائرة؟ هل تعرض رسالة خطأ للمستخدم؟ هذا سيء. الأفضل هو أن يكون لديك خطة بديلة، مثل عرض بيانات قديمة من الكاش (cache) أو عرض قيمة افتراضية.

2. المراقبة والإنذار (Monitoring and Alerting)

مش منطق القاطع يفصل ويفتح وإحنا مش عارفين! من الضروري جدًا أن يكون لديك نظام مراقبة يخبرك بحالة قواطع الدائرة في نظامك. يجب أن تصلك تنبيهات فورية عند فتح دائرة لخدمة حرجة. هذا يساعدك على اكتشاف المشاكل بشكل استباقي قبل أن يشعر بها المستخدم.

3. ضبط الإعدادات بحكمة (Fine-Tuning Configuration)

الأرقام التي تضعها في إعدادات قاطع الدائرة (عدد المحاولات، مدة الفتح) ليست أرقامًا مقدسة. هي تعتمد بشكل كبير على سياق الخدمة.

  • خدمة حرجة (مثل الدفع): قد تحتاج إلى قاطع دائرة حساس جدًا يفتح بسرعة ولكنه يحاول التعافي بسرعة أيضًا.
  • خدمة غير أساسية (مثل التوصيات): يمكن أن يكون القاطع أكثر تساهلاً، وقد تكون مدة الفتح أطول.

لا تخف من التجربة وتعديل هذه الأرقام بناءً على أداء نظامك الفعلي.

الخلاصة: من الفوضى إلى المرونة Resilience

الانتقال من نظام ينهار بالكامل بسبب فشل بسيط إلى نظام يتعامل مع الفشل برشاقة لم يكن مجرد تحديث تقني، بل كان تغييرًا في طريقة تفكيرنا كفريق. تعلمنا أن نبني أنظمة تتوقع الفشل وتتعامل معه، بدلاً من أنظمة تفترض أن كل شيء سيعمل بشكل مثالي طوال الوقت.

نمط قاطع الدائرة كان حجر الأساس في هذه الرحلة. لقد أعطانا الأداة لعزل الأخطاء، وحماية مواردنا، ومنح أنظمتنا القدرة على الشفاء الذاتي. والأهم من ذلك كله، أعاد لنا ليالي نومنا الهادئة.

نصيحتي الأخيرة لك: لا تنتظر حتى تحترق دارك لتشتري طفاية حريق. ابدأ بتطبيق أنماط المرونة مثل قاطع الدائرة اليوم، لأن الفشل ليس “إذا” حدث، بل “متى” سيحدث. ما فيه أحلى من نظام صامد ومستقر! 😉

أبو عمر

سجل دخولك لعمل نقاش تفاعلي

كافة المحادثات خاصة ولا يتم عرضها على الموقع نهائياً

آراء من النقاشات

لا توجد آراء منشورة بعد. كن أول من يشارك رأيه!

آخر المدونات

التكنلوجيا المالية Fintech

كانت قراراتنا الائتمانية صندوقاً أسود: كيف أنقذنا ‘الذكاء الاصطناعي القابل للتفسير’ (XAI) من جحيم التحيز والشكاوى التنظيمية؟

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

16 مايو، 2026 قراءة المزيد
البنية التحتية وإدارة السيرفرات

كانت أعطالنا تباغتنا في منتصف الليل: كيف أنقذنا Prometheus من جحيم المراقبة التفاعلية؟

أشارككم قصتي، يا جماعة، من ليالي السهر الطويلة أمام شاشات السيرفرات المحترقة، إلى راحة البال التي منحنا إياها نظام Prometheus. هذه ليست مجرد مقالة تقنية،...

16 مايو، 2026 قراءة المزيد
ادارة الفرق والتنمية البشرية

طلبات الدمج تموت في الانتظار: كيف أنقذ “ميثاق مراجعة الكود” فريقنا من جحيم التأخير والجدل؟

أتذكر ذلك اليوم جيداً، طلب دمج (Pull Request) عالق لأسبوع، ونقاش حاد بين اثنين من أفضل المبرمجين حول تفصيل بسيط. كانت هذه هي القشة التي...

16 مايو، 2026 قراءة المزيد
اختبارات الاداء والجودة

كانت تحديثاتنا تكسر التصميم: كيف أنقذنا ‘اختبار التراجع البصري’ من جحيم الأخطاء المرئية؟

أشارككم قصة حقيقية من قلب المعركة البرمجية، وكيف تحولنا من فوضى الأخطاء المرئية بعد كل تحديث إلى ثقة وهدوء بفضل اختبارات التراجع البصري (Visual Regression...

16 مايو، 2026 قراءة المزيد
أتمتة العمليات

كان مطورنا الجديد ينتظر أياماً: كيف أنقذتنا ‘أتمتة إعداد البيئة’ من جحيم الأسبوع الأول الضائع؟

أتذكر جيداً كيف كان انضمام مطور جديد للفريق يعني بداية أسبوع من المعاناة والإحباط. في هذه المقالة، أسرد لكم قصة حقيقية حول كيف أنقذتنا أتمتة...

15 مايو، 2026 قراءة المزيد
نصائح برمجية

كانت إعادة المحاولة تدمر بياناتنا: كيف أنقذتنا ‘اللامتناهية’ (Idempotency) من جحيم العمليات المكررة؟

في ليلة لم أنم فيها، كانت أنظمتنا المالية تنهار بسبب عمليات دفع متكررة. أشارككم اليوم قصة كيف أنقذنا مفهوم "اللامتناهية" (Idempotency) من كارثة محققة، وكيف...

15 مايو، 2026 قراءة المزيد
​معمارية البرمجيات

كانت خدماتنا تتحدث في نفس الوقت: كيف أنقذتنا ‘المعمارية القائِمَة على الأحداث’ (EDA) من جحيم الاقتران المحكم؟

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

15 مايو، 2026 قراءة المزيد
ذكاء اصطناعي

كانت نماذجنا تموت بصمت: كيف أنقذتنا ‘مراقبة تعلم الآلة’ (ML Monitoring) من كارثة التنبؤات الفاسدة؟

أشارككم قصة حقيقية من الميدان، حين كادت نماذج الذكاء الاصطناعي التي بنيناها بجهد أن تنهار بصمت. اكتشفوا معنا ما هي "مراقبة تعلم الآلة" (ML Monitoring)،...

15 مايو، 2026 قراءة المزيد
البودكاست