如何在Ubuntu 14.04上使用wrk对HTTP延迟进行基准测试

介绍

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

延迟是指请求发生的时间(通过wrk)和收到响应的时刻(来自服务)之间的时间间隔。这可用于模拟访问者在使用浏览器或任何其他发送HTTP请求的方法访问网站时在您的网站上遇到的延迟。

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

  • Rails和其他Ruby应用程序
  • Express和其他JavaScript应用程序
  • PHP应用程序
  • 在Web服务器上运行的静态网站
  • Nginx这样的负载均衡器背后的站点和应用程序
  • 你的缓存层

测试无法真实用户进行比较,但它们应该能够很好地估计预期延迟,以便您可以更好地规划基础架构。测试还可以让您深入了解性能瓶颈。

wrk是开源的,可以在GitHub找到

它非常稳定,并且由于其多线程特性,可以模拟高负载。wrk的最大特点是它能够集成Lua脚本,这增加了许多可能性,例如:

  • 使用cookie对请求进行基准测试
  • 自定义报告
  • 对多个URL进行基准测试 - 也就是现在流行的ab,这个功能也是Apache HTTP服务器基准测试工具所不具备的。

先决条件

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

如您所见,我们将在非常简单的场景中使用wrk。我们将在Node.js应用程序上对Express进行基准测试。

我们将启动两个腾讯CVM:一个用于生成负载的wrk,另一个用于应用程序。如果他们在同一个盒子上,他们会竞争资源,我们的结果将不可靠。

基准测试的机器应该足够强大以处理受压系统,但在我们的情况下,应用程序非常简单,我们将使用相同尺寸的机器。

  • 因为它们将通过私有IP进行通信,所以在同一区域中旋转两个腾讯CVM
  • 在本教程中调用一个腾讯CVM wrk1和另一个app1
  • 选择2 GB内存
  • 选择Ubuntu 14.04,没有服务器的同学可以在这里购买,不过我个人更推荐您使用免费的腾讯云开发者实验室进行试验,学会安装后在购买服务器
  • 在“ 可用设置”部分中选择“ 专用网络**”**
  • 在每台服务器上创建一个sudo用户

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

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

为了让我们的生活更轻松,我们将使用Docker,因此我们可以在容器内启动wrk和我们的应用程序。这让我们可以跳过设置Node.js环境,npm模块和deb软件包; 我们所需要的只是下载并运行相应的容器。节省的时间将用于学习wrk。

注意:本节中的命令应在两个腾讯CVM上执行。

要安装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

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

OutputDocker version 1.7.1, build 786b29d

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

app1腾讯CVM上执行这些命令。

出于测试目的,作者在公共Docker注册表中发布了Docker镜像。它包含一个用Node.js编写的HTTP调试应用程序。它不是一个性能野兽(我们今天不打破任何记录)但它足以进行测试和调试。你可以在这里查看源代码。

当然,在现实生活中,您可能希望测试自己的应用程序。

在我们启动应用程序之前,让我们将腾讯CVM的私有IP地址保存到一个名为APP1_PRIVATE_IP的变量中:

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

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

echo $APP1_PRIVATE_IP

输出:

Output
10.135.232.163

您的私人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之间的所有通信

现在测试curl以查看应用程序是否正在运行:

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

预期产量:

Output
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

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

Output
[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,因此非常容易。只需使用以下命令从Docker注册表中心下载映像williamyeh/wrk

docker pull williamyeh/wrk

上面的命令下载了包含wrk的Docker镜像。我们不需要构建wrk,也不需要安装任何其他软件包。要运行wrk(在容器内),我们只需要根据此图像启动容器就可以完成,这也是我们即将去做的事情。

下载应该只需几秒钟,因为图像非常小 - 小于3 MB。如果您想直接在您喜爱的Linux发行版上安装wrk,请访问此Wiki页面并按照说明操作。

我们还将在此服务器上设置变量APP1_PRIVATE_IP。我们需要来自app1 腾讯CVM 的私有IP地址。

导出变量:

export APP1_PRIVATE_IP=10.135.232.163

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

第4步 - 运行wrk基准测试

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

本节中的所有命令都应该在wrk1腾讯CVM上执行。

让我们看看wrk为我们提供的选项。仅使用--version标志运行wrk容器将打印出关于其用法的简短总结:

docker run --rm williamyeh/wrk --version

输出:

Output
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
  • 对我们的应用程序/的路径进行基准测试

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

下图显示了这种情况:

请记住,无法将连接与真实用户进行比较,因为真实用户在查看主页时也会下载CSS,图像和JavaScript文件。

这是测试的实际命令:

让我们在我们的wrk 腾讯CVM容器中运行所描述的场景:

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

等待测试运行几秒钟,然后查看结果,我们将在下一步中对其进行分析。

第5步 - 评估输出

输出:

OutputRunning 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+/- Stdev值。如果这些值很高,那么您可以预期您的分布可能会有很重的尾巴。
  • 有关请求编号,传输数据和吞吐量的统计信息: 5494 requests in 5.01s, 1.05MB read Requests/sec: 1096.54 Transfer/sec: 215.24KB 在这里,我们看到在5.01几秒钟内,wrk可以进行5494请求和1.05MB数据传输。结合简单的math(total number of requrests/benchmark duration),我们得到1096.54每秒请求的结果。

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

什么结果最好?

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

理想情况下,延迟不应该太高,至少对于网页而言。当资产大约两秒或更短时,资产的页面加载时间限制是最佳的。

现在你可能会问自己: 550.90 Requests/sec3.82ms的延迟是否是一个好的结果?不幸的是,没有简单的答案。这取决于许多因素,如:

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

第6步 - 采取措施改善延迟

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

  • 调整您的服务 - 检查您的代码,看看可以更有效地完成哪些工作
  • 检查您的数据库,看看它是否是您的瓶颈
  • 垂直扩展 - 为您的计算机添加资源
  • 水平扩展 - 添加服务的另一个实例并将其添加到负载均衡器
  • 添加缓存层

请记住在对其进行更改后对您的服务进行基准测试 - 只有这样才能确保您的服务得到改进。

你可能会想,如果没有Lua的话,那事情可能就是这样了。。。

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

因为wrk有一个内置的LuaJIT(Lua的即时编译器),所以可以使用Lua脚本进行扩展。正如介绍中所提到的,这为wrk增加了许多功能。

在wrk中使用Lua脚本很简单。只需将文件路径附加到-s标志即可。

因为我们在Docker中使用wrk,所以我们必须先与容器共享此文件。这可以通过Docker的-v选项实现。

wrk的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执行以下执行阶段:

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

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

此外,运行阶段可以分为三个步骤:initrequestresponse

根据提供的图表和文档,我们可以在Lua脚本中使用以下方法:

  • setup(thread):所有线程初始化但尚未启动时执行。用于将数据传递给线程
  • init(args):初始化每个线程时调用 此函数接收脚本的额外命令行参数,我们必须用--将该参数与wrk参数分开。 例: wrk -c3 -d1s -t2 -s /scripts/debug.lua http://$APP1_PRIVATE_IP:3000 -- debug true
  • request():需要为每个请求返回HTTP对象。在这个函数中,我们可以修改方法,标题,路径和正文 使用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状态代码> 399summary.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表单处理程序:使用HTML表单在action属性中的地址: <form action="/login.php"> ... </form>
  • POST API端点:如果您有一个restful API,请创建文章的地方使用该端点: POST /articles

首先在wrk1腾讯CVM 上创建一个scripts/post.lua文件。

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标头指定为HTML表单使用的MIME类型。

现在是关键时刻 - 使用此命令对应用程序进行基准测试(在wrk1 腾讯CVM上执行):

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

输出:

OutputRunning 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.txt的文件,该文件可以在data目录中调用,并添加我们想要在基准测试期间使用的所有路径。

cd ~
mkdir data
nano data/paths.txt

查找以下示例data/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

-- 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数组中。然后,对于每个请求,将采用下一个路径。

要运行此基准测试,请使用以下命令(在wrk1 腾讯CVM上执行)。您会注意到我们正在添加一些换行符以便于复制:

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

输出:

Outputmultiplepaths: 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的Avanced请求

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

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

作者在作者的技术博客上发布了一个带有JSON请求的高级示例。

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

结论

阅读本文后,您应该能够使用wrk来对您的应用程序进行基准测试。作为旁注,您还可以看到Docker的优点以及它如何极大地最小化您的应用程序和测试环境的设置。

最后,您可以使用带有wrk的Lua脚本进行高级HTTP请求。

更多 Ubuntu教程请前往腾讯云+社区学习更多知识。


参考文献:《 How To Benchmark HTTP Latency with wrk on Ubuntu 14.04》

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

编辑于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏信安之路

使用 Wave 文件绕过 CSP 策略

CSP 全称 Content Security Policy,即内容安全策略。CSP 是一个额外的安全层,用于检测并削弱某些特定类型的攻击,包括 XSS 和注入...

1170
来自专栏owent

理解Raft算法

之前已经有Paxos算法,用于解决分布式系统最终一致性问题,而且已经有了zookeeper这个成熟的开源实现。那么这个Raft算法有啥用呢?按照Raft官网的说...

4733
来自专栏三杯水

服务稳定性及应用防攻击方案

日志收集推荐使用Elastic Stack协议栈,可以满足收集海量日志需求,而且便于后续分析、报表、报警操作

1483
来自专栏用户画像

3.2 虚拟内存管理

②当大量作业要求运行时,由于内存不足以容纳所有作业,只能使少数作业先运行,导致多道程序度的下降。

762
来自专栏数据和云

如何提高Linux下块设备IO的整体性能?

编辑手记:本文主要讲解Linux IO调度层的三种模式:cfp、deadline和noop,并给出各自的优化和适用场景建议。 作者简介: ? 邹立巍 Linux...

6654
来自专栏JavaEdge

漫谈缓存更新之道

许多人在更新缓存时,先删除缓存,然后再更新数据库,而后续的操作会把数据再装载入缓存中。

2162
来自专栏linux驱动个人学习

Linux核心调度器之周期性调度器scheduler_tick--Linux进程的管理与调度(十八)

因而内核提供了两个调度器主调度器,周期性调度器,分别实现如上工作, 两者合在一起就组成了核心调度器(core scheduler), 也叫通用调度器(gener...

1622
来自专栏烙馅饼喽的技术分享

我的CMS开发记-4 介绍一下DotNetNuke的系统执行流程

       有朋友说应该写个大致结构出来。想想也有道理,那么我就来介绍一下Dotnetnuke的执行流程。基本上我这个就是照搬他的 基本思路     一个站点...

3388
来自专栏运维

服务稳定性及应用防护方案

日志收集推荐使用Elastic Stack协议栈,可以满足收集海量日志需求,而且便于后续分析、报表、报警操作

1171
来自专栏逸鹏说道

【推荐】C#线程篇---你所不知道的线程池(4)

线程的创建和销毁都要耗费大量的时间,有什么更好的办法?用线程池! 太多的线程浪费内存资源,有什么更好的办法?用线程池! 太多线程有损性能,有什么更好的办法?用线...

3748

扫码关注云+社区

领取腾讯云代金券