半夏的博客

liangbogopher's blog

Http 压测工具 -- wrk

介绍

简单的 http 性能测试工具,记录一下使用方法。

安装

MacOS

brew install wrk

Ubuntu/Debian

sudo apt-get install build-essential libssl-dev git -y
git clone https://github.com/wg/wrk.git wrk
cd wrk
sudo make
# move the executable to somewhere in your PATH, ex:
sudo cp wrk /usr/local/bin

CentOS / RedHat / Fedora

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
# move the executable to somewhere in your PATH
sudo cp wrk /somewhere/in/your/PATH

简单使用

1.命令行敲下wrk,可以看到使用帮助

Usage: wrk <options> <url>                            
  Options:                                            
    -c, --connections <N>  Connections to keep open   
    -d, --duration    <T>  Duration of test           
    -t, --threads     <N>  Number of threads to use   
                                                      
    -s, --script      <S>  Load Lua script file       
    -H, --header      <H>  Add header to request      
        --latency          Print latency statistics   
        --timeout     <T>  Socket/request timeout     
    -v, --version          Print version details      
                                                      
  Numeric arguments may include a SI unit (1k, 1M, 1G)
  Time arguments may include a time unit (2s, 2m, 2h)

翻译成中文:

使用方法: 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)

2.查看下版本

wrk 4.1.0 [epoll] Copyright (C) 2012 Will Glozer

3.一次简单压测,分析一下结果

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

输出:
Running 30s test @ http://www.bing.com
  8 threads and 200 connections
  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 
  1790465 requests in 30.01s, 684.08MB read
Requests/sec:  59658.29
Transfer/sec:     22.79MB

以上使用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)

使用lua脚本进行个性化wrk压测

简单的压测可能不能满足我们的需求。比如我们可能需要使用POST METHOD跟服务器交互;可能需要为每一次请求使用不同的参数,以更好的模拟服务的实际使用场景等。wrk支持用户使用-s--script指定Lua脚本,来定制压测过程,满足个性化需求。

Lua脚本如何交互的

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

Overview

  wrk supports executing a LuaJIT script during three distinct phases: setup,
  running, and done. Each wrk thread has an independent scripting environment
  and the setup & done phases execute in a separate environment which does
  not participate in the running phase.

  The public Lua API consists of a global table and a number of global
  functions:

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

  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.

  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.

  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().

  The following globals are optional, and if defined must be functions:

    global setup    -- called during thread setup
    global init     -- called when the thread is starting
    global delay    -- called to get the request delay
    global request  -- called to generate the HTTP request
    global response -- called with HTTP response data
    global done     -- called with results of run

Setup

  function setup(thread)

  The setup phase begins after the target IP address has been resolved and all
  threads have been initialized but not yet started.

  setup() is called once for each thread and receives a userdata object
  representing the thread.

    thread.addr             - get or set the thread's server address
    thread: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

  Only boolean, nil, number, and string values or tables of the same may be
  transfered via get()/set() and thread:stop() can only be called while the
  thread is running.

Running

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

  The running phase begins with a single call to init(), followed by
  a call to request() and response() for each request cycle.

  The init() function receives any extra command line arguments for the
  script which must be separated from wrk arguments with "--".

  delay() returns the number of milliseconds to delay sending the next
  request.

  request() returns a string containing the HTTP request. Building a new
  request each time is expensive, when testing a high performance server
  one solution is to pre-generate all requests in init() and do a quick
  lookup in request().

  response() is called with the HTTP response status, headers, and body.
  Parsing the headers and body is expensive, so if the response global is
  nil after the call to init() wrk will ignore the headers and body.

Done

  function done(summary, latency, requests)

  The done() function receives a table containing result data, and two
  statistics objects representing the per-request latency and per-thread
  request rate. Duration and latency are microsecond values and rate is
  measured in requests per second.

  latency.min              -- minimum value seen
  latency.max              -- maximum value seen
  latency.mean             -- average value seen
  latency.stdev            -- standard deviation
  latency:percentile(99.0) -- 99th percentile value
  latency(i)               -- raw value and count

  summary = {
    duration = N,  -- run duration in microseconds
    requests = N,  -- total completed requests
    bytes    = N,  -- total bytes received
    errors   = {
      connect = N, -- total socket connection errors
      read    = N, -- total socket read errors
      write   = N, -- total socket write errors
      status  = N, -- total HTTP status codes > 399
      timeout = N  -- total request timeouts
    }
  }

使用 POST 请求

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

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

脚本执行如下:

wrk -t12 -c20 -d30s -s post.lua http://xx.xx.xx.xx:8080

如果你想要查看一些其他的脚本,具体可以看github上的官方提供的 Script Example

Top