I contenuti di questo sito sono stati tradotti mediante intelligenza artificiale (IA) o tecnologia di traduzione automatica e potrebbero contenere errori.

Skip to content

Come Roblox ha scalato la propria piattaforma Kafka fino a gestire oltre 18 trilioni di messaggi al giorno

Ottimizzazione end-to-end per sistemi efficienti

Negli ultimi due anni abbiamo scalato la nostra piattaforma interna di code di messaggi di oltre 2,5 volte ogni anno, raggiungendo oltre 18 trilioni di messaggi al giorno. Attualmente elaboriamo oltre 300 milioni di messaggi al secondo, mantenendo una disponibilità del 99,995%. Questa piattaforma di code di messaggi, basata su Apache Kafka, è diventata una parte fondamentale del modo in cui trasferiamo i dati all’interno dell’azienda. Supporta carichi di lavoro quali eventi di analisi, pipeline di moderazione, sistemi di matchmaking e molti altri. Questi numeri sono entusiasmanti, ma la storia più importante è come siamo arrivati a questo risultato. 

L’impatto dell’ottimizzazione end-to-end 

Un malinteso comune sui sistemi su larga scala è che la capacità sia principalmente un problema hardware. Secondo la nostra esperienza, i miglioramenti più significativi si ottengono considerando la scalabilità come un problema ingegneristico end-to-end.

I team di Roblox hanno sviluppato diverse piattaforme basate sulla coda di messaggistica, tra cui pipeline di addestramento dell’IA, astrazioni per l’elaborazione dei flussi e sistemi di telemetria. Collaborare con i responsabili di queste piattaforme ci permette di garantire il rispetto delle best practice, come l’abilitazione del batching e della compressione. Il risultato sono sistemi efficienti senza che gli utenti finali debbano occuparsi di configurazioni dettagliate.

La nostra architettura basata su code e le relative piattaforme consentono ad altri team di sviluppare senza dover reinventare la ruota né preoccuparsi della configurazione

Allinearsi con i team che utilizzano la piattaforma in merito alle priorità aziendali, alle metriche e ai criteri di successo è fondamentale e contribuisce a promuovere la collaborazione tra i team. Le metriche chiave variano a seconda dei livelli del sistema: mentre noi potremmo ottimizzare i server Kafka per gestire in modo efficiente grandi volumi di dati con un numero inferiore di macchine, gli utenti finali potrebbero voler ottimizzare la latenza end-to-end fino al livello del millisecondo, oppure il tempo totale necessario affinché il loro processo batch completi la scrittura dei dati. 

Non tutto il traffico è uguale

Una lezione fondamentale che abbiamo imparato è che le piattaforme su larga scala sono più affidabili quando non trattano tutto il traffico allo stesso modo. Alcuni carichi di lavoro richiedono una latenza molto bassa e una protezione elevata durante i picchi, come i nostri sistemi di matchmaking, che operano con una latenza end-to-end inferiore a 10 millisecondi. Altri hanno volumi estremamente elevati ma sono più tolleranti ai ritardi, come le nostre pipeline di acquisizione degli eventi analitici, che possono funzionare senza intoppi anche con una latenza end-to-end di diversi secondi. Se entrambi vengono gestiti allo stesso modo, il traffico con il volume più elevato può soffocare quello più critico per l’azienda, specialmente durante i picchi di carico.

Per risolvere questo problema, raggruppiamo il traffico in base ai requisiti dei casi d’uso per garantire che ciascuno sia adeguatamente supportato. Il compromesso tra latenza end-to-end e throughput in Kafka è una delle considerazioni principali quando si configurano nuovi casi d’uso. Ciò vale per l’intero percorso dei dati dal produttore al server fino al consumatore: ciascuno di questi elementi dispone di configurazioni che possono essere ottimizzate per il throughput o per la latenza. Le configurazioni lato client possono essere ottimizzate per singoli casi d’uso, mentre quelle lato server sono meno flessibili, poiché non è sostenibile mantenere un cluster dedicato per ogni singolo caso d’uso. 

Applichiamo questo approccio di ottimizzazione di ciascun cluster per i casi d’uso che serve lungo lo stack fino al livello hardware. 

L’hardware rimane fondamentale

Abbiamo iniziato eseguendo Kafka su macchine con HDD in RAID10. Con l’aumentare del volume di traffico, la capacità di I/O su disco delle nostre macchine è diventata il principale collo di bottiglia che limitava la velocità di trasmissione di Kafka per singolo server. Per risolvere questo problema, abbiamo apportato due ottimizzazioni. Abbiamo convertito i broker per utilizzare il più possibile gli SSD, spostando il collo di bottiglia dalla capacità di I/O su disco alla velocità di trasmissione di rete o allo spazio su disco (poiché gli SSD che abbiamo acquistato avevano meno spazio su disco). Ciò ha aumentato di 10 volte la velocità di I/O su disco. Abbiamo inoltre iniziato a convertire gli HDD rimanenti da RAID10 a RAID0. Ciò ha raddoppiato approssimativamente la capacità di I/O su disco, poiché ogni operazione di scrittura con RAID0 richiede una sola scrittura su disco anziché due. 

Il compromesso è che, poiché ogni replica di messaggio viene scritta su un solo disco per broker anziché su due, il passaggio da RAID10 a RAID0 può comportare un rischio in termini di affidabilità. Per mantenere l’affidabilità, abbiamo lavorato su casi d’uso importanti per aumentare gli ack e la replica dei dati, inclusa la replica in più regioni.

Sfruttare appieno le capacità di archiviazione e di throughput di ciascuna macchina ha richiesto un lavoro aggiuntivo. Abbiamo ottimizzato le nostre allocazioni di memoria e le configurazioni di gestione della cache delle pagine in modo che Kafka possa memorizzare i messaggi nel buffer e servire le letture o i dati recenti dalla memoria, ricorrendo al disco e consumando preziose IOPS solo quando necessario, ad esempio durante la lettura dei dati storici. 

Eseguiamo inoltre benchmark delle prestazioni per comprendere i limiti dell’hardware e determinare quali limiti a livello di software dobbiamo impostare. In pratica, ciò significa impostare limiti di lettura e scrittura per i broker di Kafka, che variano in base all’hardware su cui girano i cluster. Le nostre macchine con SSD possono gestire un throughput più elevato per broker, mentre quelle con HDD sono più adatte a casi d’uso che richiedono la conservazione a lungo termine dei dati storici. 

Progettare per l’affidabilità

La piattaforma delle code è una componente fondamentale dell’infrastruttura di Roblox e garantirne l’affidabilità è cruciale. I test pre-lancio, così come i test continui sugli scenari di guasto (talvolta denominati “game day exercises”), sono stati determinanti per verificare che la nostra infrastruttura sia resiliente a modalità di guasto quali il guasto di un singolo host, il guasto di un rack, problemi di rete e molti altri. La piattaforma interna di test del caos del nostro team dedicato all’affidabilità ha reso possibile e ottimizzato il processo di simulazione di vari scenari di guasto. Questi test aiutano a identificare miglioramenti o regressioni del sistema e a costruire l’eccellenza operativa, convalidando regolarmente gli avvisi e assicurando che gli operatori abbiano familiarità con le procedure per gestire vari problemi, anche quelli rari.

Abbiamo inoltre progettato e implementato processi di failover in grado di ripristinare rapidamente la funzionalità delle code per i casi d’uso dipendenti. Anche in caso di guasto totale di un singolo cluster, siamo in grado di trasferire i client a un cluster funzionante con capacità sufficiente. Per garantire la disponibilità di una capacità inutilizzata adeguata, manteniamo un cluster Kafka di riserva. Ciò contribuisce a eliminare qualsiasi rischio di problemi legati ai “vicini rumorosi” durante lo spostamento del traffico tra i cluster attivi.

Il diagramma sottostante mostra le fasi generali per l’esecuzione di un failover. Innanzitutto, un operatore identifica l’ambito del failover. Successivamente, gli argomenti e gli elenchi di controllo degli accessi (ACL) vengono creati sul cluster di riserva tramite un’operazione automatizzata in blocco. Infine, l’operatore aggiorna le configurazioni del nostro servizio di piano di controllo, che i client interrogano per determinare se sia necessaria una riconnessione. L’illustrazione sottostante si riferisce a casi d’uso generici in tempo reale. Disponiamo di ulteriori strategie e strumenti per altri casi d’uso e scenari, tra cui il mirroring degli argomenti tramite Mirror Maker 2, strumenti per il backfill dei dati e altro ancora.

Roblox ha requisiti rigorosi in materia di sicurezza, che costituisce anche una componente fondamentale dell’affidabilità. Abbiamo implementato funzionalità di sicurezza standard quali la crittografia in transito (con mTLS), la crittografia a riposo (con disco crittografato), i requisiti relativi alla durata di conservazione dei dati e l’autenticazione/autorizzazione tramite entità Kafka e ACL. Inoltre, effettuiamo la rotazione automatica delle credenziali dei nostri utenti per migliorare ulteriormente la sicurezza.

Oltre a ciò, abbiamo integrato una gestione personalizzata nel nostro piano di controllo per facilitare l’applicazione rapida delle patch al sistema operativo e lo svuotamento dei rack. Durante l’applicazione delle patch al sistema operativo, conserviamo i dati su disco in modo che le macchine aggiornate possano tornare rapidamente in condizioni ottimali dopo essersi ricollegate al cluster Kafka. In questo modo siamo in grado di applicare le patch all’intera flotta entro 30 giorni, anche se questa continua a crescere. Grazie a strumenti come Cruise Control, replichiamo i nostri dati tenendo conto dei rack, in modo che interi rack possano essere messi offline per la manutenzione e l’applicazione di patch di rete senza compromettere la disponibilità dei dati.

Il futuro

Il prossimo capitolo per la nostra piattaforma di code consiste nel renderla ancora più semplice per gli sviluppatori, continuando al contempo a migliorarne l’elasticità e l’isolamento. Stiamo investendo in nuove astrazioni, tra cui proxy per produttori e consumatori, nonché connettori completamente gestiti. Questi sistemi separeranno ulteriormente i team applicativi dalla complessità dell’infrastruttura e renderanno le nostre piattaforme più facili da adottare e gestire su larga scala. Ad esempio, un proxy consumer consentirà il modello del consumer in competizione, permettendo alla piattaforma di supportare una gamma più ampia di casi d’uso e di separare il ridimensionamento dei processori da quello delle partizioni di Kafka. Ciò è prezioso per casi d’uso come l’elaborazione di picchi massicci di notifiche, che non richiedono un ordine specifico e possono trarre vantaggio da un aumento temporaneo del numero di processori senza dover raggiungere obiettivi di consegna end-to-end prestabiliti.

Il ridimensionamento di Kafka non riguarda solo il numero e le dimensioni dei cluster. Si tratta di sviluppare la disciplina ingegneristica, le astrazioni e il modello operativo che consentono a Roblox di proseguire il proprio percorso per connettere un miliardo di persone ogni giorno con ottimismo e civiltà. 

Ringraziamenti: Grazie a Danny Yuan e George Li per i loro significativi contributi alla piattaforma di coda di Roblox.