如何在 Ubuntu 14.04 上使用 wrk 对 HTTP 延迟进行基准测试
简介 本文重点介绍一个名为wrk的开源 HTTP 基准测试工具,该工具可测量 HTTP 服务在高负载下的延迟。 Latency 是指从发出请求(通过 wrk)到收到响应(来自服务)之间的时间间隔。这可用于模拟访问者在使用浏览器或任何其他发送 HTTP 请求的方法访问您的网站时会遇到的延迟。 wrk 可用于测试任何依赖 HTTP 的网站或应用程序,例如: Rails和其他 Ruby 应用程序 Exp
简介
本文重点介绍一个名为wrk的开源 HTTP 基准测试工具,该工具可测量 HTTP 服务在高负载下的延迟。
Latency 是指从发出请求(通过 wrk)到收到响应(来自服务)之间的时间间隔。这可用于模拟访问者在使用浏览器或任何其他发送 HTTP 请求的方法访问您的网站时会遇到的延迟。
wrk 可用于测试任何依赖 HTTP 的网站或应用程序,例如:
-
Rails和其他 Ruby 应用程序
-
Express等 JavaScript 应用
-
个 PHP 应用程序
-
在 Web 服务器上运行的静态网站
-
负载均衡器后面的站点和应用程序,例如Nginx
-
你的缓存层
测试不能与真实用户进行比较,但它们应该为您提供一个良好的估计预期延迟,以便您可以更好地规划您的基础设施。测试还可以让您深入了解性能瓶颈。
wrk 是开源的,可以在GitHub上找到。
由于其多线程特性,它非常稳定并且允许模拟高负载。 wrk 最大的特点是它能够集成Lua脚本,这增加了许多可能性,例如:
-
使用 cookie 进行基准测试
-
自定义报告
-
对多个 URL 进行基准测试 - 流行的
ab
,Apache HTTP 服务器基准测试工具,不能
先决条件
我们将在本教程中使用的基础架构如下图所示:
如您所见,我们将在一个非常简单的场景中使用 wrk。我们将在Node.js应用程序上对Express进行基准测试。
我们将启动两个 Droplet:一个用于 wrk,它生成负载,另一个用于应用程序。如果他们在同一个盒子上,他们会争夺资源,我们的结果将不可靠。
基准测试的机器应该足够强大以处理压力系统,但在我们的例子中,应用程序非常简单,我们将使用相同大小的机器。
-
在同一区域启动两个 Droplet,因为它们将通过私有 IP 进行通信
-
调用一个 Droplet wrk1 和另一个 app1,按照本教程进行操作
-
选择 2 GB 内存
-
选择免费 14.04
-
在 Available Settings 部分中选择 Private Networking
-
在每台服务器上创建一个sudo 用户
较小的液滴也可以,但您应该期望测试结果有更多的延迟。在真实的测试环境中,您的应用服务器应该与您打算在生产中使用的大小相同。
如果您在设置 Droplets 时需要帮助,请参阅这篇文章。
第 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
您应该得到以下或类似的输出:
OutputDocker version 1.7.1, build 786b29d
第 2 步 — 准备测试申请
在 app1 Droplet 上执行这些命令。
出于测试目的,作者在公共 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
输出:
Output10.135.232.163
您的私有 IP 地址会有所不同,因此请记下它。
您也可以从digitalocean.com控制面板获取私有 IP。只需选择您的 Droplet,然后转到设置部分,如下图所示:
现在只需执行以下命令即可启动应用程序:
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
上进出本地容器的所有流量,以及在端口3000
上进出主机私有 IP 的所有流量。
下图描述了这种情况:
现在使用curl
进行测试以查看应用程序是否正在运行:
curl -i -XPOST http://$APP1_PRIVATE_IP:3000/test -d 'test=true'
预期输出:
OutputHTTP/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 Droplet 的私有 IP 地址。
导出变量:
export APP1_PRIVATE_IP=10.135.232.163
请记住将10.135.232.163
IP 地址更改为您的 app1 Droplet 的私有 IP。这个变量只会保存在当前会话中,所以下次登录使用wrk时记得重新设置。
第 4 步 - 运行 wrk 基准测试
在本节中,我们将最终看到 wrk 的实际应用。
本节中的所有命令都应在 wrk1 Droplet 上执行。
让我们看看 wrk 为我们提供的选项。仅使用--version
标志运行 wrk 容器将打印出其用法的简要摘要:
docker run --rm williamyeh/wrk --version
输出:
Outputwrk 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 Docker 容器中运行所描述的场景:
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
- 当前配置摘要:
运行5s测试@http://10.135.232.163:3000
2个线程和5个连接
在这里,我们可以看到我们的基准配置的简要总结。基准测试耗时 5 秒,基准测试机器 IP 为10.135.232.163
,测试使用了两个线程。
- 延迟和请求/秒统计的正态分布参数:
线程统计平均标准偏差最大值 +/- 标准偏差
延迟 3.82ms 2.64ms 26.68ms 85.81%
请求/秒 550.90 202.40 0.98k 68.00%
这部分向我们展示了我们的基准测试的正态分布细节 -高斯函数将具有哪些参数。
基准并不总是具有正态分布,这就是这些结果可能具有误导性的原因。因此,请始终查看 Max 和 +/- Stdev 值。如果这些值很高,那么您可以预期您的分布可能有一个沉重的尾巴。
- 请求数、传输数据、吞吐量统计:
5.01 秒内 5494 个请求,读取 1.05MB
请求/秒:1096.54
传输/秒:215.24KB
这里我们看到,在5.01
秒的时间内,wrk 可以做5494
个请求,传输1.05MB
个数据。结合简单的数学运算(total number of requrests/benchmark duration
),我们得到每秒1096.54
个请求的结果。
通常,您设置的客户端越多,您每秒获得的请求数就越少。延迟也会增加。这是因为应用程序将承受较重的负载。
什么结果最好?
您的目标是使Requests/sec
尽可能高,而Latency
尽可能低。
理想情况下,延迟不应该太高,至少对于网页来说是这样。带有资产的页面加载时间限制在大约 2 秒 或更短时是最佳的。
现在你可能会问自己:延迟为3.82ms
的550.90 Requests/sec
是一个好的结果吗?不幸的是,没有简单的答案。这取决于许多因素,例如:
-
客户数量,正如我们之前讨论过的
-
服务器资源 - 是大实例还是小实例?
-
为应用程序服务的机器数量
-
您的服务类型 - 是提供静态文件的缓存还是提供动态响应的广告服务器?
-
数据库类型、数据库集群大小、数据库连接类型
-
请求和响应类型 - 它是一个小的 AJAX 请求还是一个胖 API 调用?
-
还有很多
第 6 步 — 采取措施改善延迟
如果您对自己的服务表现不满意,您可以:
-
调整您的服务 - 检查您的代码,看看可以更有效地完成哪些工作
-
检查你的数据库,看看它是否是你的瓶颈
-
垂直扩展 - 向您的机器添加资源
-
水平扩展 - 添加另一个服务实例并将其添加到负载均衡器
-
添加缓存层
有关应用程序改进的更详细讨论,请查看改进生产 Web 应用程序服务器设置的 5 种方法。
请记住在应用更改后对您的服务进行基准测试 - 只有这样您才能确定您的服务已经改进。
就是这样,你可能会想,如果没有这个 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地址
-
从线程 setup 开始
-
执行压力测试阶段,称为运行阶段
-
最后一步被简单地称为完成
当使用多个线程时,您将有一个解决阶段和一个完成阶段,但有两个设置阶段和两个运行阶段:
此外,运行阶段可以分为三个步骤:init、request 和 response。
根据提供的图表和文档我们可以在 Lua 脚本中使用以下方法:
-
setup(thread)
:在所有线程都已初始化但尚未启动时执行。用于向线程传递数据 -
init(args)
:每个线程初始化时调用
此函数接收脚本的额外命令行参数,这些参数必须与 wrk 参数隔开,为--
。
例子:
wrk -c3 -d1s -t2 -s /scripts/debug.lua http://$APP1_PRIVATE_IP:3000 -- 调试真
request()
:需要为每个请求返回HTTP对象。在这个函数中我们可以修改方法、标题、路径和正文
使用wrk.format
辅助函数来塑造请求对象。
例子:
return wrk.format(方法、路径、标题、正文)
-
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 表单处理程序:使用 HTML 表单的
action
属性中的地址:
<form actionu003d"/login.php">
...
</form>
POST
API 端点:如果您有一个 RESTful API,请使用您创建文章的端点:
发布/文章
首先在 wrk1 Droplet 上创建一个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
标头指定为 MIME 类型的 HTML 表单使用。
在我们开始基准测试之前,这里有一张图表,可以帮助您直观地了解脚本、Docker 容器和应用服务器之间的关系:
现在是关键时刻 - 使用此命令对应用程序进行基准测试(在 wrk1 Droplet 上执行):
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 路径
另一个常见的需求是同时测试应用程序的多个路径。
让我们在data
目录中创建一个名为paths.txt
的文件,并添加我们要在基准测试期间使用的所有路径。
cd ~
mkdir data
nano data/paths.txt
在下面找到data/paths.txt
的示例:
路径.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
:
多个 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 Droplet 上执行)。您会注意到我们添加了一些换行符以便于复制:
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 的高级请求
现在您可能认为其他基准测试工具也可以进行这些类型的测试。但是,wrk 还能够使用 JSON 或 YAML 格式处理高级 HTTP 请求。
例如,您可以加载详细描述每个请求的 JSON 或 YAML 文件。
作者在作者的技术博客上发布了一个带有 JSON 请求的高级示例。
您可以使用 wrk 和 Lua 对您能想到的任何类型的 HTTP 请求进行基准测试。
结论
阅读本文后,您应该能够使用 wrk 对您的应用程序进行基准测试。作为旁注,您还可以看到 Docker 的美妙之处以及它如何极大地减少您的应用程序和测试环境的设置。
最后,您可以使用带有 wrk 的 Lua 脚本来处理高级 HTTP 请求。
更多推荐
所有评论(0)