Add some unit test for StatisticNode, ClusterNode and DefaultNodeBuilder class (#423)
This commit is contained in:
parent
e893dd8c64
commit
22e8d85a8f
|
|
@ -0,0 +1,149 @@
|
|||
/*
|
||||
* 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.node;
|
||||
|
||||
import com.alibaba.csp.sentinel.slots.block.flow.FlowException;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Test cases for {@link ClusterNode}.
|
||||
*
|
||||
* @author cdfive
|
||||
* @date 2019-01-11
|
||||
*/
|
||||
public class ClusterNodeTest {
|
||||
|
||||
@Test
|
||||
public void testGetOrCreateOriginNodeSingleThread() {
|
||||
ClusterNode clusterNode = new ClusterNode();
|
||||
|
||||
String origin1 = "origin1";
|
||||
Node originNode1 = clusterNode.getOrCreateOriginNode(origin1);
|
||||
assertNotNull(originNode1);
|
||||
assertEquals(1, clusterNode.getOriginCountMap().size());
|
||||
|
||||
String origin2 = "origin2";
|
||||
Node originNode2 = clusterNode.getOrCreateOriginNode(origin2);
|
||||
assertNotNull(originNode2);
|
||||
assertEquals(2, clusterNode.getOriginCountMap().size());
|
||||
assertNotSame(originNode1, originNode2);
|
||||
|
||||
// test same origin, no StatisticNode added into the originCountMap
|
||||
Node tmpOriginNode = clusterNode.getOrCreateOriginNode(origin1);
|
||||
assertEquals(2, clusterNode.getOriginCountMap().size());
|
||||
assertSame(tmpOriginNode, originNode1);
|
||||
|
||||
assertTrue(clusterNode.getOriginCountMap().containsKey(origin1));
|
||||
assertTrue(clusterNode.getOriginCountMap().containsKey(origin2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetOrCreateOriginNodeMultiThread() {
|
||||
// note: in junit4, repeat execute a test method is not very convenient
|
||||
// for simple, here use a loop instead
|
||||
// https://stackoverflow.com/questions/1492856/easy-way-of-running-the-same-junit-test-over-and-over
|
||||
// in junit5, use @RepeatedTest(10)
|
||||
int testTimes = 10;// execute 10 times, test will have chance to failed, if remove the lock in ClusterNode
|
||||
for (int times = 0; times < testTimes; times++) {
|
||||
final ClusterNode clusterNode = new ClusterNode();
|
||||
|
||||
// store all distinct nodes by calling ClusterNode#getOrCreateOriginNode
|
||||
final Set<Node> createdNodes = new HashSet<Node>();
|
||||
|
||||
final Random random = new Random();
|
||||
|
||||
// 10 threads, 3 origins, 20 tasks(in total, calling 20 times of ClusterNode#getOrCreateOriginNode concurrently)
|
||||
final ExecutorService es = Executors.newFixedThreadPool(10);
|
||||
final List<String> origins = Arrays.asList("origin1", "origin2", "origin3");
|
||||
int taskCount = 20;
|
||||
|
||||
List<Callable<Object>> tasks = new ArrayList<Callable<Object>>(taskCount);
|
||||
for (int i = 0; i < taskCount; i++) {
|
||||
tasks.add(new Callable<Object>() {
|
||||
@Override
|
||||
public Object call() throws Exception {
|
||||
// one task call one times of ClusterNode#getOrCreateOriginNode
|
||||
Node node = clusterNode.getOrCreateOriginNode(origins.get(random.nextInt(origins.size())));
|
||||
// add the result node to the createdNodes set
|
||||
// node: since HashSet is non-threadsafe, synchronized the ClusterNodeTest.class
|
||||
synchronized (ClusterNodeTest.class) {
|
||||
createdNodes.add(node);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
es.invokeAll(tasks);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
es.shutdown();
|
||||
|
||||
// origins.size() origins, the same count as the originCountMap
|
||||
assertEquals(origins.size(), clusterNode.getOriginCountMap().size());
|
||||
|
||||
// not use `assertEquals(origins.size(), createdNodes.size());`, but a compare judgement for debug
|
||||
if (origins.size() != createdNodes.size()) {
|
||||
// for debug, we can add a breakpoint here to see the detail info of createdNodes, if remove the lock in ClusterNode
|
||||
fail("originCountMap's size should " + origins.size() + ", but actual " + createdNodes.size());
|
||||
}
|
||||
|
||||
// verify originCountMap's key
|
||||
for (String origin : origins) {
|
||||
assertTrue(clusterNode.getOriginCountMap().containsKey(origin));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testTrace() {
|
||||
ClusterNode clusterNode = new ClusterNode();
|
||||
|
||||
Exception exception = new RuntimeException("test");
|
||||
|
||||
// test count<=0, no exceptionQps added
|
||||
clusterNode.trace(exception, 0);
|
||||
clusterNode.trace(exception, -1);
|
||||
assertEquals(0, clusterNode.exceptionQps());
|
||||
assertEquals(0, clusterNode.totalException());
|
||||
|
||||
// test count=1, not BlockException, 1 exceptionQps added
|
||||
clusterNode.trace(exception, 1);
|
||||
assertEquals(1, clusterNode.exceptionQps());
|
||||
assertEquals(1, clusterNode.totalException());
|
||||
|
||||
// test count=1, BlockException, no exceptionQps added
|
||||
FlowException flowException = new FlowException("flow");
|
||||
clusterNode.trace(flowException, 1);
|
||||
assertEquals(1, clusterNode.exceptionQps());
|
||||
assertEquals(1, clusterNode.totalException());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
/*
|
||||
* 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.node;
|
||||
|
||||
import com.alibaba.csp.sentinel.EntryType;
|
||||
import com.alibaba.csp.sentinel.slotchain.ResourceWrapper;
|
||||
import com.alibaba.csp.sentinel.slotchain.StringResourceWrapper;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Test cases for {@link DefaultNodeBuilder}.
|
||||
*
|
||||
* @author cdfive
|
||||
* @date 2019-01-15
|
||||
*/
|
||||
public class DefaultNodeBuilderTest {
|
||||
|
||||
@Test
|
||||
public void testBuildTreeNode() {
|
||||
DefaultNodeBuilder builder = new DefaultNodeBuilder();
|
||||
|
||||
ResourceWrapper id = new StringResourceWrapper("resA", EntryType.IN);
|
||||
ClusterNode clusterNode = new ClusterNode();
|
||||
DefaultNode defaultNode = builder.buildTreeNode(id, clusterNode);
|
||||
|
||||
assertNotNull(defaultNode);
|
||||
assertEquals(id, defaultNode.getId());
|
||||
assertEquals(clusterNode, defaultNode.getClusterNode());
|
||||
|
||||
// verify each call returns a different instance
|
||||
DefaultNode defaultNode2 = builder.buildTreeNode(id, clusterNode);
|
||||
assertNotNull(defaultNode2);
|
||||
assertNotSame(defaultNode, defaultNode2);
|
||||
// now DefaultNode#equals(Object) is not implemented, they are not equal
|
||||
assertNotEquals(defaultNode, defaultNode2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBuildTreeNode_NullClusterNode() {
|
||||
DefaultNodeBuilder builder = new DefaultNodeBuilder();
|
||||
|
||||
ResourceWrapper id = new StringResourceWrapper("resA", EntryType.IN);
|
||||
DefaultNode defaultNode = builder.buildTreeNode(id, null);
|
||||
|
||||
assertNotNull(defaultNode);
|
||||
assertEquals(id, defaultNode.getId());
|
||||
assertEquals(null, defaultNode.getClusterNode());
|
||||
|
||||
// verify each call returns a different instance
|
||||
DefaultNode defaultNode2 = builder.buildTreeNode(id, null);
|
||||
assertNotNull(defaultNode2);
|
||||
assertNotSame(defaultNode, defaultNode2);
|
||||
// now DefaultNode#equals(Object) is not implemented, they are not equal
|
||||
assertNotEquals(defaultNode, defaultNode2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBuildClusterNode() {
|
||||
DefaultNodeBuilder builder = new DefaultNodeBuilder();
|
||||
ClusterNode clusterNode = builder.buildClusterNode();
|
||||
assertNotNull(clusterNode);
|
||||
|
||||
// verify each call returns a different instance
|
||||
ClusterNode clusterNode2 = builder.buildClusterNode();
|
||||
assertNotNull(clusterNode2);
|
||||
assertNotSame(clusterNode, clusterNode2);
|
||||
// as new a ClusterNode instance in DefaultNodeBuilder#buildClusterNode(), they are not equal
|
||||
assertNotEquals(clusterNode, clusterNode2);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,202 @@
|
|||
/*
|
||||
* 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.node;
|
||||
|
||||
import com.alibaba.csp.sentinel.Constants;
|
||||
import com.alibaba.csp.sentinel.util.TimeUtil;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* Test cases for {@link StatisticNode}.
|
||||
*
|
||||
* @author cdfive
|
||||
* @date 2019-01-08
|
||||
*/
|
||||
public class StatisticNodeTest {
|
||||
|
||||
private static final String LOG_PREFIX = "[StatisticNodeTest]";
|
||||
|
||||
private static final SimpleDateFormat SDF = new SimpleDateFormat("yyyy-HH-dd HH:mm:ss");
|
||||
|
||||
private static final Random RANDOM = new Random();
|
||||
|
||||
private static final int THREAD_COUNT = 20;
|
||||
|
||||
/**
|
||||
* A simple test for statistic threadNum and qps by using StatisticNode
|
||||
*
|
||||
* <p>
|
||||
* 20 threads, 30 tasks, every task execute 10 times of bizMethod
|
||||
* one bizMthod execute within 1 second, and within 0.5 second interval to exceute next bizMthod
|
||||
* so that the total time cost will be within 1 minute
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* Print the statistic info of StatisticNode and verify some results
|
||||
* </p>
|
||||
*/
|
||||
@Test
|
||||
public void testStatisticThreadNumAndQps() {
|
||||
long testStartTime = TimeUtil.currentTimeMillis();
|
||||
|
||||
int taskCount = 30;
|
||||
int taskBizExecuteCount = 10;
|
||||
|
||||
StatisticNode node = new StatisticNode();
|
||||
|
||||
ExecutorService bizEs = Executors.newFixedThreadPool(THREAD_COUNT);
|
||||
ExecutorService tickEs = Executors.newSingleThreadExecutor();
|
||||
|
||||
tickEs.submit(new TickTask(node));
|
||||
|
||||
List<BizTask> bizTasks = new ArrayList<BizTask>(taskBizExecuteCount);
|
||||
for (int i = 0; i < taskCount; i++) {
|
||||
bizTasks.add(new BizTask(node, taskBizExecuteCount));
|
||||
}
|
||||
try {
|
||||
bizEs.invokeAll(bizTasks);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
log("====================================================");
|
||||
log("all biz task done, waiting 3 second to exit");
|
||||
sleep(3000);
|
||||
|
||||
bizEs.shutdown();
|
||||
tickEs.shutdown();
|
||||
|
||||
// now no biz method execute, so there is no curThreadNum,passQps,successQps
|
||||
assertEquals(0, node.curThreadNum());
|
||||
assertEquals(0, node.passQps());
|
||||
assertEquals(0, node.successQps());
|
||||
|
||||
// note: total time cost should be controlled within 1 minute,
|
||||
// as the node.totalRequest() holding statistics of recent 60 seconds
|
||||
int totalRequest = taskCount * taskBizExecuteCount;
|
||||
// verify totalRequest
|
||||
assertEquals(totalRequest, node.totalRequest());
|
||||
// as all execute success, totalRequest should equal to totalSuccess
|
||||
assertEquals(totalRequest, node.totalSuccess());
|
||||
|
||||
// now there are no data in time span, so the minRT should be equals to TIME_DROP_VALVE
|
||||
assertEquals(node.minRt(), Constants.TIME_DROP_VALVE);
|
||||
|
||||
log("====================================================");
|
||||
log("testStatisticThreadNumAndQps done, cost " + (TimeUtil.currentTimeMillis() - testStartTime) + "ms");
|
||||
}
|
||||
|
||||
private static class BizTask implements Callable<Object> {
|
||||
|
||||
private StatisticNode node;
|
||||
|
||||
private Integer bizExecuteCount;
|
||||
|
||||
public BizTask(StatisticNode node, Integer bizExecuteCount) {
|
||||
this.node = node;
|
||||
this.bizExecuteCount = bizExecuteCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object call() throws Exception {
|
||||
while (true) {
|
||||
node.increaseThreadNum();
|
||||
node.addPassRequest(1);
|
||||
|
||||
long startTime = TimeUtil.currentTimeMillis();
|
||||
bizMethod();
|
||||
|
||||
node.decreaseThreadNum();
|
||||
|
||||
// decrease one ThreadNum, so curThreadNum should less than THREAD_COUNT
|
||||
assertTrue(node.curThreadNum() < THREAD_COUNT);
|
||||
|
||||
long rt = TimeUtil.currentTimeMillis() - startTime;
|
||||
node.addRtAndSuccess(rt, 1);
|
||||
|
||||
// wait random 0.5 second for simulate method call interval,
|
||||
// otherwise the curThreadNum will always be THREAD_COUNT at the beginning
|
||||
sleep(RANDOM.nextInt(500));
|
||||
|
||||
bizExecuteCount--;
|
||||
if (bizExecuteCount <= 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static void bizMethod() {
|
||||
// simulate biz method call in random 1 second
|
||||
sleep(RANDOM.nextInt(1000));
|
||||
}
|
||||
|
||||
private static class TickTask implements Runnable {
|
||||
|
||||
private StatisticNode node;
|
||||
|
||||
public TickTask(StatisticNode node) {
|
||||
this.node = node;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
while (true) {
|
||||
// print statistic info every 1 second
|
||||
sleep(1000);
|
||||
|
||||
// the curThreadNum should not greater than THREAD_COUNT
|
||||
assertTrue(node.curThreadNum() <= THREAD_COUNT);
|
||||
|
||||
logNode(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void sleep(long ms) {
|
||||
try {
|
||||
TimeUnit.MILLISECONDS.sleep(ms);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private static void logNode(StatisticNode node) {
|
||||
log(SDF.format(new Date()) + " curThreadNum=" + node.curThreadNum() + ",passQps=" + node.passQps()
|
||||
+ ",successQps=" + node.successQps() + ",maxSuccessQps=" + node.maxSuccessQps()
|
||||
+ ",totalRequest=" + node.totalRequest() + ",totalSuccess=" + node.totalSuccess()
|
||||
+ ",avgRt=" + node.avgRt() + ",minRt=" + node.minRt());
|
||||
}
|
||||
|
||||
private static void log(Object obj) {
|
||||
System.out.println(LOG_PREFIX + obj);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue