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

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

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

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

دخلنا في حالة طوارئ، والكل صار يركض زي المجنون يدور على سبب المشكلة. بعد ساعات من التوتر والضغط وتحليل السجلات (Logs)، اكتشفنا المصيبة. المشكلة ما كانت في الكود اللي كتبناه، ولا في أكبر خدمة عندنا. المصيبة كانت في خدمة صغيرة ومستقلة، خدمة طرف ثالث (Third-party API) بنستخدمها لتحويل أسعار العملات. هاي الخدمة، لسبب ما، توقفت عن الاستجابة وصارت طلباتنا إلها تنتهي بـ Timeout.

كل طلب لعرض صفحة منتج كان يستدعي هاي الخدمة. ولما الخدمة بطلت ترد، كل طلب صار ينتظر 30 ثانية كاملة قبل ما يفشل. خلال دقائق، كل الـ Threads في خوادمنا استُنْزِفت وهي معلقة بانتظار رد وهمي لن يأتي. النظام بأكمله اختنق وانهار. فشل خدمة صغيرة واحدة، تسبب في انهيار كارثي متسلسل (Cascading Failure) للنظام بأكمله. في هذيك الليلة، عرفت إنه بناء نظام قوي مش بس بكتابة كود نظيف، بل ببنائه ليصمد في وجه الفشل.

ومن رحم هذيك المعاناة، تعلمنا عن طوق النجاة اللي أنقذنا لاحقاً: نمط “قاطع الدائرة” أو الـ Circuit Breaker.

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

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

في عالم الخدمات المصغرة، الخدمات بتعتمد على بعضها البعض. خدمة المستخدمين (User Service) ممكن تستدعي خدمة الطلبات (Order Service)، اللي بدورها بتستدعي خدمة الدفع (Payment Service)، وهكذا. هذا الاعتماد المتبادل هو نقطة قوة وضعف في نفس الوقت.

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

المحاولة المستمرة لاستدعاء خدمة فاشلة لا تصلحها، بل هي وصفة أكيدة لتدمير نظامك بأكمله.

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

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

في البرمجة، قاطع الدائرة هو كائن (Object) بيجلس بين الخدمة اللي بتستدعي (Consumer) والخدمة اللي يتم استدعاؤها (Provider)، ويراقب حالة الاتصال. وله ثلاث حالات رئيسية:

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

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

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

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

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

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

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

هذا الأسلوب الذكي بيمنع ما يسمى بـ “Thundering Herd”، وهو هجوم عدد هائل من الطلبات على خدمة بدأت للتو بالتعافي، مما قد يؤدي لانهيارها مرة أخرى.

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

الخبر الجيد إنه مش مضطر تبني هذا النمط من الصفر. في مكتبات جاهزة وممتازة في معظم لغات البرمجة المشهورة.

  • Java/Kotlin: أشهر مكتبة حالياً هي Resilience4j. (كانت Hystrix من Netflix هي المسيطرة لكنها الآن في وضع الصيانة).
  • .NET/C#: مكتبة Polly هي المعيار الذهبي، رائعة وسهلة الاستخدام.
  • Python: مكتبة pybreaker خيار بسيط وفعال.
  • JavaScript/Node.js: مكتبة opossum تقوم بالمهمة على أكمل وجه.

مثال بسيط باستخدام Python ومكتبة `pybreaker`

للتوضيح، خلينا نشوف مثال بسيط جداً بلغة بايثون.

أولاً، تثبيت المكتبة:

pip install pybreaker

ثانياً، الكود:


import pybreaker
import time
import requests

# تعريف قاطع الدائرة
# سيفتح بعد 3 أخطاء متتالية
# وسيحاول إعادة الإغلاق بعد 10 ثواني
breaker = pybreaker.CircuitBreaker(fail_max=3, reset_timeout=10)

# هذه هي الخدمة الخارجية التي قد تفشل
def get_currency_rate_from_flaky_service():
    # في الواقع، هذا سيقوم باستدعاء API حقيقي
    # لكن هنا سنقوم بمحاكاة الفشل
    response = requests.get("http://some-external-service.com/api/rate")
    response.raise_for_status() # سيطلق استثناء إذا كان كود الحالة 4xx or 5xx
    return response.json()

# هذه هي الدالة التي سنستدعيها في تطبيقنا
# وهي محمية بقاطع الدائرة
@breaker
def get_rate_safely():
    print("محاولة استدعاء الخدمة...")
    return get_currency_rate_from_flaky_service()

# --- لنبدأ التجربة ---

for i in range(10):
    try:
        print(f"nالمحاولة رقم {i + 1}:")
        rate = get_rate_safely()
        print(f"نجاح! سعر الصرف هو: {rate}")
        time.sleep(1)
    except pybreaker.CircuitBreakerError as e:
        # هذا الخطأ يحدث عندما تكون الدائرة مفتوحة
        print(f"فشل! الدائرة مفتوحة. لن نحاول استدعاء الخدمة. الخطأ: {e}")
        # هنا يمكنك تنفيذ منطق الـ Fallback
        # مثلاً: return {"rate": 1.0, "default": True}
        time.sleep(2)
    except requests.exceptions.RequestException as e:
        # هذا الخطأ يحدث عندما تكون الدائرة مغلقة ولكن الاستدعاء يفشل
        print(f"فشل! فشل استدعاء الخدمة. الخطأ: {e}")
        time.sleep(1)

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

نصائح أبو عمر الذهبية (خبرة من الميدان)

بعد سنوات من التعامل مع هذا النمط، تعلمت كم شغلة من “كيس” الخبرة، وحاب أشاركم إياها:

  1. لا تكن بخيلاً في الـ Fallbacks: لما يفشل استدعاء وتفتح الدائرة، شو لازم تعمل؟ أسوأ إشي هو إنك ترجع خطأ عام للمستخدم. جهز “خطة بديلة” أو Fallback. إذا كانت الخدمة الفاشلة لجلب توصيات المنتجات، ممكن تعرض قائمة بالمنتجات الأكثر مبيعاً من الكاش (Cache). إذا كانت خدمة تحويل العملات، ممكن تعرض السعر بالعملة الافتراضية مع ملاحظة صغيرة للمستخدم. “أهم إشي يكون عندك خطة بديلة”.
  2. الضبط الدقيق (Fine-Tuning) هو سر النجاح: الأرقام اللي بتحدد متى تفتح الدائرة (fail_max) وكم تنتظر قبل المحاولة مرة أخرى (reset_timeout) مش قرآن منزل. بتعتمد على أهمية الخدمة وطبيعتها. خدمة أساسية وحرجة مثل الدفع ممكن تحتاج عتبة فشل (threshold) أقل وزمن انتظار أطول. خدمة ثانوية ممكن تكون متساهل معها أكثر. راقب أداء نظامك وعدّل هاي الأرقام بناءً على البيانات الحقيقية. “مش كل إشي إله نفس الميزان”.
  3. المراقبة والإنذار (Monitoring and Alerting): تطبيق قاطع الدائرة لا يعني أن تتجاهل الأخطاء. بالعكس، فتح الدائرة هو إنذار مبكر واضح جداً بوجود مشكلة في خدمة معينة. يجب أن تقوم بإعداد نظام مراقبة يرسل لك تنبيهاً فورياً عندما يفتح أي قاطع دائرة في نظامك. هذا يسمح لك بالتحرك لإصلاح المشكلة الأساسية قبل أن يلاحظها المستخدم حتى.
  4. لا تستخدمه لكل شيء: قاطع الدائرة مفيد للاستدعاءات عبر الشبكة (APIs) أو أي عملية ممكن تفشل بشكل متكرر وتكون بطيئة. لا تستخدمه لعمليات داخلية بحتة في نفس التطبيق، فهذا قد يعقد الأمور بدون فائدة حقيقية.

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

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

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

نصيحتي الأخيرة لكم: ما تستنى المصيبة تصير عشان تتعلم تصلحها. كن استباقياً، ابنِ أنظمتك لتكون صامدة من اليوم الأول. وهيك، بدل ما تقضي ليلتك تصلح مشاكل، بتقضيها مع كاسة شاي بالنعنع وإنت مرتاح البال.

سلام!

أبو عمر

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

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

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

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

آخر المدونات

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

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

وداعاً ليالي الصيانة الطويلة والمستخدمين الغاضبين! في هذه المقالة، أشارككم قصة حقيقية وكيف غيرت استراتيجيات 'الهجرات بدون توقف' (Zero-Downtime Migrations) طريقة عملنا، مع دليل عملي...

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

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

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

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

كنا شركة مجهولة للمطورين: كيف أنقذنا ‘برنامج سفراء التقنية’ من جحيم المنافسة على المواهب؟

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

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

قاعدة بياناتنا تحتضر: كيف أنقذها ‘التخزين المؤقت’ (Caching) من جحيم البطء القاتل؟

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

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

سجلاتنا كانت ثقباً أسود: كيف أنقذنا ‘التسجيل المنظم’ (Structured Logging) من جحيم البحث عن إبرة في كومة قش؟

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

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

اجتماعاتنا الفردية كانت مضيعة للوقت: كيف حولناها إلى محرك للنمو؟ دليلك العملي لاجتماعات 1-on-1 فعالة

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

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