一. 分布式系統面臨的問題
複雜分布式體系結構中的應用程式有數十個依賴關系,每個依賴關系在某些時候将不可避免地損失的。
服務雪崩
多個微服務之間調用的時候,假設微服務A調用微服務B和微服務C,微服務B和微服務C又調用其它的微服務,這就是所謂的"扇出"。
如果扇出的鍊路上某個微服務的調用響應時間過長或者不可用,對微服務A的調用就會占用越來越多的系統資源,進而引起系統崩潰,所謂的“雪崩效應”.
對于高流量的應用來說,單-的後端依賴可能會導緻所有伺服器上的所有資源都在幾秒鐘内飽和。比失敗更糟糕的是,這些應用程式還可能導緻服務之間的延遲增加,備份隊列,線程和其他系統資源緊張,導緻整個系統發生更多的級聯故障。這些都表示需要對故障和延遲進行隔離和管理,以便單個依賴關系的失敗,不能取消整個應用程式或系統。
是以,通常當你發現一個子產品下的某個執行個體失敗後,這時候這個子產品依然還會接收流量,然後這個有問題的子產品還調用了其他的子產品,這樣就會發生級聯故障,或者叫雪崩。
二. Hystrix是什麼?
Hystrix是一個用于處理分布式系統的延遲和容錯的開源庫, 在分布式系統裡,許多依賴不可避免的會調用失敗,比如逾時、異常等
Hystrix能夠保證在一個依賴出問題的情況下,不會導緻整體服務失敗,避免級聯故障,以提高分布式系統的彈性。
“斷路器”本身是一種開關裝置,當某個服務單元發生故障之後,通過斷路器的故障監控(類似熔斷保險絲),向調用方傳回一個符合預期的、可處理的備選響應(FallBack) ,而不是長時間的等待或者抛出調用方無法處理的異常,這樣就保證了服務調用方的線程不會被長時間、不必要地占用,進而避免了故障在分布式系統中的蔓延,乃至雪崩。
但是停更了…,不影響先學一波,畢竟設計理論與思想最重要嘛,大家都是抄作業,像阿裡的sentinel…
總結一下:
Hystrix主要有三個功能:
- 服務降級
- 服務熔斷
- 服務限流
其中服務降級和服務熔斷最為突出,這裡對于服務降級和服務熔斷做一個簡單說明:
服務降級:例如我們通路一個網站時,有時由于網絡/并發高,此時網站會給我們響應一個伺服器忙請稍後再試。此時因為網絡或某些原因,網站的背景暫時處理不過來,為了不讓使用者等太長時間,此時就會直接傳回一個服務降級頁面給使用者。
![]()
springcloud元件入門使用——Hystrix 服務熔斷:這裡相當于一個保險絲,當保險絲斷了的時候就會利用服務降級給客戶提示,并直接拒絕通路服務,當保險絲再某段時間修好了就會自動又恢複功能。熔斷相對于降級是一個更高壓力和網站錯誤出現的情況,而服務降級更多是網絡/高通路的情況觸發的保護系統其他功能的實作。
服務熔斷不等于服務降級,服務熔斷錯誤程度大于服務降級,服務熔斷通過服務降級通知使用者不可用,當服務可用時會自動恢複。
那麼此時怎麼使用Hystrix進行服務降級、熔斷處理?
一、服務降級:
1、當要在提供者服務做服務降級:
pom:
<!--hystrix--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </dependency>
啟動類加上:@EnableHystrix注解
在相應處理服務降級的方法/接口上編寫,這裡改造了一個接口
(超過3秒就觸發降級方法paymentInfoTimeOutHandler,這裡我睡眠了4秒)
http://localhost:8001/payment/lb@HystrixCommand(fallbackMethod = "paymentInfoTimeOutHandler",commandProperties = { @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "3000") }) @GetMapping("/payment/lb") public String getPaymentLB() throws InterruptedException { Thread.sleep(4000); return serverPort; } public String paymentInfoTimeOutHandler(){ return "程式運作繁忙或報錯,請稍後再試*****"+"目前線程: "+Thread.currentThread().getName()+id+"\t "+"orz!"; }
如果我想應用到整個controller類的所有接口:![]()
springcloud元件入門使用——Hystrix @DefaultProperties(defaultFallback = "payment_Global_FallbackMethod") @RestController @Slf4j public class PaymentController { @Resource private PaymentService paymentService; @Value("${server.port}") private String serverPort; // @HystrixCommand(fallbackMethod = "paymentInfoTimeOutHandler",commandProperties = { // @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "3000") // }) @HystrixCommand //将之前的注釋掉,如果沒注釋掉還是會走自己方法上定義的規則 @GetMapping("/payment/lb") public String getPaymentLB() throws InterruptedException { int i =10/0; return serverPort; } public String paymentInfoTimeOutHandler(){ return "程式運作繁忙或報錯,請稍後再試*****"+"目前線程: "+Thread.currentThread().getName()+"\t "+"orz!"; } public String payment_Global_FallbackMethod(){ return "Global異常處理資訊,請稍後再試: orz~"; } }
![]()
springcloud元件入門使用——Hystrix 注意如果用消費者去掉提供者,其中提供者有兩個服務叢集,其中一個出錯了,另一個沒出錯,此時會去調用沒出錯的提供者。
2、消費者實作降級(如果提供者有做降級處理,則調用出錯傳回提供者降級結果,如是消費者自己出錯/提供者沒做降級處理則傳回自己的那個降級結果)
pom:
yml:(提供者不用)<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </dependency>
在feign接口上:其中PaymentHystrixServiceImpl是降級處理類#yml添加配置,開啟 hystrix feign: hystrix: enabled: true
編寫降級處理類:(實作feign接口)@FeignClient(value = "cloud-payment-service",fallback = PaymentHystrixServiceImpl.class)//調用的服務名 @Component public interface PaymentFeignService { @GetMapping(value = "/payment/get/{id}") public CommonResult getPaymentById(@PathVariable("id") Long id); @GetMapping(value="/payment/lb") public String getPaymentLB() throws InterruptedException; }
啟動類:@Component public class PaymentHystrixServiceImpl implements PaymentFeignService { @Override public CommonResult getPaymentById(Long id) { return null; } @Override public String getPaymentLB() throws InterruptedException { return "程式運作繁忙或報錯,請稍後再試*****"+"目前線程: "+Thread.currentThread().getName()+"\t "+"orz!"; } }
controller:@SpringBootApplication @EnableFeignClients @EnableHystrix public class OrderOpenFeignMain80 { public static void main(String[] args) { SpringApplication.run(OrderOpenFeignMain80.class,args); } }
此時遠端提供者我們僞造其接口出錯:@RestController public class OrderFeignController { @Resource private PaymentFeignService paymentFeignService; @GetMapping(value = "/consumer/payment/get/{id}") public CommonResult getPaymentById(@PathVariable("id") Long id){ CommonResult paymentById = paymentFeignService.getPaymentById(id); return paymentById ; } @GetMapping(value = "/consumer/payment/lb") public String getPaymentB() throws InterruptedException { String paymentLB = paymentFeignService.getPaymentLB(); return paymentLB ; } }
測試:http://localhost/consumer/payment/lb@GetMapping("/payment/lb") public String getPaymentLB() throws InterruptedException { int i = 10/0; return serverPort; }
![]()
springcloud元件入門使用——Hystrix
至于服務熔斷,也是調用服務降級方法,隻是我們可以配置服務熔斷的條件:
例如:下面表示在10s内,請求10次如果成功低于60則進行熔斷@HystrixCommand(fallbackMethod="paymentCircuitBreakerFallback", commandProperties={ @HystrixProperty(name = "circuitBreaker.enabled" ,value = "true"),//是否開啟斷路器 @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold",value = "10"),// 請求次數 @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds",value = "10000"),//時間視窗期 " @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage",value = "60")//失敗率達到多少後跳閘 }) public String paymentCircuitBreaker(@PathVariable("id") Integer id){ if (id<0){ throw new RuntimeException(); } String serialNumber = IdUtil.simpleUUID(); return Thread.currentThread().getName()+"\t "+"調用成功,流水号: "+serialNumber; } public String paymentCircuitBreakerFallback(@PathVariable("id") Integer id){ return "id不能為負數,請稍後再試~ id: "+ id; }
![]()
springcloud元件入門使用——Hystrix 這裡就不作測試了
總結
設計熔斷的三個參數
熔斷打開
請求不再進行調用目前服務,内部設定時鐘一般為MTTR (平均故障處理時間),當打開時長達到所設時鐘則進入半熔斷狀态
熔斷關閉
熔斷關閉不會對服務進行熔斷
熔斷半開
部分請求根據規則調用目前服務,如果請求成功且符合規則則認為目前服務恢複正常,關閉熔斷
![]()
springcloud元件入門使用——Hystrix 就是這哥三~
涉汲到斷路器的三個重要參數:快照時間窗、請求總數閥值、錯誤百分比閥值。
- 1:快照時間窗:斷路器确定是否打開需要統計一些請求和錯誤資料,而統計的時間範圍就是快照時間窗,預設為最近的10秒。
- 2:請求總數閥值:在快照時間窗内,必須滿足請求總數閥值才有資格熔斷。預設為20, 意味着在10秒内,如果該hystrix指令的調用次數不足20次,即使所有的請求都逾時或其他原因失敗,斷路器都不會打開。
- 3:錯誤百分比閥值:當請求總數在快照時間窗内超過了閥值,比如發生了30次調用,如果在這30次調用中,有15次發生了逾時異常,也就是超過50%的錯誤百分比,在預設設定50%閥值情況下,這時候就會将斷路器打開。
原來的主邏輯要如何恢複呢?
對于這一-問題,hystrix也為我們實作了自動恢複功能。
- 當斷路器打開,對主邏輯進行熔斷之後,hystrix會啟動一個休眠時間窗在這個時間窗内,降級邏輯是臨時的成為主邏輯,
- 當休眠時間窗到期,斷路器将進入半開狀态,釋放一次請求到原來的主邏輯上,如果此次請求正常傳回,那麼斷路器将繼續閉合,
- 主邏輯恢複,如果這次請求依然有問題,斷路器繼續進入打開狀态,休眠時間窗重新計時。
斷路器開啟/關閉條件
![]()
springcloud元件入門使用——Hystrix Hystrix 全部配置一覽
此部分内容,可參考官方文檔:https://github.com/Netflix/Hystrix/wiki/Configuration#execution.isolation.strategy@HystrixCommand(fallbackMethod = "str_fallbackMethod", groupKey = "strGroupCommand", commandKey = "strCommand", threadPoolKey = "strThreadPool", commandProperties = { //設定執行隔離政策,THREAD 表示線程池 SEMAPHORE:信号量隔離 預設為THREAD線程池 @HystrixProperty(name = "execution.isolation.strategy", value = "THREAD"), // 當隔離政策選擇信号池隔離的時候,用來設定信号池的大小(最大并發數) @HystrixProperty(name = "execution.isolation.semaphore.maxConcurrentRequests", value = "10"), // 配置指令執行的逾時時間 @HystrixProperty(name = "execution.isolation.thread.timeoutinMilliseconds", value = "10"), // 是否啟用逾時時間 @HystrixProperty(name = "execution.timeout.enabled", value = "true"), // 執行逾時的時候是否中斷 @HystrixProperty(name = "execution.isolation.thread.interruptOnTimeout", value = "true"), // 執行被取消的時候是否中斷 @HystrixProperty(name = "execution.isolation.thread.interruptOnCancel", value = "true"), // 允許回調方法執行的最大并發數 @HystrixProperty(name = "fallback.isolation.semaphore.maxConcurrentRequests", value = "10"), // 服務降級是否啟用,是否執行回調函數 @HystrixProperty(name = "fallback.enabled", value = "true"), // 設定斷路器是否起作用。 @HystrixProperty(name = "circuitBreaker.enabled", value = "true"), // 該屬性用來設定在滾動時間窗中,斷路器熔斷的最小請求數。例如,預設該值為 20 的時候, // 如果滾動時間窗(預設10s)内僅收到了19個請求,及時這19個請求都失敗了,斷路也不會打開。 @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "20"), // 該屬性用來設定在滾動時間窗中,表示在滾動時間窗中,在請求數量超過 circuitBreaker.requestVolumeThreshold 的情況下, // 如果錯誤請求數的百分比超過 50,就把斷路器設定為"打開"狀态,否則就設定為"關閉"狀态 @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "50"), // 該屬性用來設定當斷路器打開之後的休眠時間窗。休眠時間窗結束之後,會将斷路器置為"半開"狀态, // 嘗試熔斷的請求指令,如果依然失敗就将斷路器繼續設定為"打開"狀态,如果成功就設定為"關閉"狀态 @HystrixProperty(name = "circuitBreaker.sleepWindowinMilliseconds", value = "5000"), // 斷路器強制打開 @HystrixProperty(name = "circuitBreaker.forceOpen", value = "false"), // 斷路器強制關閉 @HystrixProperty(name = "circuitBreaker.forceClosed", value = "false"), // 滾動時間窗設定,該時間用于斷路器判斷健康度時,需要收集資訊的持續時間 @HystrixProperty(name = "metrics.rollingStats.timeinMilliseconds", value = "10000"), // 該屬性用來設定滾動時間窗統計名額資訊時,劃分"桶"的數量,斷路器在手機名額資訊的時候會根據設定的時間窗長度拆分成多個"桶"來累計各路徑成本,每個 // "桶"記錄了一段時間内的采集名額。比如 10 秒内拆分成 10 個"桶'收集這樣,是以 timeinMilliseconds 必須能被 numBuckets 整除。否則會抛異常 @HystrixProperty(name = "metrics.rollingStats.numBuckets", value = "10"), // 該屬性用來設定對指令執行的延遲是否采用百分位數來跟蹤和計算。如果設定為 false,name所有的概要統計都将傳回-1 @HystrixProperty(name = "metrics.rollingPercentile.enabled", value = "false"), // 該屬性用來設定百分位統計的滾動視窗的持續時間,機關為毫秒 @HystrixProperty(name = "metrics.rollingPercentile.timeInMilliseconds", value = "60000"), // 該屬性用來設定百分位統計滾動視窗中使用 "桶" 的數量 @HystrixProperty(name = "metrics.rollingPercentile.numBuckets", value = "60000"), // 該屬性用來設定在執行過程中每個"桶"中保留的最大執行次數。如果在滾動時間窗内發生超過該設定值的執行次數 // 就從最初的位置開始重寫。例如,将該值設定為100,滾動視窗為10秒,若在10秒内一個"桶"中發生了500次執行, // 那麼該"桶"中隻保留最後的100次執行的統計。另外,增加該值的大小将會增加記憶體量的消耗,并增加排序百分位數所需的計算時間 @HystrixProperty(name = "metrics.rollingPercentile.bucketSize", value = "100"), // 該屬性用來設定采集意向斷路器狀态的健康快照(請求的成功、錯誤百分比)的間隔等待時間 @HystrixProperty(name = "metrics.healthSnapshot.intervalinMilliseconds", value = "500"), // 是否開啟請求緩存 @HystrixProperty(name = "requestCache.enabled", value = "true"), // HystrixCommand 的執行和事件是否列印日志到 HystrixRequestLog 中 @HystrixProperty(name = "requestLog.enabled", value = "true") }, threadPoolProperties = { // 該參數用來設定執行指令線程池的核心線程數,該值也就是指令執行的最大并發量 @HystrixProperty(name = "coreSize", value = "10"), // 該參數用來設定線程池的最大隊列大小。當設定為 -1 時,線程池将使用 SynchronousQueue 實作的隊列,否則将使用 LinkedBlockingQueue 實作的隊列 @HystrixProperty(name = "maxQueueSize", value = "-1"), // 該參數用來為隊列設定拒絕門檻值。通過該參數,即使隊列沒有達到最大值也能拒絕請求。該參數主要是對 LinkedBlockingQueue 隊列的補充,因為LinkedBlockingQueue // 隊列不能動态修改它的對象大小,而通過該屬性就可以調整拒絕請求的隊列大小了 @HystrixProperty(name = "queueSizeRejectionThreshold", value = "5") } )
Hystrix DashBoard 圖形化界面搭建
這裡就不多說了,因為我也沒建成功,後面再去實作一下
還有Hystrix的限流就不講了現在都是用sentinel。Hystrix雖然已經不行了但是其設計思想和解決方案是非常不錯的,
eureka、hystrix雖然不太行了,但面試經常問,是以之後會對hystrix的原理寫一些分析分析。
(eureka、Hystrix、gateway、ribbon、openfign、nacos、Sentinel、seata都會對其原理做分析)