是什麼
Hystix是Netflix開源的一個延遲和容錯庫,用于隔離通路遠端服務、第三方庫,防止出現級聯失敗。
這麼說,可能大家還是不明白,那Hystrix到底是什麼呢?
我們來模拟一個場景,在微服務的大環境下,一個請求可能調用多個後端服務,而某一個後端服務也可能需要調用其他的服務,如果此時,其中一個服務當機或出現異常,導緻服務無法傳回結果,那麼調用這個服務的其他服務将進行等待狀态,如果此時又來了多個同樣的請求,那麼這些請求也将處于等待阻塞的狀态,由于伺服器支援的線程和并發的數量有限,請求一直阻塞将導緻伺服器資源耗盡,進而導緻所有其它服務都不可用,形成雪崩效應。
那麼Hystix的出現有什麼作用呢?它用來解決雪崩問題的手段有兩個:
1、線程隔離
2、服務熔斷
目前Hystix也已經不在更新,逐漸被resilience4j和sentinel代替。
線程隔離,服務降級
1、服務降級
優先保證核心服務,而非核心服務不可用或弱可用。
觸發Hystix服務降級的情況:
- 線程池已滿
- 請求逾時
2、線程隔離(限流)
Hystrix為每個依賴服務調用配置設定一個小的線程池,如果線程池已滿調用将被立即拒絕,預設不采用排隊.加速失敗判定時間。這樣一來,如果線程池已滿,或者請求逾時,則會對該請求進行降級處理。
服務降級雖然會導緻請求失敗,但是不會導緻阻塞,而且最多會影響這個依賴服務對應的線程池中的資源,對其它服務沒有響應。
3、實踐
在上一篇文章項目的基礎上服務調用-Ribbon,我們做以下配置:
(1)在消費者子產品引入依賴(也可以在服務提供者服務進行hystrix處理)
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
(2)開啟熔斷
啟動類中加上注解@EnableCircuitBreaker
@SpringBootApplication
@EnableDiscoveryClient
@EnableCircuitBreaker
public class ConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class,args);
}
}
Spring提供了一個組合注解:@SpringCloudApplication,用來替換 上面的三個注解。
@SpringCloudApplication
public class ConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class,args);
}
}
(3)編寫降級邏輯
在controller中,添加降級邏輯
@RestController
public class TestServer {
// private static final String ITEMSERVER_URL="http://localhost:8002";
private static final String ITEMSERVER_URL="http://ITEM-SERVER";
@Autowired
private RestTemplate restTemplate;
@GetMapping("/test")
@HystrixCommand(fallbackMethod = "queryUserByIdFallBack" @HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds", value = "3000")) //用來聲明一個降級邏輯的方法,出現異常或請求逾時都會進行降級處理
public CommonResult<Integer> test(){
return restTemplate.getForObject(ITEMSERVER_URL+"/test",CommonResult.class);
}
public CommonResult<Integer> queryUserByIdFallBack(){
return new CommonResult<>(500,"請求繁忙,請稍後再試!");
}
}
在接口方法上面添加一個注解@HystrixCommand,用來聲明一個降級邏輯的方法。熔斷的降級邏輯方法必須跟正常邏輯方法保證:相同的參數清單和傳回值聲明。
我們重新開機服務,當item-server提供正常服務時,通路與之前一緻,但是我們突然關掉item-server 會發現頁面傳回了降級處理資訊(降級處理的線程是hystrix新起的一個 線程)。
上面隻是示範了一個方法,如果有多個方法需要進行降級處理,難道我們需要一個個的加上一個降級方法嗎?答案當然不是的,我們可以将我們的降級處理的注解加載類上,在類上制定一個全局熔斷的方法:
@RestController
@DefaultProperties(defaultFallback = "queryUserByIdFallBack") // 指定一個類的全局熔斷方法
public class TestServer {
// private static final String ITEMSERVER_URL="http://localhost:8002";
private static final String ITEMSERVER_URL="http://ITEM-SERVER";
@Autowired
private RestTemplate restTemplate;
@GetMapping("/test")
// @HystrixCommand(fallbackMethod = "queryUserByIdFallBack") //用來聲明一個降級邏輯的方法
@HystrixCommand //标記該方法需要熔斷
public CommonResult<Integer> test(){
return restTemplate.getForObject(ITEMSERVER_URL+"/test",CommonResult.class);
}
/**
* 熔斷方法
* 傳回值要和被熔斷的方法的傳回值一緻
* 熔斷方法不需要參數
* @return
*/
public CommonResult<Integer> queryUserByIdFallBack(){
return new CommonResult<>(500,"請求繁忙,請稍後再試!");
}
}
重新運作産生了和上面同樣的效果。
說明,如果方法上有特殊配置降級處理,則走方法上的 ,如果沒有則找defaultProperties全局的處理方式。
(4)設定逾時
在之前的案例中,請求在超過1秒後都會傳回錯誤資訊,這是因為Hystix的預設逾時時長為1,我們可以通過配置修改這個值:
1、我們可以通過
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds
來設定Hystrix逾時時間。該配置沒有提示。
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 6000 # 設定hystrix的逾時時間為6000ms
2、也可以通過在注解中添加屬性解決
服務熔斷
1、是什麼
熔斷機制的原理就像家裡的電路熔斷器,如果電路發生短路能力可熔斷電路。在分布式系統中應用後,服務調用方可以自己進行判斷某些服務反應慢或者存在大量逾時的情況時,能夠主動熔斷,防止整個系統被拖垮。
Hystrix可以實作彈性容錯,當情況好轉之後,可以自動重連。通過熔斷的方式可以将厚度請求直接拒絕掉,一段時間之後允許部分請求通過,如果調用成功則回到電路閉合狀态,否則繼續斷開。
熔斷狀态機3個狀态:
- Closed:關閉狀态,所有請求都正常通路。
- Open:打開狀态,所有請求都會被降級。Hystix會對請求情況計數,當一定時間内失敗請求百分比達到門檻值,則觸發熔斷,斷路器會完全打開。預設失敗比例的門檻值是50%,請求次數最少不低于20次。
- Half Open:半開狀态,open狀态不是永久的,打開後會進入休眠時間(預設是5S)。随後斷路器會自動進入半開狀态。此時會釋放部分請求通過,若這些請求都是健康的,則會完全關閉斷路器,否則繼續保持打開,再次進行休眠計時。
2、實踐
我們在提供者子產品item-server的controller中加入邏輯:
@RestController
public class TestServer {
@Value("${server.port}")
private String serverPort;
@GetMapping("/test/{id}")
public CommonResult test(@PathVariable("id")Long id){
if (id == 1) {
throw new RuntimeException("太忙了");
}
Integer a=1;
return new CommonResult(200,"查詢成功"+serverPort,a) ;
}
}
這樣隻要參數為1就一定失敗,準備兩個請求接口:
http://localhost:9002/test/1
http://localhost:9002/test/2
當我們瘋狂通路id為1的請求時(超過20次),就會觸發熔斷。斷路器會斷開,一切請求都會被降級處理。
此時你通路id為2的請求,會發現傳回的也是失敗,而且失敗時間很短,隻有幾秒左右:
預設的熔斷觸發要求較高,休眠時間窗較短,為了測試友善,我們可以通過配置修改熔斷政策,可以在配置檔案中進行配置,也可以在注解 中寫入參數屬性:
circuitBreaker.requestVolumeThreshold=10
circuitBreaker.sleepWindowInMilliseconds=10000
circuitBreaker.errorThresholdPercentage=50
- requestVolumeThreshold:觸發熔斷的最小請求次數,預設20
- errorThresholdPercentage:觸發熔斷的失敗請求最小占比,預設50%
- sleepWindowInMilliseconds:休眠時長,預設是5000毫秒
// 熔斷測試
@HystrixCommand(fallbackMethod = "paymentCircuitBreaker_fallback",commandProperties = {
@HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds", value = "3000"),
@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold",value = "10"),
@HystrixProperty(name = "circuitBreaker.enabled",value = "true"),
@HystrixProperty(name= "circuitBreaker.sleepWindowInMilliseconds",value = "10000"),
@HystrixProperty(name="circuitBreaker.errorThresholdPercentage",value = "60")
})
注解中相關參數說明: