原文: https://community.uwa4d.com/blog/detail?id=1638476203938906113
写在前面
本文我想写一下,一款游戏上线前需要做哪些事情,以保证项目上线后的稳定性。若你们公司之前没有大DAU游戏上线和运维经验,现在准备上线一款新游戏,可以按照这个RoadMap去做或者检查一遍自己游戏的上线准备工作。
本文不会写具体的操作,只是提供一个RoadMap,具体如何做大家可以自行查找相关文档。
所有的工作,都会存在一个工作量和收益的权衡。一般来说,对于大部分游戏,不需要做到100w以上的同时在线,也不需要做到100%的可用性。具体工作做到哪一步,项目根据自己的情况权衡即可,我在文中会介绍需要重点关注的地方。
这些工作,至少需要提前6个月以上的时间准备,至少需要一个资深服务端和一个靠谱的执行服务端。
1. 游戏服务器架构
1.1 选择适合自己项目和团队的服务器架构
游戏行业的服务器架构真的是百花齐放,什么方案都有。传统的MMO(逆水寒)和近年来常见的开房间游戏(王者荣耀)的关注重点和技术方案也不一样了。
设计游戏服务器架构时,所有的技术方案都不是一个选择题,没有说哪种一定是好的,哪种一定是不好的(我很讨厌脱离业务背景讨论技术方案优劣的事情。)
若团队专业,人力充足,可以不考虑架构的复杂性,微服务、K8S等技术用起来,可以做到互联网级别的水平扩展和高并发高可用。这个思路会导致写代码比较复杂,带来很大的工作量和人力需求,做的好上限很高,做的差下限很低。
但是若团队较小,经验也不多,建议不要选特别复杂的架构,尽量让架构优雅、简单。尽量不要用各种互联网的各种新技术,也没必要追求百万级的同时在线和高级别的可用性。
这里推荐一下 Skynet,很稳。下限够高,上限也够高。
1.2 支持水平扩展的架构
从服务器架构上来看,对于主要逻辑,一定要支持水平扩展。游戏上线后大量玩家涌入,第一个考验就是,服务器能否承载这么多玩家。
比如一款MOBA或者吃鸡这种游戏,它的主要逻辑是大厅和战斗,那么对于大厅和战斗就一定要支持水平扩展。而对于登录、匹配这种非主要逻辑,是否支持水平扩展是可选项(虽然也不难)。
但是,不支持水平扩展的逻辑单点,往往就是游戏承载上限的瓶颈。找到成为承载瓶颈的单点,并且把它们改造为支持水平扩展,是提升游戏服务器承载量的主要方法。大家可以根据自己项目的人力、游戏预期玩家量、游戏服务器部署方式等方面进行权衡。
水平扩展包括进程级别的,也包括线程级别的。对于一个多线程的服务器进程,也尽量不要出现某个线程成为瓶颈。这可能会导致这个线程跑满了一个CPU,然后阻塞住了其它线程。压测的时候可以看一下,是否存在某些机器某一个CPU跑满了,其它CPU还空着。
2. 容灾
服务器环境是复杂的,难免机器、进程、网络等出现异常的情况。虽然这种几率较低,但是对于大集群来说,机器很多,环境复杂,概率就不低了。
2.1 服务器机器宕机
首先考虑游戏服务器机器宕机的情况,在某些情况下游戏进程被杀死也类似于这种情况。
对于这种情况,我们要尽量保证大部分进程被杀死,不会影响到集群整体的可用性。比如,游戏中的大厅服和战斗服进程被杀死,若只会影响到此进程的玩家和战斗,并且玩家和战斗能自动地转移到可用的进程上继续游戏,那么,问题的影响范围就会小很多。
而对于有些关键的进程,比如管理进程,这种进程数量少,且写容灾逻辑的难度更大一些,可以不做容灾。
2.2 业务容灾
为了做到某些业务节点出现问题时,不会造成整个集群的不可用,需要在业务层面做一些容灾处理。
1)重要模块和高风险模块的解耦
比如,某些单点服务(高风险模块)若出现异常,最好只是这些服务不可用,不要影响到登录(重要模块)。
2)非重要模块可以做到关闭/重启
有些边缘服务,比如录像服,即使出现问题或关闭,也要保证对业务主流程不造成阻塞影响。
3)客户端、服务端综合考虑
业务的容灾需要从玩家体验的角度关注,而不只是服务端需要关注的事情。在很多游戏项目,客户端服务端是两个人分开写的,而考虑业务容灾时需要综合考虑(一般由服务端负责)。
在做业务容灾处理的时候,一定要和客户端一起做,比如,要保证客户端在登录时,不会调用一些没有必要的接口。
4)排队系统
建议所有游戏都做一个复杂或者简单的排队系统,当服务端出现问题时,或者当玩家在线量超过预期时,可以让玩家去排队,而不是登录报错或卡住无反应。
对于可横向扩展的游戏,排队系统不用做的太复杂,因为比较容易提高承载量,只有在出现问题的时候启用即可;对于传统MMO这种分区游戏,每个区的承载量是有上限的,这种需要把排队好好做一下。排队系统也不用搞得太复杂,显示信息没必要特别准确实时,尽量不要出Bug,参考明日之后百万阴兵排队。
2.3 第三方服务异常
游戏服务器一般都会使用一些云厂商提供的SaaS服务,比如最常见的数据库。
一般来说,这些服务是有容灾机制的,所以不会出现完全不可用。但是,DB的异常往往会导致网络闪断,那么数据库网络连接的断线重连,以及在这种情况下如何尽量保证信息部丢失且保序,就是项目组要做的事情了。
除了网络闪断,也可能有其他异常情况,比如我们曾遇到数据库分钟级别的不可用。大家可以根据自己游戏使用的服务进行评估。
2.4 容灾测试
容灾做好了,游戏上线前要测试一遍,看看在异常情况下, 集群的表现是否符合预期。
建议对每类服务、每类进程都测试一下不可用时游戏整个集群的表现,以及对玩家的游戏体验的影响。玩家游戏体验要从客户端的角度评估,才能确认真实环境下的游戏体验影响。
此外,在测试的过程中,对于每类故障,也要给出故障恢复手段的建议和演练。比如,是需要重启整个游戏服务器;还是只需要重启该进程;还是不需要做任何处理。
3. 日志
服务器日志一定要写得尽量详细,包括日志级别、提示信息、运行状态等。
不要因为觉得日志写多了,会浪费日志服务的机器成本。只要别输出完全没有意义和价值的Debug日志,以及那种循环里面的海量日志,日志多多益善,不然到了线上,会出现“日志用时方恨少”的情况。
若日志实在太多,可以在游戏压测时观察一下,一般是因为有太多的无效日志导致的。
建议使用云厂商提供的日志服务,比如,阿里云日志服务SLS。AWS没有类似的服务,我们用的OpenSearch。
4. 压测
压测不是万能的,没有压测是万万不能的。上线前压测的目的:
验证架构的承载能力。我个人建议在架构层面要能支持100w的PCU。架构承载和业务承载不是一个东西,某个业务不一定能支撑这么高的量级,但是当玩家分散到各个玩法后,你的架构要能支撑这个量级。
评估各业务模块的性能表现,关注CPU、内存、网络带宽,对于超出预期的,针对性优化。
根据性能数据和硬件指标,为开服资源评估提供数据支撑。
检查玩家在服务器高并发情况下的表现。
4.1 压测框架和压测脚本
游戏服务器不像WEB都是用HTTP,所以一般来说,需要自己写一套自己游戏服务器对应的压测框架或平台,基于这套框架,可以写压测脚本。
开发完了框架,就可以基于框架写脚本了。写脚本是一个工作量比较大的工作,最好上线测试前三个月到半年的时间开始写。写压测脚本就是模拟玩家操作,包括登录、购买物品、升级、匹配、加好友、加帮派、进入战斗、战斗内的各种操作等等。
一般来说,大厅里的压测脚本比较容易写,但战斗里面会麻烦一些。不过一般来说,现在常见的开房间类游戏的玩家上限是有限的,一般不用压力测试,反而是需要性能测试。传统的MMO复杂很多。
写脚本的时候,尽量模拟玩家的真实操作。比如,测试背包,可以隔三秒获得一个物品,然后隔三秒使用一个物品。不要写成一秒钟执行100次获取物品和使用物品的逻辑。
4.2 压测流程
压测脚本写完了,就可以进行压测了。
对于压测承载目标,建议按照策划/发行评估的同时在线乘以3~5倍的量级进行压测,或者直接按照100w同时在线去压测。一方面因为压测脚本往往和玩家线上操作有一定的差别,另一方面游戏也可能超出预期嘛。
压测过程建议四轮:
1.小规模压测:把压测脚本和流程跑通,确定框架和压测脚本的正确性。
2.分模块中规模压测:针对某个系统/功能模块进行压测,可以定量地分析某个模块的承载量。这种方式可以评估某些单点进程/服务的最大承载量,防止这些单点成为瓶颈。比如登录,可以分析每秒钟最多能支持多少玩家登录,若QPS太低,可以去找性能瓶颈针对性的优化。
3.大规模压测:这时候,按照承载目标(100W同时在线)同时上线这么多玩家,然后随机或者按照规律把所有的脚本都跑起来,模拟玩家的各种操作。此外,在大规模压测期间,也可以用这个集群作为内部体验测试集群,检查玩家在服务器高并发情况下的表现。注意,对于小公司,这个压测时间建议控制在2~3天,,服务器费用的花费就像烧钱一样。
4.线上集群压测:上线之前,针对准备用于线上部署的集群再进行一次整体压测,按照预期的承载量去压测,同时确定线上集群部署的正确性。游戏开服之前,把数据清档。
4.3 定位瓶颈
1)架构瓶颈
压测期间,若没有达到要求,则要去寻找性能瓶颈。比如登录的时候,发现登录速度上不去了,就去找问题出在哪里。是数据库的问题,还是某个逻辑节点已经跑满了一个CPU核心。
2)代码性能瓶颈
压测的时候,也可以通过Profile工具,去找到逻辑中的性能热点,针对性的对代码进行性能优化。
4.4 周边服务压测
除了游戏业务本身需要压测,游戏业务使用的周边业务,也要进行压测。比如账号平台、支付平台、或者自己部署的日志服务等。
若使用了第三方SaaS服务,有些可以自己压测一下,比如DB;有些可以跟第三方明确自己游戏可能达到的量级,让他们做好准备。
5. 热更
要尽量保证绝大部分代码可以支持热更或者滚动更新。
游戏上线后,不可能没有Bug,也不可能通过重启服务器来修复Bug。
要做好热更流程的演练和测试,对于各类模块和服务,都要测试一下热更是正确的。
6. 数据库相关
6.1 数据库选型
游戏服务器常用的数据库包括:Mysql、Mongo、Redis。选择合适的并且自己团队熟悉的数据库。
建议使用云厂商的SaaS服务,都比较成熟了。而且不用招DBA了,现在好的DBA都在云厂商那里。
云厂商一般都会提供各种监控工具,可以找到数据库的性能问题。压测时要重点关注一下数据库的性能问题,根据经验,很多时候业务的瓶颈都是因为对数据库的使用不当造成的。
6.2 数据库的分片和扩容
不要使用单点数据库,容易成为系统瓶颈且不容易扩展。Mongo可以使用Sharding模式,Redis可以使用集群模式。
有的数据库扩容比较容易,有的比较麻烦。要评估一下项目使用的数据库扩容的成本,若游戏成功,数据肯定越来越多,若扩容成本很高的话就比较麻烦了。
对于大规模集群,还要注意数据库连接数的使用情况。
6.3 数据库备份和恢复
要对线上环境使用的数据库设置备份策略,以作为最终的托底。常见的数据库都有对应的备份方案和策略,运维同学去配置一下即可。
建议游戏上线前,把游戏数据库备份和恢复的流程跑一遍,以保证这个流程是没有问题的。听说过业内有的游戏线上出了大问题,因无法回档游戏直接关服下架,这就离了大谱。
7. 服务器部署
游戏上线前,就要把服务器集群部署到真实的线上环境上。这时候,就要考虑哪些进程、哪些服务以何种形式分配在线上的机器上。
7.1 业务隔离
一个有游戏的分布式集群往往存在多种类型的进程,每种类型的进程又可能有一个或者N个,我们假设每个进程都是多线程的,可以跑满多个CPU核心。
部署时的业务隔离,只要是防止不同业务进程占用了较多资源,影响了其他进程的资源使用率。如果使用K8S部署,可以比较容易地做业务隔离,每个进程使用一个Pod即可,Pod的分配交由K8S管理。若使用虚拟机部署,则需要考虑哪些进程部署到同一台机器。
若集群很小,每个游戏集群承载的玩家量也不大(这种情况常见于多逻辑服游戏,每个逻辑服一个服务器集群),一个集群部署到一台高配机器上即可。这种方案比较简单。
若集群很大,建议尽量一台机器只部署一个进程,这样尽量避免进程间的影响。若有些进程资源占用较少,也可以多个进程放到一台机器上。但是尽量保证单点进程或核心逻辑进程与其他进程之间有隔离,不要让他们被非核心逻辑影响到。
7.2 上线前的云资源准备
游戏上线前,团队会对玩家量有一个大概的预估。基于玩家量预估以及压测时对游戏服务器的性能评估,我们大概能估计出我们需要多少物理资源。
在游戏上线前,一定要按照预估玩家量2~3倍的情况去多申请/购买资源,游戏玩家数量基本稳定后,再将冗余的机器资源下线。
7.3 环境和配置检查
上线前一周,要做一次线上环境部署和配置相关的检查,这种一定要做好Double Check。
重点关注网络拓扑、数据库索引、配置信息、安全等。
8. 客户端相关
不要只关注服务端,一定要知道客户端是以什么样的方式和频率调用服务端接口的,即客户端调用服务端接口的必要性、频率和流量。
这个在平时开发模块的时候,就应该去通过协作机制或文档保证客户端调用服务端的接口的合理性。
上线之前,建议测试和统计主要游戏主流程期间(尤其是登录)客户端对服务端接口的访问,并且针对访问的统计结果,进行分析。
对于没有必要的接口调用,可以取消。比如,我们在登录时,尽量保证玩家不会访问单点服务,以避免单点服务出现异常时对游戏登录的影响。
此外,对于接口的访问频率和网络流量,也从服务端的角度去评估是否有优化的必要。
9. 运维工具和平台
游戏上线前,要把运维操作基于相关工具和平台跑通,常见的运维操作包括:
热更/Hotfix
停服或者不停服维护
修改数据库或者配置信息等
对于较小的团队,建议由公司的服务端开发工程师负责线上的运维工作,不太需要专职运维人员。这样,可以减少沟通成本,降低事故率。但是,也要把运维工具和线上环境管理好,不要弄混了开发环境和线上环境。
当游戏服务器越来越庞大,线上环境越来越复杂,业务场景越来越多,就需要组建专职运维团队了。
我们使用的是Ansible管理的游戏服务器集群,使用K8S管理平台(账号、支付等)服务器集群。
小团队没必要搭建运维平台,把运维工具和脚本做好即可,写好执行运维操作的CheckList,执行操作时按照List执行。
10. 监控和报警
部署游戏服务器集群后,要对机器和业务增加对应的监控和报警。
通过监控,我们要对线上服务器的情况尽量了解,并且能提前发现问题,防止问题扩大化后才知道。报警和监控相辅相成,监控到异常后,马上报警,我们就能立刻对线上问题进行处理。
无论是机器的监控还是业务的监控,监控项往往有很多,一定要关注必要的、有价值的监控,防止无效信息和无效报警淹没了有效信息。
11. 安全
网络安全是随时都需要关注的点,建议游戏上线前关注以下几个点:
网络通信的加密:对网络通信协议尤其是登录流程期间,一定要做好网络通信的加密。
网络访问控制:确保自己的线上机器是外网不能访问的,给客户端只暴露需要的IP和端口。运维同学建议通过跳板机/堡垒机/JumpServer访问线上机器。
DDoS:国外的DDoS攻击比较严重,需提前做好预案和准备。国内的DDoS不太严重,只有某些特殊类型的游戏需要重点关注。
权限控制:做好基本的权限控制,比如开发环境/线上环境,游戏数据库的读写权限等。
客户端安全(防外挂)是另一个事情了,参考我的另一篇文章:《游戏开发中防外挂的那些事儿》。
对于大部分游戏类型,不建议中小型团队过早过多考虑防外挂,做一些内存和资源混淆差不多就够了,建议接入网易易盾或者腾讯ACE。其他性价比太低,而且若游戏火了再去关注也来得及(游戏不火也就不用关注了)。
12. 线上事故的预案和演练
线上游戏出问题甚至事故是个很正常的事情,越火的游戏越容易出问题。线上出了问题以后,我们能越快地把问题处理好,对玩家的影响就越小。
预案和演练的目的是,让我们对线上的每个操作,都是可预期的。我们需要知道遇到了各种情况需要使用什么操作,以及这个操作对应的后果都是明确的。
常见的预案和演练包括:
回档:无论事故多大,最差能通过回档解决问题。这个保底,最重要的是两点:
数据库有定时备份,比如Mysql和Mongo是能支持按时间点回档的,Redis也支持针对某个时间点定时备份。
因为即使基于一个时间点,也可能会有数据不一致的问题。所以不同的数据库之间要想办法确保一致性,或者互相之间没有强耦合。
基于日志或者行为数据根据玩家某些行为进行扫档,按需获取玩家列表。
日志要全。
把日志系统(比如OpenSearch)用明白。
机器、进程异常演练,明确每台机器或者每个进程出问题时,使用什么方案处理。
业务异常:比如可以快速关闭某些系统。
13. 动态扩(缩)容
若我们的游戏服务器架构是能支持水平扩展的,那么做动态扩容就不会太复杂。
可以支持以下几种情况的动态扩容:
手动:当我们发现机器不够了,可以手动增加一些机器。
定时:在游戏活动/游戏大推期间,或者其他可以预见性的游戏玩家量较大的时间,可以自动地增加一些机器。(对于大部分游戏做到这一步就够了。)
自适应:根据玩家的在线量或者机器负载,动态地增加或者减少机器。
对于准备上线的游戏,动态扩容的游戏机较高,缩容优先级低。缩容要考虑优雅退出的机制,所以开发量一般比扩容更大一些。
14. 做好运营工具和平台
业内游戏上线后,运营同学发错奖励的例子有很多,大家要关注一下。比如:把人民币货币当成游戏货币发了几万个;把发给某些玩家的礼包发给了所有玩家。(这些案例在行业内都出现过。)
这种情况不能只怪运营同学,因为人都可能犯错。我们要把工具、平台以及工作流做的更加好用一些,减少犯错的概率和影响范围,常见的手段比如:
在运营工具和平台中增加二次确认框,并且提示更加可读和友好。
增加审批流,每次发放礼包或者重要操作时,需要两个人Check。
增加一些保底机制,比如,不允许发1000以上的人民币货币。
千万不要让运营同学有能力直接操作线上,比如执行后台命令,建议至少做一个简单的运营工具或者平台,并且通过工作流程来控制好。
这些工作其实不是技术问题,但是建议在游戏上线前,和各团队一起把这些策略定好,防止出现运营事故。
每个游戏、团队的情况都不一样,关注点也有一些区别。若有什么具体的问题,欢迎友善交流。