使用Envoy 做Sidecar Proxy的微服务模式-1.熔断

本博客是深刻研究Envoy Proxy和Istio.io 以及它如何实现更优雅的方式来链接和管理微服务系列文章的一部分。html

这是接下来几个部分的想法(将在发布时更新连接):java

  • 断路器(第一部分)
  • 重试/超时(第二部分)
  • 分布式跟踪(第三部分)
  • Prometheus的指标收集(第四部分)
  • rate limiter(第五部分)

第一部分 - 使用envoy proxy 熔断

这篇第一篇博文向您介绍了Envoy Proxy实现的熔断功能。有意进行一些简单的演示,所以我能够单独说明模式和用法。请下载此演示的源代码并按照说明进行操做!git

该演示由一个客户端和一个服务组成。客户端是一个Java http应用程序,模拟对“上游”服务进行http调用(注意,咱们在这里使用Envoys术语,并贯穿整个repo)。客户端打包在docker.io/ceposta/http-envoy-client:latest的Docker镜像中。除了http-client Java应用程序以外,还有Envoy Proxy的一个实例。在此部署模型中,Envoy被部署为服务的sidercar(在本例中为http客户端)。当http-client进行出站调用(到“上游”服务)时,全部调用都经过Envoy Proxy sidercar。github

这些示例的“上游”服务是httpbin.org。 httpbin.org容许咱们轻松模拟HTTP服务行为。它很棒,因此若是你没有看到它,请查看它。算法

图片描述

这个熔断器演示有本身的envoy.json配置文件。我绝对建议您查看配置文件每一个部分的参考文档,以帮助理解完整配置。 datawire.io的优秀人员也为Envoy及其配置提供了一个很好的介绍,你也应该检查一下。docker

运行 circuit-breaker demo

运行熔断器演示,请熟悉演示框架,而后运行:json

./docker-run.sh -d circuit-breaker

熔断器的Envoy配置以下所示(请参阅此处的完整配置):bash

"circuit_breakers": {
  "default": {
    "max_connections": 1,
    "max_pending_requests": 1,
    "max_retries": 3
  }
}

该配置文件容许咱们实现下面的功能:多线程

  • 限制咱们对上游集群的HTTP / 1链接的数量,若是咱们超过设定限制则将它们短路。
  • 限制排队/等待链接可用的请求数量,若是咱们超过设定限制则将它们短路。
  • 限制在任何给定时间的总并发重试次数(假设重试策略已到位)有效地实施重试配额。

咱们来看看每一个配置。咱们如今将忽略最大重试次数设置有两个缘由:并发

  • 咱们的设置并无多大意义;咱们不能有3个并发重试,由于咱们只容许1个HTTP链接和1个排队请求。
  • 咱们实际上没有为此演示制定任何重试政策;咱们能够在重试演示中看到重试。

不管如何,此处的重试设置容许咱们避免大型重试风暴 - 在大多数状况下,这可能会在处理与群集中全部实例的链接时出现问题。这是一个重要的设置,咱们将回到重试演示。

max_connections

让咱们看看当应用程序中有太多线程试图与上游集群创建太多并发链接时,envoy会作什么。

回想一下咱们的上游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 Proxy。咱们将直接向Envoy Proxy发送流量,以使其帮帮助处理熔断。让咱们调用咱们的服务:

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为咱们收集的metrics指标:

./get-envoy-stats.sh

Envoy为咱们采集了不少的追踪指标!让咱们经过如下方式查看:

/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和咱们系统中的bug。

继续上一节,让咱们重置特使的统计数据:

./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'

咱们应该看到以下的输出:

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

果真,咱们看到咱们的4个请求被短路了:

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结果。咱们将发起十次调用,由于咱们的异常检测将检查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检测!咱们还从负载均衡组中删除了该主机。

相关文章
相关标签/搜索