使用Envoy 作Sidecar Proxy的微服務模式-2.超時和重試

iyacontrol發表於2019-02-22

本部落格是深入研究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,以便上游服務可以瞭解所期望的內容。

相關文章