diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/Constants.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/Constants.java index ed861a3c..913c83a7 100755 --- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/Constants.java +++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/Constants.java @@ -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() {} diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/degrade/DefaultCircuitBreakerRuleManager.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/degrade/DefaultCircuitBreakerRuleManager.java new file mode 100644 index 00000000..03d1ca8f --- /dev/null +++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/degrade/DefaultCircuitBreakerRuleManager.java @@ -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> circuitBreakers = new ConcurrentHashMap<>(); + + private static volatile Set rules = new HashSet<>(); + + /** + * Resources in this set will not be affected by default rules. + */ + private static final Set excludedResource = ConcurrentHashMap.newKeySet(); + + private static final DefaultCircuitBreakerRuleManager.RulePropertyListener LISTENER + = new DefaultCircuitBreakerRuleManager.RulePropertyListener(); + private static SentinelProperty> 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> 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 getDefaultCircuitBreakers(String resourceName) { + if (rules == null || rules.isEmpty()) { + return null; + } + List 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 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 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 getCircuitBreakers(String resourceName) { + return circuitBreakers.get(resourceName); + } + + private static class RulePropertyListener implements PropertyListener> { + + private synchronized void reloadFrom(List list) { + + if (list == null || list.isEmpty()) { + // clearing all rules + DefaultCircuitBreakerRuleManager.circuitBreakers = new ConcurrentHashMap<>(); + DefaultCircuitBreakerRuleManager.rules = new HashSet<>(); + return; + } + + Set rules = new HashSet(); + 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> cbMap = new ConcurrentHashMap>(8); + for (String resourceNameKey : DefaultCircuitBreakerRuleManager.circuitBreakers.keySet()) { + List cbs = new ArrayList(); + 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 conf) { + reloadFrom(conf); + RecordLog.info("[DefaultCircuitBreakerRuleManager] Default circuit breaker rules has been updated to: {}", + rules); + } + + @Override + public void configLoad(List conf) { + reloadFrom(conf); + RecordLog.info("[DefaultCircuitBreakerRuleManager] Default circuit breaker rules loaded: {}", rules); + } + } +} diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/degrade/DefaultDegradeSlot.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/degrade/DefaultCircuitBreakerSlot.java similarity index 62% rename from sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/degrade/DefaultDegradeSlot.java rename to sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/degrade/DefaultCircuitBreakerSlot.java index 4d2c97fc..1362c2b3 100644 --- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/degrade/DefaultDegradeSlot.java +++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/degrade/DefaultCircuitBreakerSlot.java @@ -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; /** + *

A {@link ProcessorSlot} dedicates to universal default circuit breaker.

+ * * @author wuwen + * @since 2.0.0 */ -@Spi(order = Constants.ORDER_DEGRADE_SLOT + 100) -public class DefaultDegradeSlot extends AbstractLinkedProcessorSlot { +@Spi(order = Constants.ORDER_DEFAULT_CIRCUIT_BREAKER_SLOT) +public class DefaultCircuitBreakerSlot extends AbstractLinkedProcessorSlot { @Override public void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count, @@ -27,12 +46,12 @@ public class DefaultDegradeSlot extends AbstractLinkedProcessorSlot } 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 circuitBreakers = DefaultDegradeRuleManager.getDefaultCircuitBreakers(r.getName()); + List circuitBreakers = DefaultCircuitBreakerRuleManager.getDefaultCircuitBreakers(r.getName()); if (circuitBreakers == null || circuitBreakers.isEmpty()) { return; @@ -58,9 +77,10 @@ public class DefaultDegradeSlot extends AbstractLinkedProcessorSlot return; } - List circuitBreakers = DefaultDegradeRuleManager.getDefaultCircuitBreakers(r.getName()); + List circuitBreakers = DefaultCircuitBreakerRuleManager.getDefaultCircuitBreakers(r.getName()); if (circuitBreakers == null || circuitBreakers.isEmpty()) { + fireExit(context, r, count, args); return; } diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/degrade/DefaultDegradeRuleManager.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/degrade/DefaultDegradeRuleManager.java deleted file mode 100644 index 06214c3d..00000000 --- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/degrade/DefaultDegradeRuleManager.java +++ /dev/null @@ -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> circuitBreakers = new ConcurrentHashMap<>(); - private static volatile Set rules = new HashSet<>(); - - private static final DefaultDegradeRuleManager.RulePropertyListener LISTENER = new DefaultDegradeRuleManager.RulePropertyListener(); - private static SentinelProperty> currentProperty - = new DynamicSentinelProperty<>(); - - static { - currentProperty.addListener(LISTENER); - } - - - static List getDefaultCircuitBreakers(String resourceName) { - List 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 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> { - - private synchronized void reloadFrom(List list) { - - if (list == null) { - return; - } - - Set rules = new HashSet<>(); - List 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> cbMap = new ConcurrentHashMap<>(8); - - DefaultDegradeRuleManager.circuitBreakers.forEach((k, v) -> cbMap.put(k, cbs)); - - DefaultDegradeRuleManager.rules = rules; - DefaultDegradeRuleManager.circuitBreakers = cbMap; - } - - @Override - public void configUpdate(List conf) { - reloadFrom(conf); - RecordLog.info("[DefaultDegradeRuleManager] Degrade rules has been updated to: {}", rules); - } - - @Override - public void configLoad(List conf) { - reloadFrom(conf); - RecordLog.info("[DefaultDegradeRuleManager] Degrade rules loaded: {}", rules); - } - } -} diff --git a/sentinel-core/src/main/resources/META-INF/services/com.alibaba.csp.sentinel.slotchain.ProcessorSlot b/sentinel-core/src/main/resources/META-INF/services/com.alibaba.csp.sentinel.slotchain.ProcessorSlot index 2e887a93..867777ce 100644 --- a/sentinel-core/src/main/resources/META-INF/services/com.alibaba.csp.sentinel.slotchain.ProcessorSlot +++ b/sentinel-core/src/main/resources/META-INF/services/com.alibaba.csp.sentinel.slotchain.ProcessorSlot @@ -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 \ No newline at end of file +com.alibaba.csp.sentinel.slots.block.degrade.DefaultCircuitBreakerSlot \ No newline at end of file diff --git a/sentinel-core/src/test/java/com/alibaba/csp/sentinel/slots/DefaultSlotChainBuilderTest.java b/sentinel-core/src/test/java/com/alibaba/csp/sentinel/slots/DefaultSlotChainBuilderTest.java index 489843f1..9527dc39 100644 --- a/sentinel-core/src/test/java/com/alibaba/csp/sentinel/slots/DefaultSlotChainBuilderTest.java +++ b/sentinel-core/src/test/java/com/alibaba/csp/sentinel/slots/DefaultSlotChainBuilderTest.java @@ -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); diff --git a/sentinel-core/src/test/java/com/alibaba/csp/sentinel/slots/block/degrade/CircuitBreakingIntegrationTest.java b/sentinel-core/src/test/java/com/alibaba/csp/sentinel/slots/block/degrade/CircuitBreakingIntegrationTest.java index 0716f6e5..ebf49077 100755 --- a/sentinel-core/src/test/java/com/alibaba/csp/sentinel/slots/block/degrade/CircuitBreakingIntegrationTest.java +++ b/sentinel-core/src/test/java/com/alibaba/csp/sentinel/slots/block/degrade/CircuitBreakingIntegrationTest.java @@ -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); diff --git a/sentinel-core/src/test/java/com/alibaba/csp/sentinel/slots/block/degrade/DefaultCircuitBreakerRuleManagerTest.java b/sentinel-core/src/test/java/com/alibaba/csp/sentinel/slots/block/degrade/DefaultCircuitBreakerRuleManagerTest.java new file mode 100644 index 00000000..13c9a605 --- /dev/null +++ b/sentinel-core/src/test/java/com/alibaba/csp/sentinel/slots/block/degrade/DefaultCircuitBreakerRuleManagerTest.java @@ -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 rules = new ArrayList(); + 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()); + DegradeRuleManager.loadRules(new ArrayList()); + } + + @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 defaultCircuitBreakers1 = DefaultCircuitBreakerRuleManager.getDefaultCircuitBreakers( + resourceName); + assertNotNull(defaultCircuitBreakers1); + + List defaultCircuitBreakers2 = DefaultCircuitBreakerRuleManager.getDefaultCircuitBreakers( + resourceName); + assertSame(defaultCircuitBreakers1, defaultCircuitBreakers2); + } + + @Test + public void testGetDefaultCircuitBreakersWhileAddingCustomizedRule() { + String resourceNameI = RESOURCE_NAME + "I"; + List rules = new ArrayList(); + 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 rules2 = new ArrayList(); + 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 rules = new ArrayList(); + 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 rules2 = new ArrayList(); + 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 ruleList = new ArrayList(); + ruleList.add(rule); + assertTrue(DefaultCircuitBreakerRuleManager.loadRules(ruleList)); + assertFalse(DefaultCircuitBreakerRuleManager.loadRules(ruleList)); + } + + @Test + public void testLoadRulesUseDifferentCircuitBreakers() throws Exception { + String resA = "resA"; + String resB = "resB"; + List cbsForResourceA = DefaultCircuitBreakerRuleManager.getDefaultCircuitBreakers(resA); + assertNotNull(cbsForResourceA); + List 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> cbs = (Map>) cbsField.get( + DefaultCircuitBreakerRuleManager.class); + assertEquals(2, cbs.size()); + + List rules = new ArrayList(); + 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> newCbs = (Map>) cbsField.get( + DefaultCircuitBreakerRuleManager.class); + assertEquals(2, newCbs.size()); + assertNotEquals(cbs, newCbs); + + List resACbs = newCbs.get(resA); + assertNotNull(resACbs); + List resBCbs = newCbs.get(resB); + assertNotNull(resBCbs); + assertNotEquals(resACbs, resBCbs); + } + +} diff --git a/sentinel-core/src/test/java/com/alibaba/csp/sentinel/slots/block/degrade/DefaultCircuitBreakerSlotTest.java b/sentinel-core/src/test/java/com/alibaba/csp/sentinel/slots/block/degrade/DefaultCircuitBreakerSlotTest.java new file mode 100644 index 00000000..74481d38 --- /dev/null +++ b/sentinel-core/src/test/java/com/alibaba/csp/sentinel/slots/block/degrade/DefaultCircuitBreakerSlotTest.java @@ -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 rules = new ArrayList(); + 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()); + } + + @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)); + } + +} diff --git a/sentinel-core/src/test/java/com/alibaba/csp/sentinel/spi/SpiLoaderTest.java b/sentinel-core/src/test/java/com/alibaba/csp/sentinel/spi/SpiLoaderTest.java index 6fde7524..2aa1c921 100644 --- a/sentinel-core/src/test/java/com/alibaba/csp/sentinel/spi/SpiLoaderTest.java +++ b/sentinel-core/src/test/java/com/alibaba/csp/sentinel/spi/SpiLoaderTest.java @@ -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()); } /**