System Design

بناء المرونة في الخدمات المصغرة

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

فهم نمط مفتاح الدائرة

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

يوجد ثلاثة حالات في مفتاح الدائرة:

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

إليك تنفيذًا مفاهيميًا بلغة بايثون باستخدام مكتبة tenacity لإعادة المحاولة وآلة حالة بسيطة للمفتاح:


import time
from enum import Enum

class CircuitState(Enum):
    CLOSED = "closed"
    OPEN = "open"
    HALF_OPEN = "half_open"

class CircuitBreaker:
    def __init__(self, failure_threshold=5, recovery_timeout=30):
        self.failure_count = 0
        self.state = CircuitState.CLOSED
        self.failure_threshold = failure_threshold
        self.recovery_timeout = recovery_timeout
        self.last_failure_time = None

    def call(self, func, *args, **kwargs):
        if self.state == CircuitState.OPEN:
            if time.time() - self.last_failure_time > self.recovery_timeout:
                self.state = CircuitState.HALF_OPEN
            else:
                raise Exception("Circuit is Open")

        try:
            result = func(*args, **kwargs)
            if self.state == CircuitState.HALF_OPEN:
                self.state = CircuitState.CLOSED
                self.failure_count = 0
            return result
        except Exception as e:
            self.record_failure()
            raise e

    def record_failure(self):
        self.failure_count += 1
        self.last_failure_time = time.time()
        if self.failure_count >= self.failure_threshold:
            self.state = CircuitState.OPEN

إعادة المحاولة الاستراتيجية مع التراجع

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

يعمل التذبذب على عشوائية التأخير لمنع "سرب الرعد" من عمليات إعادة المحاولة من ضرب الخدمة في نفس اللحظة تمامًا.


import random

def retry_with_backoff(func, max_retries=3, base_delay=1):
    for attempt in range(max_retries):
        try:
            return func()
        except Exception as e:
            if attempt == max_retries - 1:
                raise e
            delay = (2 ** attempt) + random.uniform(0, 1)
            time.sleep(delay)

التدهور السلس: بدائل ذات أهمية

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

تشمل أمثلة التدهور السلس ما يلي:

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

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

الخلاصة

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

Share: