开发笔记(6) Erlang服务器集群常见问题

集群的安全启动/终止

典型地,我们需要在GS和各个服务都启动完成之后,再打开网关接入网络连接,这需要通过master节点进行监控,GS和服务也需要向cluster汇报自己的状态(启动中,运行中),master在关键服务都启动完成并状态正常之后,再通知gateway开放连接。基于cluster中的控制信息,这一点不难做到。集群的终止流程也类似。

集群OPS管理

master节点上还可以做一些简单运维工作:

  • 监控服务器状态:所有GS和服务是否状态正常
  • 自动开服:统计所有GS的人数,确认是否开新服,并同步到推荐服列表(DB操作)
  • 故障恢复:服务器本身在sup之下,其组件Crash可通过sup重启恢复,而遇上物理机宕机或失联,master会通过心跳检测确认节点不可达,然后代为注销问题节点上所有的服务,之后master将问题节点上的GS重新部署到其它可用节点。比起物理机宕机,更棘手的是网络故障,当失联节点重新回到集群而master可能已经在其它节点重新部署了故障节点上的GS,解决方案之一是,当一个节点与master节点失联时,终止自身。

这些工作本质上仍然是,定时监控状态,统计状态,确认下一步操作(开新服,重新部署故障服务器等)。由于OPS监控时间间隔一般至少是分钟级别,master不需要将所监控的表设为ram_copies,减轻mnesia同步的网络负担。

master单点

由于master负担了一些逻辑和状态(比如监控其它节点),想要做一个透明过渡的主从是非常困难的,目前虽然有个主从,但应该问题还很多,待后续完善。或者是说,对一个游戏GS来说,master节点的单点是能够容忍的(因为Erlang OTP足够健壮),遇上物理故障,停止整个集群或许是更好的办法。

多节点写入的安全性

这在Actor模型中的经典问题,比如当服务进程S需要对玩家进程P上的资源进行操作,分两种情况,需要对资源进行增加时,向P发送资源增量即可,而需要对资源进行扣除时,则要分两步:1.检查资源是否足够,以决定下一步操作,2.向P发送消息扣除资源 ,有多种方案可以检查玩家进程资源是否足够,同步消息,异步消息,公共缓存,数据副本等,事实上,无论何种方式都不能保证当P进程在收到消息扣除资源时,资源还是S当时检查到的情形,因为这两步操作之间,P进程可能收到其它消息对资源进行了改动。这里我们选择容忍这种情况,因此一般我选择通过公共缓存或者数据副本来进行同步资源检查,然后通过向P发送消息来进行异步资源改动。注意,玩家资源的变动只能由玩家进程来进行,对缓存数据保证单写入者是减少不一致问题最有效的方式。

如果这类问题在开发中带来很多困扰,那么可以重新考虑一下资源所有权的问题。比如有一种资源A,它只在进程X中增加,在进程Y中扣除,如果这种设计是稳定的,那么我觉得将A挂在Y上更为适合,而如果X和Y都对A有频繁的改动操作的话,是否可以从设计上,将X中的A和Y中的A设计成两种资源,当玩家从X系统进入Y系统时,进行一次资源转换(策划案上可以叫”投放”,”运输”之类的东西),这样两个系统都独立操作自己的资源,只在玩家选择进程资源转换时,再同步一次。当然,这里只是提供一种思路。

全联通问题

由于cluster用mnesia来做后台同步,而mnesia后台同步时,会形成一个全联通网络(即使集群节点都是hidden节点),虽然我们可以通过控制表的属性来避免不要的数据同步,但全联通网络的负载是比较大的,对于几十个节点的规模或许可以忍受,但更大的集群则需要正视这个问题了。由于负载量依赖于单个GS开销和机器性能,因此目前我们暂未考虑这个问题,等上线之后再观察。