پست

طراحی پلتفرم پخش ویدیو مانند YouTube

بررسی گام‌به‌گام طراحی YouTube شامل آپلود ویدیوهای حجیم، پردازش ویدیو به Segment و فرمت‌های مختلف، پخش با نرخ بیت تطبیقی، آپلود قابل ازسرگیری و مقیاس‌دهی با CDN و کش

طراحی پلتفرم پخش ویدیو مانند YouTube

درک مسئله

YouTube یک پلتفرم اشتراک‌گذاری ویدیو است که به کاربران اجازه می‌دهد محتوای ویدیویی را آپلود کنند، ببینند و با آن تعامل داشته باشند. تا زمان نوشتن این مطلب، دومین وب‌سایت پربازدید جهان است.

بین این سؤال و طراحی Dropbox همپوشانی مفهومی وجود دارد. اگر با اصول طراحی سیستم برای طراحی‌های آپلود و دانلود فایل کمتر آشنایید، توصیه می‌کنم اول تحلیل Dropbox را بخوانید.

نیازمندی‌های عملکردی

نیازمندی‌های اصلی:

  • کاربران بتوانند ویدیو آپلود کنند.
  • کاربران بتوانند ویدیو تماشا (Stream) کنند.

موارد خارج از محدوده:

  • کاربران بتوانند اطلاعات مربوط به ویدیو، مثل تعداد بازدید، را ببینند.
  • کاربران بتوانند ویدیوها را جست‌وجو کنند.
  • کاربران بتوانند برای ویدیوها نظر بگذارند.
  • کاربران بتوانند ویدیوهای پیشنهادی ببینند.
  • کاربران بتوانند کانال بسازند و مدیریت کنند.
  • کاربران بتوانند در کانال‌ها مشترک شوند.

شایان ذکر است که این سؤال بیشتر جنبه‌های اشتراک‌گذاری ویدیوی YouTube را بررسی می‌کند. اگر مطمئن نیستید برای اپلیکیشن پرویژگی مثل YouTube روی کدام ویژگی‌ها تمرکز کنید، کوتاه با مصاحبه‌کننده صحبت کنید تا بفهمید کدام بخش سیستم برایش مهم‌تر است.

نیازمندی‌های غیرعملکردی

نیازمندی‌های اصلی:

  • سیستم باید بسیار در دسترس باشد (Availability را بر Consistency مقدم بدارد).
  • سیستم باید از آپلود و پخش ویدیوهای بزرگ (تا 10 گیگابایت) پشتیبانی کند.
  • سیستم باید پخش ویدیو با Latency (تأخیر) کم را حتی در محیط‌های با پهنای باند کم ممکن کند.
  • سیستم باید برای تعداد بالای ویدیوهای آپلودشده و تماشاشده در روز مقیاس بگیرد (حدود 1 میلیون ویدیو آپلودشده و 100 میلیون ویدیو تماشاشده در روز).
  • سیستم باید از آپلود قابل ازسرگیری (Resumable Upload) پشتیبانی کند.

موارد خارج از محدوده:

  • سیستم در برابر محتوای نامناسب در ویدیوها محافظت کند.
  • سیستم در برابر ربات‌ها یا حساب‌های جعلی که ویدیو آپلود یا مصرف می‌کنند محافظت کند.
  • سیستم مانیتورینگ و هشداردهی داشته باشد.

برای این سؤال، با توجه به تعداد کم نیازمندی‌های عملکردی، تعیین نیازمندی‌های غیرعملکردی بسیار مهم‌تر است. آن‌ها پیچیدگی این تعامل‌های «آپلود» و «تماشا» را که فریبنده ساده‌اند مشخص می‌کنند. برشمردن این چالش‌ها مهم است، چون روی طراحی عمیقاً اثر می‌گذارد.

آماده‌سازی

برنامه‌ریزی رویکرد

پیش از ورود به طراحی سیستم، کمی وقت بگذارید و استراتژی‌تان را مشخص کنید. به‌طور کلی توصیه می‌کنیم طراحی را مرحله‌به‌مرحله بسازید و نیازمندی‌های عملکردی را یکی‌یکی جلو ببرید. این کار تمرکزتان را حفظ می‌کند و نمی‌گذارد در جزئیات گم شوید. بعد از پوشش نیازمندی‌های عملکردی، نیازمندی‌های غیرعملکردی مسیر بررسی‌های عمیق را مشخص می‌کنند.

تعریف موجودیت‌های اصلی

بهتر است با یک نمای کلی از موجودیت‌های اولیه شروع کنیم. در این مرحله لازم نیست هر ستون یا جزئیات را بدانیم. وقتی درک روشن‌تری از سیستم (در طراحی سطح بالا) داشتیم، به این پیچیدگی‌ها می‌پردازیم. همین موجودیت‌های کلیدی فکرمان را هدایت می‌کنند و برای تعریف API پایه‌ای محکم می‌سازند.

برای YouTube موجودیت‌های اصلی ساده‌اند:

  • User (کاربر): کاربر سیستم، آپلودکننده یا بیننده.
  • Video (ویدیو): ویدیویی که آپلود یا تماشا می‌شود.
  • VideoMetadata: Metadata مرتبط با ویدیو، مثل کاربر آپلودکننده، ارجاع URL به رونوشت (Transcript) و غیره. بعداً درباره آنچه دقیقاً اینجا ذخیره می‌کنیم جزئیات بیشتری می‌گوییم.

در مصاحبه واقعی، یک فهرست کوتاه مثل همین کافی است. فقط حتماً موجودیت‌ها را با مصاحبه‌کننده مرور کنید تا در یک مسیر باشید.

API

API مهم‌ترین رابطی است که کاربران با آن کار می‌کنند. تعریف زودهنگام آن مهم است، چون طراحی سطح بالا را هدایت می‌کند. فقط باید برای هر نیازمندی عملکردی یک endpoint تعریف کنیم.

با endpoint آپلود ویدیو شروع می‌کنیم:

1
2
3
4
5
6
POST /upload
Request:
{
  Video,
  VideoMetadata
}

برای پخش ویدیو، endpoint ما ممکن است داده ویدیو را برای پخش روی دستگاه بازیابی کند:

1
GET /videos/{videoId} -> Video & VideoMetadata

توجه کنید که API های شما ممکن است با پیشرفت طراحی تغییر کنند یا تکامل پیدا کنند. در این مورد، API های آپلود و Stream ما در واقع به‌طور قابل توجهی تکامل می‌یابند، چون مبادلات رویکردهای مختلف را در طراحی سطح بالا می‌سنجیم (بعداً بیشتر می‌گوییم). می‌توانید این را فعالانه با مصاحبه‌کننده در میان بگذارید: «چند API ساده را بیان می‌کنم، اما ممکن است با عمیق‌تر شدن در طراحی برگردم و آن‌ها را بهتر کنم.»

طراحی سطح بالا

پس‌زمینه: پخش ویدیو

قبل از پرداختن به هر نیازمندی، ارزش دارد بعضی اطلاعات پایه درباره ذخیره‌سازی و پخش ویدیو بدانید.

برای موفقیت در طراحی YouTube لازم نیست در پخش یا ذخیره‌سازی ویدیو متخصص باشید. این سؤال نامنصفانه‌ای خواهد بود. با این حال، ارزش دارد اصول را در سطح بالا بفهمید تا بتوانید این سؤال را موفق پیش ببرید.

  • کدک ویدیو (Video Codec): کدک ویدیو، ویدیوی دیجیتال را فشرده و از حالت فشرده خارج می‌کند و آن را برای ذخیره‌سازی و انتقال کارآمدتر می‌کند. «کدک» مخفف Encoder/Decoder است. کدک‌ها سعی می‌کنند حجم ویدیو را کم کنند و کیفیت را حفظ کنند. کدک‌ها معمولاً این مبادلات را دارند: ۱) زمان لازم برای فشرده کردن فایل، ۲) پشتیبانی روی پلتفرم‌های مختلف، ۳) کارایی فشرده‌سازی (معمولاً چقدر از حجم فایل اصلی کم می‌شود) و ۴) کیفیت فشرده‌سازی (با اتلاف یا بدون اتلاف). چند کدک محبوب: H.264، H.265 (HEVC)، VP9، AV1، MPEG-2 و MPEG-4.
  • کانتینر ویدیو (Video Container): کانتینر ویدیو قالب فایلی است که داده ویدیو (فریم‌ها، صدا) و Metadata را ذخیره می‌کند. کانتینر ممکن است اطلاعاتی مثل رونوشت‌های ویدیو را هم نگه دارد. تفاوتش با کدک این است که کدک مشخص می‌کند ویدیو چطور فشرده یا از حالت فشرده خارج شود، در حالی که کانتینر قالب فایل را برای نحوه ذخیره ویدیو تعیین می‌کند. پشتیبانی از کانتینرهای ویدیو بسته به دستگاه یا سیستم‌عامل فرق می‌کند.
  • نرخ بیت (Bitrate): نرخ بیت ویدیو تعداد بیت‌هایی است که در یک بازه زمانی منتقل می‌شود، معمولاً بر حسب کیلوبیت یا مگابیت بر ثانیه. اندازه و کیفیت ویدیو روی نرخ بیت اثر می‌گذارد. ویدیوهای با وضوح بالا و نرخ فریم بالاتر (بر حسب FPS) نرخ بیت بسیار بالاتری نسبت به ویدیوهای با وضوح پایین و نرخ فریم کمتر دارند، چون به معنای واقعی کلمه داده بیشتری برای پخش ویدیو باید منتقل شود. فشرده‌سازی با کدک‌ها هم می‌تواند روی نرخ بیت اثر بگذارد، چون فشرده‌سازی کارآمدتر می‌تواند ویدیوی بزرگ‌تر را قبل از انتقال به اندازه بسیار کوچک‌تر فشرده کند.
  • فایل‌های مانیفست (Manifest Files): فایل‌های مانیفست اسناد متنی‌اند که جزئیاتی درباره جریان‌های ویدیو می‌دهند. معمولاً دو نوع فایل مانیفست هست: اصلی (Primary) و رسانه‌ای (Media). فایل مانیفست اصلی همه نسخه‌های موجود یک ویدیو (قالب‌های مختلف) را فهرست می‌کند. فایل اصلی، فایل «ریشه» است و به فایل‌های مانیفست رسانه‌ای اشاره می‌کند که هرکدام یک نسخه متفاوت از ویدیو را نشان می‌دهند. نسخه ویدیو معمولاً به Segment های کوچکی تقسیم می‌شود که هرکدام چند ثانیه طول می‌کشند. فایل‌های مانیفست رسانه‌ای لینک‌های این فایل‌های Segment را فهرست می‌کنند و پخش‌کننده‌های ویدیو آن‌ها را مثل «ایندکس» این Segment ها برای Stream ویدیو به کار می‌برند.

در سراسر این تحلیل، «فرمت ویدیو» اصطلاح کوتاهی است که برای اشاره به ترکیب کانتینر و کدک به کار می‌بریم. حالا سراغ نیازمندی‌های عملکردی می‌رویم!

1) آپلود ویدیو توسط کاربران

یکی از نیازمندی‌های اصلی YouTube این است که کاربران بتوانند ویدیو آپلود کنند تا در نهایت توسط کاربران دیگر دیده شود. هنگام آپلود ویدیو باید چند نکته پایه را در نظر بگیریم:

  • Metadata ویدیو (نام، توضیحات و غیره) را کجا ذخیره کنیم؟
  • داده ویدیو (فریم‌ها، صدا و غیره) را کجا ذخیره کنیم؟
  • برای داده ویدیو دقیقاً چه چیزی ذخیره کنیم؟

برای Metadata ویدیو، نرخ آپلود حدود 1 میلیون ویدیو در روز را در نظر می‌گیریم. یعنی در طول سال 365 میلیون رکورد در پایگاه داده خواهیم داشت که ویدیوها را نشان می‌دهند. در نتیجه، باید Metadata ویدیو را در پایگاه داده‌ای در نظر بگیریم که بتواند افقی پارتیشن‌بندی شود، مثل Cassandra. Cassandra دسترس‌پذیری بالا می‌دهد و اجازه می‌دهد Partition Key برای داده‌هایمان انتخاب کنیم. می‌توانیم بر اساس videoId پارتیشن‌بندی کنیم، چون در این طراحی نگران دسترسی انبوه به ویدیوها نیستیم و فقط ویدیوهای مجزا را جست‌وجو می‌کنیم.

هنگام طراحی راه‌حل ذخیره‌سازی داده‌ای که نیاز به مقیاس دارد، ارزش دارد درباره پارتیشن‌بندی فکر کنید. بعضی سیستم‌ها به پارتیشن‌بندی دقیقی نیاز دارند تا بتوان داده را از یک گره یا تعداد انگشت‌شمار گره خواند. سیستم‌های دیگر در دامنه‌های محدودی نیاز به سازگاری دارند، که پایگاه داده رابطه‌ای با تضمین‌های ACID می‌خواهد که با کلیدی که آن دامنه را نشان می‌دهد پارتیشن شده باشد. مثلاً Ticketmaster ممکن است بر اساس شناسه کنسرت پارتیشن کند تا سازگاری خرید بلیت را تضمین کند. با این حال بعضی سیستم‌ها اصلاً به پارتیشن‌بندی دقیق نیاز ندارند. برای این سیستم می‌توانیم بر اساس videoId پارتیشن کنیم، چون فقط جست‌وجوی نقطه‌ای با videoId انجام می‌دهیم.

برای ذخیره داده ویدیو، می‌توانیم با این مسئله و سؤال مصاحبه Dropbox همپوشانی داشته باشیم. خلاصه این است که آپلود داده مستقیم به ذخیره‌ساز مثل S3 از طریق Presigned URL با آپلود چندقسمتی (Multipart Upload) کارآمدتر است. برای تحلیل آن بخش از سیستم می‌توانید تحلیل Dropbox را بخوانید.

الگو: مدیریت Blob های بزرگ (Handling Large Blobs). آپلود ویدیوی YouTube الگوی Blob های بزرگ را کامل نشان می‌دهد. فایل‌های ویدیویی چندگیگابایتی با استفاده از Presigned URL برای آپلود مستقیم به S3، با انتقال‌های تکه‌ای قابل ازسرگیری و توزیع CDN، کاملاً سرورهای اپلیکیشن را دور می‌زنند. همین الگو برای هر سیستمی که فایل‌های بزرگ را مدیریت می‌کند صدق می‌کند، مثل ذخیره‌سازی عکس، اشتراک‌گذاری سند یا سرویس‌های Backup.

تصمیم طراحی برای آپلود مستقیم به S3 یعنی باید API ‏POST /upload را به POST /presigned_url تغییر بدهیم. سرور یک Presigned URL می‌سازد تا کلاینت مستقیم در S3 آپلود کند. Payload درخواست فقط Metadata ویدیو خواهد بود، نه Metadata و فایل ویدیو.

در نهایت، وقتی نوبت به ذخیره ویدیو می‌رسد، ارزش دارد در نظر بگیریم دقیقاً چه چیزی را ذخیره می‌کنیم. فهمیدن این نکته به شما می‌گوید بعداً باید چه بررسی عمیقی انجام بدهیم تا روشن شود ویدیوها را چطور پردازش می‌کنیم تا سیستم بتواند نیازمندی‌های عملکردی و غیرعملکردی را با موفقیت برآورده کند. چند گزینه را بررسی می‌کنیم.

رویکرد بد: ذخیره فایل اصلی بدون پردازش

این رویکرد اساساً این واقعیت را نادیده می‌گیرد که باید نوعی پس‌پردازش (Post-processing) ویدیو انجام بدهیم. فقط فایلی را که کاربر می‌دهد ذخیره می‌کنیم و هیچ پس‌پردازشی نمی‌کنیم.

چالش‌ها: این طراحی ساده و سرراست است، اما در عمل کار نمی‌کند، چون دستگاه‌های مختلف به فرمت‌های ویدیویی متفاوتی برای پخش نیاز دارند. باید درباره نحوه ذخیره ویدیوها هوشمندتر باشیم.

رویکرد خوب: پس‌پردازش به فرمت‌های مختلف

این رویکرد شامل انجام نوعی پس‌پردازش ویدیو برای تضمین تبدیل آن به فرمت‌های مختلف قابل پخش روی دستگاه‌های مختلف است. وقتی کاربر ویدیو آپلود کرد، S3 اعلان رویداد را به Video Processing Service می‌فرستد. این سرویس کار تبدیل ویدیوی اصلی به فرمت‌های مختلف را انجام می‌دهد. هر فرمت را به‌صورت یک فایل در S3 ذخیره می‌کند. همچنین رکورد Metadata ویدیو را با URL های فایل‌هایی که فرمت‌های مختلف را نشان می‌دهند به‌روز می‌کند.

چالش‌ها: این رویکرد نمی‌تواند نیاز به ذخیره بخش‌های کوچک ویدیو را برای پخش آینده پیش‌بینی کند. اگر کل ویدیو را ذخیره کنیم، راهی برای دانلود «بخشی» از ویدیو برای کلاینت وجود ندارد. همان‌طور که بعداً می‌بینیم، دانلود «بخشی» از ویدیو به دلایل مختلف برای Stream بسیار مهم است.

رویکرد عالی: پس‌پردازش به Segment و فرمت‌های مختلف

این رویکرد ویدیوها را با تقسیم آن‌ها به Segment های کوچک (هر کدام واحد قابل پخشی که چند ثانیه طول می‌کشد) پس‌پردازش می‌کند و بعد هر Segment را به فرمت‌های مختلف قابل پخش روی دستگاه‌های مختلف تبدیل می‌کند. این طراحی کاملاً از طراحی قبلی بهتر است، چون در آینده پخش ویدیوی کارآمدتری را ممکن می‌کند، که هنگام طراحی سیستم روشن می‌کنیم.

چالش‌ها: این رویکرد کمی پیچیدگی می‌آورد. اول اینکه سرویس پس‌پردازش را پیچیده‌تر می‌کند و آن را به یک خط لوله (Pipeline) تبدیل می‌کند. اول باید ویدیو را به Segment ها تقسیم کند و بعد فرمت‌های ویدیو را روی هر Segment تولید کند. علاوه بر این، سیستم باید ارجاع به این Segment ها را درست ذخیره کند و در پایین‌دست جریان Stream به‌طور مؤثر از آن‌ها استفاده کند. این یک سیستم کلیدی برای روشن کردن در بخش «بررسی عمیق» مصاحبه است.

2) تماشای ویدیو توسط کاربران

حالا که ویدیوها را ذخیره می‌کنیم، کاربران باید بتوانند آن‌ها را تماشا کنند. هنگام تماشای اولیه ویدیو، می‌توانیم فرض کنیم سیستم VideoMetadata را از Video Metadata DB می‌گیرد. چون محتوای ویدیو را در S3 ذخیره می‌کنیم، می‌خواهیم endpoint ‏GET /video را طوری تغییر بدهیم که فقط VideoMetadata را برگرداند، چون آن رکورد URL های لازم برای تماشای ویدیو را دارد. گزینه‌هایی را که برای تماشای ویدیو داریم بررسی می‌کنیم.

رویکرد بد: دانلود کامل ویدیو

این رویکرد شامل دانلود کل ویدیو توسط کلاینت برای پخش آن است. از نظر فنی این «Stream» نیست، بلکه دانلود و پخش ویدیو است. ویدیو از طریق یک URL از S3 دانلود می‌شود.

چالش‌ها: برای پخش ویدیو، کلاینت باید ویدیو را کامل دانلود کند. حتی اگر ویدیو به‌شدت فشرده و وضوح پایین باشد، باز هم می‌تواند بزرگ باشد و به زمان دانلود طولانی برسد. این به دو دلیل مشکل‌ساز است:

  • اگر لازم باشد قبل از پخش کل ویدیو دانلود شود، کاربر ممکن است مدت طولانی برای تماشای ویدیو منتظر بماند. مثلاً دانلود ویدیوی 10 گیگابایتی روی اینترنت 100 مگابیت بر ثانیه بیش از 13 دقیقه طول می‌کشد، که زمان غیرمنطقی است.
  • اگر کلاینت ویدیو را در یک درخواست HTTP بخواهد و در همان درخواست اختلال شبکه رخ بدهد، دانلود ممکن است شکست بخورد و پیشرفت دانلود از دست برود، و زمان زیادی برای تلاش مجدد هدر برود.

به‌طور کلی، این رویکرد برای ساخت سرویس پخش ویدیو عملی نیست.

رویکرد خوب: دانلود تدریجی Segment ها

به‌جای دانلود کامل ویدیو در یک‌بار، سیستم می‌تواند Segment های ویدیو را برای Stream درست ویدیو دانلود کند.

کلاینت یک فرمت ویدیو را بر اساس دستگاه، پهنای باند و ترجیحات کاربر انتخاب می‌کند (مثلاً اگر کاربر ویدیوی HD را مشخص کرده باشد، کلاینت ویدیوی 1080p را پخش می‌کند). سپس کلاینت اولین Segment ویدیو را که چند ثانیه طول می‌کشد بارگذاری می‌کند. این به کاربر اجازه می‌دهد بدون Buffering اضافه سریع شروع به تماشای ویدیو کند. در پس‌زمینه، کلاینت Segment های بیشتری را دانلود می‌کند تا بتواند پخش یکپارچه ویدیو را ادامه بدهد.

چالش‌ها: این رویکرد پیچیده‌تر است و به این متکی است که ویدیوهای آپلودشده به‌صورت Segment ذخیره شوند (که تصمیم طراحی قبلی بود). علاوه بر این، این رویکرد محدودیت‌های نوسان شبکه را وقتی کاربر ویدیو را تماشا می‌کند در نظر نمی‌گیرد. اگر ویدیوی 1080p پخش شود و شرایط شبکه بد شود، دانلود Segment های 1080p ممکن است کند شود و برای کاربر Buffering ایجاد شود.

ظاهراً ممکن است رویکرد «خوب» فقط دانلود «تکه‌تکه» ویدیو به نظر برسد، اما در واقع رویکردی متمایز و کاملاً بهتر و روشن‌تر است. بعضی فرمت‌های ویدیو را می‌توان به‌صورت ترتیبی ذخیره کرد، یعنی فایل را تکه‌تکه (مثلاً 5 مگابایت در هر بار) دانلود کنید و به Stream فایلی برسید که واقعاً قابل پخش است. با این حال این برای همه فرمت‌های ویدیو درست نیست و ممکن است برای مصاحبه‌کننده روشن نباشد که این رویکرد در واقع به داده‌ای می‌رسد که می‌تواند با زمان بازگشت سریع برای کاربر پخش شود. چون راه‌حل «خوب» شامل تقسیم ویدیو به Segment های قابل پخش است، روش‌هایی را که با آن می‌توان سریع و تدریجی پخش کرد به‌وضوح نشان می‌دهد و به راه‌حل «عالی» زیر نزدیک‌تر است.

رویکرد عالی: پخش با نرخ بیت تطبیقی (Adaptive Bitrate Streaming)

Adaptive Bitrate Streaming به ذخیره Segment های ویدیو در فرمت‌های مختلف متکی است. همچنین به یک فایل مانیفست متکی است که هنگام آپلود ویدیو ساخته می‌شود و به همه Segment های ویدیو که در فرمت‌های مختلف موجودند اشاره می‌کند. فایل مانیفست را «ایندکس» همه Segment های مختلف ویدیو با فرمت‌های مختلف در نظر بگیرید. کلاینت از آن برای Stream کردن Segment های ویدیو استفاده می‌کند، چون شرایط شبکه تغییر می‌کند.

هنگام پخش ویدیو، کلاینت این منطق را اجرا می‌کند:

  1. کلاینت Metadata ویدیو را می‌گیرد، که URL فایل مانیفست در S3 را دارد.
  2. کلاینت فایل مانیفست را دانلود می‌کند.
  3. کلاینت بر اساس شرایط شبکه و تنظیمات کاربر یک فرمت را انتخاب می‌کند. URL Segment را در فرمت انتخابی از فایل مانیفست می‌گیرد. اولین Segment را دانلود می‌کند.
  4. کلاینت آن Segment را پخش می‌کند و شروع به دانلود Segment های بیشتر می‌کند.
  5. اگر کلاینت تشخیص بدهد شرایط شبکه در حال بدتر (یا بهتر) شدن است، فرمت Segment های ویدیویی را که دانلود می‌کند تغییر می‌دهد. اگر شرایط شبکه بدتر شود (مثلاً نرخ بیت کمتر شود)، کلاینت سعی می‌کند Segment های فشرده‌تر و با وضوح پایین‌تر را دانلود کند تا از هر وقفه در جریان پخش جلوگیری کند.

چالش‌ها: این رویکرد پیچیده‌ترین است و کلاینت فعال‌تری در سیستم دارد (که بد نیست). همچنین به تصمیم‌های طراحی بالادستی متکی است که شامل تقسیم ویدیو به Segment ها، تنوع فرمت ذخیره‌سازی و ساخت فایل‌های مانیفست است.

بررسی‌های عمیق

1) پردازش ویدیو برای پشتیبانی از Adaptive Bitrate Streaming

پخش روان ویدیو برای تجربه کاربری این سیستم کلیدی است. برای پشتیبانی از پخش یکنواخت ویدیو باید از Adaptive Bitrate Streaming پشتیبانی کنیم، پس کلاینت می‌تواند تدریجی Segment های ویدیو را در فرمت‌های مختلف دانلود کند تا با شرایط متغیر شبکه سازگار شود. برای پشتیبانی از چنین طراحی‌ای، مهم است جزئیات نحوه پردازش و ذخیره داده ویدیو را بررسی کنیم.

وقتی ویدیو در فرمت اصلی‌اش آپلود می‌شود، باید بعد از پردازش به‌عنوان ویدیوی قابل پخش روی طیف گسترده‌ای از دستگاه‌ها در دسترس قرار بگیرد. همان‌طور که قبلاً گفتیم، پردازش ویدیو به یک «خط لوله» تبدیل می‌شود. خروجی این خط لوله:

  • فایل‌های Segment ویدیو در فرمت‌های مختلف (ترکیب کدک و کانتینر) که در S3 ذخیره می‌شوند.
  • فایل‌های مانیفست (یک فایل مانیفست اصلی و چند فایل مانیفست رسانه‌ای) که در S3 ذخیره می‌شوند. فایل‌های مانیفست رسانه‌ای به فایل‌های Segment در S3 ارجاع می‌دهند.

برای ساختن Segment ها و فایل‌های مانیفست، ترتیب گام‌به‌گام عملیات این است:

  1. فایل اصلی را به Segment ها تقسیم کنید (با ابزاری مثل ffmpeg یا مشابه). این Segment ها Encode می‌شوند (از یک Encoding به دیگری تبدیل می‌شوند) و برای ساخت کانتینرهای ویدیویی مختلف استفاده می‌شوند.
  2. هر Segment را Transcode کنید (تبدیل از یک Encoding به دیگری) و جنبه‌های دیگر Segment ها (صدا، تولید رونوشت) را پردازش کنید.
  3. فایل‌های مانیفست را با ارجاع به Segment های مختلف در فرمت‌های ویدیویی مختلف بسازید.
  4. آپلود را «کامل» علامت بزنید.

توجه کنید این طرح فرض می‌کند قبل از شروع پردازش و تقسیم، اول ویدیوی اصلی را کامل آپلود می‌کنیم. اگر کلاینت ویدیو را هنگام آپلود به Segment ها تقسیم کند، بعضی سرویس‌های ویدیو زودتر شروع به پردازش می‌کنند. این یک «Pipeline» ممکن می‌کند که در آن کلاینت بخشی از ویدیو را آپلود می‌کند و کار پایین‌دست می‌تواند قبل از اینکه کلاینت آپلود کامل فرمت اصلی را تمام کند انجام شود. برای سادگی از این رویکرد دوری می‌کنیم، اما اگر بخواهیم تجربه آپلود را برای کاربر سریع کنیم، می‌توانیم این بهینه‌سازی اضافه را در نظر بگیریم.

این مجموعه عملیات را می‌توان «گراف» کارهایی در نظر گرفت که باید انجام شوند. هر عملیات یک مرحله است که بر اساس «وابستگی‌ها»، کار را Fan-out یا Fan-in می‌کند. پردازش Segment ها (Transcode، پردازش صدا، رونویسی) می‌تواند موازی روی گره‌های Worker مختلف انجام شود، چون بین Segment ها وابستگی نیست و Segment ها «تقسیم شده‌اند». با گراف کار و وابستگی‌های یک‌طرفه، کاری که باید اینجا انجام شود را می‌توان گراف جهت‌دار بدون دور (Directed Acyclic Graph یا DAG) در نظر گرفت.

برای این DAG، گران‌ترین محاسبه‌ای که باید انجام شود Encode کردن Segment های ویدیو است. در حالت ایده‌آل، این کار با موازی‌سازی شدید روی بسیاری از کامپیوترها (و هسته‌ها) انجام می‌شود، چون تبدیل ویدیو از یک کدک به کدک دیگر CPU-bound است.

علاوه بر این، برای تنظیم واقعی کار این DAG، می‌توان از یک سیستم ارکستریتور (Orchestrator) برای ساخت گراف کار و اختصاص کار به گره‌های Worker در زمان مناسب استفاده کرد. می‌توانیم فرض کنیم برای این کار از فناوری موجودی مثل Temporal استفاده می‌کنیم.

در نهایت، برای هر داده موقتی که در این خط لوله تولید می‌شود (Segment ها، فایل‌های صوتی و غیره)، می‌توان از S3 استفاده کرد تا از فرستادن فایل‌ها بین Worker ها جلوگیری شود. URL ها را می‌توان برای ارجاع به فایل‌ها فرستاد.

لازم نیست DAG کاملی با مراحل دقیق و مثال‌های دقیق Encode و پردازش صدا و غیره بکشیم. در عوض، مهم است به ورودی‌ها و خروجی‌های صریح پس‌پردازش ویدیو بپردازیم و بفهمیم ویدیوها را چطور به روش مقیاس‌پذیر و کارآمد پردازش کنیم. با توجه به موازی‌سازی و هماهنگی لازم برای پردازش کارآمد ویدیو، توضیح راه‌حل DAG اینجا منطقی است.

2) پشتیبانی از آپلود قابل ازسرگیری

برای پشتیبانی از آپلود قابل ازسرگیری برای ویدیوهای بزرگ‌تر، باید در نظر بگیریم پیشرفت Stream آپلود ویدیو را چطور ردیابی کنیم. توجه کنید این به ردیابی پیشرفت آپلود فایل اصلی اشاره دارد.

اگر تحلیل Dropbox ما را خوانده باشید، می‌فهمید این بررسی عمیق هم‌پوشانی قوی با بررسی عمیق Dropbox دارد که شامل نحوه پشتیبانی از آپلود فایل‌های بزرگ است. خلاصه:

  1. کلاینت فایل ویدیو را به تکه‌هایی (Chunk) تقسیم می‌کند که هرکدام یک هش Fingerprint (اثر انگشت) دارند. هر تکه کوچک است، حدود 5 تا 10 مگابایت.
  2. VideoMetadata فیلدی به نام chunks دارد که فهرستی از JSON های تکه است، هرکدام با فیلد Fingerprint و وضعیت.
  3. کلاینت از باطن می‌خواهد Metadata ویدیو را با فهرست تکه‌ها به‌روز کند، هرکدام با وضعیت NotUploaded.
  4. کلاینت هر تکه را در S3 آپلود می‌کند.
  5. S3 اعلان‌های رویداد S3 را به یک AWS Lambda می‌فرستد که VideoMetadata را با علامت زدن تکه (که با Fingerprint اش مشخص می‌شود) به‌عنوان آپلودشده به‌روز می‌کند.
  6. اگر کلاینت آپلود را متوقف کند، می‌تواند با گرفتن Metadata ویدیو برای دیدن تکه‌های آپلودشده و رد کردن تکه‌هایی که قبلاً آپلود شده‌اند، آن را از سر بگیرد.

این موارد در عمل با آپلود چندقسمتی AWS S3 (Multipart Upload) انجام می‌شود. با این حال، مرور جزئیات در مصاحبه‌ای مثل این برای نشان دادن عمق درک شما از چگونگی آپلود فایل در عمل مهم است.

3) مقیاس‌دهی به تعداد زیاد ویدیوی آپلودشده و تماشاشده در روز

سیستم ما فرض می‌کند حدود 1 میلیون ویدیو در روز آپلود و 100 میلیون ویدیو در روز تماشا می‌شود. این ترافیک بسیار زیاد است و لازم است همه سیستم‌هایمان مقیاس بگیرند و تجربه قوی برای کاربران نهایی تضمین شود. اجزای اصلی سیستم را مرور می‌کنیم تا مقیاسشان را تحلیل کنیم:

  • Video Service (سرویس ویدیو): این سرویس بدون حالت (Stateless) است و مسئول پاسخ به درخواست‌های HTTP برای Presigned URL ها و درخواست‌های endpoint ‏Metadata ویدیو خواهد بود. می‌توان آن را افقی مقیاس داد و Load Balancer جلویش دارد.
  • Video Metadata (Cassandra): این یک پایگاه داده Cassandra است و به‌خاطر Replication بدون Leader و Consistent Hashing داخلی Cassandra، کارآمد افقی مقیاس می‌گیرد. ویدیوها یکنواخت توزیع می‌شوند، چون داده بر اساس videoId پارتیشن شده است. شایان ذکر است گره‌ای که ویدیوی پرطرفدار را نگه می‌دارد ممکن است روی آن ویدیو «داغ» شود، که می‌تواند گلوگاه باشد.
  • Video Processing Service (سرویس پردازش ویدیو): این سرویس می‌تواند برای تعداد زیاد ویدیو مقیاس بگیرد و می‌تواند هماهنگی داخلی درباره نحوه توزیع کار DAG بین گره‌های Worker داشته باشد. شایان ذکر است این سرویس احتمالاً مکانیزم صف داخلی دارد که ما تصورش نمی‌کنیم. این سیستم صف به آن اجازه می‌دهد جهش آپلود ویدیو را تحمل کند. علاوه بر این، تعداد کارهای داخل صف ممکن است محرکی باشد برای اینکه این سیستم الاستیک مقیاس بگیرد و Worker بیشتری داشته باشد.
  • S3: S3 با ترافیک بالا و حجم فایل بالا خوب کنار می‌آید. چندمنطقه‌ای (Multi-region) است و می‌تواند الاستیک مقیاس بگیرد. با این حال، اگر ویدیو توسط مخاطبان گسترده‌ای پخش شود، ممکن است مرکز داده‌ای که S3 را نگه می‌دارد از بخشی از کاربرانی که ویدیو را پخش می‌کنند دور باشد، که ممکن است بارگذاری اولیه ویدیو را کند کند یا برای آن کاربران Buffering بسازد.

بر اساس تحلیل بالا، چند فرصت برای بهتر کردن توانایی طراحی در مقیاس و ارائه تجربه کاربری عالی برای پایگاه کاربری گسترده شناسایی کردیم.

برای رفع مشکل ویدیوی داغ می‌توانیم Cassandra را طوری تنظیم کنیم که داده را روی چند گره Replicate کند تا بتوانند بار ذخیره Metadata ویدیو را تقسیم کنند. یعنی چند گره می‌توانند کوئری داده ویدیو را سرو کنند. علاوه بر این، می‌توانیم یک کش اضافه کنیم که Metadata ویدیوهای پربازدید را ذخیره می‌کند. این کش می‌تواند Metadata ویدیوهای محبوب را نگه دارد تا از جست‌وجو در پایگاه داده برای آن‌ها دوری کنیم. کش را می‌توان توزیع کرد، از استراتژی Least Recently Used (LRU) استفاده کرد و بر اساس videoId هم پارتیشن کرد. کش راه سریع‌تری برای بازیابی داده ویدیوهای محبوب است و کمی پایگاه داده را عایق می‌کند.

الگو: مقیاس‌دهی خواندن (Scaling Reads). پلتفرم‌های ویدیو مثل YouTube چالش‌های کلاسیک مقیاس‌دهی خواندن را با میلیاردها بازدید روزانه نشان می‌دهند. ویدیوهای وایرال نقطه‌های داغ خواندن می‌سازند که به کش تهاجمی Metadata، توزیع CDN برای محتوای ویدیو و Read Replica برای مقیاس‌دهی پایگاه داده نیاز دارند. نسبت خواندن به نوشتن بسیار زیاد است: ویدیوی وایرال ممکن است میلیون‌ها بار دیده شود اما فقط یک‌بار آپلود شود.

برای مدیریت مشکل Stream برای کاربرانی که ممکن است از مراکز داده S3 دور باشند، می‌توانیم از CDN استفاده کنیم. CDN ها می‌توانند فایل‌های ویدیوی محبوب (هم Segment ها و هم فایل‌های مانیفست) را کش کنند و از نظر جغرافیایی از طریق شبکه‌ای از سرورهای لبه (Edge) به بسیاری از کاربران نزدیک باشند. یعنی داده ویدیو باید مسافت بسیار کوتاه‌تری تا رسیدن به کاربران طی کند و کندی و Buffering کم می‌شود. همچنین اگر همه داده (فایل‌های Segment و مانیفست) در CDN باشد، آن‌وقت برای ادامه پخش ویدیو اصلاً لازم نیست با باطن تعامل کنیم.

سیستم نهایی ما با اعمال بهبودهای بالا شامل این اجزاست: کلاینت، API Gateway، Video Service، Video Metadata DB (Cassandra) با کش، S3 برای فایل اصلی و Segment ها و مانیفست‌ها، Video Processing Service با ارکستریتور DAG و CDN جلوی S3.

بررسی‌های عمیق اضافی

YouTube اپلیکیشن پیچیده و جالبی است و پرداختن به همه ملاحظات ممکن در این راهنما دشوار است. چند بررسی عمیق اضافی که ممکن است در نظر بگیرید:

  • سریع‌تر کردن آپلود: طراحی بالا فرض می‌کند کلاینت اول کل فایل ویدیو را آپلود می‌کند، قبل از اینکه پس‌پردازش (تولید فایل‌های Segment شده، Encode شده و ساخت مانیفست) انجام شود. برای سریع‌تر کردن آپلود، ممکن است «Pipeline کردن» آپلود و پس‌پردازش را در نظر بگیریم. کلاینت می‌تواند ویدیو را Segment کند و آپلود کند و باطن می‌تواند همان Segment ها را برداشته و فوراً روی آن‌ها کار کند. این از کلاینت می‌خواهد در پردازش ویدیو نقش داشته باشد و اگر ویدیو کامل آپلود نشود می‌تواند Segment های «آشغال» بسازد، اما به‌طور متوسط به بهتر شدن تجربه کاربری و سریع‌تر شدن آپلود کمک می‌کند.
  • ادامه پخش از جایی که کاربر متوقف کرده: بسیاری از اپلیکیشن‌ها امکان ادامه تماشای ویدیو از همان‌جایی را می‌دهند که کاربر قبلاً متوقف کرده است. این به ذخیره داده بیشتر برای هر کاربر در هر ویدیو نیاز دارد.
  • شمارش بازدید: اگر مصاحبه‌کننده بخواهد ویژگی‌های بیشتری به سیستم اضافه شود، می‌توانید گزینه‌های مختلف برای شمارش بازدید ویدیو، چه دقیق چه تقریبی، را در نظر بگیرید. این به‌راحتی می‌تواند یک بررسی عمیق مستقل باشد.

انتظارات در هر سطح

شاید فکر کنید «واقعاً چقدر از این‌ها در مصاحبه لازم است؟» بیایید تفکیک کنیم.

سطح Mid-level

  • عرض در برابر عمق: تمرکز بیشتر روی عرض است (حدود 80 درصد عرض و 20 درصد عمق). باید بتوانید طراحی سطح بالایی بسازید که نیازمندی‌های عملکردی تعریف‌شده را پوشش بدهد، هرچند با بسیاری از اجزا فقط آشنایی سطحی داشته باشید.
  • مرور مبانی: مصاحبه‌کننده مدتی را صرف بررسی مبانی می‌کند تا مطمئن شود می‌دانید هر جزء چه کاری می‌کند. مثلاً اگر API Gateway اضافه کنید، انتظار داشته باشید بپرسد چه کاری می‌کند و در سطح بالا چطور کار می‌کند. او هیچ چیز را بدیهی فرض نمی‌کند.
  • ترکیب هدایت و همراهی: در مراحل اولیه مصاحبه باید خودتان جلو بروید، اما مصاحبه‌کننده انتظار ندارد مشکلات طراحی را با دقت بالا پیش‌دستانه تشخیص بدهید. طبیعی است که در مراحل بعد، با بررسی طراحی شما، خودش هدایت را به دست بگیرد.
  • معیار برای YouTube: داوطلب Mid-level باید endpoint های API و مدل داده را روشن تعریف کرده باشد و طراحی سطح بالایی ساخته باشد که برای آپلود و پخش ویدیو کاربردی است. انتظار ندارم داوطلبان دانش عمیقی از پخش ویدیو داشته باشند، اما باید روی ایده‌های مربوط به آپلود چندقسمتی و پخش مبتنی بر Segment همگرا شوند. همچنین انتظار دارم داوطلب نیاز به ارتباط مستقیم با سیستمی مثل S3 را برای آپلود و Stream بفهمد. انتظار دارم درباره یک موضوع مرتبط با «بررسی عمیق» شفافیت ایجاد کند.

سطح Senior

  • عمق تخصص: انتظار به سمت دانش عمیق‌تر می‌رود، حدود 60 درصد عرض و 40 درصد عمق. باید در حوزه‌هایی که تجربه عملی دارید وارد جزئیات فنی شوید و درک عمیقی از مفاهیم و فناوری‌های کلیدی مسئله نشان بدهید.
  • طراحی سیستم پیشرفته: باید با اصول پیشرفته طراحی سیستم (فناوری‌های مختلف، موارد استفاده‌شان و چگونگی تطبیق آن‌ها با هم) آشنا باشید. توانایی شما در حرکت با اطمینان و وضوح در این موضوع‌های پیشرفته کلیدی است.
  • تشریح تصمیم‌های معماری: باید مزایا و معایب انتخاب‌های معماری را روشن بیان کنید، به‌ویژه اثر آن‌ها روی مقیاس‌پذیری، عملکرد و قابلیت نگهداری، و تصمیم‌هایتان را توجیه کنید.
  • حل مسئله و پیش‌قدمی: باید مهارت حل مسئله قوی و رویکرد فعالانه نشان بدهید. یعنی چالش‌های احتمالی طراحی را پیش‌بینی کنید، بهبود پیشنهاد بدهید، گلوگاه‌ها را پیدا و رفع کنید، عملکرد را بهینه کنید و از قابلیت اطمینان سیستم مطمئن شوید.
  • معیار برای YouTube: از داوطلبان Senior انتظار می‌رود طراحی سطح بالای اولیه را سریع جلو ببرند تا بتوانند وقتشان را صرف بحث درباره نحوه مدیریت پس‌پردازش ویدیو و جزئیات آپلود کنند. انتظار دارم داوطلب درباره آپلود چندقسمتی بداند و درباره نحوه استفاده از آن برای مدیریت آپلودهای قابل ازسرگیری بحث کند. همچنین انتظار دارم بداند ویدیو چطور کارآمد پس‌پردازش می‌شود تا فایل‌هایی بسازد که پخش تطبیقی را ممکن کنند.

سطح Staff به بالا

  • تأکید بر عمق: انتظار می‌رود وارد ظرافت‌های طراحی سیستم شوید، با حدود 40 درصد عرض و 60 درصد عمق. حتی اگر این مسئله خاص را قبلاً حل نکرده باشید، باید در دنیای واقعی آن‌قدر مسئله حل کرده باشید که با پشتوانه تجربه‌تان با اطمینان راه‌حل طراحی کنید.
  • دانش عملی فناوری‌ها: باید بدانید از چه فناوری‌هایی استفاده کنید، نه فقط در تئوری بلکه در عمل، و با تجربه‌های قبلی توضیح بدهید چطور برای حل مسائل خاص به کار می‌روند. مصاحبه‌کننده می‌داند مباحث ساده‌تر (REST API، نرمال‌سازی داده و غیره) را می‌دانید، پس سریع از آن‌ها عبور می‌کنید تا برای بخش‌های جذاب‌تر وقت بماند.
  • پیش‌قدمی بالا: در این سطح انتظار درجه استثنایی از پیش‌قدمی می‌رود. باید مسائل را مستقل شناسایی و حل کنید و چالش‌های اصلی طراحی را بشناسید. این فقط پاسخ به مشکلات پس از بروز نیست، بلکه پیش‌بینی آن‌ها و اجرای راه‌حل‌های پیشگیرانه هم هست. مصاحبه‌کننده فقط باید برای تمرکز دخالت کند، نه برای هدایت.
  • کاربرد عملی فناوری: باید در به‌کارگیری عملی فناوری‌های مختلف مسلط باشید. تجربه شما باید گفت‌وگو را هدایت کند و درک روشنی از پیکربندی ابزارها و سیستم‌ها در سناریوهای واقعی نشان بدهد.
  • حل مسئله پیچیده و تصمیم‌گیری: نه‌تنها باید چالش‌های فنی پیچیده را حل کنید، بلکه تصمیم‌های آگاهانه‌ای بگیرید که مقیاس‌پذیری، عملکرد، قابلیت اطمینان و نگهداری را در نظر می‌گیرد.
  • طراحی پیشرفته و مقیاس‌پذیری: رویکرد شما باید بر مقیاس‌پذیری و قابلیت اطمینان در بار بالا متمرکز باشد. این شامل درک کامل سیستم‌های توزیع‌شده، Load Balancing، استراتژی‌های ذخیره‌سازی و دیگر مفاهیم پیشرفته است.
  • معیار برای YouTube: برای داوطلب Staff به بالا، انتظار از عمق و کیفیت راه‌حل‌ها بالاست، به‌ویژه برای سناریوهای پیچیده‌ای که بحث شد. داوطلبان استثنایی در هر یک از موضوع‌های بالا عمیق می‌شوند و حتی ممکن است گفت‌وگو را به سمت موضوعی که به نظرشان جالب یا مرتبط‌تر است هدایت کنند و گسترده روی آن تمرکز کنند. همچنین انتظار می‌رود درک کاملی از مبادلات بین راه‌حل‌های مختلف داشته باشند، بتوانند آن‌ها را روشن بیان کنند و با مصاحبه‌کننده مثل یک همتا رفتار کنند.