Loading...

مطالعه موردی

نظرسنجی زنده در مقیاس: الگوی کش و پاکسازی

نظرسنجی روی پخش زنده بدترین الگوی خواندن ممکن است: همه بینندگان در یک ثانیه یک پاسخ را می‌خواهند، و لحظه‌ای که مجری سؤال تازه‌ای باز می‌کند، همه دوباره می‌پرسند. سرکشی دوره‌ای به بک‌اند از پس این برنمی‌آید. ما نظرسنجی‌های yayında.tv و تعامل‌های داخل پخش‌کننده Videoonly را با تقریباً یک درخواست به مبدأ به‌ازای هر تغییر اجرا می‌کنیم — بدون چیزی عجیب‌تر از یک URL کش‌شده و یک فراخوان پاکسازی.

9 متوسط Updated

نظرسنجی زنده در مقیاس: الگوی کش و پاکسازی

چرا نظرسنجی زنده راه‌حل بدیهی را می‌شکند

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

همه یک لحظه را تماشا می‌کنند. مجری سؤالی باز می‌کند و سی هزار مرورگر در همان ثانیه آن را می‌خواهند. همه پاسخی یکسان می‌خواهند، یعنی شما همان کوئری را سی هزار بار اجرا می‌کنید و همان بایت‌ها را برمی‌گردانید. بعد هم تا پایان پخش هر چند ثانیه دوباره می‌پرسند، چیزی عوض شده باشد یا نه.

راه فرار معمول WebSocket است: نگه‌داشتن یک اتصال برای هر بیننده و ارسال داده. کار می‌کند، اما وضعیت اتصال، مقیاس‌دهی لایه سوکت، طوفان اتصال مجدد وقتی شبکه موبایل می‌لنگد، و مسیر جایگزین برای کلاینت‌هایی که نمی‌توانند اتصال نگه دارند را هم به گردن گرفته‌اید.

گزینه ساده‌تری هست و همان است که ما در محیط واقعی اجرا می‌کنیم.

الگو: یک URL کش‌شده که هنگام تغییر پاک می‌شود

وضعیت نظرسنجی کوچک است، برای همه یکسان است و به‌ندرت تغییر می‌کند — چند بار در هر پخش. این دقیقاً همان شکلی است که CDN برایش ساخته شده.

پس آن را به‌صورت یک URL جیسون ساده و قابل کش منتشر کنید. بینندگان آن URL را می‌گیرند و نزدیک‌ترین لبه پاسخ می‌دهد. مبدأ شما اولین درخواست هر موقعیت را می‌بیند و پس از آن عملاً هیچ، هر تعداد بیننده که باشد.

وقتی مجری سؤالی را باز، بسته یا ویرایش می‌کند، بک‌اند همان یک URL را پاک می‌کند. درخواست بعدی روی لبه خطا می‌خورد، یک‌بار وضعیت تازه را می‌گیرد، و همه بینندگان بعدی دوباره از کش سرویس می‌گیرند.

نکته جالب این است که با رشد مخاطب چه اتفاقی می‌افتد: هیچ. ده هزار بیننده یا صد هزار، مبدأ همچنان یک درخواست برای هر تغییر در هر موقعیت می‌بیند. هزینه تابع این است که نظرسنجی چند بار عوض می‌شود، نه اینکه چند نفر تماشا می‌کنند.

در yayında.tv چطور کار می‌کند

yayında.tv پلتفرم پخش زنده ماست و کل سازوکار همین است.

سمت خواندن یک نقطه‌پایانی است که نظرسنجی فعال را با پرسش‌ها و پاسخ‌هایش به‌صورت جیسون برمی‌گرداند و از طریق CDN سرویس می‌شود. هدرهای CORS بازی می‌فرستد تا پخش‌کننده بتواند آن را از هر دامنه‌ای که پخش در آن جاسازی شده بگیرد. URL یک هش برگرفته از نام کانال و یک راز سمت سرور دارد؛ این کار حدس‌زدن ساده را می‌بندد و در عین حال قابلیت کش را کاملاً حفظ می‌کند — URL برای هر کانال ثابت است، پس یک کلید کش واقعی است نه کلیدی به‌ازای هر کاربر.

سمت نوشتن چند خط در کنترلری است که نظرسنجی را شروع یا متوقف می‌کند: همان URL را بساز، API پاکسازی را صدا بزن، تمام. نه صفی هست، نه توزیعی، نه لایه سوکتی — توزیع را خود CDN انجام می‌دهد.

The two halves
// READ — what every viewer hits, cached at the edge
GET https://cdn.example.tv/{channel}/survey?s={hash}

{
  "status": "success",
  "survey": {
    "id": 42,
    "questions": [
      { "text": "Who takes the penalty?",
        "answers": [{"id": 1, "text": "..."}, {"id": 2, "text": "..."}] }
    ]
  }
}

// WRITE — the broadcaster opens the poll; the backend purges that one URL
cdnctl purge --account <account_uuid> \
             --path "/{channel}/survey?s={hash}" --type exact

// or straight from the app, on the same event that flips the poll live

و داخل پخش‌کننده: Videoonly

Videoonly همین ایده را داخل پخش‌کننده ویدئو به کار می‌برد. تعامل‌های داخل پخش‌کننده — لایه پرسش، کارت اسپانسر، فراخوان اقدامی که به لحظه‌ای از پخش گره خورده — همه از نظر شکل یک مسئله‌اند: تکه کوچکی از وضعیت که همه بینندگان به آن نیاز دارند و وقتی یک اپراتور تصمیم بگیرد تغییر می‌کند.

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

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

درست‌درآوردن جزئیات

چند چیز تعیین می‌کند این کار در عمل روان باشد یا آزاردهنده.

**URL را ثابت نگه دارید.** هر چیز مخصوص کاربر در مسیر یا کوئری — شناسه نشست، مهر زمانی ضدکش — به هر بیننده کلید کش خودش را می‌دهد و دوباره به‌ازای همه به مبدأ می‌زنید. جای شخصی‌سازی، درخواست دوم و جداست، نه درخواست مشترک.

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

**دقیقاً همان URLی را پاک کنید که سرویس می‌دهید.** اگر شیء کش‌شده `?s=abc` است و شما مسیر را بدون کوئری پاک کنید، هیچ چیز باطل نمی‌شود و نظرسنجی قدیمی سر جایش می‌ماند. این رایج‌ترین راهی است که این الگو بی‌صدا خراب می‌شود.

**خواندن را از نوشتن جدا کنید.** بینندگان URL کش‌شده را می‌خوانند؛ رأی‌ها به یک نقطه‌پایانی معمولی و بدون کش می‌روند. نوشتن کسری از خواندن است — هر بیننده یک‌بار رأی می‌دهد و پیوسته می‌خواند — پس مسیر نوشتن می‌تواند عادی بماند.

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

چه وقت سراغ WebSocket برویم

این الگو جایگزین همه‌کاره نیست و بهتر است روشن بگوییم کجا تمام می‌شود.

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

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

پرسش‌های پرتکرار

بینندگان چقدر سریع تغییر را می‌بینند؟

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

اگر فراخوان پاکسازی شکست بخورد چه؟

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

با وجود کوئری‌استرینگ در URL کار می‌کند؟

بله، تا وقتی CDN کوئری‌استرینگ را در کلید کش لحاظ کند و شما دقیقاً همان URLی را که سرویس می‌دهید پاک کنید. پاک‌کردن تنها مسیر، درحالی‌که شیء با کوئری‌استرینگ کش شده، اشتباه کلاسیک است.

رأی‌ها کجا می‌روند؟

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

آیا از WebSocket ارزان‌تر است؟

معمولاً بله، و صرفه‌جویی بزرگ‌تر عملیاتی است. نه لایه سوکتی هست که مقیاس بدهید، نه وضعیت اتصالی که نگه دارید، نه طوفان اتصال مجدد وقتی شبکه موبایل قطع می‌شود. هزینه مبدأ تابع تواتر تغییر نظرسنجی است، نه تعداد بینندگان.