压测工具

一、wrk

wrk 只能运行在 Unix 类的系统上. 比如 linux, mac, solaris 等. 也只能在这些系统上编译.  wrk 的一个很好的特性就是能用很少的线程压出很大的并发量. 原因是它使用了一些操作系统特定的高性能 io 机制, 比如 select, epoll, kqueue 等. 其实它是复用了 redis 的 ae 异步事件驱动框架.  确切的说 ae 事件驱动框架并不是 redis 发明的, 它来至于 Tcl的解释器 jim, 这个小巧高效的框架, 因为被 redis 采用而更多的被大家所熟知. 

CentOS环境下安装wrk

sudo yum groupinstall 'Development Tools'
sudo yum install openssl-devel
sudo yum install git
git clone https://github.com/wg/wrk.git wrk
cd wrk
make
# 把生成的wrk移到一个PATH目录下面, 比如
sudo cp wrk /usr/local/bin

基本压测

wrk -t12 -c400 -d30s http://127.0.0.1:8080/index.html

使用12个线程运行30秒, 400个http并发

命令行选项

-c, --connections: 总的http并发数

-d, --duration:    持续压测时间, 比如: 2s, 2m, 2h

-t, --threads:     总线程数(一般线程数不宜过多,核数的2到4倍足够,wrk不是使用每个连接一个线程的模型,而是通过异步网络io提升并发量,所以网络通信不会阻塞线程执行)

-T, --timeout:     设置http超时时间(默认是1S)

-s, --script:      lua脚本,使用方法往下看

-H, --header:      添加http header, 比如. "User-Agent: wrk"

    --latency:     在控制台打印出延迟统计情况

    --timeout:     http超时时间

返回结果

Latency:响应时间
Req/Sec:每个线程每秒钟的执行的连接数
Avg:平均
Max:最大
Stdev:标准差
+/- Stdev: 正负一个标准差占比
Requests/sec:每秒请求数(也就是QPS),这是一项压力测试的性能指标,通过这个参数可以看出吞吐量
Latency Distribution,如果命名中添加了—latency就会出现相关信息

lua脚本压测

在基本压测中, 每次发送的请求都是一样的,很多时候我们压测的请求体是每个请求都不一样, 这时候就要写lua基本来压测

压测JSON格式的POST接口示例

updateUser.lua(wrk.body后的数据,最好放在同一行内)

-- example HTTP POST script which demonstrates setting the
-- HTTP method, body, and adding a header

wrk.method = "POST"
wrk.body = "{\"header\": {\"requestTime\":\"2018120910320000\", \"requestSeq\": \"e379853d-d93e-4249-b9be-8a272b\"}, \"content\": {\"id\": 1 }}"
wrk.headers["Content-Type"] = "application/json"

命令行执行

 wrk -t12 -c100 -d30s -T2s -s updateUser.lua --latency http://127.0.0.1:9100/updateUser

30秒后,看到压测的结果

Running 30s test @ http://127.0.0.1:9100/updateUser
  12 threads and 100 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   219.49ms  145.69ms   1.00s    59.02%
    Req/Sec    38.30     18.40   110.00     66.07%
  Latency Distribution
     50%  211.10ms
     75%  281.05ms
     90%  432.63ms
     99%  620.00ms
  13629 requests in 30.10s, 3.63MB read
Requests/sec:    452.78
Transfer/sec:    123.42KB

二、jmeter

updateUser.jmx

<?xml version="1.0" encoding="UTF-8"?>
<jmeterTestPlan version="1.2" properties="4.0" jmeter="4.0 r1823414">
  <hashTree>
    <TestPlan guiclass="TestPlanGui" testclass="TestPlan" testname="测试计划" enabled="true">
      <stringProp name="TestPlan.comments"></stringProp>
      <boolProp name="TestPlan.functional_mode">false</boolProp>
      <boolProp name="TestPlan.tearDown_on_shutdown">true</boolProp>
      <boolProp name="TestPlan.serialize_threadgroups">false</boolProp>
      <elementProp name="TestPlan.user_defined_variables" elementType="Arguments" guiclass="ArgumentsPanel" testclass="Arguments" testname="用户定义的变量" enabled="true">
        <collectionProp name="Arguments.arguments"/>
      </elementProp>
      <stringProp name="TestPlan.user_define_classpath"></stringProp>
    </TestPlan>
    <hashTree>
      <ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="线程组" enabled="true">
        <stringProp name="ThreadGroup.on_sample_error">continue</stringProp>
        <elementProp name="ThreadGroup.main_controller" elementType="LoopController" guiclass="LoopControlPanel" testclass="LoopController" testname="循环控制器" enabled="true">
          <boolProp name="LoopController.continue_forever">false</boolProp>
          <stringProp name="LoopController.loops">1</stringProp>
        </elementProp>
        <stringProp name="ThreadGroup.num_threads">10</stringProp>
        <stringProp name="ThreadGroup.ramp_time">1</stringProp>
        <boolProp name="ThreadGroup.scheduler">false</boolProp>
        <stringProp name="ThreadGroup.duration"></stringProp>
        <stringProp name="ThreadGroup.delay"></stringProp>
      </ThreadGroup>
      <hashTree>
        <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="updateUser" enabled="true">
          <boolProp name="HTTPSampler.postBodyRaw">true</boolProp>
          <elementProp name="HTTPsampler.Arguments" elementType="Arguments">
            <collectionProp name="Arguments.arguments">
              <elementProp name="" elementType="HTTPArgument">
                <boolProp name="HTTPArgument.always_encode">false</boolProp>
                <stringProp name="Argument.value">{&#xd;
  &quot;header&quot;: {&#xd;
  	&quot;requestTime&quot;: &quot;2018100910320000&quot;,&#xd;
    &quot;requestSeq&quot;: &quot;e379853d-d93e-4249-b9be-8a272b&quot;&#xd;
  },&#xd;
  &quot;content&quot;: {&#xd;
    &quot;id&quot;: 1&#xd;
  }&#xd;
}</stringProp>
                <stringProp name="Argument.metadata">=</stringProp>
              </elementProp>
            </collectionProp>
          </elementProp>
          <stringProp name="HTTPSampler.domain">192.168.124.129</stringProp>
          <stringProp name="HTTPSampler.port">9001</stringProp>
          <stringProp name="HTTPSampler.protocol"></stringProp>
          <stringProp name="HTTPSampler.contentEncoding"></stringProp>
          <stringProp name="HTTPSampler.path">/updateUser</stringProp>
          <stringProp name="HTTPSampler.method">POST</stringProp>
          <boolProp name="HTTPSampler.follow_redirects">true</boolProp>
          <boolProp name="HTTPSampler.auto_redirects">false</boolProp>
          <boolProp name="HTTPSampler.use_keepalive">true</boolProp>
          <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
          <stringProp name="HTTPSampler.embedded_url_re"></stringProp>
          <stringProp name="HTTPSampler.connect_timeout"></stringProp>
          <stringProp name="HTTPSampler.response_timeout"></stringProp>
        </HTTPSamplerProxy>
        <hashTree>
          <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="响应断言" enabled="true">
            <collectionProp name="Asserion.test_strings">
              <stringProp name="527098352">&quot;respCode&quot;:&quot;1&quot;,</stringProp>
            </collectionProp>
            <stringProp name="Assertion.custom_message"></stringProp>
            <stringProp name="Assertion.test_field">Assertion.response_data</stringProp>
            <boolProp name="Assertion.assume_success">false</boolProp>
            <intProp name="Assertion.test_type">2</intProp>
          </ResponseAssertion>
          <hashTree/>
        </hashTree>
        <ResultCollector guiclass="ViewResultsFullVisualizer" testclass="ResultCollector" testname="察看结果树" enabled="true">
          <boolProp name="ResultCollector.error_logging">false</boolProp>
          <objProp>
            <name>saveConfig</name>
            <value class="SampleSaveConfiguration">
              <time>true</time>
              <latency>true</latency>
              <timestamp>true</timestamp>
              <success>true</success>
              <label>true</label>
              <code>true</code>
              <message>true</message>
              <threadName>true</threadName>
              <dataType>true</dataType>
              <encoding>false</encoding>
              <assertions>true</assertions>
              <subresults>true</subresults>
              <responseData>false</responseData>
              <samplerData>false</samplerData>
              <xml>false</xml>
              <fieldNames>true</fieldNames>
              <responseHeaders>false</responseHeaders>
              <requestHeaders>false</requestHeaders>
              <responseDataOnError>false</responseDataOnError>
              <saveAssertionResultsFailureMessage>true</saveAssertionResultsFailureMessage>
              <assertionsResultsToSave>0</assertionsResultsToSave>
              <bytes>true</bytes>
              <sentBytes>true</sentBytes>
              <threadCounts>true</threadCounts>
              <idleTime>true</idleTime>
              <connectTime>true</connectTime>
            </value>
          </objProp>
          <stringProp name="filename"></stringProp>
        </ResultCollector>
        <hashTree/>
        <HeaderManager guiclass="HeaderPanel" testclass="HeaderManager" testname="HTTP信息头管理器" enabled="true">
          <collectionProp name="HeaderManager.headers">
            <elementProp name="" elementType="Header">
              <stringProp name="Header.name">Content-Type</stringProp>
              <stringProp name="Header.value">application/json;charset=UTF-8</stringProp>
            </elementProp>
          </collectionProp>
        </HeaderManager>
        <hashTree/>
      </hashTree>
    </hashTree>
  </hashTree>
</jmeterTestPlan>

新建一个run.sh脚本,方便我们执行目标jmx压测脚本

#! /bin/bash

rm -rf ./html/* ./out/*
cd bin

sh ./jmeter.sh  -n -t  /home/jmeter/apache-jmeter-4.0/scripts/$1.jmx  -l   /home/jmeter/apache-jmeter-4.0/out/result.jtl  -e  -o  /home/jmeter/apache-jmeter-4.0/html

 准备完毕,我们执行

sh run.sh updateUser

可以看到result文件夹内,出现了测试结果。

三、ab

安装

  yum -y install httpd-tools

验证安装是否成功

ab -V

简单测试命令示例

a. 同时10个请求,连续点击1000次(每次Request完毕自动销毁,重新请求)
	
ab -n 1000 -c 10 http://www.test.com/index.html

b. 同时10个请求,连续点击1000次,并保持Keep-Alive方式(保持长连接的方式)
	
ab -n 1000 -c 10 -k http://www.test.com/index.html

c. 同时10个请求,请求测试时间为20s
	
ab -c 10 -t 20 http://www.test.com/index.html

d. 将请求性能详情输出到CSV档文件
	
ab -e output.csv -n 1000 -c 10 http://www.test.com/index.html 

out.csv: {请求序号 - 请求耗时ms}

e. 包含Post的数据的文件,例如POST发送JSON格式数据
	
ab -p p.json -T application/json -n 1000 -c 10 http://www.test.com/index.html

测试用的Json报文updateUser.json

{
  "header": {
    "requestTime": "2018100910320000",
    "requestSeq": "e379853d-d93e-4249-b9be-8a272b"
  },
  "content": {
    "id": 1
  }
}

执行

ab -p updateUser.json -T application/json -c 10 -t 10 http://192.168.124.129:9001/updateUser

-n在测试会话中所执行的请求个数。默认时,仅执行一个请求。请求的总数量

-c一次产生的请求个数。默认是一次一个。请求的用户量

-t测试所进行的最大秒数。其内部隐含值是-n 50000,它可以使对服务器的测试限制在一个固定的总时间以内。默认时,没有时间限制。

控制台出现测试结果,压测结果参数说明

This is ApacheBench, Version 2.3 <$Revision: 655654 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking www.myvick.cn (be patient).....done


Server Software:        nginx/1.13.6   #测试服务器的名字
Server Hostname:        www.myvick.cn  #请求的URL主机名
Server Port:            80             #web服务器监听的端口

Document Path:          /index.php    #请求的URL中的根绝对路径,通过该文件的后缀名,我们一般可以了解该请求的类型
Document Length:        799 bytes       #HTTP响应数据的正文长度

Concurrency Level:      10        # 并发用户数,这是我们设置的参数之一
Time taken for tests:   0.668 seconds   #所有这些请求被处理完成所花费的总时间 单位秒
Complete requests:      100         # 总请求数量,这是我们设置的参数之一
Failed requests:        0          # 表示失败的请求数量,这里的失败是指请求在连接服务器、发送数据等环节发生异常,以及无响应后超时的情况
Write errors:           0
Total transferred:      96200 bytes    #所有请求的响应数据长度总和。包括每个HTTP响应数据的头信息和正文数据的长度
HTML transferred:       79900 bytes    # 所有请求的响应数据中正文数据的总和,也就是减去了Total transferred中HTTP响应数据中的头信息的长度
Requests per second:    149.71 [#/sec] (mean) #吞吐率,计算公式:Complete requests/Time taken for tests  总请求数/处理完成这些请求数所花费的时间
Time per request:       66.797 [ms] (mean)   # 用户平均请求等待时间,计算公式:Time token for tests/(Complete requests/Concurrency Level)。处理完成所有请求数所花费的时间/(总请求数/并发用户数)
Time per request:       6.680 [ms] (mean, across all concurrent requests) #服务器平均请求等待时间,计算公式:Time taken for tests/Complete requests,正好是吞吐率的倒数。也可以这么统计:Time per request/Concurrency Level
Transfer rate:          140.64 [Kbytes/sec] received  #表示这些请求在单位时间内从服务器获取的数据长度,计算公式:Total trnasferred/ Time taken for tests,这个统计很好的说明服务器的处理能力达到极限时,其出口宽带的需求量。

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        1    2   0.7      2       5
Processing:     2   26  81.3      3     615
Waiting:        1   26  81.3      3     615
Total:          3   28  81.3      6     618

Percentage of the requests served within a certain time (ms)
  50%      6
  66%      6
  75%      7
  80%      7
  90%     10
  95%    209
  98%    209
  99%    618
 100%    618 (longest request)

#Percentage of requests served within a certain time(ms)这部分数据用于描述每个请求处理时间的分布情况,比如以上测试,80%的请求处理时间都不超过7ms,这个处理时间是指前面的Time per request,即对于单个用户而言,平均每个请求的处理时间

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

猎户星座。

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值