聲明:本文根據魯班學院商鞅老師課程資料整理得來
幫助:本文涉及到的詳細代碼請參考:https://github.com/LoveWK/mySpringCloud.git
Hystrix(斷路器)是什么?
Hystrix是一個用於處理分布式系統的延遲和容錯的開源庫,在分布式系統里,許多依賴不可避免的會調用失敗,比如超時、異常等,Hystrix能夠保證在一個依賴出問題的情況下,不會導致整體服務失敗,避免級聯故障,以提高分布式系統的彈性。
“斷路器”本身是一種開關裝置,當某個服務單元發生故障之后,通過斷路器的故障監控(類似熔斷保險絲),向調用方返回一個符合預期的、可處理的備選響應(FallBack),而不是長時間的等待或者拋出調用方無法處理的異常,這樣就保證了服務調用方的線程不會被長時間、不必要地占用,從而避免了故障在分布式系統中的蔓延,乃至雪崩。
大型項目中會出現的一些問題?
典型的一個案例就是服務血崩效應 我們來看一張圖:
上圖是一條微服務調用鏈, 正常的情況我們就不必在討論了, 我們來說一下非正常情況, 假設現在 微服務H 響應時間過長,或者微服務H直接down機了如圖:
來看下上圖, 我們聯想一下上圖, 如果發生這種情況, 也就是說所有發給微服務D的請求 都會被卡在微服務H那, 就會導致線程一直累計在這里, 那么其他的微服務(比如A,B,C...) 就沒有可用線程了, 導致整個服務器崩潰,這就是服務血崩。
導致服務雪崩的情況我們來總結一下,再看看怎么解決:
程序BUG,數據不匹配,響應時間過長,服務不可用等等.....
針對上面的問題,我們來看看有哪些解決方案 :
服務限流
超時監控
服務熔斷
服務降級
降級,超時:
我們先來解釋一下降級,降級是當我們的某個微服務響應時間過長,或者不可用了,講白了也就是那個微服務調用不了了,我們不能把錯誤信息返回出來,或者讓他一直卡在那里,所以要在准備一個對應的策略(一個方法)當發生這種問題的時候我們直接調用這個方法來快速返回這個請求,不讓他一直卡在那 。
講了這么多,我們來看看具體怎么操作:
我們剛剛說了某個微服務調用不了了要做降級,也就是說,要在調用方做降級(不然那個微服務都down掉了再做降級也沒什么意義了) 比如說我們 user 調用power 那么就在user 做降級
1.首先加入Hystrix的依賴
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
2.在啟動類加入注解@EnableHystrix或者@EnableCircuitBreaker(他們之間是一個繼承關系,2個注解所描述的內容是完全一樣的
/** * User啟動類 */ @SpringBootApplication @EnableEurekaClient @EnableHystrix public class AppUser { public static void main(String[] args) { SpringApplication.run(AppUser.class); } }
3.然后在我們的controller中對應的方法上加入注解@HystrixCommand(fallbackMethod就是我們剛剛說的降級方法的名字)
@RequestMapping("/getHystrix.do") @HystrixCommand(fallbackMethod = "HystrixFallBackMethod") public R getHystrix(String name){ return R.success("操作成功","熔斷器方法"); }
4.接下來我們寫熔斷降級后的方法,也就是@HystrixCommand(fallbackMethod = "HystrixFallBackMethod")這個注解對應的方法
public R HystrixFallBackMethod(String name){ System.out.println("name:"+name); return R.error("熔斷降級"); }
這里的這個降級信息具體內容得根據業務需求來, 比如說返回一個默認的查詢信息,亦或是系統維護(因為有可能要暫時關閉某個微服務而吧資源讓給其他服務)等等...
5.我們在power代碼里面模擬一個異常
6.啟動服務,調用程序,查看一下結果,我們發現當調用出現異常的服務時,會走熔斷方法。
7.當我們給程序設置等待時間時,也會觸發熔斷方法。
比如我們在power1中設置2秒的睡眠時間,也會發現觸發熔斷
因為hystrix他有默認的超時監聽,當你這個請求默認超過了1秒鍾就會超時,當然這個時間時可以配置的。
降級的作用?
第一, 他可以監聽你的請求有沒有超時。
第二,報錯了他這里直接截斷了沒有讓請求一直卡在這里。
其實降級還有一個好處, 就是當你的系統馬上迎來大量的並發(雙十一秒殺這種 或者促銷活動) 這時候如果發現系統馬上承載不了這么大的並發時, 可以考慮先關閉一些不重要的微服務(在降級方法里面返回一個比較友好的信息),把資源讓給主微服務。
總結一下就是,整體資源快不夠了,忍痛將某些服務先關掉,待渡過難關,再開啟回來。
熔斷,限流:
講完降級,我們來講講熔斷,其實熔斷,就好像我們生活中的跳閘一樣, 比如說你的電路出故障了,為了防止出現大型事故 這里直接切斷了你的電源以免意外繼續發生, 把這個概念放在我們程序上也是如此, 當一個微服務調用多次出現問題時(默認是10秒內20次當然 這個也能配置),hystrix就會采取熔斷機制,不再繼續調用你的方法(會在默認5秒鍾內和電器短路一樣,5秒鍾后會試探性的先關閉熔斷機制,但是如果這時候再失敗一次{之前是20次} 那么又會重新進行熔斷) 而是直接調用降級方法,這樣就一定程度上避免了服務雪崩的問題
限流:
限流, 顧名思義, 就是限制你某個微服務的使用量(可用線程)
hystrix通過線程池的方式來管理你的微服務調用,他默認是一個線程池(10大小) 管理你的所有微服務,你可以給某個微服務開辟新的線程池:
@RequestMapping("/feignOrder.do") @HystrixCommand(fallbackMethod = "fallbackOrderMethod" , threadPoolKey = "order", threadPoolProperties ={@HystrixProperty(name = "coreSize",value = "2") ,@HystrixProperty(name = "maxQueueSize",value = "1"}) public Object feignOrder(String name){ System.out.println(1); return restTemplate.getForObject(ORDERURL+"/order.do",Object.class); }
threadPoolKey 就是在線程池唯一標識, hystrix 會拿你這個標識去計數,看線程占用是否超過了, 超過了就會直接降級該次調用
比如, 這里coreSize給他值為2 那么假設你這個方法調用時間是3s執行完, 那么在3s內如果有超過2個請求進來的話, 剩下的請求則全部降級
feign整合Hystrix:
feign 默認是支持hystrix的, 但是在Spring - cloud Dalston 版本之后就默認關閉了, 因為不一定業務需求要用的到,所以現在要使用首先得打開他,在yml文件加上如下配置:
feign:
hystrix:
enabled: true
加上配置之后降級方法怎么寫呢?
首先在在feign客戶端的注解上 有個屬性叫fallback 然后指向一個類
/** * feign客戶端 */ @FeignClient(name = "SERVER-POWER",fallback = PowerFeignClientFallBack.class) public interface PowerFeignClient { @RequestMapping("/getPower.do") public Object getPower(); }
編寫降級類,並使用注解@Component將其注入到容器中去
/** * feign的熔斷類 */ @Component public class PowerFeignClientFallBack implements PowerFeignClient { @Override public Object getPower() { return R.error("測試降級"); } }
這樣子,方法降級就寫好了
當然 可能你有這種需求, 需要拿到具體的錯誤信息, 那么可以這樣寫一個類實現FallbackFactory<>接口,泛型就穿你的接口類。
/** * 熔斷類 */
public class PowerFeignClientFallBackFactory implements FallbackFactory<PowerFeignClient> { @Override public PowerFeignClient create(Throwable throwable) { return new PowerFeignClient() { @Override public Object getPower() { //得到具體錯誤信息
String message = throwable.getMessage(); return R.error("測試降級"); } }; } }
然后在客戶端指定一個fallbackFactory就好了
/** * feign客戶端 */
//@FeignClient(name = "SERVER-POWER",fallback = PowerFeignClientFallBack.class)
@FeignClient(name = "SERVER-POWER",fallbackFactory = PowerFeignClientFallBackFactory.class) public interface PowerFeignClient { @RequestMapping("/getPower.do") public Object getPower(); }
至此, 就完成了feign與hystrix的整合
Hystrix相關配置:
Execution相關的屬性的配置
hystrix.command.default.execution.isolation.strategy 隔離策略,默認是Thread, 可選Thread| Semaphor
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds 命令執行超時時 間,默認1000ms
hystrix.command.default.execution.timeout.enabled 執行是否啟用超時,默認啟用true
hystrix.command.default.execution.isolation.thread.interruptOnTimeout 發生超時是是否中斷, 默認true
hystrix.command.default.execution.isolation.semaphore.maxConcurrentRequests 最大並發請求 數,默認10,該參數當使用ExecutionIsolationStrategy.SEMAPHORE策略時才有效。如果達到最大並發請求 數,請求會被拒絕。理論上選擇semaphore size的原則和選擇thread size一致,但選用semaphore時每次執行 的單元要比較小且執行速度快(ms級別),否則的話應該用thread。 semaphore應該占整個容器(tomcat)的線程池的一小部分。 Fallback相關的屬性 這些參數可以應用於Hystrix的THREAD和SEMAPHORE策略
hystrix.command.default.fallback.isolation.semaphore.maxConcurrentRequests 如果並發數達到 該設置值,請求會被拒絕和拋出異常並且fallback不會被調用。默認10
hystrix.command.default.fallback.enabled 當執行失敗或者請求被拒絕,是否會嘗試調用
hystrixCommand.getFallback() 。默認true
Circuit Breaker相關的屬性
hystrix.command.default.circuitBreaker.enabled 用來跟蹤circuit的健康性,如果未達標則讓request短路。默認true
hystrix.command.default.circuitBreaker.requestVolumeThreshold 一個rolling window內最小的請 求數。如果設為20,那么當一個rolling window的時間內(比如說1個rolling window是10秒)收到19個請求, 即使19個請求都失敗,也不會觸發circuit break。默認20
hystrix.command.default.circuitBreaker.sleepWindowInMilliseconds 觸發短路的時間值,當該值設 為5000時,則當觸發circuit break后的5000毫秒內都會拒絕request,也就是5000毫秒后才會關閉circuit。 默認5000
hystrix.command.default.circuitBreaker.errorThresholdPercentage錯誤比率閥值,如果錯誤率>=該 值,circuit會被打開,並短路所有請求觸發fallback。默認50
hystrix.command.default.circuitBreaker.forceOpen 強制打開熔斷器,如果打開這個開關,那么拒絕所 有request,默認false
hystrix.command.default.circuitBreaker.forceClosed 強制關閉熔斷器 如果這個開關打開,circuit將 一直關閉且忽略circuitBreaker.errorThresholdPercentage
Metrics相關參數
hystrix.command.default.metrics.rollingStats.timeInMilliseconds 設置統計的時間窗口值的,毫秒 值,circuit break 的打開會根據1個rolling window的統計來計算。若rolling window被設為10000毫秒, 則rolling window會被分成n個buckets,每個bucket包含success,failure,timeout,rejection的次數 的統計信息。默認10000
hystrix.command.default.metrics.rollingStats.numBuckets 設置一個rolling window被划分的數 量,若numBuckets=10,rolling window=10000,那么一個bucket的時間即1秒。必須符合rolling window % numberBuckets == 0。默認10
hystrix.command.default.metrics.rollingPercentile.enabled 執行時是否enable指標的計算和跟蹤, 默認true
hystrix.command.default.metrics.rollingPercentile.timeInMilliseconds 設置rolling percentile window的時間,默認60000
hystrix.command.default.metrics.rollingPercentile.numBuckets 設置rolling percentile window的numberBuckets。邏輯同上。默認6
hystrix.command.default.metrics.rollingPercentile.bucketSize 如果bucket size=100,window =10s,若這10s里有500次執行,只有最后100次執行會被統計到bucket里去。增加該值會增加內存開銷以及排序 的開銷。默認100
hystrix.command.default.metrics.healthSnapshot.intervalInMilliseconds 記錄health 快照(用 來統計成功和錯誤綠)的間隔,默認500ms
Request Context 相關參數
hystrix.command.default.requestCache.enabled 默認true,需要重載getCacheKey(),返回null時不 緩存
hystrix.command.default.requestLog.enabled 記錄日志到HystrixRequestLog,默認true
Collapser Properties 相關參數
hystrix.collapser.default.maxRequestsInBatch 單次批處理的最大請求數,達到該數量觸發批處理,默認 Integer.MAX_VALU
hystrix.collapser.default.timerDelayInMilliseconds 觸發批處理的延遲,也可以為創建批處理的時間 +該值,默認10
hystrix.collapser.default.requestCache.enabled 是否對HystrixCollapser.execute() and HystrixCollapser.queue()的cache,默認true
ThreadPool 相關參數
線程數默認值10適用於大部分情況(有時可以設置得更小),如果需要設置得更大,那有個基本得公式可以 follow: requests per second at peak when healthy × 99th percentile latency in seconds + some breathing room 每秒最大支撐的請求數 (99%平均響應時間 + 緩存值) 比如:每秒能處理1000個請求,99%的請求響應時間是60ms,那么公式是: 1000 (0.060+0.012)
基本得原則時保持線程池盡可能小,他主要是為了釋放壓力,防止資源被阻塞。 當一切都是正常的時候,線程池一般僅會有1到2個線程激活來提供服務
hystrix.threadpool.default.coreSize 並發執行的最大線程數,默認10
hystrix.threadpool.default.maxQueueSize BlockingQueue的最大隊列數,當設為-1,會使用
SynchronousQueue,值為正時使用LinkedBlcokingQueue。該設置只會在初始化時有效,之后不能修改threadpool的queue size,除非reinitialising thread executor。默認-1。
hystrix.threadpool.default.queueSizeRejectionThreshold 即使maxQueueSize沒有達到,達到 queueSizeRejectionThreshold該值后,請求也會被拒絕。因為maxQueueSize不能被動態修改,這個參數將允 許我們動態設置該值。if maxQueueSize == 1,該字段將不起作用
hystrix.threadpool.default.keepAliveTimeMinutes 如果corePoolSize和maxPoolSize設成一樣(默認 實現)該設置無效。如果通過plugin(https://github.com/Netflix/Hystrix/wiki/Plugins)使用自定義 實現,該設置才有用,默認1.
hystrix.threadpool.default.metrics.rollingStats.timeInMilliseconds 線程池統計指標的時間,默 認10000
hystrix.threadpool.default.metrics.rollingStats.numBuckets 將rolling window划分為n個 buckets,默認10