问答 压力测试遇上了一个奇怪的问题,请有经验的兄弟们帮忙看下。

徐旻 · August 26, 2018 · Last by jackyin replied at December 10, 2018 · 3980 hits

本人现在要对一个 API 网关的性能做测试,在搭建环境的时候,遇上了一个让我百思不得其解的问题,想请各位帮忙看一下,能解决当然万分感激,当然能提供解决思路的也十分感谢。

前提

发压机: 云服务器 8 核 8G
被测机: 云服务器 2 核 4G

测试工具:基于 python 的 locust。以下的测试我用 docker 容器起的 locust 和 非 docker 容器起的 locust 都尝试过了,数据还是一样的。
locust 运行模式 都是 master-slave

测试接口 1:
http://.../s/0ms/1k
测试接口 2:
http://.../s/200ms/1k

为了方便的调试,用 java 的 spring boot 框架写的测试接口。
测试接口 1 和测试接口 2 之间的不同就是接口测试 2,我加了 200ms 的延迟,测试接口没有任何业务逻辑运算。

数据一

场景一

测试接口 1:http://.../s/0ms/1k
slave 数:1 个
并发用户:1000 个


这组数据我得到的信息是一个 slave 的时候,1000 个用户并发下得到的 TPS 是 600 左右,并且是在 linux 服务器上一核满负荷的情况下。被测服务器的 cpu 使用率属于正常。

场景二

测试接口 1:http://.../s/0ms/1k
slave 数:8 个
并发用户:1000 个


当我把 slave 加到 8 个的时候,1000 个用户并发下,TPS 差不多要 4400 左右了,发压机现在是 8 核满负荷运行。被测服务器的 cpu 使用也从前面的低使用率上升到了快平均 cpu 使用率接近 45% 了。我想说的是 1slave 的极限 tps 数 * slave 的数量 应该等于整体的 tps 的数量。以前测的都是开发写的接口,所以这条规则我是用到现在的。

场景三

测试接口 2:http://.../s/200ms/1k
slave 数: 1 个
并发用户: 1000 个

测试接口 2,因为有 200ms 的延迟,所以 1 个 slave 进行请求的时候,我得到的 500 左右 tps。其他情况也正常。

场景四

测试接口 2:http://.../s/200ms/1k
slave 数: 8 个
并发用户: 1000 个

当我把 slave 加到 8 个的时候,问题出现了,就好像突然遇上了什么限制,TPS 在 1000 的时候就到了一个稳定值,如果按照我以往的经验,一个 slave 的 TPS 在 500,那么 8 个 slave 的 TPS 应该在 4000 左右才是比较正确的。在看一下负压机的 cpu,竟然不是满负荷的,十分悠闲的分在 8 个 cpu 上,再看被测服务器这里的 cpu 和 一个 slave 进行压力测试数据差不多。

1.刚开始的时候我以为是因为我用容器启动 locust 的关系,然后在云主机上直接安装 locust,发现结果没有改变。所以就排除了容器的问题。
2.本来我是没有做测试接口 1 的,因为我觉得测试接口 2 的数据和我想的不一样,会不会应该延迟的关系,所以我才加了测试接口 1,但是加了以后,我发现测试接口 1 的数据和我预想的是一致的,那么在 locust 的使用上我没有出问题。
3.那么问题是出在哪里,还是我对压力测的概念没有理解透彻?
4.我现在在尝试用 jmeter 进行测试,这个非 GUI 的还真的要稍微花点时间,不知道在我能够在 liunx 上熟练使用 jmeter 的时候,是否有人能帮忙指点一下。

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
共收到 13 条回复 时间 点赞

性能瓶颈了

徐旻 #2 · August 27, 2018 Author
小小测试 回复

昨天我看了下论坛里的帖子,感觉是被测服务器这里的问题,不知道老兄你说的性能瓶颈具体是指哪里的瓶颈和什么样的瓶颈。谢谢。

看下网关、Nginx 这些有吗限制
可能压力在服务器层面已经限制了,导致压力到不了被测服务器

有可能是服务器的连接数瓶颈,加了 200ms 等待。导致请求连接无法快速释放,出现服务器大量等待线程。

先看下各个机器的资源又没到到瓶颈,一般的 CPU、内存、磁盘 IO、带宽。
这些没问题,再看压测机和被测机上的 TCP 端口是否已经满了,最后看代码层面,比如 java 的看下 jvm,还有就是数据库的连接数限制。

1、性能上不去无非两方面原因:压测服务器的性能问题、被压服务器的性能问题

(局域网环境,初步不考虑网络环境因素)

2、压测服务器性能分析:

初步分析得出结论 1: 8slave 测试接口 1(http://.../s/0ms/1k)的时候,压力服务器 CPU 资源达到瓶颈,服务 tps 性能应该大于 4794,可以增加压力服务器来测试服务器的真实性能;

分析过程如下:

8slave 测试接口 1(http://.../s/0ms/1k)的时候(算了一下,你的持续运行时间不到 90s,大概 75s):
测试接口 1 性能结果:tps 可以达到 4000,平均响应时间 152ms;
服务器资源情况:被测服务器资源无异常,压力服务器 8 核 CPU 全部满载,初步推测,随着你加压时间的增加,cpu load 会增加至一个稳定的数值;
接口 1 性能测试结论:

  • 被测服务器的单接口 1(http://.../s/0ms/1k)可支持 tps 性能大于 4794;
  • 压力服务器 CPU 资源出现瓶颈;

分析结论 2:压力服务器可以达到每秒发送 4000tps 的性能。

3、被压服务器性能分析

8slave 测试接口 2(http://.../s/200ms/1k)的时候,tps 上不去,初步认定为被压服务器的性能问题为:连接堵塞 + 响应时间过长。

压力服务器性能分析过程:根据第 2 部分可以看到,压力服务器最少可支持每秒发送 4000tps 的能力。
被压服务器连接数分析过程:

8slave 测试接口 1 的时候,被测服务器每核 cpu 均不到 50%,假设目前被测服务器的硬件资源没问题。
计算被测服务器支持的连接数:假设连接数为 n,接口平均响应时间为 t ms,
则每个连接数每秒可以处理的请求数为 x = 1000 / t;
tps = n * 1000 / t;
则,被测服务器的连接数 n = tps * t / 1000;

根据接口 1 测试结果,被压服务器连接数至少为:n = tps * t / 1000 = 4794 * 152 / 1000 >= 728;
8slave 测试接口 2 的时候,平均响应时间达到了:999ms,根据上面的公式 tps = n * 1000 / t,可以计算出理论上的 tps 应该>=728,但是实际测试结果 tps 为 990,说明 8slave 测试接口 1 时,没有达到被压服务器的最大性能。8slave 测试接口 2 的时候,达到了被压服务器的最大性能,原因是连接数过大或过小造成的请求连接堵塞 和 接口 2 响应时间过长;

真实连接数计算:8slave 测试接口 2 的时候,平均响应时间 999ms,tps990,根据公式: n = tps * t / 1000 = 990 * 999 / 1000 = 989,约等于 1000 连接数;

结论:

  • 结论 1: 8slave 测试接口 1(http://.../s/0ms/1k)的时候,压力服务器 CPU 资源达到瓶颈,服务 tps 性能应该大于 4794,可以增加压力服务器来测试服务器的真实性能;
  • 结论 2: 8slave 测试接口 2(http://.../s/200ms/1k)的时候,tps 上不去,初步认定为被压服务器的性能问题为:请求连接堵塞 + 响应时间过长。
  • 结论 3:当前服务器连接数设置为 1000

建议:

  • 建议 1: 8slave 测试接口 1(http://.../s/0ms/1k)的时候,增加压测服务器资源测试,或更换测试工具 locust 为 wrk(目前得知的硬件性能转化率最高的工具)
  • 建议 2:8slave 测试接口 2(http://.../s/200ms/1k)的时候,tps 上不去,可以上下调整连接数观察 tps 变化和响应时间变化,有可能因为连接堵塞导致的响应时间增大。
徐旻 #7 · August 27, 2018 Author

首先,谢谢大家给的意见和解决方案。太感谢。

楼主,弱弱的问下,你在一台机器上起了 8 个 slave?

简单看了描述,我这里总结及猜测一下,应该 8 9 不离十。
楼主觉得奇怪的地方:
为什么相同的接口实现,仅仅是加了 200ms 的延时,为什么总 TPS 就涨不上去了?
为此,你先搞压力端,排除了压力端的原因。
你接着分析服务器端,发现 CPU,内存,网络啥问题也没有,“基本排除” 了服务器端的问题。
表示奇怪。
你还在搞 Jmeter,打算换一种工具。可惜结果不会有变化。

其实不奇怪。
你 200ms 的延迟是加在了 Java 实现里,使用的是 new Thread().sleep(200), 此方法及其耗费 Java 线程资源。因为你是 spring boot 的自带启动,本身的线程总数就不多,还用了这么消耗线程资源的方式来实现延时,服务器性能会大大降低。
如果你能监控到 JVM 进程中的线程状态,会发现都是 block 或者 wait。
说白了,服务器性能不行,不是因为硬件资源不行,而是被你的代码强制的暂停住了。

解决方式:

  1. 200ms 的延迟放到压力端去实现,不要放在服务器端。
  2. 如果还是要压力端实现,把 spring boot 给换了,换成 tomcat 等其他,但记得调优 tomcat 等其他。

查了一下 Jmeter4 的源码,其中的停止也是 TimeUnit.MILLISECONDS.sleep(pause); 本质也是 Thread.sleep()。

后续楼主有没有得出结论?既然是测试接口应该很好搞。

一些排查原则

  1. 不管什么问题,首先怀疑是客户端的问题
  2. 排查顺序通常是:自己的脚本、设置、机器资源、网络带宽和设置、工具/框架选型本身
  3. 工具是 Locust 的跳过前 2 点,直接怀疑是它本身有问题,马上用另一款更高效的工具,类似的设置做对比。

这里不提服务端,因为我猜到这步就能结束了。

一个小实验

楼主如果有空可以写这么个简单的接口对比下各种测试工具:

  • 从收到第 1 个请求开始,前 100 秒正常响应,比如什么也不做,sleep 10*毫秒* 返回
  • 第 100 秒~第 200 秒,sleep 50** 返回,没看错是 50 秒
  • 之后又是 100 秒的正常响应,再接 100 秒 50 秒响应,如此反复

这就是识别 coordinated omission(CO)问题的典型方法,然后你看看各种工具的各个百分位数的统计结果,有多少能让你简单地发现这么明显的问题的,结果很可能会吓一跳。

除非你一下子就找到了某 2 款,通常不管开源还是商业还是自研都全军覆没。

(如果用这个作为首要选型标准,你就会像我一样把 JMeter、nGrinder、Locust、ab、siege 等等统统淘汰了 😆

Keith Mo 回复

大神,某 2 款是什么工具?你现在用啥工具?

你在用这个 locust 和 jmeter 的时候,有没有遇到过最终输出的结果 tps 和服务端的 cpu 相差特别大的时候, 我这边测试同一个的环境中的同一个登录接口。最终查看服务端 java cpu 使用率 2000%,而用 locust 的时候 ,1 个 slave 的时候 100% 左右,开了 4 个 slave,也就在 200%,按照这个比例,难道我要开 N 个 slave,才能达到有效的 cpu 占用率吗。如果这么计算的话,我想问下,这个 locust 适合什么样的场景和平台进行测试呢?

需要 Sign In 后方可回复, 如果你还没有账号请点击这里 Sign Up