Wie Roblox seine Kafka-Plattform auf über 18 Billionen Nachrichten pro Tag skalierte
End-to-End-Optimierung für effiziente Systeme

In den letzten zwei Jahren haben wir unsere interne Message-Queue-Plattform jedes Jahr um das 2,5-Fache auf über 18 Billionen Nachrichten pro Tag skaliert. Derzeit verarbeiten wir über 300 Millionen Nachrichten pro Sekunde und gewährleisten dabei eine Verfügbarkeit von 99,995 %. Diese auf Apache Kafka basierende Nachrichtenwarteschlangenplattform ist zu einem entscheidenden Bestandteil unseres unternehmensweiten Datenaustauschs geworden. Sie unterstützt Workloads wie Analyseereignisse, Moderationspipelines, Matchmaking-Systeme und vieles mehr. Diese Zahlen sind beeindruckend, doch noch wichtiger ist die Geschichte, wie wir dorthin gelangt sind.
Die Auswirkungen einer durchgängigen Optimierung
Ein weit verbreitetes Missverständnis in Bezug auf groß angelegte Systeme ist, dass Kapazität hauptsächlich ein Hardware-Problem sei. Nach unserer Erfahrung lassen sich die wirkungsvollsten Verbesserungen erzielen, wenn man Skalierbarkeit als ein durchgängiges technisches Problem betrachtet.
Teams bei Roblox haben auf Basis der Messaging-Warteschlange verschiedene Plattformen aufgebaut, darunter KI-Trainingspipelines, Abstraktionen für die Stream-Verarbeitung und Telemetriesysteme. Durch die Zusammenarbeit mit den Verantwortlichen dieser Plattformen können wir dazu beitragen, die Einhaltung von Best Practices wie Batching und Komprimierung sicherzustellen. Dies führt zu effizienten Systemen, ohne dass Endnutzer in detaillierte Konfigurationsvorgänge einbezogen werden müssen.

Die Abstimmung mit den Teams, die die Plattform nutzen, hinsichtlich geschäftlicher Prioritäten, Messgrößen und Erfolgskriterien ist entscheidend und fördert die teamübergreifende Zusammenarbeit. Die wichtigsten Kennzahlen variieren je nach Ebene des Systems: Während wir die Kafka-Server möglicherweise so optimieren, dass sie große Datenmengen mit weniger Maschinen effizient verarbeiten, möchten Endnutzer möglicherweise die End-to-End-Latenz auf Millisekundenebene oder die Gesamtzeit bis zum Abschluss des Schreibvorgangs ihres Batch-Jobs optimieren.
Nicht jeder Datenverkehr ist gleich
Eine wichtige Erkenntnis, die wir gewonnen haben, ist, dass groß angelegte Plattformen zuverlässiger sind, wenn sie nicht den gesamten Datenverkehr gleich behandeln. Einige Workloads erfordern eine sehr geringe Latenz und einen starken Schutz bei Spitzenauslastungen, wie beispielsweise unsere Matchmaking-Systeme, die mit einer End-to-End-Latenz von unter 10 Millisekunden arbeiten. Andere weisen ein extrem hohes Volumen auf, sind jedoch verzögerungstoleranter, wie beispielsweise unsere Pipelines zur Erfassung von Analyseereignissen, die selbst bei einer End-to-End-Latenz von mehreren Sekunden reibungslos laufen können. Werden beide gleich behandelt, kann der Datenverkehr mit dem höchsten Volumen den geschäftskritischeren Datenverkehr verdrängen, insbesondere bei Spitzenauslastung.
Um dieses Problem zu lösen, gruppieren wir den Datenverkehr nach den Anforderungen der jeweiligen Anwendungsfälle, um sicherzustellen, dass jeder einzelne gut unterstützt wird. Der Kompromiss zwischen End-to-End-Latenz und Durchsatz in Kafka ist einer der wichtigsten Aspekte bei der Einrichtung neuer Anwendungsfälle. Dies gilt für den gesamten Datenpfad vom Produzenten über den Server bis zum Konsumenten: Jeder dieser Abschnitte verfügt über Konfigurationen, die so angepasst werden können, dass entweder der Durchsatz oder die Latenz optimiert wird. Clientseitige Konfigurationen lassen sich für einzelne Anwendungsfälle anpassen, serverseitige Konfigurationen sind jedoch weniger flexibel, da es nicht tragbar ist, für jeden einzelnen Anwendungsfall einen eigenen Cluster zu unterhalten.
Wir wenden diesen Ansatz, jeden Cluster für die Anwendungsfälle zu optimieren, die er bedient, über den gesamten Stack hinweg bis hin zur Hardware-Ebene an.
Hardware ist nach wie vor entscheidend
Wir begannen damit, Kafka auf RAID10-HDD-Maschinen zu betreiben. Als das Datenverkehrsvolumen wuchs, wurde die Festplatten-E/A-Kapazität unserer Maschinen zum primären Engpass, der den Kafka-Durchsatz pro Server begrenzte. Um dies zu beheben, nahmen wir zwei Optimierungen vor. Wir stellten die Broker so weit wie möglich auf SSDs um und verlagerten den Engpass damit von der Festplatten-E/A-Kapazität auf den Netzwerkdurchsatz oder den Festplattenspeicher (da die von uns angeschafften SSDs über weniger Speicherplatz verfügten). Dadurch stieg der Festplatten-E/A-Durchsatz um das Zehnfache. Außerdem begannen wir, unsere verbleibenden Festplatten von RAID10 auf RAID0 umzustellen. Dies verdoppelte die Festplatten-E/A-Kapazität in etwa, da bei RAID0 für jeden Schreibvorgang nur ein einziger Festplattenschreibvorgang statt zweier erforderlich ist.
Der Nachteil dabei ist, dass jede Nachrichtenreplik pro Broker auf nur eine Festplatte statt auf zwei geschrieben wird, sodass der Wechsel von RAID10 zu RAID0 mit einem Zuverlässigkeitsrisiko verbunden sein kann. Um die Zuverlässigkeit aufrechtzuerhalten, haben wir bei wichtigen Anwendungsfällen Maßnahmen ergriffen, um die Bestätigungen (Acks) und die Datenreplikation zu erhöhen, einschließlich der Replikation in mehrere Regionen.
Die vollständige Ausschöpfung der Speicher- und Durchsatzkapazitäten jedes Rechners erforderte zusätzlichen Aufwand. Wir haben unsere Speicherzuweisungen und die Konfigurationen für das Seiten-Cache-Management so optimiert, dass Kafka Nachrichten puffern und Lesevorgänge oder aktuelle Daten aus dem Arbeitsspeicher bedienen kann, wobei nur bei Bedarf – beispielsweise beim Lesen historischer Daten – auf die Festplatte zugegriffen und wertvolle IOPS verbraucht werden.
Außerdem führen wir Leistungsbenchmarks durch, um die Grenzen der Hardware zu ermitteln und festzustellen, welche Beschränkungen auf Softwareebene wir festlegen müssen. In der Praxis bedeutet dies, Lese- und Schreibbeschränkungen für Kafka-Broker festzulegen, die je nach der Hardware, auf der die Cluster laufen, variieren. Unsere SSD-Maschinen können einen höheren Durchsatz pro Broker bewältigen, während unsere HDD-Maschinen besser für Anwendungsfälle geeignet sind, die eine langfristige Aufbewahrung historischer Daten erfordern.
Auf Zuverlässigkeit ausgelegt
Die Warteschlangenplattform ist eine kritische Komponente der Roblox-Infrastruktur, und die Aufrechterhaltung der Zuverlässigkeit ist von entscheidender Bedeutung. Tests vor der Inbetriebnahme sowie fortlaufende Tests von Ausfallszenarien (manchmal auch als „Game-Day-Übungen“ bezeichnet) haben maßgeblich dazu beigetragen, zu validieren, dass unsere Infrastruktur widerstandsfähig gegenüber Ausfallmodi wie dem Ausfall einzelner Hosts, Rack-Ausfällen, Netzwerkproblemen und vielen anderen ist. Die interne Chaos-Testplattform unseres Zuverlässigkeitsteams hat die Simulation verschiedener Ausfallszenarien ermöglicht und optimiert. Diese Tests helfen dabei, Systemverbesserungen oder Regressionen zu identifizieren und operative Exzellenz aufzubauen, indem Warnmeldungen regelmäßig validiert werden und sichergestellt wird, dass die Betreiber mit den Schritten zur Behebung verschiedener Probleme – selbst seltener – vertraut sind.
Außerdem haben wir Failover-Prozesse entworfen und implementiert, mit denen sich die Warteschlangenfunktionalität für abhängige Anwendungsfälle schnell wiederherstellen lässt. Selbst im Falle eines Totalausfalls eines einzelnen Clusters sind wir in der Lage, Clients auf einen funktionsfähigen Cluster mit ausreichender Kapazität umzuschalten. Um sicherzustellen, dass wir über genügend ungenutzte Kapazität verfügen, unterhalten wir einen Kafka-Standby-Cluster. Dies trägt dazu bei, jegliches Risiko von „Noisy-Neighbor“-Problemen bei der Umleitung des Datenverkehrs zwischen aktiven Clustern auszuschließen.
Das folgende Diagramm zeigt die allgemeinen Schritte zur Durchführung eines Failovers. Zunächst legt ein Operator den Umfang des Failovers fest. Anschließend werden Themen und Zugriffskontrolllisten (ACLs) auf dem Standby-Cluster mittels eines automatisierten Massenvorgangs angelegt. Schließlich aktualisiert der Operator die Konfigurationen unseres Control-Plane-Dienstes, die von den Clients abgefragt werden, um festzustellen, ob eine erneute Verbindung erforderlich ist. Die folgende Abbildung bezieht sich auf allgemeine Echtzeit-Anwendungsfälle. Für andere Anwendungsfälle und Szenarien stehen uns zusätzliche Strategien und Werkzeuge zur Verfügung, darunter die Themen-Spiegelung mit Mirror Maker 2, Werkzeuge zur Daten-Nachfüllung und vieles mehr.

Roblox stellt strenge Anforderungen an die Sicherheit, die auch ein wesentlicher Bestandteil der Zuverlässigkeit ist. Wir haben Standard-Sicherheitsfunktionen wie Verschlüsselung während der Übertragung (mit mTLS), Verschlüsselung im Ruhezustand (mit verschlüsselten Festplatten), Anforderungen an die Aufbewahrungsdauer von Daten sowie Authentifizierung und Autorisierung mit Kafka-Prinzipalen und ACLs implementiert. Außerdem rotieren wir die Anmeldedaten unserer Nutzer automatisch, um die Sicherheit weiter zu verbessern.
Darüber hinaus haben wir in unserer Steuerungsebene eine maßgeschneiderte Verarbeitung integriert, um schnelle Betriebssystem-Patches und das Leeren von Racks zu ermöglichen. Während der Installation von Betriebssystem-Patches behalten wir die Daten auf der Festplatte bei, sodass gepatchte Maschinen nach dem Wiedereintreten in den Kafka-Cluster schnell wieder voll funktionsfähig sind. Auf diese Weise können wir unsere gesamte Infrastruktur innerhalb von 30 Tagen patchen, selbst wenn sie weiter wächst. Mit Tools wie Cruise Control replizieren wir unsere Daten rackorientiert, sodass ganze Racks für Wartungsarbeiten und Netzwerk-Patches offline genommen werden können, ohne die Datenverfügbarkeit zu beeinträchtigen.
Der Weg in die Zukunft
Im nächsten Kapitel für unsere Queue-Plattform geht es darum, die Plattform für Entwickler noch einfacher zu gestalten und gleichzeitig die Elastizität und Isolation weiter zu verbessern. Wir investieren in neue Abstraktionen, darunter Producer- und Consumer-Proxys sowie vollständig verwaltete Konnektoren. Diese Systeme werden Anwendungsteams noch stärker von der Komplexität der Infrastruktur entkoppeln und die Einführung und Verwaltung unserer Plattformen in großem Maßstab vereinfachen. Ein Consumer-Proxy ermöglicht beispielsweise das „Competing Consumer“-Muster, wodurch die Plattform ein breiteres Spektrum an Anwendungsfällen unterstützt und die Skalierung der Prozessoren von der Skalierung der Kafka-Partitionen entkoppelt. Dies ist wertvoll für Anwendungsfälle wie die Verarbeitung massiver Benachrichtigungsspitzen, die keine Reihenfolge erfordern und von einer vorübergehenden Erhöhung der Prozessoranzahl profitieren können, ohne dass End-to-End-Ziele für die Zustellung erreicht werden müssen.
Bei der Skalierung von Kafka geht es nicht nur um die Skalierung der Anzahl und Größe von Clustern. Es geht darum, die technischen Rahmenbedingungen, Abstraktionen und das Betriebsmodell zu schaffen, die es Roblox ermöglichen, unseren Weg fortzusetzen, jeden Tag eine Milliarde Menschen mit Optimismus und Höflichkeit miteinander zu verbinden.
Danksagung: Vielen Dank an Danny Yuan und George Li für ihre bedeutenden Beiträge zur Roblox-Warteschlangenplattform.


