游戏业务的不停服更新
一. 概述
前面说到,游戏业务属于内容向的互联网业务,有着运营灵活、内容更新频繁等特点。从玩家游戏体验考虑,游戏的运营方希望做到游戏内容的变更对于玩家来说无感知,也就是做到不停服的游戏更新。游戏更新从更新范围和内容上分为:
1. 运营版本更新(即常规运营活动的发布)
2. 紧急版本更新(即现网程序bug和配置错误的紧急修复)
3. 大版本更新(即月度或者季度大型游戏玩法发布)三类。
而为了在保证游戏服务不中断的前提下应对上述发布需求各个项目组也研发出各自的系统架构,这里介绍应用比较广的几种:
1. 基于共享内存和信号处理的游戏进程结构
2. 基于C++/Lua混合编程的进程结构
3. 基于流量治理的发布机制
下面将结合游戏发布场景逐个进行介绍。
二. 基于共享内存和信号处理的游戏进程结构
游戏业务的更新发布总体上可以分为配置更新和应用程序更新:
1. 对于配置更新
是指在游戏进程的底层框架在初始化的时候就进行相关的信号注册,当需要更新的配置分发到进程的配置目录的时候给对应进程发信号,触发进程重载配置的逻辑,进程读取配置之后则新的配置立刻生效。整个过程就相当于重新调用了一个读取配置的接口,整个流程对玩家的游戏体验是无感知的。
2. 对于程序更新
本进程结构的做法是:首先将业务层和接入层进行分离,由接入层负责链接的维护,而由业务层进行逻辑的处理,同时应对产品复杂的需求;其次业务层进程将进程数据放在共享内存之中,这样当有程序需要更新的时候,直接重启业务进程,因为数据都是缓存在共享内存中,所以游戏进程重新挂载即可,原先的状态并没有丢失,另一方面链接由接入网关层统一管理,客户端并不会因进程重启而掉线,因而整个过程对于用户无感知。
这样的游戏进程结构可以应对线上大部分的配置更新和逻辑更新,但是由于游戏进程的数据托管在共享内存中,因此本框架并不能适配于共享内存结构发生变化的发布场景。
三. 基于C++/Lua混合编程的进程结构
这种结构是利用了脚本语言作为解释性编程语言在程序运行时才进行翻译的特性。底层代码比较稳定不常变动or调用频繁性能要求高的部分由C++代码实现,而其他调用频度不高的业务逻辑交给脚本语言(比如Lua)来实现。在这样的进程结构下,实现逻辑变更可以做到配置变更一样,将对应的Lua脚本通过配置中心将代码像分发配置一样,分发到对应的代码目录下,并给i进程发送信号,触发脚本重载的操作。
这种进程结构下,逻辑变更相对前者更加轻量,但是也存在一些问题:
① C++部分代码变更无法做到支持
② Lua的执行性能使得不能应用于对性能要求较高的业务场景
四. 基于流量治理的版本更新机制
这种更新机制的发展受启发于云原生思想并得益于底层存储组件、通信组件等技术及设计理念的发展。随着存储技术的提升,很多游戏业务的后台进程可以选择利用数据库缓存游戏进程状态,从而使得业务进程可以做到弱状态,甚至完全无状态;而另一方面,随着消息组件的发展,游戏后台系统的进程之间不需要直接对接,而是只需要和底层消息队列建立链接,通过消息队列进行消息收发,使得进程之间由原先的网状和星状结构变成以消息队列为主干的树状结构。
在此背景下,业务进程的功能被拆分的更细,更加聚焦,如有专门的聊天服务、邮件服务、商城服务等;而对于后台的单一服务,由多个对等进程构建的进程组提供。消息队列通过特定的路由规则,将新的请求分发到进程组中某一个节点上进行处理,此时若是需要某个业务进行更新,则通过将进程组分为A/B组,对其中一组执行下线操作(但是不停进程),则针对后端的无状态路由请求会被派发到未被下线的进程组那里;而由于进程未停止,原先未走完的事务可以通过有状态路由(即指定收包端节点的方式)保证中间态的事务可以顺利完成,当下线后的残留事务完成之后,我们可对这些进程进行停机更新的操作,更新完成顺利上线后另一组同理。
基于流量治理的版本更新方式,应用面比较广,不仅可应用于配置更新、逻辑更新,还可以应用于数据结构更新的发布场景,甚至可以做到大版本更新也对用户无感知。但是相比前面两种发布机制,流量管理的发布方式操作更重,运维也更加复杂。
后记
本文介绍了不停服更新的几种方式,以及各自优缺点和适用的场景,当然还有一些其他的不停服更新的机制,比如生成动态链接库等,这里只是挑了几种典型的发布机制进行介绍,不同的项目可以针对自己项目的具体情况、团队的技术栈等,进行对应的架构选型。