处理玩家的登录,是服务端框架的主要功能之一。分布式系统涉及多个服务,让它们相互配合不产生冲突是一大难点。
1、登录流程
分布式服务端的登录功能要处理好如下两个问题:
- 问题一:完成角色对象的构建和销毁。如下图所示:
当客户端连接、发起登录时,服务端要创建一个对应角色的程序对象,用以加载角色数据。当客户端掉线时,服务端要保存角色数据,并销毁程序对象。这套框架会为每个客户端创建一个agent服务。
如何解决:需要创建名为agent的服务,它也代表角色对象。
- 问题二:防止角色重复登录。如下图所示:
在同一时间,一个角色只能由一个客户端控制,如果用已在线的角色登录,需要先把已登录的客户端踢下线。
如何解决:需要一个记录agent在线状态的服务agentmgr。
2、完整登录流程
下图展现了登录过程中最复杂的一种情形,即假设客户端B已在线,客户端A要在另一节点登录同一账号。
- 虚线内的服务位于同个节点;
- 不同虚线方框的服务可能配置于不同节点;
- gateway及其对应的login和agent一定位于同一节点上;
- agentmgr可位于任意节点上。
阶段 | 说明 |
① | 客户端A连接服务端某个节点的某个gateway (为实现负载均衡,客户端知道所有gateway的地址,并随机选择一个) |
② | 虽然能连接上,但服务端并不知道客户端A要登录哪个角色。客户端A发送消息,消息包含账号和密码,gateway收到后,随机选择节点内某个login服务,并将消息转发给它 (login服务是无状态的服务,专门用于处理登录校验,一个节点可以开启多个login服务,以分散负载) |
③ | 账号,密码通过校验后,login服务会向agentmgr发起登录请求 agentmgr会记录所有在线玩家的状态(包括登录中、游戏中、登出中),通过向agentmgr发起请求 agentmgr可以判断账号是否已登录。如果未登录,直接进入阶段⑥,否则先将已登录的客户端踢下线 agentmgr是个“权威”的服务,角色能不能上线,能不能下线都由它裁决 |
④ | agentmgr要求原客户端对应的agent下线(发起kick和exit请求),原agent会保存角色数据,然后退出服务。 |
⑤ | agentmgr通知原客户端对应的gateway,让它告诉客户端B“你已被踢下线”。然后设置gateway的状态,取消客户端B与角色101的关联 |
⑥ | agentmgr向nodemgr请求创建agent服务 nodemgr即节点管理器,它可以提供创建服务、节点监控等功能 |
⑦ | 创建客户端对应的agent,新agent读取角色数据 |
⑧ | agent创建完毕,agentmgr会记录角色处于“已登录”状态,再通知gateway,让它把新客户端和新agent关联起来 |
⑨ | 进入游戏阶段,客户端发送的消息被转发到新agent上 |
3、掉线登出流程
当客户端掉线时,登出流程如下图所示:
阶段 | 说明 |
① | 客户端A掉线,gateway要取消客户端与agent的关联 |
② | gateway向agentmgr发起下线请求,所有上线下线的请求都必须由agentmgr仲裁 |
③ | 如果仲裁通过,agentmgr要求代理服务agent下线(发起kic和exit请求),agent会保存角色数据,然后退出服务 |
4、总结
登录登出过程涉及的步骤较多,越多就越复杂,也就越容易出错。
问题:
假设客户端B已在线的情形,但如果在客户端B登录的过程中(比如角色数据尚未全部加载),客户端A请求登录,又该如何处理?会不会造成数据紊乱?
解决:
这些情况颇为复杂,我们的解决办法是。所有上线下线的请求都要经过agentmgr,由它裁决,只有“已在线”状态的客户端方可被顶替下线,如果处于“登录中”或“登出中”,agentmgr会告诉新登录的客户端“其他玩家正在尝试登录该账号,请稍后再试”。