ArrayMetric的类图如下所示,ArrayMetric是滑动窗口核心实现类,主要用于统计时间窗口的通过数、阻塞数、异常数,以及通过数、阻塞数、异常数的增加等,其包含了一个LeapArray<MetricBucket>类型的data成员变量;LeapArray可以看做一个时间窗口的数组,主要用于WindowWrap(时间窗口)的维护、当前时间对应的时间窗口的获取、当前时间窗口的开始时间的计算等;WindowWrap是时间窗口的具体实现类,内部维护了时间窗口的windowStart(开始时间)、窗口间隔(windowLengthInM)、指标桶(MetricBucket)等;MetricBucket相当于指标桶,主要通过LongAdder数组记录当前窗口的通过数、阻塞数、异常数,以及通过数、阻塞数、异常数的增加;
查看ArrayMetric.addPass方法,其首先调用LeapArray.currentWindowd方法,根据当前时间获取对应的WindowWrap(时间窗口),然后调用MetricBucket.addPass方法增加通过的请求数,MetricBucket中根据MetricEvent枚举的序数,维护着一个6个长度的LongAdder数组,用于记录当前窗口的通过、阻塞、异常、成功等的数量。
public void addPass(int count) {
//获取当前的WindowWrap
WindowWrap wrap = this.data.currentWindow();
((MetricBucket)wrap.value()).addPass(count);
}
LeapArray.currentWindow
public WindowWrap<T> currentWindow() {
//根据当前时间获取WindowWrap
return this.currentWindow(TimeUtil.currentTimeMillis());
}
public WindowWrap<T> currentWindow(long timeMillis) {
if(timeMillis < 0L) {
return null;
} else {
//根据传入的时间,计算所处窗口在数组中的角标
int idx = this.calculateTimeIdx(timeMillis);
//计算时间窗口开始的时间
long windowStart = this.calculateWindowStart(timeMillis);
while(true) {
while(true) {
//获取老的时间窗口
WindowWrap old = (WindowWrap)this.array.get(idx);
WindowWrap window;
if(old == null) {
//如果老的窗口不存在,创建一个新窗口,并保存到数组中
window = new WindowWrap((long)this.windowLengthInMs, windowStart, this.newEmptyBucket(timeMillis));
if(this.array.compareAndSet(idx, (Object)null, window)) {
return window;
}
Thread.yield();
} else {
if(windowStart == old.windowStart()) {
return old;
}
//如果老的窗口存在,并且老窗口的开始时间小于计算出的窗口开始时间,说明老窗口需要重置
if(windowStart > old.windowStart()) {
if(this.updateLock.tryLock()) {
try {
window = this.resetWindowTo(old, windowStart);
} finally {
this.updateLock.unlock();
}
return window;
}
Thread.yield();
} else if(windowStart < old.windowStart()) {
return new WindowWrap((long)this.windowLengthInMs, windowStart, this.newEmptyBucket(timeMillis));
}
}
}
}
}
}
LeapArray.calculateWindowStart
protected long calculateWindowStart(long timeMillis) {
//计算窗口的开始时间
return timeMillis - timeMillis % (long)this.windowLengthInMs;
}
MetricBucket.add public MetricBucket add(MetricEvent event, long n) { this.counters[event.ordinal()].add(n); return this; }