记一个特殊操作可能会引起资源泄露的严重bug

2018-09-05  本文已影响42人  fooboo

在开发和测试中,有几次遇见一直打印某种info的日志。因为这个跟资源回收相关,大概就是客户端没有响应,不清楚客户端什么情况,然后收不到心跳包,所以会保留一段时间并作下线处理。因为这边的架构(大部分游戏后台架构都是分布式的进程,这边比较特殊,用lua虚拟机模拟出分布式的情况)。

参考云风skynet框架里的demo,一个玩家由一个agent代理,然后有场景服务scene,有网关服务gate。

当scene收不到由agent路由过去的客户端心跳包时,固定时间后scene便通知agent做下线处理,然后agent发消息到gate断开链接。底层框架处理后会发条消息到gate表示断开成功(为什么这么处理见后面),gate再发消息到agent做断开处理,agent处理后通知scene做下线处理,这个过程是正常完整的逻辑,如果中间出现问题,就会带来底层套接字没有正确释放和回收,也引起发送缓冲区堆积占用内存,也包括占用agent服务,毕竟这个资源有限不能正确回收,也包括scene上有user对象,甚至后面一系列的数据不正确和回档问题,还有cpu资源浪费等等,很严重。

为什么会出现这样的bug,还不是偶现的?

正常登出处理会走正常下线处理,如果异常,比如因为异常把进程杀了链接没断开(我这边用的是unity客户端,不是手机安装的或者模拟器),并且该套接字队伍发送缓冲区需要写满故意让他返回-1,那么就会出现这种问题。大致情况是:skynet框架在close对应套接字时,会判断有没有数据要发送或者之前缓存的数据没有发送完,因为注册的可写事件不会再触发,所以每次write的返回AGAIN_WOULDBLOCK(内部枚举,表示暂时不可写),然后返回-1,如果不为-1就会发一条消息到对应的服务,后面会处理这条消息并断开;如果是-1则continue到循环开始,所以一直收不到消息,那么就出现了上面的问题。

具体代码流程可以参考skynet源码,就不贴了,整个流程还是蛮清晰的。

lsof这个进程查看链接还是ESTABLISH。

所以应用层需要心跳包来判断对方是否还存活,而不是依靠tcp自带的keepalive属性来,因为有些情况并不难立即发fin或者rst包断开…

大致写这么点,整个过程需要熟悉后台框架,流程走向,skynet底层实现原理。

上一篇 下一篇

猜你喜欢

热点阅读