本文主要研究一下sentinel的ArrayMetricjava
com/alibaba/csp/sentinel/slots/statistic/metric/ArrayMetric.javanode
public class ArrayMetric implements Metric { private final WindowLeapArray data; public ArrayMetric(int windowLength, int interval) { this.data = new WindowLeapArray(windowLength, interval); } /** * For unit test. */ public ArrayMetric(WindowLeapArray array) { this.data = array; } @Override public long success() { data.currentWindow(); long success = 0; List<Window> list = data.values(); for (Window window : list) { success += window.success(); } return success; } @Override public long maxSuccess() { data.currentWindow(); long success = 0; List<Window> list = data.values(); for (Window window : list) { if (window.success() > success) { success = window.success(); } } return Math.max(success, 1); } @Override public long exception() { data.currentWindow(); long exception = 0; List<Window> list = data.values(); for (Window window : list) { exception += window.exception(); } return exception; } @Override public long block() { data.currentWindow(); long block = 0; List<Window> list = data.values(); for (Window window : list) { block += window.block(); } return block; } @Override public long pass() { data.currentWindow(); long pass = 0; List<Window> list = data.values(); for (Window window : list) { pass += window.pass(); } return pass; } @Override public long rt() { data.currentWindow(); long rt = 0; List<Window> list = data.values(); for (Window window : list) { rt += window.rt(); } return rt; } @Override public long minRt() { data.currentWindow(); long rt = Constants.TIME_DROP_VALVE; List<Window> list = data.values(); for (Window window : list) { if (window.minRt() < rt) { rt = window.minRt(); } } return Math.max(1, rt); } @Override public List<MetricNode> details() { List<MetricNode> details = new ArrayList<MetricNode>(); data.currentWindow(); for (WindowWrap<Window> window : data.list()) { if (window == null) { continue; } MetricNode node = new MetricNode(); node.setBlockedQps(window.value().block()); node.setException(window.value().exception()); node.setPassedQps(window.value().pass()); long passQps = window.value().success(); node.setSuccessQps(passQps); if (passQps != 0) { node.setRt(window.value().rt() / passQps); } else { node.setRt(window.value().rt()); } node.setTimestamp(window.windowStart()); details.add(node); } return details; } @Override public Window[] windows() { data.currentWindow(); return data.values().toArray(new Window[data.values().size()]); } @Override public void addException() { WindowWrap<Window> wrap = data.currentWindow(); wrap.value().addException(); } @Override public void addBlock() { WindowWrap<Window> wrap = data.currentWindow(); wrap.value().addBlock(); } @Override public void addSuccess() { WindowWrap<Window> wrap = data.currentWindow(); wrap.value().addSuccess(); } @Override public void addPass() { WindowWrap<Window> wrap = data.currentWindow(); wrap.value().addPass(); } @Override public void addRT(long rt) { WindowWrap<Window> wrap = data.currentWindow(); wrap.value().addRT(rt); } @Override public void debugQps() { data.currentWindow(); StringBuilder sb = new StringBuilder(); sb.append(Thread.currentThread().getId()).append("_"); for (WindowWrap<Window> windowWrap : data.list()) { sb.append(windowWrap.windowStart()).append(":").append(windowWrap.value().pass()).append(":") .append(windowWrap.value().block()); sb.append(","); } System.out.println(sb); } @Override public long previousWindowBlock() { WindowWrap<Window> wrap = data.currentWindow(); wrap = data.getPreviousWindow(); if (wrap == null) { return 0; } return wrap.value().block(); } @Override public long previousWindowPass() { WindowWrap<Window> wrap = data.currentWindow(); wrap = data.getPreviousWindow(); if (wrap == null) { return 0; } return wrap.value().pass(); } }
com/alibaba/csp/sentinel/slots/statistic/metric/WindowLeapArray.javagit
public class WindowLeapArray extends LeapArray<Window> { public WindowLeapArray(int windowLengthInMs, int intervalInSec) { super(windowLengthInMs, intervalInSec); } private ReentrantLock addLock = new ReentrantLock(); /** * Reset current window to provided start time and reset all counters. * * @param startTime the start time of the window * @return new clean window wrap */ private WindowWrap<Window> resetWindowTo(WindowWrap<Window> w, long startTime) { w.resetTo(startTime); w.value().reset(); return w; } @Override public WindowWrap<Window> currentWindow(long time) { long timeId = time / windowLength; // Calculate current index. int idx = (int)(timeId % array.length()); // Cut the time to current window start. time = time - time % windowLength; while (true) { WindowWrap<Window> old = array.get(idx); if (old == null) { WindowWrap<Window> window = new WindowWrap<Window>(windowLength, time, new Window()); if (array.compareAndSet(idx, null, window)) { return window; } else { Thread.yield(); } } else if (time == old.windowStart()) { return old; } else if (time > old.windowStart()) { if (addLock.tryLock()) { try { // if (old is deprecated) then [LOCK] resetTo currentTime. return resetWindowTo(old, time); } finally { addLock.unlock(); } } else { Thread.yield(); } } else if (time < old.windowStart()) { // Cannot go through here. return new WindowWrap<Window>(windowLength, time, new Window()); } } } }
com/alibaba/csp/sentinel/slots/statistic/base/LeapArray.javagithub
public abstract class LeapArray<T> { protected int windowLength; protected int sampleCount; protected int intervalInMs; protected AtomicReferenceArray<WindowWrap<T>> array; public LeapArray(int windowLength, int intervalInSec) { this.windowLength = windowLength; this.sampleCount = intervalInSec * 1000 / windowLength; this.intervalInMs = intervalInSec * 1000; this.array = new AtomicReferenceArray<WindowWrap<T>>(sampleCount); } public WindowWrap<T> currentWindow() { return currentWindow(TimeUtil.currentTimeMillis()); } /** * Get window at provided timestamp. * * @param time a valid timestamp * @return the window at provided timestamp */ abstract public WindowWrap<T> currentWindow(long time); public WindowWrap<T> getPreviousWindow(long time) { long timeId = (time - windowLength) / windowLength; int idx = (int)(timeId % array.length()); time = time - windowLength; WindowWrap<T> wrap = array.get(idx); if (wrap == null || isWindowDeprecated(wrap)) { return null; } if (wrap.windowStart() + windowLength < (time)) { return null; } return wrap; } public WindowWrap<T> getPreviousWindow() { return getPreviousWindow(System.currentTimeMillis()); } public T getWindowValue(long time) { long timeId = time / windowLength; int idx = (int)(timeId % array.length()); WindowWrap<T> old = array.get(idx); if (old == null || isWindowDeprecated(old)) { return null; } return old.value(); } AtomicReferenceArray<WindowWrap<T>> array() { return array; } private boolean isWindowDeprecated(WindowWrap<T> windowWrap) { return TimeUtil.currentTimeMillis() - windowWrap.windowStart() >= intervalInMs; } public List<WindowWrap<T>> list() { ArrayList<WindowWrap<T>> result = new ArrayList<WindowWrap<T>>(); for (int i = 0; i < array.length(); i++) { WindowWrap<T> windowWrap = array.get(i); if (windowWrap == null || isWindowDeprecated(windowWrap)) { continue; } result.add(windowWrap); } return result; } public List<T> values() { ArrayList<T> result = new ArrayList<T>(); for (int i = 0; i < array.length(); i++) { WindowWrap<T> windowWrap = array.get(i); if (windowWrap == null || isWindowDeprecated(windowWrap)) { continue; } result.add(windowWrap.value()); } return result; } }
com/alibaba/csp/sentinel/slots/statistic/base/WindowWrap.javawindows
public class WindowWrap<T> { /** * The length of the window. */ private final long windowLength; /** * Start time of the window in milliseconds. */ private long windowStart; /** * Statistic value. */ private T value; /** * @param windowLength the time length of the window * @param windowStart the start timestamp of the window * @param value window data */ public WindowWrap(long windowLength, long windowStart, T value) { this.windowLength = windowLength; this.windowStart = windowStart; this.value = value; } public long windowLength() { return windowLength; } public long windowStart() { return windowStart; } public T value() { return value; } public void setValue(T value) { this.value = value; } public WindowWrap<T> resetTo(long startTime) { this.windowStart = startTime; return this; } @Override public String toString() { return "WindowWrap{" + "windowLength=" + windowLength + ", windowStart=" + windowStart + ", value=" + value + '}'; } }
com/alibaba/csp/sentinel/slots/statistic/base/Window.java數組
public class Window { private final LongAdder pass = new LongAdder(); private final LongAdder block = new LongAdder(); private final LongAdder exception = new LongAdder(); private final LongAdder rt = new LongAdder(); private final LongAdder success = new LongAdder(); private volatile long minRt; public Window() { initMinRt(); } private void initMinRt() { this.minRt = Constants.TIME_DROP_VALVE; } /** * Clean the adders and reset window to provided start time. * * @return new clean window */ public Window reset() { pass.reset(); block.reset(); exception.reset(); rt.reset(); success.reset(); initMinRt(); return this; } public long pass() { return pass.sum(); } public long block() { return block.sum(); } public long exception() { return exception.sum(); } public long rt() { return rt.sum(); } public long minRt() { return minRt; } public long success() { return success.sum(); } public void addPass() { pass.add(1L); } public void addException() { exception.add(1L); } public void addBlock() { block.add(1L); } public void addSuccess() { success.add(1L); } public void addRT(long rt) { this.rt.add(rt); // Not thread-safe, but it's okay. if (rt < minRt) { minRt = rt; } } }
sentinel使用ArrayMetric進行指標統計,底層使用的是WindowLeapArray,而WindowLeapArray使用的是WindowWrap<Window>,具體的統計值在Window對象,而WindowWrap包裝了窗口信息。app