五邑隐侠,本名关健昌,10年游戏生涯,现隐居海边。
本教程以Go语言分区游戏服务端框架搭建为例。
Go语言是Google开发的一种静态强类型、编译型、并发型、具有垃圾回收功能的编程语言。语法上近似C语言,支持接口、可通过struct包含另一个struct方式实现继承等面向对象的概念。性能上媲美C/C++,相比C/C++更健壮,更易开发并发程序。我以前也写C++服务端,接触Go后,更倾向用Go做游戏服务端开发。
分区游戏,指游戏将分为很多个区,不同区之间玩家不能互动或只有少量互动。玩家需要选择分区进入指定分区开始游戏,一个玩家可以同时在不同分区有角色。目前市面上大多中重度网络游戏都采用这种模式,分区游戏适合不需要大DAU互动的游戏,如卡牌、MMORPG、SLG等。从技术层面,分区属于集群扩容的一种手段;运营上,有利于分区精细运营,滚服运营已经是比较成熟的游戏运营手段。分区的基础上,可以按相邻分区编号设置组服务,例如10个分区一组设置组服务,用于跨分区的玩法。相对分区游戏,也存在不分区的社交游戏,这类游戏核心玩法是匹配对抗竞技,例如COC。简单的匹配竞技游戏可以把分区游戏的分区改为节点服务组,把组服务拆分成组匹配服务和多个竞技服务,匹配服务只做简单的报名和匹配,匹配成功后把玩家分配到指定的竞技服务,并在里面分配房间开始游戏。
搭建分区游戏服务端框架,首先建立一个服务注册发现机制。我们以中心服作为机制的核心,提供服务注册,以及服务信息广播。作为游戏主体,分区是注册的对象。分区启动成功后,连接中心服,并注册分区。这样中心服知道所有分区的基本信息和状态。由于所有分区都连接中心服,中心服还有转发、广播的功能。分区注册后,有哪些服务需要发现分区、关注分区的状态变化?对于玩家,登录后要知道有哪些分区以及状态情况。所以登录服需要发现分区。玩家通过登录返回知道分区情况,选择分区进入游戏。而玩家是通过网关连接分区的,所以网关需要发现分区。分区注册后,需要切换状态来支持开服测试、开服、维护。一般会搭建一个管理服,运营人员可以通过管理服发指令给分区更改状态。所以管理服也需要发现分区。这样得到第一个框架模型如下:
按照这个模型,先启动中心服等待其他服务注册。然后分别启动管理服、登录服、网关,这些服务注册到中心服。新开一个分区就启动该分区服务,分区状态为未开服。分区启动成功后注册到中心服,中心服通知管理服该分区启动成功,状态为未开服。管理服通过中心服转发指令给该分区,更改状态为开服测试。该分区把状态更改发给中心服通知状态变更。中心服通知管理服、登录服、网关该分区状态变化。网关连接该分区的服务。这时候测试账号可以登录进入该分区开始测试。测试通过后同理管理服可以发指令更改该分区状态为正常正式开服。当分区需要维护时,管理服发指令给该分区更改状态为维护,网关收到中心服广播后,断开与该分区服务的连接,5分钟后该分区的服务可以关闭进入维护。这样一个整体流程算是跑通了。
接下来看看分区的服务怎样划分。先看一个简单的情况。分区有一个主服务,负责注册到中心服、处理玩家的基本请求,包括分区登录、玩家基本信息操作、养成等业务。这些业务都需要频繁修改玩家数据,应避免直接操作数据库,因此使用redis做缓存,主服务直接操作redis上的数据。redis数据要有一个落地的机制避免redis崩溃或数据过期后数据丢失。这里选用mysql做固化数据库,玩家的游戏数据会从redis固化到mysql。对于redis上数据已过期的玩家再次登录,还需要把数据从mysql加载到redis。所以这里增加一个数据服,负责数据的预热和落地。一个基本的流程,玩家进入分区,主服务处理请求,从redis获取玩家数据,redis上不存在该玩家数据,主服务通过redis发布消息给数据服预热该玩家数据,数据服查询mysql,如果mysql没有该玩家数据,数据服往mysql新加一条玩家数据注册个新玩家,数据服把玩家mysql的数据添加到redis,数据服发布消息通知主服务该玩家数据已准备好,主服务继续从redis获取该玩家数据,返回给客户端。这样分区的基本流程也能跑通。
除了基本流程,分区可能需要广播消息,例如世界聊天、系统公告。在分区搭建一个广播服,在redis里维护一个广播队列,主服务往广播队列里添加广播消息,广播服从广播队列里拿消息进行广播。对于MMORPG,还需要有场景服管理各个游戏地图,回合制的卡牌、MMORPG游戏需要有单独战斗服进行战斗的预演。如果有跨服玩法,还需要有组服务,相邻几个分区共用一个组服务。这样得到的分区服务模型如下:
这样一个基本的游戏服务端框架轮廓就出来了。接下来需要封装一些公共基础功能,以便快速搭建各个服务