Utilizzo di Cloud SQL per MySQL di seconda generazione come database backend di giochi mobile

Last reviewed 2022-10-28 UTC

Un pattern ben collaudato per la creazione del backend di un gioco online utilizza un database relazionale, come MySQL. Questo database archivia lo stato del mondo di gioco e i dati essenziali sulla persistenza. Per i giochi di base basati su sessioni, il database non contiene nulla di più complicato dei risultati finali delle corrispondenze. Nel caso di giochi multigiocatore online (MMO) di grandi dimensioni e permanenti, potrebbe trattarsi di un insieme estremamente complicato di tabelle interconnesse con i progressi dei giocatori e l'inventario. La velocità delle query nel livello database del backend influisce direttamente sull'esperienza di reattività dell'utente nel client di gioco.

Sebbene questo pattern sia familiare, la maggior parte dei team di sviluppo di giochi non dispone di un amministratore di database dedicato e man mano che le dimensioni del database e le relazioni che i modelli crescono in complessità, l'amministrazione può diventare un'attività che molti team preferirebbero delegare. Per i giochi mobile asincroni a turni di piccole e medie dimensioni, un database come Google Cloud SQL può essere una scelta eccellente. Cloud SQL per MySQL di seconda generazione offre un'istanza di MySQL completamente ospitata e gestita con prestazioni solide, operazioni minime e backup automatici.

Progettazione di pattern di database a fronte dei servizi

Panoramica dell'architettura del database.

Il paradigma dei microservizi è utile per i backend di database di giochi per dispositivi mobili. Una progettazione comune prevede che il database sia preceduto da un servizio di database, che è formato da un pool di processi worker che accetta le richieste di query dal frontend del gioco, esegue queste query sul database e restituisce i risultati.

Vantaggi del pattern di database basato sui servizi

Avere un servizio intermedio che esegue query sul database per conto dei tuoi server di gioco presenta diversi vantaggi:

  • Maggiore disponibilità: i database spesso limitano il numero di connessioni simultanee. L'utilizzo di un servizio disaccoppia il numero di server di gioco che possono effettuare richieste al database dal numero massimo di connessioni consentite.
  • Tolleranza di errore: i servizi di database possono essere creati per elaborare temporaneamente le richieste in caso di problemi del database.
  • Richieste ottimizzate: un servizio di database può ottimizzare le richieste di database, fornendo:
    • Convalida delle query.
    • Assegnazione della priorità alle query.
    • Controllo del flusso della velocità di query.
    • Memorizzazione nella cache di lettura tramite memorizzazione nella cache.
  • Astrazione del database: purché il contratto di servizio sia soddisfatto, il servizio di database e il database che lo supporta possono essere sostituiti senza modifiche ai server di gioco frontend. Questa progettazione consente di utilizzare diversi database in ambienti di sviluppo o QA, oppure di eseguire la migrazione a una tecnologia di database diversa in produzione.

Un pattern di database basato sui servizi per i giochi

Il seguente diagramma illustra come creare un solido pattern di database basato sui servizi utilizzando i servizi Google Cloud Platform.

Pattern di database con Google Cloud Platform.

È possibile creare un solido pattern di database basato sui servizi utilizzando i seguenti componenti:

  • Server di gioco/frontend dedicati: le applicazioni client di gioco si connettono direttamente ai server di gioco, rendendoli un servizio di frontend. I server di gioco dedicati sono in genere eseguibili personalizzati, creati con il motore di gioco, che devono essere eseguiti su hardware virtualizzato, come le VM di Google Compute Engine. Se stai scrivendo un gioco in cui l'interazione online può essere modellata con la semantica di richiesta/risposta di stile HTTP, Google App Engine è la scelta giusta.

  • Comunicazione tra server di gioco con i servizi di database: spesso modellata utilizzando lo stile di accesso CRUD (creazione, lettura, aggiornamento ed eliminazione), il che rende un'API REST o un endpoint gRPC su Compute Engine un'ottima scelta.

  • Comunicazione degli endpoint API/RPC con un pool di worker di database: un classico esempio per le code. Le opzioni più popolari sono middleware autogestiti, open source che mettono in coda in coda come RabbitMQ o ZeroMQ. Su Cloud Platform, puoi utilizzare Google Cloud Pub/Sub, un servizio sicuro, durevole e ad alta disponibilità. che offre una soluzione gestita per le code senza dover gestire i server.

  • Worker di database che si connettono a Cloud SQL di seconda generazione: i worker di database possono essere scritti in qualsiasi linguaggio che fornisca un metodo di accesso MySQL aggiornato. Questi worker possono essere gestiti manualmente su Compute Engine o pacchettizzati in container Docker per essere facilmente gestiti utilizzando il DSL di Kubernetes su Google Kubernetes Engine.

Esistono alcune limitazioni da considerare quando si utilizza Cloud SQL di seconda generazione:

  • Limite di dimensione del database di 10 TB.
  • Limite di connessioni simultanee di 4000.
  • Manca il supporto di NDB (sharding), sebbene siano supportate le repliche.

Per risolvere questi problemi:

  • Scegli un modello dei dati che ottimizzi la quantità di dati archiviati.

  • Incorpora processi che spostano dati a cui si accede raramente fuori dal database principale in un data warehouse, come Google BigQuery.

  • Utilizza un pattern in cui i server di gioco accedono al database tramite un microservizio. Anche se il numero di giocatori è sufficientemente modesto da consentire ai server di gioco di accedere direttamente al database, esistono molti vantaggi nel disaccoppiare il livello del database dal server di gioco: coda, livellamento della frequenza di query, tolleranza di errore della connessione e così via. Inoltre, tentare di aggiungere un livello di accesso al database separato dopo che il gioco è diventato popolare può causare tempi di inattività e la perdita di entrate.

  • Esegui l'analisi del gioco e la telemetria dei giocatori su una replica di sola lettura del database. Questo impedisce che l'analisi influisca sulla reattività del database. Cloud SQL per MySQL di seconda generazione consente la replica MySQL standard e puoi dimensionare la seconda istanza in modo appropriato per le query di analisi al fine di mantenere i costi marginali.

Esempio di progettazione: gioco MASS (Massively Single-Player Social)

Un paradigma emergente dell'ultimo decennio è costituito da un enorme numero di singoli giocatori che giocano contemporaneamente sessioni online. Le meccaniche sociali come il prestito di unità, il trading di unità e le classifiche sono gli unici punti di contatto tra i giocatori. Alcuni esempi di giochi MASS sono Puzzle and DragonsTM e Monster StrikeTM. I giochi MASS mobile sono progettati per essere economici con la comunicazione client/server. Ciò consente agli utenti di godersi il gioco anche in caso di connettività limitata o sporadica. Il modello dei dati per un gioco come questo, in cui quasi tutta l'archiviazione permanente dello stato è correlata al meta-gioco (raccolta delle unità e gestione della valuta dei giocatori), fornisce due tipi di base di oggetti da archiviare nel database. Questi oggetti possono essere facilmente manipolati utilizzando i meccanismi CRUD.

Oggetti giocatore, che tracciano:

  • Gioco e valute vere.
  • Numero totale di slot dell'inventario delle unità.
  • Resistenza.
  • Esperienza.

Oggetti unità, che monitorano:

  • Proprietario (ID giocatore).
  • Esperienza.
  • Costo di acquisizione.
  • Unità di inventario.

Per meno di 100.000 player, questo modello dei dati si inserisce bene in un database relazionale come Cloud SQL di seconda generazione.

Mimo

Mimus è un'applicazione simulata di giochi mobile MASS con un backend in stile Puzzle and DragonsTM o Monster StrikeTM. Presuppone che ogni giocatore possa accedere da un solo dispositivo alla volta e che sia necessario completare qualsiasi azione prima di iniziarne un altro. Con Mimus, puoi eseguire carichi di lavoro simulati per valutare la capacità ottimale dell'architettura, che in genere si basa sul numero di utenti simultanei (CCU).

Puoi trovare il codice sorgente di Mimus all'indirizzo https://github.com/GoogleCloudPlatform/mimus-game-simulator.

Panoramica dell'architettura Mimus

Simulazione di client Mimus all'interno del server Mimus

Nei giochi MASS, la frequenza con cui il client del gioco genera query sul database può essere controllata dallo sviluppatore del gioco chiedendo al giocatore di visualizzare le animazioni e di interagire con il client del gioco per continuare. Mimus simula queste strategie di limitazione della frequenza con chiamate sleep(). In questo modo, Mimus simula un'approssimazione ragionevole del carico del database per client eseguendo un numero di processi pari a quello dei player simulati. Questo processo viene orchestrato in modo efficiente utilizzando un pod client/server Mimus containerizzato, in un cluster Kubernetes, che genera query sul database.

Il client di gioco Mimus simula la comunicazione con il server di backend utilizzando un loop continuo che chiama direttamente le procedure del server Mimus, scegliendo le chiamate alle funzioni in base allo stato del player e all'inventario.

Le azioni del giocatore simulate da Mimus includono:

  • Un giro di gioco.
  • Acquisto della valuta.
  • Valuta di spesa.
  • Livellamento o evoluzione delle unità.

Ognuna di queste azioni viene implementata come più interazioni CRUD con gli oggetti player o unità, manipolate dal client tramite chiamate al server Mimus. Il server Mimus effettua le richieste di database utilizzando l'API sincrona (di blocco) del database Mimus. Questa API è un modulo Python importato dal server Mimus e può essere configurata per testare diverse implementazioni del backend del database.

Comunicazione dell'API del database Mimus con il pool di worker del database Mimus

Il seguente diagramma illustra la comunicazione tra il server Mimus e il servizio di database.

Il design della comunicazione di Mimus.

L'API database Mimus accetta batch di query di database e restituisce i risultati. Pubblica questi batch come messaggi Cloud Pub/Sub e attende che i risultati vengano restituiti tramite Redis. Prima di inviare un messaggio, l'API di database convalida tutti i valori che verranno scritti nel database e codifica il messaggio con un ID transazione univoco. Il messaggio viene quindi pubblicato in un argomento Work in Cloud Pub/Sub. Quindi, l'API di database esegue i loop, eseguendo il polling per verificare l'esistenza dell'ID transazione come chiave Redis. I risultati nel valore della chiave vengono quindi recuperati dall'API di database e restituiti al server Mimus, da cui vengono resi disponibili al client Mimus.

Considerazioni sulla selezione delle comunicazioni

Mimus utilizza Cloud Pub/Sub per la comunicazione con query in attesa, perché le azioni degli utenti richiedono durabilità e una distribuzione affidabile. Mimus utilizza Redis per comunicare i risultati, dove la durabilità e l'affidabilità sono meno fondamentali. Se i risultati vengono persi a causa di un errore dell'applicazione o di un errore Redis, il client Mimus invia una nuova query per ricevere i risultati finali dal database. Utilizzando le transazioni in un database relazionale, hai la certezza che tutte le modifiche richieste siano state applicate o nessuna. Il raro caso in cui Mimus abbia bisogno di effettuare una seconda richiesta è considerato un compromesso accettabile in cambio della semplicità e della velocità di recupero offerte da Redis. Per mantenere ragionevole l'utilizzo di Redis, i risultati delle richieste possono scadere da Redis dopo trenta secondi.

Pool di worker del database Mimus

Il pool di worker del database Mimus contiene più processi in esecuzione su Kubernetes Engine. Ogni istanza di container in esecuzione esegue il polling dell'argomento Work Cloud Pub/Sub in un loop infinito per trovare nuovi messaggi. Quando riceve un messaggio, esegue le query nel messaggio utilizzando il modulo Python MySQLdb. Un singolo messaggio rappresenta una transazione relazionale e può contenere più query. Tutte le query nel messaggio devono essere completate prima di poter essere inserite nel database. Dopo che le query sono state completate (o non riuscite), il worker di database pubblica i risultati in Redis sotto l'ID transazione ricevuto come parte del messaggio originale.

Database Mimus

Il database relazionale che supporta il servizio di database Mimus è un'istanza Cloud SQL per MySQL di seconda generazione. Durante la creazione dell'istanza, puoi selezionare configurazioni macchina fino a 32 core e 208 GB di RAM, con dimensioni del disco fino a 10 TB. Oltre a supportare le repliche, le istanze Cloud SQL di seconda generazione sono configurate per l'esecuzione di backup regolari per impostazione predefinita. Se hai bisogno di un'ulteriore ottimizzazione per MySQL, puoi impostare flag MySQL specifici sull'istanza Cloud SQL. Per ulteriori informazioni sulla configurazione, consulta la documentazione di Cloud SQL.

Conclusioni del test di Cloud SQL con Mimus

Tipo di macchina SG di Cloud SQL Numero suggerito di utenti simultanei
n1-standard-4 15.000
n1-standard-8 30.000
n1-standard-16 60.000
n1-standard-32 120.000
n1-highmem-8 50.000
n1-highmem-16 100.000
n1-highmem-32 200.000

Quando si utilizza il cablaggio di test Mimus per simulare 100.000 utenti simultanei su un'istanza Cloud SQL per MySQL di seconda generazione creata su un'istanza Compute Engine n1-highmem-16, il tempo di risposta alle query è rimasto inferiore a 2 secondi durante il test.

Se stai creando un gioco per dispositivi mobili MASS e hai bisogno di supportare centinaia di migliaia di giocatori simultanei, un pattern di database basato su Cloud SQL per MySQL di seconda generazione può fornire le prestazioni necessarie. Quando il tuo gioco diventa più popolare, puoi introdurre altre istanze Cloud SQL, con sharding o come repliche, e una strategia di memorizzazione nella cache Redis o Memcached a livello di servizio di database per mantenere le prestazioni a livelli accettabili.

Per i giochi con meno utenti simultanei previsti, puoi usare un tipo di macchina più piccolo per ridurre i costi.

Per i giochi con milioni di CCU previste, dovresti prendere in considerazione un database NoSQL, come Google Cloud Datastore o Google Cloud Bigtable, con solide caratteristiche di scalabilità e prestazioni.

Passaggi successivi

  • Esplora le architetture di riferimento, i diagrammi e le best practice su Google Cloud. Dai un'occhiata al nostro Cloud Architecture Center.