极客时间已完结课程限时免费阅读

26丨案例:手把手带你理解TPS趋势分析

26丨案例:手把手带你理解TPS趋势分析-极客时间

26丨案例:手把手带你理解TPS趋势分析

讲述:高楼

时长24:19大小22.21M

在性能分析中,前端的性能工具,我们只需要关注几条曲线就够了:TPS、响应时间和错误率。这是我经常强调的。
但是关注 TPS 到底应该关注什么内容,如何判断趋势,判断了趋势之后,又该如何做出调整,调整之后如何定位原因,这才是我们关注 TPS 的一系列动作。
今天,我们就通过一个实际的案例来解析什么叫 TPS 的趋势分析。

案例描述

这是一个案例,用一个 2C4G 的 Docker 容器做服务器。结构简单至极,如下所示:
当用个人电脑(上图中压力工具 1)测试云端服务器时,达到 200 多 TPS。但是当用云端同网段压力机(上图中压力工具 2)测试时,TPS 只有 30 多,并且内网压力机资源比本地压力机要高出很多,服务器资源也没有用完。
在这样的问题面前,我通常都会有一堆的问题要问。
现象是什么?
脚本是什么?
数据是什么?
架构是什么?
用了哪些监控工具?
看了哪些计数器?
在分析之前,这些问题都是需要收集的信息,而实际上在分析的过程中,我们会发现各种数据的缺失,特别是远程分析的时候,对方总是不知道应该给出什么数据。
我们针对这个案例实际说明一下。
这个案例的现象是 TPS 低,资源用不上。
下面是一个 RPC 脚本的主要代码部分。
public SampleResult runTest(JavaSamplerContext arg0) {
// 定义results为SampleResult类
SampleResult results = new SampleResult();
// 定义url、主机、端口
String url = arg0.getParameter("url");
String host = arg0.getParameter("host");
int port = Integer.parseInt(arg0.getParameter("port"));
results.sampleStart();
try {
message=detaildata_client.detaildata_test(url);// 访问URL并将结果保存在message中
System.out.println(message); //打印message,注意这里
results.setResponseData("返回值:"+ message, "utf-8");
results.setDataType(SampleResult.TEXT);
results.setSuccessful(true);
} catch (Throwable e) {
results.setSuccessful(false);
e.printStackTrace();
} finally {
String temp_results=results.getResponseDataAsString();
results.setResponseData("请求值:"+arg0.getParameter("url")+"\n"+"返回值:"+temp_results, "utf-8");
results.sampleEnd();
}
return results;
JMeter 脚本关键部分:
<stringProp name="ThreadGroup.num_threads">100</stringProp>
//我们来看这里,ramp_time只有1秒,意味着线程是在1秒内启动的,这种场景基本上都和真实的生产场景不相符。
<stringProp name="ThreadGroup.ramp_time">1</stringProp>
<boolProp name="ThreadGroup.scheduler">true</boolProp>
<stringProp name="ThreadGroup.duration">300</stringProp>
...............
<CSVDataSet guiclass="TestBeanGUI" testclass="CSVDataSet" testname="CSV Data File Config" enabled="true">
<stringProp name="delimiter">,</stringProp>
<stringProp name="fileEncoding">utf-8</stringProp>
<stringProp name="filename">filename</stringProp>
<boolProp name="ignoreFirstLine">false</boolProp>
<boolProp name="quotedData">false</boolProp>
<boolProp name="recycle">true</boolProp>
<stringProp name="shareMode">shareMode.all</stringProp>
<boolProp name="stopThread">false</boolProp>
<stringProp name="variableNames">url</stringProp>
</CSVDataSet>
在这个脚本中,逻辑非常简单,一个 RPC 接口:1. 发出请求;2. 返回响应;3. 打印返回信息。
本机跑出来的结果如下:
在这个案例中,参数化数据就是根据真实的业务量来计算的,这个可以肯定没有问题。
那么架构呢?在最上面的图中已经有了部署的说明。在逻辑实现上,也就是一个很简单的服务端,内部并没有复杂的逻辑。所用到的监控工具是 top、Vmstat。
看了哪些计数器呢?CPU、内存、I/O 等。
下面我们开始分析。

第一阶段

对公网上的测试来说,基本上压力都会在网络上,因为出入口带宽会成为瓶颈,所以先要看一眼自己的带宽用到了多少,再比对一下出口路由上的带宽。
这里 1Gbps 只用到了 0.01%,也就是 (1000/8)x0.01%=12.5k(这里是将带宽 bit 换成 byte 计算)。
在这样的带宽使用率之下,即使是公网也不见得会有问题,更别说在内网了。可见带宽不是瓶颈点。
既然这样,我们直接在内网里来做分析,看原因是什么。
但是我们要以什么样的场景来跑呢?因为带宽现在看到用得并不多,但 TPS 也上不去。首先应该想到的场景就是把 TPS 曲线给做出梯度来。
为什么要这么做?最重要的就是要知道到底 TPS 在多少压力线程下会达到最大值,也就是我在各种场合经常强调的一个场景,最大 TPS 场景。关于这种曲线,我们不需要性能指标应该就可以做得出来。如下图所示:
在一个既定场景、既定数据、既定环境的压力场景中,我们一定要拿到这样趋势的 TPS 和 RT 曲线。其中绿色和红色的点都是不需要业务指标来限定的,而是通过压力场景中观察 TPS 趋势线来确定。
我来解读一下这个趋势图:
响应时间一定是从低到高慢慢增加的;
TPS 一定也是从低到高慢慢增加的,并且在前面的梯度中,可以和线程数保持正比关联。举例来说,如果 1 个线程 TPS 是 10,那 2 个线程的 TPS 要在 20。依次类推。
而在这个例子中,前面有提到 100 线程 1 秒加载完,这样的比例完全看不出来梯度在哪,所以,改为 100 秒加载 100 个线程,再来看看梯度。
测试结果如下:
从这个结果可以看出几点:
1.TPS 一点梯度没看出来。为什么说没有看出来呢?这里我发一个有明显梯度的 TPS 曲线出来以备参考(这张图不是本实例中的,只用来做分析比对):
2. 响应时间增加的太快了,明显不符合前面我们说的那个判断逻辑。那什么才是我们判断的逻辑呢?这里我发一个有明显梯度的出来以备参考(这张图不是本实例中的,只用来做分析比对):
粒度太粗,对一个 duration 只有五分钟的场景来说,这样的粒度完全看不出过程中产生的毛刺。
至少看到内网的 TPS 能到 180 了,但是这里没有做过其他改变,只是把 Ramp-up 放缓了一些,所以我觉得这个案例的信息是有问题的。

第二阶段

针对以上的问题,下面要怎么玩?我们列一下要做的事情。
将 Ramp-up 再放缓,改为 300 秒。这一步是为了将梯度展示出来。
将粒度改小,JMeter 默认是 60 秒,这里改为 1 秒。这一步是为了将毛刺显示出来。强调一点,如果不是调优过程,而是为了出结果报告的话,粒度可以设置大一些。至于应该设置为多大,完全取决于目标。
接着我们再执行一遍,看看测试结果:
这样看下来,有点意思了哈。明显可以看到如下几个信息了。
响应时间随线程数的增加而增加了。
TPS 的梯度还是没有出来。
显然还是没有达到我们说的梯度的样子。但是这里我们可以看到一个明显的信息,线程梯度已经算是比较缓的了,为什么响应时间还是增加得那么快?
这里的服务器端压力情况呢?如下所示:
从监控图大概看一下,服务端 CPU、内存、网络几乎都没用到多少,有一种压力没有到服务端的感觉。
在这一步要注意,压力在哪里,一定要做出明确的判断。
在这里,当我们感觉服务端没有压力的时候,一定要同时查看下网络连接和吞吐量、队列、防火墙等等信息。查看队列是非常有效的判断阻塞在哪一方的方式。
如果服务端的 send-Q 积压,那就要查一下压力端了。如下所示:
State Recv-Q Send-Q Local Address:Port Peer Address:Port
......
LISTEN 0 54656 :::10001 :::*
......
在网络问题的判断中,我们一定要明确知道到底在哪一段消耗时间。我们来看一下发送数据的过程:
从上图可以看出,发送数据是先放到tcp_wmem缓存中,然后通过tcp_transmit_skb()放到TX Queue 中,然后通过网卡的环形缓冲区发出去。而我们看到的 send-Q 就是 Tx 队列了。
查看压力端脚本,发现一个问题。
System.out.println(message);
一般情况下,我们在调试脚本的时候会打印日志,因为要看到请求和响应都是什么内容。但是压力过程中,基本上我们都会把日志关掉。一定要记住这一点,不管是什么压力工具,都要在压力测试中把日志关掉,不然 TPS 会受到很严重的影响
了解 JMeter 工具的都知道 -n 参数是命令行执行,并且不打印详细的返回信息的。但是这里,一直在打印日志,并且这个日志在 JMeter 中执行时加了 -n 参数也是没用的。
这样一来,时间全耗在打印日志中了。知道这里就好办了。我们在这里做两件事:
把打印日志这一行代码注释掉,再执行一遍。
把 ramp-up 时间再增加到 600 秒。
为什么我要执着于把 ramp-up 时间不断增加?在前面也有强调,就是要知道 TPS 和响应时间曲线的趋势。
在性能分析的过程中,我发现有很多性能工程师都是看平均值、最大值、最小值等等这些数据,并且也只是描述这样的数据,对曲线的趋势一点也不敏感。这是完全错误的思路,请注意,做性能分析一定要分析曲线的趋势,通过趋势的合理性来判断下一步要做的事情。
什么叫对曲线的趋势敏感?就是要对趋势做出判断,并且要控制曲线的趋势。
有时,我们经常会看到 TPS 特别混乱的曲线,像前面发的 TPS 图一样,抖动幅度非常大,这种情况就是完全不合理的,在遇到这种情况时,一定要记得降低压力线程。
你可能会问,降到多少呢?这里会有一个判断的标准,就是一直降到 TPS 符合我们前面看到的那个示意图为止
再给你一个经验,如果实在不知道降多少,就从一个线程开始递增,直到把梯度趋势展示出来。

第三阶段

通过注释掉打印日志的代码,可以得到如下结果:
从TPS 曲线上可以看到,梯度已经明显出来了。在有一个用户的时候,一秒就能达到 1000 多 TPS,并且在持续上升;两个线程时达到 2500 以上,并且也是在持续上升的。
从响应时间上来看,也是符合这个趋势的,前面都在 1ms 以下,后面慢慢变长。
压力越大,曲线的毛刺就会越多,所以在 TPS 达到 6000 以上后,后面的 TPS 在每增加一个线程,都会出现强烈的抖动。
在这种情况下,我们再往下做,有两条路要走,当然这取决于我们的目标是什么。
接着加压,看系统什么时候崩溃。做这件事情的目标是找到系统的崩溃点,在以后避免出现。
将线程最大值设置为 10,增加 ramp up 的时间,来看一下更明确的递增梯度,同时分析在线程增加过程中,系统资源分配对 TPS 的影响,以确定线上应该做相对应的配置。

总结

在这个案例中,我们将 TPS 从 150 多调到 6000 以上,就因为一句日志代码。
我分析过非常多的性能案例,到最后发现,很多情况下都是由各种简单的因素导致的,这一反差也会经常让人为这一路分析的艰辛不值得。
但我要说的是,性能分析就是这样,当你不知道问题在哪里的时候,有一个思路可以引导着你走向最终的原因,那才是最重要的。
我希望通过本文可以让你领悟到,趋势这个词对曲线分析的重要性。在本文中,我们通过对曲线的不合理性做出判断,你需要记住以下三点:
性能分析中,TPS 和响应时间的曲线是要有明显的合逻辑的趋势的。如果不是,则要降线程,增加 Ramp-up 来让 TPS 趋于平稳。
我们要对曲线的趋势敏感,响应时间的增加不可以过于陡峭,TPS 的增幅在一开始要和线程数对应。
当 TPS 和响应时间曲线抖动过于强烈,要想办法让曲线平稳下来,进而分析根本原因,才能给出线上的建议配置。

思考题

今天我结合案例具体说明了下如何分析 TPS 的趋势,如果你吸收了文章的内容,不妨思考一下这两个问题?
Ramp-up 配置有什么样的作用?
为什么说压力工具中 TPS 和响应时间曲线抖动过大会不易于分析?
欢迎你在评论区写下你的思考,也欢迎把这篇文章分享给你的朋友或者同事,一起学习交流一下。
分享给需要的人,Ta购买本课程,你将得18
生成海报并分享

赞 8

提建议

上一篇
25丨SkyWalking:性能监控工具之链路级监控及常用计数器解析
下一篇
27丨案例:带宽消耗以及Swap(上)
unpreview
 写留言

精选留言(28)

  • 大拇哥
    2020-02-22
    Ramp-up 配置有什么样的作用? 答:Ramp-up 配置的时间是指启动所有配置的线程总数所用的时间,例如设置的线程总数为500,Ramp-up设置的时间为50s,意为:启动500个线程数需要50s,平均为每一秒启动10个线程 为什么说压力工具中 TPS 和响应时间曲线抖动过大会不易于分析? 答:性能分析一定要分析曲线的趋势,通过趋势的合理性来判断性能瓶颈所在的原因,光靠平均值、最大值、最小值、中位数是无法确切的分析处压测过程中服务器的具体情况,只有通过分析曲线趋势,增加对趋势的敏感程度才是压测过程中更好的保障和前提。
    展开

    作者回复: 理解到位,已经出师。

    12
  • Geek_6a9aeb
    2021-02-03
     老师,你这里压力工具1是公网,压力工具2是内网,为何内网的TPS反而低,公网的反而快,按普通理解内网的网速是最快的啊,为何网速快反而TPS低?

    作者回复: 因为内网TPS上的快,而场景又设置的线程增加太快,所以导致了tps看起来是低的,实际上已经是压死之后的状态了。

    7
  • Geek_6a9aeb
    2021-01-30
    这篇文章给人没有写完整的感觉,最好TPS上去了,也没提服务端有没积压的数据,以及cpu 内存这些大的指标有没提升? 实际操作很难得到完美的TPS幅度曲线,都有一点波动, 还有后面TPS曲线有了之后,就没有完整的描述到定位到是打印耗费时间多?如果是有经验的人可能会一下子定位到打印问题

    作者回复: 看下一个专栏吧。会有相似的案例。

    共 2 条评论
    4
  • 蔡森冉
    2020-03-25
    今天好像明白一些东西,发现自己之前好像是一直是错的(好像一直理解这个都错了),就是Ramp-up是启动所有线程的时间,但是只是启动,实际上所有线程启动后压力时间是Duration,可以这样理解吗?然后tps抖动过大,那其实中间值这些数据就是完全没有意义的。

    作者回复: duration包括rampup。 抖动的tps就是要分析的内容,不是没意义。

    4
  • Geek_6a9aeb
    2021-01-30
    第三阶段 通过注释掉打印日志的代码,可以得到如下结果: 仔细看这个阶段第二个图的标识,不是TPS,而是hit s,这有点偷换概念了吧?

    作者回复: 在每个http request为一个T的情况下,这是同样的含义。

    共 2 条评论
    3
  • Geek_6a9aeb
    2021-01-30
    当用个人电脑(上图中压力工具 1)测试云端服务器时,达到 200 多 TPS。但是当用云端同网段压力机(上图中压力工具 2)测试时,TPS 只有 30 多,并且内网压力机资源比本地压力机要高出很多,服务器资源也没有用完。 老师,下面又说是日志打印, 那最先的TPS差异是因为打印吗?

    作者回复: 这里有两个问题:1,是在内网测试时网络快,所以资源使用的会高;2,在本地压力机里由于网络不够快,日志打印的问题没有突显出来,在内网中才体现出来。

    共 2 条评论
    3
  • zwm
    2020-03-06
    Ramp-up 配置有什么样的作用?为什么说压力工具中 TPS 和响应时间曲线抖动过大会不易于分析? (1)ramp-up就是线程启动时间 。比如10个线程120s基本就是 梯度加压 5个线程运行60s之后再启动50线程运行,是否停止要看是否设置循环等,是一个等比例的梯度加压。 (2) 曲线抖动过大,可以肯定会有异常,这个题确实不会做

    作者回复: 曲线抖动大了,不容易分析异常和趋势之间的关系。

    3
  • 涵涵
    2021-07-30
    老师,请教2个问题 1、参数化数据的时候是读取文件比较合适,还是读取数据库数据比较合适? 2、BeanShell取样器中对请求数据进行加签加密处理,会对测试结果有影响吗?

    作者回复: 1. 看数据量,太大的参数化数据放在文件中读取慢的时候,就要考虑用数据库或缓存了。 2. 会有影响。不过根据业务逻辑,如果客户端也是必须做加签加密的,也是必须要在脚本中处理的。这也是符合真实业务逻辑的。

    共 3 条评论
    2
  • 🌻Naomi
    2020-03-03
    TPS的趋势是否应该跟线上流量分布一致?

    作者回复: 应该,这个说法方向最正确。

    2
  • 吴小喵
    2020-02-19
    老师,怎么计算压测服务器已使用的带宽可以详细说一下吗

    作者回复: 没明白这个问题是什么已使用的带宽不需要计算,直接用命令就能看。比如说iftop。

    2
  • Geek_admin
    2022-01-22
    请教几个问题: 1. 我看留言中有说梯度压测是通过Ramp-up来实现 但网上普遍再说用Stepping Thread Group插件来做,这两者哪种更合适? 2. 另外Ramp-up假如是10s内启动10个线程,那就是平均1s启动1个 对于接口而言,请求到拿到返回的时间是较短的,那么前1秒请求的接口已经返回了,第二个10秒也只会有一个请求,这时候等于每1s内只有一个请求,TPS曲线应该不会存在梯度?
    展开

    作者回复: 1,都可以实现。但我推荐用前者,根据我的经验,插件本身的性能就不好。 2,启动了之后还会持续的,不能停下哦。

    1
  • cathy
    2020-08-04
    老师好,老师讲的都是通过压出梯度来确定并发数,想跟老师确认一下以下做法是正确的吗:使用jmeter,然后设置集合点(同步定时器),大量的线程数(几百个),循环次数设为1,来做瞬间一次性并发,看看系统能不能承受住,如果没报错并且响应时间可接受,就说可以支持几百个并发

    作者回复: 不正确。

    1
  • 小破鸟儿
    2020-07-10
    老师我看输出message的脚本是在RPC里,是说服务端的接口代码中不要有打印信息的代码是吗,不是说的jmeter工具吧?而且后面也说,不管是什么压力工具,都要在压力测试中把日志关掉,关的都是服务端代码里的吧。

    作者回复: 这里的日志是说的jmeter工具中的日志。服务端的日志应该根据生产环境的配置来配置测试环境。

    1
  • Geek_66dcc6
    2020-04-24
    问题1.Ramp-up 配置有什么样的作用 Ramp-up 时间长度决定了启动所有线程数所用的时间,如果ramp-up 时间过短,就不能看到阶梯型的TPS 和RT 时间的变化过程,不利于分析TPS 和RT,同时有可能数据不可信。调整ramp-up的值, 到能看到RPS和RT的趋势变化。 问题2:为什么说压力工具中 TPS 和响应时间曲线抖动过大会不易于分析 分析TPS 和RT 的曲线是基础数据,如果基础数据抖动过大,那分析结果也会不完美。所以要尽量找到原因拿到完美的阶梯上升曲线。
    展开

    作者回复: 理解的很好。

    1
  • 不将就
    2020-04-19
    老师,请问文章中提到的Send-Q大,是因为服务端网卡往压力机写数据需要排队导致的吗?如果客户端IO大,服务端就会等待,导致Send-Q积压大,我这样理解有问题吗?

    作者回复: 没问题。

    共 3 条评论
    1
  • 追风筝的人
    2021-12-21
    在性能分析中,前端的性能工具,我们只需要关注几条曲线就够了:TPS、响应时间和错误率。

    作者回复: 对的。

  • 七月的雨
    2021-12-08
    之前碰到过开了日志之后tps比较低,但我理解的是压力发起机的问题,因为读取不到所有的数据,而实际的tps在内网肯定会比公网高,这样理解对不。

    作者回复: 打开日志后tps低应该去看打开日志的机器上的资源消耗,应该不是压力机的问题哦。

  • jy
    2021-07-10
    1.文中说“将粒度改小,JMeter 默认是 60 秒,这里改为 1 秒”,请问这是在哪里改? 2.看了队列,发送队列大,这个是什么原因呢?这里没动,后面就转到了客户端压力工具了。 请老师解答下,谢谢
    展开

    作者回复: 1,配置文件。 2,发送队列大,所以要看对端。

  • 一步
    2021-01-17
    响应时间 和 PTS 的曲线趋势 必要要有明显的梯度趋势吗? 翻了一下之前的阿里云PTS压测报告,好像都没有出现明显的梯度趋势,刚开始都是斜线上升的

    作者回复: 递增连续是场景最基本的要求。要想分析性能,没有趋势是很难判断合理不合理的。 如果你只是做下验证,那就无所谓了。

    共 2 条评论
  • windy
    2020-11-27
    Ramp-up 配置的调整,可以使tps的趋势变缓或者变抖。请问老师,经过参数调整,趋势图合理后,是用趋势图中的最大线程数进行后续的测试吗?如果测试目标是找到系统最大的tps的话。

    作者回复: 不是,直接用递增的就可以。