记录一次游戏服务器的压测调优记录(Golang语言)

前言

辛苦开发了一年的服务,终于要上线了,进入了关键的质检阶段--压力测试环节。
目标值:一千个用户,300rps

问题

因开发节奏的紧张,初期并没有注重优化,果不其然,一压测就爆炸了,主要问题有如下:

  1. RPS太低,1000个用户下,rps初期仅100
  2. 随着时间的推移,rps逐步降低
  3. 服务器随时闪崩

很明显,压测后感觉我们的代码是一坨乐色,还能咋办呢,慢慢排查吧。

思考

1.服务器为什么会崩溃?
2.为什么请求的响应耗时很奇怪,时而快时而慢(且多次随着时间的推移 响应越来越慢)?
3.rps为什么那么低?瓶颈点在哪里?如何优化?
4.某些业务,没有复杂业务逻辑,却耗时较高,且不稳定

排查过程

 1.考虑压测服务本身是和其他服务共用资源,导致压测数据有波动(时而快时而慢),内存不足导致崩溃
 2.内存泄露,导致压测后期崩掉,内存不足导致崩溃(通过pprof排查)
 3.排查过程中,因埋点太多,导致日志发送太多,因公司的日志组件实现,若不能及时发送到远程,会堆积在本地,则导致本地磁盘占满,服务器打开了太多文件句柄,导致内存溢出.(后提出优化,若磁盘占用一定大小,则先删旧的文件,再写入新文件)
 最常见的服务崩溃,但排查后发现,无论是异步业务,或者普通请求,都有捕获,并不是该原因
 代码很难看出来,经过多次排查日志,发现部分崩溃原因是因为未对map加锁(尽量使用sync.map)
 排查方向,写一个中间件工具,针对压测结果,出错业务单独埋点,第一步,第二步的耗时
 此时出现问题,对某个接口的压测结果,时快时慢(发现是资源问题)。
 排查方向,在最底层,每一步对数据库的操作,都埋点排查(优化慢查询,all查询,建立索引)
 发现日志记录 和压测结果不符,多方讨论后,得出问题在于压测脚本。
 对加锁埋点记录,发现部分业务加锁不正确,如某个死锁问题,导致用户超时断线,则脚本中丢失了一个用户数,使后期rps越来越低

总结

总的来说,遇到很多很多问题,算是各方面的大坑都踩了个遍,大概有以下几个:

  1. 针对关键业务埋点,优化部分接口(通过压测结果分析)(如分对推送数据做优先级,分多次下发,和某些业务的异步处理)
  2. 排查内存泄露(通过pprof抓取)
  3. 排查锁问题,对不可重入锁,重复加锁(多次调用中间件 重复对不可重入锁加锁)
  4. 排查并发问题(检查代码和日志)
  5. 压测服务,独立部署,升级配置(通过Grafana监控)

感悟

总的来说,个人主导的该压测优化过程,历经三天四夜,圆满结束.(压测稳定,上线不慌)

过程中有疑惑,有压力,有烦躁也有感悟,不过耐心细致的分析排查,进行了七八个优化,解决了三五个隐患,最终顺利的结束压测,还是很有成就感的.

这是一次很好的解决问题过程,故做此记录.最终达到1500个用户量,450rps,其中98%的请求耗时,都在200ms内,90%耗时都在50ms内.且后续进行压测48小时,仍能保持稳定的rps和合理的资源占用.(撒花 ✿✿ヽ(°▽°)ノ✿)

分享

另外分享排查过程中,几个常用的pprof命令:
可实时抓取目标服务的状态,并在本地web直接查看火焰图属性,非常方便.

  1. pprof 实时 查看实时内存:
go tool pprof -http=:[本地解析pprof文件的端口(随便填)] http://[目标服务器IP:端口]/debug/pprof/heap
例子:
go tool pprof -http=:8001 http://127.0.0.1:8087/debug/pprof/heap
  1. pprof 压测 实时 查看实时协程:
go tool pprof -http=:[本地端口] http://[目标服务器IP:端口]/debug/pprof/goroutine
  1. pprof 压测 实时 查看60s 的cpu占用情况:
go tool pprof -http=:[本地端口] http://[目标服务器IP:端口]/debug/pprof/profile  --second=60