如果你想做一个很多人参与的游戏,那么无论你是项目负责人、产品、开发或者测试,服务器性能就是你需要直面的关键问题。开服后大量玩家的涌入本是件好事,但若是服务器性能出现问题,导致玩家掉线、卡顿、crash,往往乐极生悲,好事变坏事,甚至不少大作都因此落马。
而在游戏开服前进行服务器压测,是规避服务器事故的不二选择。
Gaps压测工具(即将在WeTest上对外发布),是腾讯内部为游戏服务器压测打造的专业工具,本文主要跟大家分享我在腾讯使用Gaps进行腾讯著名的天天系列手游和腾讯代理游戏压测的经验,希望对大家有所启发和帮助。
一、编写服务器压测脚本(基于GAPS)
用gaps写压测脚本主要分成三部分:网络通信部分、游戏业务逻辑so编写、lua脚本编写控制整个压测流程。
1、网络通信部分
Gaps支持tcp、udp、http协议,平台已经做了大部分的工作。对于使用者来说,只需要编写head.so。里面含两个函数的编写,GetHeaderLen()告知平台先预收多少字节,一般根据预收的字节数可以分析出整个包体要收多长、GetPkgLen()明确告知平台包体要收多长字节,GetHeaderLen()和GetPkgLen()返回的字节数加起来必须等于一个完整包的字节数长度。
TCP协议:
Head.so的编写,TCP和HTTP协议的编写稍有差异,TCP协议,一般都在包头中会明确的指出包长度,比如某游戏的协议头为:
head.so中GetHeaderLen()、及GetPkgLen()的编写:当包字节流来了之后,其实解析出sizeof(uint32_t)就可以分析出整个包的长度。
HTTP协议:
目前市场上游戏协议大多采用HTTP,如果HTTP包头中含有CONTENT-LENGTH字段的,直接配置gaps目录下的config/ gaps_user_conf.xml即可。
如果http包头是不含有CONTENT-LENGTH字段的,且数据是CHUNK格式的。分下列几种情况:
(1)http请求也分有长连接的、或是短连接的。对于短连接而言,很简单,由于仅发送一个请求,之后一个回应消息被送回来,然后TCP连接被释放。建立一次连接收到的就是一个完整的包,Head.so的编写很简单。
(2)http请求是长连接的,但必须在数据流结构体中有能够标识协议包数据长度的字段,这样才能分析出GetPkgLen()要具体返回多少字节数。否则,如果既没有CONTENT-LENGTH,又不是短连接、在预收的部分又无法解析出具体包长要收多少字节数的,目前的GAPS平台是不支持的。下面以一款HTTP协议游戏的head.so编写为例:
1、通过抓包分析,下行包的HTTP包头的字段数据字节长度基本上是固定不变的。http包头之后是具体的数据内容。那就可预估出GetHeaderLen()第一次大概收多长字节。在后续调用GetHeaderLen(),可以用’\r\n\r\n’来解析。
具体的协议格式:
2、预估的GetHeaderLen()返回的长度,一定要能解析到消息长度的字节数。GetPkgLen()根据字节流解析出来,然后加上chunk数据格式格外要加上的chunksize、\r\n、及最后一个0chunk标识等字节流,就可以计算出具体需要返回的包体字节长度了。
2、游戏业务逻辑so编写
发包流程:
收包流程:
3、LUA脚本的编写
在lua脚本中主要是压测场景的编写。例如,每秒以多少人进入某个场景,按怎么样比例分配等。
二、定位分析服务器性能热点
在压测过程中,主要关注的性能指标有:服务器的并发事务处理能力、响应时间是否符合预期、CPU是否稳定或过高、稳定性压测时内存是否泄露、磁盘读写繁忙程度、网络等。但最常见的是性能问题。如果出了性能问题,我们如何去分析性能热点函数。
常见的工具中,Gprof需要重新编译,OProfile、Perf需要内核的支持。一般地,我们较多的是选择valgrind、和TProfile来进行分析,它们无需重新编译即可使用。
1、TProfile的使用(结合某款鹅厂自研卡牌游戏)
Tprofiler是由gperftools二次开发而来的服务器性能分析工具(https://root.cern.ch/root/html/TProfile.html)。下面结合某款鹅厂自研卡牌游戏,简单讲述下用TProfile进行分析。
1、用TProfile拉起待分析的服务器进程后。如果服务器进程间相互独立,可用配置脚本批量将进程拉起后,Kill掉待测进程,然后用TProfile重新将待测进程拉起。(调用start_tprofiler)
2、上机器人数量。当CPU上去后,压测个5~10分钟左右,即可调用stop_tprofiler。一定要正常关闭服务器进程。否则无法收集到数据。
3、获取收集的数据。在使用TProfile分析时,CPU性能分析、与堆内存泄露注意不要同时进行,分两次进行采集。采集数据格式如下:
TProfile对待测的服务器进程性能损耗极小(默认设置下,通常小于5%),支持多线程、支持动态库函数分析。
2、Valgrind的使用(结合天美工作室旗下某款手游)
valgrind的使用很简单,方法如下:
1、安装valgrind。去官网下载源代码编译安装即可。
2、要测试的程序不需要重新编译,直接用valgrind运行。
运行结束后,会自动生成名为callgrind.out.xxx的文件,xxx是进程的pid.
3、valgrind运行程序时,程序不能以daemon的方式运行,如果运行时间很长,可以使用screen来运行。Screen的使用可以google一下,有很多资料。
4、结果分析:Valgrind的结果可以使用callgrind_annotate命令来查看。
打开log文件即可查看哪个函数耗时最多。
当初该款手游在压测过程中,当压测总人数为1万、登录并发数为每秒100人时,gamesvr服务器进程CPU已经99+%。并且在进游戏场景后,300人/秒、和500人/秒的并发量时,gamesvr的CPU占用率相差近40%+。后利用Valgrind进行定位分析,主要是由于构造数据库请求的代码占了55%的处理时间。
Valgrind的优点:无需编译程序,使用简单,直接运行就行,支持stl,支持系统库函数。缺点:valgrind程序本身会占用很多的cpu,导致测试程序本身的并发量上不去。但当压测时发现性能上不去时,可上少量的机器人,用来分析在当前测试场景下,到底哪个函数是性能热点,已经足够了。
三、服务器压测常见问题总结
在压测项目过程中,最容易出问题的是登录场景,主要表现为TPS上不去,服务器CPU使用率过高。对之前所做压测项目中对它们碰到的问题、及所做的优化方法进行简单总结下:
对某款代理游戏登录优化的一些尝试:
1、应用自身的登录逻辑,以及环境(Linux、JVM、Tomcat)配置。
2、用jstack看看,CPU100%的时候,看在具体哪些线程上面。
3、游戏逻辑优化:
· 优化判断逻辑。
· 对登录接口中的好友同步进行优化,不必每次登录都同步。
· 对首页接口中的同步好友信息,排行榜信息等功能屏蔽,需要的时候再获取。
· 对首页接口中的重新计算背包格子等功能进行屏蔽,没有必要。
4、对tomcat参数进行优化。
5、redis连接池的优化:升级redis连接池jar包:为2.4.1,对应的commons-pool升级为commons-pool2-2.1,优化代码里释放redis连接的逻辑。
6、java代码优化:
· 全局HashMap改为ConcurrentHashMap
· 循环中的list.size()改为先赋值给一个变量再循环
· String.split改为StringTokenizer
· 通过增加各个小模块的执行时间打印,定位执行时间长的代码,进行优化
7、登录代码优化:
优化前:原来的接口是首页接口的数据里包括了好友信息和排行榜数据。
优化后:现在改成了首页数据不包括这些,只有当用户首次进入无尽模式时,调用一次好友信息和排行榜数据接口。
现在只有注册的时候,获取好友信息,其他时候不是每次登录都拉好友数据的。
其他时候只有距离上次进入无尽模式界面超过5次心跳后再次进入无尽模式界面时,才会调用积分接口(返回好友和自己的积分)。
天天系列游戏压测中一些问题记录:
1、响应超时问题、及在压力较小情形时gamesvr的CPU占用率高。总在线人数为3000人,每秒放500人进行游戏请求,gamesvr的CPU为90+%;如果改为每秒放300人进行游戏请求,gamesvr的CPU为50+%。
解决方法:由于是数据库请求构造比较慢,构造数据库请求代码占55%的处理时间。优化时需要消除这部分事件。解决方法是加缓存(tcalplus)、减少数据库操作。
2、服务器响应慢,还有一个原因,是消息转换开销。有一个日志配置项错误,导致所有进出消息转换成了字符串,但是没有打印到日志。
3、共1.2w人,每秒放100人进入登录场景,部分出现响应超时、及返回系统错误。
解决方法:由于是有状况没有解锁用户导致返回“系统错误”、及排行榜的一个性能选项没有打开,导致随着用户数增加,服务器性能下降。后调整方案后,此问题解决。
4、存在部分用户登录返回错误码-1、1(操作失败)、35(好友关系查询失败)、服务器响应超时情形。
解决方法:由于随机会出现一些自己踢掉自己的情形,对各个服务都有影响。后加入一些保护机制,已解决该问题。
5、当“服务器在线人数”大于“服务器配置人数”时,如配置3000人,但测试时上线人数为4000人,服务器未加保护策略:出了错误无法恢复,内存池用尽,且全部用户下线30分钟后,CPU仍旧是99+%。
解决方法:加入保护机制,当超出配置人数后,后续来的帐号登录请求,服务器不会对其响应。
6、拉排行榜偶现错误码。存在服务器返回多个好友的OpenID是自己的。偶现最后一个好友的OPENID是空的。
解决方法:这是由于修改其他bug而导致新出现的bug。拉排行榜时候,新加了离线保护功能,做的保护还不全面,导致用户重复登录时会出现该问题。及数据库表的某个字段在插入时存在问题。
7、在压测比赛场景完后,ranksvr的CPU定期会进行抖动,CPU从<1%突然飙升到100%。主要是由于ranksvr定时更新造成。
在压测过程中,当压测时间比较紧时,会优先关注主流程的压测,例如登录、拉排行、购买、战斗、结算等。当主流程通过后,再针对游戏其他协议综合压测。根据大部分的压测过程来看,当并发量上去后相较其他方面,数据库方面会比较容易出问题。