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:
parent
8b43caede4
commit
19418e71ad
|
|
@ -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() {}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue