分佈式系統中常常會出現某個基礎服務不可用形成整個系統不可用的狀況,這種現象被稱爲服務雪崩效應。爲了應對服務雪崩,一種常見的作法是手動服務降級。而 Hystrix 的出現,給咱們提供了另外一種選擇。git
Hystrix [hɪst’rɪks] 的中文含義是 「豪豬」,豪豬周身長滿了刺,能保護本身不受天敵的傷害,表明了一種防護機制,這與 Hystrix 自己的功能不謀而合,所以 Netflix 團隊將該框架命名爲 Hystrix,並使用了對應的卡通形象作做爲 logo。github
服務雪崩效應
定義
服務雪崩效應是一種因 服務提供者 的不可用致使 服務調用者 的不可用,並將不可用 逐漸放大 的過程。若是所示:
上圖中,A 爲服務提供者,B 爲 A 的服務調用者,C 和 D 是 B 的服務調用者。當 A 的不可用,引發 B 的不可用,並將不可用逐漸放大 C 和 D 時,服務雪崩就造成了。spring
造成的緣由
我把服務雪崩的參與者簡化爲 服務提供者 和 服務調用者,並將服務雪崩產生的過程分爲如下三個階段來分析造成的緣由:segmentfault
- 服務提供者不可用
- 重試加大流量
- 服務調用者不可用
服務雪崩的每一個階段均可能由不一樣的緣由形成,好比形成 服務不可用 的緣由有:後端
- 硬件故障
- 程序 Bug
- 緩存擊穿
- 用戶大量請求
硬件故障可能爲硬件損壞形成的服務器主機宕機,網絡硬件故障形成的服務提供者的不可訪問。
緩存擊穿通常發生在緩存應用重啓,全部緩存被清空時,以及短期內大量緩存失效時。大量的緩存不命中,使請求直擊後端,形成服務提供者超負荷運行,引發服務不可用。
在秒殺和大促開始前,若是準備不充分,用戶發起大量請求也會形成服務提供者的不可用。緩存
而造成 重試加大流量 的緣由有:服務器
- 用戶重試
- 代碼邏輯重試
在服務提供者不可用後,用戶因爲忍受不了界面上長時間的等待,而不斷刷新頁面甚至提交表單。
服務調用端的會存在大量服務異常後的重試邏輯。
這些重試都會進一步加大請求流量。網絡
最後, 服務調用者不可用 產生的主要緣由是:架構
- 同步等待形成的資源耗盡
當服務調用者使用 同步調用 時,會產生大量的等待線程佔用系統資源。一旦線程資源被耗盡,服務調用者提供的服務也將處於不可用狀態,因而服務雪崩效應產生了。
應對策略
針對形成服務雪崩的不一樣緣由,可使用不一樣的應對策略:
- 流量控制
- 改進緩存模式
- 服務自動擴容
- 服務調用者降級服務
流量控制 的具體措施包括:
- 網關限流
- 用戶交互限流
- 關閉重試
由於 Nginx 的高性能,目前一線互聯網公司大量採用 Nginx+Lua 的網關進行流量控制,由此而來的 OpenResty 也愈來愈熱門。
用戶交互限流的具體措施有: 1. 採用加載動畫,提升用戶的忍耐等待時間。2. 提交按鈕添增強制等待時間機制。
改進緩存模式 的措施包括:
- 緩存預加載
- 同步改成異步刷新
服務自動擴容 的措施主要有:
- AWS 的 auto scaling
服務調用者降級服務 的措施包括:
- 資源隔離
- 對依賴服務進行分類
- 不可用服務的調用快速失敗
資源隔離主要是對調用服務的線程池進行隔離。
咱們根據具體業務,將依賴服務分爲: 強依賴和若依賴。強依賴服務不可用會致使當前業務停止,而弱依賴服務的不可用不會致使當前業務的停止。
不可用服務的調用快速失敗通常經過 超時機制, 熔斷器 和熔斷後的 降級方法 來實現。
使用 Hystrix 預防服務雪崩
服務降級(Fallback)
對於查詢操做,咱們能夠實現一個 fallback 方法,當請求後端服務出現異常的時候,可使用 fallback 方法返回的值。fallback 方法的返回值通常是設置的默認值或者來自緩存。
資源隔離
貨船爲了進行防止漏水和火災的擴散,會將貨倉分隔爲多個,以下圖所示:
這種資源隔離減小風險的方式被稱爲: Bulkheads(艙壁隔離模式)。
Hystrix 將一樣的模式運用到了服務調用者上。
在 Hystrix 中,主要經過線程池來實現資源隔離。一般在使用的時候咱們會根據調用的遠程服務劃分出多個線程池。例如調用產品服務的 Command 放入 A 線程池,調用帳戶服務的 Command 放入 B 線程池。這樣作的主要優勢是運行環境被隔離開了。這樣就算調用服務的代碼存在 bug 或者因爲其餘緣由致使本身所在線程池被耗盡時,不會對系統的其餘服務形成影響。
經過對依賴服務的線程池隔離實現,能夠帶來以下優點:
- 應用自身獲得徹底的保護,不會受不可控的依賴服務影響。即使給依賴服務分配的線程池被填滿,也不會影響應用自身的額其他部分。
- 能夠有效的下降接入新服務的風險。若是新服務接入後運行不穩定或存在問題,徹底不會影響到應用其餘的請求。
- 當依賴的服務從失效恢復正常後,它的線程池會被清理而且可以立刻恢復健康的服務,相比之下容器級別的清理恢復速度要慢得多。
- 當依賴的服務出現配置錯誤的時候,線程池會快速的反應出此問題(經過失敗次數、延遲、超時、拒絕等指標的增長狀況)。同時,咱們能夠在不影響應用功能的狀況下經過實時的動態屬性刷新(後續會經過 Spring Cloud Config 與 Spring Cloud Bus 的聯合使用來介紹)來處理它。
- 當依賴的服務因實現機制調整等緣由形成其性能出現很大變化的時候,此時線程池的監控指標信息會反映出這樣的變化。同時,咱們也能夠經過實時動態刷新自身應用對依賴服務的閾值進行調整以適應依賴方的改變。
- 除了上面經過線程池隔離服務發揮的優勢以外,每一個專有線程池都提供了內置的併發實現,能夠利用它爲同步的依賴服務構建異步的訪問。
總之,經過對依賴服務實現線程池隔離,讓咱們的應用更加健壯,不會由於個別依賴服務出現問題而引發非相關服務的異常。同時,也使得咱們的應用變得更加靈活,能夠在不中止服務的狀況下,配合動態配置刷新實現性能配置上的調整。
雖然線程池隔離的方案帶了如此多的好處,可是不少使用者可能會擔憂爲每個依賴服務都分配一個線程池是否會過多地增長系統的負載和開銷。對於這一點,使用者不用過於擔憂,由於這些顧慮也是大部分工程師們會考慮到的,Netflix 在設計 Hystrix 的時候,認爲線程池上的開銷相對於隔離所帶來的好處是沒法比擬的。同時,Netflix 也針對線程池的開銷作了相關的測試,以證實和打消 Hystrix 實現對性能影響的顧慮。
下圖是 Netflix Hystrix 官方提供的一個 Hystrix 命令的性能監控,該命令以每秒 60 個請求的速度(QPS)向一個單服務實例進行訪問,該服務實例每秒運行的線程數峯值爲 350 個。
從圖中的統計咱們能夠看到,使用線程池隔離與不使用線程池隔離的耗時差別以下表所示:
比較狀況 | 未使用線程池隔離 | 使用了線程池隔離 | 耗時差距 |
---|---|---|---|
中位數 | 2ms | 2ms | 2ms |
90 百分位 | 5ms | 8ms | 3ms |
99 百分位 | 28ms | 37ms | 9ms |
在 99% 的狀況下,使用線程池隔離的延遲有 9ms,對於大多數需求來講這樣的消耗是微乎其微的,更況且爲系統在穩定性和靈活性上所帶來的巨大提高。雖然對於大部分的請求咱們能夠忽略線程池的額外開銷,而對於小部分延遲自己就很是小的請求(可能只須要 1ms),那麼 9ms 的延遲開銷仍是很是昂貴的。實際上 Hystrix 也爲此設計了另外的一個解決方案:信號量(Semaphores)。
Hystrix 中除了使用線程池以外,還可使用信號量來控制單個依賴服務的併發度,信號量的開銷要遠比線程池的開銷小得多,可是它不能設置超時和實現異步訪問。因此,只有在依賴服務是足夠可靠的狀況下才使用信號量。在 HystrixCommand 和 HystrixObservableCommand 中 2 處支持信號量的使用:
- 命令執行:若是隔離策略參數 execution.isolation.strategy 設置爲 SEMAPHORE,Hystrix 會使用信號量替代線程池來控制依賴服務的併發控制。
- 降級邏輯:當 Hystrix 嘗試降級邏輯時候,它會在調用線程中使用信號量。
信號量的默認值爲 10,咱們也能夠經過動態刷新配置的方式來控制併發線程的數量。對於信號量大小的估算方法與線程池併發度的估算相似。僅訪問內存數據的請求通常耗時在 1ms 之內,性能能夠達到 5000rps,這樣級別的請求咱們能夠將信號量設置爲 1 或者 2,咱們能夠按此標準並根據實際請求耗時來設置信號量。
斷路器模式
斷路器模式源於 Martin Fowler 的 Circuit Breaker 一文。「斷路器」 自己是一種開關裝置,用於在電路上保護線路過載,當線路中有電器發生短路時,「斷路器」 可以及時的切斷故障電路,防止發生過載、發熱、甚至起火等嚴重後果。
在分佈式架構中,斷路器模式的做用也是相似的,當某個服務單元發生故障(相似用電器發生短路)以後,經過斷路器的故障監控(相似熔斷保險絲),直接切斷原來的主邏輯調用。可是,在 Hystrix 中的斷路器除了切斷主邏輯的功能以外,還有更復雜的邏輯,下面咱們來看看它更爲深層次的處理邏輯。
斷路器開關相互轉換的邏輯以下圖:
當 Hystrix Command 請求後端服務失敗數量超過必定閾值,斷路器會切換到開路狀態 (Open)。這時全部請求會直接失敗而不會發送到後端服務。
這個閾值涉及到三個重要參數:快照時間窗、請求總數下限、錯誤百分比下限。這個參數的做用分別是:
快照時間窗:斷路器肯定是否打開須要統計一些請求和錯誤數據,而統計的時間範圍就是快照時間窗,默認爲最近的 10 秒。
請求總數下限:在快照時間窗內,必須知足請求總數下限纔有資格進行熔斷。默認爲 20,意味着在 10 秒內,若是該 Hystrix Command 的調用此時不足 20 次,即時全部的請求都超時或其餘緣由失敗,斷路器都不會打開。
錯誤百分比下限:當請求總數在快照時間窗內超過了下限,好比發生了 30 次調用,若是在這 30 次調用中,有 16 次發生了超時異常,也就是超過 50% 的錯誤百分比,在默認設定 50% 下限狀況下,這時候就會將斷路器打開。
斷路器保持在開路狀態一段時間後 (默認 5 秒),自動切換到半開路狀態 (HALF-OPEN)。這時會判斷下一次請求的返回狀況,若是請求成功,斷路器切回閉路狀態 (CLOSED),不然從新切換到開路狀態 (OPEN)。
使用 Feign Hystrix
由於熔斷只是做用在服務調用這一端,所以咱們根據上一篇的示例代碼只須要改動 eureka-consumer-feign 項目相關代碼就能夠。
POM 配置
由於 Feign 中已經依賴了 Hystrix 因此在 maven 配置上不用作任何改動。
配置文件
在原來的 application.yml 配置的基礎上修改