网关登录流程上的一处并发错误
使用 golang 写网关时,通常每个 socket (玩家) 都是并发处理逻辑的
网关的第一条消息,玩家账号令牌验证
大概率存在并发错误,而程序员往往无法察觉
本人也是昨天刚刚在压力测试中发现的,这里记录一二
常见的 Golang 玩家账号令牌验证
逻辑
(这里假设 gateway 具备会话保持的特性)
常见的逻辑基本如下(伪代码):
func onMsgLogin(...) {
// 1. 验证 Token
// 2. 如果有旧账号,踢出旧账号
// 3. 分配 Lobby 服(或是 游戏服、逻辑服,叫法很多)
// 4. 返回结果
}
以上代码同一账号同时并发登录存在问题
分析1 - Token 验证
通常会想当然的认为,同账号 2 次登录,数据库 Token 已经变掉了,所以同账号 2 次登录,只会有 1 个验证成功
真实并发时,会存在,数据库 Token 被覆盖前,上个账号已经完成 Token 验证
那么同账号 2 次登录,在 Token 验证 环节都会成功!
即 1 操作仅为验证连接的合法性功能,无法保证 onMsgLogin 函数被重入
分析2 - 踢出旧账号
踢出旧账号,常见的仅为关闭上个 socket 对象,是无法阻止上个账号其 gorouine 中的 onMsgLogin 继续执行完毕的。
即 2 操作仅为延迟关闭、释放旧账号资源,无法保证 onMsgLogin 函数被重入
分析3 - 分配 Lobby 服
如果想当然的认为, 1 、 2 操作双保险保证下面的逻辑不会被重入,那么分配 Lobby 服,同一账号同时并发登录,会被分配不同的 Lobby 服
因此,这个操作必须具备原子性,保证 2 次并发能是同一个 Lobby 服
分析4 - 可能的后果
同一账号同时 2 次并发登录,可能会被分配不同 Lobby 服
由于其中 1 个账号稍后会马上被踢掉
主要看 Lobby 服上会有哪些业务,才能分析可能的后果
比如,账号登录会在 Lobby 上加载账号数据, Lobby 上账号数据过期 5 分钟会下线
那么可能存在账号数据同时在 2 个 Lobby 上的风险