共计 4742 个字符,预计需要花费 12 分钟才能阅读完成。
本博客是深入研究 Envoy Proxy 和 Istio.io 以及它如何实现更优雅的方式来连接和管理微服务系列文章的一部分。
这是接下来几个部分的想法(将在发布时更新链接 ):
断路器(第一部分)
重试 / 超时(第二部分)
分布式跟踪(第三部分)
Prometheus 的指标收集(第四部分)
服务发现(第五部分)
第一部分 – 使用 envoy proxy 实现超时和重试
第一篇博文向您介绍了 Envoy Proxy 的断路功能实现。在第二部分中,我们将详细介绍如何启用其他弹性功能,如超时和重试。有意进行一些简单的演示,因此我可以单独说明模式和用法。请下载此演示的源代码并按照说明进行操作!
该演示由一个客户端和一个服务组成。客户端是一个 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。
这些示例的“上游”服务是 httpbin.org。httpbin.org 允许我们轻松模拟 HTTP 服务行为。它很棒,所以如果你没有看到它,请查看它。
重试和超时演示有自己的 envoy.json 配置文件。我绝对建议您查看配置文件每个部分的参考文档,以帮助理解完整配置。datawire.io 的优秀人员也为 Envoy 及其配置提供了一个很好的介绍,你也应该检查一下。
运行 重试 demo
对于重试演示,我们将在 Envoy 中配置我们的路由,如下所示:
“routes”: [
{
“timeout_ms”: 0,
“prefix”: “/”,
“auto_host_rewrite”: true,
“cluster”: “httpbin_service”,
“retry_policy”: {
“retry_on”: “5xx”,
“num_retries”: 3
}
}
这里我们在 HTTP 状态为 5xx 时重试最多 3 次。
如果您已经运行过以前的演示,请确保为此(或任何)演示开始一个新的初始化状态。我们为每个演示提供不同的 Envoy 配置,并希望确保每次都从一个新的初始化状态开始。
首先停止已经存在的 demo:
./docker-stop.sh
现在开始运行重试 demo:
./docker-run.sh -d retries
现在让我们通过一次调用来运行客户端,该调用将触发应该返回 HTTP 500 错误的 HTTP 端点。我们将使用 curl.sh 脚本,该脚本设置为在我们的演示容器中调用 curl。
./curl.sh -vvvv localhost:15001/status/500
我们将会看到类似的输出:
* Hostname was NOT found in DNS cache
* Trying ::1…
* connect to ::1 port 15001 failed: Connection refused
* Trying 127.0.0.1…
* Connected to localhost (127.0.0.1) port 15001 (#0)
> GET /status/500 HTTP/1.1
> User-Agent: curl/7.35.0
> Host: localhost:15001
> Accept: */*
>
< HTTP/1.1 500 Internal Server Error
* Server envoy is not blacklisted
< server: envoy
< date: Thu, 25 May 2017 05:55:37 GMT
< content-type: text/html; charset=utf-8
< access-control-allow-origin: *
< access-control-allow-credentials: true
< x-powered-by: Flask
< x-processed-time: 0.000718116760254
< content-length: 0
< via: 1.1 vegur
< x-envoy-upstream-service-time: 684
<
* Connection #0 to host localhost left intact
现在我们检查一下,envoy 为我们做了哪些工作:
./get-envoy-stats.sh | grep retry
cluster.httpbin_service.retry.upstream_rq_500: 3
cluster.httpbin_service.retry.upstream_rq_5xx: 3
cluster.httpbin_service.upstream_rq_retry: 3
cluster.httpbin_service.upstream_rq_retry_overflow: 0
cluster.httpbin_service.upstream_rq_retry_success: 0
我们在这里看到由于 HTTP 500 错误,envoy 重试了 3 次。
如果从另外一个角度看待,重试可能会对您的服务架构产生有害影响。它们可以帮助传播故障或对可能正在挣扎的内部服务造成 DDoS 类型攻击。
对于重试,需要注意以下几点:
envoy 将通过抖动进行自动指数重试。有关更多信息,请参阅文档
您可以设置重试超时(每次重试超时),但总路由超时(为路由表配置; 请参阅超时演示以获取确切配置)仍将保留 / 应用; 这是为了使任何失控的重试 / 指数退避短路您应始终设置断路器重试配置,以便在可能具有大量连接时限制重试的配额。请参阅 Envoy 文档中断路器部分的有效重试
运行超时 demo
对于超时演示,我们将在 Envoy 中配置我们的路由,如下所示:
“routes”: [
{
“timeout_ms”: 0,
“prefix”: “/”,
“auto_host_rewrite”: true,
“cluster”: “httpbin_service”,
“timeout_ms”: 3000
}
此配置为通过此路由到 httpbin_service 群集的任何调用设置全局(即,包括所有重试)3s 超时。
每当处理超时时,我们必须知道源自边缘的请求的整体全局超时。当我们深入到网络调用图中时,我们发现自己很难调试超时不会逐渐减少的情况。换句话说,当您浏览调用图时,调用图中更深层次的服务调用的服务超时应该小于先前服务的调用:
envoy 可以帮助传播超时信息,像 gRPC 这样的协议可以传播截止时间信息。随着我们继续本系列,我们将看到如何使用 Istio Mesh 控制 Envoy 代理,并且控制平面可以帮助我们进行故障注入以发现超时异常。
如果您已经运行过以前的演示,请确保为此(或任何)演示开始一个新的初始化状态。我们为每个演示提供不同的 Envoy 配置,并希望确保每次都从一个新的初始化状态开始。
首先停止已经存在的 demo:
./docker-stop.sh
现在开始运超时 demo:
./docker-run.sh -d timeouts
现在让我们用一个调用来运行客户端,该调用将触发 HTTP 端点,该端点应该将响应延迟大约 5 秒。此延迟应足以触发 envoy 超时。我们将使用 curl.sh 脚本,该脚本设置为在我们的演示容器中调用 curl。
./curl.sh -vvvv localhost:15001/delay/5
我们将看到类似的输出:
* Hostname was NOT found in DNS cache
* Trying ::1…
* connect to ::1 port 15001 failed: Connection refused
* Trying 127.0.0.1…
* Connected to localhost (127.0.0.1) port 15001 (#0)
> GET /delay/5 HTTP/1.1
> User-Agent: curl/7.35.0
> Host: localhost:15001
> Accept: */*
>
< HTTP/1.1 504 Gateway Timeout
< content-length: 24
< content-type: text/plain
< date: Thu, 25 May 2017 06:13:53 GMT
* Server envoy is not blacklisted
< server: envoy
<
* Connection #0 to host localhost left intact
upstream request timeout
我们看到我们的请求是超时的。
下面我们检查以下 envoy 的状态:
./get-envoy-stats.sh | grep timeout
在这里,我们看到 1 个请求(我们发送的请求!)由 Envoy 超时。
cluster.httpbin_service.upstream_cx_connect_timeout: 0
cluster.httpbin_service.upstream_rq_per_try_timeout: 0
cluster.httpbin_service.upstream_rq_timeout: 1
http.admin.downstream_cx_idle_timeout: 0
http.egress_http.downstream_cx_idle_timeout: 0
如果我们发送请求,这次延迟较小,我们应该看到调用:
./curl.sh -vvvv localhost:15001/delay/2
* Hostname was NOT found in DNS cache
* Trying ::1…
* connect to ::1 port 15001 failed: Connection refused
* Trying 127.0.0.1…
* Connected to localhost (127.0.0.1) port 15001 (#0)
> GET /delay/2 HTTP/1.1
> User-Agent: curl/7.35.0
> Host: localhost:15001
> Accept: */*
>
< HTTP/1.1 200 OK
* Server envoy is not blacklisted
< server: envoy
< date: Thu, 25 May 2017 06:15:41 GMT
< content-type: application/json
< access-control-allow-origin: *
< access-control-allow-credentials: true
< x-powered-by: Flask
< x-processed-time: 2.00246119499
< content-length: 309
< via: 1.1 vegur
< x-envoy-upstream-service-time: 2145
<
{
“args”: {},
“data”: “”,
“files”: {},
“form”: {},
“headers”: {
“Accept”: “*/*”,
“Connection”: “close”,
“Host”: “httpbin.org”,
“User-Agent”: “curl/7.35.0”,
“X-Envoy-Expected-Rq-Timeout-Ms”: “3000”
},
“origin”: “68.3.84.124”,
“url”: “http://httpbin.org/delay/2”
}
* Connection #0 to host localhost left intact
另请注意,Envoy 会传播超时 headers,以便上游服务可以了解所期望的内容。