تصميم أنظمة الخدمات الخلفية

إطار تصميم الأنظمة والمكونات الأساسية

5 دقيقة للقراءة

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

إطار تصميم الأنظمة من 4 خطوات

يجب أن تتبع كل مقابلة تصميم أنظمة هذا الهيكل. يقيّم المحاورون عمليتك بقدر ما يقيّمون حلك.

الخطوة 1: توضيح المتطلبات (3-5 دقائق)

لا تبدأ التصميم فورًا أبدًا. اطرح أسئلة محددة لتضييق النطاق.

المتطلبات الوظيفية تحدد ماذا يفعل النظام:

  • من هم المستخدمون؟ كم عددهم؟
  • ما هي الميزات الأساسية؟ (اذكر 3-5، ثم رتبها حسب الأولوية)
  • ما هي صيغ المدخلات/المخرجات؟

المتطلبات غير الوظيفية تحدد مدى جودة أداء النظام:

الخاصية السؤال الذي تطرحه الهدف النموذجي
زمن الاستجابة ما هو وقت الاستجابة المقبول؟ p99 أقل من 200 مللي ثانية للقراءة
الإنتاجية كم طلب في الثانية؟ مشتقة من DAU
التوفر ما هو وقت التشغيل المطلوب؟ 99.9% = 8.7 ساعة توقف/سنة
الاتساق هل الاتساق النهائي مقبول؟ يعتمد على المجال
المتانة هل يمكن أن نفقد بيانات؟ 99.999999999% للتخزين

الخطوة 2: التقدير التقريبي (3-5 دقائق)

أظهر أنك تستطيع التفكير حول الحجم. استخدم صيغًا بسيطة:

QPS (استعلامات في الثانية) = DAU x طلبات_لكل_مستخدم / 86,400

التخزين سنويًا = السجلات_الجديدة_يوميًا x حجم_السجل x 365

عرض النطاق = QPS x متوسط_حجم_الاستجابة

مثال: خدمة تغذية أخبار وسائل التواصل الاجتماعي

DAU = 100 مليون مستخدم
كل مستخدم يحمّل التغذية 5 مرات/يوم ← قراءات = 500 مليون/يوم
QPS القراءة = 500M / 86,400 ≈ 5,800 QPS
QPS الذروة = 5,800 x 3 ≈ 17,400 QPS (3 أضعاف المتوسط للذروة)

كل مستخدم ينشر 0.5 مرة/يوم ← كتابات = 50 مليون/يوم
QPS الكتابة = 50M / 86,400 ≈ 580 QPS

التخزين لكل منشور = 1 KB نص + 500 KB وسائط متوسط = ~500 KB
التخزين اليومي = 50M x 500 KB = 25 TB/يوم
التخزين السنوي = 25 TB x 365 ≈ 9 PB/سنة

الخطوة 3: البنية عالية المستوى (5-10 دقائق)

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

                        ┌─────────────┐
                        │  العملاء    │
                        │(ويب/موبايل)│
                        └──────┬──────┘
                        ┌──────▼──────┐
                        │     CDN     │
                        │(ثابت/صور)  │
                        └──────┬──────┘
                        ┌──────▼──────┐
                        │   موزع     │
                        │  الأحمال   │
                        └──────┬──────┘
              ┌────────────────┼────────────────┐
              │                │                │
       ┌──────▼──────┐ ┌──────▼──────┐ ┌──────▼──────┐
       │ خادم API   │ │ خادم API   │ │ خادم API   │
       └──────┬──────┘ └──────┬──────┘ └──────┬──────┘
              │                │                │
              └────────────────┼────────────────┘
                      ┌────────┼────────┐
                      │        │        │
               ┌──────▼──┐ ┌──▼───┐ ┌──▼──────────┐
               │ الكاش   │ │ قاعدة│ │ قائمة       │
               │(Redis)  │ │البيانات│ │ الرسائل    │
               └──────────┘ └──────┘ └─────────────┘

الخطوة 4: التعمق (15-25 دقيقة)

اختر 2-3 مكونات بناءً على اهتمام المحاور أو نقطة الاختناق في النظام. تعمّق في نماذج البيانات والخوارزميات ومعالجة الأعطال واستراتيجيات التوسع.

نصيحة احترافية: غالبًا ما يسأل المحاورون "ما الذي سيتعطل أولاً عند 10 أضعاف الحجم؟" هذه إشارتك لمناقشة المكون الأكثر ضغطًا.

مكون أساسي: موزعات الأحمال

توزع موزعات الأحمال حركة المرور عبر خوادم متعددة لضمان عدم إرهاق خادم واحد.

L4 مقابل L7

الميزة L4 (النقل) L7 (التطبيق)
الطبقة TCP/UDP HTTP/HTTPS
السرعة أسرع (بدون فحص الحمولة) أبطأ (يفحص الرؤوس/الجسم)
التوجيه IP + المنفذ فقط مسار URL، الرؤوس، الكوكيز
SSL تمرير أو إنهاء إنهاء دائمًا
حالة الاستخدام إنتاجية عالية، خدمات TCP توجيه API، اختبار A/B
أمثلة AWS NLB، HAProxy (وضع TCP) AWS ALB، Nginx، Envoy

خوارزميات توزيع الأحمال

الخوارزمية كيف تعمل الأفضل لـ
Round-robin تدور عبر الخوادم بالتسلسل خوادم متساوية القدرة
Weighted round-robin الخوادم الأقوى تحصل على طلبات أكثر أحجام خوادم مختلطة
Least connections توجيه إلى الخادم بأقل اتصالات نشطة مدد طلبات متفاوتة
IP hash تجزئة IP العميل لاختيار الخادم تقارب الجلسة بدون كوكيز
Consistent hashing توزيع قائم على حلقة التجزئة طبقات التخزين المؤقت، الخدمات ذات الحالة

فحوصات الصحة

ترسل موزعات الأحمال فحوصات صحة دورية (HTTP GET إلى /health أو اتصال TCP) وتزيل العقد غير السليمة من المجموعة. التهيئة النموذجية: فحص كل 10 ثوانٍ، تعليم كغير سليم بعد 3 فشلات متتالية، إعادة الإضافة بعد نجاحين متتاليين.

مكون أساسي: استراتيجيات التخزين المؤقت

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

مقارنة استراتيجيات التخزين المؤقت

الاستراتيجية مسار القراءة مسار الكتابة الاتساق حالة الاستخدام
Cache-aside التطبيق يفحص الكاش ← فقدان ← قراءة DB ← ملء الكاش التطبيق يكتب في DB فقط نهائي (قراءات قديمة ممكنة) عام، كثيف القراءة
Write-through التطبيق يقرأ من الكاش التطبيق يكتب في الكاش و DB معًا قوي (محدّث دائمًا) كثيف القراءة مع حاجة للاتساق
Write-back التطبيق يقرأ من الكاش التطبيق يكتب في الكاش فقط، تفريغ غير متزامن إلى DB نهائي (خطر فقدان البيانات) كثيف الكتابة، يتحمل الفقدان
Write-around التطبيق يفحص الكاش ← فقدان ← قراءة DB ← ملء الكاش التطبيق يكتب في DB فقط، إبطال الكاش نهائي البيانات نادرًا ما تُقرأ بعد الكتابة

نمط Cache-Aside (الأكثر شيوعًا)

def get_user(user_id: str) -> dict:
    # الخطوة 1: فحص الكاش
    cached = redis.get(f"user:{user_id}")
    if cached:
        return json.loads(cached)

    # الخطوة 2: فقدان الكاش — القراءة من قاعدة البيانات
    user = db.query("SELECT * FROM users WHERE id = %s", user_id)

    # الخطوة 3: ملء الكاش مع TTL
    redis.setex(f"user:{user_id}", 3600, json.dumps(user))

    return user

سياسات إخراج الكاش

السياسة الآلية الأفضل لـ
LRU (الأقل استخدامًا مؤخرًا) إخراج العنصر الذي لم يُوصل إليه لأطول فترة عام — الخيار الأكثر شعبية
LFU (الأقل استخدامًا تكرارًا) إخراج العنصر الذي وُصل إليه أقل عدد مرات أعباء عمل بمفاتيح ساخنة مستقرة
TTL (وقت البقاء) إخراج بعد وقت انتهاء ثابت بيانات بتحمل معروف للقِدم

مكون أساسي: CDN (شبكة توصيل المحتوى)

تخزن شبكات CDN المحتوى الثابت (الصور، JS، CSS، الفيديوهات) في مواقع طرفية قريبة من المستخدمين، مما يقلل زمن الاستجابة ويخفف الحمل عن الخوادم الأصلية.

النموذج كيف يعمل الأفضل لـ
Push CDN الأصل يدفع المحتوى إلى عقد CDN استباقيًا محتوى صغير نادر التغيير (شعارات، حزم CSS)
Pull CDN CDN يجلب من الأصل عند أول طلب ثم يخزنه كتالوجات كبيرة، محتوى المستخدمين

نصيحة للمقابلة: معظم شبكات CDN الواقعية تستخدم نموذج Pull مع إبطال قائم على TTL. اذكر CloudFront أو Cloudflare أو Akamai لإظهار معرفة عملية.

مكون أساسي: قوائم الرسائل

تفصل قوائم الرسائل المنتجين عن المستهلكين، مما يتيح المعالجة غير المتزامنة وتسوية الأحمال والتحمل للأعطال.

نقطة لنقطة مقابل نشر-اشتراك

الميزة نقطة لنقطة نشر-اشتراك
التسليم مستهلك واحد لكل رسالة جميع المشتركين يحصلون على كل رسالة
حالة الاستخدام توزيع المهام، قوائم الوظائف بث الأحداث، الإشعارات
مثال SQS، Celery مواضيع Kafka، SNS، Redis Pub/Sub

مقارنة قوائم الرسائل

الميزة Kafka RabbitMQ SQS
النموذج سجل موزع (نشر-اشتراك) وسيط رسائل (نقطة لنقطة + نشر-اشتراك) قائمة مُدارة (نقطة لنقطة)
الإنتاجية عالية جدًا (ملايين/ثانية) متوسطة (عشرات الآلاف/ثانية) عالية (مُدارة، تتوسع تلقائيًا)
الترتيب ترتيب لكل قسم ترتيب لكل قائمة أفضل جهد (FIFO متاح)
الاحتفاظ قابل للتهيئة (أيام/أسابيع) حتى الاستهلاك 14 يومًا كحد أقصى
إعادة التشغيل نعم (إزاحات المستهلك) لا (بمجرد الاستهلاك، تذهب) لا
الأفضل لـ بث الأحداث، تجميع السجلات قوائم المهام، RPC وظائف غير متزامنة بسيطة، serverless

مكون أساسي: التجزئة المتسقة

عند توزيع البيانات عبر عقد متعددة (خوادم الكاش، أجزاء قاعدة البيانات)، التجزئة المعيارية البسيطة (hash(key) % N) تتعطل عند إضافة أو إزالة العقد — تقريبًا كل المفاتيح تُعاد تعيينها. التجزئة المتسقة تحل هذا.

كيف تعمل

1. عيّن فضاء التجزئة إلى حلقة (من 0 إلى 2^32 - 1)
2. ضع كل خادم في موقع على الحلقة: hash(server_id)
3. لكل مفتاح، جزّئه وامشِ باتجاه عقارب الساعة لأقرب خادم

        الخادم A
           |
    ───────●───────
   /                \
  ●  المفتاح X      ●  الخادم C
   \  (يذهب إلى A) /
    ───────●───────
           |
        الخادم B

العقد الافتراضية

الخوادم الحقيقية غير متساوية في السعة ومواقع التجزئة قد تتجمع. العقد الافتراضية تحل هذا بوضع كل خادم فعلي في مواقع متعددة على الحلقة (مثلاً 150-200 عقدة افتراضية لكل خادم). هذا يضمن توزيعًا متساويًا وإعادة توازن سلسة.

عند إضافة عقدة: فقط المفاتيح بين العقدة الجديدة وسابقتها على الحلقة تحتاج للانتقال — تقريبًا K / N مفاتيح بدلاً من كل المفاتيح (حيث K = إجمالي المفاتيح، N = إجمالي العقد).

عند إزالة عقدة: فقط مفاتيحها تنتقل إلى العقدة التالية باتجاه عقارب الساعة على الحلقة.

تُستخدم بواسطة: DynamoDB (توجيه الأقسام)، Cassandra (حلقة الرموز)، تجزئة Memcached من جانب العميل، CDN Akamai.

التالي: سنمر على أربع مسائل كلاسيكية في تصميم الأنظمة الخلفية خطوة بخطوة — مُختصر الروابط، ومُحدد المعدل، وخدمة الإشعارات، ونظام الدردشة. :::

اختبار

اختبار الوحدة 4: تصميم أنظمة الخدمات الخلفية

خذ الاختبار