StickEngine-架构8-弹性计算
整体概述是 http://dreamyouxi.com:7129/blog/1203
在这里弹性计算的主要目的是为了节约成本。为了实现弹性计算,至少要有2大块的协作,才能实现。
游戏进程侧的自动伸缩 和 云计算平台的可编程扩展。
本文包含了一下几个方面阐述:
1.费用成本分析。
2.方案选型分析(kubernetes & docker 和 自建方案)。
3.StickEngine中的实践方案细则(进程弹性计算和云平台可编程扩展)。
本文阐述案例场景为:和平精英,王者荣耀 这种开房间式游戏,腾讯云。
费用成本分析:
这个更像是一个数学问题。腾讯云计费模式可以是以下模式:
1.包月计费。(4核心8G,385/月,0.53/小时)
2.按使用量计费(使用时间精度为秒)。(4核心8G,0.71/小时)
实际中24小时游戏实际人数曲线如下绿色线条
同理可以看做是自然时间和费用的曲线连续可导。包年包月方式也连续可导 这样就可以量化分析了。
那么成本对比问题就变为了黄色曲线(包年包月)围成的面积 和 绿色曲线(按量计费-秒)的面积大小。按照上述单价,时间线拉长为1个月的话,曲线面积要是矩形面积的74%以下,按量计费才有优势。
上面只是一个简单的分析情况。实际应用中还有一些其他维度需要考虑(比如按量计费 新申请到可用大约会有分钟级延迟,游戏推广计划)
方案分析:
k8s和docker在弹性计算着方面算是比较良好的组合了,纯只用这2个来构建的话在本文的应用场景下会有一些限制和问题:
游戏进程可docker化,进程无状态,腾讯云计费在进行docker扩容缩容的时候会进行虚拟机的申请和销毁操作,费用也是按照虚拟机的按量计费模式。
在本文的应用场景中带来的弊端是,游戏战斗时包含了大量状态和本地计算结果,进程的迁徙需要精确控制和比较复杂的控制逻辑。虚拟机扩容的话延迟大约是分钟级的,虽然docker启动速度快但是木桶效应导致整体来说时间是分钟级的。
如果资源池储备得够多的话,docker+k8s组合在启动速度上更胜一筹。
在StickEngine中,采用的是自建方案。下面开始阐述分析自建方案的实践过程。
在云平台的可编程扩展中StickEngine中采用的是https的API方式和云平台进行交互。当然在这里也可以采用它提供的SDK进行交互。
(这里有个小插曲,原本打算用最新C#-SDK进行开发的,但是下载下来要用VS2017编译,本机没有安装,就又去拿了老版本的C#-SDK发现已经弃用了,就拿来改了改,修改为了C#和C++协作的本质是http协议的API调用方式)。
在游戏进程侧,需要具备的是伸缩功能(随时加服务器和能随时减服务器),在此之上构建自动化伸缩这个过程,在这里进行控制的CenterServer,该进程是扮演服务发现,进程控制等功能,本文的弹性计算控制也是在该进行下进行。
在弹性计算上面,CenterServer发挥的作用包括(和云平台交互,全局负载监控,自动伸缩控制,人工控制等)
下图是CenterServer的控制台提供的信息。
从整个系统来看,一次快照下系统负载有三个状态,正常,过载,低负载。
过载或者低负载才会进行伸缩控制,虽然分类为3个状态但是他们并不像是一个密闭的状态机,外部负载变化过快的话,三个状态转移之间转移切换,情况稍微复杂了一些。
为了简化理念,在这里实践的是无状态的CenterServer,center本身无状态,上述3个状态指示描述当前快照操作下是什么状态。
这样设计的一些好处包括:center进程可自由启停,不依赖系统历史状态,灵活等。
要达到这个设计理念,整个系统的数据存储设计就很关键了,如果存储在DB里面,那么会涉及到云平台,游戏进程,DB 三者之间的数据协调问题。
在StickEngine中,去掉了DB,变为了云平台和游戏进程之间的二者数据协调问题。之所能去掉因为这里用了一些magic的做法,比如云平台的自定义状态存储在云主机的name字段如下图
在云平台上每一台虚拟机的名称可以是60个字符,因此吧这个利用起来那就是一个去掉DB的契机了。
过载:
处于该状态下需要进行扩容操作,扩容操作的话在这里可以分为好几个层次,一个是新申请机器,一个是从可用资源池中释放负载能力。
从资源池中添加负载是最快的方法,秒级利用。新申请机器的话扩容就是分钟级的了。
低负载:
当负载过低时,需要关闭一些进程缩减机器来减少成本。在该应用场景下,先做的操作是屏蔽进程,加入可用资源池中,后续操作可能是销毁或者继续呆在资源池或者被复用。
可用资源池:
之所以引入此概念是有以下原因:
1.新申请机器到可用需要的时间是3分钟左右,这个响应速度比较慢。
2.在计费模式中频繁申请机器相比复用已经申请的机器,可以减少更多一些不可抗拒的固定成本。
单台云主机状态和其状态转换
New(新申请的机器,还未初始化环境,从未链接上CenterServer的)
Running(运作中的标记)
Disable(清空负载操作中的标记,或者资源池里的标记)
Destroy(标记为需要退库的机器)
Disble和Running他们之间能够相互转换是因为Disable包含了资源池,过载时将会优先从这里面进行释放负载,缩容时也会在这里面进行销毁云主机。
虽然云主机分为了3个状态,和标记了状态转移。但是在这里并不能完全严格按照这种状态转移模式去操作,直接原因是健壮性。比如宕机,数据异常,网络异常,欠费等操作,都会导致逻辑的混乱。
在StickEngine中,在这里持谨慎的态度进行操作,只是尽可能按照状态图进行操作。为了应付不可预知的情况,也因此添加了一些比较爆炸的规则
比如:新申请的云主机5分钟后还未切换到running状态的将会强制重启几次。
比如:新申请的云主机10分钟后还未切换到running状态的将会直接销毁。
比如:云主机名字不符合规则直接销毁。
云主机的初始化:
在StickEngine中,由于每个进程启动的命令行不一样,机器配置和IP等信息每台机器也不一致。因此云主机申请成功后需要进行一些初始化工作。
在这里基础模式是云主机启动脚本,上面通过http访问CenterServer获取该机器的一些信息以完成初始化工作。
在这里有一个比较奇特的做法是虚拟机通过获取自己的IP作为标示符,访问CenterServer来进行信息获取和交互。
总结:
1.在StickEngine弹性计算的实践中,无状态和不可靠健壮性处理和理念,贯穿很多细节。以此来避免一些常见的故障转移,恢复,一致性事务等问题。
2.本文主要阐述一种实践方式,笔者认为一些实施细节里面可以引入很多数学建模问题,比如
A.在伸缩时机引入机器学习来辅助决策
B.在可用资源池中如何达到最优性价比问题。
C.更好的一维化多维度负载衡量问题和降噪。
3.还可以有一些其他拓展做法来优化一些问题,比如引入docker来加快扩容速度。
4.在本文的案例中,使用弹性计算模式比原包月计费模式服务器机器成本降低了51%