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

ليلة الخميس التي لا تُنسى: قصة انهيار وشيك

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

فجأة، بدأت التنبيهات تنهال علينا كالمطر. “High CPU Usage”, “Increased Response Time”, “5xx Server Errors”. نظرنا إلى بعضنا البعض، وبدأت علامات القلق تظهر على وجوهنا. فتحت الموقع لأرى ما يحدث، فكان بطيئاً جداً، بطيئاً لدرجة الشلل التام. الصفحات لا تفتح، وعمليات إضافة المنتجات للسلة تفشل. الوضع صار، زي ما بنحكي عنا، “طنجرة ضغط” على وشك الانفجار.

بدأنا عملية التحقيق المحمومة. فحصنا قواعد البيانات، خوادم التطبيقات، موازِن الأحمال (Load Balancers)… كل شيء يبدو سليماً من ناحية الموارد. لكن النظام بأكمله كان يختنق. بعد دقائق طويلة من البحث تحت الضغط، وجدنا الخيط الأول: خدمة صغيرة، تبدو غير مهمة، وهي “خدمة التوصيات” (Recommendation Service). هذه الخدمة مسؤولة عن عرض منتجات مقترحة للمستخدمين، وكانت تعتمد على واجهة برمجية لطرف ثالث (Third-party API).

المشكلة؟ هذه الواجهة الخارجية بدأت فجأة بالتباطؤ الشديد، ثم توقفت عن الاستجابة تماماً. كانت كل طلباتنا إليها تنتهي بـ “Timeout”. لكن كيف لخدمة صغيرة كهذه أن تسقط النظام بأكمله؟

ما هي الكارثة المتتالية (Cascading Failure)؟

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

هذا بالضبط ما حدث في نظامنا المبني على الخدمات المصغرة (Microservices).

  1. الخادم الرئيسي لدينا (لنسميه “الخدمة الأم”) يستقبل طلب المستخدم لعرض صفحة المنتج.
  2. كجزء من عملية تجهيز الصفحة، تقوم “الخدمة الأم” باستدعاء “خدمة التوصيات”.
  3. “خدمة التوصيات” بدورها تحاول الاتصال بالـ API الخارجي الفاشل.
  4. الـ API الخارجي لا يستجيب، فتبقى “خدمة التوصيات” منتظرة حتى انتهاء المهلة الزمنية (Timeout)، والتي كانت للأسف مضبوطة على 30 ثانية!
  5. طوال هذه الثلاثين ثانية، يبقى الخيط (Thread) الذي يعالج طلب المستخدم في “الخدمة الأم” محجوزاً ومعلقاً، في انتظار رد لن يأتي أبداً.

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

الحل السحري: نمط قاطع الدائرة (Circuit Breaker Pattern)

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

هنا يأتي دور البطل في قصتنا: نمط “قاطع الدائرة”.

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

يعمل قاطع الدائرة من خلال ثلاث حالات:

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

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

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

    عندما يكون القاطع في هذه الحالة، فإنه يرفض جميع الطلبات الموجهة للخدمة الفاشلة فوراً ودون محاولة الاتصال بها. بدلاً من أن ينتظر تطبيقك ويستهلك الموارد، يتلقى خطأً فورياً (أو ما يسمى بـ “الفشل السريع” – Fail Fast). هذا يحمي تطبيقك من الاستنزاف ويسمح له بالتعامل مع الفشل برشاقة (مثلاً، عن طريق عرض بيانات بديلة). بعد فترة زمنية محددة، ينتقل القاطع إلى الحالة نصف المفتوحة.

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

    في هذه الحالة، يسمح القاطع بمرور طلب واحد فقط “كتجربة” إلى الخدمة التي كانت فاشلة.

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

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

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

الخطوة الأولى: إضافة المكتبة اللازمة

أولاً، قمنا بإضافة مكتبة Polly إلى مشروعنا. يمكن فعل ذلك بسهولة عبر مدير الحزم NuGet.

Install-Package Polly

الخطوة الثانية: تهيئة سياسة قاطع الدائرة

بعد ذلك، قمنا بتعريف “سياسة” قاطع الدائرة. في هذه السياسة، نحدد الشروط التي سيفتح القاطع بناءً عليها.


// مثال باستخدام مكتبة Polly في C#
using Polly;
using Polly.CircuitBreaker;

// تعريف سياسة قاطع الدائرة
// ستفتح الدائرة بعد 3 محاولات فاشلة متتالية
// وستبقى مفتوحة لمدة 30 ثانية
var circuitBreakerPolicy = Policy
    .Handle<HttpRequestException>() // تحديد نوع الأخطاء التي سيتعامل معها
    .CircuitBreakerAsync(
        exceptionsAllowedBeforeBreaking: 3, 
        durationOfBreak: TimeSpan.FromSeconds(30) 
    );

في هذا الكود، قلنا للقاطع: “راقب الأخطاء من نوع HttpRequestException. إذا حدث هذا الخطأ 3 مرات متتالية، افتح الدائرة لمدة 30 ثانية”.

الخطوة الثالثة: استخدام السياسة عند استدعاء الخدمة

الآن، كل ما علينا فعله هو “تغليف” الكود الذي يستدعي الخدمة بهذه السياسة.


try
{
    // ExecuteAsync يقوم بتنفيذ الكود مع تطبيق سياسة قاطع الدائرة
    var response = await circuitBreakerPolicy.ExecuteAsync(async () =>
    {
        // هنا يتم استدعاء الخدمة التي قد تفشل
        Console.WriteLine("Trying to call the recommendation service...");
        return await httpClient.GetAsync("http://api.recommendations.com/products");
    });
    
    // في حال النجاح، تعامل مع الاستجابة
    Console.WriteLine("Service call successful!");
}
catch (BrokenCircuitException) // هذا هو الاستثناء الذي يرميه Polly عندما تكون الدائرة مفتوحة
{
    // الدائرة مفتوحة! لا تحاول الاتصال بالخدمة
    // يمكننا هنا إرجاع بيانات افتراضية أو رسالة خطأ سريعة
    Console.WriteLine("Circuit is open! Returning fallback data.");
    // return GetDefaultRecommendations(); // مثال: إرجاع توصيات افتراضية
}
catch (HttpRequestException ex)
{
    // هذا الخطأ يحدث عندما تفشل المحاولة والدائرة لا تزال مغلقة
    Console.WriteLine($"An error occurred while calling the service: {ex.Message}");
}

الجمال في هذا الكود يكمن في التعامل مع BrokenCircuitException. عندما تكون الدائرة مفتوحة، لن يتم تنفيذ الكود داخل ExecuteAsync أصلاً. بدلاً من ذلك، سيتم رمي هذا الاستثناء فوراً، مما يسمح لنا بالرد بسرعة فائقة بدلاً من الانتظار لثوانٍ طويلة.

نصائح أبو عمر الذهبية لتطبيق قاطع الدائرة

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

  • ابدأ بالخدمات الحرجة والأقل استقراراً: لست بحاجة لتطبيق النمط على كل استدعاء في نظامك. ابدأ بالخدمات التي تتصل بأطراف خارجية (Third-parties)، أو الخدمات الداخلية التي لديك شك في استقرارها.
  • هيّئ الإعدادات بحكمة: عدد الأخطاء المسموحة ومدة فتح الدائرة هي أرقام حساسة. خدمة دفع إلكتروني حرجة قد تحتاج لقاطع أكثر حساسية (يفصل بعد محاولتين مثلاً)، بينما خدمة جلب حالة الطقس يمكن أن تكون أكثر تسامحاً. “مش كل إشي إلو نفس المكيال”، فكّر في سياق كل خدمة.
  • لا تنسَ استراتيجية الاحتياط (Fallback): عندما تفتح الدائرة، ماذا يجب أن يفعل نظامك؟ لا تترك المستخدم معلقاً أو تعرض له صفحة خطأ قبيحة. أفضل حل هو توفير بديل: بيانات من الكاش (Cache)، أو بيانات افتراضية، أو حتى إخفاء الجزء المتعلق بالخدمة الفاشلة من واجهة المستخدم مؤقتاً.
  • المراقبة والتنبيهات ضرورية: يجب أن تعرف متى ولماذا تفتح الدوائر في نظامك. قم بإعداد تنبيهات (Alerts) لفريقك عند انتقال أي قاطع للحالة المفتوحة. هذا مؤشر واضح على وجود مشكلة حقيقية تحتاج إلى تحقيق فوري.
  • اجمع بين قاطع الدائرة والمهلة الزمنية (Timeout): قاطع الدائرة لا يغني عن وجود مهلة زمنية قصيرة ومنطقية. إذا كان تطبيقك ينتظر 60 ثانية قبل تسجيل الفشل، فإن قاطع الدائرة لن يكون فعالاً بالقدر الكافي. اضبط مهلة زمنية قصيرة (مثلاً 2-5 ثوانٍ) على استدعاءات الشبكة.

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

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

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

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

أبو عمر

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

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

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

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

آخر المدونات

التوظيف وبناء الهوية التقنية

سيرتي الذاتية عبرت فلتر الـ ATS لكنها فشلت أمام المدير التقني: كيف أعدت بناءها لتتحدث لغة المهندسين؟

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

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

لقد ‘هاجمت’ تطبيقي بنفسي عمداً: كيف كشفت لي ‘هندسة الفوضى’ نقاط الضعف التي لم تظهرها الاختبارات التقليدية

أشارككم قصة حقيقية حول إطلاق فاشل كاد أن يدمر سمعتنا، وكيف قادتنا هذه التجربة المريرة إلى تبني "هندسة الفوضى" (Chaos Engineering). اكتشفوا معنا كيف يمكن...

26 فبراير، 2026 قراءة المزيد
التوسع والأداء العالي والأحمال

عاصفة من الطلبات كادت أن تغرق تطبيقي: كيف أنقذتني طوابير الرسائل (Message Queues) من كارثة الجمعة السوداء؟

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

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

تحديث النظام القديم كان كابوساً: كيف ‘خنقت’ المونوليث تدريجياً بنمط Strangler Fig دون توقف الخدمة

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

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