使用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

posted @ 2020-05-20 08:29  昕友软件开发  阅读(3092)  评论(0编辑  收藏  举报
欢迎访问我的开源项目:xyIM企业即时通讯