服務隔離介紹
當大多數人在使用Tomcat時,多個HTTP服務會共享一個線程池,假設其中一個HTTP服務訪問的數據庫響應非常慢,這將造成服務響應時間延遲增加,大多數線程阻塞等待數據響應返回,導致整個Tomcat線程池都被該服務占用,甚至拖垮整個Tomcat。因此,如果我們能把不同HTTP服務隔離到不同的線程池,則某個HTTP服務的線程池滿了也不會對其他服務造成災難性故障。這就需要線程隔離或者信號量隔離來實現了。
使用線程隔離或信號隔離的目的是為不同的服務分配一定的資源,當自己的資源用完,直接返回失敗而不是占用別人的資源。
Hystrix實現服務隔離兩種方案
Hystrix的資源隔離策略有兩種,分別為:線程池和信號量。
線程池方式
優點:
1、 使用線程池隔離可以完全隔離第三方應用,請求線程可以快速放回。
2、 請求線程可以繼續接受新的請求,如果出現問題線程池隔離是獨立的不會影響其他應用。
3、 當失敗的應用再次變得可用時,線程池將清理並可立即恢復,而不需要一個長時間的恢復。
4、 獨立的線程池提高了並發性
缺點:
線程池隔離的主要缺點是它們增加計算開銷(CPU)。每個命令的執行涉及到排隊、調度和上 下文切換都是在一個單獨的線程上運行的。
線程池方式案例
1、使用上一章項目工程,在服務端項目的業務類Service中編寫如下方法:
1 // 服務限流 2 @HystrixCommand(fallbackMethod = "paymentCircuitBreaker_Thread", 3 // 屬性設置參考:HystrixCommandProperties 4 commandProperties = { 5 // 隔離策略,有THREAD和SEMAPHORE 6 @HystrixProperty(name="execution.isolation.strategy", value="THREAD") 7 }, 8 threadPoolProperties = { 9 // 線程池核心線程數 10 @HystrixProperty(name = "coreSize", value = "3"), 11 // 隊列最大長度 12 @HystrixProperty(name = "maxQueueSize", value = "5"), 13 // 排隊線程數量閾值,默認為5,達到時拒絕,如果配置了該選項,隊列的大小是該隊列 14 @HystrixProperty(name = "queueSizeRejectionThreshold", value = "7") 15 }) 16 public String paymentCircuitBreakerThread(@PathVariable("id") Integer id){ 17 int second = 500; 18 try { 19 // 休眠500毫秒 20 TimeUnit.MILLISECONDS.sleep(second); 21 } catch (InterruptedException e) { 22 // e.printStackTrace(); 23 } 24 // 異常 25 // int n = 10/0; 26 String result = "線程池:" + Thread.currentThread().getName() 27 + ",paymentCircuitBreakerThreadPool,ID == " + id 28 + ",耗時" + second + "毫秒"; 29 return result; 30 } 31 32 public String paymentCircuitBreaker_Thread(@PathVariable("id") Integer id){ 33 return " paymentCircuitBreaker_Thread 服務限流,ID == " + id; 34 }
2、在controller中調用該方法
1 // 線程 2 @GetMapping(value = "/payment/hystrix/thread/{id}") 3 public String paymentCircuitBreakerThreadPool(@PathVariable("id") Integer id) { 4 String result = paymentService.paymentCircuitBreakerThread(id); 5 log.info("result===" + result); 6 return result; 7 }
3、重啟項目,使用JMeter進行並發測試,測試url地址:http://localhost:8008/payment/hystrix/thread/1
測試發現,有一部分請求調用了fallback方法,一部分正常響應
信號量方式
使用一個原子計數器(或信號量)來記錄當前有多少個線程在運行,當請求進來時先判斷計數 器的數值,若超過設置的最大線程個數則拒絕該請求,若不超過則通行,這時候計數器+1,請求返 回成功后計數器-1。
與線程池隔離最大不同在於執行依賴代碼的線程依然是請求線程
提示:信號量的大小可以動態調整, 線程池大小不可以
信號量方式案例
1、使用上一章項目工程,在服務端項目的業務類Service中編寫如下方法:
1 // 服務限流 2 @HystrixCommand(fallbackMethod = "paymentCircuitBreaker_Semaphore", 3 // 屬性設置參考:HystrixCommandProperties 4 commandProperties = { 5 // 隔離策略,有THREAD和SEMAPHORE 6 // THREAD - 它在單獨的線程上執行,並發請求受線程池中的線程數量的限制(默認) 7 // SEMAPHORE - 它在調用線程上執行,並發請求受到信號量計數的限制 8 @HystrixProperty(name="execution.isolation.strategy", value="SEMAPHORE"), 9 // 設置在使用時允許到HystrixCommand.run()方法的最大請求數。默認值:10 ,SEMAPHORE模式有效 10 @HystrixProperty(name="execution.isolation.semaphore.maxConcurrentRequests", value="1") 11 12 }) 13 public String paymentCircuitBreakerSemaphore(@PathVariable("id") Integer id){ 14 int second = 500; 15 try { 16 // 休眠500毫秒 17 TimeUnit.MILLISECONDS.sleep(second); 18 } catch (InterruptedException e) { 19 // e.printStackTrace(); 20 } 21 // 異常 22 // int n = 10/0; 23 String result = "線程池:" + Thread.currentThread().getName() 24 + ",paymentCircuitBreaker_Semaphore,ID == " + id 25 + ",耗時" + second + "毫秒"; 26 return result; 27 } 28 29 public String paymentCircuitBreaker_Semaphore(@PathVariable("id") Integer id){ 30 return " paymentCircuitBreaker_Semaphore 服務限流,ID == " + id; 31 }
2、在controller中調用該方法
1 // 信號量 2 @GetMapping(value = "/payment/hystrix/semaphore/{id}") 3 public String paymentCircuitBreakerLimit(@PathVariable("id") Integer id) { 4 String result = paymentService.paymentCircuitBreakerSemaphore(id); 5 log.info("result===" + result); 6 return result; 7 }
3、重啟項目,使用JMeter進行並發測試,測試url地址:http://localhost:8008/payment/hystrix/semaphore/1
測試發現,有一部分請求調用了fallback方法,一部分正常響應