手把手教你编写最简单的性能脚本

时间:2024-02-01 10:00:29

在脚本实现中,我们最常用的协议就是 HTTP 和 TCP 了吧,所以在今天的内容里,我简单地说一下如何编写 HTTP 和 TCP 脚本,以应测试主题。

先上图

 

 

我们知道 HTTP 是应用层的协议之一,现在很多场景都在用它,并且是用的 HTTP1.1 的版本,对应的是 RFC2616,当然还有补充协议 RFC7231、6265。

还有一点也需要注意,HTTP 是通过 Socket 来使用 TCP 的,Socket 做为套接层 API,它本身不是协议,只规定了 API。

而我们通常在 JMeter 中写 TCP 脚本,就是直接调用 Socket 层的 API。TCP 脚本和 HTTP 脚本最大的区别就是,TCP 脚本中发送和接收的内容完全取决于 Socket server 是怎么处理的,并没有通用的规则。所以脚本中也就只有根据具体的项目来发挥了。

这个接口的访问逻辑:JMeter——SprintBoot 的应用——MySQL。

 

 

 编写 JMeter 脚本

1、创建线程组

 Ramp-up Period(in seconds):递增时间,以秒为单位。指的就是上面配置的线程数将在多长时间内会全部递增完。如果我们配置了 100 线程,这里配置为 10 秒,那么就是 100/(10s*1000ms)=1 线程 /100ms;如果我们配置了 10 线程,这里配置为 1 秒,则是 10/1000=1 线程 /100ms。这时我们要注意了哦,在 10 线程启动的这个阶段中,对服务器的压力是一样的。示意图如下:

 Loop Count 这个值指的是一个线程中脚本迭代的次数。这里你需要注意,这个值和后面的 Scheduler 有一个判断关系,下面我们会提到。

Delay Thread creation until needed:这个含义从字面看不是特别清楚。这里有一个默认的知识点,那就是 JMeter 所有的线程是一开始就创建完成的,只是递增的时候会按照上面的规则递增。如果选择了这个选项,则不会在一开始创建所有线程,只有在需要时才会创建。这一点和 LoadRunner 中的初始化选项类似。只是不知道你有没有注意过,基本上,我们做性能测试的工程师,很少有选择这个选项的。选与不选之间,区别到底是什么呢?

如果不选择,在启动场景时,JMeter 会用更多的 CPU 来创建线程,它会影响前面的一些请求的响应时间,因为压力机的 CPU 在做其他事情嘛。

如果选择了的话,就会在使用时再创建,CPU 消耗会平均一些,但是这时会有另一个隐患,就是会稍微影响正在跑的线程。这个选项,选择与否,取决于压力机在执行过程中,它能产生多大的影响。如果你的线程数很多,一旦启动,压力机的 CPU 都被消耗在创建线程上了,那就可以考虑选择它,否则,可以不选择。

即便设置了 Scheduler 的 Duration 为 100 秒,线程仍然会以 10 秒为结束点。

有些人不太理解这一点,经常会设置迭代次数,同时又设置 Scheduler 中的 Duration。而对 TPS 来说,就会产生这样的图:

 

场景没执行完,结果 TPS 全掉下去了,于是开始查后端系统,其实和后端没有任何关系。

 

 2、创建 HTTP Sampler

这个图片可以表示成功吗?

不是的,业务的成功,只能靠业务来判断。这里只是查询成功了,没返回数据也是查询成功了。

POST 接口

下面我将 Method 改为 POST,POST 接口与 GET 接口的区别有这么几处:要把 Path 改为 /pa/add;输入 JSON 格式的 Body Data。

 执行起来,查看下结果。

 

 

 报错了,先看懂问题,再处理问题,别瞎蒙!

上面这个问题其实提示得很清楚:“不支持的媒体类型”。这里就两个信息,一个是 Content type,一个是 charset。它们是 JMeter 中 HTTP Header 里默认自带的。我们要发送的是 JSON 数据,而 JMeter 默认是把它当成 text 发出去的,这就出现了问题。所以我们要加一个 Header,将 Content type 指定为 JSON。

加一个 HTTP Header,如下所示:

 在这里,我需要跟你强调的是,手工编写 HTTP 脚本时,要注意以下几点:

要知道请求的类型,我们选择的类型和后端接口的实现类型要是一致的。

业务的成功要有明确的业务判断(在下面的 TCP 中,我们再加断言来判断)。

判断问题时,请求的逻辑路径要清晰。

编写完 HTTP 脚本时,我们再来看一下如何编写 TCP 脚本。

手工编写 TCP 脚本

首先创建 TCP Sampler。右键点击 Thread Group - Add - Sampler - TCP Sampler 即可创建。

 

 

 输入配置和要发送的信息。

IP 地址和端口是必须要输入的。对于创建一个 TCP 协议的 JMeter 脚本来说,简单地说,过程就是这样的:创建连接 - 发数据 - 关闭连接。

但是,通常我们在创建 TCP 协议的脚本时,都是根据业务接口规范来说的,复杂点其实不在脚本本身上,而是在接口的规则上。

添加断言

我回放了一下脚本,发现如下情况:

 都执行对了呀,为什么下面的没有返回信息呢?这种情况下只有第一个请求有返回信息,但是下面也没有报错。这里就需要注意了。

测试工具的成功,并不等于业务的成功。

所以我们必须要做的就是响应断言,也就是返回值的判断。在 JMeter 中,断言有以下这些:

 

 

 什么是断言呢?

 

 

 断言指的就是服务器端有一个业务成功的标识,会传递给客户端,客户端判断是否正常接收到了这个标识的过程。

 

 

 在这里我添加了一个断言,用以判断服务器是否返回了 OK。 你要注意这个“OK”是从哪来的哦,它是从服务端的这一行代码中来的。 String response = message + " is OK";

请注意,这个断言的信息,一是可以判断出业务的正确性。我在工作中发现有些人用页面中一些并不必要的文字来判断,这样就不对了,我们应该用有业务含义的判断标识。

如果我们再次回放脚本,你会发现除了第一个请求,后面 9 个请求都错了。

所以,在做脚本时,请你一定要注意,断言是必须要加的。

长短连接的问题

我们查看一下 JMeter 的控制台错误信息:

 ERROR o.a.j.p.t.s.TCPSampler: 
java.net.SocketException: Broken pipe (Write failed)
  at java.net.SocketOutputStream.socketWrite0(Native Method) ~[?:1.8.0_111]
  at java.net.SocketOutputStream.socketWrite(SocketOutputStream.java:109) ~[?:1.8.0_111]
  at java.net.SocketOutputStream.write(SocketOutputStream.java:141) ~[?:1.8.0_111]
  at org.apache.jmeter.protocol.tcp.sampler.TCPClientImpl.write(TCPClientImpl.java:78) ~[ApacheJMeter_tcp.jar:5.1.1 r1855137]
  at org.apache.jmeter.protocol.tcp.sampler.TCPSampler.sample(TCPSampler.java:401) [ApacheJMeter_tcp.jar:5.1.1 r1855137]
  at org.apache.jmeter.threads.JMeterThread.doSampling(JMeterThread.java:622) [ApacheJMeter_core.jar:5.1.1 r1855137]
  at org.apache.jmeter.threads.JMeterThread.executeSamplePackage(JMeterThread.java:546) [ApacheJMeter_core.jar:5.1.1 r1855137]
  at org.apache.jmeter.threads.JMeterThread.processSampler(JMeterThread.java:486) [ApacheJMeter_core.jar:5.1.1 r1855137]
  at org.apache.jmeter.threads.JMeterThread.run(JMeterThread.java:253) [ApacheJMeter_core.jar:5.1.1 r1855137]
  at java.lang.Thread.run(Thread.java:745) [?:1.8.0_111]

Broken pipe。这个提示表明客户端上没有这个连接了,而 JMeter 还以为有这个链接,于是接着用这个链接来发,显然是找不到这个通道,于是就报错了。

为什么会报这个错呢?因为我们代码是短链接的,服务端处理完之后,就把这个链接给断掉了。

这是为什么呢?因为在 JMeter 中,默认是复用 TCP 连接的,但是在我们这个示例中,服务端并没有保存这个连接。所以,我们应该在脚本中,把下图中的 Re-use connection 给去掉。

 

 这时再回放脚本,你就会发现 10 次迭代全都对了。如下图所示:

 

 但是,这里还有一个知识点,希望你注意。短连接的时候,必然会产生更多的 TCP 连接的创建和销毁,对性能来说,这会让系统变得缓慢。

所以你可以看到上面 10 条迭代全都对了的同时,响应时间也增加了。

长短连接的选择取决于业务的需要,如果必须用短链接,那可能就需要更多的 CPU 来支撑;要是长连接,就需要更多的内存来支撑(用以保存 TCP 连接)。

TCP 连接超时

下面这个错误,属于典型的主机连不上。

java.net.ConnectException: Operation timed out (Connection timed out)
  at java.net.PlainSocketImpl.socketConnect(Native Method) ~[?:1.8.0_111]
  at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:350) ~[?:1.8.0_111]
  at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:206) ~[?:1.8.0_111]
  at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:188) ~[?:1.8.0_111]
  at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392) ~[?:1.8.0_111]
  at java.net.Socket.connect(Socket.java:589) ~[?:1.8.0_111]
  at org.apache.jmeter.protocol.tcp.sampler.TCPSampler.getSocket(TCPSampler.java:168) [ApacheJMeter_tcp.jar:5.1.1 r1855137]
  at org.apache.jmeter.protocol.tcp.sampler.TCPSampler.sample(TCPSampler.java:384) [ApacheJMeter_tcp.jar:5.1.1 r1855137]
  at org.apache.jmeter.threads.JMeterThread.doSampling(JMeterThread.java:622) [ApacheJMeter_core.jar:5.1.1 r1855137]
  at org.apache.jmeter.threads.JMeterThread.executeSamplePackage(JMeterThread.java:546) [ApacheJMeter_core.jar:5.1.1 r1855137]
  at org.apache.jmeter.threads.JMeterThread.processSampler(JMeterThread.java:486) [ApacheJMeter_core.jar:5.1.1 r1855137]
  at org.apache.jmeter.threads.JMeterThread.run(JMeterThread.java:253) [ApacheJMeter_core.jar:5.1.1 r1855137]
  at java.lang.Thread.run(Thread.java:745) [?:1.8.0_111]

要想解决这个问题,就要先确定服务端是可以正常连通的。

如果不能正常连通,那么通常都是 IP 不正确、端口不正确、防火墙阻止之类的问题。解决了网络连通性的问题,就可以解决 connection timed out 的问题。

总结

其实这篇文章只想告诉你一件事情,手工编写脚本,从基础上说,是非常简单的,只是有三点需要特别强调:

1、涉及到业务规则和逻辑判断之后,编写脚本就复杂了起来。但是了解业务规则是做脚本的前提条件,也是性能测试工程师的第一步。

2、编写脚本的时候,要知道后端的逻辑。这里的意思不是说,你一开始写脚本的时候,就要去读后端的代码,而是说你在遇到问题的时候,要分析整个链路上每个环节使用到了什么技术,以便快速地分析判断。

3、写脚本是以最简为最佳,用不着故意复杂。