نمط قاطع الدائرة: كيف نجا نظامنا من جحيم الانهيارات المتسلسلة؟

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

بتذكرها زي كإنها امبارح. كانت ليلة خميس، الشباب في الفريق شبه خلصوا شغلهم وبجهزوا حالهم للويكند. الأجواء كانت هادية، وأنا كنت براجع آخر التحديثات قبل ما أروح. وفجأة، بدون سابق إنذار، بلشت التنبيهات توصل على قنوات المراقبة زي المطر. “Service Unreachable”, “Request Timeout”, “503 Service Unavailable”… الدنيا قامت وقعدت!

خدمة الدفع، وهي خدمة خارجية بنتعامل معها، قررت تاخذ إجازة مبكرة وتعطلت. المشكلة ما كانت هون، المشكلة إنه نظامنا، اللي مبني على معمارية الخدمات المصغرة (Microservices)، بلش ينهار زي أحجار الدومينو. خدمة الطلبات (Orders Service) بتحاول تتصل بخدمة الدفع وبتستنى… وبتستنى… لحد ما تفشل. وكل طلب جديد بيجي بيفتح اتصال جديد وبضل يستنى، وهيك لحد ما استهلكنا كل الموارد المتاحة. بعدها، خدمة المستخدمين (Users Service) اللي بتحتاج بيانات من خدمة الطلبات عشان تعرض تاريخ المشتريات، كمان هي علّقت. وخلال دقائق، الموقع والتطبيق كله صار يعطي أخطاء للمستخدمين. صرنا زي اللي قاعد في قارب صغير والمي بتدخل عليه من كل مكان، وكل ما نسكر ثقب، بيفتح عشرة غيره.

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

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

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

المشكلة، اللي بسميها “تأثير الدومينو” أو الانهيار المتسلسل (Cascading Failure)، بتصير لما خدمة وحدة (خلينا نسميها “خدمة B”) تتعطل أو تصير بطيئة جداً. أي خدمة تانية (ولتكن “خدمة A”) بتحاول تتصل فيها راح تعلق وهي بتستنى رد. مع كل طلب جديد لـ “خدمة A”، بينفتح اتصال جديد أو thread جديد بيضل محجوز وهو بيستنى “خدمة B” اللي عمرها ما راح ترد.

بسرعة كبيرة، كل موارد “خدمة A” (مثل الـ connection pools والـ thread pools) بتنتهي، وبتصير هي نفسها غير قادرة على استقبال أي طلبات جديدة، حتى لو كانت هاي الطلبات ما إلها علاقة بـ “خدمة B” أصلًا. وبهيك، “خدمة A” بتصير هي نفسها مصدر للمشكلة وبتنقل العدوى للخدمات اللي بتعتمد عليها. هذا هو جحيم الأخطاء المتتالية.

قد تفكر: “بسيطة، بنحط Timeout قصير وخلص!”. هذا حل جزئي ولكنه غير كافٍ. حتى مع وجود Timeout، طلبك راح يضل يستنى ويستهلك موارد لمدة ثانية أو ثانيتين. اضرب هذا الرقم في آلاف الطلبات في الدقيقة، وستجد أنك ما زلت تستنزف مواردك على خدمة ميتة.

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

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

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

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

هاي هي الحالة الطبيعية. القاطع بكون “مغلق”، والطلبات بتمر من خلاله بشكل عادي إلى الخدمة المطلوبة. في هاي الحالة، القاطع براقب بصمت عدد مرات الفشل (مثل حالات الـ timeout أو أخطاء الشبكة). إذا زاد عدد الأخطاء عن حد معين خلال فترة زمنية محددة (مثلاً، أكثر من 10 أخطاء في الدقيقة)، القاطع “بفصل” وبينتقل للحالة المفتوحة.

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

لما القاطع “يفتح”، هون بصير السحر. لأي طلب جديد بحاول يوصل للخدمة العطلانة، القاطع ما بحاول حتى يتصل فيها. فورًا، وبدون أي انتظار، برجع خطأ للخدمة الطالبة (fail-fast). هذا الإجراء له فائدتين عظيمتين:

  1. حماية نظامك: بمنع استنزاف الموارد في محاولات فاشلة، وبالتالي بحمي الخدمة الطالبة وباقي النظام من الانهيار.
  2. إعطاء فرصة للخدمة المتعثرة: بوقف الضغط الهائل عليها، بيعطيها فرصة ومساحة “تتنفس” وتتعافى بدون ما يتم قصفها بآلاف الطلبات.

القاطع بضل في هاي الحالة لفترة زمنية محددة مسبقًا (مثلاً، 30 ثانية)، وبعدها بينتقل للحالة التالية.

حالة نصف المفتوحة (Half-Open State)

بعد انتهاء فترة الانتظار في الحالة المفتوحة، القاطع بصير حذر. ما برجع يفتح الباب على مصراعيه، لا. بينتقل لحالة “نصف مفتوحة”، وبيسمح لطلب واحد فقط بالمرور للخدمة البعيدة. هذا الطلب هو بمثابة “جس نبض”.

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

“ورجينا عرض كتافك”: تطبيق عملي لقاطع الدائرة

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

تخيل عنا دالة بتحاول تتصل بخدمة خارجية:


// هذا هو الكود "قبل" استخدام قاطع الدائرة
public async Task<string> CallExternalService()
{
    // تخيل أن هذا الاتصال يفشل بشكل متكرر
    var response = await _httpClient.GetStringAsync("http://api.external-service.com/data");
    return response;
}

الآن، لنقم بتغليف هذا الاستدعاء باستخدام سياسة قاطع الدائرة من Polly:


using Polly;
using Polly.CircuitBreaker;

// ... في مكان تهيئة الخدمات (Startup.cs or Program.cs) ...

// 1. تعريف سياسة قاطع الدائرة
// السياسة: افتح الدائرة إذا حدث 5 أخطاء متتالية
// اترك الدائرة مفتوحة لمدة 30 ثانية
AsyncCircuitBreakerPolicy<HttpResponseMessage> circuitBreakerPolicy =
    Policy.HandleResult<HttpResponseMessage>(r => !r.IsSuccessStatusCode) // تعامل مع الأخطاء
          .Or<HttpRequestException>() // أو استثناءات الشبكة
          .CircuitBreakerAsync(
              exceptionsAllowedBeforeBreaking: 5, // عدد الأخطاء المسموح بها قبل فتح الدائرة
              durationOfBreak: TimeSpan.FromSeconds(30) // مدة بقاء الدائرة مفتوحة
          );


// 2. استخدام السياسة عند إجراء الاستدعاء
public async Task<string> CallExternalServiceWithBreaker()
{
    try
    {
        // ExecuteAsync يغلف استدعاء الشبكة بالسياسة التي عرفناها
        var response = await circuitBreakerPolicy.ExecuteAsync(() => 
            _httpClient.GetAsync("http://api.external-service.com/data")
        );

        if (response.IsSuccessStatusCode)
        {
            return await response.Content.ReadAsStringAsync();
        }

        // في حال لم تكن الاستجابة ناجحة ولكن الدائرة لم تفتح بعد
        return "Fallback: Service returned an error.";
    }
    catch (BrokenCircuitException) // هذا الاستثناء يتم إلقاؤه عندما تكون الدائرة مفتوحة
    {
        // الدائرة مفتوحة! لا تحاول الاتصال، فقط أرجع قيمة بديلة فورًا
        // ممكن تسجل log هنا عشان تعرف إن الدائرة فتحت
        return "Fallback: Service is unavailable, circuit is open.";
    }
    catch (HttpRequestException) // أخطاء الشبكة الأخرى
    {
        return "Fallback: Network issue.";
    }
}

لاحظ كيف تغير المنطق. بدلًا من الفشل العشوائي، أصبح لدينا سلوك منظم. عندما تفشل الخدمة 5 مرات متتالية، ستفتح الدائرة. وأي استدعاء للدالة خلال الـ 30 ثانية التالية لن يصل إلى الشبكة أصلًا، بل سيدخل مباشرة في `catch (BrokenCircuitException)` ويعيد رسالة بديلة فورًا.

نصيحة من أبو عمر: عند تطبيق قاطع الدائرة، لا تكتفِ بإرجاع خطأ. فكر دائمًا في “استراتيجية بديلة” (Fallback). هل يمكنك إرجاع بيانات من الكاش؟ هل يمكنك إرجاع قيمة افتراضية؟ هل يمكنك عرض رسالة لطيفة للمستخدم تخبره أن جزءًا من الخدمة غير متاح مؤقتًا؟ الفشل السريع مع بديل مفيد أفضل ألف مرة من الانتظار الطويل ثم الفشل الكامل.

الخلاصة: من الفوضى إلى الصمود 💪

تبني نمط قاطع الدائرة كان نقطة تحول في طريقة تفكيرنا وتصميمنا للأنظمة. لم نعد نعيش في خوف دائم من فشل خدمة واحدة قد تدمر كل شيء. لقد انتقلنا من عقلية “أتمنى ألا يحدث خطأ” إلى عقلية “تصميم الأنظمة لتتحمل الأخطاء”.

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

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

في البرمجة، كما في الحياة، الأمر لا يتعلق بتجنب السقوط، بل بكيفية النهوض بعد كل عثرة. نمط قاطع الدائرة هو ما يعلم أنظمتنا كيف تنهض بنفسها. ودمتم سالمين.

أبو عمر

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

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

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

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

آخر المدونات

الحوسبة السحابية

من التوقف التام إلى النجاة: كيف أنقذتنا استراتيجية “الضوء المرشد” (Pilot Light) يوم انقطعت السحابة؟

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

4 يونيو، 2026 قراءة المزيد
التوظيف وبناء الهوية التقنية

كانت مهمتي البرمجية للاختبار مجرد كود: كيف أنقذني توثيق القرارات من جحيم الصمت بعد المقابلة؟

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

4 يونيو، 2026 قراءة المزيد
التكنلوجيا المالية Fintech

من الانتظار لأيام إلى الدفع في ثوانٍ: كيف أنقذتنا شبكات الدفع الفوري من جحيم التحويلات البنكية؟

أسرد لكم من واقع تجربتي كـ "أبو عمر"، كيف عانينا من بطء وتكلفة التحويلات البنكية الدولية، وكيف جاءت شبكات الدفع الفوري ومعيار ISO 20022 لتكون...

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

كان كل خادم لدينا ‘ندفة ثلج’ فريدة: كيف أنقذنا ‘الكود كبنية تحتية’ (IaC) من جحيم الانجراف اليدوي؟

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

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

كانت تغطية الاختبارات 100% لكن الأخطاء تتسرب: كيف أنقذنا “الاختبار الطفري” من جحيم الثقة الزائفة؟

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

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

من كوابيس الحالة المفقودة إلى الأتمتة المنظمة: كيف أنقذتنا محركات سير العمل (Workflow Engines)؟

في هذه المقالة، أشارككم قصة حقيقية عن معاناة فريقنا مع العمليات الطويلة والمعقدة في الأنظمة الموزعة، وكيف كانت محركات تنسيق سير العمل (Workflow Engines) هي...

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