1.Sentinel源码分析—FlowRuleManager加载规则做了什么? (4)

举例:
例如当前timeMillis = 1567175708975
timeId = 1567175708975/1000 = 1567175708
timeId % array.length() = 1567175708%60 = 8
也就是说当前的时间窗口是第八个。

然后调用calculateWindowStart计算当前时间开始时间

protected long calculateWindowStart(/*@Valid*/ long timeMillis) { //用当前时间减去窗口大小,计算出窗口开始时间 return timeMillis - timeMillis % windowLengthInMs; }

接下来就是一个while循环:
在看while循环之前我们看一下array数组里面是什么样的对象
WindowWrap<T> window = new WindowWrap<T>(windowLengthInMs, windowStart, newEmptyBucket(timeMillis));
WindowWrap是一个时间窗口的包装对象,里面包含时间窗口的长度,这里是1000;窗口开始时间;窗口内的数据实体,是调用newEmptyBucket方法返回一个MetricBucket。

MetricBucket

public class MetricBucket { private final LongAdder[] counters; //默认4900 private volatile long minRt; public MetricBucket() { MetricEvent[] events = MetricEvent.values(); this.counters = new LongAdder[events.length]; for (MetricEvent event : events) { counters[event.ordinal()] = new LongAdder(); } //初始化minRt,默认是4900 initMinRt(); } ... }

MetricEvent是一个枚举类:

public enum MetricEvent { PASS, BLOCK, EXCEPTION, SUCCESS, RT, OCCUPIED_PASS }

也就是是MetricBucket为每个窗口通过一个内部数组counters统计了这个窗口内的所有数据。

接下来我们来讲一下while循环里所做的事情:

从array里获取bucket节点

如果节点已经存在,那么用CAS更新一个新的节点

如果节点是新的,那么直接返回

如果节点失效了,设置当前节点,清除所有失效节点

举例:

1. 如果array数据里面的bucket数据如下所示: B0 B1 B2 NULL B4 ||_______|_______|_______|_______|_______||___ 200 400 600 800 1000 1200 timestamp ^ time=888 正好当前时间所对应的槽位里面的数据是空的,那么就用CAS更新 2. 如果array里面已经有数据了,并且槽位里面的窗口开始时间和当前的开始时间相等,那么直接返回 B0 B1 B2 B3 B4 ||_______|_______|_______|_______|_______||___ 200 400 600 800 1000 1200 timestamp ^ time=888 3. 例如当前时间是1676,所对应窗口里面的数据的窗口开始时间小于当前的窗口开始时间,那么加上锁,然后设置槽位的窗口开始时间为当前窗口开始时间,并把槽位里面的数据重置 (old) B0 B1 B2 NULL B4 |_______||_______|_______|_______|_______|_______||___ ... 1200 1400 1600 1800 2000 2200 timestamp ^ time=1676

所以上面的array数组大概是这样:

array数组由一个个的WindowWrap实例组成,WindowWrap实例里面由MetricBucket进行数据统计。

然后继续回到ArrayMetric的details方法,讲完了上面的data.currentWindow(),现在再来讲data.list()

list方法最后也会调用到LeapArray的list方法中:
LeapArray#list

public List<WindowWrap<T>> list(long validTime) { int size = array.length(); List<WindowWrap<T>> result = new ArrayList<WindowWrap<T>>(size); for (int i = 0; i < size; i++) { WindowWrap<T> windowWrap = array.get(i); //如果windowWrap节点为空或者当前时间戳比windowWrap的窗口开始时间大超过60s,那么就跳过 //也就是说只要60s以内的数据 if (windowWrap == null || isWindowDeprecated(validTime, windowWrap)) { continue; } result.add(windowWrap); } return result; }

这个方法是用来把array里面都统计好的节点都找出来,并且是不为空,且是当前时间60秒内的数据。

最后Constants.ENTRY_NODE.metrics() 会返回所有符合条件的统计节点数据然后传入aggregate方法中,遍历为每个MetricNode节点设置Resource为TOTAL_IN_RESOURCE_NAME,封装好调用metricWriter.write进行写日志操作。

最后总结一下在初始化FlowRuleManager的时候做了什么:

FlowRuleManager在初始化的时候会调用静态代码块进行初始化

在静态代码块内调用ScheduledExecutorService线程池,每隔1秒调用一次MetricTimerListener的run方法

MetricTimerListener会调用Constants.ENTRY_NODE.metrics()进行定时的统计

调用StatisticNode进行统计,统计60秒内的数据,并将60秒的数据分割成60个小窗口

在设置当前窗口的时候如果里面没有数据直接设置,如果存在数据并且是最新的直接返回,如果是旧数据,那么reset原来的统计数据

每个小窗口里面的数据由MetricBucket进行封装

最后将统计好的数据通过metricWriter写入到log里去

FlowRuleManager加载规则

FlowRuleManager是调用loadRules进行规则加载的:

FlowRuleManager#loadRules

public static void loadRules(List<FlowRule> rules) { currentProperty.updateValue(rules); }

currentProperty这个实例是在FlowRuleManager是在静态代码块里面进行加载的,上面我们讲过,生成的是DynamicSentinelProperty的实例。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpzwzz.html