Signed-off-by: Eric Zhao <sczyh16@gmail.com>master
@@ -0,0 +1,213 @@ | |||
/* | |||
* 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.block.degrade; | |||
import com.alibaba.csp.sentinel.Entry; | |||
import com.alibaba.csp.sentinel.SphU; | |||
import com.alibaba.csp.sentinel.Tracer; | |||
import com.alibaba.csp.sentinel.slots.block.BlockException; | |||
import com.alibaba.csp.sentinel.slots.block.RuleConstant; | |||
import com.alibaba.csp.sentinel.slots.block.degrade.circuitbreaker.CircuitBreaker.State; | |||
import com.alibaba.csp.sentinel.slots.block.degrade.circuitbreaker.CircuitBreakerStateChangeObserver; | |||
import com.alibaba.csp.sentinel.slots.block.degrade.circuitbreaker.EventObserverRegistry; | |||
import com.alibaba.csp.sentinel.test.AbstractTimeBasedTest; | |||
import org.junit.After; | |||
import org.junit.Before; | |||
import org.junit.Test; | |||
import java.util.ArrayList; | |||
import java.util.Arrays; | |||
import java.util.concurrent.ThreadLocalRandom; | |||
import static org.junit.Assert.assertEquals; | |||
import static org.junit.Assert.assertFalse; | |||
import static org.junit.Assert.assertTrue; | |||
import static org.mockito.Mockito.*; | |||
/** | |||
* @author jialiang.linjl | |||
* @author Eric Zhao | |||
*/ | |||
public class CircuitBreakingIntegrationTest extends AbstractTimeBasedTest { | |||
@Before | |||
public void setUp() { | |||
DegradeRuleManager.loadRules(new ArrayList<DegradeRule>()); | |||
} | |||
@After | |||
public void tearDown() throws Exception { | |||
DegradeRuleManager.loadRules(new ArrayList<DegradeRule>()); | |||
} | |||
private boolean entryAndSleepFor(String res, int sleepMs) { | |||
Entry entry = null; | |||
try { | |||
entry = SphU.entry(res); | |||
sleep(sleepMs); | |||
} catch (BlockException ex) { | |||
return false; | |||
} catch (Exception ex) { | |||
Tracer.traceEntry(ex, entry); | |||
} finally { | |||
if (entry != null) { | |||
entry.exit(); | |||
} | |||
} | |||
return true; | |||
} | |||
private boolean entryWithErrorIfPresent(String res, Exception ex) { | |||
Entry entry = null; | |||
try { | |||
entry = SphU.entry(res); | |||
if (ex != null) { | |||
Tracer.traceEntry(ex, entry); | |||
} | |||
sleep(ThreadLocalRandom.current().nextInt(5, 10)); | |||
} catch (BlockException b) { | |||
return false; | |||
} finally { | |||
if (entry != null) { | |||
entry.exit(); | |||
} | |||
} | |||
return true; | |||
} | |||
@Test | |||
public void testSlowRequestMode() throws Exception { | |||
CircuitBreakerStateChangeObserver observer = mock(CircuitBreakerStateChangeObserver.class); | |||
setCurrentMillis(System.currentTimeMillis() / 1000 * 1000); | |||
int retryTimeoutSec = 5; | |||
int maxRt = 50; | |||
int statIntervalMs = 20000; | |||
int minRequestAmount = 10; | |||
String res = "CircuitBreakingIntegrationTest_testSlowRequestMode"; | |||
EventObserverRegistry.getInstance().addStateChangeObserver(res, observer); | |||
DegradeRuleManager.loadRules(Arrays.asList( | |||
new DegradeRule(res).setTimeWindow(retryTimeoutSec).setCount(maxRt) | |||
.setStatIntervalMs(statIntervalMs).setMinRequestAmount(minRequestAmount) | |||
.setSlowRatioThreshold(0.8d).setGrade(0) | |||
)); | |||
// Try first N requests where N = minRequestAmount. | |||
for (int i = 0; i < minRequestAmount; i++) { | |||
if (i < 7) { | |||
assertTrue(entryAndSleepFor(res, maxRt + ThreadLocalRandom.current().nextInt(10, 20))); | |||
} else { | |||
assertTrue(entryAndSleepFor(res, maxRt + ThreadLocalRandom.current().nextInt(-20, -10))); | |||
} | |||
} | |||
// Till now slow ratio should be 70%. | |||
assertTrue(entryAndSleepFor(res, maxRt + ThreadLocalRandom.current().nextInt(10, 20))); | |||
assertTrue(entryAndSleepFor(res, maxRt + ThreadLocalRandom.current().nextInt(10, 20))); | |||
assertTrue(entryAndSleepFor(res, maxRt + ThreadLocalRandom.current().nextInt(10, 20))); | |||
assertTrue(entryAndSleepFor(res, maxRt + ThreadLocalRandom.current().nextInt(10, 20))); | |||
assertTrue(entryAndSleepFor(res, maxRt + ThreadLocalRandom.current().nextInt(10, 20))); | |||
assertTrue(entryAndSleepFor(res, maxRt + ThreadLocalRandom.current().nextInt(10, 20))); | |||
// Circuit breaker has transformed to OPEN since here. | |||
verify(observer) | |||
.onStateChange(eq(State.CLOSED), eq(State.OPEN), any(DegradeRule.class), anyDouble()); | |||
assertEquals(State.OPEN, DegradeRuleManager.getCircuitBreakers(res).get(0).currentState()); | |||
assertFalse(entryAndSleepFor(res, 1)); | |||
sleepSecond(1); | |||
assertFalse(entryAndSleepFor(res, 1)); | |||
sleepSecond(retryTimeoutSec); | |||
// Test HALF-OPEN to OPEN. | |||
assertTrue(entryAndSleepFor(res, maxRt + ThreadLocalRandom.current().nextInt(10, 20))); | |||
verify(observer) | |||
.onStateChange(eq(State.OPEN), eq(State.HALF_OPEN), any(DegradeRule.class), nullable(Double.class)); | |||
verify(observer) | |||
.onStateChange(eq(State.HALF_OPEN), eq(State.OPEN), any(DegradeRule.class), anyDouble()); | |||
// Wait for next retry timeout; | |||
reset(observer); | |||
sleepSecond(retryTimeoutSec + 1); | |||
assertTrue(entryAndSleepFor(res, maxRt - ThreadLocalRandom.current().nextInt(10, 20))); | |||
verify(observer) | |||
.onStateChange(eq(State.OPEN), eq(State.HALF_OPEN), any(DegradeRule.class), nullable(Double.class)); | |||
verify(observer) | |||
.onStateChange(eq(State.HALF_OPEN), eq(State.CLOSED), any(DegradeRule.class), nullable(Double.class)); | |||
// Now circuit breaker has been closed. | |||
assertTrue(entryAndSleepFor(res, maxRt + ThreadLocalRandom.current().nextInt(10, 20))); | |||
EventObserverRegistry.getInstance().removeStateChangeObserver(res); | |||
} | |||
@Test | |||
public void testExceptionRatioMode() throws Exception { | |||
CircuitBreakerStateChangeObserver observer = mock(CircuitBreakerStateChangeObserver.class); | |||
setCurrentMillis(System.currentTimeMillis() / 1000 * 1000); | |||
int retryTimeoutSec = 5; | |||
double maxRatio = 0.5; | |||
int statIntervalMs = 25000; | |||
final int minRequestAmount = 10; | |||
String res = "CircuitBreakingIntegrationTest_testExceptionRatioMode"; | |||
EventObserverRegistry.getInstance().addStateChangeObserver(res, observer); | |||
DegradeRuleManager.loadRules(Arrays.asList( | |||
new DegradeRule(res).setTimeWindow(retryTimeoutSec).setCount(maxRatio) | |||
.setStatIntervalMs(statIntervalMs).setMinRequestAmount(minRequestAmount) | |||
.setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_RATIO) | |||
)); | |||
// Try first N requests where N = minRequestAmount. | |||
for (int i = 0; i < minRequestAmount - 1; i++) { | |||
if (i < 6) { | |||
assertTrue(entryWithErrorIfPresent(res, new IllegalArgumentException())); | |||
} else { | |||
assertTrue(entryWithErrorIfPresent(res, null)); | |||
} | |||
} | |||
// Till now slow ratio should be 60%. | |||
assertTrue(entryWithErrorIfPresent(res, new IllegalArgumentException())); | |||
// Circuit breaker has transformed to OPEN since here. | |||
assertEquals(State.OPEN, DegradeRuleManager.getCircuitBreakers(res).get(0).currentState()); | |||
assertFalse(entryWithErrorIfPresent(res, null)); | |||
sleepSecond(2); | |||
assertFalse(entryWithErrorIfPresent(res, null)); | |||
sleepSecond(retryTimeoutSec); | |||
// Test HALF-OPEN to OPEN. | |||
assertTrue(entryWithErrorIfPresent(res, new IllegalArgumentException())); | |||
verify(observer) | |||
.onStateChange(eq(State.OPEN), eq(State.HALF_OPEN), any(DegradeRule.class), nullable(Double.class)); | |||
verify(observer) | |||
.onStateChange(eq(State.HALF_OPEN), eq(State.OPEN), any(DegradeRule.class), anyDouble()); | |||
// Wait for next retry timeout; | |||
reset(observer); | |||
sleepSecond(retryTimeoutSec + 1); | |||
assertTrue(entryWithErrorIfPresent(res, null)); | |||
verify(observer) | |||
.onStateChange(eq(State.OPEN), eq(State.HALF_OPEN), any(DegradeRule.class), nullable(Double.class)); | |||
verify(observer) | |||
.onStateChange(eq(State.HALF_OPEN), eq(State.CLOSED), any(DegradeRule.class), nullable(Double.class)); | |||
// Now circuit breaker has been closed. | |||
assertTrue(entryWithErrorIfPresent(res, new IllegalArgumentException())); | |||
EventObserverRegistry.getInstance().removeStateChangeObserver(res); | |||
} | |||
@Test | |||
public void testExceptionCountMode() throws Throwable { | |||
// TODO | |||
} | |||
} |
@@ -15,8 +15,14 @@ | |||
*/ | |||
package com.alibaba.csp.sentinel.slots.block.degrade; | |||
import java.util.ArrayList; | |||
import java.util.Arrays; | |||
import com.alibaba.csp.sentinel.slots.block.RuleConstant; | |||
import com.alibaba.csp.sentinel.slots.block.degrade.circuitbreaker.CircuitBreaker; | |||
import org.junit.After; | |||
import org.junit.Before; | |||
import org.junit.Test; | |||
import static org.junit.Assert.*; | |||
@@ -28,6 +34,32 @@ import static org.junit.Assert.*; | |||
*/ | |||
public class DegradeRuleManagerTest { | |||
@Before | |||
public void setUp() throws Exception { | |||
DegradeRuleManager.loadRules(new ArrayList<DegradeRule>()); | |||
} | |||
@After | |||
public void tearDown() throws Exception { | |||
DegradeRuleManager.loadRules(new ArrayList<DegradeRule>()); | |||
} | |||
@Test | |||
public void loadSameRuleUseSameCircuitBreaker() { | |||
String resource = "loadSameRuleUseSameCircuitBreaker"; | |||
DegradeRule rule = new DegradeRule(resource) | |||
.setCount(100) | |||
.setSlowRatioThreshold(0.9d) | |||
.setTimeWindow(20) | |||
.setStatIntervalMs(20000); | |||
DegradeRuleManager.loadRules(Arrays.asList(rule)); | |||
CircuitBreaker cb = DegradeRuleManager.getCircuitBreakers(resource).get(0); | |||
DegradeRuleManager.loadRules(Arrays.asList(rule, | |||
new DegradeRule("abc").setTimeWindow(20).setCount(20).setSlowRatioThreshold(0.8d))); | |||
assertSame(cb, DegradeRuleManager.getCircuitBreakers(resource).get(0)); | |||
} | |||
@Test | |||
public void testIsValidRule() { | |||
DegradeRule rule1 = new DegradeRule("abc"); | |||
@@ -46,13 +78,19 @@ public class DegradeRuleManagerTest { | |||
DegradeRule rule5 = new DegradeRule("Sentinel") | |||
.setCount(97) | |||
.setGrade(RuleConstant.DEGRADE_GRADE_RT) | |||
.setTimeWindow(15) | |||
.setRtSlowRequestAmount(0); | |||
.setSlowRatioThreshold(15) | |||
.setTimeWindow(15); | |||
DegradeRule rule6 = new DegradeRule("Sentinel") | |||
.setCount(0.93d) | |||
.setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_RATIO) | |||
.setTimeWindow(20) | |||
.setMinRequestAmount(0); | |||
DegradeRule rule7 = new DegradeRule("Sentinel") | |||
.setCount(100) | |||
.setSlowRatioThreshold(0.8d) | |||
.setTimeWindow(10) | |||
.setStatIntervalMs(0) | |||
.setMinRequestAmount(20); | |||
assertFalse(DegradeRuleManager.isValidRule(rule1)); | |||
assertFalse(DegradeRuleManager.isValidRule(rule2)); | |||
assertFalse(DegradeRuleManager.isValidRule(rule3)); | |||
@@ -61,5 +99,6 @@ public class DegradeRuleManagerTest { | |||
assertFalse(DegradeRuleManager.isValidRule(rule4)); | |||
assertFalse(DegradeRuleManager.isValidRule(rule5)); | |||
assertFalse(DegradeRuleManager.isValidRule(rule6)); | |||
assertFalse(DegradeRuleManager.isValidRule(rule7)); | |||
} | |||
} |
@@ -0,0 +1,51 @@ | |||
/* | |||
* 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.block.degrade; | |||
import com.alibaba.csp.sentinel.slots.block.RuleConstant; | |||
import org.junit.Test; | |||
import static org.junit.Assert.*; | |||
/** | |||
* @author Eric Zhao | |||
*/ | |||
public class DegradeRuleTest { | |||
@Test | |||
public void testRuleEquals() { | |||
DegradeRule degradeRule1 = new DegradeRule(); | |||
DegradeRule degradeRule2 = new DegradeRule(); | |||
int minRequestAmount = 20; | |||
double count = 1.0; | |||
int timeWindow = 2; | |||
degradeRule1.setMinRequestAmount(minRequestAmount); | |||
degradeRule1.setCount(count); | |||
degradeRule1.setTimeWindow(timeWindow); | |||
degradeRule1.setGrade(RuleConstant.DEGRADE_GRADE_RT); | |||
degradeRule2.setMinRequestAmount(minRequestAmount); | |||
degradeRule2.setCount(count); | |||
degradeRule2.setGrade(RuleConstant.DEGRADE_GRADE_RT); | |||
degradeRule2.setTimeWindow(timeWindow); | |||
assertEquals(degradeRule1, degradeRule2); | |||
degradeRule2.setMinRequestAmount(100); | |||
assertNotEquals(degradeRule1, degradeRule2); | |||
} | |||
} |
@@ -1,182 +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.block.degrade; | |||
import com.alibaba.csp.sentinel.EntryType; | |||
import com.alibaba.csp.sentinel.context.Context; | |||
import com.alibaba.csp.sentinel.node.ClusterNode; | |||
import com.alibaba.csp.sentinel.node.DefaultNode; | |||
import com.alibaba.csp.sentinel.slotchain.StringResourceWrapper; | |||
import com.alibaba.csp.sentinel.slots.block.RuleConstant; | |||
import com.alibaba.csp.sentinel.slots.clusterbuilder.ClusterBuilderSlot; | |||
import org.junit.Test; | |||
import java.util.concurrent.TimeUnit; | |||
import static org.junit.Assert.assertFalse; | |||
import static org.junit.Assert.assertTrue; | |||
import static org.mockito.Mockito.mock; | |||
import static org.mockito.Mockito.when; | |||
/** | |||
* @author jialiang.linjl | |||
*/ | |||
public class DegradeTest { | |||
@Test | |||
public void testAverageRtDegrade() throws InterruptedException { | |||
String key = "test_degrade_average_rt"; | |||
ClusterNode cn = mock(ClusterNode.class); | |||
ClusterBuilderSlot.getClusterNodeMap().put(new StringResourceWrapper(key, EntryType.IN), cn); | |||
Context context = mock(Context.class); | |||
DefaultNode node = mock(DefaultNode.class); | |||
when(node.getClusterNode()).thenReturn(cn); | |||
when(cn.avgRt()).thenReturn(2d); | |||
int rtSlowRequestAmount = 10; | |||
DegradeRule rule = new DegradeRule(); | |||
rule.setCount(1); | |||
rule.setResource(key); | |||
rule.setTimeWindow(2); | |||
rule.setGrade(RuleConstant.DEGRADE_GRADE_RT); | |||
rule.setRtSlowRequestAmount(rtSlowRequestAmount); | |||
//Will true | |||
for (int i = 0; i < rtSlowRequestAmount - 1; i++) { | |||
assertTrue(rule.passCheck(context, node, 1)); | |||
} | |||
// The third time will fail. | |||
assertFalse(rule.passCheck(context, node, 1)); | |||
assertFalse(rule.passCheck(context, node, 1)); | |||
// Restore. | |||
TimeUnit.MILLISECONDS.sleep(2200); | |||
assertTrue(rule.passCheck(context, node, 1)); | |||
} | |||
@Test | |||
public void testExceptionRatioModeDegrade() throws Throwable { | |||
String key = "test_degrade_exception_ratio"; | |||
ClusterNode cn = mock(ClusterNode.class); | |||
ClusterBuilderSlot.getClusterNodeMap().put(new StringResourceWrapper(key, EntryType.IN), cn); | |||
Context context = mock(Context.class); | |||
DefaultNode node = mock(DefaultNode.class); | |||
when(node.getClusterNode()).thenReturn(cn); | |||
DegradeRule rule = new DegradeRule(); | |||
rule.setCount(0.15); | |||
rule.setResource(key); | |||
rule.setTimeWindow(2); | |||
rule.setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_RATIO); | |||
rule.setMinRequestAmount(20); | |||
// Will true. While totalQps < minRequestAmount | |||
when(cn.totalQps()).thenReturn(8d); | |||
assertTrue(rule.passCheck(context, node, 1)); | |||
// Will true. | |||
when(cn.totalQps()).thenReturn(21d); | |||
when(cn.successQps()).thenReturn(9d); | |||
when(cn.exceptionQps()).thenReturn(9d); | |||
assertTrue(rule.passCheck(context, node, 1)); | |||
// Will true. While totalQps > minRequestAmount and exceptionRation < count | |||
when(cn.totalQps()).thenReturn(100d); | |||
when(cn.successQps()).thenReturn(90d); | |||
when(cn.exceptionQps()).thenReturn(10d); | |||
assertTrue(rule.passCheck(context, node, 1)); | |||
// Will fail. While totalQps > minRequestAmount and exceptionRation > count | |||
rule.setMinRequestAmount(5); | |||
when(cn.totalQps()).thenReturn(12d); | |||
when(cn.successQps()).thenReturn(8d); | |||
when(cn.exceptionQps()).thenReturn(6d); | |||
assertFalse(rule.passCheck(context, node, 1)); | |||
// Restore from the degrade timeout. | |||
TimeUnit.MILLISECONDS.sleep(2200); | |||
// Will pass. | |||
when(cn.totalQps()).thenReturn(106d); | |||
when(cn.successQps()).thenReturn(100d); | |||
assertTrue(rule.passCheck(context, node, 1)); | |||
} | |||
@Test | |||
public void testExceptionCountModeDegrade() throws Throwable { | |||
String key = "test_degrade_exception_count"; | |||
ClusterNode cn = mock(ClusterNode.class); | |||
when(cn.totalException()).thenReturn(10L); | |||
ClusterBuilderSlot.getClusterNodeMap().put(new StringResourceWrapper(key, EntryType.IN), cn); | |||
Context context = mock(Context.class); | |||
DefaultNode node = mock(DefaultNode.class); | |||
when(node.getClusterNode()).thenReturn(cn); | |||
DegradeRule rule = new DegradeRule(); | |||
rule.setCount(4); | |||
rule.setResource(key); | |||
rule.setTimeWindow(2); | |||
rule.setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_COUNT); | |||
when(cn.totalException()).thenReturn(4L); | |||
// Will fail. | |||
assertFalse(rule.passCheck(context, node, 1)); | |||
// Restore from the degrade timeout. | |||
TimeUnit.MILLISECONDS.sleep(2200); | |||
when(cn.totalException()).thenReturn(0L); | |||
// Will pass. | |||
assertTrue(rule.passCheck(context, node, 1)); | |||
} | |||
@Test | |||
public void testEquals() { | |||
DegradeRule degradeRule1 = new DegradeRule(); | |||
DegradeRule degradeRule2 = new DegradeRule(); | |||
assertTrue(degradeRule1.equals(degradeRule2)); | |||
int rtSlowRequestAmount = 10; | |||
int minRequestAmount = 20; | |||
double count = 1.0; | |||
int timeWindow = 2; | |||
degradeRule1.setRtSlowRequestAmount(rtSlowRequestAmount); | |||
degradeRule1.setMinRequestAmount(minRequestAmount); | |||
degradeRule1.setCount(count); | |||
degradeRule1.setTimeWindow(timeWindow); | |||
degradeRule1.setGrade(RuleConstant.DEGRADE_GRADE_RT); | |||
degradeRule2.setRtSlowRequestAmount(rtSlowRequestAmount); | |||
degradeRule2.setMinRequestAmount(minRequestAmount); | |||
degradeRule2.setCount(count); | |||
degradeRule2.setGrade(RuleConstant.DEGRADE_GRADE_RT); | |||
degradeRule2.setTimeWindow(timeWindow); | |||
assertTrue(degradeRule1.equals(degradeRule2)); | |||
degradeRule2.setMinRequestAmount(100); | |||
assertFalse(degradeRule1.equals(degradeRule2)); | |||
} | |||
} |
@@ -0,0 +1,111 @@ | |||
/* | |||
* Copyright 1999-2019 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.block.degrade.circuitbreaker; | |||
import com.alibaba.csp.sentinel.slots.block.RuleConstant; | |||
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule; | |||
import com.alibaba.csp.sentinel.slots.block.degrade.circuitbreaker.CircuitBreaker.State; | |||
import com.alibaba.csp.sentinel.slots.block.degrade.circuitbreaker.ExceptionCircuitBreaker.SimpleErrorCounter; | |||
import com.alibaba.csp.sentinel.slots.statistic.base.LeapArray; | |||
import com.alibaba.csp.sentinel.slots.statistic.base.WindowWrap; | |||
import com.alibaba.csp.sentinel.test.AbstractTimeBasedTest; | |||
import org.junit.Test; | |||
import static org.junit.Assert.*; | |||
import static org.mockito.Mockito.mock; | |||
import static org.mockito.Mockito.when; | |||
/** | |||
* @author Eric Zhao | |||
*/ | |||
public class ExceptionCircuitBreakerTest extends AbstractTimeBasedTest { | |||
@Test | |||
@SuppressWarnings("unchecked") | |||
public void testStateChangeAndTryAcquire() { | |||
int retryTimeout = 10; | |||
DegradeRule rule = new DegradeRule("abc") | |||
.setCount(0.5d) | |||
.setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_RATIO) | |||
.setStatIntervalMs(20 * 1000) | |||
.setTimeWindow(retryTimeout) | |||
.setMinRequestAmount(10); | |||
LeapArray<SimpleErrorCounter> stat = mock(LeapArray.class); | |||
SimpleErrorCounter counter = new SimpleErrorCounter(); | |||
WindowWrap<SimpleErrorCounter> bucket = new WindowWrap<>(20000, 0, counter); | |||
when(stat.currentWindow()).thenReturn(bucket); | |||
ExceptionCircuitBreaker cb = new ExceptionCircuitBreaker(rule, stat); | |||
assertTrue(cb.tryPass()); | |||
assertTrue(cb.tryPass()); | |||
setCurrentMillis(System.currentTimeMillis()); | |||
cb.fromCloseToOpen(0.52d); | |||
assertEquals(State.OPEN, cb.currentState()); | |||
assertFalse(cb.tryPass()); | |||
assertFalse(cb.tryPass()); | |||
// Wait for next retry checkpoint. | |||
sleepSecond(retryTimeout); | |||
sleep(100); | |||
// Try a request to trigger state transformation. | |||
assertTrue(cb.tryPass()); | |||
assertEquals(State.HALF_OPEN, cb.currentState()); | |||
// Mark this request as error | |||
cb.onRequestComplete(20, new IllegalArgumentException()); | |||
assertEquals(State.OPEN, cb.currentState()); | |||
// Wait for next retry checkpoint. | |||
sleepSecond(retryTimeout); | |||
sleep(100); | |||
assertTrue(cb.tryPass()); | |||
assertEquals(State.HALF_OPEN, cb.currentState()); | |||
setCurrentMillis(System.currentTimeMillis()); | |||
// Mark this request as success. | |||
cb.onRequestComplete(20, null); | |||
assertEquals(State.CLOSED, cb.currentState()); | |||
} | |||
@Test | |||
@SuppressWarnings("unchecked") | |||
public void testRecordErrorOrSuccess() { | |||
DegradeRule rule = new DegradeRule("abc") | |||
.setCount(0.5d) | |||
.setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_RATIO) | |||
.setStatIntervalMs(20 * 1000) | |||
.setTimeWindow(10) | |||
.setMinRequestAmount(10); | |||
LeapArray<SimpleErrorCounter> stat = mock(LeapArray.class); | |||
SimpleErrorCounter counter = new SimpleErrorCounter(); | |||
WindowWrap<SimpleErrorCounter> bucket = new WindowWrap<>(20000, 0, counter); | |||
when(stat.currentWindow()).thenReturn(bucket); | |||
CircuitBreaker cb = new ExceptionCircuitBreaker(rule, stat); | |||
cb.onRequestComplete(15, null); | |||
assertEquals(1L, counter.getTotalCount().longValue()); | |||
assertEquals(0L, counter.getErrorCount().longValue()); | |||
cb.onRequestComplete(15, new IllegalArgumentException()); | |||
assertEquals(2L, counter.getTotalCount().longValue()); | |||
assertEquals(1L, counter.getErrorCount().longValue()); | |||
} | |||
} |