20190719顶号宕机事件

继上一次写宕机总结后,服务器稳定运行了将近一年,终于在午夜的越南服发生了一次诡异的宕机。
宕机的堆栈很明确:mgr上用来管理登录创建的一个clt指针变成了野指针。
但大陆服用这套代码跑了一年多也没有出现过宕机,事情一下子变得有趣起来。
mgr上定义的clt实例可以简要的写成这样

class MgrGateClient{
    CLIENT_ID _client_id;
    AccInfo acc_info;///账号信息,包含username
    SessionLoadingPipe* curPool;///直接导致宕机发生的指针
}

GateConnection维护连接到本gate的clt实例,负责指针的new和delete

map m_clients

LoginSys的镜像map,包含所有gate上的clt实例

map sessMapName
普通的创建流程:

1.gate收到客户端的登录请求,向mgr发送一个登录消息,包含clientid,username,ssid
2.mgr收到登录消息后,在该gate对应的GateConnection中创建一个新的MgrGateClient实例,插入到m_clients这个map中
3.将这个MgrGateClient实例插入到sessMapName这个镜像管理map中

普通的销毁流程

1.gate与客户端断开连接,向mgr发送一个logout消息,包含clientid
2.mgr收到logout消息后,从该gate对应的GateConnection中删除MgrGateClient实例,抛出消息通知LoginSys
3.LoginSys收到消息后从sessMapName删除记录的这个MgrGateClient实例
4.GateConnection回收实例的内存

顶号

顶号是指一个账号已经登录,在登出之前通过一个新的客户端再次发送了第二个登录请求(比如用两台设备登录同一个账号的不同角色)
在处理第二个登录请求时,由于sessMapName中已经记录了这个账号的clt实例,所以插入时需要先销毁前一个实例,再插入第二次创建的实例。
具体流程:
1.gate收到客户端的登录请求,向mgr发送一个登录消息,包含clientid,username,ssid
2.mgr收到登录消息后,在该gate对应的GateConnection中创建一个新的MgrGateClient实例,插入到m_clients这个map中
这时候与普通的创建流程不同的是,需要先将sessMapName记录的旧clt实例销毁
3.通过旧的cltid找到对应的GateConnect,执行一遍clt的销毁流程
4.在sessMapName中插入新的实例

这套流程在设计上是没有问题的,但是sessMapName这个map在插入和删除时依赖的username理论上在玩家的本次登录登出过程中是不会变的一个值,却在某个情境下发生了变化。
简单理解为:

插入时
///clt->username此时为"aaa"
sessMapName.emplace(makepair(clt->username, clt));
删除时
///clt->username此时变成了"bbb"
sessMapName.erase(clt->username);

clt中记录的账号名发生变化的原因是:
玩家登录是一个包含很多步骤的过程,本次宕机涉及到的关键步骤是:
1.收到登录请求后先建立一个clt实例,记录clientid,username,ssid
2.向账号服务器发送一个Http请求,验证玩家账号信息,并在Http的回调中,用账号服务器发送过来的完整数据填充账号信息
在这一步中,username实际上是被修改了,只不过正常情况下账号服务器发过来的username和客户端请求中发过来的(之前记录的)是一致的。这也是最容易让人误解和忽视的一点(username这玩意儿还能变?),满满的侦探剧既视感,最像小白兔的人是真正的凶手
在事发当天,由于账号服务器的负载问题,在账号验证的返回中发了一串奇怪的html,导致mgr没有正确解析,username被修改成了一个空值
于是当sessMapName想通过clt实例中记录的username来删除实例时就删除失败了
又由于GateConnection依旧会回收实例内存,sessMapName中存的clt指针就变成了一个野指针

一些小插曲:
1.在gdb中打印野指针的内存时,由于指针原先指向的内存还不一定被重新使用,所以有些值比如int型的成员变量看上去还是对的
要判断是否是野指针,需要多看几个类类型的成员变量,比如string的成员变量,字符串是还存在还是已经变成空串
2.调用野指针的成员函数时不一定会在调用的地方宕掉,要实际访问成员变量时才会触发宕机
3.类中的static成员变量在gdb下会显示成static xxx =

你可能感兴趣的:(20190719顶号宕机事件)