三、Hystrix容錯
Hystrix的容錯主要是通過添加容許延遲和容錯方法,幫助控制這些分布式服務之間的交互。 還通過隔離服務之間的訪問點,阻止它們之間的級聯故障以及提供回退選項來實現這一點,從而提高系統的整體彈性。Hystrix主要提供了以下幾種容錯方法:
- 資源隔離
- 熔斷
- 降級
1、資源隔離-線程池
2、資源隔離-信號量
線程池和信號量隔離比較
線程切換 | 支持異步 | 支持超時 | 支持熔斷 | 限流 | 開銷 | |
---|---|---|---|---|---|---|
信號量 | 否 | 否 | 否 | 是 | 是 | 小 |
線程池 | 是 | 是 | 是 | 是 | 是 | 大 |
當請求的服務網絡開銷比較大的時候,或者是請求比較耗時的時候,我們最好是使用線程隔離策略,這樣的話,可以保證大量的容器(tomcat)線程可用,不會由於服務原因,一直處於阻塞或等待狀態,快速失敗返回。而當我們請求緩存這些服務的時候,我們可以使用信號量隔離策略,因為這類服務的返回通常會非常的快,不會占用容器線程太長時間,而且也減少了線程切換的一些開銷,提高了緩存服務的效率。
線程池適合絕大多數的場景,對依賴服務的網絡請求的調用;信號量適合訪問不是對外部依賴的訪問,而是對內部的一些比較復雜的業務邏輯的訪問,只要做信號量的普通限流就可以了。
3、熔斷
為什么要使用斷路器?
在分布式架構中,一個應用依賴多個服務是非常常見的,如果其中一個依賴由於延遲過高發生阻塞,調用該依賴服務的線程就會阻塞,如果相關業務的QPS較高,就可能產生大量阻塞,從而導致該應用/服務由於服務器資源被耗盡而拖垮。另外,故障也會在應用之間傳遞,如果故障服務的上游依賴較多,可能會引起服務的雪崩效應。
熔斷器工作的詳細過程如下:
第一步,調用allowRequest()判斷是否允許將請求提交到線程池
1、如果熔斷器強制打開,circuitBreaker.forceOpen為true,不允許放行,返回。
2、如果熔斷器強制關閉,circuitBreaker.forceClosed為true,允許放行。此外不必關注熔斷器實際狀態,也就是說熔斷器仍然會維護統計數據和開關狀態,只是不生效而已。
第二步,調用isOpen()判斷熔斷器開關是否打開
1、如果熔斷器開關打開,進入第三步,否則繼續;
2、如果一個周期內總的請求數小於circuitBreaker.requestVolumeThreshold的值,允許請求放行,否則繼續;
3、如果一個周期內錯誤率小於circuitBreaker.errorThresholdPercentage的值,允許請求放行。否則,打開熔斷器開關,進入第三步。
第三步,調用allowSingleTest()判斷是否允許單個請求通行,檢查依賴服務是否恢復
1、如果熔斷器打開,且距離熔斷器打開的時間或上一次試探請求放行的時間超過circuitBreaker.sleepWindowInMilliseconds的值時,熔斷器器進入半開狀態,允許放行一個試探請求;否則,不允許放行。
此外,為了提供決策依據,每個熔斷器默認維護了10個bucket,每秒一個bucket,當新的bucket被創建時,最舊的bucket會被拋棄。其中每個blucket維護了請求成功、失敗、超時、拒絕的計數器,Hystrix負責收集並統計這些計數器。
執行策略如下:
Hystrix遇到一個超時/失敗請求,此時啟動一個10s的窗口,后續的請求會進行如下判斷:
(1)查看失敗次數是否超過最小調用次數
- 如果沒有超過,則放行請求。
- 如果超過最小請求數,繼續下面邏輯
(2)判斷失敗率是否超過一個閾值,這里錯誤是指超時和失敗兩種。
- 如果沒有超過,則放行
- 如果超過錯誤閾值,則繼續下面邏輯
(3)熔斷器斷開
- 請求會直接返回失敗。
- 會開一個5s的窗口,每隔5s調用一次請求,如果成功,表示下游服務恢復,否則繼續保持斷路器斷開狀態。
4、降級
降級,通常指務高峰期,為了保證核心服務正常運行,需要停掉一些不太重要的業務,或者某些服務不可用時,執行備用邏輯從故障服務中快速失敗或快速返回,以保障主體業務不受影響。
要支持回退或降級處理,一般是查詢操作,可以重寫HystrixCommand的getFallBack方法或HystrixObservableCommand的resumeWithFallback方法,通常不建議在回退邏輯中執行任何可能失敗的操作。
Hystrix在以下幾種情況下會走降級邏輯:
- 執行construct()或run()拋出異常
- 熔斷器打開導致命令短路
- 命令的線程池和隊列或信號量的容量超額,命令被拒絕
- 命令執行超時
如果降級邏輯中需要發起遠程調用,建議重新封裝一個HystrixCommand,使用不同的ThreadPoolKey,與主線程池進行隔離。
四、Hystrix配置
Hystrix默認使用Netflix Archaius進行配置管理,項目中使用zookeeper作為配置源,通過archaius-zookeeper實現hystrix命令、熔斷器、線程池、監控等參數的動態配置,根據生產環境需要動態調整hystrix參數,實現了對微服務的治理。
每個Hystrix參數都有4個地方可以配置,優先級從低到高如下,如果每個地方都配置相同的屬性,則優先級高的值會覆蓋優先級低的值:
- 內置全局默認值:寫死在Hystrix代碼里的默認值,如HystrixCommandProperties.default_executionTimeoutInMilliseconds屬性
- 動態全局默認屬性:全局配置文件讀到的默認值
- 內置實例默認值:創建HystrixCommand時,通過注解或者給父類構造器傳參的方式設置的默認值
- 動態配置實例屬性:通過屬性文件配置特定實例的值
以hystrix命令sns.grassSearchIndex執行的超時時間設置為例,屬性的優先級從低到高為
default_executionTimeoutInMilliseconds=1000
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=500
HystrixCommandProperties.Setter().withExecutionTimeoutInMilliseconds(1000)
hystrix.command.sns.grassSearchIndex.execution.isolation.thread.timeoutInMilliseconds=1500
Command 配置
# 隔離策略: 可選THREAD|SEMAPHORE,默認TREAD
hystrix.command.default.execution.isolation.strategy=TREAD
# 服務超時時間,單位毫秒,默認1000
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=3000
# 服務sns.grassSearchIndex超時時間
hystrix.command.sns.grassSearchIndex.execution.isolation.thread.timeoutInMilliseconds=2000
# 是否打開超時檢測,默認啟用true
hystrix.command.default.execution.timeout.enabled=true
# 使用信號量隔離時qps閾值,后續的請求會被拒,默認10
hystrix.command.default.execution.isolation.semaphore.maxConcurrentRequests=10
熔斷器(Circuit Breaker)配置
# 是否打開斷路器,默認開啟true
hystrix.command.default.circuitBreaker.enabled=true
# 斷路器檢測的基礎請求值,只有時間窗口內的請求數達到這個閾值時,才會判定錯誤率,否則比如只有一兩個請求,即便都失敗了,也不會打開斷路器,因為基數太少了,默認20
hystrix.command.default.circuitBreaker.requestVolumeThreshold=100
# 錯誤百分比,超過就會短路,默認值50
hystrix.command.default.circuitBreaker.errorThresholdPercentage=75
# 指的是從斷路器打開狀態到半開半閉狀態需要的時間,即斷路后,需要等多久才能放一個請求進來,默認值5000
hystrix.command.default.circuitBreaker.sleepWindowInMilliseconds=5000
# Metrics
# 統計的時間窗口大小,默認10000
hystrix.command.default.metrics.rollingStats.timeInMilliseconds=10000
# 時間窗口的桶的數目,必須能被時間窗口大小整除,否則報錯,每個bucket包含success,failure,timeout,rejection的次數的統計信息,默認10
hystrix.command.default.metrics.rollingStats.numBuckets=10
# 每一次檢測的間隙。因為就算分窗口統計錯誤率,也會很占cpu,所以每一次統計都會等一個時間間隔再開始,默認500
hystrix.command.default.metrics.healthSnapshot.intervalInMilliseconds=500
# 帶rollingPercentile的都表示調用時延的統計,該選項表示是否打開時延統計,比如說95分位99分位等,如果關閉都返回-1,默認true
hystrix.command.default.metrics.rollingPercentile.enabled=true
# 時延統計的時間窗口,默認60000
hystrix.command.default.metrics.rollingPercentile.timeInMilliseconds=60000
# 時延統計的桶數目
hystrix.command.default.metrics.rollingPercentile.numBuckets=6
# 時延統計的桶大小,時延統計每一個桶只維持最新的該數值的請求的數據,早一些的將會被覆蓋。如果bucket size=100,window=10s,若這10s里有500次執行,只有最后100次執行會被統計到bucket里去,增加該值會增加內存開銷以及排序的開銷,默認100
hystrix.command.default.metrics.rollingPercentile.bucketSize=100
線程池(ThreadPool)配置
# 默認核心線程數,不會變,默認10
hystrix.threadpool.default.coreSize=30
# userLogin隔離線程池核心線程數
hystrix.threadpool.userLogin.coreSize=20
# 等待隊列,還超就會被拒,這個數值無法動態修改,默認-1
hystrix.threadpool.default.maxQueueSize=50000
# userLogin隔離線程池等待隊列
hystrix.threadpool.userLogin.maxQueueSize=3000
# 進入queue時被拒的概率值,即便是沒有達到maxQueueSize。這個為了彌補上面無法動態修改的不足。可以通過這個概率值來控制隊列大小
hystrix.threadpool.default.queueSizeRejectionThreshold=45000
#線程池統計指標的時間,默認10000
hystrix.threadpool.default.metrics.rollingStats.timeInMilliseconds=10000
#將rolling window划分為n個buckets,默認10
hystrix.threadpool.default.metrics.rollingStats.numBuckets=10
五、服務監控
Hystrix Dashboard
Hystrix Dashboard主要用來實時監控Hystrix的各項指標信息。通過Hystrix Dashboard反饋的實時信息,可以幫助我們快速發現系統中存在的問題。
通過 https://search.maven.org 站點下載standalone-hystrix-dashboard,運行jar包后訪問http://localhost:7979/hystrix-dashboard/,即可進入hystrix dashboard頁面
nohup java -jar -DserverPort=7979 -DbindAddress=localhost standalone-hystrix-dashboard-1.5.3-all.jar &
集群環境監控可使用Netflix提供的turbine進行監控。通過maven公服https://search.maven.org下載並部署war包turbine-web,修改集群節點配置,將turbine地址http://localhost:${port}/turbine.stream?cluster=default添加監控到dashboard
turbine.aggregator.clusterConfig=default
turbine.instanceUrlSuffix=:8080/gateway/hystrix.stream
turbine.ConfigPropertyBasedDiscovery.test.instances=10.66.70.1,10.66.70.2,10.66.70.3