集群实现细节(2)-玩家在线状态续
玩家离线
由于离线的状况多,常见的玩家自己拿自己的账号的离线,也有自己的账号在多个设备来挤掉线,还有就是被非法的人使用时的挤掉线。这里应该将玩家理解为一个账号,下边尽量用账号来表述。
网络断开或者主动离开游戏
这时玩家所在的服务器在玩家 net.close
时加入向通信 Redis 汇报的逻辑,即通信 Redis 里删除这个玩家的在线纪录。
同服重复登陆时的 Kick
首先肯定要踢掉之前登陆的链接。因为同服时新旧链接都在这个服务器上,逻辑上简单,允许新链接的登陆比较好处理,但是后边有其他考虑。
异服登陆时的 Kick
首先也是踢掉之前的登陆。需要在之前登陆到的服务器将该账号的链接断开并整理数据存储之后,才能允许在新的服务器登陆。
旧服务器清理完该玩家后,向通信 Redis 报告删除该账号的在线状态。新服务器需要知道玩家已经在旧服被 Kick 完了才可以让玩家登陆。刚才同服时提到的允许新链接立刻登陆的问题,这里就变的比较不可控了。美好的过程是,新链接接入后挂起在新服,等异服踢完后,继续做新链接的登陆操作。但是这需要新旧服之间的同步逻辑,需要将新服玩家做个状态机为维护这种挂起或继续登陆状态。另外还要考虑这个期间玩家又在另一个服登陆。要考虑的东西很多……
还有一种有问题的处理方式,即新服只是向老服发送踢人指令,新服自己却马上进入新链接的登陆操作。这样的问题是,即使业务逻辑简单到不会发生数据不同步问题,但登陆操作不会一定成功。新服登陆完向通信 Redis 报告玩家在线,旧服踢完要向通信 Redis 报告玩家离线。这两个操作异步时,如果旧服的离线报告在后,通信 Redis 上就会错误的记录账号当前不在线……
总结
最终重复登陆问题简单处理方法:账号登陆时,只要检测到该账号同服或异服已登陆,先将旧链接踢掉,再将新链接断开。就给玩家一个提示“账号已登录,请稍后重试”,让玩家自己来多操作几次,直到旧链接被踢完。
同一账号的同瞬间多起登陆事件
上边重复登陆的检查还有一种情况不能防止,就是瞬间的多个客户端用同一账号登陆。同服时由于 Node.js 的异步,异服时由于天然异步,检查该账号是否已登陆与将当前链接成功登陆并向通信 Redis 报告这些操作不具原子性。
所以在最后向通信 Redis 写入上线状态时再次判断是否已登陆(即判断是否被瞬时并发的另一个客户端的登陆给标记为已上线了)。Redis 里用 hsetnx 代替 hset,前者在数据已经被设置时操作失败。
需要在最后向通信 Redis 写入玩家在线状态时为原子操作。这个原子就交给 Redis 的事务吧。
(这个事物下周再添加。)
后记
刚接触不久或者本身逻辑就复杂的东西(例如分布式)很多思考的结果不及时记录的话,后边容易忘记当初的理由,导致需要冗余的重复思考。所以现在博客写琐碎些,记载些细节的思考。