diff --git a/sentinel-cluster/sentinel-cluster-server-default/pom.xml b/sentinel-cluster/sentinel-cluster-server-default/pom.xml index ed98f2add2..b222431b0e 100644 --- a/sentinel-cluster/sentinel-cluster-server-default/pom.xml +++ b/sentinel-cluster/sentinel-cluster-server-default/pom.xml @@ -41,7 +41,16 @@ sentinel-datasource-nacos test - + + org.powermock + powermock-module-junit4 + test + + + org.powermock + powermock-api-mockito2 + test + junit junit diff --git a/sentinel-cluster/sentinel-cluster-server-default/src/main/java/com/alibaba/csp/sentinel/cluster/flow/statistic/metric/ClusterParamMetric.java b/sentinel-cluster/sentinel-cluster-server-default/src/main/java/com/alibaba/csp/sentinel/cluster/flow/statistic/metric/ClusterParamMetric.java index 12710e71af..20279b0589 100644 --- a/sentinel-cluster/sentinel-cluster-server-default/src/main/java/com/alibaba/csp/sentinel/cluster/flow/statistic/metric/ClusterParamMetric.java +++ b/sentinel-cluster/sentinel-cluster-server-default/src/main/java/com/alibaba/csp/sentinel/cluster/flow/statistic/metric/ClusterParamMetric.java @@ -15,20 +15,13 @@ */ package com.alibaba.csp.sentinel.cluster.flow.statistic.metric; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; - -import com.alibaba.csp.sentinel.cluster.flow.statistic.data.ClusterFlowEvent; import com.alibaba.csp.sentinel.slots.statistic.base.LongAdder; import com.alibaba.csp.sentinel.slots.statistic.cache.CacheMap; import com.alibaba.csp.sentinel.util.AssertUtil; +import java.util.*; +import java.util.Map.Entry; + /** * @author Eric Zhao * @since 1.4.0 @@ -89,6 +82,7 @@ public double getAvg(Object value) { } public Map getTopValues(int number) { + AssertUtil.isTrue(number > 0, "number must be positive"); metric.currentWindow(); List> buckets = metric.values(); @@ -114,7 +108,7 @@ public Map getTopValues(int number) { @Override public int compare(Entry a, Entry b) { - return (int)(b.getValue() == null ? 0 : b.getValue()) - (int)(a.getValue() == null ? 0 : a.getValue()); + return (int) (b.getValue() == null ? 0 : b.getValue()) - (int) (a.getValue() == null ? 0 : a.getValue()); } }); @@ -126,7 +120,7 @@ public int compare(Entry a, if (x.getValue() == 0) { break; } - doubleResult.put(x.getKey(), ((double)x.getValue()) / metric.getIntervalInSecond()); + doubleResult.put(x.getKey(), ((double) x.getValue()) / metric.getIntervalInSecond()); } return doubleResult; diff --git a/sentinel-cluster/sentinel-cluster-server-default/src/test/java/com/alibaba/csp/sentinel/cluster/flow/statistic/limit/GlobalRequestLimiterTest.java b/sentinel-cluster/sentinel-cluster-server-default/src/test/java/com/alibaba/csp/sentinel/cluster/flow/statistic/limit/GlobalRequestLimiterTest.java new file mode 100644 index 0000000000..e618d7dec5 --- /dev/null +++ b/sentinel-cluster/sentinel-cluster-server-default/src/test/java/com/alibaba/csp/sentinel/cluster/flow/statistic/limit/GlobalRequestLimiterTest.java @@ -0,0 +1,56 @@ +/* + * 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.cluster.flow.statistic.limit; + +import com.alibaba.csp.sentinel.cluster.server.config.ClusterServerConfigManager; +import com.alibaba.csp.sentinel.cluster.test.AbstractTimeBasedTest; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +public class GlobalRequestLimiterTest extends AbstractTimeBasedTest { + @Before + public void preTest() { + ClusterServerConfigManager.setMaxAllowedQps(3); + } + + @Test + public void testPass() throws InterruptedException { + setCurrentMillis(System.currentTimeMillis()); + GlobalRequestLimiter.initIfAbsent("user"); + Assert.assertNotNull(GlobalRequestLimiter.getRequestLimiter("user")); + Assert.assertEquals(3, GlobalRequestLimiter.getMaxAllowedQps("user"), 0.01); + Assert.assertTrue(GlobalRequestLimiter.tryPass("user")); + Assert.assertTrue(GlobalRequestLimiter.tryPass("user")); + Assert.assertTrue(GlobalRequestLimiter.tryPass("user")); + Assert.assertFalse(GlobalRequestLimiter.tryPass("user")); + Assert.assertEquals(3, GlobalRequestLimiter.getCurrentQps("user"), 0.01); + + // wait a second to refresh the window + sleep(1000); + Assert.assertTrue(GlobalRequestLimiter.tryPass("user")); + Assert.assertTrue(GlobalRequestLimiter.tryPass("user")); + Assert.assertEquals(2, GlobalRequestLimiter.getCurrentQps("user"), 0.01); + } + + @Test + public void testChangeMaxAllowedQps() { + GlobalRequestLimiter.initIfAbsent("foo"); + Assert.assertEquals(3, GlobalRequestLimiter.getMaxAllowedQps("foo"), 0.01); + GlobalRequestLimiter.applyMaxQpsChange(10); + Assert.assertEquals(10, GlobalRequestLimiter.getMaxAllowedQps("foo"), 0.01); + } +} diff --git a/sentinel-cluster/sentinel-cluster-server-default/src/test/java/com/alibaba/csp/sentinel/cluster/flow/statistic/limit/RequestLimiterTest.java b/sentinel-cluster/sentinel-cluster-server-default/src/test/java/com/alibaba/csp/sentinel/cluster/flow/statistic/limit/RequestLimiterTest.java new file mode 100644 index 0000000000..0d3f95e6af --- /dev/null +++ b/sentinel-cluster/sentinel-cluster-server-default/src/test/java/com/alibaba/csp/sentinel/cluster/flow/statistic/limit/RequestLimiterTest.java @@ -0,0 +1,44 @@ +/* + * 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.cluster.flow.statistic.limit; + +import com.alibaba.csp.sentinel.cluster.test.AbstractTimeBasedTest; +import org.junit.Test; + +import static org.junit.Assert.*; + +public class RequestLimiterTest extends AbstractTimeBasedTest { + + @Test + public void testRequestLimiter() { + setCurrentMillis(System.currentTimeMillis()); + RequestLimiter limiter = new RequestLimiter(10); + limiter.add(3); + limiter.add(3); + limiter.add(3); + assertTrue(limiter.canPass()); + assertEquals(9, limiter.getSum()); + limiter.add(3); + assertFalse(limiter.canPass()); + + // wait a second to refresh the window + sleep(1000); + limiter.add(3); + assertTrue(limiter.tryPass()); + assertTrue(limiter.canPass()); + assertEquals(4, limiter.getSum()); + } +} diff --git a/sentinel-cluster/sentinel-cluster-server-default/src/test/java/com/alibaba/csp/sentinel/cluster/flow/statistic/metric/ClusterMetricTest.java b/sentinel-cluster/sentinel-cluster-server-default/src/test/java/com/alibaba/csp/sentinel/cluster/flow/statistic/metric/ClusterMetricTest.java new file mode 100644 index 0000000000..e8c286bc7c --- /dev/null +++ b/sentinel-cluster/sentinel-cluster-server-default/src/test/java/com/alibaba/csp/sentinel/cluster/flow/statistic/metric/ClusterMetricTest.java @@ -0,0 +1,46 @@ +/* + * 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.cluster.flow.statistic.metric; + +import com.alibaba.csp.sentinel.cluster.flow.statistic.data.ClusterFlowEvent; +import com.alibaba.csp.sentinel.cluster.test.AbstractTimeBasedTest; +import org.junit.Assert; +import org.junit.Test; + +public class ClusterMetricTest extends AbstractTimeBasedTest { + + @Test + public void testTryOccupyNext() { + setCurrentMillis(System.currentTimeMillis()); + ClusterMetric metric = new ClusterMetric(5, 25); + metric.add(ClusterFlowEvent.PASS, 1); + metric.add(ClusterFlowEvent.PASS, 2); + metric.add(ClusterFlowEvent.PASS, 1); + metric.add(ClusterFlowEvent.BLOCK, 1); + Assert.assertEquals(4, metric.getSum(ClusterFlowEvent.PASS)); + Assert.assertEquals(1, metric.getSum(ClusterFlowEvent.BLOCK)); + Assert.assertEquals(160, metric.getAvg(ClusterFlowEvent.PASS), 0.01); + Assert.assertEquals(200, metric.tryOccupyNext(ClusterFlowEvent.PASS, 111, 900)); + metric.add(ClusterFlowEvent.PASS, 1); + metric.add(ClusterFlowEvent.PASS, 2); + metric.add(ClusterFlowEvent.PASS, 1); + Assert.assertEquals(200, metric.tryOccupyNext(ClusterFlowEvent.PASS, 222, 900)); + metric.add(ClusterFlowEvent.PASS, 1); + metric.add(ClusterFlowEvent.PASS, 2); + metric.add(ClusterFlowEvent.PASS, 1); + Assert.assertEquals(0, metric.tryOccupyNext(ClusterFlowEvent.PASS, 333, 900)); + } +} diff --git a/sentinel-cluster/sentinel-cluster-server-default/src/test/java/com/alibaba/csp/sentinel/cluster/flow/statistic/metric/ClusterParamMetricTest.java b/sentinel-cluster/sentinel-cluster-server-default/src/test/java/com/alibaba/csp/sentinel/cluster/flow/statistic/metric/ClusterParamMetricTest.java new file mode 100644 index 0000000000..82645877ad --- /dev/null +++ b/sentinel-cluster/sentinel-cluster-server-default/src/test/java/com/alibaba/csp/sentinel/cluster/flow/statistic/metric/ClusterParamMetricTest.java @@ -0,0 +1,56 @@ +/* + * 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.cluster.flow.statistic.metric; + +import com.alibaba.csp.sentinel.cluster.test.AbstractTimeBasedTest; +import org.junit.Assert; +import org.junit.Test; + +import java.util.HashMap; +import java.util.Map; + +public class ClusterParamMetricTest extends AbstractTimeBasedTest { + + @Test + public void testClusterParamMetric() { + setCurrentMillis(System.currentTimeMillis()); + Map topMap = new HashMap(); + ClusterParamMetric metric = new ClusterParamMetric(5, 25, 100); + metric.addValue("e1", -1); + metric.addValue("e1", -2); + metric.addValue("e2", 100); + metric.addValue("e2", 23); + metric.addValue("e3", 100); + metric.addValue("e3", 230); + Assert.assertEquals(-3, metric.getSum("e1")); + Assert.assertEquals(-120, metric.getAvg("e1"), 0.01); + topMap.put("e3", (double) 13200); + Assert.assertEquals(topMap, metric.getTopValues(1)); + topMap.put("e2", (double) 4920); + topMap.put("e1", (double) -120); + Assert.assertEquals(topMap, metric.getTopValues(5)); + metric.addValue("e2", 100); + metric.addValue("e2", 23); + Assert.assertEquals(246, metric.getSum("e2")); + Assert.assertEquals(9840, metric.getAvg("e2"), 0.01); + } + + @Test(expected = IllegalArgumentException.class) + public void testIllegalArgument() { + ClusterParamMetric metric = new ClusterParamMetric(5, 25, 100); + metric.getTopValues(-1); + } +} diff --git a/sentinel-cluster/sentinel-cluster-server-default/src/test/java/com/alibaba/csp/sentinel/cluster/test/AbstractTimeBasedTest.java b/sentinel-cluster/sentinel-cluster-server-default/src/test/java/com/alibaba/csp/sentinel/cluster/test/AbstractTimeBasedTest.java new file mode 100644 index 0000000000..d4458a0d21 --- /dev/null +++ b/sentinel-cluster/sentinel-cluster-server-default/src/test/java/com/alibaba/csp/sentinel/cluster/test/AbstractTimeBasedTest.java @@ -0,0 +1,55 @@ +/* + * 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.cluster.test; + +import com.alibaba.csp.sentinel.util.TimeUtil; +import org.junit.runner.RunWith; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; + +/** + * Mock support for {@link TimeUtil}. + */ +@RunWith(PowerMockRunner.class) +@PrepareForTest({TimeUtil.class}) +public abstract class AbstractTimeBasedTest { + + private long currentMillis = 0; + + { + PowerMockito.mockStatic(TimeUtil.class); + PowerMockito.when(TimeUtil.currentTimeMillis()).thenReturn(currentMillis); + } + + protected final void useActualTime() { + PowerMockito.when(TimeUtil.currentTimeMillis()).thenCallRealMethod(); + } + + protected final void setCurrentMillis(long cur) { + currentMillis = cur; + PowerMockito.when(TimeUtil.currentTimeMillis()).thenReturn(currentMillis); + } + + protected final void sleep(int t) { + currentMillis += t; + PowerMockito.when(TimeUtil.currentTimeMillis()).thenReturn(currentMillis); + } + + protected final void sleepSecond(int timeSec) { + sleep(timeSec * 1000); + } +}