技术分享 | 没想到,性能压测一直不达标的原因竟然是这个!

源宝导读:随着企业应用的规模越来越大,性能压力测试已成为研发的标配动作,开发人员在处理性能压测问题时,往往会遇到各式各样的“坑”。本文将分享我们在处理性能压测发现的问题时“踩”过的“坑”,希望能帮助大家避开,提高的解决问题的效率。

一、背景

    我们针对流程平台产品做性能优化的过程中,遇到了一些“奇怪”的问题,我们对系统做了全方位的检查和优化,但性能压测结果一直未达标。经过不懈的努力,最后我们发现问题出现在意想不到的地方,本文将分享一下我们的“坎坷经历”。

    我们先看下整体压测环境部署架构:

    流程平台的后端服务部署在K8S集群中,这些服务包含“流程应用服务”(简称服务A)和“流程引擎”(简称服务B),其调用关系比较简单,压测机(客户端)→  A → B,其中“流程应用服务”服务采用PHP技术栈开发,“流程引擎”服务采用dotnet core技术栈开发。

二、故事1:测试工具本身也会影响测试结果

2.1、现象描述

    首次压测,CPU平均负载低、使用率低、内存使用量低、磁盘IO低、网络IO低、数据库无慢SQL、无大量读取行,最终压测结果整体P90超过了3秒,也就是应用服务器、DB服务器压力都很小,资源利用率很低,性能却不达标!

2.2、排查过程

    按照一般性能问题的排查套路,先从系统资源层面上排查(例如检查CPU,内存,磁盘,网络层面是否存在明显的瓶颈),然后再根据资源瓶颈圈定大致的方向,缩小排查范围,最后根据资源瓶颈找到对应的应用服务进程,再深入应用服务进程内部做进一步排查。但是现在第一步检查系统资源层面并没有发现明显的瓶颈点,无法快速定位到底是服务调用链路上的哪个环节有性能问题。
    根据以往的经验,猜测可能是压测链路上的哪一段服务资源或存在限制,比如PHP-FPM进程数不够,或者是dotnet core应用线程池中的线程数不够,出现了排队,进而导致整体资源利用率上不去。于是对于PHP应用,则检查了php-fpm进程数,以及php-fpm的日志,未发现达到最大进程数限制的日志信息(如果进程数不够,通常php-fpm日志中会有进程数达到上限,增加进程数的提示),同时fpm-slowlog也没有超1S的方法,dotnet core应用则通过dotnet-counter工具检查线程排队情况,也未发现大量的处理排队情况,还是无法定位问题!
    最后只能对各个服务分别进行压测,逐个排除,服务调用链路A→ B,先对B进行单独压测,发现压测服务B时,资源利用率可以逐步升高,平均负载快速攀升,这就说明并非服务B阻塞了整个调用链,问题可能出在B服务之前的调用链路中,可能服务A出了问题导致压力进不到后端的服务B。于是对服务A也进行单独压测,同时保险起见,Mock掉服务B的响应内容,只对服务A进行压测,以减少服务B内部由于压力上来以后出现其他未定位的影响,导致问题被分散,更难以定位。压测服务A的过程中,发现和之前完整链路压测的现象一致,整体资源利用率上不去,压力也上不去,这就说明了服务A可能确实哪里存在阻塞。由于服务A是一个PHP应用,因此我们采取代码埋点,注释掉复杂的业务处理的方式,进行快速验证。在进行了大量的业务逻辑注释后,并没发现问题,直到最后一直到请求响应的末端,注释掉向客户端输出的响应内容的代码后(只执行业务,不向客户端输出结果),发现P90数据正常,服务器资源利用率也上去了。经过进一步缩小了范围,说明问题就出在压测客户端到服务A之间。检查压测客户端工具所在的机器,发现压测过程中,机器CPU一直处于100%,也就是说压测过程已经导致压测机达到了处理瓶颈,导致了最终压测结果不通过。定位到是压测机的资源问题,于是让负责的压测的同事将压测机增加到5台进行验证,压测结果正常。

单台压测机的结果:

增加到5台压测机后的结果:

2.3、压测客户端的CPU消耗到了哪里?

    从压测脚本上来看,应该是接收服务返回的内容后,需要进行一些处理,以断言服务响应是否正常,流程中心表单响应内容较大,压测脚本要处理这些大的响应内容耗费了较多的CPU资源。识别到问题,后续就是从产品和技术层面上考虑如何减少表单内容的响应了,一方面为了减少对压测机的消耗,另一方面更重要的是提高业务服务的性能!

三、故事2:后端服务间的网络带宽也会成为瓶颈

3.1、现象描述

    解决了压测环境的问题,压力上来以后,又遇到了新的问题,随着模拟压测用户的逐步增加到5000用户时,5000用户同时在线操作不同业务模块的场景,整体响应延迟随着用户数的增加,会持续增加到一个固定的高位,这次通过prometheus监控快速发现一个资源瓶颈,其中有一个K8S的节点网络带宽被打满,刚好达到网络带宽接近1000兆带宽,到底是哪个服务耗尽了带宽?

3.2、排查过程

    资源层面上能够快速定位瓶颈,于是切入到出问题K8S节点,检查该节点上所有POD中的服务,该节点除了跑我们的业务服务,还部署了redis缓存服务,由于该业务服务也跑在其他节点上,对照其他节点上并没有出现同样大的网络带宽占用,因此推测为redis缓存读写占用了大量带宽。为了验证此推测,于是将其中一个服务A使用的缓存切换为非redis缓存,减少对redis的读写,再次进行压测。发现带宽占用从之前的1000兆变为300兆左右,基本可以确定为服务A的在读Redis时占用了大量网络带宽。
    定位到系统瓶颈是redis读写带宽问题后,就需要更进一步深入到应用层面进行排查。那如何确认是哪块业务占据了大量的缓存带宽?业务使用缓存的地方那么多,怎么快速定位具体哪些缓存读取的动作占用了大量带宽?这时有两个指标可以参考,一个是读取次数,一个是键值的大小,如果键值本身很大,且读取频繁,产生的流量、占据的带宽肯定就越大。
    通过redis自带的bigkey,hotkey扫描工具,先扫描出大键值对象,然后通过检查大键值的具体内容,发现两个主要的业务功能点存取的键值比较大,其中一个存取内容在1MB左右,另一个业务存取的内容在30~50KB左右。确定了问题范围,就可以分析制定下一步的优化方案。从前面的分析可得出两个优化方向,一个是降低存取的键值大小,一个是降低读写频次。
    对于降低键值大小,一方面要从业务上分析,能否减少存入缓存的内容,另一方面可以更换序列化压缩算法,通过CPU算力换取更低的缓存空间,从整体上降低缓存内容大小。最快的捷径还是更换序列化算法,于是将服务A的默认缓存读写的序列化算法改成了更高效的igbinary算法。在不修改任何业务逻辑的情况下,再次进行压测,整体带宽占用节省约200兆左右,有一定程度的缓解,但治标不治本,随着压测量的增加仍然会快速占满带宽。
    对于降低读写频次,先从业务上分析这个1MB左右大小的缓存内容存的是什么(且不说缓存一个这么大的内容是否合理),该值为某一个业务值的全集合,内容虽大,但值相对固定,不存在大量不同的键值都是1MB的内容,只会有这一份数据,bigkey扫描出的结果也证实确实只有一份。对于这种场景就比较适合做二级缓存,1级缓存用redis存储数据内容是否发生变化,2级缓存使用本地缓存来存储这个1MB大小的实际内容。1级缓存只是用来协调多个服务副本之间的二级本地缓存数据一致性,调整后再次压测,单场景压测性能提升明显。

    对于另一个30~50KB的键值场景,存的是表单的元数据+表单实际值,由于场景不同,键值可能存在大量的不一样的内容,因此无法套用前面的优化思路,需要从业务和技术两个层面综合考虑如何减少缓存键值的大小,比如动静分离,表单元数据和表单业务数据分开缓存,表单元数据利用浏览器缓存等方案,该项优化改造成本相对高一点,没有在性能压测环境立即优化,会在后续产品迭代中进行兑现,这里就不做深入探讨。

四、总结

    以上是我们在性能优化过程中遇到的两个“典型问题”,从中我们总结出如下经验教训:

  • 定位性能问题的一般套路:系统资源层面(系统计数器,监控工具相对容易初步定位瓶颈方向)→ 数据库层面(业务类系统大多数性能问题来源于数据库)→ 代码层面。

  • 测试工具本身也要关注:压测过程中,我们往往仅关注服务调用链路的起点到终点的数据库之间的链路,通常不太会关注发起请求的起点,也就是压测客户机器。而压测客户端机器也需要监控常见的CPU、内存、磁盘,网络资源,这些系统资源都会影响实际压测过程,如果压测机首先达到瓶颈了,压测请求就进不到压测服务中去,压测结果就不准确了。

  • 对于环境资源的监控尽量不要“留死角”:不仅要监控客户端到服务端的网络情况,还需要监控服务端内部的网络。

  • 缓存是一把“双刃剑”,需合理使用:对于缓存的使用,平常大家可能只考虑某一块业务计算耗时,或是为了降低数据库读写压力,就使劲用来提高性能。不曾想过在高并发、大流量的场景下,频繁的大键值存取可能耗尽带宽,造成网络瓶颈,使整体响应延迟增大,性能优化利器反而被用成了性能杀手。

------ END ------

作者简介

田同学: 架构师,目前负责天际-流程平台的研发工作。 

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值