本教程将向您展示如何利用Memorystore for Redis,构建起运行在Google Kubernetes Engine(GKE)之上的ASP.NET排行榜应用程序。随后,我们还将通过一款JavaScript示例游戏演示如何发布并检索分数。本教程适用于希望在云端运行游戏排行榜的开发人员群体。


内容简介


排行榜属于多人游戏中的一项常见功能。排行榜能够实时显示玩家排名,鼓励玩家们积极参与游戏。对于这样一项捕捉玩家行为并实时进行得分排名的功能,Redis这类内存内数据库无疑是排行榜类应用的理想选择。


下图所示,为排行榜应用的基础架构:


在Google Cloud中,排行榜由VPC网络内的GKE集群外加一个独立Memorystore for Redis实例构成。


下图所示,为本教程中示例应用程序的基本架构。通过排行榜API,客户端得以与运行在Google Cloud中的Memorystore for Redis实例进行交互,完成对玩家分数的写入及读取。


排行榜API中使用的方法


排行榜应用API当中包含以下方法:


PostScore(string playerName, double score)
复制代码


此方法负责将特定玩家的分数发布至排行榜中。


RetrieveScores(string centerKey, int offset, int numScores)
复制代码


此方法负责下载一组得分。如果您将玩家ID作为


centerKey
复制代码


的值进行传递,则此方法将返回当前玩家排名之前及之后几个位置的对应得分。如果您没有传递


centerKey
复制代码


的值,则此方法仅返回前N名玩家的得分,其中N值为


numScores
复制代码


的取值。例如,如果希望获取前10名玩家的得分,则调用


RetrieveScores('', 0, 10)
复制代码


如果需要获取玩家当前排名之前及之后五名的对应得分,则调用


RetrieveScores('player1', -5, 10)
复制代码


本示例中的代码库包含一款模拟游戏,外加一套排行榜概念验证实现。在本教程中,我们可以部署游戏与排行榜,并验证排行榜API能否正常工作并通过互联网进行访问。


目标


  • 创建一个Memorystore for Redis 实例。

  • 创建一项headless服务,其中包含一个负责将请求定向至该实例的端点。

  • 将排行榜应用部署至GKE。

  • 使用部署完成的应用发出API调用,借此验证排行榜功能是否正常。


成本


本教程使用以下Google Cloud计费组件:


  • Google Kubernetes Engine

  • Memorystore for Redis


您可以使用我们提供的费用计算器根据预期使用量进行成本估算。我们还为Google Cloud新用户提供免费试用选项,请点击此处了解。


准备工作


1.在 GCP Console 的项目选择器页面上,选择或创建 GCP 项目。


注意:如果您不打算保留在此过程中创建的资源,请创建新的 GCP 项目,而不要选择现有的 GCP 项目。完成本教程介绍的步骤后,您可以删除所创建的项目,并移除与该项目关联的所有资源。


转到项目选择器页面


2.确保您的 Google Cloud Platform 项目已启用结算功能。 了解如何确认您的项目已启用结算功能。


3.启用Memorystore for Redis与Google Kubernetes Engine API。


启用 API


环境筹备


在本教程中,所有命令都将运行在Cloud Shell当中。通过Cloud Shell,您可以访问Google Cloud中的命令行,包括Google Cloud开发工作中常用的Cloud SDK与其他工具。Cloud Shell的初始化操作可能需要几分钟时间。


1.打开Cloud Shell:


打开Cloud Shell


2.将Compute Engine的默认可用区设置为您希望创建Google Cloud资源的可用区。在本教程中,我们选择


us-central1
复制代码


区域中的


us-central1-a
复制代码


可用区。


3.export REGION=us-central14.export ZONE=us-central1-a5.gcloud config set compute/zone $ZONE
复制代码


6.克隆包含示例代码的GitHub库:


7.git clone https://github.com/GoogleCloudPlatform/memstore-gaming-leaderboard.git
复制代码


8.转到克隆完毕的目录:


9.cd memstore-gaming-leaderboard
复制代码


10.使用文本编辑器,打开


leaderboardapp/k8s/appdeploy.yaml
复制代码


文件并将


[YOUR_PROJECT_ID]
复制代码


占位符替换为您的Google Cloud项目ID。


创建一个Memorystore for Redis实例


在本教程中,我们将创建一个Memorystore for Redis的Basic Tier实例,此实例已经能够满足我们的演示与测试需求。对于实际生产部署,我们建议您选择部署Standard Tier实例,其提供99.9%服务水平协议(SLA)与自动故障转移机制,可确保实例具备更高可用性。关于Standard Tier实例的细节信息,请参阅Memorystore for Redis的高可用性说明。


-在Cloud Shell中,创建一个1 GB Memorystore for Redis实例:


gcloud redis instances create cm-redis --size=1 --tier=standard --region=$REGION --zone=$ZONE
复制代码


此命令可能需要运行几分钟时间。


构建应用容器镜像


在本教程中,我们将使用GKE部署一款简单的排行榜应用。考虑到Unity与C#在游戏开发领域的高流行度,我们会在应用层中使用C#与ASP.NET框架。


要将排行榜API部署至GKE集群内,我们首先需要将其上传至Container Registry等注册表当中。


1.打开克隆完毕的GitHub项目中的


README.md
复制代码


文件。


2.按照说明创建Docker镜像并将其上传至Container Registry。


在完成下一节中介绍的集群创建工作后,我们即可利用这套镜像部署自己的排行榜。


创建或重用一套GKE集群


在部署排行榜应用之前,我们首先需要创建一套启用alias IP范围的GKE集群。如果您已经拥有一套GKE集群(而且启用了alias IP范围),那么直接在本示例中使用即可。要使用现有集群,您必须通过额外操作步骤利用集群凭证配置


kubectl
复制代码


命令行工具。


1.如果您希望创建一套新集群,推荐使用


leaderboard
复制代码


名称并在其中包含两个节点:


2.gcloud container clusters create leaderboard --num-nodes=2 --enable-ip-alias
复制代码


此命令可能需要运行几分钟时间。


3.在集群启动完毕之后,验证其是否正常运行:


4.gcloud container clusters list
复制代码


只要在集群入口看到名称


leaderboard
复制代码


的状态为


RUNNING
复制代码


即证明集群正在运行。


为现有集群配置凭证


1.如果希望在本教程内使用现有GKE集群,请使用集群凭证配置


kubeconfig
复制代码


文件。在name-of-existing-cluster部分,使用现有集群的真实名称。


备注: 如果选择新建集群,则无需执行以上步骤。


gcloud container clusters get-credentials name-of-existing-cluster
复制代码


将Memorystore for Redis实例映射至Kuernetes服务


当Memorystore for Redis实例准备就绪后,我们即可在GKE集群中选定该应用层要接入的服务。


1.验证该Memorystore for Redis实例是否正在运行:


2.gcloud redis instances list --region=$REGION
复制代码


您将看到如下输出结果:


INSTANCE_NAME  VERSION    REGION           TIER       SIZE_GB    HOST       PORT  NETWORK  RESERVED_IP  STATUS    CREATE_TIMEcm-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
复制代码


如果


STATUS
复制代码


列显示为


READY
复制代码


则证明实例正在运行。如果


HOST
复制代码


字段为空,则该实例尚未完成启动流程。请稍等一会,再重新运行


redis instances list
复制代码


命令。


3.将该实例的IP地址保存为一项环境变量:


4.export REDIS_IP=$(gcloud redis instances list --filter="name:cm-redis" --format="value(HOST)" 5.    --region=$REGION)
复制代码


6.为Redis创建Kubernetes服务:


7.kubectl apply -f k8s/redis_headless_service.yaml
复制代码


此服务并未包含Pod选择器,因为我们希望将其指向Kubernetes集群之外的IP地址。


8.创建一个端点,用于定义先前检索的Redis IP地址:


9.sed "s|REDIS_IP|${REDIS_IP}|g" k8s/redis_endpoint.yaml | kubectl apply -f -
复制代码


10.验证该服务与端点是否正确创建完成:


11.kubectl get services/redis endpoints/redis
复制代码


如果一切运作正常,您将看到如下输出,包括Redis服务与Redis端点的入口:


NAME               TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGEservice/redis      ClusterIP   10.59.241.103   none        6379/TCP   5mNAME               ENDPOINTS       AGEendpoints/redis    10.0.0.3:6379   4m
复制代码


若需了解更多与Kubernetes服务与端点相关的信息,请参阅Google Cloud博客上发布的《Kubernetes最佳实践:映射至外部服务》。


在Kubernetes上部署排行榜应用与服务


现在,我们已经可以通过部署在GKE集群上的应用访问Redis实例。做好这项准备,接下来就是着手部署排行榜应用。


  • 请遵循克隆完成的GitHub repo根目录中README.md文件的具体说明。


验证部署成果


这里我们使用由JavaScript编写的模拟游戏调用排行榜API。以下代码段,展示了模拟游戏在玩家完成游戏之后发布得分的具体方式:


leaderboardapp/Views/Home/Index.cshtmlView on GitHubvar 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;})();
复制代码


此外,该应用还会执行API调用以检索排行榜上的原有得分,包括前N位最高得分以及当前玩家之前与之后几名的对应得分,具体如下:


leaderboardapp/Views/Home/Index.cshtmlView on GitHubvar 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 []}})();
复制代码


要访问这里的示例应用,请按以下步骤操作:


1.运行以下命令以获取模拟游戏的IP地址:


2.kubectl get ingress
复制代码


输出示例如下:


NAME                      HOSTS   ADDRESS        PORTS   AGEmemstore-gaming-ingress   *       34.102.192.4   80      43s
复制代码


3.浏览以下URL,其中ip_address_for_gke部分为模拟游戏的地址:


4.http://ip_address_for_gke.
复制代码


这里的示例虽然简单,但足以体现API的基本使用情况。当大家直接通过用户设备或者计算机上运行的客户端应用发布或检索分数时,本示例将在对应分配的Kubernetes Load Balancer对象公网IP地址之上、对排行榜API执行调用。在此示例应用当中,排行榜API与JavaScript客户端都托管在前文所示、使用


kubectl get ingress
复制代码


命令获取的同一IP地址上。


各方法的具体实现方式


我们这款使用C#编写的排行榜应用,利用StackExchange.Redis库实现与Redis之间的通信。以下代码片段展示了如何利用Redis与


StackExchange.Redis
复制代码


库实现


PostScore
复制代码



RetrieveScores
复制代码


方法。


以下代码片段用于显示


PostScore
复制代码


方法:


leaderboardapp/RedisLeaderboardRepo.csView on GitHubpublic async Task<bool> PostScoreAsync(ScoreModel score){IDatabase db = _redis.GetDatabase();// SortedSetAddAsync对应ZADDreturn await db.SortedSetAddAsync(LEADERBOARD_KEY, score.PlayerName, score.Score);}
复制代码


以下代码片段用于显示


RetrieveScores
复制代码


方法:


leaderboardapp/RedisLeaderboardRepo.csView on GitHubpublic 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 (!string.IsNullOrWhiteSpace(retrievalDetails.CenterKey)){// SortedSetRankAsync 对应ZREVRANKvar rank = await db.SortedSetRankAsync(LEADERBOARD_KEY, retrievalDetails.CenterKey, Order.Descending);// 如果不存在特定用户,则返回空排行榜if (!rank.HasValue){return leaderboard;}// 利用排名以计算偏移offset = Math.Max(0, rank.Value + retrievalDetails.Offset);// 在尝试居中显示时,通过// 排名[0, abs(offset))中的元素计入分数if(offset <= 0){numScores = rank.Value + Math.Abs((long)retrievalDetails.Offset) + 1;}}// SortedSetRangeByScoreWithScoresAsync对应 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;}
复制代码


关于示例游戏的补充说明


这里的排行榜API遵循REST约定,而且仅作为示例使用。在运行生产级游戏排行榜时,我们建议大家将身份验证流程整合进来,以确保首先验证用户身份、而后发布对应得分。


目前,Memorystore for Redis尚不提供对玩家得分的持久存储。因此一旦应用发生故障,排行榜信息有可能丢失。如果需要为游戏构建永久性排行榜,则应定期在永久数据库内备份排行榜分数。


清理工作


在完成本教程之后,为了避免相关资源继续产生使用成本,请在您的Google Cloud账户中删除此项目。


删除此项目


警告: 删除项目可能造成以下结果:


  • 项目中的所有内容均被删除。如果您在本教程中使用原有项目,则在删除此项目的同时,项目中其他已经完成的工作成果也将被一并删除。

  • 自定义项目ID丢失。创建此项目时,您可能创建了希望未来继续使用的自定义项目ID。为了避免URL(例如 appspot.com URL)使用该项目ID,请仅删除项目内部的特定资源,而非删除整个项目。


如果您后续还希望体验其他教程与入门指南内容,我们建议大家重复使用当前项目以控制云资源使用带来的成本。


2.在Cloud Console中,前往资源管理页面。


前往资源管理页面


3.在项目列表中,选定您希望删除的项目,并点击Delete 。


4.在对话框内输入项目ID,而后点击Shut down以删除该项目。


后续步骤


  • 了解Memorystore for Redis各关键概念。

  • 了解如何如何通过OpenCensus对Memorystore for Redis工作负载进行客户端跟踪。

  • 关于排行榜概念的更多细节信息,请参阅 Google Play Games Services说明文档。



相关推荐