sentinel接入指南

写在前面

主要介绍sentinel的限流熔断如何使用,如何与api网关进行整合,以及其dashboard的使用等;


sentinel 地址 https://github.com/alibaba/Sentinel


sentinel dashboard地址 https://github.com/alibaba/Sentinel/tree/master/sentinel-dashboard


sentinel wiki地址 https://github.com/alibaba/Sentinel/wiki


littlehow-sentinel接入指南地址

sentinel在普通项目中使用

  • 需要引入其依赖包(此处以version=1.6.1举例)
    <properties>
        <sentinel.version>1.6.1</sentinel.version>
    </properties>
    
    <dependency>
        <groupId>com.alibaba.csp</groupId>
        <artifactId>sentinel-core</artifactId>
        <version>${sentinel.version}</version>
    </dependency>

限流使用

限流详细文档地址:https://github.com/alibaba/Sentinel/wiki/%E6%B5%81%E9%87%8F%E6%8E%A7%E5%88%B6

下面举个简单的使用示例:

  • 限流简单配置代码
/**
     * 基于QPS的资源限流
     * @param name   -- 资源名称
     * @param count  -- 数量
     */
    public static void flowRule(String name, int count) {
        //限流规则,可以多个flow rule,该规则支持QPS和并发线程数的限流
        //FlowRuleManager.getRules()可以获取到已经设置的限流规则
        List<FlowRule> rules = new ArrayList<>();
        FlowRule rule = new FlowRule();
        //设置资源名称,sentinel限流都是以资源为单位进行
        rule.setResource(name);
        //使用QPS限流
        rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
        //QPS达到的数量阈值
        rule.setCount(count);
        rules.add(rule);
        //重新加载限流规则,此处将覆盖原有的限流,所以如果想要不覆盖
        //请使用FlowRuleManager.getRules()获取到的加入到rules中
        FlowRuleManager.loadRules(rules);
    }
  • 限流使用示例如下:
    public static void main(String[] args) {
        //执行限流
        flow("sayHello", 3);
    }

    public static void flow(String resourceName, int count) {
        // 配置规则,每秒可以过3个hello world的请求
        flowRule(resourceName, count);

        while (true) {
            for (int i = 0; i <= count; i++) {
                //entry这里是关键逻辑,它会基于滑动时间窗口来进行限流统计;
                //所以此处才是限流的关键
                try (Entry entry = SphU.entry(resourceName)) {
                    sayHello("" + (int)entry.getCurNode().successQps());
                } catch (BlockException ex) {
                    // 处理被流控的逻辑
                    System.out.println("blocked!");
                }
            }
            sleepSeconds(1);
        }
    }

    //需要限流的方法
    public static void sayHello(String info) {
        System.out.println("hello world! before success count = " + info);
    }

    //按秒睡眠
    private static void sleepSeconds(long time) {
        try {
            TimeUnit.SECONDS.sleep(time);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

运行上面的代码后,输出结果如下(从结果可以发现,程序以恒定每秒4次来调用被限流的方法,而配置中方法能支持的QPS为3次,所以每次调用都会有一次被保护,从而走限流分支,在限流分支中可以写自己的业务逻辑):

hello world! before success count = 0

hello world! before success count = 1

hello world! before success count = 2

blocked!

hello world! before success count = 0

hello world! before success count = 1

hello world! before success count = 2

blocked!

hello world! before success count = 0

hello world! before success count = 1

hello world! before success count = 2

熔断使用

  • 熔断规则配置代码:
/**
     * 熔断降级规则配置
     * @param name   -- 资源名称
     * @param count  -- 数量
     */
    public static void degradeRule(String name, int count) {
        //降级规则,可以多个degradeRule rule
        //DegradeRuleManager.getRules()可以获取到已经设置的降级规则
        List<DegradeRule> rules = new ArrayList<>();
        DegradeRule rule = new DegradeRule();
        //设置资源名称,sentinel降级都是以资源为单位进行
        rule.setResource(name);
        //使用异常统计降级,分钟统计,滑动时间窗口
        rule.setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_COUNT);
        //异常数达到的数量阈值
        rule.setCount(count);
        //秒级时间窗口,该值必须有且必须大于零,否则降级将无法生效
        rule.setTimeWindow(10);
        rules.add(rule);
        //重新加载限流规则,此处将覆盖原有的限流,所以如果想要不覆盖
        //请使用DegradeRuleManager.getRules()获取到的加入到rules中
        DegradeRuleManager.loadRules(rules);
    }
  • 熔断测试调用代码:

限流代码中的main调用degrade方法即可

public static void degrade(String resourceName, int count) {
        // 配置熔断规则,发生count个异常即将进行熔断
        degradeRule(resourceName, count * 2);
        int sum = 0;
        while (true) {
            for (int i = 0; i <= count; i++) {
                //所以此处才是限流的关键
                Entry entry = null;
                try {
                    entry = SphU.entry(resourceName);
                    sayHello(++sum + "");
                } catch (BlockException e) {
                    System.out.println("被降级了:" + e);
                } catch (Exception ex) {
                    // 处理业务异常,调用tracer才能统计异常数
                    Tracer.trace(ex);
                } finally {
                    if (entry != null) {
                        entry.exit();
                    }
                }
            }
            System.out.println("===============分割线================");
            sleepSeconds(1);
        }
    }
    
    public static void sayHello(String info) {
        System.out.println("hello world! before success count = " + info);
        throw new RuntimeException("test degrade");
    }

运行代码后获得结果如下(在一分钟的滑动时间窗口下,异常超过6次则进行降级处理,直到时间窗口滑到小于6次异常才会继续被调用):

hello world! before success count = 1

hello world! before success count = 2

hello world! before success count = 3

hello world! before success count = 4

=分割线==

hello world! before success count = 5

hello world! before success count = 6

被降级了:com.alibaba.csp.sentinel.slots.block.degrade.DegradeException

被降级了:com.alibaba.csp.sentinel.slots.block.degrade.DegradeException

=分割线==

被降级了:com.alibaba.csp.sentinel.slots.block.degrade.DegradeException

被降级了:com.alibaba.csp.sentinel.slots.block.degrade.DegradeException

被降级了:com.alibaba.csp.sentinel.slots.block.degrade.DegradeException

被降级了:com.alibaba.csp.sentinel.slots.block.degrade.DegradeException

使用sentinel提供的aop集成限流降级

注解文档地址:https://github.com/alibaba/Sentinel/wiki/%E6%B3%A8%E8%A7%A3%E6%94%AF%E6%8C%81

需要依赖springaop,使用springboot项目集成即可,前面的maven依赖不变,新增maven依赖如下:

    <dependency>
        <groupId>com.alibaba.csp</groupId>
        <artifactId>sentinel-annotation-aspectj</artifactId>
        <version>${sentinel.version}</version>
    </dependency>
  • 简单展示使用注解方式来进行限流降级(可以动态调整限流方案,也可以全局设置降级和限流处理类等)

  • 待代用的测试服务:

其中fallback函数必须和原函数参数一致,handler函数可以增加一个异常的参数,其他参数保持不变。

@Service
public class SentinelAnnotationService {

    @SentinelResource(value = "helloService", blockHandler = "myHandler", fallback = "myFallback")
    public String sayHello(String name) {
        if (System.currentTimeMillis() % 2 == 0) {
            throw new RuntimeException("test");
        }
        return "return hello " + name;
    }

    public String myHandler(String name, BlockException ex) {
        return "return handler " + name;
    }

    public String myFallback(String name) {
        return "return fallback " + name;
    }
}
  • springboot调用类
@SpringBootApplication
public class Run {
    public static void main(String[] args) {
        ApplicationContext context = SpringApplication.run(Run.class, args);
        test(context.getBean(SentinelAnnotationService.class));
    }

    /**
     * 每10秒访问5次以上次
     */
    private static void test(SentinelAnnotationService testService) {
        //限流规则生成中
        AddRule.flowRule("helloService", 3);
        int sum = 10, total = sum;
        while (sum-- > 0) {
            for (int i = 1; i <= 5; i++) {
                String result = testService.sayHello("littlehow" + i);
                System.out.println("the " + (total - sum) + " result---->" + result);
            }
            sleepSeconds(10);
        }
    }

    //按秒睡眠
    private static void sleepSeconds(long time) {
        try {
            TimeUnit.SECONDS.sleep(time);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * 这里很重要,这里是sentinel实现注解限流降级最关键的地方
     * @return
     */
    @Bean
    public SentinelResourceAspect sentinelResourceAspect() {
        return new SentinelResourceAspect();
    }
}
  • 执行后结果如下:

执行结果的前三行输出是不确定的,因为异常是随机抛出的,概率为50%,抛出异常后,将执行fallback,后两行的输出证明每秒限流3次效果达到了;

the 1 result---->return hello littlehow1

the 1 result---->return hello littlehow2

the 1 result---->return fallback littlehow3

the 1 result---->return handler littlehow4

the 1 result---->return handler littlehow5

sentinel与api网关的集成

与api网关集成文档地址:https://github.com/alibaba/Sentinel/wiki/%E4%B8%BB%E6%B5%81%E6%A1%86%E6%9E%B6%E7%9A%84%E9%80%82%E9%85%8D

sentinel与spring cloud gateway集成

因为当前springboot与springcloud版本为1.x,所以暂时集成不了spring cloud gateway;以后版本升级后可以考虑进行集成;

sentinel与zuul1.x的集成

待续…

sentinel与zuul2.x的集成

目前sentinel官方还没实现与zuul2.x集成

sentinel dashboard使用

dashboard的使用文档地址:https://github.com/alibaba/Sentinel/wiki/%E6%8E%A7%E5%88%B6%E5%8F%B0

sentinel规则管理

dashboard的生产实践地址:https://github.com/alibaba/Sentinel/wiki/%E5%9C%A8%E7%94%9F%E4%BA%A7%E7%8E%AF%E5%A2%83%E4%B8%AD%E4%BD%BF%E7%94%A8-Sentinel

结合当前项目管理规则

sentinel提供了基于zk、apollo等配置中心来管理规则,基于apollo的规则需要项目内部写入自己的namespace,所以基于sentinel的扩展,自定义规则管理;
tips:也可以直接基于RuleManage进行改变

自定义规则改变不需要添加任何其他依赖,核心是sentinel提供的datasource接口,实现如下:

  • 数据源公共处理类
@Slf4j
public abstract class CommonDataSource<T> extends AbstractDataSource<String, List<T>> {

    private String typeName;

    public CommonDataSource(RuleConverter<T> ruleConverter, String typeName) {
        super(ruleConverter);
        this.typeName = typeName;
    }

    @Override
    public void close() throws Exception {
        log.info("data source close");
    }

    /**
     * 修改规则
     * @throws Exception
     */
    public void updateRules() {
        String rules = null;
        try {
            //加载改变后的规则值
            rules = this.readSource();
            log.info("update rules {}, typeName={}", rules, typeName);
            //将改变写入RuleManage中
            this.getProperty().updateValue(this.parser.convert(rules));
        } catch (Exception e) {
            log.error("update rules {} fail, typeName={}", rules, typeName, e);
        }
    }
}
  • 公共规则转换器
abstract class RuleConverter<T> implements Converter<String, List<T>> {
    private static final List EMPTY_LIST = new ArrayList<>(0);

    /**
     * 判断是否为空
     * @param value
     */
    protected boolean isEmpty(String value) {
        return null == value || "".equals(value) || "0".equals(value);
    }

    /**
     * 创建flow规则
     * @param info
     * @return
     */
    protected List<FlowRule> createFlowRule(String info) {
        if (isEmpty(info)) {
            return EMPTY_LIST;
        }
        List<FlowRule> flowRules = new ArrayList<>();
        //解析出错将异常抛往上层,只简单配置resourceName,grade,count
        for (String s : getRulesInfo(info)) {
            String[] ruleInfo = getRuleInfo(s);
            FlowRule rule = new FlowRule(ruleInfo[0].trim())
                    .setGrade(Integer.parseInt(ruleInfo[1]))
                    .setCount(Double.parseDouble(ruleInfo[2]));
            flowRules.add(rule);
        }
        return flowRules;
    }

    protected List<DegradeRule> createDegradeRule(String info) {
        if (isEmpty(info)) {
            return EMPTY_LIST;
        }
        List<DegradeRule> degradeRules = new ArrayList<>();
        //解析出错将异常抛往上层,只简单配置resourceName,grade,count,timeWindow
        for (String s : getRulesInfo(info)) {
            String[] ruleInfo = getRuleInfo(s);
            DegradeRule rule = new DegradeRule(ruleInfo[0].trim())
                    .setGrade(Integer.parseInt(ruleInfo[1]))
                    .setCount(Double.parseDouble(ruleInfo[2]))
                    .setTimeWindow(Integer.parseInt(ruleInfo[3]));
            degradeRules.add(rule);
        }
        return degradeRules;
    }


    private String[] getRulesInfo(String info) {
        return info.trim().split("#");
    }

    private String[] getRuleInfo(String info) {
        return info.trim().split(",");
    }

}
  • 限流规则转换器
@Slf4j
public class FlowRuleConverter extends RuleConverter<FlowRule> {
    @Override
    public List<FlowRule> convert(String s) {
        log.info("update flow rule to {}", s);
        return createFlowRule(s);
    }

    public static FlowRuleConverter create() {
        return new FlowRuleConverter();
    }
}
  • 配置管理器
@Configuration
public class SentinelRuleConfig {
    private static final String RULE_FLOW_KEY = "flow";
    private static final String RULE_DEGRADE_KEY = "degrade";
    private Map<String, String> rulesConfig = new HashMap<>();
    private CommonDataSource<FlowRule> flowDataSource;
    private CommonDataSource<DegradeRule> degradeDataSource;

    public SentinelRuleConfig() {
        super();
        init();
    }

    /**
     * 多个规则用;隔开,一个规则内部用,隔开
     * 顺序为resourceName,grade,count
     * 如helloService,1,3;sayBye,0,5
     * grade参考如下
     * @see com.alibaba.csp.sentinel.slots.block.RuleConstant
     * @param rules
     */
    @Value("${rule.flow:helloService,1,3}")
    private void setFlowRules(String rules) {
        rulesConfig.put(RULE_FLOW_KEY, rules);
        flowDataSource.updateRules();
    }

    /**
     * 多个规则用;隔开,一个规则内部用,隔开
     * 顺序为resourceName,grade,count,timeWindow
     * 如helloService,1,3,10;sayBye,0,5,10
     * grade参考如下
     * @see com.alibaba.csp.sentinel.slots.block.RuleConstant
     * @param rules
     */
    @Value("${rule.degrade:}")
    private void setDegradeRules(String rules) {
        rulesConfig.put(RULE_DEGRADE_KEY, rules);
        degradeDataSource.updateRules();
    }


    public void init() {
        flowDataSource = new CommonDataSource<FlowRule>(FlowRuleConverter.create(), RULE_FLOW_KEY) {
            @Override
            public String readSource() {
                return rulesConfig.get(RULE_FLOW_KEY);
            }
        };
        //将新的数据源配置注入到ruleManager
        FlowRuleManager.register2Property(flowDataSource.getProperty());

        degradeDataSource = new CommonDataSource<DegradeRule>(DegradeRuleConverter.create(), RULE_DEGRADE_KEY) {
            @Override
            public String readSource() {
                return rulesConfig.get(RULE_DEGRADE_KEY);
            }
        };
        DegradeRuleManager.register2Property(degradeDataSource.getProperty());
    }
}

apollo只需配置相应的rule.flow和rule.degrade的值即可

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值