上篇文章:
Spring Cloud系列— Alibaba Sentinel限流https://blog.csdn.net/sniper_fandc/article/details/149944260?fromshare=blogdetail&sharetype=blogdetail&sharerId=149944260&sharerefer=PC&sharesource=sniper_fandc&sharefrom=from_link
目錄
1 熔斷策略
1.1 狀態機
1.2 慢調用比例(SLOW_REQUEST_RATIO)
1.3 異常比例(ERROR_RATIO)
1.4 異常數(ERROR_COUNT)
2 降級
2.1 捕獲異常
2.2 FallbackFactory
1 熔斷策略
????????當微服務系統中存在某個服務或接口故障,由于調用鏈路比較復雜,某個接口的故障可能就會導致整個系統不可用。因此需要對故障的接口或服務進行熔斷降級,熔斷降級主要是對弱依賴服務(不是系統運行的必須服務組件),常見三種策略:
1.1 狀態機
????????熔斷是由斷路機(熔斷器)統計服務調用的慢調用比例、異常比例和異常數,如果超過閾值則進行熔斷,攔截請求該服務的請求;否則放行請求該服務的請求。斷路機是基于狀態機來完成工作的:
????????狀態機有三種狀態:OPEN、HALF_OPEN和CLOSED。
????????CLOSED:所有請求都會通過斷路機,通過的過程中統計慢調用比例、異常比例和異常數,如果失敗率高于閾值就會轉化為OPEN狀態。
????????OPEN:所有請求都會被斷路機攔截,即服務被熔斷。每隔一段時間轉化為HALF_OPEN狀態,并將一定的請求放入斷路機。
????????HALF_OPEN:OPEN狀態每隔一段時間轉化為HALF_OPEN狀態,并根據放入的請求統計慢調用比例、異常比例和異常數,如果失敗率高于閾值,則此時服務還得繼續熔斷,因此再次轉化為OPEN狀態;如果失敗率低于閾值,則此時服務被視為可以正常訪問,不需要進行熔斷,因此轉化為CLOSED狀態。
1.2 慢調用比例(SLOW_REQUEST_RATIO)
????????在統計時長內,請求數超過最小請求數,且慢調用(請求響應時間超過最大RT(最大響應時間))比例超過比例閾值,就進行熔斷,暫停該資源一定時間(熔斷時長)的訪問。
????????假設order-service服務調用product-service服務的接口,如果product-service服務的接口出現響應速度慢的情況,則會被認為是慢調用請求:
????@RequestMapping("/{productId}")public ProductInfo getProductById(@PathVariable("productId") Integer productId){try {long millis = new Random().nextInt(20)+50;Thread.sleep(millis);//模擬慢調用System.out.println("收到請求,Id:"+productId);return productService.selectProductById(productId);} catch (InterruptedException e) {throw new RuntimeException(e);}}
????????將調用方的調用方法定義為資源,便于設置熔斷規則:
????@SentinelResource("selectOrderById")public OrderInfo selectOrderById(Integer orderId) {OrderInfo orderInfo = orderMapper.selectOrderById(orderId);//OpenFeign遠程調用方式ProductInfo productInfo = productInterface.getProductById(orderInfo.getProductId());orderInfo.setProductInfo(productInfo);return orderInfo;}
????????針對資源設置熔斷規則,10s內如果請求數超過5次,并且慢調用(響應時間超過50ms)比例超過0.5,則熔斷5s:
????????當重啟服務后,快速手動多次發送請求,如果發送請求超過5次以上,就會出現熔斷現象:
1.3 異常比例(ERROR_RATIO)
????????在統計時長內,請求數超過最小請求數,且異常比例超過比例閾值,就進行熔斷,暫停該資源一定時間(熔斷時長)的訪問。
????????首先在被調用接口中分別設置慢調用和異常兩種情況,由于(這里用到了服務降級,會在下面進行闡述)服務降級會針對異常也生效,因此想要觀察熔斷,就需要先調用慢調用接口(沒有設置慢調用的熔斷策略,因此不會熔斷)會正常通行,再多次調用異常接口觸發熔斷,最后再調用慢調用接口也會被攔截:
????@RequestMapping("/{productId}")public ProductInfo getProductById(@PathVariable("productId") Integer productId) {if (productId == 1001) {//模擬慢響應try {Thread.sleep(60);} catch (InterruptedException e) {throw new RuntimeException(e);}} else if (productId == 1002) {// 模擬異常throw new RuntimeException("發生異常");}return productService.selectProductById(productId);}
????????設置熔斷策略為異常比例,10s請求數大于5次,異常比例超過50%就觸發5s熔斷:
????????調用order/1接口,最終會遠程調用product/1001接口,不會觸發熔斷:
????????調用order/2接口,最終會遠程調用product/1002接口,多次調用會觸發熔斷(異常比例超過50%):
????????在觸發熔斷后,調用order/1接口,可以發現productInfo為NULL,說明觸發熔斷,并且服務降級:
1.4 異常數(ERROR_COUNT)
????????在統計時長內,請求數超過最小請求數,且發生異常的數量超過設置的異常數,就進行熔斷,暫停該資源一定時間(熔斷時長)的訪問。
????????異常數與異常比例類似,只不過熔斷判斷標準變為超過數字而不是比例。
2 降級
????????上述慢調用熔斷時,用戶會直接看到500內部異常這個響應,這對用戶體驗并不好。應該針對失敗返回用戶友好性的提示或默認結果,這就是降級。
????????實現服務降級有兩種方式:
????????1.捕獲異常,根據異常邏輯來進行降級。適用于通用場景。
????????2.FallbackFactory,適合遠程調用場景。
2.1 捕獲異常
????????由于訪問的是/order/{orderId}接口,因此可以在調用時發生異常后返回一個NULL對象:
????@SentinelResource("order-param")@RequestMapping("/{orderId}")public OrderInfo getOrderById(@PathVariable("orderId") Integer orderId) {try {OrderInfo orderInfo = orderService.selectOrderById(orderId);return orderInfo;}catch (UndeclaredThrowableException e){System.out.println("獲取訂單發生異常,exception:"+e);;return new OrderInfo();}}
????????此時在發生熔斷,就不會直接在頁面顯示500內部異常,而是一個NULL對象:
2.2 FallbackFactory
????????FallbackFactory是微服務架構中用于服務降級的接口,可以根據遠程服務調用失敗或異常信息,提供一個降級處理實例,該實例來代替原服務被調用。
????????首先需要創建這個降級處理實例,該實例實現FallbackFactory并接受ProductInterface類型的泛型參數:
@Slf4jpublic class ProductFallbackFactory implements FallbackFactory<ProductInterface> {@Overridepublic ProductInterface create(Throwable cause) {return new ProductInterface() {//返回服務降級實例@Overridepublic ProductInfo getProductById(Integer productId) {log.error("查詢商品信息異常");return new ProductInfo();}@Overridepublic String param1(Integer productId){return "發生錯誤";}@Overridepublic String param2(Integer productId,String name){return "發生錯誤";}@Overridepublic String object(ProductInfo productInfo){return "發生錯誤";}@Overridepublic String json(ProductInfo productInfo){return "發生錯誤";}};}}
????????ProductInterface類型是OpenFeign遠程調用的客戶端(抽取),需要注意設置fallbackFactory來和服務降級實例關聯:
@FeignClient(value = "product-service",path = "/product", fallbackFactory = ProductFallbackFactory.class)public interface ProductInterface {@RequestMapping("/{productId}")ProductInfo getProductById(@PathVariable("productId") Integer productId);@RequestMapping("/param1")String param1(@RequestParam("productId") Integer productId);@RequestMapping("/param2")String param2(@RequestParam("productId")Integer productId,@RequestParam("name")String name);@RequestMapping("/object")String object(@SpringQueryMap ProductInfo productInfo);@RequestMapping("/json")String json(@RequestBody ProductInfo productInfo);}
????????然后還需要定義ProductFallbackFactory的Bean,由于該DefaultFeignConfiguration需要多個遠程調用的接口使用,因此不添加五大注解交給Spring來管理,而是由調用方自行掃描注冊該Bean:
//創建ProductFallbackFactory的Beanpublic class DefaultFeignConfiguration {@Beanpublic ProductFallbackFactory productFallbackFactory() {return new ProductFallbackFactory();}}
????????調用方的啟動類上需要配置OpenFeign客戶端和FallbackFactory管理的服務降級實例的路徑:
@EnableFeignClients(basePackages = {"com.demo.product.api2"},defaultConfiguration = DefaultFeignConfiguration.class)
????????最后在配置文件中開啟OpenFeign的Sentinel的功能:
feign:sentinel:enabled: true # 開啟feign對sentinel的支持
????????重啟服務,觀察服務降級:
????????可以發現,開啟OpenFeign的Sentinel功能后,遠程調用的接口也出現在簇點鏈路中,針對遠程調用設置熔斷降級:
????????此時在多次慢調用出發熔斷后,不再是直接的500內部異常,而是遠程調用的服務降級。并且該服務降級返回了NULL的productInfo對象(服務降級實例),并不是像異常處理那樣處理粒度很粗。
????????注意:FallbackFactory不僅對熔斷生效,對限流也生效,即如果觸發限流,頁面不再顯示Blocked by Sentinel,而是返回服務降級實例。并且,服務降級還會對異常生效,即只要程序發生異常,就會走服務降級。因此發生服務降級現象,不一定是因為熔斷,也可能是因為限流或異常。
下篇文章:
Spring Cloud系列—Alibaba Sentinel授權與規則管理及推送https://blog.csdn.net/sniper_fandc/article/details/149945898?fromshare=blogdetail&sharetype=blogdetail&sharerId=149945898&sharerefer=PC&sharesource=sniper_fandc&sharefrom=from_link