Refactor and refine implementation of default circuit breaker rule

* Rename: DefaultDegradeSlot -> DefaultCircuitBreakerSlot
* Refine DefaultCircuitBreakerRuleManager
* Add test cases

Signed-off-by: Eric Zhao <sczyh16@gmail.com>
This commit is contained in:
Eric Zhao 2022-11-08 11:57:52 +08:00 committed by LearningGp
parent 8b43caede4
commit 19418e71ad
10 changed files with 569 additions and 154 deletions

View File

@ -80,6 +80,7 @@ public final class Constants {
public static final int ORDER_AUTHORITY_SLOT = -6000;
public static final int ORDER_SYSTEM_SLOT = -5000;
public static final int ORDER_FLOW_SLOT = -2000;
public static final int ORDER_DEFAULT_CIRCUIT_BREAKER_SLOT = -1500;
public static final int ORDER_DEGRADE_SLOT = -1000;
private Constants() {}

View File

@ -0,0 +1,229 @@
/*
* Copyright 1999-2022 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.log.RecordLog;
import com.alibaba.csp.sentinel.property.DynamicSentinelProperty;
import com.alibaba.csp.sentinel.property.PropertyListener;
import com.alibaba.csp.sentinel.property.SentinelProperty;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import com.alibaba.csp.sentinel.slots.block.degrade.circuitbreaker.CircuitBreaker;
import com.alibaba.csp.sentinel.slots.block.degrade.circuitbreaker.ExceptionCircuitBreaker;
import com.alibaba.csp.sentinel.slots.block.degrade.circuitbreaker.ResponseTimeCircuitBreaker;
import com.alibaba.csp.sentinel.util.AssertUtil;
import com.alibaba.csp.sentinel.util.StringUtil;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
/**
* The rule manager for universal default circuit breaker rule.
*
* @author wuwen
* @author Eric Zhao
* @since 2.0.0
*/
public final class DefaultCircuitBreakerRuleManager {
public static final String DEFAULT_KEY = "*";
private static volatile Map<String, List<CircuitBreaker>> circuitBreakers = new ConcurrentHashMap<>();
private static volatile Set<DegradeRule> rules = new HashSet<>();
/**
* Resources in this set will not be affected by default rules.
*/
private static final Set<String> excludedResource = ConcurrentHashMap.newKeySet();
private static final DefaultCircuitBreakerRuleManager.RulePropertyListener LISTENER
= new DefaultCircuitBreakerRuleManager.RulePropertyListener();
private static SentinelProperty<List<DegradeRule>> currentProperty = new DynamicSentinelProperty<>();
static {
currentProperty.addListener(LISTENER);
}
/**
* Listen to the {@link SentinelProperty} for default circuit breaker rules.
*
* @param property the property to listen.
*/
public static void register2Property(SentinelProperty<List<DegradeRule>> property) {
AssertUtil.notNull(property, "property cannot be null");
synchronized (LISTENER) {
RecordLog.info("Registering new property to DefaultCircuitBreakerRuleManager");
currentProperty.removeListener(LISTENER);
property.addListener(LISTENER);
currentProperty = property;
}
}
static List<CircuitBreaker> getDefaultCircuitBreakers(String resourceName) {
if (rules == null || rules.isEmpty()) {
return null;
}
List<CircuitBreaker> circuitBreakers = DefaultCircuitBreakerRuleManager.circuitBreakers.get(resourceName);
if (circuitBreakers == null && !rules.isEmpty() && !excludedResource.contains(resourceName)) {
circuitBreakers = new ArrayList<>();
for (DegradeRule rule : rules) {
circuitBreakers.add(DefaultCircuitBreakerRuleManager.newCircuitBreakerFrom(rule));
}
DefaultCircuitBreakerRuleManager.circuitBreakers.put(resourceName, circuitBreakers);
return circuitBreakers;
}
return circuitBreakers;
}
/**
* Exclude the resource that does not require default rules.
*
* @param resourceName the name of resource that does not require default rules
*/
public static void addExcludedResource(String resourceName) {
if (StringUtil.isEmpty(resourceName)) {
return;
}
excludedResource.add(resourceName);
}
public static void removeExcludedResource(String resourceName) {
if (StringUtil.isEmpty(resourceName)) {
return;
}
excludedResource.remove(resourceName);
}
public static void clearExcludedResource() {
excludedResource.clear();
}
/**
* Load default circuit breaker rules, former rules will be replaced.
*
* @param rules new rules to load.
*/
public static boolean loadRules(List<DegradeRule> rules) {
try {
return currentProperty.updateValue(rules);
} catch (Throwable e) {
RecordLog.error("[DefaultCircuitBreakerRuleManager] Unexpected error when loading default rules", e);
return false;
}
}
public static boolean isValidDefaultRule(DegradeRule rule) {
if (!DegradeRuleManager.isValidRule(rule)) {
return false;
}
return rule.getResource().equals(DEFAULT_KEY);
}
/**
* Create a circuit breaker instance from provided circuit breaking rule.
*
* @param rule a valid circuit breaking rule
* @return new circuit breaker based on provided rule; null if rule is invalid or unsupported type
*/
private static CircuitBreaker newCircuitBreakerFrom(/*@Valid*/ DegradeRule rule) {
switch (rule.getGrade()) {
case RuleConstant.DEGRADE_GRADE_RT:
return new ResponseTimeCircuitBreaker(rule);
case RuleConstant.DEGRADE_GRADE_EXCEPTION_RATIO:
case RuleConstant.DEGRADE_GRADE_EXCEPTION_COUNT:
return new ExceptionCircuitBreaker(rule);
default:
return null;
}
}
private static CircuitBreaker getExistingSameCbOrNew(/*@Valid*/ DegradeRule rule) {
List<CircuitBreaker> cbs = getCircuitBreakers(rule.getResource());
if (cbs == null || cbs.isEmpty()) {
return newCircuitBreakerFrom(rule);
}
for (CircuitBreaker cb : cbs) {
if (rule.equals(cb.getRule())) {
// Reuse the circuit breaker if the rule remains unchanged.
return cb;
}
}
return newCircuitBreakerFrom(rule);
}
static List<CircuitBreaker> getCircuitBreakers(String resourceName) {
return circuitBreakers.get(resourceName);
}
private static class RulePropertyListener implements PropertyListener<List<DegradeRule>> {
private synchronized void reloadFrom(List<DegradeRule> list) {
if (list == null || list.isEmpty()) {
// clearing all rules
DefaultCircuitBreakerRuleManager.circuitBreakers = new ConcurrentHashMap<>();
DefaultCircuitBreakerRuleManager.rules = new HashSet<>();
return;
}
Set<DegradeRule> rules = new HashSet<DegradeRule>();
for (DegradeRule rule : list) {
if (!isValidDefaultRule(rule)) {
RecordLog.warn(
"[DefaultCircuitBreakerRuleManager] Ignoring invalid rule when loading new rules: {}", rule);
} else {
if (StringUtil.isBlank(rule.getLimitApp())) {
rule.setLimitApp(RuleConstant.LIMIT_APP_DEFAULT);
}
// TODO: Set a special ID for default circuit breaker rule (so that it could be identified)
rules.add(rule);
}
}
Map<String, List<CircuitBreaker>> cbMap = new ConcurrentHashMap<String, List<CircuitBreaker>>(8);
for (String resourceNameKey : DefaultCircuitBreakerRuleManager.circuitBreakers.keySet()) {
List<CircuitBreaker> cbs = new ArrayList<CircuitBreaker>();
for (DegradeRule rule : rules) {
CircuitBreaker cb = getExistingSameCbOrNew(rule);
cbs.add(cb);
}
cbMap.put(resourceNameKey, cbs);
}
DefaultCircuitBreakerRuleManager.rules = rules;
DefaultCircuitBreakerRuleManager.circuitBreakers = cbMap;
}
@Override
public void configUpdate(List<DegradeRule> conf) {
reloadFrom(conf);
RecordLog.info("[DefaultCircuitBreakerRuleManager] Default circuit breaker rules has been updated to: {}",
rules);
}
@Override
public void configLoad(List<DegradeRule> conf) {
reloadFrom(conf);
RecordLog.info("[DefaultCircuitBreakerRuleManager] Default circuit breaker rules loaded: {}", rules);
}
}
}

View File

@ -1,3 +1,18 @@
/*
* Copyright 1999-2022 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.Constants;
@ -5,6 +20,7 @@ import com.alibaba.csp.sentinel.Entry;
import com.alibaba.csp.sentinel.context.Context;
import com.alibaba.csp.sentinel.node.DefaultNode;
import com.alibaba.csp.sentinel.slotchain.AbstractLinkedProcessorSlot;
import com.alibaba.csp.sentinel.slotchain.ProcessorSlot;
import com.alibaba.csp.sentinel.slotchain.ResourceWrapper;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.degrade.circuitbreaker.CircuitBreaker;
@ -13,10 +29,13 @@ import com.alibaba.csp.sentinel.spi.Spi;
import java.util.List;
/**
* <p>A {@link ProcessorSlot} dedicates to universal default circuit breaker.</p>
*
* @author wuwen
* @since 2.0.0
*/
@Spi(order = Constants.ORDER_DEGRADE_SLOT + 100)
public class DefaultDegradeSlot extends AbstractLinkedProcessorSlot<DefaultNode> {
@Spi(order = Constants.ORDER_DEFAULT_CIRCUIT_BREAKER_SLOT)
public class DefaultCircuitBreakerSlot extends AbstractLinkedProcessorSlot<DefaultNode> {
@Override
public void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count,
@ -27,12 +46,12 @@ public class DefaultDegradeSlot extends AbstractLinkedProcessorSlot<DefaultNode>
}
private void performChecking(Context context, ResourceWrapper r) throws BlockException {
// If user has set a degrade rule for the resource, the default rule will not be activated
if (DegradeRuleManager.hasConfig(r.getName())) {
return;
}
List<CircuitBreaker> circuitBreakers = DefaultDegradeRuleManager.getDefaultCircuitBreakers(r.getName());
List<CircuitBreaker> circuitBreakers = DefaultCircuitBreakerRuleManager.getDefaultCircuitBreakers(r.getName());
if (circuitBreakers == null || circuitBreakers.isEmpty()) {
return;
@ -58,9 +77,10 @@ public class DefaultDegradeSlot extends AbstractLinkedProcessorSlot<DefaultNode>
return;
}
List<CircuitBreaker> circuitBreakers = DefaultDegradeRuleManager.getDefaultCircuitBreakers(r.getName());
List<CircuitBreaker> circuitBreakers = DefaultCircuitBreakerRuleManager.getDefaultCircuitBreakers(r.getName());
if (circuitBreakers == null || circuitBreakers.isEmpty()) {
fireExit(context, r, count, args);
return;
}

View File

@ -1,134 +0,0 @@
package com.alibaba.csp.sentinel.slots.block.degrade;
import com.alibaba.csp.sentinel.log.RecordLog;
import com.alibaba.csp.sentinel.property.DynamicSentinelProperty;
import com.alibaba.csp.sentinel.property.PropertyListener;
import com.alibaba.csp.sentinel.property.SentinelProperty;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import com.alibaba.csp.sentinel.slots.block.degrade.circuitbreaker.CircuitBreaker;
import com.alibaba.csp.sentinel.slots.block.degrade.circuitbreaker.ExceptionCircuitBreaker;
import com.alibaba.csp.sentinel.slots.block.degrade.circuitbreaker.ResponseTimeCircuitBreaker;
import com.alibaba.csp.sentinel.util.StringUtil;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
/**
* @author wuwen
*/
public class DefaultDegradeRuleManager {
public static final String DEFAULT_KEY = "*";
private static volatile Map<String, List<CircuitBreaker>> circuitBreakers = new ConcurrentHashMap<>();
private static volatile Set<DegradeRule> rules = new HashSet<>();
private static final DefaultDegradeRuleManager.RulePropertyListener LISTENER = new DefaultDegradeRuleManager.RulePropertyListener();
private static SentinelProperty<List<DegradeRule>> currentProperty
= new DynamicSentinelProperty<>();
static {
currentProperty.addListener(LISTENER);
}
static List<CircuitBreaker> getDefaultCircuitBreakers(String resourceName) {
List<CircuitBreaker> circuitBreakers = DefaultDegradeRuleManager.circuitBreakers.get(resourceName);
if (circuitBreakers == null && !rules.isEmpty()) {
return DefaultDegradeRuleManager.circuitBreakers.computeIfAbsent(resourceName,
r -> rules.stream().map(DefaultDegradeRuleManager::newCircuitBreakerFrom).collect(Collectors.toList()));
}
return circuitBreakers;
}
/**
* Load {@link DegradeRule}s, former rules will be replaced.
*
* @param rules new rules to load.
*/
public static void loadRules(List<DegradeRule> rules) {
try {
currentProperty.updateValue(rules);
} catch (Throwable e) {
RecordLog.error("[DefaultDegradeRuleManager] Unexpected error when loading degrade rules", e);
}
}
public static boolean isValidDefaultRule(DegradeRule rule) {
if (!DegradeRuleManager.isValidRule(rule)) {
return false;
}
return rule.getResource().equals(DEFAULT_KEY);
}
/**
* Create a circuit breaker instance from provided circuit breaking rule.
*
* @param rule a valid circuit breaking rule
* @return new circuit breaker based on provided rule; null if rule is invalid or unsupported type
*/
private static CircuitBreaker newCircuitBreakerFrom(/*@Valid*/ DegradeRule rule) {
switch (rule.getGrade()) {
case RuleConstant.DEGRADE_GRADE_RT:
return new ResponseTimeCircuitBreaker(rule);
case RuleConstant.DEGRADE_GRADE_EXCEPTION_RATIO:
case RuleConstant.DEGRADE_GRADE_EXCEPTION_COUNT:
return new ExceptionCircuitBreaker(rule);
default:
return null;
}
}
private static class RulePropertyListener implements PropertyListener<List<DegradeRule>> {
private synchronized void reloadFrom(List<DegradeRule> list) {
if (list == null) {
return;
}
Set<DegradeRule> rules = new HashSet<>();
List<CircuitBreaker> cbs = new ArrayList<>();
for (DegradeRule rule : list) {
if (!isValidDefaultRule(rule)) {
RecordLog.warn("[DefaultDegradeRuleManager] Ignoring invalid rule when loading new rules: {}", rule);
} else {
if (StringUtil.isBlank(rule.getLimitApp())) {
rule.setLimitApp(RuleConstant.LIMIT_APP_DEFAULT);
}
CircuitBreaker cb = newCircuitBreakerFrom(rule);
cbs.add(cb);
rules.add(rule);
}
}
Map<String, List<CircuitBreaker>> cbMap = new ConcurrentHashMap<>(8);
DefaultDegradeRuleManager.circuitBreakers.forEach((k, v) -> cbMap.put(k, cbs));
DefaultDegradeRuleManager.rules = rules;
DefaultDegradeRuleManager.circuitBreakers = cbMap;
}
@Override
public void configUpdate(List<DegradeRule> conf) {
reloadFrom(conf);
RecordLog.info("[DefaultDegradeRuleManager] Degrade rules has been updated to: {}", rules);
}
@Override
public void configLoad(List<DegradeRule> conf) {
reloadFrom(conf);
RecordLog.info("[DefaultDegradeRuleManager] Degrade rules loaded: {}", rules);
}
}
}

View File

@ -7,4 +7,4 @@ com.alibaba.csp.sentinel.slots.block.authority.AuthoritySlot
com.alibaba.csp.sentinel.slots.system.SystemSlot
com.alibaba.csp.sentinel.slots.block.flow.FlowSlot
com.alibaba.csp.sentinel.slots.block.degrade.DegradeSlot
com.alibaba.csp.sentinel.slots.block.degrade.DefaultDegradeSlot
com.alibaba.csp.sentinel.slots.block.degrade.DefaultCircuitBreakerSlot

View File

@ -18,7 +18,7 @@ package com.alibaba.csp.sentinel.slots;
import com.alibaba.csp.sentinel.slotchain.AbstractLinkedProcessorSlot;
import com.alibaba.csp.sentinel.slotchain.ProcessorSlotChain;
import com.alibaba.csp.sentinel.slots.block.authority.AuthoritySlot;
import com.alibaba.csp.sentinel.slots.block.degrade.DefaultDegradeSlot;
import com.alibaba.csp.sentinel.slots.block.degrade.DefaultCircuitBreakerSlot;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeSlot;
import com.alibaba.csp.sentinel.slots.block.flow.FlowSlot;
import com.alibaba.csp.sentinel.slots.clusterbuilder.ClusterBuilderSlot;
@ -69,10 +69,10 @@ public class DefaultSlotChainBuilderTest {
assertTrue(next instanceof FlowSlot);
next = next.getNext();
assertTrue(next instanceof DegradeSlot);
assertTrue(next instanceof DefaultCircuitBreakerSlot);
next = next.getNext();
assertTrue(next instanceof DefaultDegradeSlot);
assertTrue(next instanceof DegradeSlot);
next = next.getNext();
assertNull(next);

View File

@ -50,7 +50,7 @@ public class CircuitBreakingIntegrationTest extends AbstractTimeBasedTest {
@After
public void tearDown() {
DegradeRuleManager.loadRules(new ArrayList<>());
DefaultDegradeRuleManager.loadRules(new ArrayList<>());
DefaultCircuitBreakerRuleManager.loadRules(new ArrayList<>());
}
@Test
@ -126,8 +126,8 @@ public class CircuitBreakingIntegrationTest extends AbstractTimeBasedTest {
String res = "CircuitBreakingIntegrationTest_testSlowRequestModeUseDefaultRule";
EventObserverRegistry.getInstance().addStateChangeObserver(res, observer);
DefaultDegradeRuleManager.loadRules(Arrays.asList(
new DegradeRule(DefaultDegradeRuleManager.DEFAULT_KEY).setTimeWindow(retryTimeoutSec).setCount(maxRt)
DefaultCircuitBreakerRuleManager.loadRules(Arrays.asList(
new DegradeRule(DefaultCircuitBreakerRuleManager.DEFAULT_KEY).setTimeWindow(retryTimeoutSec).setCount(maxRt)
.setStatIntervalMs(statIntervalMs).setMinRequestAmount(minRequestAmount)
.setSlowRatioThreshold(0.8d).setGrade(0)));
@ -150,7 +150,7 @@ public class CircuitBreakingIntegrationTest extends AbstractTimeBasedTest {
// Circuit breaker has transformed to OPEN since here.
verify(observer)
.onStateChange(eq(State.CLOSED), eq(State.OPEN), any(DegradeRule.class), anyDouble());
assertEquals(State.OPEN, DefaultDegradeRuleManager.getDefaultCircuitBreakers(res).get(0).currentState());
assertEquals(State.OPEN, DefaultCircuitBreakerRuleManager.getDefaultCircuitBreakers(res).get(0).currentState());
assertFalse(entryAndSleepFor(res, 1));
sleepSecond(1);

View File

@ -0,0 +1,228 @@
/*
* Copyright 1999-2022 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 java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import com.alibaba.csp.sentinel.slots.block.degrade.circuitbreaker.CircuitBreaker;
import com.alibaba.csp.sentinel.slots.block.degrade.circuitbreaker.CircuitBreakerStrategy;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
/**
* @author Eric Zhao
*/
public class DefaultCircuitBreakerRuleManagerTest {
private final String RESOURCE_NAME = "method_";
@Before
public void setUp() throws Exception {
List<DegradeRule> rules = new ArrayList<DegradeRule>();
DegradeRule rule = new DegradeRule(DefaultCircuitBreakerRuleManager.DEFAULT_KEY)
.setGrade(CircuitBreakerStrategy.SLOW_REQUEST_RATIO.getType())
.setCount(50)
.setTimeWindow(10)
.setSlowRatioThreshold(0.6)
.setMinRequestAmount(100)
.setStatIntervalMs(20000);
assertTrue(DegradeRuleManager.isValidRule(rule));
rules.add(rule);
DefaultCircuitBreakerRuleManager.loadRules(rules);
}
@After
public void tearDown() throws Exception {
DefaultCircuitBreakerRuleManager.loadRules(new ArrayList<DegradeRule>());
DegradeRuleManager.loadRules(new ArrayList<DegradeRule>());
}
@Test
public void testIsValidRule() {
DegradeRule rule1 = new DegradeRule("xx")
.setCount(0.1d)
.setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_RATIO)
.setTimeWindow(2);
DegradeRule rule2 = new DegradeRule(DefaultCircuitBreakerRuleManager.DEFAULT_KEY)
.setCount(0.1d)
.setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_RATIO)
.setTimeWindow(2);
assertFalse(DefaultCircuitBreakerRuleManager.isValidDefaultRule(rule1));
assertTrue(DefaultCircuitBreakerRuleManager.isValidDefaultRule(rule2));
}
@Test
public void testGetDefaultCircuitBreakers() {
String resourceName = RESOURCE_NAME + "I";
assertFalse(DegradeRuleManager.hasConfig(resourceName));
List<CircuitBreaker> defaultCircuitBreakers1 = DefaultCircuitBreakerRuleManager.getDefaultCircuitBreakers(
resourceName);
assertNotNull(defaultCircuitBreakers1);
List<CircuitBreaker> defaultCircuitBreakers2 = DefaultCircuitBreakerRuleManager.getDefaultCircuitBreakers(
resourceName);
assertSame(defaultCircuitBreakers1, defaultCircuitBreakers2);
}
@Test
public void testGetDefaultCircuitBreakersWhileAddingCustomizedRule() {
String resourceNameI = RESOURCE_NAME + "I";
List<DegradeRule> rules = new ArrayList<DegradeRule>();
DegradeRule rule = new DegradeRule(resourceNameI)
.setGrade(CircuitBreakerStrategy.SLOW_REQUEST_RATIO.getType())
.setCount(50)
.setTimeWindow(10)
.setSlowRatioThreshold(0.6)
.setMinRequestAmount(100)
.setStatIntervalMs(20000);
rules.add(rule);
DegradeRuleManager.loadRules(rules);
assertTrue(DegradeRuleManager.hasConfig(resourceNameI));
String resourceNameII = RESOURCE_NAME + "II";
List<DegradeRule> rules2 = new ArrayList<DegradeRule>();
DegradeRule rule2 = new DegradeRule(resourceNameII)
.setGrade(CircuitBreakerStrategy.SLOW_REQUEST_RATIO.getType())
.setCount(50)
.setTimeWindow(10)
.setSlowRatioThreshold(0.6)
.setMinRequestAmount(100)
.setStatIntervalMs(20000);
rules2.add(rule2);
DegradeRuleManager.loadRules(rules2);
assertFalse(DegradeRuleManager.hasConfig(resourceNameI));
assertNotNull(DefaultCircuitBreakerRuleManager.getDefaultCircuitBreakers(resourceNameI));
DegradeRuleManager.loadRules(rules);
DefaultCircuitBreakerRuleManager.addExcludedResource(resourceNameII);
assertFalse(DegradeRuleManager.hasConfig(resourceNameII));
assertNull(DefaultCircuitBreakerRuleManager.getDefaultCircuitBreakers(resourceNameII));
}
@Test
public void testGetDefaultCircuitBreakersWhileRemovingCustomizedRule() {
String resourceNameI = RESOURCE_NAME + "I";
DefaultCircuitBreakerRuleManager.addExcludedResource(resourceNameI);
List<DegradeRule> rules = new ArrayList<DegradeRule>();
DegradeRule rule = new DegradeRule(resourceNameI)
.setGrade(CircuitBreakerStrategy.SLOW_REQUEST_RATIO.getType())
.setCount(50)
.setTimeWindow(10)
.setSlowRatioThreshold(0.6)
.setMinRequestAmount(100)
.setStatIntervalMs(20000);
rules.add(rule);
DegradeRuleManager.loadRules(rules);
assertTrue(DegradeRuleManager.hasConfig(resourceNameI));
assertNull(DefaultCircuitBreakerRuleManager.getDefaultCircuitBreakers(resourceNameI));
//remove customized rule and do not recover default rule
List<DegradeRule> rules2 = new ArrayList<DegradeRule>();
DegradeRuleManager.loadRules(rules2);
assertFalse(DegradeRuleManager.hasConfig(resourceNameI));
assertNull(DefaultCircuitBreakerRuleManager.getDefaultCircuitBreakers(resourceNameI));
//recover default rule
DefaultCircuitBreakerRuleManager.removeExcludedResource(resourceNameI);
assertFalse(DegradeRuleManager.hasConfig(resourceNameI));
assertNotNull(DefaultCircuitBreakerRuleManager.getDefaultCircuitBreakers(resourceNameI));
}
@Test
public void testLoadRules() {
DegradeRule rule = mock(DegradeRule.class);
List<DegradeRule> ruleList = new ArrayList<DegradeRule>();
ruleList.add(rule);
assertTrue(DefaultCircuitBreakerRuleManager.loadRules(ruleList));
assertFalse(DefaultCircuitBreakerRuleManager.loadRules(ruleList));
}
@Test
public void testLoadRulesUseDifferentCircuitBreakers() throws Exception {
String resA = "resA";
String resB = "resB";
List<CircuitBreaker> cbsForResourceA = DefaultCircuitBreakerRuleManager.getDefaultCircuitBreakers(resA);
assertNotNull(cbsForResourceA);
List<CircuitBreaker> cbsForResourceB = DefaultCircuitBreakerRuleManager.getDefaultCircuitBreakers(resB);
assertNotNull(cbsForResourceB);
assertNotEquals(cbsForResourceA, cbsForResourceB);
Field cbsField;
try {
cbsField = DefaultCircuitBreakerRuleManager.class.getDeclaredField("circuitBreakers");
} catch (NoSuchFieldException e) {
e.printStackTrace();
throw new Exception();
}
cbsField.setAccessible(true);
Map<String, List<CircuitBreaker>> cbs = (Map<String, List<CircuitBreaker>>) cbsField.get(
DefaultCircuitBreakerRuleManager.class);
assertEquals(2, cbs.size());
List<DegradeRule> rules = new ArrayList<DegradeRule>();
DegradeRule rule = new DegradeRule(DefaultCircuitBreakerRuleManager.DEFAULT_KEY)
//rule is different in strategy
.setGrade(CircuitBreakerStrategy.ERROR_RATIO.getType())
.setCount(0.1d)
.setTimeWindow(10)
.setSlowRatioThreshold(0.6)
.setMinRequestAmount(100)
.setStatIntervalMs(20000);
assertTrue(DegradeRuleManager.isValidRule(rule));
rules.add(rule);
DefaultCircuitBreakerRuleManager.loadRules(rules);
try {
cbsField = DefaultCircuitBreakerRuleManager.class.getDeclaredField("circuitBreakers");
} catch (NoSuchFieldException e) {
e.printStackTrace();
throw new Exception();
}
cbsField.setAccessible(true);
Map<String, List<CircuitBreaker>> newCbs = (Map<String, List<CircuitBreaker>>) cbsField.get(
DefaultCircuitBreakerRuleManager.class);
assertEquals(2, newCbs.size());
assertNotEquals(cbs, newCbs);
List<CircuitBreaker> resACbs = newCbs.get(resA);
assertNotNull(resACbs);
List<CircuitBreaker> resBCbs = newCbs.get(resB);
assertNotNull(resBCbs);
assertNotEquals(resACbs, resBCbs);
}
}

View File

@ -0,0 +1,69 @@
/*
* Copyright 1999-2022 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 java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import com.alibaba.csp.sentinel.EntryType;
import com.alibaba.csp.sentinel.context.Context;
import com.alibaba.csp.sentinel.slotchain.ResourceWrapper;
import com.alibaba.csp.sentinel.slotchain.StringResourceWrapper;
import com.alibaba.csp.sentinel.slots.block.degrade.circuitbreaker.CircuitBreakerStrategy;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import static org.mockito.Mockito.mock;
/**
* @author Eric Zhao
*/
public class DefaultCircuitBreakerSlotTest {
@Before
public void setUp() throws Exception {
List<DegradeRule> rules = new ArrayList<DegradeRule>();
DegradeRule rule = new DegradeRule(DefaultCircuitBreakerRuleManager.DEFAULT_KEY)
.setGrade(CircuitBreakerStrategy.SLOW_REQUEST_RATIO.getType())
.setCount(50)
.setTimeWindow(10)
.setSlowRatioThreshold(0.6)
.setMinRequestAmount(100)
.setStatIntervalMs(20000);
rules.add(rule);
DefaultCircuitBreakerRuleManager.loadRules(rules);
}
@After
public void tearDown() throws Exception {
DefaultCircuitBreakerRuleManager.loadRules(new ArrayList<DegradeRule>());
}
@Test
public void testPerformChecking() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
DefaultCircuitBreakerSlot defaultCircuitBreakerSlot = mock(DefaultCircuitBreakerSlot.class);
Context context = mock(Context.class);
String resA = "resA";
Method pCMethod = DefaultCircuitBreakerSlot.class.getDeclaredMethod("performChecking", Context.class, ResourceWrapper.class);
pCMethod.setAccessible(true);
pCMethod.invoke(defaultCircuitBreakerSlot, context, new StringResourceWrapper(resA, EntryType.IN));
}
}

View File

@ -22,7 +22,7 @@ import com.alibaba.csp.sentinel.slotchain.ProcessorSlot;
import com.alibaba.csp.sentinel.slotchain.SlotChainBuilder;
import com.alibaba.csp.sentinel.slots.DefaultSlotChainBuilder;
import com.alibaba.csp.sentinel.slots.block.authority.AuthoritySlot;
import com.alibaba.csp.sentinel.slots.block.degrade.DefaultDegradeSlot;
import com.alibaba.csp.sentinel.slots.block.degrade.DefaultCircuitBreakerSlot;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeSlot;
import com.alibaba.csp.sentinel.slots.block.flow.FlowSlot;
import com.alibaba.csp.sentinel.slots.clusterbuilder.ClusterBuilderSlot;
@ -30,6 +30,7 @@ import com.alibaba.csp.sentinel.slots.logger.LogSlot;
import com.alibaba.csp.sentinel.slots.nodeselector.NodeSelectorSlot;
import com.alibaba.csp.sentinel.slots.statistic.StatisticSlot;
import com.alibaba.csp.sentinel.slots.system.SystemSlot;
import org.junit.Before;
import org.junit.Test;
@ -106,7 +107,7 @@ public class SpiLoaderTest {
singletonSlotClasses.add(SystemSlot.class);
singletonSlotClasses.add(FlowSlot.class);
singletonSlotClasses.add(DegradeSlot.class);
singletonSlotClasses.add(DefaultDegradeSlot.class);
singletonSlotClasses.add(DefaultCircuitBreakerSlot.class);
for (int i = 0; i < slots1.size(); i++) {
ProcessorSlot slot1 = slots1.get(i);
@ -161,8 +162,8 @@ public class SpiLoaderTest {
assertTrue(sortedSlots.get(index++) instanceof AuthoritySlot);
assertTrue(sortedSlots.get(index++) instanceof SystemSlot);
assertTrue(sortedSlots.get(index++) instanceof FlowSlot);
assertTrue(sortedSlots.get(index++) instanceof DefaultCircuitBreakerSlot);
assertTrue(sortedSlots.get(index++) instanceof DegradeSlot);
assertTrue(sortedSlots.get(index++) instanceof DefaultDegradeSlot);
}
@Test
@ -179,8 +180,8 @@ public class SpiLoaderTest {
ProcessorSlot slot = SpiLoader.of(ProcessorSlot.class).loadLowestPriorityInstance();
assertNotNull(slot);
// NodeSelectorSlot is lowest order priority with @Spi(order = -1000) among all slots
assertTrue(slot instanceof DefaultDegradeSlot);
// DegradeSlot is lowest order priority with @Spi(order = -1000) among all slots
assertTrue(slot instanceof DegradeSlot);
}
@Test
@ -221,7 +222,8 @@ public class SpiLoaderTest {
@Test
public void testLoadInstanceByAliasName() {
ProcessorSlot slot = SpiLoader.of(ProcessorSlot.class).loadInstance("com.alibaba.csp.sentinel.slots.statistic.StatisticSlot");
ProcessorSlot slot = SpiLoader.of(ProcessorSlot.class).loadInstance(
"com.alibaba.csp.sentinel.slots.statistic.StatisticSlot");
assertNotNull(slot);
assertTrue(slot instanceof StatisticSlot);
}
@ -230,7 +232,7 @@ public class SpiLoaderTest {
public void testToString() {
SpiLoader spiLoader = SpiLoader.of(ProcessorSlot.class);
assertEquals("com.alibaba.csp.sentinel.spi.SpiLoader[com.alibaba.csp.sentinel.slotchain.ProcessorSlot]"
, spiLoader.toString());
, spiLoader.toString());
}
/**