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

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

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

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

بعد ساعات من البحث والتحليل تحت ضغط رهيب، لزقنا بالمكتب للساعة 2 بالليل، اكتشف زميل إلنا ملاحظة دقيقة: كل الأخطاء كانت بتبلش من عند “خدمة الطلبات” (Orders Service). بس الغريب إنه الخدمة نفسها ما عليها ضغط، ومواردها مستقرة. بعد ما حفرنا أعمق، لقينا المصيبة. خدمة الطلبات كانت بتحاول تتصل بـ “خدمة الدفع” (Payment Service) التابعة لجهة خارجية، وهالخدمة كانت بطيئة جداً وأحياناً ما بترد بالمرة. اللي كان يصير إنه كل طلب جديد في خدمة الطلبات كان يفتح “ثريد” (thread) جديد عشان يستنى رد خدمة الدفع. ومع كثرة الطلبات، كل الـ “ثريدات” المتاحة انحجزت وهي بتستنى رد ما رح يجي، والنظام كله “جَمّد” ومات إكلينيكياً. عطل بسيط في خدمة خارجية صغيرة كان زي الفيروس اللي شلّ نظامنا بالكامل. كان يوم أسود من قرن الخروب.

هنا كانت الصرخة: “لازم نلاقي حل لهالأعطال المتتالية (Cascading Failures)!”. ومن رحم هالمعاناة، ولد القرار بتبني نمط تصميمي غيّر طريقة تفكيرنا في بناء الأنظمة المرنة: نمط قاطع الدائرة أو الـ Circuit Breaker.

ما هي مشكلة الأعطال المتتالية (Cascading Failures)؟

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

  • خدمة الواجهة الأمامية (Frontend Service) تطلب بيانات من خدمة المستخدمين (Users Service).
  • خدمة المستخدمين بدورها تطلب بيانات الطلبات من خدمة الطلبات (Orders Service).
  • خدمة الطلبات تطلب تفاصيل الدفع من خدمة الدفع (Payment Service).

هذا يسمى “سلسلة الاستدعاءات” (Call Chain). الآن، تخيل لو خدمة الدفع (آخر حلقة في السلسلة) توقفت عن العمل أو أصبحت بطيئة جداً. ماذا سيحدث؟

  1. خدمة الطلبات سترسل طلبات إلى خدمة الدفع وتنتظر الرد. وبما أن الرد لا يأتي، ستبدأ مواردها (مثل اتصالات الشبكة، الذاكرة، الـ threads) بالنفاد.
  2. بعد فترة قصيرة، ستصبح خدمة الطلبات نفسها غير قادرة على استقبال أي طلبات جديدة لأن كل مواردها محجوزة.
  3. الآن، خدمة المستخدمين التي تعتمد على خدمة الطلبات ستبدأ بمواجهة نفس المشكلة. سترسل طلبات لخدمة الطلبات التي لا ترد، وستنفد مواردها هي الأخرى.
  4. وهكذا، ينتقل الفشل مثل الدومينو عبر السلسلة حتى يصل إلى الواجهة الأمامية، وفي النهاية ينهار النظام بأكمله.

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

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

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

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

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

حالات قاطع الدائرة الثلاث

يعمل قاطع الدائرة في ثلاث حالات رئيسية:

  1. مغلق (Closed): هذا هو الوضع الطبيعي. يتم توجيه جميع الطلبات إلى الخدمة الخارجية. يقوم القاطع بحساب عدد حالات الفشل. إذا سارت الأمور على ما يرام، يبقى القاطع في هذه الحالة. أما إذا تجاوز عدد حالات الفشل في فترة زمنية معينة حدًا معينًا (مثلاً، 50% من آخر 20 طلبًا فشلت)، ينتقل القاطع إلى حالة “مفتوح”.
  2. مفتوح (Open): في هذه الحالة، “فصل” القاطع. أي طلب جديد يتم رفضه فورًا دون محاولة الاتصال بالخدمة الخارجية. يتم إرجاع خطأ فوري للخدمة الطالبة. هذا يحمي نظامك من انتظار خدمة لا تستجيب. بعد فترة زمنية محددة (Cooldown period)، ينتقل القاطع إلى حالة “نصف مفتوح”.
  3. نصف مفتوح (Half-Open): هذه هي مرحلة الاختبار. يسمح القاطع بمرور طلب واحد فقط (أو عدد قليل جدًا) إلى الخدمة الخارجية.
    • إذا نجح هذا الطلب، يفترض القاطع أن الخدمة قد تعافت، وينتقل إلى حالة “مغلق” لتعود الأمور إلى طبيعتها.
    • إذا فشل هذا الطلب، يفترض القاطع أن المشكلة لا تزال قائمة، ويعود إلى حالة “مفتوح” ليبدأ فترة انتظار جديدة.

هذه الآلية الذكية تمنح النظام مرونة وقدرة على الشفاء الذاتي (Self-healing). بدلاً من الانهيار، يقوم النظام بعزل الجزء الفاشل وحماية نفسه.

مثال عملي بالكود (C# مع مكتبة Polly)

من أهم النصائح اللي بقدر أقدمها: لا تخترع العجلة من جديد! هناك مكتبات رائعة ومجربة تطبق هذا النمط وغيره من أنماط المرونة. في عالم الدوت نت (.NET)، تعتبر مكتبة Polly هي المعيار الذهبي.

لنفترض أن لدينا الكود التالي الذي يستدعي خدمة دفع خارجية:


// هذا الكود بدون قاطع دائرة - عرضة للفشل المتتالي
public async Task<PaymentResponse> ProcessPayment(PaymentRequest request)
{
    // httpClient يستدعي خدمة الدفع الخارجية
    var response = await _httpClient.PostAsJsonAsync("api/payments", request);
    response.EnsureSuccessStatusCode();
    return await response.Content.ReadFromJsonAsync<PaymentResponse>();
}

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


// تعريف سياسة قاطع الدائرة
var circuitBreakerPolicy = HttpPolicyExtensions
    .HandleTransientHttpError() // التعامل مع أخطاء الشبكة الشائعة
    .CircuitBreakerAsync(
        failureThreshold: 0.5, // افتح الدائرة إذا فشل 50% من الطلبات
        samplingDuration: TimeSpan.FromSeconds(60), // خلال آخر 60 ثانية
        minimumThroughput: 20, // يجب أن يكون هناك 20 طلبًا على الأقل في فترة العينة
        durationOfBreak: TimeSpan.FromSeconds(30) // اترك الدائرة مفتوحة لمدة 30 ثانية
    );

// ربط السياسة مع HttpClient الخاص بخدمة الدفع
builder.Services.AddHttpClient<IPaymentService, PaymentService>(client =>
{
    client.BaseAddress = new Uri("https://api.paymentservice.com/");
})
.AddPolicyHandler(circuitBreakerPolicy);

هذا كل شيء! الآن، أي استدعاء يتم عبر HttpClient المسجل بهذه الطريقة سيكون محميًا بقاطع الدائرة. الكود الذي يستدعي الخدمة لا يتغير، ولكن سلوك النظام عند الفشل تغير بشكل جذري.

شرح الإعدادات:

  • failureThreshold: 0.5: إذا فشلت 50% من الطلبات، تفتح الدائرة.
  • samplingDuration: TimeSpan.FromSeconds(60): يتم حساب نسبة الفشل بناءً على الطلبات في آخر 60 ثانية.
  • minimumThroughput: 20: لن تفتح الدائرة إلا إذا تم إجراء 20 طلبًا على الأقل خلال فترة العينة. هذا يمنع فتح الدائرة بسبب فشل عشوائي لطلب واحد أو اثنين.
  • durationOfBreak: TimeSpan.FromSeconds(30): عندما تفتح الدائرة، ستبقى مفتوحة لمدة 30 ثانية قبل الانتقال إلى حالة “نصف مفتوح”.

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

نصائح “أبو عمر” العملية

من خلال تجربتي في تطبيق هذا النمط في مشاريع مختلفة، جمعت لكم هالنصائح اللي من القلب:

  1. ابدأ بسيطًا واستخدم مكتبة جاهزة: مرة أخرى، لا تضيع وقتك في بناء قاطع دائرة من الصفر. مكتبات مثل Polly في C#، أو Resilience4j في Java، أو Hystrix (ولو أنه لم يعد يُطوّر بنشاط) تقدم حلولاً قوية ومختبرة.
  2. لا تكتفِ بقاطع الدائرة فقط: قاطع الدائرة يعمل بشكل أفضل مع أنماط أخرى مثل “إعادة المحاولة” (Retry). يمكنك تكوين سياسة لإعادة المحاولة 2-3 مرات مع فترة انتظار قصيرة بينها (Exponential Backoff)، وإذا فشلت كل المحاولات، عندها يتدخل قاطع الدائرة.
  3. جهز خطة بديلة (Fallback): طيب، قاطع الدائرة فتح، شو نعمل للمستخدم؟ هل نظهر له رسالة خطأ “قبيحة”؟ الأفضل هو توفير سلوك بديل. مثلاً، إذا فشلت خدمة توصيات المنتجات، يمكن عرض قائمة بالمنتجات الأكثر مبيعًا من الذاكرة المؤقتة (Cache). هذا يحسن تجربة المستخدم بشكل كبير.
  4. المراقبة والتنبيهات أهم من أي شيء: لازم تعرف متى وكيف تفتح قواطع الدائرة في نظامك. قم بإضافة سجلات (Logs) عند كل تغيير في حالة القاطع (من مغلق إلى مفتوح والعكس). قم بإعداد تنبيهات (Alerts) لفريقك عندما يفتح قاطع دائرة لخدمة حرجة. هذا يعطيك رؤية فورية عن صحة نظامك.
  5. اضبط إعداداتك بحكمة: الأرقام التي وضعتها في مثال الكود ليست مقدسة. يجب أن تعكس حساسية الخدمة. خدمة الدفع الحرجة قد تحتاج إلى قاطع دائرة أكثر حساسية من خدمة إرسال الإيميلات الترحيبية مثلاً.

الخلاصة: ابنِ أنظمة تتوقع الفشل

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

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

يعطيكم ألف عافية.

أبو عمر

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

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

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

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

آخر المدونات

برمجة وقواعد بيانات

كانت تحديثات قاعدة البيانات كابوساً: كيف أنقذتنا أدوات الترحيل (Migrations) من جحيم التعديلات اليدوية؟

هل عانيت يوماً من تحديث مخطط قاعدة البيانات يدوياً بين فريقك؟ أبو عمر يشارككم قصة حقيقية حول كيف غيّرت أدوات الترحيل (Migrations) طريقة عمل فريقه،...

17 مايو، 2026 قراءة المزيد
الشبكات والـ APIs

كانت خوادمنا تستجدي التحديثات: كيف أنقذتنا ‘خطاطيف الويب’ (Webhooks) من جحيم الاستقصاء المستمر (Polling)؟

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

17 مايو، 2026 قراءة المزيد
الحوسبة السحابية

كانت بنيتنا التحتية قصراً من رمال: كيف أنقذتنا “البنية التحتية ككود” (IaC) من جحيم البيئات المتضاربة؟

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

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

كان ملفي الشخصي مقبرة لمشاريع الدورات: كيف أنقذتني ‘المساهمة في المصادر المفتوحة’ من جحيم الرفض التلقائي؟

هل تشعر أن ملفك الشخصي على GitHub لا يعكس قدراتك الحقيقية؟ في هذه المقالة، أشاركك يا صديقي تجربتي الشخصية، أنا أبو عمر، وكيف انتقلت من...

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

كانت قاعدة بياناتنا تتوسل الرحمة: كيف أنقذنا التخزين المؤقت (Caching) من جحيم الاستعلامات البطيئة

قصة حقيقية من واقع العمل عن كيفية انهيار نظامنا تحت ضغط الاستعلامات المتكررة، وكيف كان التخزين المؤقت (Caching) هو طوق النجاة. مقالة عملية للمطورين تشرح...

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

كان التحقق من هوية عملائنا يستغرق أياماً: كيف أنقذنا الذكاء الاصطناعي (eKYC) من جحيم الإجراءات اليدوية؟

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

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

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

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

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

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

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

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