spring boot 项目 TPS 压测性能优化
0x00 前言
公司的直播网站采用的是 nginx+spring boot 这样的一个架构。最近发版比较频繁,压测没有跟上,某次重要的直播就出生产事故了。高并发的请求直接击穿了缓存,加上代码有不合理的地方,直接导致数据库 CPU 被打满。当时的紧急处理是回滚了代码,重新部署,后面才算平息。由于现在处于业务关键期,老板直接下了死命令,下次直播必须平稳度过。然后我就开始着手进行性能优化的事情。
0x01 分析定位
从经验上来说,这种性能有问题的基本都是后端服务的问题,和前端的 nginx 关系不大。于是打开 jmeter,构造好请求参数以后,开始在本机进行基准测试。第一次是 100 个线程,设置并发为 100,结果如下:
这里的 TPS 只有 5.3,着实把我吓到了。由于本身接口是加了 cache 的,理论上面 TPS 不可能低到这样的地步。正常来说在 200 左右是合理的。我开始有点怀疑是不是数据量的问题,分析了下返回结果,我发现现在的返回的结果很大,单个接口返回有 76K 之多:
我去,赶紧加个配置,把返回的结果压缩下:
压缩后返回结果如下:
10 倍的压缩,传输的数据量只有十分之一了,性能应该有 10 倍的提升了。这时候再压测一波。。。
没啥区别!
这时候不要慌,再分析下结果。这个响应的最大值有点奇怪,按理来说,现在返回的数据量也不多,如果缓存生效的话,没理由要这么久才能返回。我开始有点怀疑缓存没有生效。为了确定缓存是不是没有生效,我在代码里面增加了打印日志的语句。如果是走了缓存,这个语句应该就不会执行到。重新压测,果然是缓存没有生效!基本都是走了 DB 查询。而且从日志上面来看,随着并发的增加,DB 查询的耗时越来越长,基本 DB 就是要被拖垮的节奏。这个就和当时生产事故的现象和类同了。
对代码进行简单的走读,发现这个缓存的方案采用了懒加载的实现。第一次查询 redis 为空的情况下面,先查询 DB,然后把结果缓存到 redis,后续再来的请求就从 redis 上面获取。问题就是在于这个第一次查询 redis 为空这里,理论上面这里要加锁,不然在高并发的情况下面,很容易多个线程都是得到了 redis 为空这个结果,从而导致缓存失效,DB 被拖垮。
在代码里面简单加了个对象锁,重新压测:
至此,基本完成了后端应用的性能优化。后续主要是相应调整了下应用的 tomcat 线程池大小,调整如下:
0x02 其他优化
在完成了后端应用的优化后,开始结合 API 网关和前端 Nginx 进行全链路压测。压测过程中发现 ng 日志有 warn 提示,存在临时的结果落盘的现象:
an upstream response is buffered to a temporary file /var/cache/nginx/proxy_temp
nginx 配置文件增加配置:
在测试环境压测的过程中,重新压测发现 TPS 又开始上不去。。。调整线程数也没有任何效果。由于测试环境是在公有云上面,有点怀疑是不是服务器带宽的问题导致。将压测程序放到测试环境内网进行压测,发现 TPS 恢复正常。最终确定是测试服务器的互联网带宽配置不合理,更改完以后压测正常。
0x03 小结
影响应用性能的原因有很多,有可能是应用代码本身的问题,也有可能是环境的问题。一般的调优步骤是,先本地进行压测,这个时候可以忽略网络带宽的瓶颈。在本地压测结果达到要求以后,再上到测试环境进行压测,这个时候就要着重观察是不是环境和配置有所制约,如果有就修改。特别是如果涉及到的调用链路比较长,那就要确保每一个环节的性能都要满足性能指标,最终的结果才能是你想要的。
版权声明: 本文为 InfoQ 作者【李日盛】的原创文章。
原文链接:【http://xie.infoq.cn/article/78ba873b08a0902f7b9be6d71】。文章转载请联系作者。
评论