单个skynet进程,或者说单台机器的承载业务能力是有上限的,对于负责玩家主要业务的节点,横向扩展以提高游戏承载能力是必须的。
对于滚服架构,玩家角色与指定业务节点(单服)固定对应,连接游戏业务前通过中央后台获取到指定信息进行连接。承载能力通过新增单服完成,这里我们只对世界服架构做探讨。
我们将负责玩家主要业务逻辑的skynet节点类型称为user,将负责验证登入身份信息的skynet节点称为login,单个接入的玩家使用独立的lua虚拟机代理,称之为agent,玩家唯一id称为role id。那么user必然需要是非单点且支持动态分配扩展的,现在探讨以下几个问题:
user节点如何分配
即登入时如何为agent分配目标user;这显然是在login节点上处理的逻辑,节点需要维护集群内user节点相关信息,讨论两个实现方案:
方案一:根据各节点当前承载情况动态分配。
方案二:根据role id硬哈希分配。
方案一支持根据当前各个user承载情况为接入玩家动态分配一个可用user节点,需要支持监测所有user状态、实时承载等信息;玩家与节点无强制关联关系,可能被分配到所有的节点上;方案支持热调整user机器,风险在于维护的user信息需要准确可信;
方案二根据登入的role id与当前可用的user列表哈希计算映射到指定的user节点上。类似滚服分服的思路,将指定role id跟指定的user节点固定对应,单节点异常影响范围在单节点内,相对可控但线上调整机器复杂;
对于上述方案一,user是根据登入时刻各节点状态动态分配的,那么就需要在服务器内维护一份roleid 2 user的数据,用于查询指定roleid当前所在user节点,这份数据需要是集群内可共享的,这里给出一个临时方案:使用redis缓存,节点内定时获取更新;
对于方案二,在可用user列表不变的情况下,哈希结果是固定的,roleid被分配到固定的user上,可以通过本地计算得出,从实现角度来看较方案一简洁;
节点间交互如何实现
我们希望外部节点(这里指集群内的非user节点)不必关心user的规模也不需引入user的分配等实现细节,将user当成寻常的单点节点进行交互;同样的,user之间的通信也不希望引入user分配的细节;
这时候我们实现一个代理类型的节点,暂时称之为user_proxy,user_proxy节点代理所有user节点之间、以及其他非user节点发送到user节点的消息转发工作;
比如:当login节点向roleid发送消息时,login节点将消息发送到user_proxy节点,由user_proxy节点计算得到目标agent所在的user节点,将消息进行转发;同样的,user对另一台user进行通信时,也不必关心目标agent跑在哪台user上,将消息交给user_proxy转发就可以了;
特别的,当我们从外部节点向user广播消息时,可以由user_proxy节点转发广播消息到所有user节点,这样对于外部节点来说,只是做了一次寻常的跨进程调用而已;
代理节点压力取决于user,同样需要是非单点,每个user节点开启时分配一个user_proxy代理使用。