介绍
本文重点介绍称为开源HTTP基准测试工具WRK ,衡量你的HTTP服务,在高负荷的延迟 。
延迟是指已请求(由WRK)的时刻,被接收到的响应的时刻(从服务)之间的时间间隔。 这可用于模拟访问者在使用浏览器或任何其他发送HTTP请求的方法访问网站时遇到的延迟。
wrk对于测试任何依赖HTTP的网站或应用程序很有用,例如:
测试所不能比拟的 真实用户 ,但是他们应该给你预期的延迟的一个很好的估计 ,因此您可以更好地规划你的基础设施。 测试还可以帮助您了解性能瓶颈。
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
您应该得到以下或类似的输出:
OutputDocker 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
输出:
Output10.135.232.163
您的私人IP地址将不同,因此请记下它。
您也可以从私有IP digitalocean.com控制面板。 只需选择你的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
,及到对端口的主机私有IP 3000
。
下图描述了这种情况:
现在,随着测试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这很容易。 只需下载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
输出:
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
当前配置摘要:
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执行以下执行阶段:
- 解析域的IP地址
- 与线程设置开始
- 执行压力测试阶段,这就是所谓的运行相
- 最后一步是简称为完成
当使用多个线程时,您将有一个分辨率阶段和一个完成阶段,但有两个设置阶段和两个运行阶段:
此外, 运行阶段可分成三个步骤: 初始化 , 请求和响应 。
根据所呈现的图表和文档,我们可以用一个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.lua
上wrk1Droplet文件。
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容器和应用程序服务器之间的关系:
现在的关键时刻-基准使用此命令的应用程序(在wrk1Droplet执行):
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
阵列。 然后,对每个请求,采取下一个路径。
要运行这个测试,请使用以下命令(执行对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
输出:
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请求来处理额外的问题。