Tutoriel : utiliser Memorystore pour Redis en tant que classement de jeu

Ce tutoriel explique comment utiliser Memorystore pour Redis pour créer une application de classement basée sur ASP.NET et exécutée sur Google Kubernetes Engine (GKE), puis comment publier et récupérer des scores à l'aide d'un exemple de jeu basé sur JavaScript. Ce document est destiné aux développeurs de jeux qui souhaitent exploiter leurs propres classements dans le cloud.

Présentation

Les classements sont une fonctionnalité couramment utilisée dans les jeux multijoueurs. Le classement des joueurs est affiché en temps réel, ce qui encourage les joueurs à s'impliquer davantage dans le jeu. Pour capturer les actions des joueurs et classer leur score en temps réel, une base de données en mémoire telle que Redis constitue un excellent choix.

Le schéma suivant montre l'infrastructure d'un classement :

Schéma illustrant un cluster GKE dans un VPC et une instance Memorystore pour Redis distincte.

Dans Google Cloud, le classement se compose d'un cluster GKE au sein d'un réseau VPC et d'une instance Cloud Memorystore pour Redis distincte.

Le schéma suivant montre l'architecture de l'application dans le cadre de ce tutoriel. Les clients utilisent l'API de classement pour interagir avec les scores gérés dans une instance Cloud Memorystore pour Redis exécutée dans Google Cloud.

Schéma montrant l'architecture de l'application dans ce tutoriel

Méthodes de l'API de classement

L'API pour l'application de classement comprend les méthodes suivantes :

  • PostScore(string playerName, double score) : cette méthode publie un score dans le classement pour le joueur spécifié.
  • RetrieveScores(string centerKey, int offset, int numScores) : cette méthode télécharge un ensemble de scores. Si vous utilisez un ID de joueur comme valeur pour centerKey, la méthode renvoie les scores supérieurs et inférieurs à ceux du joueur spécifié. Si vous n'utilisez pas de valeur pour centerKey, la méthode renvoie les N meilleurs scores absolus, N étant la valeur que vous utilisez pour numScores. Par exemple, pour obtenir les 10 meilleurs scores, appelez la méthode RetrieveScores('', 0, 10). Pour obtenir cinq scores au-dessus et au-dessous du score d'un joueur, appelez la méthode RetrieveScores('player1', -5, 10).

Le dépôt de code de cet exemple comprend une simulation de jeu et une mise en œuvre du classement comme démonstration de faisabilité. Au cours de ce tutoriel, vous déploierez le jeu et le classement. Vous vérifierez ensuite que l'API de classement fonctionne correctement et que vous pouvez y accéder via Internet.

Objectifs

  • Créer une instance Memorystore pour Redis
  • Créer un service sans interface graphique avec un point de terminaison qui dirige les requêtes vers cette instance
  • Déployer l'application de classement sur GKE
  • Vérifier la fonctionnalité de classement à l'aide de l'application déployée qui effectue des appels d'API

Coûts

Ce tutoriel utilise les composants facturables suivants de Google Cloud :

Obtenez une estimation des coûts en fonction de votre utilisation prévue à l'aide du simulateur de coût. Les nouveaux utilisateurs de Google Cloud peuvent bénéficier d'un essai gratuit.

Avant de commencer

  1. Connectez-vous à votre compte Google.

    Si vous n'en possédez pas déjà un, vous devez en créer un.

  2. Dans Google Cloud Console, sur la page de sélection du projet, sélectionnez ou créez un projet Google Cloud.

    Accéder à la page de sélection du projet

  3. Assurez-vous que la facturation est activée pour votre projet Cloud. Découvrez comment vérifier que la facturation est activée pour votre projet.

  4. Activer les API Memorystore for Redis and Google Kubernetes Engine.

    Activer les API

Préparer l'environnement

Dans ce tutoriel, vous exécutez des commandes dans Cloud Shell. Cloud Shell vous donne accès à l'interface de ligne de commande dans Google Cloud et comprend le SDK Cloud et d'autres outils dont vous avez besoin pour le développement dans Google Cloud. L'initialisation de Cloud Shell peut prendre quelques minutes.

  1. Ouvrez Cloud Shell.

    Ouvrir Cloud Shell

  2. Définissez la zone Compute Engine par défaut sur la zone dans laquelle vous allez créer vos ressources Google Cloud. Dans ce tutoriel, vous utilisez la zone us-central1-a de la région us-central1.

    export REGION=us-central1
    export ZONE=us-central1-a
    gcloud config set compute/zone $ZONE
    
  3. Clonez le dépôt GitHub qui contient l'exemple de code :

    git clone https://github.com/GoogleCloudPlatform/memstore-gaming-leaderboard.git
    
  4. Accédez au répertoire cloné :

    cd memstore-gaming-leaderboard
    
  5. À l'aide d'un éditeur de texte, ouvrez le fichier leaderboardapp/k8s/appdeploy.yaml et remplacez l'espace réservé [YOUR_PROJECT_ID] par votre ID de projet Google Cloud.

Créer une instance Memorystore pour Redis

Dans le cadre de ce tutoriel, vous créez une instance Memorystore pour Redis de niveau de base, adaptée aux tests et au présent tutoriel. Dans le cadre d'un déploiement en production, nous vous recommandons de déployer une instance de niveau standard qui bénéficie d'un contrat de niveau de service garantissant une disponibilité de 99,9 %, le basculement automatique permettant notamment d'atteindre ce haut niveau de disponibilité. Pour plus d'informations sur les instances de niveau standard, consultez la section Haute disponibilité pour Memorystore pour Redis.

  • Dans Cloud Shell, créez une instance Memorystore pour Redis de 1 Go.

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

    L'exécution de cette commande peut prendre quelques minutes.

Créer les images de conteneur d'application

Dans le cadre de ce tutoriel, vous déployez une application de classement simple à l'aide de GKE. Unity et C# étant couramment utilisés dans les jeux, le niveau applicatif utilise C# et le framework ASP.NET.

Pour déployer l'API de classement sur un cluster GKE, vous devez d'abord télécharger l'API dans un registre tel que Container Registry.

  1. Ouvrez le fichier README.md dans le projet GitHub que vous avez cloné.
  2. Suivez les instructions pour créer une image Docker et la télécharger dans Container Registry.

Vous utiliserez cette image pour déployer l'application de classement après avoir créé le cluster dans la section suivante.

Créer ou réutiliser un cluster GKE

Avant de déployer l'application de classement, vous devez créer un cluster GKE avec des plages d'adresses IP d'alias activées. Si vous disposez déjà d'un cluster GKE (avec des plages d'adresses IP d'alias), vous pouvez utiliser ce cluster dans le cadre de ce tutoriel. Si vous utilisez un cluster existant, vous devez exécuter une étape supplémentaire pour configurer l'outil de ligne de commande kubectl avec les identifiants de ce cluster.

  1. Si vous souhaitez créer un cluster, nommez-le leaderboard et attribuez-lui deux nœuds :

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

    L'exécution de cette commande peut prendre quelques minutes.

  2. Une fois le démarrage du cluster terminé, vérifiez qu'il est en cours d'exécution :

    gcloud container clusters list
    

    Le cluster est en cours d'exécution lorsqu'une entrée dont le nom est leaderboard et dont l'état est RUNNING s'affiche.

Configurer les identifiants pour un cluster existant

  1. Si vous utilisez un cluster GKE existant dans le cadre de ce tutoriel, configurez le fichier kubeconfig avec les identifiants de ce cluster. Pour name-of-existing-cluster, utilisez le nom de votre cluster.

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

Mapper l'instance Memorystore pour Redis avec un service Kubernetes

Lorsque l'instance Memorystore pour Redis est prête, vous créez un service dans le cluster GKE afin que le niveau applicatif puisse se connecter.

  1. Vérifiez que l'instance Memorystore pour Redis est en cours d'exécution :

    gcloud redis instances list --region=$REGION
    

    Un résultat semblable à celui-ci s'affiche :

    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
    

    L'instance est en cours d'exécution lorsque la colonne STATUS affiche l'état READY. Si le champ HOST est vide, l'instance n'a pas terminé le processus de démarrage. Attendez quelques minutes et exécutez à nouveau la commande redis instances list.

  2. Stockez l'adresse IP de l'instance dans une variable d'environnement :

    export REDIS_IP=$(gcloud redis instances list --filter="name:cm-redis" --format="value(HOST)" \
        --region=$REGION)
    
  3. Créez le service Kubernetes pour Redis :

    kubectl apply -f k8s/redis_headless_service.yaml
    

    Ce service ne dispose pas de sélecteur de pod, car vous souhaitez qu'il pointe vers une adresse IP située en dehors du cluster Kubernetes.

  4. Créez un point de terminaison qui définit l'adresse IP Redis que vous avez récupérée précédemment :

    sed "s|REDIS_IP|${REDIS_IP}|g" k8s/redis_endpoint.yaml | kubectl apply -f -
    
  5. Vérifiez que le service et le point de terminaison ont été créés correctement :

    kubectl get services/redis endpoints/redis
    

    Si tout fonctionne correctement, un résultat de ce type, qui inclut une entrée pour le service Redis et pour le point de terminaison Redis, s'affiche :

    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
    

Pour plus d'informations sur les services et les points de terminaison Kubernetes, consultez l'article sur les bonnes pratiques de Kubernetes pour le mappage de services externes sur le blog Google Cloud.

Déployer l'application et le service de classement sur Kubernetes

L'instance Redis est maintenant accessible à partir d'une application déployée dans le cluster GKE. Par conséquent, vous êtes prêt à déployer l'application de classement.

  • Suivez les instructions du fichier README.md inclus dans le répertoire racine du dépôt GitHub que vous avez cloné précédemment.

Valider le déploiement

L'application de simulation de jeu fournie, écrite en JavaScript, peut être utilisée pour faire des appels à l'API de classement. L'extrait de code suivant montre comment la simulation de jeu publie des scores après que le joueur ait fini de jouer :

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

En outre, l'application effectue un appel d'API pour récupérer les scores du classement, soit les meilleurs scores, soit ceux associés au nom du joueur, comme suit :

                    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 []
                        }
                    })();

Pour accéder à l'exemple d'application, procédez comme suit :

  1. Obtenez l'adresse IP de la simulation de jeu en exécutant la commande suivante :

    kubectl get ingress
    

    Le résultat ressemble à ce qui suit :

    NAME                      HOSTS   ADDRESS        PORTS   AGE
    memstore-gaming-ingress   *       34.102.192.4   80      43s
    
  2. Accédez à l'URL suivante, où ip_address_for_gke est l'adresse de la simulation de jeu :

    http://ip_address_for_gke.
    

Cet exemple est simple, mais il permet de démontrer l'utilisation de base de l'API. Lorsque vous publiez ou récupérez des scores directement à partir d'une application cliente de jeu en cours d'exécution sur un appareil ou une machine utilisateur, l'exemple appelle l'API de classement sur l'adresse IP publique attribuée à son objet d'équilibreur de charge Kubernetes. Pour cet exemple d'application, l'API de classement et le client JavaScript sont hébergés sur la même adresse IP que celle obtenue à l'aide de la commande kubectl get ingress affichée précédemment.

Comment mettre en œuvre les méthodes

L'application de classement, écrite en C#, utilise la bibliothèque StackExchange.Redis pour communiquer avec Redis. Les extraits de code suivants montrent comment PostScore et RetrieveScores sont mis en œuvre à l'aide de Redis et de la bibliothèque StackExchange.Redis.

L'extrait de code suivant illustre la méthode 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);
        }

L'extrait de code suivant illustre la méthode 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;
        }

Ajouts à l'exemple de jeu

L'API de classement respecte les conventions REST et n'est fournie qu'à titre d'exemple. Lorsque vous exécutez un classement de jeu en phase de production, nous vous recommandons d'intégrer un flux d'authentification afin que seuls les scores provenant d'utilisateurs validés puissent être publiés.

Actuellement, Memorystore pour Redis n'assure pas la persistance des scores des joueurs. Par conséquent, si l'application rencontre un problème, vous pouvez perdre les informations du classement. Si votre jeu nécessite un classement persistant, vous devez sauvegarder périodiquement les scores du classement dans une base de données persistante.

Nettoyer

Pour éviter que les ressources utilisées lors de ce tutoriel ne soient facturées sur votre compte Google Cloud, supprimez le projet.

Supprimer le projet

  1. Dans Cloud Console, accédez à la page Gérer les ressources.

    Accéder à la page Gérer les ressources

  2. Dans la liste des projets, sélectionnez le projet que vous souhaitez supprimer, puis cliquez sur Supprimer .
  3. Dans la boîte de dialogue, saisissez l'ID du projet, puis cliquez sur Arrêter pour supprimer le projet.

Étapes suivantes