使用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檢測!咱們還從負載均衡組中刪除了該主機。

相關文章
相關標籤/搜索