网游服务端架构

最近二十年以来,IT行业发展之迅猛。计算机也从最开始的“毒害青少年的罪魁祸首”变成了各行各业都离不开的工具。在这个过程中,网络游戏也变得容易让人接受了,再也不像二十年前一样,如过街老鼠,人人喊打。现在的游戏已经是几乎下到幼儿园上到养老院的居家旅行必备良品。因此,从技术层面来说,现在的网络游戏也面领着用户量激增、业务逻辑越来越复杂、需求频繁变化等种种问题。

所以,对于一款网游来说,拥有一套高承载、稳定性强、易扩展的服务端系统,是成功的最重要的先决条件之一。注意,我说的之一,不然又要引起世纪大战。至于要把一款游戏做成功,什么最重要?我只能说重要的因素太多,技术、美术、策划、运营、管理者等,各自的重点关注点都不一样,没有谁更重要这一说,大家尽力把自己的工作做好,剩余的交给天意就好。有点扯远了,下面进入正题。

要满足高承载和易扩展,采用单服务器系统肯定是不现实的。靠谱的服务端系统,必须是分布式的。之后的描述,都是针对于长连接游戏。这里先给出基础架构图:

在这里插入图片描述

    上图中1-n表示多个服务器,比如AgentService(1-n)表示多个AgentService。图中只包含服务端系统的基础必备节点,其他的什么充值服务、GM服务等等视具体业务需要的节点,这里就不一一列出了。下面依次对图中节点进行介绍。

GateService

网关服务,用来承载客户端连接。一般情况下,网关服务不处理任何业务逻辑,它接受了客户端的数据后,直接将其转发给具体的业务服务器即可。当然,数据加解密,消息序列号检测,这些与安全相关的逻辑建议放在网关服务,以减轻业务服务的压力,和业务开发的复杂度。

采用网关服务有什么好处?我主要总结了以下几点。

  • 解决了单机资源有限,承载TCP连接的能力有限的问题。在运营过程中,我们可以根据实际的用户量来动态调整网关服务的数量。
  • 将承载客户端连接的功能从业务服务器分离,减少业务服务的压力。同时客户端若有切换业务服务的操作时,也不用断开连接。
  • 屏蔽了业务服务器与外网环境的接触,一定程度上增强了业务服务器的安全性。
  • 从运营上来说,可节约成本。比如现在服务器,带宽都是很贵的,10个10M比1个100M便宜的多。多网关分担压力的同时也减轻了成本。

AgentService

AgentService用于管理维护玩家当前所在的业务服务器,以及玩家所在逻辑服务器的切换。每一个玩家对应固定的AgentService。一般情况下,我们使用[HASH(用户唯一标识) % AgentService总数量]来确定用户所在的AgentService,这里用户唯一标识是具体情况,一般使用用户ID。无论是什么服务,都是通过AgentService定位用户。

比如网关服务,收到客户端数据之后,不用管玩家当前在在哪个业务服务,直接转发给该玩家对应的AgentService,再由AgentService转发给玩家当前所处逻辑服务。

当逻辑服务器要给用户推送消息,也直接转发给对应的AgentService,再由AgentService定位玩家所在的GateService,将消息推送过去。

当玩家A在逻辑服务1,玩家B在逻辑服务2。玩家A要给玩家B推送消息,此时逻辑服务1直接定位到玩家B所在的AgentService,将消息推送给该AgentService。

AddrService(HTTP)

地址管理服务,此为web服务,使用HTTP协议。客户端通过此服务来获取网关地址。在多网关的情况下会考虑负载均衡,这里我们可以把负载均衡放在AddrService,一般我们使用简单的轮询即可。或者实在不放心的话,可以加入每一个网关当前负载值的记录,每次分配负载最轻的,分配后增加该GateService的负载值,另外再定时从每一个GateService获取实时的负载然后刷新本地记录即可。这样不是很精确,但是这里不用太过于精确。太过精确反而消耗资源,也不见得会使结果变得更好。

一般在实际中,包括客户端资源更新、客户端版本更新判断、打开游戏公告、分区分服等等相关功能,都放在AddrService中。

AuthService(HTTP)

用户鉴权服务,用于鉴定用户身份,此为web服务,使用HTTP协议。现在的游戏的大部分都支持很多平台账号登录,比如QQ登录,微信登录等。而这些账号验证的功能一般与具体的游戏逻辑没有什么关系,所以我们可以将其独立处理,以WebService的方式实现即可。具体的不同渠道的用户验证的逻辑都在此WebService来做区分,然后以统一的方式来告知游戏服务器用户信息,以减轻游戏服务器的复杂度。

AuthService在验证账号通过以后,根据事先约定好的秘钥加上当前时间、账号信息等等来生成一个令牌(生成算法一般用MD5即可),将其返还给客户端。客户端只需将此令牌原样发送给业务服务,业务服务使用同样的秘钥验证,验证通过即为合法。

LoginService

专门处理用户登录、登录排队功能的服务,一个或多个,采用前面描述的定位AgentService的方式,来定位玩家对应的LoginService。当某个客户端与服务端建立连接后,AgenService默认将玩家初始所在服务置为LoginService。刚刚建立连接后,客户端会发送登录消息。此消息被转发给LoginService,然后LoginService对其令牌进行合法性验证,验证合法后,完成用户登录。最后通知AgentService,将用户切换至具体的游戏服务器。

GameService

具体的游戏服务,处理具体的游戏相关逻辑,一个或者多个。一般我们根据具体的游戏业务来拆分游戏服务。比如RPG游戏,我们可以将不同的地图,不同的场景拆分到不同的游戏服务。再如棋牌游戏,将不同的牌桌放在不同的游戏服务。另外,我们还可以分出机器人AI服务、聊天服务等等。

DBService

数据库服务,为业务服务和数据库之间的桥梁,一个或者多个。可以将数据缓存放在其中来提高查询效率。对于某些量级非常庞大的数据(如玩家角色数据),往往会采用多个节点来缓存。因为数据缓存功能相对较为独立,一般都是长时间不重启的,在扩展节点的时候,我们也希望之前缓存的数据依然有效。所以定位DBService我们不再建议采用取余的方式,取余的方式在我们扩展DBService节点的时候,会使之前绝大部分的数据定位发生改变,从而造成大部分缓存数据失效,增大数据库的访问压力。这里我建议采用一致性hash的方式,我们在扩展节点时,只会使小部分数据定位受到影响,一定程度减小了数据库的缓存压力。当然也可以使用redis、memcache等来做数据缓存,这样的话DBService接只充当一个封装数据操作的作用,此时直接使用取余的方式来映射节点即可。对于一些量级不大的公共数据,往往只需要一个DBService节点就行了,这种映射关系直接放在配置文件中即可。

数据库

作为服务端,当然少不了数据库。为了能够有能力应对大量数据,当然不能使用单一的数据库。好在现在很多数据库都有自己的集群策略。如较为常用的关系数据库Mysql,或是非关系数据库mongodb都有集群策略。

在数据库的选择上,关系型数据库和非关系型数据库各有优势,比较两者的文章也很多,有兴趣的可以自己去搜索。就效率和稳定性而言,我只能说以上两者都是比较成熟的商用数据库,在效率和稳定性方面一般是不会出什么大问题的,都可以放心使用。

不过就我个人而言,我更倾向于非关系型数据库mongodb。第一因为mongodb的集群配置非常简单。第二是个人认为文档形的数据库更适合开发游戏。很多使用mysql的人可能有感触,为了满足不断变化的需求,一般在存储玩家数据上面都是直接将玩家数据对象序列化成二进制流,然后再放入在数据表。这样就不用随时修改数据库表了,但是弊端就是我们无法可视化查看数据,也无法单独修改某一项数据。如果使用非关系型数据库就不存在此问题,我们可以直接将对象存入数据库,也可单独修改一项,开发者也可以直接查看数据。

猜你喜欢

转载自blog.csdn.net/uisoul/article/details/105733515