前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >分布式系统开发实战:实战,使用AWS平台实现Serverless架构

分布式系统开发实战:实战,使用AWS平台实现Serverless架构

作者头像
IT大咖说
发布2021-06-15 17:41:52
1.7K0
发布2021-06-15 17:41:52
举报
文章被收录于专栏:IT大咖说IT大咖说

◆ 实战:使用AWS平台实现Serverless架构

本例将演示利用AWS平台的Serverless架构来让游戏实现全球同服。

全球同服的游戏架构有以下需求。

·全球所有玩家的持久化信息(包括用户基本信息、等级、装备、进度等状态信息)都保存在中心站点。玩家统一通过HTTP(S)登录中心站点并获取状态信息。

·对战初始,由中心站点对玩家进行重定向到对应的Game Server。

在对战过程中,使用TCP长连接从而保证更好的游戏体验。

·对战结束后,客户端与Game Server中断TCP连接,对战结果数据回滚到中心站点并保存最终的状态信息。

基于上述的架构,游戏完全构建在统一的“大世界”中(唯一中心站点),并且由分布在全球的Game Server来保证游戏的低延迟。由于Game Server分布在全球不同的地区,如何做到资源的快速扩展和按需伸缩将是一个难点。下面将以Serverless架构的方式阐述实现这一需求。

首先,AWS平台提供了非常完整的API接口,开发者可以选择各种语言的SDK完成对资源的调度,这里我们可以将代码运行在Lambda中。如下所示,我们的中心站点(即Lambda部署的站点)选择的是Virginia(弗吉尼亚,美国东部地区),通过Node.js SDK跨地区到Tokyo(东京,日本首都)来启动EC2服务器。

代码语言:javascript
复制
var AWS = require('aws-sdk');
exports.handler = function (event, context) {
console.log("Received data as:", event);
var ec2 = new AWS.EC2({region: 'ap-northeast-1'});
var params = {
ImageId: 'ami-29160d47',
InstanceType: 't2.micro',
KeyName: 'Tech-labs',
SecurityGroupIds: ['sg-d0aa1bb4'],
IamInstanceProfile: {Name: 'EC2-Admin'},MinCount: 1,
MaxCount: 1
};
// 创建实例
ec2.runInstances(params, function (err, data) {
if (err) {
console.log("Could not create instance", err);
context.fail(err);
}
var instanceId = data.Instances[0].InstanceId;
console.log("Created instance", instanceId);
// 存储实例id并设置状态
context.succeed(instanceId);
});
};

由于启动EC2的过程是一个异步过程,所以我们需要记录相关的服务器启动信息,并定义另一接口接收Game Server在服务就绪后返回的回执信息,代码如下。

代码语言:javascript
复制
var AWS = require('aws-sdk');
exports.handler = function (event, context) {
console.log("Received data as:", event);
var instanceId = event.instanceId;
var region = event.region;
var publicIp = event.publicIp;
var version = event.version;
...
// 检查instanceId并在线更新实例状态
};

同时,这种回执接口的API(包括其他API)都可以考虑使用Amazon API Gateway服务进行部署。API Gateway可以帮助我们将现有函数快速发布为RESTful的API接口,并同时利用CloudFront的边缘节点进行部署,以保证访问端能获得更低的延迟。按照上例的回执,Lambda函数可以构造API Gateway的配置,如图10-5所示。

图10-5 API Gateway的配置

请求示例如下。

代码语言:javascript
复制
/game/servers/i-dd861842
{
"region":"ap-northeast-1",
"publicIp":"52.193.34.102",
"version":"110"
}

接下来,为了确保Game Server的状态是正常的,使得玩家能被路由到正确的服务器上,可以构造另一个类似心跳的Lambda函数,用来接收Game Server的状态信息。心跳频率可根据需求进行调整,当然,如果在频率不需要很高的情况下(≥1min),也可以利用CloudWatch来发起报警,并同时发起SNS通知Lambda函数以更新Game Server的状态。

最后,在Game Server具备了自动按需扩展(Scale out)的能力后,我们就需要考虑如何解决Game Server的缩减(Scale in)了。在这里,我们采用CloudWatch->SNS->Lambda(cross region)的方式来实现GameServer的缩减,具体流程说明如下。

(1)Game Server自定义指标(Custom Metrics)将当前服务器的在线人数发送到CloudWatch中。

代码语言:javascript
复制
#!/bin/bash
#get instance-id from local meta-data id=$(curl -s http://169.254.169.254/
latest/meta-data/instance-id)
#get current onlinePlayers players=$(...) aws cloudwatch put-metric-data --metric-name
"OnlinePlayers" --namespace "GameServer" --dimension InstanceId= $id --value $players

(2)设定CloudWatch的报警规则,当服务器在线人数为零时,会触发SNS通知,如图10-6所示。

图10-6 CloudWatch自定义指标报警

在实际场景中,需要通过以下脚本自动建立报警。

代码语言:javascript
复制
aws cloudwatch put-metric-alarm --alarm-name $id-players--alarm-description "Alarm when
online players less than 0" --metric-name "OnlinePlayers" --namespace "GameServer" --dimension
"Name=InstanceId,Value=$id" --statistic Maximum --period 300 --threshold 0
--comparison-operator LessThanOrEqualToThreshold --evaluation-periods 2 --alarm-actions
arn:aws:sns:ap-northeast-1:111111111222: ScaleInTopic

(3)订阅了SNS服务通知的中心站点的Lambda函数,用于终止服务器,如图10-7所示。

图10-7 Lambda函数订阅SNS服务通知

用于终止服务器的Lambda函数如下。

代码语言:javascript
复制
var AWS = require('aws-sdk');
exports.handler = function (event, context) {
console.log("Received data as:", event);
var message = JSON.parse(event.Records[0].Sns.Message);
var region = event.Records[0].EventSubscriptionArn.split(":")[3];
console.log("Need to terminate the server in region:", region);
var ec2 = new AWS.EC2({region: region});
console.log("Need to terminate the server:", message);
var instanceId = message.Trigger.Dimensions[0].value;
console.log("Need to terminate the server:", instanceId);
// 检查实例的状态是否可以从DynamoDB中终止,并在终止时更新状态
var params = {InstanceIds: [instanceId]};
// 终止实例
ec2.terminateInstances(params, function (err, data) {
if (err) {
console.log("Could not terminate instance", err);
// 回滚终止的实例
context.fail(err);
}
for (var i in data.TerminatingInstances) {
var instance = data.TerminatingInstances[i];
console.log('TERM:\t' + instance.InstanceId);
// 删除终止的实例
}
context.succeed(data.TerminatingInstances);
});
};

通过以上方法,我们已基本实现了基于事件触发的Serverless架构对全球分布的Game Server的调度,Serverless全球同服游戏架构如图10-8所示。

图10-8 Serverless全球同服游戏架构

来源:

https://www.toutiao.com/i6967972069267259937/

“IT大咖说”欢迎广大技术人员投稿,投稿邮箱:aliang@itdks.com

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-06-01,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 IT大咖说 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • ◆ 实战:使用AWS平台实现Serverless架构
相关产品与服务
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档