七爪源码:使用 Toxiproxy 进行弹性测试
们现在构建的软件通常比 10 或 20 年前的软件复杂得多。 传统的 LAMP 堆栈或三层单体通常已被丢弃,取而代之的是微服务架构,并由某种编排平台(如 Kubernetes)提供支持。 让我们继续讨论在大多数情况下是否真的需要这种额外的复杂性(剧透——恕我直言,它可能不是),并承认这些先进的架构带来了很多好处,但也带来了很多额外的问题。

问题在于,在任何复杂的系统中——一个有许多活动部件的系统,尤其是当分布在网络中时——故障变得司空见惯。我找不到那句话,但(套用一下)一段时间前我脑海中浮现的一句话——
在任何足够复杂的软件系统中,几乎不可能所有部分都始终正常工作。故障总是发生在系统的某个地方。
鉴于此,只考虑晴天场景的编码是愚蠢的。这不是新闻。故障注入和混沌工程的整个学科已经变得司空见惯,以确保我们识别和修补可能存在的漏洞。
我们可以在这里选择许多工具,从 Chaos Monkey 的祖先到更现代的 SaaS 产品(如 Gremlin),甚至是平台本身内置的工具,例如。伊斯蒂奥。
不过,在这种情况下,我们不会讨论这些重量级人物,而是一个有用的小工具,当网络上发生某些异常或错误情况时,我们可以使用它来验证我们编写的代码是否具有弹性。
Toxiproxy
Toxiproxy 是一种轻量级的故障注入网络代理,最初由 Shopify 开发(并广泛使用)。 Toxiproxy 可用于模拟某些网络条件,它非常适合我们想要控制的情况——特别是对于 CI、测试和测试环境。它不会从概率上拆除我们的基础设施,但非常适合测试关键环境。
让我们看一些我们可以使用 Toxiproxy 模拟的场景。然而,首先,我们需要将它连接到某些东西上。
让我们先将 Toxiproxy 放在 Simple JSON/plain-text API to obtain the current time in, and related data about, a timezone. 前面,这是一个告诉我们时间的开放 API。例如,英国的(写作)时间是:
curl -s 'http://worldtimeapi.org/api/timezone/Europe/London' | jq
{
"abbreviation": "BST",
"client_ip": "195.94.111.1",
"datetime": "2022-07-04T07:08:42.499494+01:00",
"day_of_week": 1,
"day_of_year": 185,
"dst": true,
"dst_from": "2022-03-27T01:00:00+00:00",
"dst_offset": 3600,
"dst_until": "2022-10-30T01:00:00+00:00",
"raw_offset": 0,
"timezone": "Europe/London",
"unixtime": 1656914922,
"utc_datetime": "2022-07-04T06:08:42.499494+00:00",
"utc_offset": "+01:00",
"week_number": 27
}
我们需要一种一致的方法来测试通过 Toxiproxy 运行的流量,因此我们将使用 Gatling(一个众所周知的性能测试工具),将请求限制为每秒 - 我主要希望它用于漂亮的图表而不是绝对垃圾 API .

Gatling 的基本案例图表显示了一个健康的系统。 60 个请求,没有失败,99% 的响应时间为 63 毫秒——还不错。
增加一秒钟的延迟
我们的第一个失败案例是让事情变慢。
Toxiproxy 允许您注入称为“毒物”的行为。 可以想象,有毒物质会污染具有不良特性的 API。 每种有毒物质都有几个关键方面:
- type — 我们要应用的行为的类型
- stream——我们想要应用行为的 api/service
- toxicity——我们应该应用该行为的请求的百分比(0 到 1 之间的数字)
- attributes — 类型特定参数,进一步改变有毒物质的行为。
为了在我们的 API 调用中注入缓慢,我们将利用延迟毒性并将延迟设置为一秒。 这将应用于所有请求(毒性为 1)。 我们可以通过对 Toxiproxy 的 API 调用来设置它。 让我们试试看。
curl --location --request POST 'http://localhost:8474/proxies/time/toxics' \
--header 'Content-Type: text/plain' \
--data-raw '{
"name": "latency",
"type": "latency",
"stream": "upstream",
"toxicity": 1.0,
"attributes": {
"latency": 1000,
"jitter": 0
}
}'

将结果与基线进行比较,您会发现 Toxiproxy 已明显启动。 我们将响应时间向上移动了一秒(加特林将超过 800 毫秒的任何内容标记为警告,因此以黄色突出显示)。 一个好的开始,现在我们还能做什么?
延迟增加 0-2 秒(抖动)
缓慢的上游服务通常是不可预测的。 我们真正想要的是响应时间的差异。 我们可以通过利用 jitter 属性来实现这一点,给响应带来随机感。 抖动会在我们设置的基线上增加或减少额外的延迟。 我们现在应该看到添加了 0 毫秒到 2000 毫秒之间的延迟。
curl --location --request POST 'http://localhost:8474/proxies/time/toxics' \
--header 'Content-Type: text/plain' \
--data-raw '{
"name": "latency",
"type": "latency",
"stream": "upstream",
"toxicity": 1.0,
"attributes": {
"latency": 1000,
"jitter": 1000
}
}'

这次响应时间分布更广,从最小的 129ms 到另一边的 4693。
使用 RESET_PEER 失败 ~ 一半的上游请求
如果我们想完全拒绝一个请求,模拟一个失败的网络状况怎么办? 我们可以使用 reset_peer 毒性来做到这一点。
让我们添加有毒物质,但只有 50% 的时间(平均)使用它。 在拒绝请求之前,我们还将使失败的响应挂起 5 秒。
curl --location --request POST 'http://localhost:8474/proxies/time/toxics' \
--header 'Content-Type: text/plain' \
--data-raw '{
"name": "reset_peer",
"type": "reset_peer",
"stream": "upstream",
"toxicity": 0.5,
"attributes": {
"timeout": 5000
}
}'

不完全是 50% 的失败率,但这是有道理的概率。 不过,您肯定可以看到效果,包括我们在失败调用中添加的 5 秒挂起 — 查看 max 和 std dev 字段。
数据包拆分
我们的最后一个例子——对于这么小的请求来说并不是特别容易演示的——是数据包拆分。 通过将 TCP 请求拆分为许多较小的请求,我们可以通过另一种方式模拟网络上的奇怪行为,尽管在这种情况下,它主要只是另一种导致延迟的方式。
我们将使用有毒的切片器将我们的请求分成 2 到 6 个字节的数据包,给它们每个大约 500 毫秒的延迟。
curl --location --request POST 'http://localhost:8474/proxies/time/toxics' \
--header 'Content-Type: text/plain' \
--data-raw '{
"name": "slicer",
"type": "slicer",
"stream": "upstream",
"toxicity": 0.5,
"attributes": {
"average_size": 4,
"size_variation": 2,
"delay": 500000
}
}'

同样,我们的响应差异很大。 清楚地将这些请求分成许多数据包(有些有延迟),可以将我们的响应时间从低端的 176 毫秒扩展到最大超过 10 秒。
结论
Toxiproxy 是一个很好的小工具,可以帮助您强制网络错误并帮助您提高应用程序的弹性,希望我们已经在这里展示了这一点。 无论如何,它肯定不是一个全面的混沌工程套件——它也不假装是——而是一种在该领域获得快速胜利的方法。

浙公网安备 33010602011771号