Wie Discord gleichzeitig 2,5 Millionen Voice-Chats mithilfe von WebRTC bereitstellt

Ursprünglicher Autor: Jozsef Vass
  • Übersetzung


Wir haben von Anfang an Engineering- und Produktlösungen so geplant, dass Discord sich gut für den Voice-Chat eignet, während Sie mit Freunden spielen. Mit diesen Lösungen konnte das System mit einem kleinen Team und begrenzten Ressourcen stark skaliert werden.

Der Artikel beschreibt die verschiedenen Technologien, die Discord für Audio- / Video-Chats verwendet.

Der Klarheit halber wird die gesamte Gruppe von Benutzern und Kanälen als "Gruppe" (Gilde) bezeichnet - im Client werden sie "Server" genannt. Stattdessen bezieht sich der Begriff "Server" hier auf unsere Serverinfrastruktur.

Hauptprinzipien


Jeder Audio- / Video-Chat in Discord unterstützt viele Teilnehmer. Wir haben gesehen, wie tausend Leute abwechselnd in großen Gruppen chatten. Eine solche Unterstützung erfordert eine Client-Server-Architektur, da das Peer-to-Peer-Netzwerk mit steigender Teilnehmerzahl unerschwinglich teuer wird.

Durch das Routing des Netzwerkverkehrs über Discord-Server wird auch sichergestellt, dass Ihre IP-Adresse niemals sichtbar ist und niemand einen DDoS-Angriff startet. Das Routing durch Server hat andere Vorteile: zum Beispiel Moderation. Administratoren können Audio- und Video-Täter schnell deaktivieren.

Client-Architektur


Discord funktioniert auf vielen Plattformen.

  • Web (Chrome / Firefox / Edge usw.)
  • Standalone-Anwendung (Windows, MacOS, Linux)
  • Telefon (iOS / Android)

Wir können alle diese Plattformen auf nur eine Weise unterstützen: durch die Wiederverwendung von WebRTC- Code . Diese Spezifikation für Echtzeitkommunikation umfasst Netzwerk-, Audio- und Videokomponenten. Der Standard wird vom World Wide Web Consortium und der Internet Engineering Group übernommen . WebRTC ist in allen modernen Browsern und als native Bibliothek zum Einbetten in Anwendungen verfügbar.

Audio und Video in Discord werden auf WebRTC ausgeführt. Daher ist die Browseranwendung auf die WebRTC-Implementierung im Browser angewiesen. Anwendungen für Desktops, iOS und Android verwenden jedoch eine einzige Multimedia-C ++ - Engine, die auf einer eigenen WebRTC-Bibliothek basiert, die speziell auf die Bedürfnisse unserer Benutzer abgestimmt ist. Dies bedeutet, dass einige Funktionen in der Anwendung besser funktionieren als im Browser. In unseren nativen Anwendungen können wir beispielsweise:

  • Umgehen Sie das in Windows standardmäßig stummgeschaltete Volume, wenn alle Anwendungen bei Verwendung des Headsets automatisch stummgeschaltet werden . Dies ist unerwünscht, wenn Sie und Ihre Freunde zum Raid gehen und die Aktionen im Discord-Chat koordinieren.
  • Verwenden Sie Ihren eigenen Lautstärkeregler anstelle des globalen Betriebssystemmischers.
  • Verarbeiten Sie die ursprünglichen Audiodaten, um die Sprachaktivität zu erkennen, und übertragen Sie Audio und Video in Spielen.
  • Reduzieren Sie die Bandbreite und den CPU-Verbrauch in Zeiten der Stille - selbst in den meisten Voice-Chats gleichzeitig sprechen nur wenige Personen gleichzeitig.
  • Bieten Sie systemweite Push-to-Talk-Funktionen.
  • Senden Sie zusätzliche Informationen zusammen mit Audio-Video-Paketen (z. B. die Prioritätsanzeige im Chat).

Wenn Sie eine eigene Version von WebRTC besitzen, müssen alle Benutzer regelmäßig aktualisiert werden. Dies ist ein zeitaufwändiger Prozess, den wir zu automatisieren versuchen. Diese Anstrengungen zahlen sich jedoch mit spezifischen Features für unsere Spieler aus.

Bei Discord wird die Sprach- und Videokommunikation durch Eingabe eines Sprachkanals oder eines Anrufs initiiert. Das heißt, die Verbindung wird immer vom Client initiiert - dies reduziert die Komplexität der Client- und Serverteile und erhöht die Fehlerfestigkeit. Bei einem Ausfall der Infrastruktur können sich die Teilnehmer einfach wieder mit dem neuen internen Server verbinden.

Unter unserer Kontrolle


Durch die Steuerung der systemeigenen Bibliothek können Sie einige Funktionen anders implementieren als in der Browser-Implementierung von WebRTC.

Erstens stützt sich WebRTC auf das Session Description Protocol ( SDP ), um Audio / Video zwischen den Teilnehmern auszuhandeln (bis zu 10 KB pro Paketaustausch). In seiner eigenen Bibliothek wird die untergeordnete API von WebRTC ( webrtc::Call) verwendet, um sowohl eingehende als auch ausgehende Streams zu erstellen . Bei Verbindung mit einem Sprachkanal besteht ein minimaler Informationsaustausch. Dies ist die Adresse und der Port des Backend-Servers, die Verschlüsselungsmethode, die Schlüssel, der Codec und die Stream-Identifikation (ca. 1000 Byte).

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;
}

Darüber hinaus verwendet WebRTC Interactive Connectivity Establishment ( ICE ) , um die beste Route zwischen Mitgliedern zu ermitteln . Da wir jeden Client mit dem Server verbinden, benötigen wir kein ICE. Dadurch können Sie eine wesentlich zuverlässigere Verbindung bereitstellen, wenn Sie sich hinter NAT befinden, und Ihre IP-Adresse vor anderen Teilnehmern geheim halten. Clients pingen regelmäßig, damit die Firewall eine offene Verbindung aufrechterhält.

Schließlich verwendet WebRTC das Secure Real-Time Transport Protocol ( SRTP ), um Medien zu verschlüsseln. Die Verschlüsselungsschlüssel werden mithilfe des DTLS- Protokolls (Datagram Transport Layer Security ) basierend auf Standard-TLS eingerichtet. Mit der integrierten WebRTC-Bibliothek können Sie Ihre eigene Transportschicht mit implementierenwebrtc::TransportAPI.

Anstelle von DTLS / SRTP haben wir uns für eine schnellere Verschlüsselung mit Salsa20 entschieden . Darüber hinaus senden wir keine Audiodaten während Ruhephasen - ein häufiges Vorkommnis, insbesondere bei großen Gesprächen. Dies führt zu erheblichen Einsparungen bei der Bandbreite und den CPU-Ressourcen. Allerdings sollten sowohl der Client als auch der Server jederzeit bereit sein, den Empfang von Daten zu stoppen und die Folgenummern der Audio- / Videopakete neu zu schreiben.

Da die Webanwendung die browserbasierte Implementierung der WebRTC-API verwendet , können SDP, ICE, DTLS und SRTP hier nicht aufgegeben werden. Client und Server tauschen alle erforderlichen Informationen aus (weniger als 1200 Byte beim Austausch von Paketen) - und basierend auf diesen Informationen wird für die Clients eine SDP-Sitzung eingerichtet. Backend ist dafür verantwortlich, die Unterschiede zwischen Desktop- und Browseranwendungen zu beseitigen.

Backend-Architektur


Es gibt mehrere Voice-Chat-Dienste im Backend, aber wir werden uns auf drei konzentrieren: Discord Gateway, Discord Guilds und Discord Voice. Alle unsere Signalserver sind in Elixir geschrieben , sodass Sie Code wiederholt wiederverwenden können.

Wenn Sie online sind, unterstützen Ihre Client WebSocket - Verbindung zum Gateway Discord - Gateway (wir nennen es die Gateway WebSocket - Verbindung). Über diese Verbindung empfängt Ihr Client Ereignisse, die sich auf Gruppen und Kanäle, Textnachrichten, Präsenzpakete usw. beziehen.

Bei Verbindung mit einem Sprachkanal wird der Verbindungsstatus als Sprachstatusobjekt angezeigt . Der Client aktualisiert dieses Objekt über eine Gateway-Verbindung.

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: falseend

Wenn Sie eine Verbindung zu einem Sprachkanal herstellen, wird Ihnen einer der Discord Voice-Server zugewiesen. Er ist für die Übertragung von Ton an jeden Kanalteilnehmer verantwortlich. Alle Sprachkanäle in einer Gruppe werden einem Server zugewiesen. Wenn Sie der erste Chat sind, ist der Discord Guilds-Server dafür verantwortlich, den Discord Voice-Server der gesamten Gruppe zuzuordnen.

Zweck des Discord Voice Servers


Jeder Discord Voice-Server meldet regelmäßig seinen Status und seine Last. Diese Informationen werden auf dem Service Discovery System abgelegt (wir verwenden etcd ), wie im vorherigen Artikel beschrieben .

Discord Guilds Server überwacht das Service Discovery System und weist der Gruppe den am wenigsten verwendeten Discord Voice-Server in der Region zu. Bei Auswahl dieser Option werden alle Sprachstatusobjekte (auch vom Discord Guilds-Server unterstützt) an den Discord Voice-Server übertragen, damit er die Audio- / Video-Umleitung konfigurieren kann. Kunden werden über den ausgewählten Discord Voice-Server informiert. Dann öffnet der Client die zweite WebSocket-Verbindung zum Voice-Server (wir nennen ihn " Voice")WebSocket), um Medienweiterleitung und Sprachanzeige einzurichten.

Wenn der Client den Status Warten auf Endpunkt anzeigt , bedeutet dies, dass der Discord Guilds-Server nach dem besten Discord Voice-Server sucht. Die Voice Connected- Nachricht bedeutet, dass der Client erfolgreich UDP-Pakete mit dem ausgewählten Discord Voice-Server ausgetauscht hat.

Der Discord Voice-Server enthält zwei Komponenten: ein Signalisierungsmodul und eine Multimedia-Relay-Einheit, die als selektive Weiterleitungseinheit ( SFU ) bezeichnet wird. Das Signalisierungsmodul steuert die SFU vollständig und ist für die Erzeugung von Flusskennungen und Verschlüsselungsschlüsseln, die Umleitung von Sprachindikatoren usw. verantwortlich.

Unsere SFU (in C ++) ist für die Weiterleitung des Audio- und Videoverkehrs zwischen Kanälen verantwortlich. Sie wird eigenständig entwickelt: Für unseren speziellen Fall bietet die SFU maximale Leistung und damit die größten Einsparungen. Wenn Moderatoren gegen den Server verstoßen (stummschalten), werden ihre Audio-Packs nicht verarbeitet. SFU fungiert auch als Brücke zwischen nativen und Browseranwendungen: Es implementiert Transport und Verschlüsselung sowohl für den Browser als auch für native Anwendungen und konvertiert die Pakete während der Übertragung. Schließlich ist SFU für die Handhabung des RTCP- Protokolls verantwortlich , mit dem die Videoqualität optimiert wird. SFU sammelt und verarbeitet RTCP-Berichte von Empfängern - und benachrichtigt die Sender, welches Band für die Videoübertragung zur Verfügung steht.

Fehlertoleranz


Da wir nur Discord Voice-Server direkt aus dem Internet haben, werden wir darüber sprechen.

Das Signalmodul überwacht kontinuierlich die SFU. Wenn dies fehlschlägt, wird es sofort mit einer minimalen Dienstpause (mehrere verlorene Pakete) neu gestartet. Der Status der SFU wird vom Signalmodul ohne Interaktion mit dem Client wiederhergestellt. Obwohl SFU-Abstürze selten sind, verwenden wir denselben Mechanismus, um SFU ohne Dienstunterbrechung zu aktualisieren.

Wenn der Discord Voice-Server abstürzt, reagiert er nicht auf den Ping - und wird aus dem Service Discovery-System entfernt. Der Client bemerkt auch einen Serverabsturz aufgrund einer unterbrochenen WebSocket-Sprachverbindung und fordert dann einen Ping-Befehl des Sprachservers anüber eine Gateway-Websocket-Verbindung. Der Discord Guilds-Server bestätigt den Fehler, konsultiert das Service Discovery-System und weist der Gruppe einen neuen Discord Voice-Server zu. Anschließend senden Discord Guilds alle Sprachstatusobjekte an einen neuen Sprachserver. Alle Clients erhalten eine Benachrichtigung über den neuen Server und stellen eine Verbindung zu diesem Server her, um das Multimedia-Setup zu starten.



Häufig fallen Discord Voice-Server unter DDoS (wir sehen dies an einem rapiden Anstieg ankommender IP-Pakete). In diesem Fall führen wir dasselbe Verfahren wie bei einem Serverausfall aus: Entfernen Sie ihn aus dem Service Discovery-System, wählen Sie einen neuen Server aus, übertragen Sie alle Sprachstatusobjekte auf diesen und benachrichtigen Sie die Clients über den neuen Server. Wenn der DDoS-Angriff nachlässt, kehrt der Server zum Service Discovery-System zurück.

Wenn der Gruppeninhaber sich für eine Stimme entscheidet, führen wir ein sehr ähnliches Verfahren aus. Discord Guilds Server wählt den besten verfügbaren Voice-Server in einer neuen Region durch Rücksprache mit einem Service Discovery-System aus. Dann übersetzt er alle Sprachstatusobjekte in dieses Objekt und benachrichtigt die Clients über den neuen Server. Clients unterbrechen die aktuelle WebSocket-Verbindung zum alten Discord Voice-Server und stellen eine neue Verbindung zum neuen Discord Voice-Server her.

Skalieren


Die gesamte Infrastruktur von Discord Gateway, Discord Guilds und Discord Voice unterstützt horizontale Skalierung. Discord Gateway und Discord Guilds arbeiten in der Google Cloud.

Wir haben weltweit mehr als 850 Voice Server in 13 Regionen (in mehr als 30 Rechenzentren). Diese Infrastruktur bietet größere Redundanz bei Ausfällen in Rechenzentren und DDoS. Wir arbeiten mit mehreren Partnern zusammen und nutzen unsere physischen Server in ihren Rechenzentren. Erst kürzlich kam die Region Südafrika hinzu. Dank technischer Anstrengungen sowohl in der Client- als auch in der Serverarchitektur kann Discord jetzt mehr als 2,6 Millionen Benutzer des Voice-Chats mit ausgehendem Datenverkehr von mehr als 220 Gbit / s und 120 Millionen Paketen pro Sekunde bedienen.

Was weiter?


Wir überwachen ständig die Qualität der Sprachkommunikation (Metriken werden vom Client zu den Backend-Servern übertragen). In Zukunft werden diese Informationen zur automatischen Erkennung und Beseitigung von Verschlechterungen beitragen.

Wir haben zwar vor einem Jahr Video-Chat und Screencasts ins Leben gerufen, aber jetzt können sie nur noch in persönlichen Nachrichten verwendet werden. Im Vergleich zu Ton erfordert Video wesentlich mehr CPU-Leistung und Durchsatz. Die Herausforderung besteht darin, die Menge an Bandbreite und CPU / GPU-Ressourcen auszugleichen, um die beste Videoqualität zu gewährleisten, insbesondere wenn sich die Gruppe der Gamer im Kanal auf verschiedenen Geräten befindet. Die Lösung kann die SVC- Technologie ( Scalable Video Coding ) sein, eine Erweiterung des AVC-Standards H.264 / MPEG-4.

Screencasts erfordern aufgrund der höheren Bildrate und Auflösung eine noch höhere Bandbreite als Videos. Wir arbeiten derzeit an der Unterstützung der Hardware-Videocodierung in einer Desktopanwendung.

Jetzt auch beliebt: