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

ليلة الإطلاق التي كادت أن تكون الأخيرة…

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

وفجأة… بدأت التنبيهات تصرخ كالمجانين. “System Unresponsive”. “High Latency”. “503 Service Unavailable”. تجمد الدم في عروقي. لوحة المراقبة الرئيسية تحولت من اللون الأخضر المريح إلى اللون الأحمر المرعب. الموقع بأكمله توقف عن العمل.

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

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

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

جحيم الأعطال المتتالية: لما بتوقع خدمة، بتوقّع الكل معها

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

ببساطة، يحدث ما يسمى بـ “العطل المتتالي” أو (Cascading Failure). الخدمة المتعطلة تتسبب في تعطل الخدمات التي تعتمد عليها، والتي بدورها تتسبب في تعطل خدمات أخرى، وهكذا، مثل أحجار الدومينو التي تتساقط واحداً تلو الآخر حتى ينهار كل شيء.

المشكلة التقنية تكمن في أن الخدمة الطالبة (Calling Service) تستمر في إرسال الطلبات إلى الخدمة المتعطلة، وتستهلك مواردها (مثل الـ threads والذاكرة واتصالات الشبكة) في انتظار ردود لن تصل. بعد فترة قصيرة، تستنفد الخدمة الطالبة كل مواردها وتصبح غير قادرة على خدمة أي طلبات أخرى، حتى لو كانت تلك الطلبات لا علاقة لها بالخدمة المتعطلة. وهكذا ينتشر “المرض” في النظام بأكمله.

طوق النجاة: نمط “قاطع الدائرة” (Circuit Breaker)

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

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

الحالة المغلقة (Closed): التشغيل الطبيعي

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

الحالة المفتوحة (Open): “وقف، في مشكلة!”

عندما يتجاوز عدد الأخطاء حداً معيناً خلال فترة زمنية محددة (مثلاً، 10 أخطاء في الدقيقة الأخيرة)، “يفصل” قاطع الدائرة ويتحول إلى الحالة “المفتوحة”.

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

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

الحالة نصف المفتوحة (Half-Open): “نجرّب بحذر”

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

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

يلا نبرمج: تطبيق قاطع الدائرة عمليًا

الكلام النظري جميل، لكن دعونا نرى كيف يمكن تطبيق هذا عملياً. من أشهر المكتبات التي تسهل تطبيق هذا النمط هي مكتبة Polly في عالم .NET/C#، ومكتبة Resilience4j في عالم Java.

المثال: خدمة جلب توصيات المنتجات (باستخدام C# و Polly)

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

// هذا الكود هش وعرضة للمشاكل!
public async Task<List<Product>> GetRecommendationsAsync(string productId)
{
    // هذا الاتصال قد يستغرق وقتاً طويلاً أو يفشل
    var response = await _httpClient.GetAsync($"/recommendations/{productId}");
    response.EnsureSuccessStatusCode();
    
    return await response.Content.ReadFromJsonAsync<List<Product>>();
}

الآن، لنقم بإضافة قاطع الدائرة باستخدام Polly.

تطبيق النمط باستخدام مكتبة Polly في C#

أولاً، سنقوم بتعريف “سياسة” (Policy) قاطع الدائرة عند إعداد خدماتنا (عادة في ملف `Program.cs` أو `Startup.cs`):

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

// إضافة HttpClient مع السياسة
builder.Services.AddHttpClient<IRecommendationService, RecommendationService>()
    .AddPolicyHandler(circuitBreakerPolicy);

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

// الكود الآن مرن أكثر
public async Task<List<Product>> GetRecommendationsAsync(string productId)
{
    try
    {
        var response = await _httpClient.GetAsync($"/recommendations/{productId}");
        response.EnsureSuccessStatusCode();
        
        return await response.Content.ReadFromJsonAsync<List<Product>>();
    }
    catch (BrokenCircuitException)
    {
        // الدائرة مفتوحة! لا تحاول الاتصال مرة أخرى.
        // قم بتنفيذ منطق الاستجابة البديلة (Fallback)
        _logger.LogWarning("Circuit is open! Returning fallback recommendations.");
        return GetFallbackRecommendations(); // مثلاً، إرجاع قائمة توصيات محفوظة مسبقاً أو فارغة
    }
    catch (HttpRequestException ex)
    {
        // خطأ في الاتصال (قد يكون أحد الأخطاء التي أدت لفتح الدائرة)
        _logger.LogError(ex, "Failed to fetch recommendations.");
        return GetFallbackRecommendations();
    }
}

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

نصائح من خبرة أبو عمر: كيف تستخدم قاطع الدائرة كالمحترفين

تطبيق النمط هو البداية فقط. إليك بعض النصائح من قلب الميدان لتصبح محترفاً في استخدامه:

1. لا تكتفِ بقاطع الدائرة وحده!

قاطع الدائرة جزء من عائلة أنماط المرونة (Resiliency Patterns). للحصول على أفضل النتائج، ادمجه مع أنماط أخرى:

  • نمط إعادة المحاولة (Retry): للمشاكل العابرة، قد تنجح المحاولة الثانية أو الثالثة. استخدمه بحذر مع قاطع الدائرة.
  • نمط المهلة (Timeout): لا تجعل خدمتك تنتظر إلى الأبد. حدد مهلة قصيرة ومعقولة للاتصالات الخارجية.
  • نمط العزل (Bulkhead): اعزل الموارد المخصصة لكل اتصال خارجي، حتى لا يؤثر فشل اتصال واحد على الآخرين. “هذول زي فريق الحماية الشخصي للخدمة تبعتك”.

2. الاستجابة البديلة (Fallback) هي سر النجاح

ماذا تفعل عندما تكون الدائرة مفتوحة؟ إرجاع رسالة خطأ عامة هو أسوأ خيار. تجربة المستخدم هي الأهم. جهّز دائماً “خطة ب”:

  • بيانات مخبأة (Cached Data): هل يمكنك عرض آخر بيانات ناجحة حصلت عليها من الخدمة؟
  • قيمة افتراضية: هل يمكنك إرجاع قائمة فارغة أو بيانات عامة لا تكسر واجهة المستخدم؟
  • منطق مبسط: هل يمكنك تنفيذ نسخة مبسطة جداً من العملية محلياً؟

3. المراقبة والتنبيهات (Monitoring & Alerting)

قاطع الدائرة الذي يعمل في صمت هو قنبلة موقوتة. يجب أن تعرف متى تُفتح الدوائر في نظامك. قم بإعداد لوحات مراقبة (Dashboards) وتنبيهات تخبرك فوراً عند انتقال قاطع من حالة “مغلقة” إلى “مفتوحة”. هذا هو أول مؤشر على وجود مشكلة في إحدى خدماتك ويجب التحقيق فيها.

4. اضبط إعداداتك بحكمة

لا يوجد رقم سحري لعدد الأخطاء المسموح بها أو مدة فتح الدائرة. هذه الإعدادات تعتمد بشكل كبير على سياق الخدمة:

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

ابدأ بقيم تقديرية، ثم راقب أداء النظام وقم بتعديل هذه الأرقام بناءً على البيانات الحقيقية.

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

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

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

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

أبو عمر

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

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

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

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

آخر المدونات

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

مقابلاتي كانت كارثية: كيف أنقذني نموذج STAR من جحيم الأسئلة السلوكية والرفض المتكرر؟

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

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

التحقق من هوية عملائنا كان يستغرق أياماً: كيف أنقذني ‘التعرف الضوئي على الحروف’ (OCR) والذكاء الاصطناعي من جحيم الـ KYC اليدوي؟

كانت عملية التحقق من هوية العملاء الجدد (KYC) كابوسًا يدويًا يستنزف أيامًا من وقت فريقي. في هذه المقالة، أشارككم قصتي كـ "أبو عمر" وكيف حوّلنا...

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

فريقنا كان ينهار مع كل استقالة: كيف أنقذتني ‘كتيبات التشغيل’ (Playbooks) من جحيم فقدان المعرفة؟

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

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

تغطية اختباراتي 100% لكن الكود كان هشًا: كيف أنقذني الاختبار الطفري (Mutation Testing) من جحيم الثقة الزائفة؟

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

5 أبريل، 2026 قراءة المزيد
أتمتة العمليات

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

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

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

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

أشارككم قصتي مع نظام برمجي كاد أن ينهار بسبب التشابك والاقتران المحكم بين خدماته. اكتشفوا كيف كانت المعمارية الموجهة بالأحداث (EDA) طوق النجاة الذي حوّل...

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