如何在Ubuntu 14.04与WRK基准HTTP延迟

本文主要讨论名为WRK一个开源HTTP基准测试工具,测量你的HTTP服务,在高负荷的延迟。 WRK是用于测试依赖于HTTP任何网站或应用程序非常有用。

介绍

本文重点介绍称为开源HTTP基准测试工具WRK ,衡量你的HTTP服务,在高负荷延迟

延迟是指已请求(由WRK)的时刻,被接收到的响应的时刻(从服务)之间的时间间隔。 这可用于模拟访问者在使用浏览器或任何其他发送HTTP请求的方法访问网站时遇到的延迟。

wrk对于测试任何依赖HTTP的网站或应用程序很有用,例如:

  • 导轨和其他Ruby应用程序
  • Express和其他JavaScript应用程序
  • PHP应用程序
  • 运行在Web服务器上的静态网站
  • 网站和负载平衡器后面像应用程序的Nginx
  • 您的缓存层

测试所不能比拟 真实用户 ,但是他们应该给你预期的延迟的一个很好的估计 ,因此您可以更好地规划你的基础设施。 测试还可以帮助您了解性能瓶颈。

WRK是开源的,可以上找到GitHub上

它非常稳定,并允许模拟高负载,由于其多线程的性质。 WRK的最大特点是其整合能力的Lua脚本,这将增加像许多可能性:

  • 使用Cookie对标准请求
  • 自定义报告
  • 基准多个URL -一些流行的ab ,Apache HTTP服务器基准测试工具,不能

先决条件

我们将在本教程中使用的基础设施如下图所示:

基础设施概述

正如你可以看到,我们将在一个非常简单的场景中使用wrk。 我们将基准的快车的Node.js应用。

我们将旋转起来两个Droplet :一个用于WRK,其产生的负载,而另一个应用程序。 如果他们在同一个盒子上,他们将竞争资源,我们的结果是不可靠的。

该机器的基准应足够强大以处理应力系统,但在我们的情况下,应用程序是这么简单,我们将使用相同大小的机器。

  • 自旋向上两个Droplet在同一地区 ,因为他们将私有IP通信
  • 呼叫一Dropletwrk1和其他APP1,以及在本教程中遵循
  • 选择2 GB内存
  • 选择的Ubuntu 14.04
  • 可用设置部分专用网络
  • 创建一个sudo的用户在每台服务器上

较小的Droplet也会工作,但是您应该预期在测试结果中有更多的延迟。 在实际测试环境中,应用服务器应与您打算在生产环境中使用的大小相同。

数字海洋基础设施设置预览

如果您需要帮助设置Droplet,请参考本文

第1步 - 两个服务器:安装Docker

为了使我们的生活更轻松,我们将使用Docker ,这样我们就可以开始WRK和我们的货柜内的应用程序。 这让我们跳过设置Node.js环境,npm模块和deb包; 所有我们需要的是下载并运行相应的容器。 节省的时间将投入学习wrk。

如果你不熟悉Docker,可以读取介绍它在这里

注意:本节的命令应在两个Droplet被执行。

要安装Docker,请登录到您的服务器并执行以下命令。 首先,更新包列表:

sudo apt-get update

安装Wget和cURL:

sudo apt-get install -y wget curl

下载并安装Docker:

sudo wget -qO- https://get.docker.com/ | sh

你的用户添加到docker组,以便您可以执行没有sudo命令Docker:

sudo gpasswd -a ${USER} docker
sudo service docker restart
newgrp docker

如果您在使用不同的Linux发行版,Docker有安装文档 ,这将可能涉及你的情况。

要验证docker已安装正确使用此命令:

docker --version

您应该得到以下或类似的输出:

Docker version 1.7.1, build 786b29d

第2步 - 准备测试应用程序

APP1Droplet执行这些命令。

出于测试目的,笔者发表了Docker形象在公众Docker注册表。 它包含一个用Node.js编写的HTTP调试应用程序。 它不是一个性能野兽(我们今天不会破坏任何记录),但它足以进行测试和调试。 您可以检查出的源代码在这里

当然,在现实生活的场景中,你想测试自己的应用程序。

在我们开始之前的应用程序,让我们的Droplet的私有IP地址保存到一个叫做变量APP1_PRIVATE_IP

export APP1_PRIVATE_IP=$(sudo ifconfig eth1 | egrep -o "inet addr:[^ ]*" | awk -F ":" '{print $2}')

您可以使用以下方式查看私有IP:

echo $APP1_PRIVATE_IP

输出:

10.135.232.163

您的私人IP地址将不同,因此请记下它。

您也可以从私有IP digitalocean.com控制面板。 只需选择你的Droplet,然后去设置部分如下图中介绍:

数字海洋找到Droplet的私人IP

现在通过执行这个命令来启动应用程序:

docker run -d -p $APP1_PRIVATE_IP:3000:3000 --name=http-debugging-application czerasz/http-debugger

上面的命令将首先下载所需的Docker镜像,然后运行一个Docker容器。 容器处于分离模式 ,只是意味着它会在后台运行启动。 选项-p $APP1_PRIVATE_IP:3000:3000将代理所有的交通,并从端口本地容器3000 ,及到对端口的主机私有IP 3000

下图描述了这种情况:

调试应用程序容器可视化

现在,随着测试curl ,看是否运行应用程序:

curl -i -XPOST http://$APP1_PRIVATE_IP:3000/test -d 'test=true'

预期输出:

HTTP/1.1 200 OK
X-Powered-By: Express
X-Debug: true
Content-Type: text/html; charset=utf-8
Content-Length: 2
ETag: W/"2-79dcdd47"
Date: Wed, 13 May 2015 16:25:37 GMT
Connection: keep-alive

ok

的应用程序非常简单,并返回只是一个ok的消息。 所以每次WRK请求该应用程序,它会得到一个小ok消息返回。

最重要的部分是,通过分析应用程序日志,我们可以看到wrk对我们的应用程序做出了什么请求。

使用以下命令查看应用程序日志:

docker logs -f --tail=20 http-debugging-application

您的示例输出应如下所示:

[2015-05-13 16:25:37] Request 1

POST/1.1 /test on :::3000

Headers:
 - user-agent: curl/7.38.0
 - host: 0.0.0.0:32769
 - accept: */*
 - content-length: 9
 - content-type: application/x-www-form-urlencoded

No cookies

Body:
test=true

如果愿意,您可以在运行基准测试时保持此运行状态。 退出同尾CTRL-C

第3步 - 安装wrk

登录到服务器wrk1并准备安装WRK。

因为我们有Docker这很容易。 只需下载williamyeh/wrk从这个命令Docker注册表枢纽形象:

docker pull williamyeh/wrk

上面的命令下载一个包含wrk的Docker镜像。 我们不需要构建wrk,也不需要安装任何额外的软件包。 要运行wrk(在容器内),我们只需要基于这个图像启动一个容器,我们很快就会做到。

下载应该只需要几秒钟,因为图像非常小 - 小于3 MB。 如果您想直接在你喜欢的Linux发行版的访问安装WRK 此Wiki网页 ,并按照指示。

我们还将设置APP1_PRIVATE_IP此服务器上的变量。 我们需要从APP1Droplet的私有IP地址。

导出变量:

export APP1_PRIVATE_IP=10.135.232.163

请记住,在改变10.135.232.163 IP地址添加到APP1Droplet的私有IP。 此变量将仅保存在当前会话中,因此请记住在下次登录时使用wrk重新设置它。

第4步 - 运行wrk基准测试

在本节中,我们将最终看到wrk在行动。

在本节中的所有命令都应该在wrk1Droplet被执行。

让我们看看wrk可用的选项。 只用运行WRK容器--version标志将打印出它的用法的简要介绍:

docker run --rm williamyeh/wrk --version

输出:

wrk 4.0.0 [epoll] Copyright (C) 2012 Will Glozer
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运行的最简单的情况是:

wrk -t2 -c5 -d5s -H 'Host: example.com' --timeout 2s http://$APP1_PRIVATE_IP:3000/

意思是:

  • -t2 :使用两个独立 线程
  • -c5 :开放六个连接 (第一个客户是零)
  • -d5s :运行五秒钟测试
  • -H 'Host: example.com'传递一个Host
  • --timeout 2s :定义一个两秒钟的超时
  • http://$APP1_PRIVATE_IP:3000/目标应用程序正在监听$APP1_PRIVATE_IP:3000
  • 基准的/我们的应用程序的路径

这也可以描述为六个用户重复请求我们的主页五秒钟。

下图显示了这种情况:

wrk架构结构

请记住,连接无法比拟 真实用户,因为真实用户也下载CSS,图像和JavaScript文件,同时查看您的主页。

这里是测试的实际命令:

让我们在我们的wrk Docker容器中运行描述的场景:

docker run --rm williamyeh/wrk -t2 -c5 -d5s -H 'Host: example.com' --timeout 2s http://$APP1_PRIVATE_IP:3000/

等待几秒钟让测试运行,并查看结果,我们将在下一步中分析。

第5步 - 评估输出

输出:

Running 5s test @ http://10.135.232.163:3000
  2 threads and 5 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     3.82ms    2.64ms  26.68ms   85.81%
    Req/Sec   550.90    202.40     0.98k    68.00%
  5494 requests in 5.01s, 1.05MB read
Requests/sec:   1096.54
Transfer/sec:    215.24KB
  • 当前配置摘要:

    Running 5s test @ http://10.135.232.163:3000
      2 threads and 5 connections
    

    在这里我们可以看到我们的基准配置的简要摘要。 基准花5秒,所述基准机IP是10.135.232.163 ,并且测试用两个线程。

  • 延迟和req / sec统计的正态分布参数:

    Thread Stats   Avg      Stdev     Max   +/- Stdev
      Latency     3.82ms    2.64ms  26.68ms   85.81%
      Req/Sec   550.90    202.40     0.98k    68.00%
    

    这部分告诉我们我们的基准正态分布的细节-什么参数一个高斯函数会有。

    基准不总是有正态分布,这就是为什么这些结果可能会误导。 所以总是看Max+/-发网值。 如果这些值很高,那么您可能期望您的分配可能有一个沉重的尾巴。

  • 有关请求编号,传输数据和吞吐量的统计信息:

      5494 requests in 5.01s, 1.05MB read
    Requests/sec:   1096.54
    Transfer/sec:    215.24KB
    

    在这里,我们看到,在时间5.01秒WRK可以做5494的请求和传输1.05MB的数据。 用简单的数学(组合total number of requrests/benchmark duration ),我们得到的结果1096.54每秒的请求。

一般来说,您设置的客户端越多,您每秒应该获得的请求越少。 延迟也将增长。 这是因为应用程序将承受更重的负载。

什么结果最好?

你的目标是保持Requests/sec尽可能高和Latency尽可能地低。

理想情况下,延迟不应该太高,至少对于网页。 为页面加载时间与资产的极限是最佳时,它的大约两秒钟或更少。

现在,你可能会问自己:是550.90 Requests/sec有一个潜伏期3.82ms的好成绩? 不幸的是,没有简单的答案。 这取决于许多因素,如:

  • 客户数,正如我们之前讨论的
  • 服务器资源 - 是大还是小实例?
  • 为应用程序提供服务的机器数
  • 您的服务类型 - 是提供静态文件的缓存,还是提供动态响应的广告服务器?
  • 数据库类型,数据库集群大小,数据库连接类型
  • 请求和响应类型 - 是一个小AJAX请求还是胖API调用?
  • 和许多其他

第6步 - 采取措施提高延迟

如果您对您的服务表现不满意,您可以:

  • 调整您的服务 - 检查您的代码,看看什么可以更有效地完成
  • 检查你的数据库,看看它是你的瓶颈
  • 垂直缩放 - 为您的机器添加资源
  • 水平缩放 - 添加您的服务的另一个实例,并将其添加到负载平衡器
  • 添加缓存图层

对于应用程序的改进更详细的讨论,检查出5种方法来改善你的生产Web应用程序服务器安装

记住在应用更改后对服务进行基准测试 - 只有这样,您才能确保您的服务有所改进。

就这样,你可能会想,如果没有这个Lua的东西。

使用Lua脚本模拟高级HTTP请求

因为WRK有一个内置的LuaJIT (只是即时编译器的Lua),它可以用Lua脚本进行扩展。 如介绍中所述,这为wrk添加了很多功能。

使用带wrk的Lua脚本很简单。 您只需将文件路径-s标志。

因为我们在Docker里面使用wrk,我们必须首先与容器共享这个文件。 这可以通过多克的实现-v选项。

Wra的一个Lua脚本的部分

在通用的形式,使用脚本调用test.lua ,整个命令看起来是这样的:

docker run --rm -v `pwd`/scripts:/scripts williamyeh/wrk -c1 -t1 -d5s -s /scripts/test.lua http://$APP1_PRIVATE_IP:3000

我们在前面的步骤中解释了wrk命令及其选项。 这个命令不会增加太多; 只是脚本的路径和一些额外的命令告诉Docker如何在容器外找到它。

--rm它已停止后标志将自动删除该容器。

但是我们实际上是否知道如何写一个Lua脚本? 不要害怕; 你会很容易地学习它。 我们将在这里讨论一个简单的例子,你可以自己运行自己的更高级的脚本。

首先让我们讨论反映wrk内部逻辑的预定脚本结构。 下图说明了一个线程:

wrk Lua脚本循环

wrk执行以下执行阶段:

  • 解析域的IP地址
  • 与线程设置开始
  • 执行压力测试阶段,这就是所谓的运行
  • 最后一步是简称为完成

当使用多个线程时,您将有一个分辨率阶段和一个完成阶段,但有两个设置阶段和两个运行阶段:

wrk Lua两个线程的脚本循环

此外, 运行阶段可分成三个步骤: 初始化请求响应

wrk Lua脚本循环 - 运行阶段

根据所呈现的图表和文档,我们可以用一个Lua脚本中下面的方法:

  • setup(thread) :当所有线程都被初始化,但尚未开始执行的。 用于将数据传递到线程

  • init(args) :当每个线程调用初始化

    此函数接收额外的命令行参数必须从WRK论点分开脚本--

    例:

    wrk -c3 -d1s -t2 -s /scripts/debug.lua http://$APP1_PRIVATE_IP:3000 -- debug true
    
  • request()需要返回的HTTP对象为每个请求。 在这个函数中,我们可以修改方法,headers,path和body

    使用wrk.format辅助函数来塑造请求对象。

    例:

    return wrk.format(method, path, headers, body)
    
  • response(status, headers, body) :时调用响应回来

  • done(summary, latency, requests) :当所有的请求都完成和统计计算伏法

    在此函数内,以下属性可用:

    属性 描述
    summary.duration 运行持续时间(以微秒为单位)
    summary.requests 总完成请求
    summary.bytes 接收的总字节数
    summary.errors.connect 总套接字连接错误
    summary.errors.read 总套接字读取错误
    summary.errors.write 总的套接字写错误
    summary.errors.status 总HTTP状态代码> 399
    summary.errors.timeout 总请求超时
    latency.min 测试期间达到的最小延迟值
    latency.max 在测试期间达到的最大延迟值
    latency.mean 在测试期间达到的平均延迟值
    latency.stdev 延迟标准偏差
    latency:percentile(99.0) 第99百分位数值
    latency[i] 请求原始延迟数据i

每个线程都有自己的Lua上下文以及它自己的局部变量。

现在,我们将通过几个实际的例子,但你可以找到在WRK项目的更多有用的基准测试脚本, scripts目录

例如: POST请求

让我们先从最简单的例子,我们模拟一个POST请求。

POST请求通常用于将数据发送到服务器。 这可以用于基准:

  • HTML表单处理程序:使用这是在地址action HTML表单的属性:

    <form action="/login.php">
    ...
    </form>
    
  • POST API端点:如果你有一个宁静的API,可以使用在其中创建您的文章端点:

    POST /articles
    

与创建一个开始scripts/post.luawrk1Droplet文件。

cd ~
mkdir scripts
nano scripts/post.lua

添加到它以下内容:

wrk.method = "POST"
wrk.body   = "login=sammy&password=test"
wrk.headers["Content-Type"] = "application/x-www-form-urlencoded"

这个脚本很简单,我们甚至没有使用任何提到的方法。 我们只是修改了全局wrk对象属性。

我们改变了请求方法POST ,增加了一些登录参数,并且指定的Content-Type头的MIME类型HTML表单使用。

在我们开始基准测试之前,下面是一个图表,用于帮助您可视化脚本,Docker容器和应用程序服务器之间的关系:

Docker容器可视化

现在的关键时刻-基准使用此命令的应用程序(在wrk1Droplet执行):

docker run --rm -v `pwd`/scripts:/scripts williamyeh/wrk -c1 -t1 -d5s -s /scripts/post.lua http://$APP1_PRIVATE_IP:3000

输出:

Running 5s test @ http://10.135.232.163:3000
  1 threads and 1 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     1.04ms  718.38us  12.28ms   90.99%
    Req/Sec     1.02k   271.31     1.52k    66.00%
  5058 requests in 5.00s, 0.97MB read
Requests/sec:   1011.50
Transfer/sec:    198.55KB

输出类似于我们之前看到的输出。

注意,我们在这里只有一个连接的基准。 这对应于只有一个用户想要连续登录,传递用户名和密码的情况。 这不是请求任何CSS,图像或JavaScript文件。

对于更现实的场景,您应该增加客户端和线程的数量,同时观察延迟参数,以查看应用程序验证用户凭据的速度。

示例:多个URL路径

另一个常见的需求是同时测试应用程序的多个路径。

让我们创建一个名为paths.txtdata目录,并添加所有这一切,我们希望我们的基准过程中使用的路径。

cd ~
mkdir data
nano data/paths.txt

找到的一例data/paths.txt如下:

paths.txt
/feed.xml
/contact/
/about/
/blog/
/2015/04/21/nginx-maintenance-mode/
/2015/01/06/vagrant-workflows/
/2014/12/10/top-vagrant-plugins/

然后抓住这个简单的脚本,并将其保存为scripts/multiple-url-paths.lua

multiple-url-paths.lua
-- Load URL paths from the file
function load_url_paths_from_file(file)
  lines = {}

  -- Check if the file exists
  -- Resource: http://stackoverflow.com/a/4991602/325852
  local f=io.open(file,"r")
  if f~=nil then
    io.close(f)
  else
    -- Return the empty array
    return lines
  end

  -- If the file exists loop through all its lines
  -- and add them into the lines array
  for line in io.lines(file) do
    if not (line == '') then
      lines[#lines + 1] = line
    end
  end

  return lines
end

-- Load URL paths from file
paths = load_url_paths_from_file("/data/paths.txt")

print("multiplepaths: Found " .. #paths .. " paths")

-- Initialize the paths array iterator
counter = 0

request = function()
  -- Get the next paths array element
  url_path = paths[counter]

  counter = counter + 1

  -- If the counter is longer than the paths array length then reset it
  if counter > #paths then
    counter = 0
  end

  -- Return the request object with the current URL path
  return wrk.format(nil, url_path)
end

虽然本教程不是详细介绍Lua脚本,但如果你阅读脚本中的注释,你可以得到一个好主意。

multiple-url-paths.lua脚本打开/data/paths.txt文件,如果该文件包含路径,它们被保存到内部paths阵列。 然后,对每个请求,采取下一个路径。

要运行这个测试,请使用以下命令(执行对wrk1Droplet)。 您会注意到我们添加了一些换行符,以方便您复制:

docker run --rm \
           -v `pwd`/scripts:/scripts \
           -v `pwd`/data:/data \
           williamyeh/wrk -c1 -t1 -d5s -s /scripts/multiple-url-paths.lua http://$APP1_PRIVATE_IP:3000

输出:

multiplepaths: Found 7 paths
multiplepaths: Found 7 paths
Running 5s test @ http://10.135.232.163:3000
  1 threads and 1 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     0.92ms  466.59us   4.85ms   86.25%
    Req/Sec     1.10k   204.08     1.45k    62.00%
  5458 requests in 5.00s, 1.05MB read
Requests/sec:   1091.11
Transfer/sec:    214.17KB

使用JSON和YAML的高级请求

现在你可能认为其他基准测试工具也可以做这些类型的测试。 但是,wrk还能够使用JSON或YAML格式处理高级HTTP请求。

例如,您可以加载详细描述每个请求的JSON或YAML文件。

笔者曾出版了一JSON请求的高级例子作者的科技博客

你可以使用wrk和Lua对任何类型的HTTP请求进行基准测试。

结论

阅读本文后,您应该能够使用wrk来对应用程序进行基准测试。 作为附注,您还可以看到Docker的美丽,以及如何大大减少设置您的应用程序和测试环境。

最后,您可以使用带有wrk的Lua脚本,使用高级HTTP请求来处理额外的问题。