本博客是深入研究Envoy Proxy和Istio.io 以及它如何实现更优雅的方式来连接和管理微服务系列文章的一部分。
这是接下来几个部分的想法(将在发布时更新链接):
- 断路器(第一部分)
- 重试/超时(第二部分)
- 分布式跟踪(第三部分)
- Prometheus的指标收集(第四部分)
- rate limiter(第五部分)
第一部分 - 使用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,以便上游服务可以了解所期望的内容。