该博客是系列文章的一部分,该系列文章更深入地介绍了Envoy Proxy和Istio.io ,以及它如何实现更优雅的连接和管理微服务的方式。 跟随我@christianposta ,紧跟这些博客文章的发布。 我认为在下一个系列中介绍的流程将是这样的:
- 什么是Envoy代理 ,它如何工作?
- 如何使用Envoy Proxy实现一些基本模式?
- Istio Mesh如何适合这张照片
- Istio Mesh的工作方式,以及如何通过Envoy跨集群启用高阶功能
- Istio Mesh身份验证的工作方式
这是接下来几部分的想法(将在发布时更新链接):
- 断路器(第一部分)
- 重试/超时(第二部分)
- 分布式跟踪(第三部分)
- 普罗米修斯的度量标准收集(第四部分)
- 接下来的部分将介绍更多的客户端功能(服务发现,请求阴影,TLS等),只是不确定哪一部分将是:
第一部分– Envoy代理的电路中断
第一篇博客文章向您介绍Envoy Proxy的断路功能实现 。 这些演示故意是简单的,因此我可以分别说明这些模式和用法。 请下载此演示的源代码,然后继续!
该演示由客户端和服务组成。 客户端是一个Java http应用程序,它模拟对“上游”服务进行http调用(请注意,我们在这里使用Envoys术语,并且贯穿此repo )。 客户端打包在名为docker.io/ceposta/http-envoy-client:latest
的Docker映像中。 http-client Java应用程序旁边是Envoy Proxy的实例。 在此部署模型中,Envoy与服务(在本例中为http客户端)一起作为边车进行了部署。 当http客户端发出出站呼叫(到“上游”服务)时,所有呼叫都通过Envoy代理端进行。
这些示例的“上游”服务是httpbin.org 。 httpbin.org允许我们轻松模拟HTTP服务行为。 太棒了,所以如果您没有看过,请检查一下。
断路器演示具有其自己的 envoy.json
配置文件。 我绝对建议您查看配置文件各部分的参考文档,以帮助您了解完整的配置。 datawire.io的好伙伴还为Envoy及其配置提供了不错的介绍 ,您也应该查看一下。
运行断路器演示
要运行circuit-breaker
演示,请熟悉演示框架 ,然后运行:
./docker-run.sh -d circuit-breaker
"circuit_breakers": {
"default": {
"max_connections": 1,
"max_pending_requests": 1,
"max_retries": 3
}
}
此配置使我们能够:
- 限制我们将与上游集群建立的HTTP / 1连接的数量,如果经过这些集群,则会使它们短路
- 限制要排队/等待连接可用的请求数,如果我们过去则将其短路
- 限制在任何给定时间(假设有重试策略)的并发重试总数,有效地设置重试配额
让我们看一下每种配置。 由于两个原因,我们现在将忽略最大重试设置
- 我们的设置并没有多大意义。 我们不能进行3次并发重试,因为我们只允许1个HTTP连接和1个排队请求
- 实际上,此演示没有任何重试策略。 我们可以在
retries
演示中看到重retries
无论如何,此处的重试设置使我们避免了大的重试风暴-在大多数情况下,当处理与群集中所有实例的连接性时,这些问题可能会加剧问题。 这是retries
演示的重要设置。
max_connections
让我们看看当应用程序中的太多线程试图建立与上游群集的太多并发连接时,特使会做什么。
回想一下我们上游httbin
群集的断路设置如下(请参阅此处的完整配置 ):
"circuit_breakers": {
"default": {
"max_connections": 1,
"max_pending_requests": 1,
"max_retries": 3
}
}
如果查看./circuit-breaker/http-client.env
设置文件,我们会看到最初我们将运行一个线程,该线程创建一个连接并进行五个调用并关闭,从而开始:
NUM_THREADS=1
DELAY_BETWEEN_CALLS=0
NUM_CALLS_PER_CLIENT=5
URL_UNDER_TEST=http://localhost:15001/get
MIX_RESPONSE_TIMES=false
让我们验证一下。 运行演示:
./docker-run.sh -d circuit-breaker
这将使用其客户端库来设置应用程序,并且还将设置Envoy代理。 我们会将流量直接发送给Envoy代理,以为我们处理电路中断。 让我们致电我们的服务:
docker exec -it client bash -c 'java -jar http-client.jar'
我们应该看到如下输出:
using num threads: 1
Starting pool-1-thread-1 with numCalls=5 delayBetweenCalls=0 url=http://localhost:15001/get mixedRespTimes=false
pool-1-thread-1: successes=[5], failures=[0], duration=[545ms]
我们可以看到所有五个呼叫都成功!
让我们看一下Envoy代理收集的一些指标:
./get-envoy-stats.sh
哇! Envoy为我们跟踪了很多指标! 让我们通过以下方式进行grep:
./get-envoy-stats.sh | grep cluster.httpbin_service
这将显示我们配置的名为httpbin_service
上游群集的指标。 快速浏览其中一些统计信息,并在Envoy文档中查找其含义 。 这里要注意的重要事项:
cluster.httpbin_service.upstream_cx_http1_total: 1
cluster.httpbin_service.upstream_rq_total: 5
cluster.httpbin_service.upstream_rq_200: 5
cluster.httpbin_service.upstream_rq_2xx: 5
cluster.httpbin_service.upstream_rq_pending_overflow: 0
cluster.httpbin_service.upstream_rq_retry: 0
这告诉我们我们有1个http / 1连接,总共有5个请求,其中5个以HTTP 2xx
(甚至200
)结束。 大! 但是,如果我们尝试使用两个并发连接会怎样?
首先,让我们重置统计信息:
./reset-envoy-stats.sh
OK
让我们用2个线程调用这些调用:
docker exec -it client bash -c 'NUM_THREADS=2; java -jar http-client.jar'
我们应该看到这样的输出:
using num threads: 2
Starting pool-1-thread-1 with numCalls=5 delayBetweenCalls=0 url=http://localhost:15001/get mixedRespTimes=false
Starting pool-1-thread-2 with numCalls=5 delayBetweenCalls=0 url=http://localhost:15001/get mixedRespTimes=false
pool-1-thread-1: successes=[0], failures=[5], duration=[123ms]
pool-1-thread-2: successes=[5], failures=[0], duration=[513ms]
哇,我们的线程之一取得了5次成功,但其中之一却没有! 一个线程的所有5个请求都失败了! 让我们再次查看Envoy的统计信息:
./get-envoy-stats.sh | grep cluster.httpbin_service
现在,我们的统计数据如下所示:
cluster.httpbin_service.upstream_cx_http1_total: 1
cluster.httpbin_service.upstream_rq_total: 5
cluster.httpbin_service.upstream_rq_200: 5
cluster.httpbin_service.upstream_rq_2xx: 5
cluster.httpbin_service.upstream_rq_503: 5
cluster.httpbin_service.upstream_rq_5xx: 5
cluster.httpbin_service.upstream_rq_pending_overflow: 5
cluster.httpbin_service.upstream_rq_retry: 0
从此输出中,我们可以看到只有一个连接成功! 我们最终获得了5个导致HTTP 200
请求,以及5个最终导致了HTTP 503
请求。 我们还看到, upstream_rq_pending_overflow
已增加到5
。 这表明我们的断路器在这里完成了工作。 它将所有与我们的配置设置不匹配的呼叫短路。
注意,我们已经将max_connections
设置设置为人为的低数字,在这种情况下为1
,以说明Envoy的断路功能。 这不是一个现实的设置,但希望可以说明这一点。
max_pending_requests
让我们运行一些类似的测试来练习max_pending_requests
设置。
回想一下我们上游httbin
群集的断路设置如下(请参阅此处的完整配置 ):
"circuit_breakers": {
"default": {
"max_connections": 1,
"max_pending_requests": 1,
"max_retries": 3
}
}
我们想要做的是模拟在单个HTTP连接上发生的多个同时请求(因为我们只允许max_connections
为1)。 我们希望请求排队,但是Envoy应该拒绝排队的消息,因为我们将max_pending_requests
设置为1。我们希望设置队列深度的上限,并且不允许重试风暴,恶意下游请求,DoS和错误。我们的系统级联。
继续上一节,让我们重置Envoy的统计信息:
./reset-envoy-stats.sh
OK
让我们用1个线程(即1个HTTP连接)调用客户端,但并行发送请求(默认为5个批次)。 我们还将希望随机化发送时的延迟,以便事情可以排队:
docker exec -it client bash -c 'NUM_THREADS=1 && PARALLEL_SENDS=true && MIX_RESPONSE_TIMES=true; java -jar http-client.jar'
我们应该看到类似以下的输出:
using num threads: 1
Starting pool-1-thread-1 with numCalls=5 parallelSends=true delayBetweenCalls=0 url=http://localhost:15001/get mixedRespTimes=true
pool-2-thread-3: using delay of : 3
pool-2-thread-2: using delay of : 0
pool-2-thread-1: using delay of : 2
pool-2-thread-4: using delay of : 4
pool-2-thread-5: using delay of : 0
finished batch 0
pool-1-thread-1: successes=[1], failures=[4], duration=[4242ms]
该死的! 我们的四个请求失败了……让我们检查Envoy的统计信息:
./get-envoy-stats.sh | grep cluster.httpbin_service | grep pending
果然,我们发现其中四个请求被短路了:
cluster.httpbin_service.upstream_rq_pending_active: 0
cluster.httpbin_service.upstream_rq_pending_failure_eject: 0
cluster.httpbin_service.upstream_rq_pending_overflow: 4
cluster.httpbin_service.upstream_rq_pending_total: 1
服务何时完全瘫痪怎么办?
我们已经看到了Envoy在集群中具有用于短路和隔离线程的断路功能,但是如果集群中的节点完全崩溃(或看上去崩溃)怎么办?
Envoy 具有“异常检测”设置 ,可以检测群集中的主机何时不可靠,并且可以将它们完全从群集旋转中弹出(一段时间)。 要了解的一个有趣现象是,默认情况下,Envoy会从负载均衡算法中弹出主机,直到特定点为止。 如果过多(即,> 50%)的主机被认为不健康,Envoy的负载平衡器算法将检测到恐慌阈值 ,并且仅返回对所有主机进行负载平衡。 此紧急阈值是可配置的,并且要获得断电功能以在严重中断期间减轻所有主机的负载(一段时间),可以配置异常值检测设置。 在我们的示例断路器 ) envoy.json
配置中,您可以看到以下内容:
"outlier_detection" : {
"consecutive_5xx": 5,
"max_ejection_percent": 100,
"interval_ms": 3
}
让我们测试一下这种情况,看看会发生什么。 首先,重置统计信息:
./reset-envoy-stats.sh
OK
接下来,让我们用一个URL来调用我们的客户端,该URL将给我们返回HTTP 500
结果。 我们将进行10次通话,因为我们的异常值检测将检查5个连续的5xx响应,因此我们要进行5次以上的通话。
docker exec -it client bash -c 'URL_UNDER_TEST=http://localhost:15001/status/500 && NUM_CALLS_PER_CLIENT=10; java -jar http-client.jar'
我们应该在所有调用都失败的情况下看到这样的响应(正如我们期望的那样:其中至少有5个将返回HTTP 500):
using num threads: 1
Starting pool-1-thread-1 with numCalls=10 parallelSends=false delayBetweenCalls=0 url=http://localhost:15001/status/500 mixedRespTimes=false
pool-1-thread-1: successes=[0], failures=[10], duration=[929ms]
现在,让我们检查Envoy的统计信息,看看到底发生了什么:
./get-envoy-stats.sh | grep cluster.httpbin_service | grep outlier
cluster.httpbin_service.outlier_detection.ejections_active: 0
cluster.httpbin_service.outlier_detection.ejections_consecutive_5xx: 1
cluster.httpbin_service.outlier_detection.ejections_overflow: 0
cluster.httpbin_service.outlier_detection.ejections_success_rate: 0
cluster.httpbin_service.outlier_detection.ejections_total: 1
我们可以看到我们跳出了连续的5xx检测! 我们还从负载平衡组中删除了该主机。
系列
请继续关注 ! 关于超时/重试/跟踪的第二部分和第三部分应该在下周着陆!