我们如何使用HAProxy实现单机200万SSL连接

导读:架构师需要精确的了解服务的支撑能力,也希望通过调优来发挥单个节点最大的价值。本文分享了压测及调优 HAProxy 实现 200 万并发 SSL 连接的过程,由高可用架构翻译,转载请注明出处。

先观察上面截图,可以看到两个关键信息:

  • 这台机器已经建立了 238 万个 TCP 连接
  • 使用内存大约在 48G。

下面将会介绍在单个 HAProxy 机器上实现这种规模访问所需的配置。本文是负载测试 HAProxy 系列文章的最后一篇。有时间的读者建议阅读本系列的前两篇(见文末链接),它将帮助您了解相应的内核调优方法。

在这个配置过程中,我们也使用了很多小组件帮助我们达到目标。

在展开最终 HAProxy 配置之前,我想给大家回顾一下负载测试的历程及想法,心急的读者可以直接跳到文章后段查阅相关 HAProxy 配置。

测试目标

我们要测试的组件是 HAProxy 1.6 版。生产环境是在 4 核 30 G 的机器上运行该软件,当前所有的连接都是非 SSL 的。

测试目标有两方面:

  1. 当将整个负载从非 SSL 连接转移到 SSL 连接时, CPU 使用率增加的百分比。CPU 的使用率肯定会增加,这是由于 5 次握手的加长和数据包加密的开销所带来。
  2. 其次,希望能够测试单个 HAProxy 每秒请求数和最大并发连接数的上限

目标一主要因为业务方面功能需要通过 SSL 进行通信。 目标二是为了可以在生产环境中部署最少规模的 HAProxy 机器。

组件和配置

  • 使用多台客户端机器来执行 HAProxy 压力测试。
  • 有各种配置的 HAProxy 1.6 的机器
    • 4核,30G
    • 16核,30G
    • 16核,64G
  • 相关后端服务器,用于支持所有并发访问。

HTTP 和 MQTT

我们的整个基础设施支持两种协议:

  • HTTP
  • MQTT

在我们的技术栈中,没有使用 HTTP 2.0,因此在 HTTP 上没有长连的功能。所以在生产环境中,单个 HAProxy 机器(上行 + 下行)的最大数量的 TCP 连接在(2 * 150k)左右。虽然并发连接数量相当低,但每秒请求的数量却相当高。

另一方面,MQTT 是一种不同的通信方式。它提供高质量的服务参数和持久的连接性。因此,可以在 MQTT 通道上使用双向长连通信。对于支持 MQTT(底层 TCP)连接的 HAProxy,在高峰时段会看到每台机器上大约有 600 – 700k 个 TCP 连接。

我们希望进行负载测试,这将为我们提供基于 HTTP 和 MQTT 连接的精确结果。

有很多工具可以帮助我们轻松地测试 HTTP 服务器,并且提供了高级功能,如结果汇总,将文本转换为图形等。然而,针对 MQTT,我们找不到任何压力测试工具。我们确实有一个自己开发的工具,但是它不够稳定,不足以支持这种负载。

所以我们决定使用客户端测试 HTTP 负载,并在 MQTT 服务器使用相同配置。

初始化设置

考虑到相关内容对于进行类似的压力测试或调优的人来说有帮助,本文提供了很多相关细节,篇幅稍微有些长。

  • 我们采用了一台 16 核 30G 机器来运行 HAProxy,考虑到 HAProxy 的 SSL 产生的 CPU 巨大开销,因此没有直接使用目前生产环境。
  • 对于服务器端,我们使用了一个简单的 NodeJs 服务器,它在接收到 ping 请求时用 pong 进行回复。
  • 对于客户端,我们最终使用 Apache Bench。使用 ab 的原因是因为它是一个大家熟悉和稳定的负载测试工具,它也提供了很好的测试结果汇总,这正是我们所需要的。

ab 工具提供了许多有用的参数用于我们的负载测试,如:

  • -c,指定访问服务器的并发请求数。
  • -n,顾名思义,指定当前负载运行的请求总数。
  • -p,包含 POST 请求的正文(要测试的内容)。

如果仔细观察这些参数,您会发现通过调整所有这三个参数可以进行很多排列组合。示例 ab 请求将看起来像这样

ab -S -p post_smaller.txt -T application/json -q -n 100000 -c 3000

http://test.haproxy.in:80/ping

这样的请求的示例结果看起来像这样

我们感兴趣的数字是:

  • 99% 的返回请求的响应延迟时间。
  • Time per request:每个请求的时间
  • No. of failed requests:失败请求数。
  • Requests per second: 每秒请求量

ab 的最大问题是它不提供控制每秒发起请求量,因此我们不得不调整 -c 并发级别以获得所需的每秒钟请求数,并导致很多后文提到的问题和错误。

测试图表

我们不能随机地进行多次测试来获得结果,这不会给我们提供任何有意义的信息。我们必须以某种具体的方式执行这些测试,以便从中获得有意义的结果。来看看这个图。

该图表明,在某一点之前,如果不断增加请求数量,延迟将几乎保持不变。然而, 达到某个临界点,延迟将开始呈指数级增长。这就是该机器的临界点。

Ganglia

在提供一些测试结果之前,我想提一下 Ganglia。

Ganglia 是用于高性能计算系统(如集群和网格)的可扩展分布式监控系统。

看看截图,了解 Ganglia 是什么,以及它提供的关于底层机器的信息。

通过 Ganglia 可以监测 HAProxy 机器上一些重要参数。

  1. TCP  established 这告诉我们在系统上建立的 TCP 连接总数。注意:这是上行和下行连接的总和。
  2. packets sent and received 发送和接收的 TCP 数据包的总数。
  3. bytes sent and received 这将显示发送和接收的字节数。
  4. memory 随着时间的推移使用的内存数。
  5. network 通过线路发送数据包而消耗的网络带宽。

以下是通过通过负载测试找到的已知限制。

700k TCP 连接,

50k 发送包数量,60k 接收包数量,

10-15MB 发送及接收的字节数,

14-15G 内存峰值,

7MB 带宽。

所有这些值都是基于每秒数据

HAProxy Nbproc

最初,当我们开始测试 HAProxy 时,发现使用 SSL 情况下,CPU 很早就到了瓶颈,而每秒请求数都很低。 在使用 top 命令后,发现 HAProxy 只使用 1 个 CPU 核。 而我们还有 15 个以上的核没用。
Google 了 10 分钟后,我们在 HAProxy 中找到某个设置,可以让 HAProxy 使用多个核。

它被称为 nbproc,具体设置请看这篇文章 [8]:
调整此设置是我们的负载测试策略的基础。  让我们可以方面的进行 HAProxy 组合以便测试。

使用 AB 进行压力测试

当开始负载测试之旅时,我们不清楚应该测量的指标和需要达到的目标。

最初,我们只有一个目标: 通过改变所有下面提到的参数来找到临界点

我保留了各种负载测试结果的表格。 总而言之,做了 500 多次测试,以达到最终的效果。 您可以清楚地看到,每次测试都有很多不同的部分。

单客户端问题

我们看到客户端正在成为瓶颈,因为我们不断增加每秒的请求数。 ab 使用单个核,从文档中可以看出,它不提供使用多核的功能。
为了有效地运行多个客户端,我们发现一个有用的 Linux 工具叫做 Parallel [7]。 顾名思义,它可以帮助您同时运行多个命令来达到并行的目的。 正是我们想要的。
看一下使用 Parallel 运行多个客户端的示例命令。

上述命令将运行 3 个 ab 客户端击访问同一个 URL。 这有助于我们消除客户端瓶颈。

Sleep 及 Times 参数的问题

下面是 Ganglia 中的一些参数。让我们简单讨论一下。

  1. packets sent and received 为了产生更多数据,可以在 post 请求中添加更多数据
  2. tcp_established 这是想实现的目标。想象一下,如果单个 ping 请求大约需要一秒钟,那么每秒需要大约 700k 个请求来达到 tcp_established 的目标。现在这个数字在生产环境中可能看起来更容易达到,但是在测试场景中不太可能达到。

我们在 POST 调用中加入了一个 sleep 参数,它指定了服务端发送返回之前需要 sleep 的毫秒数。这将模拟长时间运行的生产环境请求。如果让请求 sleep 20 分钟的话,只需要每秒发出 583 个请求就能达到 700k 并发连接的标准。

此外,我们还在 POST 调用中引入了另一个参数: times。服务器在返回请求时应该在 TCP 连接上写入响应的指定次数,这有助于模拟更多的数据。

Apache Bench (AB) 的问题

虽然使用 AB 也得到了不少测试结果,但同时也遇到了很多问题。我不会在这里提到所有问题,因为不是这篇文章重点(下面介绍另一个客户端)。

我们非常满意从 ab 上获得的结果,但是它不支持在一段时间内生成所需指定的 TCP 连接数。不知何故,我们设置的 sleep 参数在 ab 上无法生效。

虽然在一台机器上可以运行多个 ab 客户端并且可以用工具合并结果,但是在多台客户机上运行此设置对我们来说仍然是一件痛苦的事情。那时我还没有听说过 pdsh [4] 这个工具。

此外,我们也没有关注过超时的问题。在 HAProxy,ab 客户端和服务器上有一些默认的超时设置,我们完全忽略了这些。后文会讲到。

我们一开始就提到通过临界点图来检测系统的上限,但讲了这么多有点偏离了最主要目标。然而,要得到有意义的结果只能着眼于这一点。

使用 AB 碰到的一个问题是到了某个点 TCP 连接数不再增加。我们有大约 40 – 45 个客户端运行在 5 – 6 台客户端机上,但依然不能达到想要的规模。理论上,TCP 连接的数量应该随着 sleep 时间的增加而增加,但对我们来说并非如此。

引入 Vegeta

因此我们需要寻找一个负载测试工具,这些工具需要具有更好的扩展性和更好的功能性,最终,我们找到了 Vegeta [6]。
从我的个人经验来看,我已经看到 Vegeta 具有极高的扩展性,与 ab 相比,它提供了更好的功能。 在我们的负载测试中,单个 Vegeta 客户端能够产生相当于 15 倍 ab 的吞吐量。

下面,我将提供使用 Vegeta 的负载测试结果。

使用 Vegeta 进行负载测试

首先,看看我们用来运行一个 Vegeta 客户端的命令。 进行测试的命令称为 attack:(酷吧?)

我们太喜欢 Vegeta 提供的参数了,来看看下面的一些参数。

  • -cpus = 32 指定此客户机要使用的 CPU 核数。 由于要生成的负载量,我们不得不将客户机扩展到 32 核 64G。 虽然上面的速度也不是特别高。 但是当有大量处于 sleep 状态的连接时,维持这些连接也会产生比较大的开销。
  • -duration = 10m 我想这是不言自明的。如果没有指定任何持续时间,测试将永远运行。
  • -rate = 2000 每秒请求的数量。

所以如上图所示,我们在一台 4 核机器上每秒达到了 32k 请求量。 如果你记得临界点图,在这种情况下,非 SSL 请求的临时点是 31.5k。
从负载测试中看更多的结果。

16k 的 SSL 连接也不错。 请注意,在我们的负载测试过程中,必须从头开始,因为我们采用了一个新的客户端,它给了我们比 ab 更好的结果。 所以不得不再来一遍。

CPU 核数的增加导致机器在未达到 CPU 限制前,每秒可以用的请求数增加。

如果将 CPU 核数从 8 个增加到 16 个,我们发现每秒的请求数量并没有大幅度增加。如果在生产环境中使用 8 核机器,那么我们不会分配所有的核给 HAProxy,或者是它的任何其他进程。 所以我们决定用 6 核机器进行一些测试,看看是否能得到可接受的数字。

结果还不错。

引入 sleep

我们现在对负载测试结果非常满意。 然而,这并没有模拟真正的生产场景。 当我们引入 sleep,才开始模拟生产环境的情况。

echo “POST https://test.haproxy.in:443/ping” | vegeta -cpus=32 attack -duration=10m  -header=”sleep:1000″  -body=post_smaller.txt-rate=2000 -workers=500  | tee reports.bin | vegeta report

因此,x 毫秒的随机 sleep 时间将导致服务器 sleep 时间为 0 < x < 1000 。 因此上述负载测试将给出平均 ≥ 500ms 的延迟。

最后一个单元格中的含义是 TCP established, Packets Rec, Packets Sent

从表中可以看到,6 核机器可以支持的最大请求量从 20k 减少到 8k。 显然,sleep 有其影响,影响的是 TCP 连接的数量。 然而这距离我们设定的 700K 目标还很远。

里程碑 #1

我们如何增加 TCP 连接的数量? 很简单,不断增大 sleep 时间,连接数应该上升。 我们一直增加 sleep 时间并在 60 秒的 sleep 时间停了下来。 这意味着大约 30 秒的平均延迟。

Vegeta 可以提供成功请求百分比的结果参数。 我们看到,在上述的 sleep 时间,只有 50% 的调用是成功的。 请看下面的结果。

我们达到了 400 万个 TCP 连接,在每秒 8k 请求和 60s 的 sleep 时间的情况下。 60000R 的 R 表示随机。

我们的第一个的发现是,在 Vegeta 中有一个默认的超时时间是 30 秒,这就解释了为什么 50% 的请求会失败。 所以我们在后续测试中将超时调整到 70 秒,并随着需求的变化而不断变化。

在客户端调整超时值之后,我们可以轻松地达到 700k 标准。 唯一的问题是这些不可持续,只是峰值的数据。 系统达到了 600k 或 700k 的峰值链接,但并没有坚持很长时间。

但是我们想要得到图上连接持续很久的效果

这显示了稳定保持 780k 连接的状态。如果仔细查看上面的统计信息,每秒的请求数量非常多。然而,在生产环境中,我们在单个  HAProxy 机器上的请求数量要少得多(约 300 个)。

我们确信,如果减少生产环境的 HAProxy 的数量(约 30 个,这意味着每秒 30 * 300〜9k 的连接),我们将会达到机器 TCP 连接限制,而不是 CPU。

所以我们决定实现每秒 900 个请求、30MB/s 的网络流量,以及 210 万 TCP 连接。我们选用这些数字,因为这将是单个生产环境 HAProxy 机器的 3 倍流量。

到目前为止,我们已经配置了 HAProxy 使用 6 核。我们只想测试 3 核,因为这是我们在我们的生产机器上使用的最简单的方法(如前所述,我们的生产机器是 4 核 30G,所以用 nbproc = 3 进行更改将是最简单的)。

里程碑 #2

现在我们对每秒请求的最大限制可以随机器不同而变化,所以我们只剩下一个任务,如上所述,实现 3 倍的生产负载:

  • 每秒 900 个请求
  • 建立了 210 万个 TCP 链接。
  • 30 MB/s 网络。

在 220k 的测试环境下,我们再次陷入僵局。 无论客户机数量多少或睡眠时间多少,TCP 连接数似乎都停留在那里。

我们来看一些估算数据。 220k TCP 连接,每秒 900 个请求 = 110,000 / 900〜= 120 秒。达到了 110k,因为 220k 连接包括上行和下行。

当我们在 HAProxy 开启日志时,我们怀疑 2 分钟是系统某处的限制。 我们可以看到 120,000 ms 是日志中大量连接的总时间。

在进一步调查中,我们发现 NodeJs 的默认请求超时为 2 分钟。 瞧!

但我们的高兴显然很短暂,在 130 万,HAProxy 连接数突然下降到 0,并再次开始增长。我们很快检查了 dmesg 命令,里面可以查到 HAProxy 进程一些有用的内核信息。

基本上,HAProxy 进程已经耗尽内存。因此,我们决定增加机器内存,并将其转移到 nbproc = 3 的 16 核 64GB 的机器,经过调整,我们终于可以达到 240 万长连。

后端代码

下面是正在使用的后端服务器代码。 我们还在服务器代码中使用 statsd 来获取客户端接收的每秒请求的统计数据。

我们还有一个小脚本运行多个服务器。 我们有 8 台机器,每台机器部署了 10 个后端服务。 我们真的认为有条件的话可以进行无限扩容进行压测。

客户端代码

对于客户端,每个 IP 有最大 63k TCP 连接的限制。 如果您不确定这个概念,请参阅本系列之前的文章。

所以为了实现 240 万个连接(双向,来自客户机的是 120 万),我们需要约 20 台机器。 我们在所有机器上运行 Vegeta 命令,甚至找到了一种方法来使用像 csshx [3] 这样的工具,但仍然需要合并所有的 Vegeta 客户端的结果。

查看下面的脚本。

Vegeta 提供了名为 pdsh [4] 的工具信息,可让您在多台计算机上同时运行命令。 此外,Vegeta 可以让我们将多个结果合并成一个,这就是我们想要的。

HAProxy 配置

下面可能是很多读者最关心的,我们在测试中使用的 HAProxy 配置。 最重要的部分是 nbproc 和 maxconn 参数。 maxconn 设置 HAProxy 允许提供的最大 TCP 连接数(单向)。

对 maxconn 设置的更改导致 HAProxy 进程的 ulimit 增加。 看看下面

最大打开文件已增加到 400 万,因为 HAProxy 的最大连接数设置为 200 万。

参阅文章 [5] 获得更多 HAProxy 优化。

相关链接

  1. 系统一 https://medium.com/@sachinmalhotra/load-testing-haproxy-part-1-f7d64500b75d
  2. 系列二 https://medium.com/@sachinmalhotra/load-testing-haproxy-part-2-4c8677780df6
  3. csshx: https://github.com/brockgr/csshx
  4. pdsh: https://github.com/grondo/pdsh
  5. haproxy 配置: https://www.linangran.com/?p=547
  6. Vegeta: https://github.com/tsenart/vegeta
  7. Parallel: http://www.shakthimaan.com/posts/2014/11/27/gnu-parallel/news.html
  8. nbproc setup: http://blog.onefellow.com/post/82478335338/haproxy-mapping-process-to-cpu-core-for-maximum文章来自微信公众号:高可用架构

你可能感兴趣的:(运维干货,HAProxy,SSL连接)