降級策略
我們通常用以下幾種方式來衡量資源是否處於穩定的狀態:
- 平均響應時間 (
DEGRADE_GRADE_RT
):當資源的平均響應時間超過閾值(DegradeRule
中的count
,以 ms 為單位)之后,資源進入准降級狀態。接下來如果持續進入 5 個請求,它們的 RT 都持續超過這個閾值,那么在接下的時間窗口(DegradeRule
中的timeWindow
,以 s 為單位)之內,對這個方法的調用都會自動地返回(拋出DegradeException
)。注意 Sentinel 默認統計的 RT 上限是 4900 ms,超出此閾值的都會算作 4900 ms,若需要變更此上限可以通過啟動配置項-Dcsp.sentinel.statistic.max.rt=xxx
來配置。 - 異常比例 (
DEGRADE_GRADE_EXCEPTION_RATIO
):當資源的每秒異常總數占通過量的比值超過閾值(DegradeRule
中的count
)之后,資源進入降級狀態,即在接下的時間窗口(DegradeRule
中的timeWindow
,以 s 為單位)之內,對這個方法的調用都會自動地返回。異常比率的閾值范圍是[0.0, 1.0]
,代表 0% - 100%。 - 異常數 (
DEGRADE_GRADE_EXCEPTION_COUNT
):當資源近 1 分鍾的異常數目超過閾值之后會進行熔斷。
注意:異常降級僅針對業務異常,對 Sentinel 限流降級本身的異常(BlockException
)不生效。為了統計異常比例或異常數,需要通過 Tracer.trace(ex)
記錄業務異常。
針對這些規則,Sentinel中給出了響應的字段來設置:
Sentinel限流中最重要的處理鏈:
public class DefaultSlotChainBuilder implements SlotChainBuilder { @Override public ProcessorSlotChain build() { ProcessorSlotChain chain = new DefaultProcessorSlotChain(); chain.addLast(new NodeSelectorSlot()); chain.addLast(new ClusterBuilderSlot()); chain.addLast(new LogSlot()); chain.addLast(new StatisticSlot()); chain.addLast(new SystemSlot()); chain.addLast(new AuthoritySlot()); chain.addLast(new FlowSlot()); chain.addLast(new DegradeSlot()); return chain; } }
可以看到最后一個Slot,就是熔斷降級部分,他與限流是可以並存的。
實戰例子:
服務類:

package com.xin.sentinel.demo.service; import com.alibaba.csp.sentinel.Entry; import com.alibaba.csp.sentinel.SphU; import com.alibaba.csp.sentinel.annotation.SentinelResource; import com.alibaba.csp.sentinel.slots.block.BlockException; import com.alibaba.csp.sentinel.slots.block.RuleConstant; import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule; import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager; import com.alibaba.csp.sentinel.slots.block.flow.FlowRule; import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager; import com.xin.sentinel.demo.dao.DB; import com.xin.sentinel.demo.entity.User; import org.springframework.stereotype.Service; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.concurrent.TimeUnit; @Service public class RTUserService { public static final String USER_DEGRADERULE_RES = "userDegradeRuleResource"; public static final String USER_DEGRADERATIORULE_RES = "userDegradeRATIORuleResource"; public RTUserService(){ //initDegradeRTRule(); initDegradeRATIORule(); } private static void initDegradeRTRule() { List<DegradeRule> rules = new ArrayList<DegradeRule>(); DegradeRule rule = new DegradeRule(); rule.setResource(USER_DEGRADERULE_RES); // set threshold rt, 100 ms rule.setCount(100);//資源的平均響應時間超過閾值100 ms,進入降級 rule.setGrade(RuleConstant.DEGRADE_GRADE_RT);//平均響應時間 (DEGRADE_GRADE_RT) rule.setTimeWindow(3);//持續降級的時間窗口3秒 rules.add(rule); DegradeRuleManager.loadRules(rules); } private static void initDegradeRATIORule() { List<DegradeRule> rules = new ArrayList<DegradeRule>(); DegradeRule rule = new DegradeRule(); rule.setResource(USER_DEGRADERATIORULE_RES); // 當資源的每秒異常總數占通過量的比值超過閾值(DegradeRule 中的 count)49% 之后,資源進入降級狀態 rule.setCount(0.49); rule.setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_RATIO); rule.setTimeWindow(10); rules.add(rule); DegradeRuleManager.loadRules(rules); } /** * 通過 @SentinelResource 注解定義資源並配置 blockHandler 和 fallback 函數來進行限流之后的處理 * @param id * @return */ @SentinelResource(blockHandler = "blockHandlerForGetUser", fallback = "fallbackHandlerForGetUser") public User getUserByRTDegradeRule(int id) { Entry entry = null; try { entry = SphU.entry(USER_DEGRADERULE_RES); //第5個請求開始超過閥值100ms if (id>5){ TimeUnit.MILLISECONDS.sleep(150); } // 業務代碼 User user = new User(); user.setId(id); user.setName("user-" + id); DB.InsertUser(user); //長耗時的工作 return user; } catch (InterruptedException e) { e.printStackTrace(); } catch (BlockException e) { e.printStackTrace(); System.out.println(e+"[getUser] has been protected! id="+ id); return new User("block user"+id); } finally { if (entry != null) { entry.exit(); } } return null; } @SentinelResource(blockHandler = "blockHandlerForGetUser", fallback = "fallbackHandlerForGetUser") public User getUserByRATIODegradeRule(int id) { Entry entry = null; try { entry = SphU.entry(USER_DEGRADERATIORULE_RES); //第5個請求開始出現異常 if (id>5){ throw new RuntimeException("throw runtime "); } // 業務代碼 User user = new User(); user.setId(id); user.setName("user-" + id); DB.InsertUser(user); //長耗時的工作 return user; } catch (Exception e) { e.printStackTrace(); System.out.println(e+"[getUser] has been protected! id="+ id); } finally { if (entry != null) { entry.exit(); } } return new User("block user"+id); } // blockHandler 函數,原方法調用被限流/降級/系統保護的時候調用 public User blockHandlerForGetUser() { return new User("block user"); } public User fallbackHandlerForGetUser() { return new User("fallback user"); } }
控制器:

package com.xin.sentinel.demo.controller; import com.xin.sentinel.demo.entity.User; import com.xin.sentinel.demo.service.RTUserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.ResponseBody; import java.util.ArrayList; import java.util.List; @Controller public class RTDemo { @Autowired private RTUserService userService; @GetMapping("/getUserByRTDegradeRule") public @ResponseBody List<User> getUserByRTDegradeRule() throws InterruptedException { List<User> usersList = new ArrayList<User>(); for (int i=0;i<50;i++){ usersList.add(userService.getUserByRTDegradeRule(i)); } return usersList; } @GetMapping("/getUserByRATIODegradeRule") public @ResponseBody List<User> getUserByRATIODegradeRule() throws InterruptedException { List<User> usersList = new ArrayList<User>(); for (int i=0;i<50;i++){ usersList.add(userService.getUserByRATIODegradeRule(i)); } return usersList; } }
可以看到,在使用getUserByRTDegradeRule規則時,第16個輸出為block user,即進入了超時熔斷。
項目源碼