Loading...

دراسة حالة

استطلاعات مباشرة على نطاق واسع: نمط التخزين المؤقت والتفريغ

الاستطلاع أثناء البث المباشر هو أسوأ نمط قراءة ممكن: كل المشاهدين يريدون الإجابة نفسها في الثانية نفسها، وما إن يفتح المقدّم سؤالًا جديدًا حتى يسألوا جميعًا من جديد. استجواب الواجهة الخلفية دوريًا لا يصمد أمام ذلك. هكذا نشغّل استطلاعات yayında.tv والتفاعلات داخل مشغّل Videoonly بطلب واحد تقريبًا إلى الخادم الأصلي لكل تغيير، بلا شيء أغرب من رابط مخزَّن مؤقتًا واستدعاء تفريغ.

9 متوسط Updated

استطلاعات مباشرة على نطاق واسع: نمط التخزين المؤقت والتفريغ

لماذا يكسر الاستطلاع المباشر الحلّ البديهي

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

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

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

هناك خيار أبسط، وهو ما نشغّله في الإنتاج.

النمط: رابط واحد مخزَّن مؤقتًا، يُفرَّغ عند التغيير

حالة الاستطلاع صغيرة، ومتطابقة للجميع، ونادرة التغيّر — بضع مرات في البث الواحد. وهذا بالضبط الشكل الذي بُنيت له شبكات CDN.

لذا انشرها كرابط JSON عادي قابل للتخزين المؤقت. يطلب المشاهدون ذلك الرابط فتجيبهم أقرب حافة. يرى خادمك الأصلي أول طلب في كل موقع ولا شيء تقريبًا بعده، مهما بلغ عدد المشاهدين.

وحين يفتح المقدّم سؤالًا أو يغلقه أو يعدّله، تفرّغ الواجهة الخلفية ذلك الرابط الواحد. يخطئ الطلب التالي التخزين المؤقت على الحافة فيجلب الحالة الجديدة مرة واحدة، ثم يُخدَم كل من بعده من التخزين المؤقت مجددًا.

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

كيف يبدو ذلك في yayında.tv

yayında.tv هي منصّتنا للبث المباشر، وهذه هي الآلية كاملة.

جانب القراءة نقطة نهاية واحدة تعيد الاستطلاع النشِط بأسئلته وإجاباته بصيغة JSON، وتُقدَّم عبر شبكة CDN. ترسل ترويسات CORS متساهلة كي يتمكن المشغّل من جلبها من أي نطاق يُضمَّن فيه البث. يحمل الرابط بصمة مشتقّة من اسم القناة ومفتاح سري على الخادم، ما يمنع تخمينه بسهولة مع بقائه قابلًا للتخزين المؤقت تمامًا — فالرابط ثابت لقناة بعينها، أي أنه مفتاح تخزين مؤقت حقيقي وليس مفتاحًا لكل مستخدم.

وجانب الكتابة بضعة أسطر في المتحكّم الذي يبدأ الاستطلاع أو يوقفه: ابنِ الرابط نفسه، استدعِ واجهة التفريغ، انتهى الأمر. لا طابور ولا توزيع ولا طبقة سوكِت — شبكة 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 الفكرة نفسها داخل مشغّل الفيديو. التفاعلات داخل المشغّل — طبقة سؤال، بطاقة راعٍ، دعوة لاتخاذ إجراء موقوتة على لحظة في البث — كلها المشكلة نفسها شكلًا: قطعة حالة صغيرة يحتاجها كل مشاهد، وتتغيّر حين يقرّر مشغّل تغييرها.

لذا يجلب المشغّل إعدادات الطبقات من رابط مخزَّن مؤقتًا بدل استجواب الواجهة البرمجية بمؤقّت. وحين يجدول أحدهم طبقة أو يسحبها، يُفرَّغ ذلك الرابط. يلتقط المشغّل التغيير في الجلب التالي، ولا تكون الواجهة البرمجية في المسار الساخن إطلاقًا.

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

ضبط التفاصيل بشكل صحيح

بضعة أمور تحدّد إن كان هذا سلسًا أم مزعجًا عمليًا.

**أبقِ الرابط ثابتًا.** أي شيء خاص بالمستخدم في المسار أو الاستعلام — معرّف جلسة، طابع زمني لكسر التخزين المؤقت — يمنح كل مشاهد مفتاح تخزين خاصًا به، فتعود إلى ضرب الأصل بالجميع. مكان التخصيص طلب ثانٍ منفصل، لا الطلب المشترك.

**اضبط مدة صلاحية تستطيع التعايش معها أصلًا.** التفريغ هو ما يجعل التغييرات سريعة، لكن مدة صلاحية محدودة هي شبكة أمانك يوم يفشل استدعاء التفريغ. قصيرة بما يكفي ليكون التفريغ الفائت مجرد تعثّر، وطويلة بما يكفي ليبقى الأصل هادئًا.

**فرّغ الرابط نفسه الذي تقدّمه.** إذا كان الكائن المخزَّن `?s=abc` وفرّغت المسار من دون الاستعلام، لن يُبطَل شيء ويبقى الاستطلاع القديم معروضًا. هذه أشيع طريقة يفشل بها النمط بصمت.

**افصل القراءة عن الكتابة.** يقرأ المشاهدون الرابط المخزَّن؛ وتذهب الأصوات إلى نقطة نهاية عادية غير مخزَّنة. الكتابات كسر ضئيل من القراءات — يصوّت المشاهد مرة ويقرأ باستمرار — فيبقى مسار الكتابة عاديًا.

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

متى تلجأ إلى WebSockets بدلًا من ذلك

هذا النمط ليس بديلًا شاملًا، ومن الأنصف توضيح أين ينتهي.

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

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

أسئلة شائعة

بأي سرعة يرى المشاهدون التغيير؟

بسرعة انتشار التفريغ مضافًا إليها الجلب التالي من العميل. عمليًا ثانية أو ثانيتان — وهذا أكثر من كافٍ لبث مباشر، لأن المقدّم يتحدث فوق الانتقال أصلًا.

ماذا لو فشل استدعاء التفريغ؟

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

هل يعمل هذا مع سلاسل الاستعلام في الرابط؟

نعم، ما دامت الشبكة تُدخل سلسلة الاستعلام في مفتاح التخزين المؤقت وتفرّغ أنت الرابط نفسه الذي تقدّمه. تفريغ المسار وحده بينما الكائن مخزَّن مع سلسلة استعلام هو الخطأ الكلاسيكي.

إلى أين تذهب الأصوات؟

إلى نقطة نهاية عادية غير مخزَّنة مؤقتًا. القراءة المشتركة وحدها هي المخزَّنة. الأصوات كسر ضئيل من الترافيك، لأن كل مشاهد يصوّت مرة ويقرأ باستمرار.

هل هذا أرخص من WebSockets؟

غالبًا نعم، والتوفير الأكبر تشغيلي. لا طبقة سوكِت تحتاج توسيعًا، ولا حالة اتصال تُحفَظ، ولا عاصفة إعادة اتصال حين تسقط شبكة الجوال. كلفة الأصل تتبع تواتر تغيّر الاستطلاع لا عدد المشاهدين.