本文重点介绍称为开源HTTP基准测试工具WRK,它可以在高负荷下测量HTTP服务的延迟。
延迟是指请求发生的时间(通过wrk)和收到响应的时刻(来自服务)之间的时间间隔。这可用于模拟访问者在使用浏览器或任何其他发送HTTP请求的方法访问网站时在您的网站上遇到的延迟。
wrk对于测试任何依赖HTTP的网站或应用程序非常有用,例如:
测试无法与真实用户进行比较,但它们应该能够很好地估计预期延迟,以便您可以更好地规划基础架构。测试还可以让您深入了解性能瓶颈。
它非常稳定,并且由于其多线程特性,可以模拟高负载。wrk的最大特点是它能够集成Lua脚本,这增加了许多可能性,例如:
ab
,这个功能也是Apache HTTP服务器基准测试工具所不具备的。我们将在本教程中使用的基础结构如下图所示:
如您所见,我们将在非常简单的场景中使用wrk。我们将在Node.js应用程序上对Express进行基准测试。
我们将启动两个腾讯CVM:一个用于生成负载的wrk,另一个用于应用程序。如果他们在同一个盒子上,他们会竞争资源,我们的结果将不可靠。
基准测试的机器应该足够强大以处理受压系统,但在我们的情况下,应用程序非常简单,我们将使用相同尺寸的机器。
较小的腾讯CVM也可以工作,但是你应该期望测试结果有更多的延迟。在实际测试环境中,您的应用服务器应与您打算在生产中使用的大小相同。
为了让我们的生活更轻松,我们将使用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
在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
退出尾巴。
登录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.163
IP地址更改为app1 腾讯CVM的私有IP。此变量仅保存在当前会话中,因此请记住在下次登录使用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/
等待测试运行几秒钟,然后查看结果,我们将在下一步中对其进行分析。
输出:
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
10.135.232.163
,并且测试使用了两个线程。5.01
几秒钟内,wrk可以进行5494
请求和1.05MB
数据传输。结合简单的math(total number of requrests/benchmark duration
),我们得到1096.54
每秒请求的结果。通常,您设置的客户端越多,您应该获得的每秒请求越少。延迟也会增加。这是因为应用程序将承受更重的负载。
什么结果最好?
你的目标是保持Requests/sec
尽可能高和Latency
尽可能低。
理想情况下,延迟不应该太高,至少对于网页而言。当资产大约两秒或更短时,资产的页面加载时间限制是最佳的。
现在你可能会问自己: 550.90 Requests/sec
和 3.82ms
的延迟是否是一个好的结果?不幸的是,没有简单的答案。这取决于许多因素,如:
如果您对服务表现不满意,您可以:
请记住在对其进行更改后对您的服务进行基准测试 - 只有这样才能确保您的服务得到改进。
你可能会想,如果没有Lua的话,那事情可能就是这样了。。。
因为wrk有一个内置的LuaJIT(Lua的即时编译器),所以可以使用Lua脚本进行扩展。正如介绍中所提到的,这为wrk增加了许多功能。
在wrk中使用Lua脚本很简单。只需将文件路径附加到-s
标志即可。
因为我们在Docker中使用wrk,所以我们必须先与容器共享此文件。这可以通过Docker的-v
选项实现。
在通用形式中,使用调用的脚本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执行以下执行阶段:
使用多个线程时,您将拥有一个分辨率阶段和一个完成阶段,但有两个设置阶段和两个运行阶段:
此外,运行阶段可以分为三个步骤:init,request和response。
根据提供的图表和文档,我们可以在Lua脚本中使用以下方法:
setup(thread)
:所有线程初始化但尚未启动时执行。用于将数据传递给线程init(args)
:初始化每个线程时调用
此函数接收脚本的额外命令行参数,我们必须用--
将该参数与wrk参数分开。
例:
wrk -c3 -d1s -t2 -s /scripts/debug.lua http://$APP1_PRIVATE_IP:3000 -- debug truerequest()
:需要为每个请求返回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
请求通常用于将数据发送到服务器。这可用于基准测试:
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文件。
对于更现实的场景,您应该增加客户端和线程的数量,同时观察延迟参数,以查看应用程序验证用户凭据的速度。
另一个常见需求是同时测试应用程序的多个路径。
让我们创建一个名为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
现在您可能会认为其他基准测试工具也可以执行这些类型的测试。但是,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》
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。