
منذ البداية ، قمنا بتخطيط الحلول الهندسية والمنتجات بحيث تكون ديسكورد مناسبة تمامًا للدردشة الصوتية أثناء اللعب مع الأصدقاء. سمحت هذه الحلول بتوسيع نطاق النظام بشكل كبير ، مع وجود فريق صغير وموارد محدودة.
تتناول هذه المقالة التقنيات المختلفة التي يستخدمها Discord لدردشات الصوت / الفيديو.
للتوضيح ، سوف نطلق على المجموعة الكاملة من المستخدمين والقنوات "المجموعة" (النقابة) - في العميل تسمى "الخوادم". بدلاً من ذلك ، يشير مصطلح "الخادم" إلى البنية التحتية للخادم.المبادئ الرئيسية
تدعم كل محادثة صوتية / مرئية في Discord العديد من المشاركين. شاهدنا ألف شخص يتحدثون بدورهم في محادثات جماعية كبيرة. يتطلب مثل هذا الدعم بنية خادم العميل ، لأن شبكة نظير إلى نظير تصبح باهظة التكلفة مع زيادة عدد المشاركين.
يضمن توجيه حركة مرور الشبكة عبر خوادم Discord أيضًا أن عنوان IP الخاص بك لن يكون مرئيًا أبدًا - ولن يقوم أحد بشن هجوم DDoS. التوجيه عبر الخوادم له مزايا أخرى: على سبيل المثال ، الإشراف. يمكن للمسؤولين إيقاف تشغيل الصوت والفيديو للمتطفلين.
هندسة العميل
يعمل Discord على العديد من المنصات.
- الويب (Chrome / Firefox / Edge ، إلخ.)
- تطبيق مستقل (Windows و MacOS و Linux)
- هاتف (iOS / Android)
يمكننا دعم جميع هذه الأنظمة الأساسية بطريقة واحدة فقط: من خلال إعادة استخدام رمز
WebRTC . تتضمن هذه المواصفات للاتصالات في الوقت الحقيقي مكونات الشبكة والصوت والفيديو. تم اعتماد المعيار من
قبل اتحاد شبكة الويب العالمية ومجموعة
هندسة الإنترنت . WebRTC متاح في جميع المتصفحات الحديثة وكمكتبة أصلية للتنفيذ في التطبيقات.
يعمل الصوت والفيديو في Discord على WebRTC. وبالتالي ، يعتمد تطبيق المتصفح على تنفيذ WebRTC في المتصفح. ومع ذلك ، تستخدم تطبيقات أجهزة الكمبيوتر المكتبية و iOS و Android محرك وسائط متعددة C ++ واحد مبني على مكتبة WebRTC الخاصة بهم ، والتي يتم تكييفها خصيصًا لاحتياجات مستخدمينا. هذا يعني أن بعض الوظائف في التطبيق تعمل بشكل أفضل من المتصفح. على سبيل المثال ، في تطبيقاتنا الأصلية يمكننا:
- تجاوز كتم صوت Windows افتراضيًا ، عندما يتم كتم صوت جميع التطبيقات تلقائيًا عند استخدام سماعة رأس . هذا غير مرغوب فيه عندما تقوم أنت وأصدقاؤك بغارة وتنسيق أنشطة دردشة Discord.
- استخدم التحكم في مستوى الصوت بدلاً من خالط نظام التشغيل العالمي.
- معالجة البيانات الصوتية الأصلية للكشف عن النشاط الصوتي وبث الصوت والفيديو في الألعاب.
- قلل من عرض النطاق الترددي واستهلاك وحدة المعالجة المركزية خلال فترات الصمت - حتى في أكثر الدردشات الصوتية في أي وقت ، لا يتحدث سوى عدد قليل من الأشخاص في نفس الوقت.
- توفير وظائف على مستوى النظام لوضع الضغط والتحدث.
- أرسل مع حزم الصوت والفيديو معلومات إضافية (على سبيل المثال ، مؤشر أولوية في الدردشة).
إن امتلاك نسختك الخاصة من WebRTC يعني تحديثات متكررة لجميع المستخدمين: هذه عملية تستغرق وقتًا طويلاً ونحن نحاول أتمتة. ومع ذلك ، فإن هذا الجهد يؤتي ثماره بفضل الميزات المحددة للاعبين لدينا.
في Discord ، يبدأ الاتصال الصوتي والفيديو عن طريق إدخال قناة صوتية أو مكالمة. أي أن الاتصال يبدأ دائمًا من قبل العميل - وهذا يقلل من تعقيد أجزاء العميل والخادم ، ويزيد أيضًا من تحمل الخطأ. في حالة فشل البنية التحتية ، يمكن للمشاركين ببساطة إعادة الاتصال بالخادم الداخلي الجديد.
تحت سيطرتنا
يتيح لك التحكم في المكتبة الأصلية تنفيذ بعض الوظائف بشكل مختلف عن تنفيذ متصفح WebRTC.
أولاً ، يعتمد WebRTC على بروتوكول وصف الجلسة (
SDP ) للتفاوض على الصوت / الفيديو بين المشاركين (حتى 10 كيلوبايت لكل تبادل للحزم). في مكتبتها الخاصة ، يتم استخدام API ذات المستوى الأدنى من WebRTC (
webrtc::Call
) لإنشاء كل من التدفقات - الواردة والصادرة. عند الاتصال بقناة صوتية ، يكون هناك حد أدنى من تبادل المعلومات. هذا هو عنوان الخادم الخلفي والمنفذ وطريقة التشفير والمفاتيح وبرامج الترميز وتحديد الدفق (حوالي 1000 بايت).
webrtc::AudioSendStream* createAudioSendStream( uint32_t ssrc, uint8_t payloadType, webrtc::Transport* transport, rtc::scoped_refptr<webrtc::AudioEncoderFactory> audioEncoderFactory, webrtc::Call* call) { webrtc::AudioSendStream::Config config{transport}; config.rtp.ssrc = ssrc; config.rtp.extensions = {{"urn:ietf:params:rtp-hdrext:ssrc-audio-level", 1}}; config.encoder_factory = audioEncoderFactory; const webrtc::SdpAudioFormat kOpusFormat = {"opus", 48000, 2}; config.send_codec_spec = webrtc::AudioSendStream::Config::SendCodecSpec(payloadType, kOpusFormat); webrtc::AudioSendStream* audioStream = call->CreateAudioSendStream(config); audioStream->Start(); return audioStream; }
بالإضافة إلى ذلك ، يستخدم WebRTC مؤسسة الاتصال التفاعلي (
ICE ) لتحديد أفضل مسار بين المشاركين. نظرًا لأن لدينا كل عميل متصل بالخادم ، فإننا لا نحتاج إلى ICE. يتيح لك ذلك توفير اتصال أكثر موثوقية إذا كنت وراء NAT ، وكذلك الحفاظ على سرية عنوان IP الخاص بك من المشاركين الآخرين. عملاء ping بشكل دوري بحيث يحافظ جدار الحماية على اتصال مفتوح.
وأخيرًا ، يستخدم WebRTC بروتوكول النقل الآمن في الوقت الحقيقي (
SRTP ) لتشفير الوسائط. يتم تعيين مفاتيح التشفير باستخدام بروتوكول Datagram Transport Layer Security (
DTLS ) بناءً على TLS القياسي. تسمح لك مكتبة WebRTC المدمجة بتنفيذ طبقة النقل الخاصة بك باستخدام واجهة برمجة تطبيقات
webrtc::Transport
.
بدلاً من DTLS / SRTP ، قررنا استخدام
تشفير Salsa20 أسرع. بالإضافة إلى ذلك ، نحن لا نرسل بيانات صوتية خلال فترات الصمت - وهو أمر شائع ، خاصة في غرف الدردشة الكبيرة. هذا يؤدي إلى توفير كبير في عرض النطاق الترددي وموارد وحدة المعالجة المركزية ، ومع ذلك ، يجب أن يكون كل من العميل والخادم جاهزين في أي وقت لإيقاف تلقي البيانات وإعادة كتابة الأرقام التسلسلية لحزم الصوت / الفيديو.
نظرًا لأن تطبيق الويب يستخدم التنفيذ المستند إلى المتصفح
لواجهة برمجة تطبيقات WebRTC و SDP و ICE و DTLS و SRTP ، فلا يمكن التخلي عنها. يتبادل العميل والخادم جميع المعلومات الضرورية (أقل من 1200 بايت عند تبادل الحزم) - ويتم تأسيس جلسة SDP على أساس هذه المعلومات للعملاء. الواجهة الخلفية مسؤولة عن حل الاختلافات بين تطبيقات سطح المكتب والمتصفح.
العمارة الخلفية
هناك العديد من خدمات الدردشة الصوتية على الواجهة الخلفية ، ولكننا سنركز على ثلاث خدمات: Discord Gateway و Discord Guilds و Discord Voice. تتم كتابة جميع خوادم الإشارات لدينا في
Elixir ، مما يسمح لنا بإعادة استخدام التعليمات البرمجية بشكل متكرر.
عندما تكون متصلاً بالإنترنت ، يدعم عميلك اتصال WebSocket ببوابة Discord (نسميها اتصال
بوابة WebSocket). من خلال هذا الاتصال ، يتلقى عميلك الأحداث المتعلقة بالمجموعات والقنوات والرسائل النصية وحزم التواجد وما إلى ذلك.
عند الاتصال بقناة صوتية ، يتم عرض حالة الاتصال بواسطة
كائن حالة الصوت. يقوم العميل بتحديث هذا الكائن عبر اتصال البوابة.
defmodule VoiceStates.VoiceState do @type t :: %{ session_id: String.t(), user_id: Number.t(), channel_id: Number.t() | nil, token: String.t() | nil, mute: boolean, deaf: boolean, self_mute: boolean, self_deaf: boolean, self_video: boolean, suppress: boolean } defstruct session_id: nil, user_id: nil, token: nil, channel_id: nil, mute: false, deaf: false, self_mute: false, self_deaf: false, self_video: false, suppress: false end
عند الاتصال بقناة صوتية ، يتم تعيينك لأحد خوادم Discord Voice. وهو مسؤول عن نقل الصوت إلى كل مشارك في القناة. يتم تعيين جميع قنوات الصوت في مجموعة لخادم واحد. إذا كنت أول من تحدث ، فإن خادم Discord Guilds مسؤول عن تعيين خادم Discord Voice إلى المجموعة بأكملها باستخدام العملية الموضحة أدناه.
وجهة Discord Voice Server
يقدم كل خادم Discord Voice تقارير دورية عن حالته وتحميله. يتم وضع هذه المعلومات في نظام اكتشاف الخدمة (نستخدم
إلخ ) ، كما تمت مناقشته في
مقال سابق .
يراقب خادم Discord Guilds نظام اكتشاف الخدمة ويعين المجموعة الأقل استخدامًا لخادم Discord Voice في المنطقة. عند تحديده ، يتم نقل جميع كائنات حالة الصوت (التي يدعمها خادم Discord Guilds) إلى خادم Discord Voice بحيث يمكنه تكوين إعادة توجيه الصوت / الفيديو. يتم إخطار العملاء بخادم Discord Voice المحدد. ثم يفتح العميل اتصال WebSocket
الثاني مع خادم الصوت (نسميه اتصال
صوت WebSocket) ، والذي يستخدم لتكوين إعادة توجيه الوسائط المتعددة ومؤشر الكلام.
عندما يعرض العميل حالة
انتظار نقطة النهاية ، فهذا يعني أن خادم Discord Guilds يبحث عن خادم Discord Voice الأمثل. تشير الرسالة
المتصلة بالاتصال الصوتي إلى أن العميل قام بتبادل حزم UDP بنجاح مع خادم Discord Voice المحدد.
يحتوي خادم Discord Voice على مكونين: وحدة إشارة ووحدة ترحيل وسائط متعددة ، تسمى وحدة إعادة توجيه انتقائية (
SFU ). تتحكم وحدة الإشارة بشكل كامل في SFU وهي مسؤولة عن توليد معرفات التدفق ومفاتيح التشفير ، وإعادة توجيه مؤشرات الكلام ، إلخ.
SFU (في C ++) هو المسؤول عن توجيه حركة مرور الصوت والفيديو بين القنوات. تم تطويره بمفرده: بالنسبة لحالتنا الخاصة ، يوفر SFU أقصى أداء وبالتالي أكبر توفير. عندما ينتهك المشرفون (كتم الخادم) ، لا تتم معالجة حزم الصوت الخاصة بهم. يعمل SFU أيضًا كجسر بين التطبيقات الأصلية والتطبيقات القائمة على المتصفح: فهو ينفذ النقل والتشفير لكل من المتصفح والتطبيقات الأصلية ، ويحول الحزم أثناء الإرسال. وأخيرًا ، فإن SFU مسؤولة عن معالجة بروتوكول
RTCP ، والذي يُستخدم لتحسين جودة الفيديو. تجمع SFU تقارير RTCP من المستلمين وتعالجها - وتخطر المرسلين بالنطاق المتاح لإرسال الفيديو.
التسامح مع الخطأ
نظرًا لأن خوادم Discord Voice فقط متاحة مباشرة من الإنترنت ، فسنتحدث عنها.
وحدة الإشارة تراقب باستمرار SFU. إذا تعطلت ، يتم إعادة تشغيلها على الفور مع حد أدنى من التوقف المؤقت في الخدمة (العديد من الحزم المفقودة). تتم استعادة حالة SFU بواسطة وحدة الإشارة دون أي تفاعل مع العميل. على الرغم من أن أعطال SFU نادرة ، فإننا نستخدم نفس الآلية لتحديث SFUs دون انقطاع في الخدمة.
عندما يتعطل خادم Discord Voice ، فإنه لا يستجيب لـ ping - ويتم إزالته من نظام اكتشاف الخدمة. يلاحظ العميل أيضًا تعطل الخادم بسبب انقطاع اتصال WebSocket الصوتي ، ثم يطلب الأمر
ping لخادم الصوت من خلال اتصال بوابة WebSocket. يؤكد خادم Discord Guilds الفشل ، ويستشير نظام اكتشاف الخدمة ، ويعين خادم Discord Voice جديدًا إلى المجموعة. ثم ترسل Discord Guilds جميع كائنات حالة الصوت إلى خادم الصوت الجديد. يتلقى جميع العملاء إشعارًا حول الخادم الجديد ويتصلون به لبدء إعداد الوسائط المتعددة.

غالبًا ما تقع خوادم Discord Voice تحت DDoS (نرى هذا من خلال الزيادة السريعة في حزم IP الواردة). في هذه الحالة ، نقوم بتنفيذ نفس الإجراء الذي حدث عند تعطل الخادم: نقوم بإزالته من نظام اكتشاف الخدمة ، وتحديد خادم جديد ، ونقل جميع كائنات حالة الاتصال الصوتي إليه وإخطار العملاء بالخادم الجديد. عندما ينحسر هجوم DDoS ، يعود الخادم إلى نظام اكتشاف الخدمة.
إذا قرر مالك المجموعة اختيار منطقة جديدة للتصويت ، فإننا نتبع إجراء مشابهًا جدًا. يختار Discord Guilds Server أفضل خادم صوت متاح في منطقة جديدة بالتشاور مع نظام اكتشاف الخدمة. ثم يترجم إليها جميع كائنات حالة الاتصال الصوتي ويخطر العملاء بالخادم الجديد. يقوم العملاء بقطع اتصال WebSocket الحالي مع خادم Discord Voice القديم وإنشاء اتصال جديد مع خادم Discord Voice الجديد.
تحجيم
البنية التحتية الكاملة لـ Discord Gateway و Discord Guilds و Discord Voice تدعم التحجيم الأفقي. تعمل Discord Gateway و Discord Guilds في Google Cloud.
لدينا أكثر من 850 خادم صوت في 13 منطقة (تقع في أكثر من 30 مركز بيانات) حول العالم. توفر هذه البنية التحتية تكرارًا أكبر في حالة الفشل في مراكز البيانات و DDoS. نحن نعمل مع العديد من الشركاء ونستخدم خوادمنا المادية في مراكز البيانات الخاصة بهم. في الآونة الأخيرة ، تمت إضافة منطقة جنوب إفريقيا. بفضل الجهود الهندسية في كل من معماريات العميل والخادم ، أصبح Discord قادرًا الآن على خدمة أكثر من 2.6 مليون مستخدم للدردشة الصوتية في وقت واحد مع حركة مرور صادرة تزيد عن 220 جيجابت / ثانية و 120 مليون حزمة في الثانية.
ما هي الخطوة التالية؟
نحن نراقب باستمرار جودة الاتصالات الصوتية (يتم إرسال المقاييس من جانب العميل إلى خوادم الواجهة الخلفية). في المستقبل ، ستساعد هذه المعلومات في الكشف التلقائي عن التدهور والقضاء عليه.
على الرغم من أننا أطلقنا دردشة الفيديو والتسجيلات الرقمية قبل عام ، إلا أنه لا يمكن استخدامها الآن إلا في الرسائل الخاصة. مقارنة بالصوت ، يتطلب الفيديو المزيد من طاقة وحدة المعالجة المركزية وعرض النطاق الترددي. يكمن التحدي في موازنة كمية النطاق الترددي وموارد وحدة المعالجة المركزية / وحدة معالجة الرسومات المستخدمة لضمان أفضل جودة للفيديو ، خاصة عندما تكون مجموعة من اللاعبين في القناة على أجهزة مختلفة. يمكن أن تصبح
تقنية ترميز الفيديو القابل للتطوير (SVC) ، وهي امتداد لمعيار H.264 / MPEG-4 AVC ، حلاً للمشكلة.
تحتاج Screencasts إلى عرض نطاق ترددي أكثر من الفيديو ، نظرًا لارتفاع FPS ودقة الوضوح من كاميرات الويب التقليدية. نحن نعمل حاليًا على دعم ترميز الفيديو المستند إلى الأجهزة في تطبيق سطح المكتب.