使用wrk实现简单自动化压测,寻找服务器基准值

2019-05-20 00:06:00
CJL
原创
551

安装参考: Centos7 编译安装 wrk 压力测试

脚本参考: wrk中的lua脚本(转)

git源码: https://github.com/wg/wrk/blob/master/scripts/setup.lua

进行系统压测时最简单的是apache的ab工具,但是ab不能进行脚本编写,限制比较大,同时在系统性能比较好时ab压力上不去,测试结果较低。

通过对比发现wrk测试结果比较,使用也很简单,参考之前的压测文章 单机简单压测及调优。当然如果我们要进行系统化的压测还是推荐使用Jmeter或Gatling,这两个工具可以生成丰富的报表。

简单使用

使用方法: wrk <选项> <被测HTTP服务的URL>                            
  Options:                                            
    -c, --connections <N>  跟服务器建立并保持的TCP连接数量  
    -d, --duration    <T>  压测时间           
    -t, --threads     <N>  使用多少个线程进行压测   
                                                      
    -s, --script      <S>  指定Lua脚本路径       
    -H, --header      <H>  为每一个HTTP请求添加HTTP头      
        --latency          在压测结束后,打印延迟统计信息   
        --timeout     <T>  超时时间     
    -v, --version          打印正在使用的wrk的详细版本信息                                                      
  <N>代表数字参数,支持国际单位 (1k, 1M, 1G)
  <T>代表时间参数,支持时间单位 (2s, 2m, 2h)

样例:wrk -t8 -c200 -d30s --latency  "http://www.bing.com"

以上使用8个线程200个连接,对bing首页进行了30秒的压测,并要求在压测结果中输出响应延迟信息。以下对压测结果进行简单注释:

Running 30s test @ http://www.bing.com (压测时间30s)  8 threads and 200 connections (共8个测试线程,200个连接)
  Thread Stats   Avg      Stdev     Max   +/- Stdev
              (平均值) (标准差)(最大值)(正负一个标准差所占比例)
    Latency    46.67ms  215.38ms   1.67s    95.59%(延迟)
    Req/Sec     7.91k     1.15k   10.26k    70.77%(处理中的请求数)
  Latency Distribution (延迟分布)     50%    2.93ms 75%    3.78ms 90%    4.73ms 99%    1.35s (99分位的延迟)
  1790465 requests in 30.01s, 684.08MB read (30.01秒内共处理完成了1790465个请求,读取了684.08MB数据)
Requests/sec:  59658.29 (平均每秒处理完成59658.29个请求)
Transfer/sec:     22.79MB (平均每秒读取数据22.79MB)

配合脚本

大部分场景下单一的URL地址并不能满足我们的压测需求,我们常常需要请求动态的数据,将数据保存下来等。

令人兴奋的是wrk支持lua脚本,对应程序员来说支持脚本就是打开了新世界,我们可以通过脚本做我们想做的事情。

参考这片文章: https://www.cnblogs.com/jiftle/p/7158291.html
以下一段内容为转载:

  1. 介绍wrk对Lua脚本的支持
    wrk支持在三个阶段对压测进行个性化,分别是启动阶段、运行阶段和结束阶段。每个测试线程,都拥有独立的Lua运行环境。

    启动阶段

    function setup(thread)

    在脚本文件中实现setup方法,wrk就会在测试线程已经初始化但还没有启动的时候调用该方法。wrk会为每一个测试线程调用一次setup方法,并传入代表测试线程的对象thread作为参数。setup方法中可操作该thread对象,获取信息、存储信息、甚至关闭该线程。

    thread.addr             - get or set the thread's server addressthread:get(name)        - get the value of a global in the thread's env
    thread:set(name, value) - set the value of a global in the thread's env
    thread:stop()           - stop the thread

    运行阶段

    function init(args)function delay()
    function request()
    function response(status, headers, body)

    init由测试线程调用,只会在进入运行阶段时,调用一次。支持从启动wrk的命令中,获取命令行参数;
    delay在每次发送request之前调用,如果需要delay,那么delay相应时间;
    request用来生成请求;每一次请求都会调用该方法,所以注意不要在该方法中做耗时的操作;
    reponse在每次收到一个响应时调用;为提升性能,如果没有定义该方法,那么wrk不会解析headers和body;

    结束阶段

    function done(summary, latency, requests)

    该方法在整个测试过程中只会调用一次,可从参数给定的对象中,获取压测结果,生成定制化的测试报告。

  2. 自定义脚本中可访问的变量和方法
    变量:wrk 一个table类型的变量wrk,是全局变量,修改该table,会影响所有请求。

    wrk = {
    scheme  = "http",
    host    = "localhost",
    port    = nil,
    method  = "GET",
    path    = "/",
    headers = {},
    body    = nil,
    thread  = <userdata>,
    }

    方法:wrk.fomat wrk.lookup wrk.connect

    function wrk.format(method, path, headers, body)wrk.format returns a HTTP request string containing the passed parameters
    merged with values from the wrk table.
    根据参数和全局变量wrk,生成一个HTTP rquest string。
    function wrk.lookup(host, service)
    wrk.lookup returns a table containing all known addresses for the host
    and service pair. This corresponds to the POSIX getaddrinfo() function.
    给定host和service(port/well known service name),返回所有可用的服务器地址信息。
    function wrk.connect(addr)
    wrk.connect returns true if the address can be connected to, otherwise
    it returns false. The address must be one returned from wrk.lookup().
    测试与给定的服务器地址信息是否可以成功创建连接

样例

使用POST METHOD

通过修改全局变量wrk,使得所有请求都使用POST方法,并指定了body和Content-Type头。

wrk.method = "POST"wrk.body   = "foo=bar&baz=quux"wrk.headers["Content-Type"] = "application/x-www-form-urlencoded"

为每次request更换一个参数

request = function()
   uid = math.random(1, 10000000)   path = "/test?uid=" .. uid   return wrk.format(nil, path)end

每个线程要先进行认证,认证之后获取token以进行压测

在没有token的情况下,先访问/authenticate认证。认证成功后,读取token并替换path为/resource。

token = nilpath  = "/authenticate"request = function()
   return wrk.format("GET", path)endresponse = function(status, headers, body)
   if not token and status == 200 then  token = headers["X-Token"]      path  = "/resource"  wrk.headers["X-Token"] = token   endend

压测支持HTTP pipeline的服务

通过在init方法中将三个HTTP request请求拼接在一起,实现每次发送三个请求,以使用HTTP pipeline。

init = function(args)
   local r = {}
   r[1] = wrk.format(nil, "/?foo")
   r[2] = wrk.format(nil, "/?bar")
   r[3] = wrk.format(nil, "/?baz")
req = table.concat(r)endrequest = function()
return reqend

读取文件内容进行请求

#!/usr/local/bin/lua
counter = 0
-- https://www.jianshu.com/p/67e56c32413b   io
-- https://github.com/wg/wrk/blob/master/SCRIPTING     wrk script
datas ={}
dataIndex = 0;
for cnt in io.lines("data/register.txt") do
    datas[dataIndex] = cnt
    dataIndex = dataIndex + 1
end
dataIndex = dataIndex - 1
wrk.method = "GET"
wrk.scheme = "http"
-- http://s.it603.com/app/User/Login4Pwd?userName=12700085229&password=123456
request = function()
   current = datas[counter % #datas + 1]
   path = "app/User/Login4Pwd?password=123456&userName=" .. current
   print(path)
   counter = counter + 1
   return wrk.format(nil, path)
end
function response(status, headers, body)
   print(current)
   print(body)
end

高级用法

看到功能丰富的脚本,我们可以写个自动化一些的测试脚本,做到自动识别压力瓶颈点,找到功能基准数据。
总体的设计思路是如下:

1、定义一个线程增长模型,包含线程数增加规则、探测到瓶颈点(比如报错率突增、响应时间突增)后的回退规则
2、记录每次测试的结果,根据历史结果决定下一轮测试的配置
3、管理脚本负责分析数据启动wrk脚本,对多次任务进行管理
4、根据全部测试任务自动生成总体脚本,发布性能整体报告
5、还可以通过基础zabbix等性能监控工具对压测后的机器状态进行评估,等待机器恢复正常后再进行下一轮次的压测

针对每一步实现思路如下:
使用PHP脚本语言实现管理脚本,实现增长模型,根据历史结果计算下一次的配置。
使用Lua脚本实现每次测试任务的定义,测试数据的读取,简单测试数据的生成,以及测试结果的保存。
使用PHP脚本实现整体测试报告的生成输出csv格式表格
通过shell脚本实现服务器状态的读取,判断服务器恢复正常后作为测试任务的准入标准
使用PHP脚本实现大批量测试数据的生成
使用Redis作为测试数据、临时数据的存储引擎
使用PHP作为压测任务的管理平台,使用Mysql作为管理平台的存储引擎

发表评论
评论通过审核后显示。
流量统计