参考
首先当然要先看下官方文档知道大概原理,然后再细看
构建demo走源码
在这里参考官网demo好了
pom中引入:
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-core</artifactId>
<version>1.7.2</version>
</dependency>
main函数
package com.vava.sentinel;
import java.util.ArrayList;
import java.util.List;
import com.alibaba.csp.sentinel.Entry;
import com.alibaba.csp.sentinel.SphU;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
/**
* @author Steve
* Created on 2020-07
*/
public class Demo {
public static void main(String[] args) throws InterruptedException {
initFlowRules();
while (true) {
Thread.sleep(10);
Entry entry = null;
try {
entry = SphU.entry("HelloWorld");
/*您的业务逻辑 - 开始*/
System.out.println("hello world");
/*您的业务逻辑 - 结束*/
} catch (BlockException e1) {
/*流控逻辑处理 - 开始*/
System.out.println("block!");
/*流控逻辑处理 - 结束*/
} finally {
if (entry != null) {
entry.exit();
}
}
}
}
private static void initFlowRules(){
List<FlowRule> rules = new ArrayList<>();
FlowRule rule = new FlowRule();
rule.setResource("HelloWorld");
rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
// Set limit QPS to 20.
rule.setCount(20);
rules.add(rule);
FlowRuleManager.loadRules(rules);
}
}
可以看到输出:
有block的,有helloworld的
开始提问题看源码
自定义的rule,写在哪了?
从demo中看,是由FlowRuleManager把规则load进来的:
点进去看,应该是注册了一堆listener然后告知他们配置有更新
看不到啥有用信息,先放一放,看下真正跑起来的时候是如何加载规则的
如何加载规则?
demo中是通过新建
entry = SphU.entry(“HelloWorld”);
点进去:
对资源名称“HelloWorld”包装了一下,然后跳进这个entry函数
找一下处理链(责任链模式),这里是DefaultProcessorSlotChain
然后chain.entry再跳进去:
一大段注释说明用context的名字而不是resource的名字作为key去查规则。
在这个最简单的demo里,没有context只有resource。所以以resource的名字作为key去查。发现也是null
然后创建了一个DefaultNode,然后把这个新的node放进去map里了。
然后走到最后一行,最后调了SPI了,会在每个SPI的责任链里做不同的处理。
从官网我们得知,这个责任链的顺序是:
我们就跟着责任链一步一步的走
我比较关心的、比较核心的应该是监控统计StatisticSlot 、流量控制FlowSlot,我们可以重点看下这两块的代码
StatisticSlot
addPassRequest(count) 看着比较可疑 跳进去来到一个ArrayMetric
这里的data就是官网介绍的高性能数组LeapArray
至于怎么高性能我们先leave it be吧,按照官网的说法就是用滑动窗口的方法来统计指标
这个获取当前窗口的算法的注释有点牛皮。。
先看下官网这个图:大概知道获取个window是什么意思:
假设我们设定好了窗口大小是1000ms,然后sampleCount=5,传入一个当前时间910,怎么知道当前的窗口是200-1000呢?当前时间是1001的时候,怎么知道窗口已经滑动到400-1200呢?
我们做一个假设,当前时间永远要在最后一格里,想象这个当前时间带着整个窗口往右移动:(910在最后一格子,1001也在最后一个格子)
抽象一下,我们需要根据输入: 窗口大小:1000ms, sampleCount:5 , 当前时间:910;输出:windowStart=0。这个算法怎么写!这个静下心来想想应该不难对吧。假设让你写你会怎么写?
每个采样的格子大小是:1000ms/5=200ms。
910/200200=4200=800(当前时间所在格子的左区间)
1001/200200=5200=1000(当前时间所在格子的左区间)
然后根据假设,我们永远认为当前格子在最后一个格子,所以800-4200=0;1000-4200=200,这就算出了当前窗口的左区间。
函数的入参是当前时间,出参是WindowWrap
算法肯定跟我们上面说的类似。
int idx = calculateTimeIdx(timeMills)
当前时间/采用间隔 910/200=4, 取模4%5=4,idx=4
windowStart = 910-910%200=910-110=800
然后根据之前这个槽里的时间 做一下比较 决定是否要挪动窗口 具体原理跟我们上面的算法很类似,可以看那个漂亮的注释理解一下:
如果发现槽是空,则直接新建一个东西放进去
如果发现槽不空,里面的东西的windowStart跟算出来的windowStart相等,说明还在这个时间间隔里,直接返回就行
如果发现槽里的东西过期了,那就需要更新一把,拖着window走。
FlowSlot
上来就跳进去checkFlow,检查有没有超过限流规则
ruleProvider,是一个函数,输入String(resourceName),输出 规则列表 Collection<FlowRule>
然后逐条检查canPassCheck,一旦有没过检查的就抛出异常。
这么一取就取出来了
单机模式下一直跳到这个canPass