简介

本文重点介绍一个名为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,然后转到设置部分,如下图所示:

Digitalocean 查找 Droplets 私有 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上进出本地容器的所有流量,以及在端口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.163IP 地址更改为您的 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

  • 对我们应用程序的/路径进行基准测试

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

下图显示了这种情况:

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 步 - 评估输出

输出:

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.82ms550.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 Lua 脚本周期

wrk 执行以下执行阶段:

  • 解析域的IP地址

  • 从线程 setup 开始

  • 执行压力测试阶段,称为运行阶段

  • 最后一步被简单地称为完成

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

wrk 两个线程的 Lua 脚本循环

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

wrk Lua 脚本周期 - 运行阶段

根据提供的图表和文档我们可以在 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>
  • POSTAPI 端点:如果您有一个 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 容器和应用服务器之间的关系:

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 请求。

Logo

Ubuntu 社区为您提供最前沿的新闻资讯和知识内容

更多推荐