基于本地緩存的 fallback 降級機制
Hystrix 出現(xiàn)以下四種情況专钉,都會去調(diào)用 fallback 降級機制:
- 斷路器處于打開的狀態(tài)弦赖。
- 資源池已滿(線程池+隊列 / 信號量)。
- Hystrix 調(diào)用各種接口将谊,或者訪問外部依賴,比如 MySQL、Redis旗笔、Zookeeper、Kafka 等等拄踪,出現(xiàn)了任何異常的情況蝇恶。
- 訪問外部依賴的時候,訪問時間過長惶桐,報了 TimeoutException 異常撮弧。
兩種最經(jīng)典的降級機制
純內(nèi)存數(shù)據(jù)
在降級邏輯中,你可以在內(nèi)存中維護一個 ehcache姚糊,作為一個純內(nèi)存的基于 LRU 自動清理的緩存贿衍,讓數(shù)據(jù)放在緩存內(nèi)。如果說外部依賴有異常叛拷,fallback 這里直接嘗試從 ehcache 中獲取數(shù)據(jù)舌厨。默認值
fallback 降級邏輯中,也可以直接返回一個默認值忿薇。
在 HystrixCommand
裙椭,降級邏輯的書寫躏哩,是通過實現(xiàn) getFallback() 接口;而在 HystrixObservableCommand
中揉燃,則是實現(xiàn) resumeWithFallback() 方法扫尺。
現(xiàn)在,我們用一個簡單的栗子炊汤,來演示 fallback 降級是怎么做的正驻。
比如,有這么個場景抢腐。我們現(xiàn)在有個包含 brandId 的商品數(shù)據(jù)姑曙,假設正常的邏輯是這樣:拿到一個商品數(shù)據(jù),根據(jù) brandId 去調(diào)用品牌服務的接口迈倍,獲取品牌的最新名稱 brandName伤靠。
假如說,品牌服務接口掛掉了啼染,那么我們可以嘗試從本地內(nèi)存中宴合,獲取一份稍過期的數(shù)據(jù),先湊合著用迹鹅。
步驟一:本地緩存獲取數(shù)據(jù)
本地獲取品牌名稱的代碼大致如下卦洽。
/**
* 品牌名稱本地緩存
*
*/
public class BrandCache {
private static Map<Long, String> brandMap = new HashMap<>();
static {
brandMap.put(1L, "Nike");
}
/**
* brandId 獲取 brandName
* @param brandId 品牌id
* @return 品牌名
*/
public static String getBrandName(Long brandId) {
return brandMap.get(brandId);
}
步驟二:實現(xiàn) GetBrandNameCommand
在 GetBrandNameCommand 中,run() 方法的正常邏輯是去調(diào)用品牌服務的接口獲取到品牌名稱斜棚,如果調(diào)用失敗阀蒂,報錯了,那么就會去調(diào)用 fallback 降級機制打肝。
這里脂新,我們直接模擬接口調(diào)用報錯,給它拋出個異常粗梭。
而在 getFallback() 方法中争便,就是我們的降級邏輯,我們直接從本地的緩存中断医,獲取到品牌名稱的數(shù)據(jù)滞乙。
/**
* 獲取品牌名稱的command
*
*/
public class GetBrandNameCommand extends HystrixCommand<String> {
private Long brandId;
public GetBrandNameCommand(Long brandId) {
super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("BrandService"))
.andCommandKey(HystrixCommandKey.Factory.asKey("GetBrandNameCommand"))
.andCommandPropertiesDefaults(HystrixCommandProperties.Setter()
// 設置降級機制最大并發(fā)請求數(shù)
.withFallbackIsolationSemaphoreMaxConcurrentRequests(15)));
this.brandId = brandId;
}
@Override
protected String run() throws Exception {
// 這里正常的邏輯應該是去調(diào)用一個品牌服務的接口獲取名稱
// 如果調(diào)用失敗,報錯了鉴嗤,那么就會去調(diào)用fallback降級機制
// 這里我們直接模擬調(diào)用報錯斩启,拋出異常
throw new Exception();
}
@Override
protected String getFallback() {
return BrandCache.getBrandName(brandId);
}
}
FallbackIsolationSemaphoreMaxConcurrentRequests
用于設置 fallback 最大允許的并發(fā)請求量,默認值是 10醉锅,是通過 semaphore 信號量的機制去限流的兔簇。如果超出了這個最大值,那么直接 reject。
步驟三:CacheController 調(diào)用接口
在 CacheController 中垄琐,我們通過 productInfo 獲取 brandId边酒,然后創(chuàng)建 GetBrandNameCommand 并執(zhí)行,去嘗試獲取 brandName狸窘。這里執(zhí)行會報錯墩朦,因為我們在 run() 方法中直接拋出異常,Hystrix 就會去調(diào)用 getFallback() 方法走降級邏輯翻擒。
@Controller
public class CacheController {
@RequestMapping("/getProductInfo")
@ResponseBody
public String getProductInfo(Long productId) {
HystrixCommand<ProductInfo> getProductInfoCommand = new GetProductInfoCommand(productId);
ProductInfo productInfo = getProductInfoCommand.execute();
Long brandId = productInfo.getBrandId();
HystrixCommand<String> getBrandNameCommand = new GetBrandNameCommand(brandId);
// 執(zhí)行會拋異常報錯氓涣,然后走降級
String brandName = getBrandNameCommand.execute();
productInfo.setBrandName(brandName);
System.out.println(productInfo);
return "success";
}
}
關于降級邏輯的演示,基本上就結束了陋气。