Refactor LeapArray to reuse code for current bucket

Signed-off-by: Eric Zhao <sczyh16@gmail.com>
This commit is contained in:
Eric Zhao 2018-09-12 19:16:39 +08:00
parent ca2f4d9fae
commit 54905497d0
9 changed files with 179 additions and 171 deletions

View File

@ -18,10 +18,13 @@ package com.alibaba.csp.sentinel.slots.statistic.base;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicReferenceArray;
import java.util.concurrent.locks.ReentrantLock;
import com.alibaba.csp.sentinel.util.TimeUtil;
/**
* Basic data structure for statistic metrics.
*
* @param <T> type of data wrapper
* @author jialiang.linjl
* @author Eric Zhao
@ -32,27 +35,86 @@ public abstract class LeapArray<T> {
protected int sampleCount;
protected int intervalInMs;
protected AtomicReferenceArray<WindowWrap<T>> array;
protected final AtomicReferenceArray<WindowWrap<T>> array;
private final ReentrantLock updateLock = new ReentrantLock();
public LeapArray(int windowLength, int intervalInSec) {
this.windowLength = windowLength;
this.sampleCount = intervalInSec * 1000 / windowLength;
this.intervalInMs = intervalInSec * 1000;
this.sampleCount = intervalInMs / windowLength;
this.array = new AtomicReferenceArray<WindowWrap<T>>(sampleCount);
}
/**
* Get the window at current timestamp.
*
* @return the window at current timestamp
*/
public WindowWrap<T> currentWindow() {
return currentWindow(TimeUtil.currentTimeMillis());
}
/**
* Create a new bucket.
*
* @return the new empty bucket
*/
public abstract T newEmptyBucket();
/**
* Reset current window to provided start time and reset all counters.
*
* @param startTime the start time of the window
* @param windowWrap current window
* @return new clean window wrap
*/
protected abstract WindowWrap<T> resetWindowTo(WindowWrap<T> windowWrap, long startTime);
/**
* 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> 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<T> old = array.get(idx);
if (old == null) {
WindowWrap<T> window = new WindowWrap<T>(windowLength, time, newEmptyBucket());
if (array.compareAndSet(idx, null, window)) {
return window;
} else {
Thread.yield();
}
} else if (time == old.windowStart()) {
return old;
} else if (time > old.windowStart()) {
if (updateLock.tryLock()) {
try {
// if (old is deprecated) then [LOCK] resetTo currentTime.
return resetWindowTo(old, time);
} finally {
updateLock.unlock();
}
} else {
Thread.yield();
}
} else if (time < old.windowStart()) {
// Cannot go through here.
return new WindowWrap<T>(windowLength, time, newEmptyBucket());
}
}
}
public WindowWrap<T> getPreviousWindow(long time) {
long timeId = (time - windowLength) / windowLength;
@ -87,16 +149,12 @@ public abstract class LeapArray<T> {
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>>();
List<WindowWrap<T>> result = new ArrayList<WindowWrap<T>>();
for (int i = 0; i < array.length(); i++) {
WindowWrap<T> windowWrap = array.get(i);
@ -110,7 +168,7 @@ public abstract class LeapArray<T> {
}
public List<T> values() {
ArrayList<T> result = new ArrayList<T>();
List<T> result = new ArrayList<T>();
for (int i = 0; i < array.length(); i++) {
WindowWrap<T> windowWrap = array.get(i);

View File

@ -23,7 +23,7 @@ import com.alibaba.csp.sentinel.Constants;
* @author jialiang.linjl
* @author Eric Zhao
*/
public class Window {
public class MetricBucket {
private final LongAdder pass = new LongAdder();
private final LongAdder block = new LongAdder();
@ -33,7 +33,7 @@ public class Window {
private volatile long minRt;
public Window() {
public MetricBucket() {
initMinRt();
}
@ -46,7 +46,7 @@ public class Window {
*
* @return new clean window
*/
public Window reset() {
public MetricBucket reset() {
pass.reset();
block.reset();
exception.reset();

View File

@ -20,27 +20,27 @@ import java.util.List;
import com.alibaba.csp.sentinel.Constants;
import com.alibaba.csp.sentinel.node.metric.MetricNode;
import com.alibaba.csp.sentinel.slots.statistic.base.Window;
import com.alibaba.csp.sentinel.slots.statistic.base.MetricBucket;
import com.alibaba.csp.sentinel.slots.statistic.base.WindowWrap;
/**
* The basic metric class in Sentinel using a {@link WindowLeapArray} internal.
* The basic metric class in Sentinel using a {@link MetricsLeapArray} internal.
*
* @author jialiang.linjl
* @author Eric Zhao
*/
public class ArrayMetric implements Metric {
private final WindowLeapArray data;
private final MetricsLeapArray data;
public ArrayMetric(int windowLength, int interval) {
this.data = new WindowLeapArray(windowLength, interval);
this.data = new MetricsLeapArray(windowLength, interval);
}
/**
* For unit test.
*/
public ArrayMetric(WindowLeapArray array) {
public ArrayMetric(MetricsLeapArray array) {
this.data = array;
}
@ -49,8 +49,8 @@ public class ArrayMetric implements Metric {
data.currentWindow();
long success = 0;
List<Window> list = data.values();
for (Window window : list) {
List<MetricBucket> list = data.values();
for (MetricBucket window : list) {
success += window.success();
}
return success;
@ -61,8 +61,8 @@ public class ArrayMetric implements Metric {
data.currentWindow();
long success = 0;
List<Window> list = data.values();
for (Window window : list) {
List<MetricBucket> list = data.values();
for (MetricBucket window : list) {
if (window.success() > success) {
success = window.success();
}
@ -74,8 +74,8 @@ public class ArrayMetric implements Metric {
public long exception() {
data.currentWindow();
long exception = 0;
List<Window> list = data.values();
for (Window window : list) {
List<MetricBucket> list = data.values();
for (MetricBucket window : list) {
exception += window.exception();
}
return exception;
@ -85,8 +85,8 @@ public class ArrayMetric implements Metric {
public long block() {
data.currentWindow();
long block = 0;
List<Window> list = data.values();
for (Window window : list) {
List<MetricBucket> list = data.values();
for (MetricBucket window : list) {
block += window.block();
}
return block;
@ -96,9 +96,9 @@ public class ArrayMetric implements Metric {
public long pass() {
data.currentWindow();
long pass = 0;
List<Window> list = data.values();
List<MetricBucket> list = data.values();
for (Window window : list) {
for (MetricBucket window : list) {
pass += window.pass();
}
return pass;
@ -108,8 +108,8 @@ public class ArrayMetric implements Metric {
public long rt() {
data.currentWindow();
long rt = 0;
List<Window> list = data.values();
for (Window window : list) {
List<MetricBucket> list = data.values();
for (MetricBucket window : list) {
rt += window.rt();
}
return rt;
@ -119,8 +119,8 @@ public class ArrayMetric implements Metric {
public long minRt() {
data.currentWindow();
long rt = Constants.TIME_DROP_VALVE;
List<Window> list = data.values();
for (Window window : list) {
List<MetricBucket> list = data.values();
for (MetricBucket window : list) {
if (window.minRt() < rt) {
rt = window.minRt();
}
@ -133,7 +133,7 @@ public class ArrayMetric implements Metric {
public List<MetricNode> details() {
List<MetricNode> details = new ArrayList<MetricNode>();
data.currentWindow();
for (WindowWrap<Window> window : data.list()) {
for (WindowWrap<MetricBucket> window : data.list()) {
if (window == null) {
continue;
}
@ -156,38 +156,38 @@ public class ArrayMetric implements Metric {
}
@Override
public Window[] windows() {
public MetricBucket[] windows() {
data.currentWindow();
return data.values().toArray(new Window[data.values().size()]);
return data.values().toArray(new MetricBucket[data.values().size()]);
}
@Override
public void addException() {
WindowWrap<Window> wrap = data.currentWindow();
WindowWrap<MetricBucket> wrap = data.currentWindow();
wrap.value().addException();
}
@Override
public void addBlock() {
WindowWrap<Window> wrap = data.currentWindow();
WindowWrap<MetricBucket> wrap = data.currentWindow();
wrap.value().addBlock();
}
@Override
public void addSuccess() {
WindowWrap<Window> wrap = data.currentWindow();
WindowWrap<MetricBucket> wrap = data.currentWindow();
wrap.value().addSuccess();
}
@Override
public void addPass() {
WindowWrap<Window> wrap = data.currentWindow();
WindowWrap<MetricBucket> wrap = data.currentWindow();
wrap.value().addPass();
}
@Override
public void addRT(long rt) {
WindowWrap<Window> wrap = data.currentWindow();
WindowWrap<MetricBucket> wrap = data.currentWindow();
wrap.value().addRT(rt);
}
@ -196,7 +196,7 @@ public class ArrayMetric implements Metric {
data.currentWindow();
StringBuilder sb = new StringBuilder();
sb.append(Thread.currentThread().getId()).append("_");
for (WindowWrap<Window> windowWrap : data.list()) {
for (WindowWrap<MetricBucket> windowWrap : data.list()) {
sb.append(windowWrap.windowStart()).append(":").append(windowWrap.value().pass()).append(":")
.append(windowWrap.value().block());
@ -208,7 +208,7 @@ public class ArrayMetric implements Metric {
@Override
public long previousWindowBlock() {
WindowWrap<Window> wrap = data.currentWindow();
WindowWrap<MetricBucket> wrap = data.currentWindow();
wrap = data.getPreviousWindow();
if (wrap == null) {
return 0;
@ -218,12 +218,11 @@ public class ArrayMetric implements Metric {
@Override
public long previousWindowPass() {
WindowWrap<Window> wrap = data.currentWindow();
WindowWrap<MetricBucket> wrap = data.currentWindow();
wrap = data.getPreviousWindow();
if (wrap == null) {
return 0;
}
return wrap.value().pass();
}
}

View File

@ -18,7 +18,7 @@ package com.alibaba.csp.sentinel.slots.statistic.metric;
import java.util.List;
import com.alibaba.csp.sentinel.node.metric.MetricNode;
import com.alibaba.csp.sentinel.slots.statistic.base.Window;
import com.alibaba.csp.sentinel.slots.statistic.base.MetricBucket;
/**
* Represents a basic structure recording invocation metrics of protected resources.
@ -79,7 +79,7 @@ public interface Metric {
*
* @return window metric array
*/
Window[] windows();
MetricBucket[] windows();
/**
* Increment by one the current exception count.

View File

@ -0,0 +1,45 @@
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.csp.sentinel.slots.statistic.metric;
import com.alibaba.csp.sentinel.slots.statistic.base.LeapArray;
import com.alibaba.csp.sentinel.slots.statistic.base.MetricBucket;
import com.alibaba.csp.sentinel.slots.statistic.base.WindowWrap;
/**
* The fundamental data structure for metric statistics in a time window.
*
* @author jialiang.linjl
* @author Eric Zhao
*/
public class MetricsLeapArray extends LeapArray<MetricBucket> {
public MetricsLeapArray(int windowLengthInMs, int intervalInSec) {
super(windowLengthInMs, intervalInSec);
}
@Override
public MetricBucket newEmptyBucket() {
return new MetricBucket();
}
@Override
protected WindowWrap<MetricBucket> resetWindowTo(WindowWrap<MetricBucket> w, long startTime) {
w.resetTo(startTime);
w.value().reset();
return w;
}
}

View File

@ -1,88 +0,0 @@
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.csp.sentinel.slots.statistic.metric;
import java.util.concurrent.locks.ReentrantLock;
import com.alibaba.csp.sentinel.slots.statistic.base.LeapArray;
import com.alibaba.csp.sentinel.slots.statistic.base.Window;
import com.alibaba.csp.sentinel.slots.statistic.base.WindowWrap;
/**
* The fundamental data structure for metric statistics in a time window.
*
* @author jialiang.linjl
* @author Eric Zhao
*/
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());
}
}
}
}

View File

@ -19,10 +19,10 @@ import java.util.ArrayList;
import org.junit.Test;
import com.alibaba.csp.sentinel.slots.statistic.base.Window;
import com.alibaba.csp.sentinel.slots.statistic.base.MetricBucket;
import com.alibaba.csp.sentinel.slots.statistic.base.WindowWrap;
import com.alibaba.csp.sentinel.slots.statistic.metric.ArrayMetric;
import com.alibaba.csp.sentinel.slots.statistic.metric.WindowLeapArray;
import com.alibaba.csp.sentinel.slots.statistic.metric.MetricsLeapArray;
import static org.junit.Assert.*;
@ -40,10 +40,10 @@ public class ArrayMetricTest {
@Test
public void testOperateArrayMetric() {
WindowLeapArray leapArray = mock(WindowLeapArray.class);
final WindowWrap<Window> windowWrap = new WindowWrap<Window>(windowLengthInMs, 0, new Window());
MetricsLeapArray leapArray = mock(MetricsLeapArray.class);
final WindowWrap<MetricBucket> windowWrap = new WindowWrap<MetricBucket>(windowLengthInMs, 0, new MetricBucket());
when(leapArray.currentWindow()).thenReturn(windowWrap);
when(leapArray.values()).thenReturn(new ArrayList<Window>() {{ add(windowWrap.value()); }});
when(leapArray.values()).thenReturn(new ArrayList<MetricBucket>() {{ add(windowWrap.value()); }});
ArrayMetric metric = new ArrayMetric(leapArray);

View File

@ -21,30 +21,30 @@ import java.util.List;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import com.alibaba.csp.sentinel.slots.statistic.base.MetricBucket;
import com.alibaba.csp.sentinel.util.TimeUtil;
import com.alibaba.csp.sentinel.slots.statistic.base.Window;
import com.alibaba.csp.sentinel.slots.statistic.base.WindowWrap;
import com.alibaba.csp.sentinel.slots.statistic.metric.WindowLeapArray;
import com.alibaba.csp.sentinel.slots.statistic.metric.MetricsLeapArray;
import org.junit.Test;
import static org.junit.Assert.*;
/**
* Test cases for {@link WindowLeapArray}.
* Test cases for {@link MetricsLeapArray}.
*
* @author Eric Zhao
*/
public class WindowLeapArrayTest {
public class MetricsLeapArrayTest {
private final int windowLengthInMs = 1000;
private final int intervalInSec = 2;
@Test
public void testNewWindow() {
WindowLeapArray leapArray = new WindowLeapArray(windowLengthInMs, intervalInSec);
MetricsLeapArray leapArray = new MetricsLeapArray(windowLengthInMs, intervalInSec);
long time = TimeUtil.currentTimeMillis();
WindowWrap<Window> window = leapArray.currentWindow(time);
WindowWrap<MetricBucket> window = leapArray.currentWindow(time);
assertEquals(window.windowLength(), windowLengthInMs);
assertEquals(window.windowStart(), time - time % windowLengthInMs);
@ -54,11 +54,11 @@ public class WindowLeapArrayTest {
@Test
public void testLeapArrayWindowStart() {
WindowLeapArray leapArray = new WindowLeapArray(windowLengthInMs, intervalInSec);
MetricsLeapArray leapArray = new MetricsLeapArray(windowLengthInMs, intervalInSec);
long firstTime = TimeUtil.currentTimeMillis();
long previousWindowStart = firstTime - firstTime % windowLengthInMs;
WindowWrap<Window> window = leapArray.currentWindow(firstTime);
WindowWrap<MetricBucket> window = leapArray.currentWindow(firstTime);
assertEquals(windowLengthInMs, window.windowLength());
assertEquals(previousWindowStart, window.windowStart());
@ -66,15 +66,15 @@ public class WindowLeapArrayTest {
@Test
public void testWindowAfterOneInterval() {
WindowLeapArray leapArray = new WindowLeapArray(windowLengthInMs, intervalInSec);
MetricsLeapArray leapArray = new MetricsLeapArray(windowLengthInMs, intervalInSec);
long firstTime = TimeUtil.currentTimeMillis();
long previousWindowStart = firstTime - firstTime % windowLengthInMs;
WindowWrap<Window> window = leapArray.currentWindow(previousWindowStart);
WindowWrap<MetricBucket> window = leapArray.currentWindow(previousWindowStart);
assertEquals(windowLengthInMs, window.windowLength());
assertEquals(previousWindowStart, window.windowStart());
Window currentWindow = window.value();
MetricBucket currentWindow = window.value();
assertNotNull(currentWindow);
currentWindow.addPass();
@ -87,7 +87,7 @@ public class WindowLeapArrayTest {
window = leapArray.currentWindow(middleTime);
assertEquals(previousWindowStart, window.windowStart());
Window middleWindow = window.value();
MetricBucket middleWindow = window.value();
middleWindow.addPass();
assertSame(currentWindow, middleWindow);
assertEquals(2L, middleWindow.pass());
@ -106,18 +106,18 @@ public class WindowLeapArrayTest {
@Deprecated
public void testWindowDeprecatedRefresh() {
WindowLeapArray leapArray = new WindowLeapArray(windowLengthInMs, intervalInSec);
MetricsLeapArray leapArray = new MetricsLeapArray(windowLengthInMs, intervalInSec);
final int len = intervalInSec * 1000 / windowLengthInMs;
long firstTime = TimeUtil.currentTimeMillis();
List<WindowWrap<Window>> firstIterWindowList = new ArrayList<WindowWrap<Window>>(len);
List<WindowWrap<MetricBucket>> firstIterWindowList = new ArrayList<WindowWrap<MetricBucket>>(len);
for (int i = 0; i < len; i++) {
WindowWrap<Window> w = leapArray.currentWindow(firstTime + windowLengthInMs * i);
WindowWrap<MetricBucket> w = leapArray.currentWindow(firstTime + windowLengthInMs * i);
w.value().addPass();
firstIterWindowList.add(i, w);
}
for (int i = len; i < len * 2; i++) {
WindowWrap<Window> w = leapArray.currentWindow(firstTime + windowLengthInMs * i);
WindowWrap<MetricBucket> w = leapArray.currentWindow(firstTime + windowLengthInMs * i);
assertNotSame(w, firstIterWindowList.get(i - len));
}
}
@ -126,7 +126,7 @@ public class WindowLeapArrayTest {
public void testMultiThreadUpdateEmptyWindow() throws Exception {
final long time = TimeUtil.currentTimeMillis();
final int nThreads = 16;
final WindowLeapArray leapArray = new WindowLeapArray(windowLengthInMs, intervalInSec);
final MetricsLeapArray leapArray = new MetricsLeapArray(windowLengthInMs, intervalInSec);
final CountDownLatch latch = new CountDownLatch(nThreads);
Runnable task = new Runnable() {
@Override
@ -147,9 +147,9 @@ public class WindowLeapArrayTest {
@Test
public void testGetPreviousWindow() {
WindowLeapArray leapArray = new WindowLeapArray(windowLengthInMs, intervalInSec);
MetricsLeapArray leapArray = new MetricsLeapArray(windowLengthInMs, intervalInSec);
long time = TimeUtil.currentTimeMillis();
WindowWrap<Window> previousWindow = leapArray.currentWindow(time);
WindowWrap<MetricBucket> previousWindow = leapArray.currentWindow(time);
assertNull(leapArray.getPreviousWindow(time));
long nextTime = time + windowLengthInMs;
@ -165,16 +165,16 @@ public class WindowLeapArrayTest {
final int intervalInSec = 1;
final int intervalInMs = intervalInSec * 1000;
WindowLeapArray leapArray = new WindowLeapArray(windowLengthInMs, intervalInSec);
MetricsLeapArray leapArray = new MetricsLeapArray(windowLengthInMs, intervalInSec);
long time = TimeUtil.currentTimeMillis();
Set<WindowWrap<Window>> windowWraps = new HashSet<WindowWrap<Window>>();
Set<WindowWrap<MetricBucket>> windowWraps = new HashSet<WindowWrap<MetricBucket>>();
windowWraps.add(leapArray.currentWindow(time));
windowWraps.add(leapArray.currentWindow(time + windowLengthInMs));
List<WindowWrap<Window>> list = leapArray.list();
for (WindowWrap<Window> wrap : list) {
List<WindowWrap<MetricBucket>> list = leapArray.list();
for (WindowWrap<MetricBucket> wrap : list) {
assertTrue(windowWraps.contains(wrap));
}
@ -191,18 +191,18 @@ public class WindowLeapArrayTest {
final int windowLengthInMs = 100;
final int intervalInSec = 1;
WindowLeapArray leapArray = new WindowLeapArray(windowLengthInMs, intervalInSec);
MetricsLeapArray leapArray = new MetricsLeapArray(windowLengthInMs, intervalInSec);
long time = TimeUtil.currentTimeMillis();
Set<WindowWrap<Window>> windowWraps = new HashSet<WindowWrap<Window>>();
Set<WindowWrap<MetricBucket>> windowWraps = new HashSet<WindowWrap<MetricBucket>>();
windowWraps.add(leapArray.currentWindow(time));
windowWraps.add(leapArray.currentWindow(time + windowLengthInMs));
Thread.sleep(intervalInSec * 1000 + windowLengthInMs * 3);
List<WindowWrap<Window>> list = leapArray.list();
for (WindowWrap<Window> wrap : list) {
List<WindowWrap<MetricBucket>> list = leapArray.list();
for (WindowWrap<MetricBucket> wrap : list) {
assertTrue(windowWraps.contains(wrap));
}

View File

@ -39,12 +39,6 @@ public class WarmUpFlowTest {
FlowRuleManager.loadRules(Arrays.asList(flowRule));
//ContextUtil.enter(null);
//when(flowRule.selectNodeByRequsterAndStrategy(null, null, null)).thenReturn(value)
// flowRule.passCheck(null, DefaultNode, acquireCount, args)
// when(leapArray.values()).thenReturn(new ArrayList<Window>() {{ add(windowWrap.value()); }});
ContextUtil.enter("test");
ContextUtil.exit();