spring cloud深入學習(五)-----熔斷器Hystrix
什麽是Hystrix?
在分布式系統中,服務與服務之間的依賴錯綜復雜,一種不可避免的情況就是某些服務會出現故障,導致依賴於它們的其他服務出現遠程調度的線程阻塞。Hystrix提供了熔斷器功能,能夠阻止分布式系統中出現聯動故障,通過隔離服務的訪問點阻止聯動故障的,並提供了故障的解決方案,從而提高 了整個分布式系統的彈性。
簡單來說:微服務架構中存在很多的服務,例如A、B、C、D、E5個服務,並且對於一次調用來說,並不僅僅是訪問一個服務,按照功能劃分來說,每個服務做得事情是單一的,所以大部分的請求都是類似於A-C-E,跨過多個服務。
如果單個服務的正常運行幾率為99.00%(機房的不可靠性、運營商的不可靠性等等),那麽對於A-C-E調用流程來說,幾率為99.00%*99.00%*99.00%=97.03%,也就意味著10000次請求有300多次會失敗,問題還是比較嚴重的。
Hystrix設計原則
- 防止單個服務的故障耗盡整個服務的Servlet容器(例如Tomcat)的線程資源。
- 快速失敗機制,如果某個服務出現了故障,則調用該服務的請求快速失敗,而不是線程等待。
- 提供回退(fallback)方案,在請求發生故障時,提供設定好的回退方案。
- 使用熔斷機制,防止故障擴散到其他服務。
- 提供熔斷器的監控組件Hystrix Dashboard ,可以實時監控熔斷器的狀態。
Hystrix工作機制
- 當服務的某個 API 接口的失敗次數在一定時間內小於設定的閥值時,熔斷器處於關閉狀態,該 API 接口正常提供服務;
- 當該API 接口處理請求的失敗次數大於設定的閥值時, Hystrix 判定該 API 接口出現了故障,打開熔斷器,這時請求該 API接口會執行快速失敗的邏輯(即 fall back 回退的邏輯),不執行業務邏輯,請求的線程不會處於阻塞狀態;
- 處於打開狀態的熔斷器一段時間後會處於半打開狀態,並將一定數量的請求執行正常邏輯。剩余的請求會執行快速失敗,若執行正常邏輯的請求失敗了,則熔斷器繼續打開;若成功了 ,則將熔斷器關閉。這樣熔斷器就具有了自我修復的能力。
使用方法
1、在RestTemplate和Ribbon上使用熔斷器
找到本人之前的eureka-consumer項目,gitbub地址:https://github.com/ali-mayun/eureka-consumer
新加依賴:
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> <version>2.1.1.RELEASE</version> </dependency>
運行主類:
package com.ty.eurekaconsumer; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; import org.springframework.cloud.netflix.hystrix.EnableHystrix; @EnableEurekaClient @SpringBootApplication //使用這註解開啟hystrix的功能 @EnableHystrix public class EurekaConsumerApplication { public static void main(String[] args) { SpringApplication.run(EurekaConsumerApplication.class, args); } }
更改之前的service
package com.ty.eurekaconsumer.service; import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.client.RestTemplate; @Service public class RibbonService { @Autowired private RestTemplate restTemplate; //使用@HystrixCommand註解啟用熔斷器的功能,當服務熔斷後,將不調用遠程服務,直接調用本地的handleError方法 @HystrixCommand(fallbackMethod = "handleError") public String hiService(String name) { return restTemplate.getForObject("http://eureka-provider/firstCall?name=" + name, String.class); } public String handleError(String name) { return name + "在訪問遠程服務時,被熔斷了,調用本地的方法"; } }
現在啟動eureka-server、eureka-consumer兩個項目,不啟動eureka-provider,制造服務不能用的假象,運行效果如下:
2、在feign上使用熔斷器
上面一種方式相對來說還是比較麻煩的,使用feign則方便很多。
打開上篇博文中的eureka-feign-consumer項目,github地址:https://github.com/ali-mayun/eureka-feign-consumer
修改application.properties文件,開啟Hystrix功能:
# 服務名稱 spring.application.name=eureka-feign-consumer # 端口號 server.port=4001 # 服務註冊中心地址 eureka.client.serviceUrl.defaultZone=http://localhost:1001/eureka/ # 增加Hystrix功能 feign.hystrix.enabled=true
更改EurekaClientFeign:
package com.ty.eurekafeignconsumer.service; import com.ty.eurekafeignconsumer.config.FeignConfig; import com.ty.eurekafeignconsumer.hystrix.BaseHystrix; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; //新增fallback屬性指定出現熔斷由哪個類來處理。 @FeignClient(value = "eureka-provider", configuration = FeignConfig.class, fallback = BaseHystrix.class) public interface EurekaClientFeign { //只需要在接口中定義方法即可。調用eureka-provider服務的firstCall方法,並且feign集成了Ribbon @GetMapping(value = "/firstCall") String sayHiFromClientEureka(@RequestParam("name") String name); }
BaseHystrix:
package com.ty.eurekafeignconsumer.hystrix; import com.ty.eurekafeignconsumer.service.EurekaClientFeign; import org.springframework.stereotype.Component; //熔斷處理類需要實現調用遠程服務的service,例如本案例在EurekaClientFeign類中調用遠程服務,就需要實現此接口, //然後相同名字的方法就可以一一對應 @Component(value = "baseHystrix") public class BaseHystrix implements EurekaClientFeign { @Override public String sayHiFromClientEureka(String name) { return name + "訪問的遠程服務被熔斷"; } }
controller中有個坑,就是EurekaClientFeign是一個接口,主類中使用@EnableFeignClients開啟了feign功能,該註解會掃描@FeignClient,將使用到該註解的接口通過動態代理的方式創建類,因此EurekaClientFeign接口就會有兩個實現類。
另一個是BaseHystrix,用來處理發生熔斷後的邏輯,因此這裏使用@Autowired就出錯了,但是使用@Resource就ok,但是暫不清楚為啥會正確的找到動態代理生成的那個類,後續研究。。。
package com.ty.eurekafeignconsumer.controller; import com.ty.eurekafeignconsumer.service.EurekaClientFeign; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.ApplicationContext; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import javax.annotation.Resource; @RestController public class FeignController { //這個地方有點坑,因為EurekaClientFeign已經有另外一個實現類就是BaseHystrix,使用@Autowired會報錯 //不過暫時不清楚@Resource會註入正確的bean,後續待研究。。。 @Resource private EurekaClientFeign eurekaClientFeign; @GetMapping("/hi") public String sayHi(@RequestParam(value = "name", defaultValue = "馬雲", required = false) String name) { return eurekaClientFeign.sayHiFromClientEureka(name); } }
當eureka-provider正常開啟,效果如下:
關閉eureka-provider,訪問效果如下:
spring cloud深入學習(五)-----熔斷器Hystrix