游戏服务器结构的设计

每个全局功能(需要和其他玩家交互的功能,例如聊天,公会,副本和场景等)都作为一个独立服务来做,方便扩展。这个服务可以是逻辑服务器内嵌的(如果没有与其他服务器交互的需求),也可以是一个单独的服务器,甚至一个服务器集群。

在分区游戏中,很多时候某些全局功能都作为一个功能模块内嵌到逻辑上去了,这样在某些需要服务器间玩家交互的功能时,就会很难做。例如: 如果聊天/好友服务内嵌到逻辑服务器上时,玩家如果需要与其他服务器上的好友(在跨服功能中加为好友的)通信时就会很麻烦。

大多可能有很大压力的全局功能都采用服务器集群的做法,集群中的成员:

  • 一个或多个(分层的或平行的)管理服务器,职责:
    • 根据某种策略(id 哈希/映射,服务器压力统计等)路由外部请求到具体的工作服务器;
    • 工作服务器的新增(外部请求或根据工作强度向其他服务请求启动新的工作服务器);
    • 工作服务器的停机
  • 多个工作服务器,职责:
    • 根据请求 id,处理对应的模块实体;
    • 向管理服务器汇报服务器压力

实现:

gate

  • 多个 gate 管理服务器,外网接入:
    • 由 DNS 根据线路等指向或者固定 ip,client 接入时,会根据玩家的线路和 gate 的压力情况选择适合的 gate ip:port 返回给玩家;
    • gate 接入,外网 ip:port 注册
  • 多个 gate ,外网接入:
    • 玩家接入,认证等等;
    • 向 gate 管理服务器发送在线压力情况;
    • 和在线统计服务器和逻辑服务器合作,负责玩家的登入及之后的消息中转

登入

  • 一个或多个在线统计服务器:

    • 如果不存在玩家记录,根据策略(id 哈希,服务器压力等)选择一个逻辑服务器供玩家登录 [player: zzz, id: xxx, ref: yyy];
    • 如果存在玩家记录,直接返回其所在逻辑服务器 [player: zzz, id: xxx, ref: yyy+1](引用加1,防止逻辑服务器的错位删除)
    • 监控逻辑服务器的状态,逻辑服务器宕机后,标记其状态为宕机,避免玩家登入时再次接入已经宕机的逻辑服务器。
    • 做成无状态的服务,所有数据都存 Redis(这些数据都是在线玩家和所在逻辑服务器的映射,只需存在内存中),避免过多的逻辑,以及宕机后的自动恢复。 (服务与存储分开的关键是,数据能够原子的更新, Redis 的 Lua Script 都可以做到这点。当然,也可以写一个定制的内存服务程序来解决这点。)
  • 多个逻辑服务器:

    • 玩家个体逻辑的大部分功能都由该服务器实现 (如果玩家逻辑功能比较复杂,可以用动态语言+虚拟机方式实现,这样当某个玩家或某个功能出现异常时,缩小影响范围);
    • 在玩家登出后的有限时间内清空玩家数据,并向在线服务器(或 Redis)发送清空玩家对该服务器的引用[player: zzz, ref: yyy]

client 在 gate 完成登录认证,由 gate 向在线统计服务器发送请求选择逻辑服务器 [id: xxx, ref: yyy] , gate 向逻辑服务器发送玩家的登入请求 [player: zzz, ref: yyy] ,玩家进入游戏。

公会:

玩家在某个服务器上登录之后,所有的公会操作请求都转发到公会的管理服务器上去, 公会管理服务器根据公会 id 将玩家的请求路由到负责该公会的工作服务器。

副本

  • 一个副本管理服务器: 选择或定位特定副本所在的副本服务器;转发玩家/副本消息包
  • 多个副本服务器: 处理副本场景同步,副本战斗

玩家创建副本:副本管理服务器根据副本服务器的压力情况将创建请求发给特定的副本服务器,并记录副本实例和副本服务器的对应关系。

玩家进入副本:找到副本对应的副本服务器,并转发玩家的请求。

聊天/好友:

玩家在某个服务器上登录之后,由所在服务器向聊天服务发送登录通知,聊天服务会加载其好友列表并通知在线好友;之后玩家的聊天信息都由其所在服务器转发到聊天服务器,由聊天服务器将聊天信息发送到目标玩家所在的逻辑服务器,再转发给玩家。

缺点: 聊天消息太多,导致逻辑服务器流量压力。 ==> 优化:可以由 gate 直接转发玩家的聊天消息到聊天服务器或者通过其他线路来做即把整个聊天功能和游戏系统完全隔离(保留登录下线的协议)。

其他: 我个人认为其实游戏里面的聊天/好友做成独立服务之后,其实就是一个聊天平台,而游戏里的聊天不过是这个聊天工具里面的一个分组而已。

其他服务: 各种监控服务器: 监控特定服务的压力/宕机情况,必要情况下发出警告或向服务启动管理程序发送请求,启动某个服务。

服务启动管理程序: 根据监控服务器的请求,动态启动特定服务。

PS: 我去一家游戏公司面试,面试官让我总结下分区式游戏和 one world 游戏服务器的不同。回来后就写了这个,总的来说,我个人认为,在将全局模块服务化之后,分区式游戏服务器可以很容易的扩展并支撑 one world 游戏。