使用Sentinel實現熔斷降級


降級策略

我們通常用以下幾種方式來衡量資源是否處於穩定的狀態:

  • 平均響應時間 (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");
    }



}
View Code

控制器:

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;
    }


}
View Code

可以看到,在使用getUserByRTDegradeRule規則時,第16個輸出為block user,即進入了超時熔斷。

 

項目源碼

Sentinel.zip


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM