游戏服务端架构演进方向
网络游戏目前主要分为四类:个人计算机客户端网络游戏(PC端游)、个人计算机网页游戏(PC页游)、手机客户端网络游戏(手机端游)、手机HTML5网络游戏(手机页游)。现在的游戏服务端框架大体都是从最早的PC端游时代演化而来,从单机系统向分布式系统演化,从单体架构向微服务架构演化,详细资料可查阅本文所列参考资料,这里不再赘述,比较有代表性的开源游戏服务端框架有:SkyNet、Pomelo、ComblockEngine(KBEngine)。
DevOps逆向架构设计
我相信我们团队和其他大多数团队在研发运维上的设计差异较大,大部分团队都是先从研发角度考虑软件系统的架构设计,其次再考虑怎样自动化运维,最后再考虑如何向DevOps方向转化,而我们团队是逆向设计的,我们先思考如何做DevOps,再思考怎样的架构设计能更有利于自动化运维,最后才是考虑系统架构设计,其实就是以终为始。能这样做主要是基于以下几点:我们团队没有端游、页游的历史包袱,直接进入手游时代;我们团队主要研发策略养成类游戏,系统架构需求和端游、页游时代的MMORPG、MOBA差异较大,我们针对性重新设计并积极思考如何利用云计算时代的微服务、容器技术更好地解决研发运维问题。2016年我们先从运维开始调研了一些自动化运维工具如SaltStack、Ansible、腾讯蓝鲸平台,那时容器技术已经在一线大型互联网公司普及使用,我们也尝试调研了容器技术。
当时调研后觉得容器技术是与云计算环境结合紧密的技术(那时还不知道云原生)而且比较彻底的解决了环境依赖问题(基础设施即代码),判断这一定是未来运维的趋势,所以果断确定了今后的运维方向一定是基于容器技术,有了容器技术才能更好地支持持续集成和持续交付。我们当时已经做了一定程度的服务拆分,便开始在一些辅助服务的生产环境尝试使用容器热更新发布版本,实验结果证明确实很好用。使用容器也带来一些问题:服务是无状态或将状态转移到共享存储比较适合容器,如果容器保留状态对于分布式系统来说会比较复杂;要满足分布式系统服务的滚动更新、蓝绿发布、灰度发布等热更新需求,就需要有完善的容器监控管理工具,这就是容器编排;如果只是使用容器部署单体应用相对简单,如果要微服务化则还要配套服务监控、服务注册与发现、服务治理、配置中心、日志聚合等一系列工具。
不管怎样要部署大规模分布式系统仅使用单个容器是不解决问题的,一定是要使用容器编排的。2016年容器技术正是群雄逐鹿的年代,各种容器技术团队混战,这对于大多数研发团队技术选型来说难以抉择。我们当时对运维的思考是底层基础运维全部依赖于云商,我们只专注于做自研服务的运维,基于这种思路很自然容器编排的底层运维也依赖于云商,这样可以随时切换基础设施而不过多地投入调研,云商有什么我们就用什么,当时阿里云只有Docker Swarm产品,于是我们就选了Docker Swarm。天下大势合久必分、分久必合,2017年容器编排群雄混战的局面终于结束,Kubernetes一统天下,阿里云也很及时地同时上线了Kubernetes容器服务产品,我们果断开始将容器编排方案向Kubernetes迁移。从Docker Swarm到Kubernetes我们团队都是使用阿里云容器服务的第一批客户,也是首个游戏行业用户,阿里云还专门为我们建立了容器技术服务群,协助解决各种产品问题和线上问题,我估计我们公司也是国内最早一批在游戏项目生产环境大规模使用容器编排的公司,我们以实践证明了完全使用容器编排运维游戏项目的可行性而且确实极大地提升了运维效率。
到这里DevOps流程中运维环节基本定型,我们还要打通从开发到运维的自动化环节,也就是持续集成和持续交付。有了容器技术支撑,剩下的问题就比较好解决,我们使用GitLab搭建持续集成流水线(现在已经支持Kubernetes),但还有些自动化工作需要细化。这里顺便提下我们对运维的愿景和理念:我们希望运维成员是SRE工程师而不是像传统运维干体力活,提供运维解决方案而不是按照固定流程或模式去操作,后端服务谁开发谁运维(参考AWS),最终目标是人管理代码、代码管理机器、人不管机器(陈皓),总之方向是智能化和自动化。这对运维人员的要求比较高,传统的运维人员研发能力较弱,但是SRE工程师要求同时具有开发、运维、架构设计能力,对于中小型公司从市场上招聘当然是很难的,所以我们完全是自己培养也用这样的目标激励团队成长。如果把运维的目标定地过低,那运维最终就是纯体力活,这样招人是容易,但想做DevOps是不可能的。
在我看来开发不懂运维或是运维不懂开发想做好DevOps都是件困难的事,因为无法以终为始,有了DevOps基础才能考虑微服务架构。
微服务化
首先说明一下我们为什么要微服务化,微服务是一把双刃剑,解决一些问题的同时产生了另一些问题,所以在架构设计上要权衡利弊。微服务的技术栈和工具链比较复杂,需要投入较多的人力和时间成本,对于创业初期的小团队我认为是不太适合使用的,但可以做一定程度的服务拆分。系统架构往往是跟着业务模式演进的,是一个动态的过程,所以时机很重要,过早使用复杂的技术方案会影响研发进度,过晚重构或改进技术方案也会影响研发进度,这就是人们常说的短期看高估技术的作用长期看低估技术的作用。当一个公司随着业务发展到一定规模后,研发要下沉并演化出技术中台,阿里集团就是沿着这种路径发展的,从最早的原始系统发展到研发数据库、操作系统、人工智能、服务器、芯片。当一个小微公司向中型公司快速发展的时候,为了更好地使技术服务于业务发展,就需要一个技术中台支撑,这时可以考虑是否微服务化。再看我们的业务场景,策略养成类手游的特点是弱联网、高并发、水平扩展、高可用、弹性伸缩、持续交付,符合微服务解决的问题。微服务还有个很大的优点就是独立部署和维护,这样后续的技术迭代不会受到编程语言或其它环境的影响,服务之间从物理上解耦合有效降低了重构一个服务的成本,我们团队现在的方案是依赖数据库实现数据强一致性的,肯定不是最佳实践,还有优化空间。
接着需要解决微服务带来的问题:首当其冲的是响应时长会增加,这点对于策略养成类手游来说影响不大,但也需要做些性能优化;需要有服务降级、过载保护等容错机制,避免并发阻塞引起整个系统雪崩;分布式节点增加故障排查复杂度,需要有一套服务监控和调用链追踪体系,日志聚合是必不可少的,我们对比了Logstash、Fluentd、Flume等日志收集工具,发现阿里云Logtail性能和吞吐非常突出,而且可以和阿里云容器服务无缝对接,但只能使用阿里云的日志服务接收日志,不想自己搭建日志聚合环境的可以试试;大规模集群管理需要有服务注册发现、配置中心配合容器编排等工具做服务治理;最难处理的一个问题是分布式系统的数据一致性问题,游戏不像其它互联网系统多数情况下最终一致性或弱一致性就能满足需求,游戏很多场景都需要数据强一致性,这也是为什么游戏项目难以微服务化的原因。
微服务配合容器技术可以实现高可用、水平扩展、热更新,这些特性与手游这种快速开新区服(滚服)的情况非常匹配。传统游戏服务端框架一般用脚本语言实现热更新,而微服务架构则是使用蓝绿发布、灰度发布、滚动更新等模式使用容器镜像实现。
数据一致性
数据一致性问题一直是分布式系统的一个经典难题,CAP和BASE理论是分布式系统数据一致性问题的理论基础。很不幸的是游戏业务场景大多数需要数据强一致性,或者说数据强一致性能获得良好的用户体验。
游戏战斗模式分为两大类:PVE模式和PVP模式。其中,PVE模式又分为个人战和团战,PVP模式又分为本服个人战、跨服个人战、本服团战、跨服战团战。不同游戏类型不同模式在数据处理上的要求差异较大:PVE团战可以转化为一个线性一致性队列问题,根据对性能的不同要求处理细节也会有些区别,若业务场景要求强一致性需要做一些性能优化,若最终一致性能满足要求异步返回结果就可以;PVP个人战或本服团战一般用锁来控制数据一致性,使用锁的前提是不能在同一时刻产生访问临界资源的高并发请求,否则会导致系统过载或响应延时;PVP跨服团战传统方案中解决数据一致性需要把不同区服的数据拉到同一个内存中,这样就把分布式数据一致性问题转化成单机数据一致性问题。
传统单体架构的游戏服务端用锁和内存基本能解决数据一致性问题,一般这种模式请求不直接访问数据库而是访问内存,再以批量同步的方式将数据持久化。对于微服务来说解决数据一致性问题复杂度较高,通常微服务需要无状态或将状态转移到共享存储(一般就是数据库或缓存),每个服务都使用自己的数据库持久化,而且容器的生命周期是动态的,也就是说内存并不是固定的,这种模式天然就难以在内存中实现数据强一致性。一种方案是使用分布式事务机制依赖数据库实现,这种方案比较复杂而且对性能有一定影响。我们目前是使用数据库事务和锁结合解决,这时如果要处理跨服战可能需要将不同区服数据同步到一个数据库处理,否则就要实现分布式事务。还有一种思路就是基于容器特性实现一套游戏分布式内存数据管理中间件,这种模式就需要在容器中保存状态,并且需要处理容器调度时的数据迁移问题,这又是一个复杂的问题。
综上所述,对于微服务的游戏数据一致性解决方案还在探索和优化中,这也是引入微服务和容器技术的代价。对于我们团队综合评估来说,基于微服务和容器技术的游戏技术中台对游戏后端研发生态的演进提供了一个基础支撑平台,在此平台上不断衍生出新的服务和技术支持并逐渐实现研运自动化,这是我们想看到的。