Anleitung: Memorystore for Redis für Bestenlisten von Spielen verwenden


In dieser Anleitung erfahren Sie, wie Sie mithilfe von Memorystore for Redis eine ASP.NET-basierte Anwendung zum Führen einer Bestenliste erstellen können, die auf Google Kubernetes Engine (GKE) ausgeführt wird, und wie Sie anschließend mithilfe eines separaten Beispiel-Games auf Basis von JavaScript Ergebnisse veröffentlichen und abrufen können. Dieses Dokument richtet sich an Spieleentwickler, die ihre eigenen Bestenlisten in der Cloud führen möchten.

Einführung

Bestenlisten sind ein geläufiges Feature von Spielen mit Mehrspielermodus. In einer Bestenliste werden die Platzierungen der Spieler in Echtzeit angezeigt, damit sich den Spielern ein Anreiz bietet, mehr zu spielen. Eine speicherinterne Datenbank wie Redis ist eine gute Wahl, um Aktionen der Spieler zu erfassen und den Punktestand in Echtzeit auszuwerten.

Im folgenden Diagramm ist die Struktur der Bestenliste dargestellt:

Diagramm mit einem GKE-Cluster in einer VPC und einer separaten Instanz von Memorystore for Redis

In Google Cloud besteht die Bestenliste aus einem GKE-Cluster in einem VPC-Netzwerk und einer separaten Instanz von Memorystore for Redis.

Im folgenden Diagramm ist die Architektur der Anwendung für diese Anleitung dargestellt. Clients verwenden für die Interaktion mit den Punkteständen, die in einer in Google Cloud ausgeführten Instanz von Memorystore for Redis geführt werden, die API der Bestenliste.

Diagramm mit der Architektur für die in dieser Anleitung beschriebene Anwendung

Methoden der API der Bestenliste

Die API für die Anwendung zum Führen einer Bestenliste umfasst die folgenden Methoden:

  • PostScore(string playerName, double score): Mit dieser Methode wird ein Punktestand für den angegebenen Spieler in der Bestenliste veröffentlicht.
  • RetrieveScores(string centerKey, int offset, int numScores): Mit dieser Methode wird eine Reihe von Punkteständen heruntergeladen. Wird als Wert für centerKey eine Spieler-ID übergeben, gibt die Methode die Punktestände zurück, die über und unter denen des angegebenen Spielers liegen. Wird kein Wert für centerKey übergeben, gibt die Methode die N höchsten absoluten Werte zurück. Dabei ist N der Wert, der in numScores übergeben wird. Rufen Sie beispielsweise RetrieveScores('', 0, 10) auf, um die besten 10 Punktestände zu erhalten. Rufen Sie RetrieveScores('player1', -5, 10) auf, um die Ergebnisse zu erhalten, die 5 Punkte über und unter dem Punktestand eines Spielers liegen.

Das Code-Repository für dieses Beispiel enthält ein Testspiel und die Implementierung einer Bestenliste als Proof of Concept. In dieser Anleitung stellen Sie das Spiel sowie die Bestenliste bereit und überprüfen, ob die API der Bestenliste richtig funktioniert und über das Internet auf sie zugegriffen werden kann.

Ziele

  • Erstellen einer Instanz von Memorystore for Redis
  • Erstellen eines monitorlosen Dienstes mit einem Endpunkt, der Anfragen an diese Instanz weiterleitet
  • Bereitstellen der Anwendung zum Führen einer Bestenliste auf GKE
  • Überprüfen der Funktionalität der Bestenliste mithilfe der bereitgestellten Anwendung, die API-Aufrufe durchführt

Kosten

In diesem Dokument verwenden Sie die folgenden kostenpflichtigen Komponenten von Google Cloud:

Mit dem Preisrechner können Sie eine Kostenschätzung für Ihre voraussichtliche Nutzung vornehmen. Neuen Google Cloud-Nutzern steht möglicherweise eine kostenlose Testversion zur Verfügung.

Hinweise

  1. Melden Sie sich bei Ihrem Google Cloud-Konto an. Wenn Sie mit Google Cloud noch nicht vertraut sind, erstellen Sie ein Konto, um die Leistungsfähigkeit unserer Produkte in der Praxis sehen und bewerten zu können. Neukunden erhalten außerdem ein Guthaben von 300 $, um Arbeitslasten auszuführen, zu testen und bereitzustellen.
  2. Wählen Sie in der Google Cloud Console auf der Seite der Projektauswahl ein Google Cloud-Projekt aus oder erstellen Sie eines.

    Zur Projektauswahl

  3. Die Abrechnung für das Google Cloud-Projekt muss aktiviert sein.

  4. Memorystore for Redis and Google Kubernetes Engine APIs aktivieren.

    Aktivieren Sie die APIs

  5. Wählen Sie in der Google Cloud Console auf der Seite der Projektauswahl ein Google Cloud-Projekt aus oder erstellen Sie eines.

    Zur Projektauswahl

  6. Die Abrechnung für das Google Cloud-Projekt muss aktiviert sein.

  7. Memorystore for Redis and Google Kubernetes Engine APIs aktivieren.

    Aktivieren Sie die APIs

Umgebung vorbereiten

In dieser Anleitung führen Sie Befehle in Cloud Shell aus. Mit Cloud Shell erhalten Sie Zugriff auf die Befehlszeile von Google Cloud. Außerdem enthält Cloud Shell das Google Cloud CLI und weitere Tools, die Sie für die Entwicklung in Google Cloud benötigen. Die Initialisierung von Cloud Shell kann mehrere Minuten dauern.

  1. Öffnen Sie Cloud Shell:

    Zu Cloud Shell

  2. Legen Sie als Standardzone von Compute Engine die Zone fest, in der Sie Ihre Google Cloud-Ressourcen erstellen möchten. In dieser Anleitung verwenden Sie die Zone us-central1-a in der Region us-central1.

    export REGION=us-central1
    export ZONE=us-central1-a
    gcloud config set compute/zone $ZONE
    
  3. Klonen Sie das GitHub-Repository, das den Beispielcode enthält.

    git clone https://github.com/GoogleCloudPlatform/memstore-gaming-leaderboard.git
    
  4. Rufen Sie das geklonte Verzeichnis auf.

    cd memstore-gaming-leaderboard
    
  5. Öffnen Sie in einem Texteditor die Datei leaderboardapp/k8s/appdeploy.yaml und ändern Sie den Platzhalter [YOUR_PROJECT_ID] in Ihre Google Cloud-Projekt-ID.

Instanz von Memorystore for Redis erstellen

Für diese Anleitung erstellen Sie eine Instanz der Basis-Stufe von Memorystore for Redis, die für das Testen und den Umfang dieser Anleitung geeignet ist. Für eine Bereitstellung in der Produktionsumgebung wird empfohlen, dass Sie eine Instanz der Standardstufe verwenden, die eine Verfügbarkeit von 99,9 % mit SLA und automatischem Failover bietet. Somit ist eine hohe Verfügbarkeit der Instanz sichergestellt. Weitere Informationen zu Instanzen der Standard-Stufe finden Sie unter Hochverfügbarkeit für Memorystore for Redis.

  • Erstellen Sie in Cloud Shell eine Instanz von Memorystore for Redis mit einer Größe von 1 GB:

    gcloud redis instances create cm-redis --size=1 \
      --tier=basic \
      --region=$REGION \
      --zone=$ZONE
    

    Die Verarbeitung dieses Befehls kann einige Minuten dauern.

Container-Images der Anwendung erstellen

In dieser Anleitung stellen Sie mithilfe von GKE eine einfache Anwendung zum Führen einer Bestenliste bereit. Da Unity und C# im Bereich Gaming sehr beliebt sind, werden für die Anwendungsebene C# und das ASP.NET-Framework verwendet.

Für die Bereitstellung der Bestenlisten-API in einem GKE-Cluster müssen Sie sie zuerst in eine Registry wie Container Registry hochladen.

  1. Öffnen Sie die Datei README.md in dem von Ihnen geklonten GitHub-Projekt.
  2. Folgen Sie der Anleitung zum Erstellen eines Docker-Images und zum Hochladen in Container Registry.

Mit diesem Image stellen Sie die Anwendung zum Führen einer Bestenliste bereit, nachdem Sie im nächsten Abschnitt den Cluster erstellt haben.

GKE-Cluster erstellen oder wiederverwenden

Bevor Sie die Anwendung zum Führen einer Bestenliste bereitstellen können, müssen Sie einen GKE-Cluster erstellen, für den Alias-IP-Bereiche aktiviert sind. Wenn Sie bereits einen solchen GKE-Cluster haben, können Sie ihn für diese Anleitung verwenden. Wenn Sie einen vorhandenen Cluster verwenden, müssen Sie einen zusätzlichen Schritt ausführen, um das kubectl-Befehlszeilentool mit Anmeldedaten für diesen Cluster zu konfigurieren.

  1. Wenn Sie einen Cluster erstellen möchten, geben Sie diesem den Namen leaderboard. Er muss zwei Knoten haben:

    gcloud container clusters create leaderboard --num-nodes=2 --enable-ip-alias
    

    Die Verarbeitung dieses Befehls kann einige Minuten dauern.

  2. Warten Sie, bis der Cluster gestartet ist, und prüfen Sie dann, ob er ausgeführt wird:

    gcloud container clusters list
    

    Der Cluster wird ausgeführt, wenn ein Eintrag mit dem Namen leaderboard und dem Status RUNNING vorhanden ist.

Anmeldedaten für einen vorhandenen Cluster konfigurieren

  1. Wenn Sie für diese Anleitung einen vorhandenen GKE-Cluster verwenden, konfigurieren Sie die Datei kubeconfig mit den Anmeldedaten für diesen Cluster. Verwenden Sie für name-of-existing-cluster den Namen Ihres Clusters.

    gcloud container clusters get-credentials name-of-existing-cluster
    

Instanz von Memorystore for Redis einem Kubernetes-Dienst zuordnen

Wenn die Instanz von Memorystore for Redis bereit ist, erstellen Sie einen Dienst innerhalb des GKE-Clusters, damit die Anwendungsebene eine Verbindung herstellen kann.

  1. Prüfen Sie, ob die Instanz von Memorystore for Redis ausgeführt wird:

    gcloud redis instances list --region=$REGION
    

    Die Ausgabe sieht in etwa so aus:

    INSTANCE_NAME  VERSION    REGION           TIER       SIZE_GB    HOST       PORT  NETWORK  RESERVED_IP  STATUS    CREATE_TIME
    cm-redis       REDIS_4_0  us-central1      STANDARD  1            10.0.0.3  6379  default  10.0.0.0/29  READY     2019-05-10T04:37:45
    

    Die Instanz wird ausgeführt, wenn in der Spalte STATUS READY steht. Ist das Feld HOST leer, hat die Instanz den Startvorgang nicht abgeschlossen. Warten Sie einen Moment und führen Sie den Befehl redis instances list noch einmal aus.

  2. Speichern Sie die IP-Adresse der Instanz in einer Umgebungsvariablen:

    export REDIS_IP=$(gcloud redis instances list --filter="name:cm-redis" --format="value(HOST)" \
        --region=$REGION)
    
  3. Erstellen Sie den Kubernetes-Dienst für Redis:

    kubectl apply -f k8s/redis_headless_service.yaml
    

    Dieser Dienst hat keinen Pod-Selektor, da er auf eine IP-Adresse außerhalb des Kubernetes-Clusters verweisen soll.

  4. Erstellen Sie einen Endpunkt, mit dem die zuvor abgerufene Redis-IP-Adresse definiert wird:

    sed "s|REDIS_IP|${REDIS_IP}|g" k8s/redis_endpoint.yaml | kubectl apply -f -
    
  5. Überprüfen Sie, ob Dienst und Endpunkt korrekt erstellt wurden:

    kubectl get services/redis endpoints/redis
    

    Wenn alles korrekt funktioniert, sieht die Ausgabe mit einem Eintrag für den Redis-Dienst und den Redis-Endpunkt in etwa so aus:

    NAME               TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
    service/redis      ClusterIP   10.59.241.103   none        6379/TCP   5m
    
    NAME               ENDPOINTS       AGE
    endpoints/redis    10.0.0.3:6379   4m
    

Weitere Informationen zu Kubernetes-Diensten und -Endpunkten finden Sie im Google Cloud-Blog unter Kubernetes best practices: mapping external services.

Anwendung und Dienst zum Führen einer Bestenliste auf Kubernetes bereitstellen

Die Redis-Instanz ist jetzt von einer Anwendung aus erreichbar, die im GKE-Cluster bereitgestellt wurde. Daher können Sie jetzt die Anwendung zum Führen einer Bestenliste bereitstellen.

  • Halten Sie sich an die Anleitung in der Datei README.md im Stammverzeichnis des GitHub-Repositorys, das Sie zuvor geklont haben.

Bereitstellung validieren

Die zur Verfügung gestellte Testspielanwendung, die in JavaScript geschrieben ist, kann dazu verwendet werden, Aufrufe an die Leaderboard API zu senden. Das folgende Code-Snippet zeigt, wie das Testspiel den Punktestand eines Spielers veröffentlicht, wenn dieser das Spiel beendet:

                    var scoreInfo = {
                        playerName: this.username,
                        score: this.calculateScore().toFixed(2)
                    };

                    var pThis = this;

                    var postUrl = "/api/score";
                    (async () => {
                        try {
                            await axios.post(postUrl, scoreInfo)
                        } catch (error) {
                            console.error(error);
                        }

                        var lbPromise = pThis.fetchLeaderboard();
                        var qPromise = pThis.fetchQuestions();

                        pThis.questions = await qPromise;
                        await lbPromise;
                    })();

Darüber hinaus führt die Anwendung einen API-Aufruf aus, um die Punktestände der Bestenliste abzurufen. Dabei werden entweder die besten Ergebnisse oder die Punktestände rund um den Namen des angegebenen Spielers aufgerufen. Dies sieht in etwa so aus:

                    var pThis = this;
                    var getUrl = "/api/score/retrievescores";

                    (async () => {
                        try {
                            var params = {
                                centerKey: '',
                                offset: 0,
                                numScores: 11
                            };

                            if (pThis.centered) {
                                params.centerKey = pThis.username;
                                params.offset = -5;
                                params.numScores = 11;
                            }

                            const response = await axios.get(getUrl, { params: params });
                            pThis.leaderboard = response.data;
                        } catch (error) {
                            console.error(error);
                            return []
                        }
                    })();

Gehen Sie so vor, um auf die Beispielanwendung zuzugreifen:

  1. Rufen Sie die IP-Adresse des Testspiels ab, indem Sie den folgenden Befehl ausführen:

    kubectl get ingress
    

    Die Ausgabe sieht etwa so aus:

    NAME                      HOSTS   ADDRESS        PORTS   AGE
    memstore-gaming-ingress   *       34.102.192.4   80      43s
    
  2. Wechseln Sie zu der folgenden URL, wobei ip_address_for_gke die Adresse für das Testspiel ist:

    http://ip_address_for_gke.
    

Hier handelt es sich um ein einfaches Beispiel, das aber ausreichend ist, um die grundlegende Verwendung der API zu demonstrieren. Wenn Sie Punktestände direkt aus einer Spieleclient-Anwendung veröffentlichen oder abrufen, die auf dem Gerät oder Rechner eines Nutzers ausgeführt wird, erfolgt der Aufruf der Bestenlisten-API in diesem Beispiel unter der öffentlichen IP-Adresse, die dem zugehörigen Kubernetes Load Balancer-Objekt zugewiesen ist. In dieser Beispielanwendung werden sowohl die Bestenlisten-API als auch der JavaScript-Client unter derselben IP-Adresse gehostet, die Sie mit dem oben aufgeführten Befehl kubectl get ingress abrufen können.

So werden die Methoden implementiert

Die in C# geschriebene Anwendung zum Führen einer Bestenliste verwendet für die Kommunikation mit Redis die StackExchange.Redis-Bibliothek. Die folgenden Code-Snippets zeigen, wie PostScore und RetrieveScores mithilfe von Redis und der StackExchange.Redis-Bibliothek implementiert werden.

Das folgende Code-Snippet veranschaulicht die Methode PostScore:

        public async Task<bool> PostScoreAsync(ScoreModel score)
        {
            IDatabase db = _redis.GetDatabase();

            // SortedSetAddAsync corresponds to ZADD
            return await db.SortedSetAddAsync(LEADERBOARD_KEY, score.PlayerName, score.Score);
        }

Das folgende Code-Snippet veranschaulicht die Methode RetrieveScores:

        public async Task<IList<LeaderboardItemModel>> RetrieveScoresAsync(RetrieveScoresDetails retrievalDetails)
        {
            IDatabase db = _redis.GetDatabase();
            List<LeaderboardItemModel> leaderboard = new List<LeaderboardItemModel>();

            long offset = retrievalDetails.Offset;
            long numScores = retrievalDetails.NumScores;

            // If centered, get rank of specified user first
            if (!string.IsNullOrWhiteSpace(retrievalDetails.CenterKey))
            {
                // SortedSetRankAsync corresponds to ZREVRANK
                var rank = await db.SortedSetRankAsync(LEADERBOARD_KEY, retrievalDetails.CenterKey, Order.Descending);

                // If specified user is not present, return empty leaderboard
                if (!rank.HasValue)
                {
                    return leaderboard;
                }

                // Use rank to calculate offset
                offset = Math.Max(0, rank.Value + retrievalDetails.Offset);

                // Account for number of scores when we're attempting to center
                // at element in rank [0, abs(offset))
                if(offset <= 0)
                {
                    numScores = rank.Value + Math.Abs((long)retrievalDetails.Offset) + 1;
                }
            }

            // SortedSetRangeByScoreWithScoresAsync corresponds to ZREVRANGEBYSCORE [WITHSCORES]
            var scores = await db.SortedSetRangeByScoreWithScoresAsync(LEADERBOARD_KEY,
                skip: offset,
                take: numScores,
                order: Order.Descending);

            var startingRank = offset;
            for (int i = 0; i < scores.Length; i++)
            {
                var lbItem = new LeaderboardItemModel
                {
                    Rank = startingRank++,
                    PlayerName = scores[i].Element.ToString(),
                    Score = scores[i].Score
                };
                leaderboard.Add(lbItem);
            }

            return leaderboard;
        }

Ergänzungen zum Beispielspiel

Die Bestenlisten-API entspricht den REST-Konventionen und dient nur als Beispiel. Zum Führen einer Bestenliste für ein Spiel in der Produktionsumgebung empfehlen wir, einen Authentifizierungsvorgang einzubinden, damit nur Punktestände von validierten Nutzern veröffentlicht werden.

Derzeit bietet Memorystore for Redis keine Möglichkeit zur nichtflüchtigen Speicherung von Punkteständen. Tritt also bei der Anwendung ein Problem auf, könnten daher alle Daten der Bestenliste verloren gehen. Wenn für Ihr Spiel eine dauerhafte Bestenliste erforderlich ist, sollten Sie die Punktestände der Bestenliste regelmäßig in einer nichtflüchtigen Datenbank sichern.

Bereinigen

Löschen Sie das Projekt, damit Ihrem Google Cloud-Konto die in dieser Anleitung verwendeten Ressourcen nicht in Rechnung gestellt werden.

Projekt löschen

  1. Wechseln Sie in der Google Cloud Console zur Seite Ressourcen verwalten.

    Zur Seite „Ressourcen verwalten“

  2. Wählen Sie in der Projektliste das Projekt aus, das Sie löschen möchten, und klicken Sie dann auf Löschen.
  3. Geben Sie im Dialogfeld die Projekt-ID ein und klicken Sie auf Shut down (Beenden), um das Projekt zu löschen.

Nächste Schritte