您好,登錄后才能下訂單哦!
這篇文章給大家分享的是有關SpringCloud-Hystrix實現原理是什么的內容。小編覺得挺實用的,因此分享給大家做個參考,一起跟隨小編過來看看吧。
(1) 服務降級
1)什么是服務降級
有了服務的熔斷,隨之就會有服務的降級,所謂服務降級,就是當某個服務熔斷之后,服務端提供的服務將不再被調用,此時由客戶端自己準備一個本地的fallback回調,返回一個默認值來代表服務端的返回;
這種做法,雖然不能得到正確的返回結果,但至少保證了服務的可用,比直接拋出錯誤或者服務不可用要好很多。
2)如何進行服務降級
(1)服務端
1、POM
<dependencies> <!--hystrix--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </dependency> <!--eureka client--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <!--web--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>com.tfjy.springcloud</groupId> <artifactId>cloud-api-commons</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>
2、YML
server: port: 8001 spring: application: name: cloud-provider-hystrix-payment eureka: client: register-with-eureka: true fetch-registry: true service-url: #defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka defaultZone: http://localhost:7001/eureka/
3、主啟動
需要在主啟動上加上
4、業務類
controller
@GetMapping("/payment/hystrix/timeout/{id}") public String paymentInfo_TimeOut(@PathVariable("id") Integer id){ String result = paymentService.paymentInfo_TimeOut(id); log.info("*****result"+result); return result; }
service
@HystrixCommand(fallbackMethod = "paymentInfo_TimeOutHandler",commandProperties = { @HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value="5000") }) public String paymentInfo_TimeOut(Integer id){ try{ TimeUnit.SECONDS.sleep(5); }catch (InterruptedException e){ e.printStackTrace(); } return "線程池:"+Thread.currentThread().getName()+"paymentInfo_TimeOut,id"+id+"O(∩_∩)O哈哈~"+"耗時5秒鐘"; } public String paymentInfo_TimeOutHandler(Integer id){ return "線程池:"+Thread.currentThread().getName()+"paymentInfo_TimeOutHandler,id"+id+"o(╥﹏╥)o"; }
在代碼上加上HystrixCommand注解,然后由個屬性fallbackMethod,value值可以填寫方法的名字。
當發生超時的時候,就會走下面的方法。
1、POM
<dependencies> <!--openfeign--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <!--hystrix--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </dependency> <!--eureka client--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <!-- 引入自己定義的api通用包,可以使用Payment支付Entity --> <dependency> <groupId>com.tfjy.springcloud</groupId> <artifactId>cloud-api-commons</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <!--web--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <!--一般基礎通用配置--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>
2、YML
server: port: 80 eureka: client: register-with-eureka: false service-url: defaultZone: http://localhost:7001/eureka/ feign: hystrix: enabled: true
3、主啟動
4、業務
@GetMapping("/consumer/payment/hystrix/timeout/{id}") @HystrixCommand(fallbackMethod = "paymentTimeOutFallbackMethod",commandProperties = { @HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value="1500") }) // @HystrixCommand public String paymentInfo_TimeOut(@PathVariable("id") Integer id) { int age = 10/0; String result = paymentHystrixService.paymentInfo_TimeOut(id); return result; } public String paymentTimeOutFallbackMethod(@PathVariable("id") Integer id) { return "我是消費者80,對方支付系統繁忙請10秒鐘后再試或者自己運行出錯請檢查自己,o(╥﹏╥)o"; }
@Component @FeignClient(value = "CLOUD-PROVIDER-HYSTRIX-PAYMENT",fallback = PaymentFallbackService.class) public interface PaymentHystrixService { @GetMapping("/payment/hystrix/ok/{id}") public String paymentInfo_ok(@PathVariable("id") Integer id); @GetMapping("/payment/hystrix/timeout/{id}") public String paymentInfo_TimeOut(@PathVariable("id") Integer id); }
這個參數要一樣
這個只能等待1.5s但是在8001要5s
@RestController @Slf4j @DefaultProperties(defaultFallback = "payment_Global_FallbackMethod") public class OrderHystirxController { @GetMapping("/consumer/payment/hystrix/timeout/{id}") // @HystrixCommand(fallbackMethod = "paymentTimeOutFallbackMethod",commandProperties = { // @HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value="1500") // }) @HystrixCommand public String paymentInfo_TimeOut(@PathVariable("id") Integer id) { int age = 10/0; String result = paymentHystrixService.paymentInfo_TimeOut(id); return result; } public String paymentTimeOutFallbackMethod(@PathVariable("id") Integer id) { return "我是消費者80,對方支付系統繁忙請10秒鐘后再試或者自己運行出錯請檢查自己,o(╥﹏╥)o"; } // 下面是全局fallback方法 public String payment_Global_FallbackMethod() { return "Global異常處理信息,請稍后再試,/(ㄒoㄒ)/~~"; } }
@DefaultProperties(defaultFallback = "payment_Global_FallbackMethod")
1:1 每個方法配置一個服務降級方法,技術上可以,實際上傻X
1:N 除了個別重要核心業務有專屬,其它普通的可以通過@DefaultProperties(defaultFallback = "") 統一跳轉到統一處理結果頁面
通用和獨享的各自分開,避免了代碼膨脹,合理減少了代碼量
(1) 程序運行異常 (當我們把service方法中改成int i =10/0)
(2) 超時
(3) 服務熔斷觸發服務降級
(4) 線程池/信號量也會導致服務降級
(2) 服務熔斷
一、概念
(1)什么是服務熔斷
熔斷這一概念來源于電子工程中的斷路器(Circuit Breaker)。在互聯網系統中,當下游服務因訪問壓力過大而響應變慢或失敗,上游服務為了保護系統整體的可用性,可以暫時切斷對下游服務的調用。
在固定時間窗口內,接口調用超時比率達到一個閾值,會開啟熔斷。進入熔斷狀態后,后續對該服務接口的調用不再經過網絡,直接執行本地的默認方法,達到服務降級的效果。
熔斷不可能是永久的。當經過了規定時間之后,服務將從熔斷狀態回復過來,再次接受調用方的遠程調用。
(2)熔斷的三種狀態
熔斷關閉狀態(Closed) 服務沒有故障時,熔斷器所處的狀態,對調用方的調用不做任何限制。
熔斷開啟狀態(Open) 在固定時間窗口內(Hystrix默認是10秒),接口調用出錯比率達到一個閾值(Hystrix默認為50),會進入熔斷開啟狀態,進入熔斷狀態后,后續對該服務接口的調用不再經過網絡,直接執行本地的fallback方法。
半熔斷狀態(half-open)哎進入熔斷開啟狀態一段時間之后(Hystrix默認是5秒),熔斷器會進入半熔斷狀態,所謂半熔斷就是嘗試恢復服務調用,允許有限的流量調用該服務,并監控調用成功率,如果成功率達到了預期,則說明服務已恢復,進入熔斷關閉狀態;如果成功率仍舊很低,則重新進入熔斷關閉狀態。
(2)pom
<dependencies> <!--hystrix--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </dependency> <!--eureka client--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <!--web--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency><!-- 引入自己定義的api通用包,可以使用Payment支付Entity --> <groupId>com.atguigu.springcloud</groupId> <artifactId>cloud-api-commons</artifactId> <version>${project.version}</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>
(2)YML
server: port: 8001 spring: application: name: cloud-provider-hystrix-payment eureka: client: register-with-eureka: true fetch-registry: true service-url: #defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka defaultZone: http://eureka7001.com:7001/eureka
(3)主啟動
@SpringBootApplication @EnableEurekaClient @EnableCircuitBreaker public class PaymentHystrixMain8001 { public static void main(String[] args) { SpringApplication.run(PaymentHystrixMain8001.class, args); } }
(4)業務
//====服務熔斷 @GetMapping("/payment/circuit/{id}") public String paymentCircuitBreaker(@PathVariable("id") Integer id) { String result = paymentService.paymentCircuitBreaker(id); log.info("****result: "+result); return result; }
//=====服務熔斷 @HystrixCommand(fallbackMethod = "paymentCircuitBreaker_fallback",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("******id 不能負數"); } String serialNumber = IdUtil.simpleUUID(); return Thread.currentThread().getName()+"\t"+"調用成功,流水號: " + serialNumber; } public String paymentCircuitBreaker_fallback(@PathVariable("id") Integer id) { return "id 不能負數,請稍后再試,/(ㄒoㄒ)/~~ id: " +id; }
涉及到斷路器的三個重要參數:快照時間窗、請求總數閾值、錯誤百分比閾值
1:快照時間窗: 斷路器確定是否打開需要統計一些請求和錯誤數據,而統計的時間范圍就是快照時間窗,默認為最近的10秒。
2: 請求總數閾值: 在快照時間窗內,必須滿足請求總數閾值才有資格熔斷。默認為20,意味著在10秒內,如果該hystrix命令的調用次數不足20次,即使所有的請求都超時或其他原因失敗,斷路器都不會打開。
3: 錯誤百分比閾值:當請求總數在快照時間窗口內超過了閾值,比如發生了30次調用,如果在這30次調用中,有15次發生了超時異常,也就是超過50%的錯誤百分比,在默認設定50%閾值情況下,這時候就會將斷路器打開。
(3) 服務限流
1、限流概念
Hystrix把一個分布式系統的某一個服務打造成一個高可用的服務最重要的手段之一就是資源隔離,即通過限流來限制對某一服務的訪問量,比如說對Mysql的訪問,為了避免過大的流量直接請求mysql服務,hstrix通過線程池或者信號量技術進行限流訪問。
我們了解到Hystrix的兩種隔離技術:線程池和信號量。線程池和信號量,也分析了在什么樣的場景下使用線程池和信號量,通常來說線程池資源隔離技術一般用于對依賴服務的網絡請求訪問,需要解決timeout問題。信號量則適合對內部的一些比較復雜的業務邏輯訪問,不涉及任何的網絡請求,當并發量超過計數器指定值時,直接拒絕。
線程池隔離的最大優點在于:任何一個依賴服務都可以被隔離在自己的線程池內,即使自己的線程池資源填滿了,也不會影響任何其他的服務調用。最大缺點在于:增加了cpu的開銷,除了tomcat本身的調用線程之外,還有hystrix自己管理的線程池。每個command的執行都依托一個獨立的線程,會進行排隊,調度,還有上下文切換。
2、資源隔離
(1)線程隔離
Service
//------------------線程隔離 //groupKey 一組command ,如果沒有配這個,相同的groupkey會使用同一個線程池 @HystrixCommand(groupKey = "thread1-group",commandKey = "thread1",threadPoolKey = "thread1",commandProperties = { @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "30000") },threadPoolProperties = { @HystrixProperty(name = "coreSize",value = "3"), @HystrixProperty(name = "maxQueueSize",value = "5") }) public void thread1(){ try { Thread.sleep(10000); } catch (InterruptedException e) { System.out.println(111); } System.out.println(Thread.currentThread().getName()); }
controller
@GetMapping("getThread") public void getThread(){ System.out.println(Thread.currentThread().getName()); paymentService.thread1(); }
當請求getThread的時候
http-nio-8001-exec-1 hystrix-thread1-1 http-nio-8001-exec-3 hystrix-thread1-2
會發現兩個線程池是不一樣的。
我現在在service中再添加一個
//------------------線程隔離 //groupKey 一組command ,如果沒有配這個,相同的groupkey會使用同一個線程池 @HystrixCommand(groupKey = "thread1-group",commandKey = "thread1",threadPoolKey = "thread1",commandProperties = { @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "3000") },threadPoolProperties = { @HystrixProperty(name = "coreSize",value = "3"), @HystrixProperty(name = "maxQueueSize",value = "5") }) public void thread1(){ try { Thread.sleep(2000); } catch (InterruptedException e) { System.out.println(111); } System.out.println(Thread.currentThread().getName()); } @HystrixCommand(groupKey = "thread2-group",commandKey = "thread2",threadPoolKey = "thread2",threadPoolProperties = { @HystrixProperty(name = "coreSize",value = "3"), @HystrixProperty(name = "maxQueueSize",value = "5"), },commandProperties = { @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "1000")}) public void thread2(){ System.out.println(111); System.out.println(Thread.currentThread().getName()); }
在controller中調用
@GetMapping("getThread") public void getThread(){ System.out.println(Thread.currentThread().getName()); paymentService.thread1(); paymentService.thread2(); }
http-nio-8001-exec-1 hystrix-thread1-1 111 hystrix-thread2-1
@HystrixProperty(name = "coreSize",value = "3"), ## 這個表示核心線程數 @HystrixProperty(name = "maxQueueSize",value = "5"), ## 這個表示這個隊列中的線程數為5個 所以這里面有8個
ps:下面看下對SpringCloud Hystrix的使用個人總結
和一般的開箱即用工具類似,SpringCloud Hystrix只需要最多四步即可基本使用上
1. 引入依賴: spring-cloud-starter-hystrix
2. 添加支持: 在啟動類上添加@EnableHystrix
3. 具體使用: 在有熔斷需求的服務接口實現上標注@HystrixCommand,指定發生熔斷時的回調方法
4. 按需配置: 比如配置熔斷時間
需要注意的是在具體使用環節:
1. 回調方法必須在聲明@HystrixCommand的方法所在的類中,即回調方法必須是服務接口的兄弟方法
2. 回調方法的參數列表必須和服務接口的參數列表完全一致,否則報找不到回調方法異常.
回調方法保持與原接口參數列表強一致,說明回調方法就是原接口的替補接口,備胎接口.
通過對Hystrix的使用總結,再次驗證了開箱即用工具的使用套路.
1.引入依賴
2.添加支持
3.具體使用
4.按需配置
感謝各位的閱讀!關于“SpringCloud-Hystrix實現原理是什么”這篇文章就分享到這里了,希望以上內容可以對大家有一定的幫助,讓大家可以學到更多知識,如果覺得文章不錯,可以把它分享出去讓更多的人看到吧!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。