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

ArrayMetric#details

public List<MetricNode> details() { List<MetricNode> details = new ArrayList<MetricNode>(); //调用BucketLeapArray data.currentWindow(); //列出统计结果 List<WindowWrap<MetricBucket>> list = data.list(); for (WindowWrap<MetricBucket> window : list) { if (window == null) { continue; } //对统计结果进行封装 MetricNode node = new MetricNode(); //代表一秒内被流量控制的请求数量 node.setBlockQps(window.value().block()); //则是一秒内业务本身异常的总和 node.setExceptionQps(window.value().exception()); // 代表一秒内到来到的请求 node.setPassQps(window.value().pass()); //代表一秒内成功处理完的请求; long successQps = window.value().success(); node.setSuccessQps(successQps); //代表一秒内该资源的平均响应时间 if (successQps != 0) { node.setRt(window.value().rt() / successQps); } else { node.setRt(window.value().rt()); } //设置统计窗口的开始时间 node.setTimestamp(window.windowStart()); node.setOccupiedPassQps(window.value().occupiedPass()); details.add(node); } return details; }

这个方法首先会调用dat.currentWindow()设置当前时间窗口到窗口列表里去。然后调用data.list()列出所有的窗口数据,然后遍历不为空的窗口数据封装成MetricNode返回。

data是BucketLeapArray的实例,BucketLeapArray继承了LeapArray,主要的统计都是在LeapArray中进行的,所以我们直接看看LeapArray的currentWindow方法。

LeapArray#currentWindow

public WindowWrap<T> currentWindow(long timeMillis) { if (timeMillis < 0) { return null; } //通过当前时间判断属于哪个窗口 int idx = calculateTimeIdx(timeMillis); //计算出窗口开始时间 // Calculate current bucket start time. long windowStart = calculateWindowStart(timeMillis); while (true) { //获取数组里的老数据 WindowWrap<T> old = array.get(idx); if (old == null) { WindowWrap<T> window = new WindowWrap<T>(windowLengthInMs, windowStart, newEmptyBucket(timeMillis)); if (array.compareAndSet(idx, null, window)) { // Successfully updated, return the created bucket. return window; } else { // Contention failed, the thread will yield its time slice to wait for bucket available. Thread.yield(); } // 如果对应时间窗口的开始时间与计算得到的开始时间一样 // 那么代表当前即是我们要找的窗口对象,直接返回 } else if (windowStart == old.windowStart()) { return old; } else if (windowStart > old.windowStart()) { //如果当前的开始时间小于原开始时间,那么就更新到新的开始时间 if (updateLock.tryLock()) { try { // Successfully get the update lock, now we reset the bucket. return resetWindowTo(old, windowStart); } finally { updateLock.unlock(); } } else { // Contention failed, the thread will yield its time slice to wait for bucket available. Thread.yield(); } } else if (windowStart < old.windowStart()) { //一般来说不会走到这里 // Should not go through here, as the provided time is already behind. return new WindowWrap<T>(windowLengthInMs, windowStart, newEmptyBucket(timeMillis)); } } }

这个方法里首先会传入一个timeMillis是当前的时间戳。然后调用calculateTimeIdx

private int calculateTimeIdx(/*@Valid*/ long timeMillis) { //计算当前时间能够落在array的那个节点上 long timeId = timeMillis / windowLengthInMs; // Calculate current index so we can map the timestamp to the leap array. return (int)(timeId % array.length()); }

calculateTimeIdx方法用当前的时间戳除以每个窗口的大小,再和array数据取模。array数据是一个容量为60的数组,代表被统计的60秒分割的60个小窗口。

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

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