最近一周做压测,发现了几个 skynet 使用上的问题,记录一下

目前的进程大概分成 center、login 和 room 三种,login 用来承接玩家连接,做一些单机玩法,center 做一些玩家间的附带玩法,比如公会、排行榜,room 处理房间相关的战斗逻辑和 aoi 剪裁

首先发现的问题是建公会相关的。压测的时候居然不是 center 进程挂了,而是 login 进程先挂掉了,公会服务负载还比较低;还附带副作用:建立的公会多了,就再也起不了服务器,center 一定会在启动的时候挂掉。用中间件针对 skynet 开发的 top 去观察内存,发现居然是 Sharedatad 吃掉了大部分的内存和 CPU。排查了一下相关调用,在实际调用 sharedatad update 的地方,fork 了协程做延时 update,并打印 update 的 res 名字。发现到动态玩法开关会频繁的更新 sharedatad,导致内存猛涨,而 sharedatad 的回收会比较慢, 看代码 每次 update 要 1 分钟才会回收。对于频繁更新这种场景,可能换用 datacenter 会好点

其次是压一个 5000 人的房间玩法,居然不是 room 进程挂掉,而是 login 进程挂掉了。全链路是施压机 ->goscon->login->room,具体房间逻辑是 room 处理的。看 skynet 的 sys_list,是用来转发房间广播的 room sender 服务 cmem 偏高。花了不少时间去 review 这块代码,以为是自己写的 C 扩展出问题了,具体优化可以看 《Skynet 广播优化小记》。CM 提示有可能是对端读不过来,用 netstat 看一下积压的队列情况。在 login 侧是 Send-Q 偏高,而 goscon 侧是 Recv-Q 偏高,施压机则是两者都不高。而 goscon 的内存和 CPU 都不高,考虑是不是 goscon 不干活了

多压了几次看问题,然后 SA 找上门了 =_=川 说把 nat 网关压爆了,影响其他项目,让换个地址再试试。于是得知全链路是施压机 ->nat 网关 ->goscon->login->room。nat 网关是项目间共用的,之前定的性能规格不够高,我们压测才 40MB/s 不到,居然也吃掉了大部分性能了 …… 去掉 nat 网关用内网地址访问后,问题就解决了。这样看来 goscon 的实现是正确的,发现写不出去的时候,就不再读新包了,所以 Recv-Q 偏高。这里不太理解的是,goscon 写不出去为啥没有 Send-Q 变大,难道是 goscon 写的都发出了,但是内核没有可写信号给 goscon,所以就不会往里写,不会积压

周末好像想明白了为啥 Recv-Q 和 Send-Q 单位是字节,因为没发出去的时候,还不知道该怎么分 TCP 包,所以这里不是包数量,而是 buff 大小