دراسات حالة واقعية لتصميم الأنظمة

المشروع التكاملي: تصميم نظام متكامل من البداية إلى النهاية

18 دقيقة الدرس 10 من 10

المشروع التكاملي: تصميم نظام متكامل من البداية إلى النهاية

يجمع هذا الدرس التكاملي كل مفهوم تعلمناه في الدورة في تمرين تصميم واحد واقعي. سنصمم منصة فيديو اجتماعية — على غرار YouTube بحجم أصغر لكنه لا يزال جدياً — عبر تطبيق الإطار الكامل المكون من خمس مراحل من الصفر: التوضيح، والتقدير، وتعريف الـ API، والبنية العامة، وتبرير المقايضات. يُشرح كل قرار بـ لماذا، لا بـ ماذا فحسب.

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

المرحلة الأولى — توضيح المتطلبات

المتطلبات الوظيفية (الضروريات):

  • يمكن للمستخدمين رفع مقاطع الفيديو (حتى 4 جيجابايت، بأي صيغة شائعة).
  • يتم تحويل الفيديوهات إلى دقات متعددة (360p، 720p، 1080p) وتقديمها بشكل تكيفي.
  • يمكن للمستخدمين البحث عن الفيديوهات بالعنوان والوسوم.
  • يعرض الخلاصة الرئيسية فيديوهات موصى بها لكل مستخدم.
  • يمكن للمستخدمين الإعجاب والتعليق والاشتراك في القنوات.
  • عدد المشاهدات متسق في نهاية المطاف — الدقة الآنية غير مطلوبة.

المتطلبات غير الوظيفية (هذه هي التي تحرك البنية):

  • الحجم: 50 مليون مستخدم نشط يومياً؛ 500,000 رفع فيديو يومياً؛ 5 مليار ثانية مشاهدة يومياً.
  • زمن الاستجابة: يجب أن يبدأ تشغيل الفيديو خلال ثانيتين عالميًا (النسبة المئوية 95).
  • التوافر: 99.95% وقت تشغيل لمسار التشغيل؛ يمكن لمسار الرفع تحمل تدهور بسيط.
  • المتانة: مقاطع الفيديو المرفوعة يجب ألا تُفقد أبداً (لا فقدان للبيانات).
  • الاتساق: عدد الإعجابات والمشاهدات متسق في نهاية المطاف. الاشتراكات متسقة بشكل قوي.
فكرة أساسية: فصل مسار الرفع/التحويل عن مسار التشغيل يتيح لك تطبيق ميزانيات موثوقية وزمن استجابة مختلفة على كل منهما. التشغيل هو المسار الحرج المدر للإيرادات؛ أما الرفع فهو مهمة خلفية يتقبل المستخدمون فيها بعض التأخير.

المرحلة الثانية — تقدير الحجم

  • QPS الرفع: 500,000 رفع/يوم ÷ 86,400 ثانية ≈ 6 رفعات/ثانية في المتوسط؛ 30/ثانية عند الذروة (معامل 5x).
  • QPS التشغيل: 5 مليار ثانية مشاهدة/يوم ÷ متوسط طول الفيديو 300 ثانية = 16.7 مليون بث متزامن؛ ÷ 86,400 ثانية ≈ 193 ألف بدء تشغيل/ثانية عند الذروة.
  • التخزين (الرفع الخام): 500,000 × متوسط 500 ميجابايت = 250 تيرابايت/يوم من الفيديو الخام. مخرجات التحويل بثلاث دقات تضيف ~300 تيرابايت/يوم. خلال سنة واحدة: ~200 بيتابايت. التخزين الكائني (S3 / GCS) هو الإجابة العملية الوحيدة.
  • عرض النطاق (الخروج): 193 ألف بث × 2 ميجابت/ثانية متوسط معدل البت = ~386 جيجابت/ثانية. شبكة توصيل المحتوى (CDN) ليست اختيارية — إنها متطلب معماري صارم على هذا الحجم.
  • قاعدة بيانات البيانات الوصفية: 500,000 صف جديد/يوم × 2 كيلوبايت/صف = 1 جيجابايت/يوم. بعد 5 سنوات ≈ 1.8 تيرابايت — يتسع بسهولة في قاعدة بيانات علائقية مقسّمة.

المرحلة الثالثة — واجهة الـ API الأساسية

POST /v1/videos/initiate-upload body: { title, description, tags[], duration_s, file_size_bytes } response: { video_id, upload_url } -- رابط S3 موقّع مسبقًا PUT <upload_url> -- رفع مباشر من العميل إلى S3 (يتجاوز خوادم التطبيق) GET /v1/videos/{video_id} response: { video_id, title, manifest_url, like_count, view_count, channel } GET /v1/feed?user_id=X&cursor=Y response: { videos[], next_cursor } POST /v1/videos/{video_id}/likes body: { user_id } response: { new_like_count } GET /v1/search?q=term&page=N response: { videos[], total_hits }

تسليم العميل رابطًا موقّعًا مسبقًا للرفع المباشر إلى التخزين الكائني هو قرار تصميمي بالغ الأهمية: فهو يُقصي خوادم التطبيق كلياً من مسار بيانات الرفع. يتولى خادم التطبيق البيانات الوصفية فقط؛ بينما تتجه الحزم الخام مباشرةً من العميل إلى S3.

المرحلة الرابعة — البنية العامة

ينقسم النظام بشكل طبيعي إلى مستويين مستقلين:

  1. مستوى الكتابة (الرفع): العميل ← خادم التطبيق (البيانات الوصفية) + رفع مباشر إلى S3 ← حدث S3 ← طابور التحويل ← عمال التحويل ← الفيديوهات المُحوَّلة إلى S3 ← إلغاء صلاحية CDN.
  2. مستوى القراءة (التشغيل): العميل ← CDN ← الأصل (لخطأ الإصابة فقط) ← خادم التطبيق ← قاعدة البيانات الوصفية/الذاكرة المؤقتة ← بيان HLS ← مقاطع CDN.
End-to-end video platform architecture UPLOAD PLANE Client Web / Mobile Upload API App Server Object Store S3 / GCS (raw) Transcode Queue Kafka / SQS Transcode Workers FFmpeg / GPU fleet Object Store S3 (transcoded) metadata direct PUT (pre-signed) S3 event 360p/720p/1080p READ / PLAYBACK PLANE Client Web / Mobile CDN Cloudfront / Akamai Playback API App Server Cache Redis Cluster Metadata DB MySQL (sharded) Search Index Elasticsearch Recommender ML service HLS request cache miss search queries origin pull (video segments)
البنية الكاملة من البداية إلى النهاية: مستوى الرفع (في الأعلى) منفصل تمامًا عن مستوى التشغيل (في الأسفل). تمتص شبكة CDN الغالبية العظمى من حركة التشغيل؛ ولا تتعامل خوادم التطبيق إلا مع خطأ الإصابة وطلبات البيانات الوصفية.

المرحلة الخامسة — التعمق: أصعب خمس مشكلات

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

1. موثوقية خط أنابيب التحويل

يُشغّل حدثٌ من S3 رسالةً على موضوع Kafka. يستهلك تجمع من عمال التحويل عديمي الحالة هذه الوظائف. يقوم كل عامل بتنزيل المصدر الخام، وتشغيل FFmpeg لإنتاج مقاطع HLS بثلاث دقات، ورفعها إلى S3. ثم ينشر العامل حدثًا transcode.completed تستهلكه خدمة البيانات الوصفية لتغيير حالة الفيديو من processing إلى published.

المقايضة الرئيسية: التسليم مرة على الأقل (سلوك Kafka الافتراضي) يعني أن عاملًا قد يتعطل في منتصف المهمة وتُعاد تسليم الرسالة. يجب أن يكون العمال ذوي خاصية المثلية (idempotent) — إعادة التحويل والكتابة فوق مخرجات S3 آمنة لأن الكتابة النهائية ذرية من منظور S3.

2. معدل إصابة ذاكرة CDN

مقاطع الفيديو غير قابلة للتغيير بمجرد تحويلها (مقطع HLS لمدة ثانيتين لا يتغير أبداً). اضبط رأس Cache-Control: max-age=31536000, immutable. ستخزنها CDN لمدة عام في كل عقدة حافة. معدلات إصابة تتجاوز 90% قابلة للتحقيق للمحتوى الشائع.

أفضل الممارسات: رقّم روابط البيانات الوصفية (/v/{video_id}/{quality}/manifest.m3u8?gen=3) حتى لا تقدم إعادة التحويل بيانات وصفية قديمة. يجب أن يكون لبيانات الفيديو الوصفية TTL قصير (60 ثانية) بينما للمقاطع TTL طويل لمدة عام.

3. عدد المشاهدات — الاتساق النهائي بالطريقة الصحيحة

زيادة عداد SQL عند كل مشاهدة بمعدل 193 ألف/ثانية ستدمر قاعدة بيانات علائقية. الحل الكلاسيكي هو خط أنابيب من مرحلتين:

  1. يخزن كل خادم تطبيق أحداث المشاهدة محلياً ويرسلها على دفعات كل 10 ثوانٍ إلى بث (Kafka).
  2. تجمع مهمة Flink أو Spark Streaming الأعداد من البث وتكتب لقطات دورية في قاعدة البيانات (كل 30 ثانية).

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

4. توليد الخلاصة — Fan-out عند الكتابة مقابل Fan-out عند القراءة

Fan-out on write vs fan-out on read Fan-out on Write (Push) Creator uploads video Fan-out Worker User A Feed Redis list User B Feed Redis list User C Feed Redis list + قراءات سريعة (O(1) لكل مستخدم) - كتابات مكلفة (منشئ بـ 10M مشترك = 10M كتابة ذاكرة مؤقتة) Fan-out on Read (Pull) User requests feed Feed Service merge N timelines + كتابات رخيصة (صف DB واحد لكل فيديو) - قراءات بطيئة (دمج N اشتراكات أثناء الطلب) هجين: دفع للمستخدمين العاديين، سحب للمشاهير (>مليون مشترك)
Fan-out عند الكتابة (الدفع) يمنح قراءات سريعة لكنه مكلف للمنشئين ذوي المتابعين الكثيرين. Fan-out عند القراءة (السحب) رخيص الكتابة لكن بطيء القراءة. الأنظمة الحقيقية تستخدم أسلوبًا هجينًا.

5. حداثة البحث

عند نشر فيديو، يجب أن تظهر بياناته الوصفية في نتائج البحث خلال ثوانٍ قليلة. تنشر خدمة البيانات الوصفية حدثًا video.published؛ يستهلكه عامل فهرسة منفصل ويستدعي Elasticsearch Bulk API لفهرسة العنوان والوسوم والوصف ومقياس جودة محسوب مسبقًا (مشاهدات، إعجابات، عمر). يعالج Elasticsearch تحديثات الفهرس المقلوب خلال ~ثانية واحدة، مما يُلبي متطلب الحداثة دون لمس قاعدة بيانات البيانات الوصفية من مسار البحث.

تجميع كل شيء — جدول قرارات التصميم الرئيسية

الخلاصة: كل قرار أدناه هو مقايضة، وليس حقيقة مطلقة. الإجابة الصحيحة تعتمد على قيودك، والقدرة على التعبير عن ذلك بوضوح هي ما يُميز المهندس الكبير عن المبتدئ.
القرار الاختيار السبب
تخزين الفيديو التخزين الكائني (S3) قابلية توسع غير محدودة، متانة 11-نايني، رخيص على نطاق البيتابايت
مسار الرفع رابط موقّع مسبقًا (العميل → S3 مباشرة) يُقصي خوادم التطبيق من مسار البيانات؛ يُوسّع عرض نطاق الرفع بشكل مستقل
تنسيق التحويل طابور Kafka + عمال عديمو الحالة يفصل الرفع عن التحويل؛ قابلية التوسع الأفقي؛ تسليم مرة على الأقل مع مهام ذات خاصية مثلية
تسليم الفيديو CDN + HLS (معدل بت تكيفي) زمن استجابة منخفض عالميًا؛ يتكيف العميل مع الشبكة؛ المقاطع غير القابلة للتغيير تُخزَّن إلى الأبد
تخزين البيانات الوصفية MySQL مقسّم + Redis ACID للكتابة؛ الذاكرة المؤقتة تمتص QPS القراءة (البيانات الوصفية كثيفة القراءة)
عدد المشاهدات كتابات دفعية عبر Kafka يتجنب الاحتكاك على الصفوف الساخنة؛ الاتساق النهائي مقبول للعدادات
توليد الخلاصة هجين دفع/سحب دفع للمستخدمين العاديين (قراءات سريعة)؛ سحب للمشاهير (يتجنب عواصف fan-out)
البحث Elasticsearch (فهرسة مدفوعة بالأحداث) بحث بالنص الكامل؛ منفصل عن قاعدة البيانات؛ زمن فهرسة ~ثانية مقبول
خطأ شائع في المقابلات: الإفراط في التعقيد منذ البداية. لا تضف شبكة خدمات، ووكيل جانبي، وبوابة GraphQL، ومخزن أحداث CQRS قبل أن تبرر الحاجة. كل طبقة تضيف زمن استجابة وعبئًا تشغيليًا وسطحًا للفشل. أضف التعقيد فقط حين تستطيع الإشارة إلى الاختناق أو المتطلب المحدد الذي يستلزم ذلك.

ما تملكه الآن

لقد مررت بتصميم نظام متكامل — من نص غامض إلى بنية جاهزة للإنتاج — باستخدام نفس المنهجية المكونة من خمس مراحل التي يمكنك تطبيقها على أي مشكلة. الأنماط التي ظهرت هنا (الرفع بالروابط الموقّعة، والتسليم عبر CDN أولاً، وخطوط أنابيب التحويل غير المتزامنة، والـ fan-out الهجيني، والفهرسة المدفوعة بالأحداث، والعدادات ذات الاتساق النهائي) هي نفسها التي تُشغّل YouTube وNetflix وTikTok وكل منصة فيديو ضخمة النطاق في الإنتاج اليوم.

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

اكتمل الدرس!

تهانينا! لقد أكملت جميع الدروس في هذا البرنامج التعليمي.