Resource rules (flow/degrade/param/authority) support regex matching (#3251)
This commit is contained in:
parent
2e18c3e916
commit
c4ed9a3aba
|
|
@ -15,6 +15,8 @@
|
||||||
*/
|
*/
|
||||||
package com.alibaba.csp.sentinel.slots.block;
|
package com.alibaba.csp.sentinel.slots.block;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Abstract rule entity.
|
* Abstract rule entity.
|
||||||
*
|
*
|
||||||
|
|
@ -44,6 +46,11 @@ public abstract class AbstractRule implements Rule {
|
||||||
*/
|
*/
|
||||||
private String limitApp;
|
private String limitApp;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to match resource names according to regular rules
|
||||||
|
*/
|
||||||
|
private boolean regex;
|
||||||
|
|
||||||
public Long getId() {
|
public Long getId() {
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
@ -72,6 +79,15 @@ public abstract class AbstractRule implements Rule {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isRegex() {
|
||||||
|
return regex;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AbstractRule setRegex(boolean regex) {
|
||||||
|
this.regex = regex;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object o) {
|
public boolean equals(Object o) {
|
||||||
if (this == o) {
|
if (this == o) {
|
||||||
|
|
@ -83,7 +99,10 @@ public abstract class AbstractRule implements Rule {
|
||||||
|
|
||||||
AbstractRule that = (AbstractRule)o;
|
AbstractRule that = (AbstractRule)o;
|
||||||
|
|
||||||
if (resource != null ? !resource.equals(that.resource) : that.resource != null) {
|
if (!Objects.equals(resource, that.resource)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (regex != that.regex) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!limitAppEquals(limitApp, that.limitApp)) {
|
if (!limitAppEquals(limitApp, that.limitApp)) {
|
||||||
|
|
@ -114,6 +133,7 @@ public abstract class AbstractRule implements Rule {
|
||||||
if (!("".equals(limitApp) || RuleConstant.LIMIT_APP_DEFAULT.equals(limitApp) || limitApp == null)) {
|
if (!("".equals(limitApp) || RuleConstant.LIMIT_APP_DEFAULT.equals(limitApp) || limitApp == null)) {
|
||||||
result = 31 * result + limitApp.hashCode();
|
result = 31 * result + limitApp.hashCode();
|
||||||
}
|
}
|
||||||
|
result = 31 * result + (regex ? 1 : 0);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,186 @@
|
||||||
|
/*
|
||||||
|
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.alibaba.csp.sentinel.slots.block;
|
||||||
|
|
||||||
|
import com.alibaba.csp.sentinel.util.function.Function;
|
||||||
|
import com.alibaba.csp.sentinel.util.function.Predicate;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unified rule management tool, mainly used for matching and caching of regular rules and simple rules.
|
||||||
|
* @author quguai
|
||||||
|
* @date 2023/10/9 20:35
|
||||||
|
*/
|
||||||
|
public class RuleManager<R> {
|
||||||
|
|
||||||
|
private Map<String, List<R>> originalRules = new HashMap<>();
|
||||||
|
private Map<Pattern, List<R>> regexRules = new HashMap<>();
|
||||||
|
private Map<String, List<R>> regexCacheRules = new HashMap<>();
|
||||||
|
private Map<String, List<R>> simpleRules = new HashMap<>();
|
||||||
|
private Function<List<R>, List<R>> generator = Function.identity();
|
||||||
|
|
||||||
|
private final Predicate<R> predicate;
|
||||||
|
|
||||||
|
public RuleManager() {
|
||||||
|
predicate = r -> r instanceof AbstractRule && ((AbstractRule) r).isRegex();
|
||||||
|
}
|
||||||
|
|
||||||
|
public RuleManager(Function<List<R>, List<R>> generator, Predicate<R> predicate) {
|
||||||
|
this.generator = generator;
|
||||||
|
this.predicate = predicate;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update rules from datasource, split rules map by regex,
|
||||||
|
* rebuild the regex rule cache to reduce the performance loss caused by publish rules.
|
||||||
|
*
|
||||||
|
* @param rulesMap origin rules map
|
||||||
|
*/
|
||||||
|
public void updateRules(Map<String, List<R>> rulesMap) {
|
||||||
|
originalRules = rulesMap;
|
||||||
|
Map<Pattern, List<R>> regexRules = new HashMap<>();
|
||||||
|
Map<String, List<R>> simpleRules = new HashMap<>();
|
||||||
|
for (Map.Entry<String, List<R>> entry : rulesMap.entrySet()) {
|
||||||
|
String resource = entry.getKey();
|
||||||
|
List<R> rules = entry.getValue();
|
||||||
|
|
||||||
|
List<R> rulesOfSimple = new ArrayList<>();
|
||||||
|
List<R> rulesOfRegex = new ArrayList<>();
|
||||||
|
for (R rule : rules) {
|
||||||
|
if (predicate.test(rule)) {
|
||||||
|
rulesOfRegex.add(rule);
|
||||||
|
} else {
|
||||||
|
rulesOfSimple.add(rule);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!rulesOfRegex.isEmpty()) {
|
||||||
|
regexRules.put(Pattern.compile(resource), rulesOfRegex);
|
||||||
|
}
|
||||||
|
if (!rulesOfSimple.isEmpty()) {
|
||||||
|
simpleRules.put(resource, rulesOfSimple);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// rebuild regex cache rules
|
||||||
|
setRules(regexRules, simpleRules);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get rules by resource name, save the rule list after regular matching to improve performance
|
||||||
|
* @param resource resource name
|
||||||
|
* @return matching rule list
|
||||||
|
*/
|
||||||
|
public List<R> getRules(String resource) {
|
||||||
|
List<R> result = new ArrayList<>(simpleRules.getOrDefault(resource, Collections.emptyList()));
|
||||||
|
if (regexRules.isEmpty()) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
if (regexCacheRules.containsKey(resource)) {
|
||||||
|
result.addAll(regexCacheRules.get(resource));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
synchronized (this) {
|
||||||
|
if (regexCacheRules.containsKey(resource)) {
|
||||||
|
result.addAll(regexCacheRules.get(resource));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
List<R> compilers = matcherFromRegexRules(resource);
|
||||||
|
regexCacheRules.put(resource, compilers);
|
||||||
|
result.addAll(compilers);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get rules from regex rules and simple rules
|
||||||
|
* @return rule list
|
||||||
|
*/
|
||||||
|
public List<R> getRules() {
|
||||||
|
List<R> rules = new ArrayList<>();
|
||||||
|
for (Map.Entry<Pattern, List<R>> entry : regexRules.entrySet()) {
|
||||||
|
rules.addAll(entry.getValue());
|
||||||
|
}
|
||||||
|
for (Map.Entry<String, List<R>> entry : simpleRules.entrySet()) {
|
||||||
|
rules.addAll(entry.getValue());
|
||||||
|
}
|
||||||
|
return rules;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get origin rules, includes regex and simple rules
|
||||||
|
* @return original rules
|
||||||
|
*/
|
||||||
|
public Map<String, List<R>> getOriginalRules() {
|
||||||
|
return originalRules;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine whether has rule based on the resource name
|
||||||
|
* @param resource resource name
|
||||||
|
* @return whether
|
||||||
|
*/
|
||||||
|
|
||||||
|
public boolean hasConfig(String resource) {
|
||||||
|
if (resource == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return !getRules(resource).isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is valid regex rules
|
||||||
|
* @param rule rule
|
||||||
|
* @return weather valid regex rule
|
||||||
|
*/
|
||||||
|
public static boolean checkRegexResourceField(AbstractRule rule) {
|
||||||
|
if (!rule.isRegex()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
String resourceName = rule.getResource();
|
||||||
|
try {
|
||||||
|
Pattern.compile(resourceName);
|
||||||
|
return true;
|
||||||
|
} catch (Exception e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<R> matcherFromRegexRules(String resource) {
|
||||||
|
List<R> compilers = new ArrayList<>();
|
||||||
|
for (Map.Entry<Pattern, List<R>> entry : regexRules.entrySet()) {
|
||||||
|
if (entry.getKey().matcher(resource).matches()) {
|
||||||
|
compilers.addAll(generator.apply(entry.getValue()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return compilers;
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized void setRules(Map<Pattern, List<R>> regexRules, Map<String, List<R>> simpleRules) {
|
||||||
|
this.regexRules = regexRules;
|
||||||
|
this.simpleRules = simpleRules;
|
||||||
|
if (regexRules.isEmpty()) {
|
||||||
|
this.regexCacheRules = Collections.emptyMap();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// rebuild from regex cache rules
|
||||||
|
Map<String, List<R>> rebuildCacheRule = new HashMap<>(regexCacheRules.size());
|
||||||
|
for (String resource : regexCacheRules.keySet()) {
|
||||||
|
rebuildCacheRule.put(resource, matcherFromRegexRules(resource));
|
||||||
|
}
|
||||||
|
this.regexCacheRules = rebuildCacheRule;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -24,6 +24,7 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
import com.alibaba.csp.sentinel.log.RecordLog;
|
import com.alibaba.csp.sentinel.log.RecordLog;
|
||||||
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
|
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
|
||||||
|
import com.alibaba.csp.sentinel.slots.block.RuleManager;
|
||||||
import com.alibaba.csp.sentinel.util.AssertUtil;
|
import com.alibaba.csp.sentinel.util.AssertUtil;
|
||||||
import com.alibaba.csp.sentinel.util.StringUtil;
|
import com.alibaba.csp.sentinel.util.StringUtil;
|
||||||
import com.alibaba.csp.sentinel.property.DynamicSentinelProperty;
|
import com.alibaba.csp.sentinel.property.DynamicSentinelProperty;
|
||||||
|
|
@ -39,7 +40,7 @@ import com.alibaba.csp.sentinel.property.SentinelProperty;
|
||||||
*/
|
*/
|
||||||
public final class AuthorityRuleManager {
|
public final class AuthorityRuleManager {
|
||||||
|
|
||||||
private static volatile Map<String, Set<AuthorityRule>> authorityRules = new ConcurrentHashMap<>();
|
private static volatile RuleManager<AuthorityRule> authorityRules = new RuleManager<>();
|
||||||
|
|
||||||
private static final RulePropertyListener LISTENER = new RulePropertyListener();
|
private static final RulePropertyListener LISTENER = new RulePropertyListener();
|
||||||
private static SentinelProperty<List<AuthorityRule>> currentProperty = new DynamicSentinelProperty<>();
|
private static SentinelProperty<List<AuthorityRule>> currentProperty = new DynamicSentinelProperty<>();
|
||||||
|
|
@ -70,7 +71,7 @@ public final class AuthorityRuleManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean hasConfig(String resource) {
|
public static boolean hasConfig(String resource) {
|
||||||
return authorityRules.containsKey(resource);
|
return authorityRules.hasConfig(resource);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -79,34 +80,27 @@ public final class AuthorityRuleManager {
|
||||||
* @return a new copy of the rules.
|
* @return a new copy of the rules.
|
||||||
*/
|
*/
|
||||||
public static List<AuthorityRule> getRules() {
|
public static List<AuthorityRule> getRules() {
|
||||||
List<AuthorityRule> rules = new ArrayList<>();
|
return authorityRules.getRules();
|
||||||
if (authorityRules == null) {
|
|
||||||
return rules;
|
|
||||||
}
|
|
||||||
for (Map.Entry<String, Set<AuthorityRule>> entry : authorityRules.entrySet()) {
|
|
||||||
rules.addAll(entry.getValue());
|
|
||||||
}
|
|
||||||
return rules;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class RulePropertyListener implements PropertyListener<List<AuthorityRule>> {
|
private static class RulePropertyListener implements PropertyListener<List<AuthorityRule>> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public synchronized void configLoad(List<AuthorityRule> value) {
|
public synchronized void configLoad(List<AuthorityRule> value) {
|
||||||
authorityRules = loadAuthorityConf(value);
|
authorityRules.updateRules(loadAuthorityConf(value));
|
||||||
|
|
||||||
RecordLog.info("[AuthorityRuleManager] Authority rules loaded: {}", authorityRules);
|
RecordLog.info("[AuthorityRuleManager] Authority rules loaded: {}", authorityRules);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public synchronized void configUpdate(List<AuthorityRule> conf) {
|
public synchronized void configUpdate(List<AuthorityRule> conf) {
|
||||||
authorityRules = loadAuthorityConf(conf);
|
authorityRules.updateRules(loadAuthorityConf(conf));
|
||||||
|
|
||||||
RecordLog.info("[AuthorityRuleManager] Authority rules received: {}", authorityRules);
|
RecordLog.info("[AuthorityRuleManager] Authority rules received: {}", authorityRules);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map<String, Set<AuthorityRule>> loadAuthorityConf(List<AuthorityRule> list) {
|
private Map<String, List<AuthorityRule>> loadAuthorityConf(List<AuthorityRule> list) {
|
||||||
Map<String, Set<AuthorityRule>> newRuleMap = new ConcurrentHashMap<>();
|
Map<String, List<AuthorityRule>> newRuleMap = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
if (list == null || list.isEmpty()) {
|
if (list == null || list.isEmpty()) {
|
||||||
return newRuleMap;
|
return newRuleMap;
|
||||||
|
|
@ -123,10 +117,10 @@ public final class AuthorityRuleManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
String identity = rule.getResource();
|
String identity = rule.getResource();
|
||||||
Set<AuthorityRule> ruleSet = newRuleMap.get(identity);
|
List<AuthorityRule> ruleSet = newRuleMap.get(identity);
|
||||||
// putIfAbsent
|
// putIfAbsent
|
||||||
if (ruleSet == null) {
|
if (ruleSet == null) {
|
||||||
ruleSet = new HashSet<>();
|
ruleSet = new ArrayList<>();
|
||||||
ruleSet.add(rule);
|
ruleSet.add(rule);
|
||||||
newRuleMap.put(identity, ruleSet);
|
newRuleMap.put(identity, ruleSet);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -140,12 +134,12 @@ public final class AuthorityRuleManager {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static Map<String, Set<AuthorityRule>> getAuthorityRules() {
|
static List<AuthorityRule> getRules(String resource) {
|
||||||
return authorityRules;
|
return authorityRules.getRules(resource);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isValidRule(AuthorityRule rule) {
|
public static boolean isValidRule(AuthorityRule rule) {
|
||||||
return rule != null && !StringUtil.isBlank(rule.getResource())
|
return rule != null && !StringUtil.isBlank(rule.getResource())
|
||||||
&& rule.getStrategy() >= 0 && StringUtil.isNotBlank(rule.getLimitApp());
|
&& rule.getStrategy() >= 0 && StringUtil.isNotBlank(rule.getLimitApp()) && RuleManager.checkRegexResourceField(rule);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,8 +15,7 @@
|
||||||
*/
|
*/
|
||||||
package com.alibaba.csp.sentinel.slots.block.authority;
|
package com.alibaba.csp.sentinel.slots.block.authority;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.List;
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import com.alibaba.csp.sentinel.Constants;
|
import com.alibaba.csp.sentinel.Constants;
|
||||||
import com.alibaba.csp.sentinel.context.Context;
|
import com.alibaba.csp.sentinel.context.Context;
|
||||||
|
|
@ -48,13 +47,8 @@ public class AuthoritySlot extends AbstractLinkedProcessorSlot<DefaultNode> {
|
||||||
}
|
}
|
||||||
|
|
||||||
void checkBlackWhiteAuthority(ResourceWrapper resource, Context context) throws AuthorityException {
|
void checkBlackWhiteAuthority(ResourceWrapper resource, Context context) throws AuthorityException {
|
||||||
Map<String, Set<AuthorityRule>> authorityRules = AuthorityRuleManager.getAuthorityRules();
|
|
||||||
|
|
||||||
if (authorityRules == null) {
|
List<AuthorityRule> rules = AuthorityRuleManager.getRules(resource.getName());
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Set<AuthorityRule> rules = authorityRules.get(resource.getName());
|
|
||||||
if (rules == null) {
|
if (rules == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,12 +21,14 @@ import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import com.alibaba.csp.sentinel.log.RecordLog;
|
import com.alibaba.csp.sentinel.log.RecordLog;
|
||||||
import com.alibaba.csp.sentinel.property.DynamicSentinelProperty;
|
import com.alibaba.csp.sentinel.property.DynamicSentinelProperty;
|
||||||
import com.alibaba.csp.sentinel.property.PropertyListener;
|
import com.alibaba.csp.sentinel.property.PropertyListener;
|
||||||
import com.alibaba.csp.sentinel.property.SentinelProperty;
|
import com.alibaba.csp.sentinel.property.SentinelProperty;
|
||||||
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
|
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
|
||||||
|
import com.alibaba.csp.sentinel.slots.block.RuleManager;
|
||||||
import com.alibaba.csp.sentinel.slots.block.degrade.circuitbreaker.CircuitBreaker;
|
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.ExceptionCircuitBreaker;
|
||||||
import com.alibaba.csp.sentinel.slots.block.degrade.circuitbreaker.ResponseTimeCircuitBreaker;
|
import com.alibaba.csp.sentinel.slots.block.degrade.circuitbreaker.ResponseTimeCircuitBreaker;
|
||||||
|
|
@ -42,8 +44,8 @@ import com.alibaba.csp.sentinel.util.StringUtil;
|
||||||
*/
|
*/
|
||||||
public final class DegradeRuleManager {
|
public final class DegradeRuleManager {
|
||||||
|
|
||||||
private static volatile Map<String, List<CircuitBreaker>> circuitBreakers = new HashMap<>();
|
private static volatile RuleManager<CircuitBreaker> circuitBreakers = new RuleManager<>(DegradeRuleManager::generateCbs, cb -> cb.getRule().isRegex());
|
||||||
private static volatile Map<String, Set<DegradeRule>> ruleMap = new HashMap<>();
|
private static volatile RuleManager<DegradeRule> ruleMap = new RuleManager<>();
|
||||||
|
|
||||||
private static final RulePropertyListener LISTENER = new RulePropertyListener();
|
private static final RulePropertyListener LISTENER = new RulePropertyListener();
|
||||||
private static SentinelProperty<List<DegradeRule>> currentProperty
|
private static SentinelProperty<List<DegradeRule>> currentProperty
|
||||||
|
|
@ -70,14 +72,11 @@ public final class DegradeRuleManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
static List<CircuitBreaker> getCircuitBreakers(String resourceName) {
|
static List<CircuitBreaker> getCircuitBreakers(String resourceName) {
|
||||||
return circuitBreakers.get(resourceName);
|
return circuitBreakers.getRules(resourceName);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean hasConfig(String resource) {
|
public static boolean hasConfig(String resource) {
|
||||||
if (resource == null) {
|
return circuitBreakers.hasConfig(resource);
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return circuitBreakers.containsKey(resource);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -88,16 +87,11 @@ public final class DegradeRuleManager {
|
||||||
* @return list of existing circuit breaking rules, or empty list if no rules were loaded
|
* @return list of existing circuit breaking rules, or empty list if no rules were loaded
|
||||||
*/
|
*/
|
||||||
public static List<DegradeRule> getRules() {
|
public static List<DegradeRule> getRules() {
|
||||||
List<DegradeRule> rules = new ArrayList<>();
|
return ruleMap.getRules();
|
||||||
for (Map.Entry<String, Set<DegradeRule>> entry : ruleMap.entrySet()) {
|
|
||||||
rules.addAll(entry.getValue());
|
|
||||||
}
|
|
||||||
return rules;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Set<DegradeRule> getRulesOfResource(String resource) {
|
public static Set<DegradeRule> getRulesOfResource(String resource) {
|
||||||
AssertUtil.assertNotBlank(resource, "resource name cannot be blank");
|
AssertUtil.assertNotBlank(resource, "resource name cannot be blank");
|
||||||
return ruleMap.get(resource);
|
return new HashSet<>(ruleMap.getRules(resource));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -124,7 +118,7 @@ public final class DegradeRuleManager {
|
||||||
public static boolean setRulesForResource(String resourceName, Set<DegradeRule> rules) {
|
public static boolean setRulesForResource(String resourceName, Set<DegradeRule> rules) {
|
||||||
AssertUtil.notEmpty(resourceName, "resourceName cannot be empty");
|
AssertUtil.notEmpty(resourceName, "resourceName cannot be empty");
|
||||||
try {
|
try {
|
||||||
Map<String, Set<DegradeRule>> newRuleMap = new HashMap<>(ruleMap);
|
Map<String, List<DegradeRule>> newRuleMap = ruleMap.getOriginalRules();
|
||||||
if (rules == null) {
|
if (rules == null) {
|
||||||
newRuleMap.remove(resourceName);
|
newRuleMap.remove(resourceName);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -134,10 +128,10 @@ public final class DegradeRuleManager {
|
||||||
newSet.add(rule);
|
newSet.add(rule);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
newRuleMap.put(resourceName, newSet);
|
newRuleMap.put(resourceName, new ArrayList<>(newSet));
|
||||||
}
|
}
|
||||||
List<DegradeRule> allRules = new ArrayList<>();
|
List<DegradeRule> allRules = new ArrayList<>();
|
||||||
for (Set<DegradeRule> set : newRuleMap.values()) {
|
for (List<DegradeRule> set : newRuleMap.values()) {
|
||||||
allRules.addAll(set);
|
allRules.addAll(set);
|
||||||
}
|
}
|
||||||
return currentProperty.updateValue(allRules);
|
return currentProperty.updateValue(allRules);
|
||||||
|
|
@ -189,6 +183,9 @@ public final class DegradeRuleManager {
|
||||||
if (rule.getMinRequestAmount() <= 0 || rule.getStatIntervalMs() <= 0) {
|
if (rule.getMinRequestAmount() <= 0 || rule.getStatIntervalMs() <= 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (!RuleManager.checkRegexResourceField(rule)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
switch (rule.getGrade()) {
|
switch (rule.getGrade()) {
|
||||||
case RuleConstant.DEGRADE_GRADE_RT:
|
case RuleConstant.DEGRADE_GRADE_RT:
|
||||||
return rule.getSlowRatioThreshold() >= 0 && rule.getSlowRatioThreshold() <= 1;
|
return rule.getSlowRatioThreshold() >= 0 && rule.getSlowRatioThreshold() <= 1;
|
||||||
|
|
@ -201,24 +198,17 @@ public final class DegradeRuleManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static List<CircuitBreaker> generateCbs(List<CircuitBreaker> cbs) {
|
||||||
|
return cbs.stream().map(cb -> newCircuitBreakerFrom(cb.getRule())).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
private static class RulePropertyListener implements PropertyListener<List<DegradeRule>> {
|
private static class RulePropertyListener implements PropertyListener<List<DegradeRule>> {
|
||||||
|
|
||||||
private synchronized void reloadFrom(List<DegradeRule> list) {
|
private synchronized void reloadFrom(List<DegradeRule> list) {
|
||||||
Map<String, List<CircuitBreaker>> cbs = buildCircuitBreakers(list);
|
Map<String, List<CircuitBreaker>> cbs = buildCircuitBreakers(list);
|
||||||
Map<String, Set<DegradeRule>> rm = new HashMap<>(cbs.size());
|
Map<String, List<DegradeRule>> rules = buildCircuitBreakerRules(cbs);
|
||||||
|
circuitBreakers.updateRules(cbs);
|
||||||
for (Map.Entry<String, List<CircuitBreaker>> e : cbs.entrySet()) {
|
ruleMap.updateRules(rules);
|
||||||
assert e.getValue() != null && !e.getValue().isEmpty();
|
|
||||||
|
|
||||||
Set<DegradeRule> rules = new HashSet<>(e.getValue().size());
|
|
||||||
for (CircuitBreaker cb : e.getValue()) {
|
|
||||||
rules.add(cb.getRule());
|
|
||||||
}
|
|
||||||
rm.put(e.getKey(), rules);
|
|
||||||
}
|
|
||||||
|
|
||||||
DegradeRuleManager.circuitBreakers = cbs;
|
|
||||||
DegradeRuleManager.ruleMap = rm;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -233,6 +223,16 @@ public final class DegradeRuleManager {
|
||||||
RecordLog.info("[DegradeRuleManager] Degrade rules loaded: {}", ruleMap);
|
RecordLog.info("[DegradeRuleManager] Degrade rules loaded: {}", ruleMap);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Map<String, List<DegradeRule>> buildCircuitBreakerRules(Map<String, List<CircuitBreaker>> cbs) {
|
||||||
|
Map<String, List<DegradeRule>> result = new HashMap<>(cbs.size());
|
||||||
|
for (Map.Entry<String, List<CircuitBreaker>> entry : cbs.entrySet()) {
|
||||||
|
String resource = entry.getKey();
|
||||||
|
Set<DegradeRule> rules = entry.getValue().stream().map(CircuitBreaker::getRule).collect(Collectors.toSet());
|
||||||
|
result.put(resource, new ArrayList<>(rules));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
private Map<String, List<CircuitBreaker>> buildCircuitBreakers(List<DegradeRule> list) {
|
private Map<String, List<CircuitBreaker>> buildCircuitBreakers(List<DegradeRule> list) {
|
||||||
Map<String, List<CircuitBreaker>> cbMap = new HashMap<>(8);
|
Map<String, List<CircuitBreaker>> cbMap = new HashMap<>(8);
|
||||||
if (list == null || list.isEmpty()) {
|
if (list == null || list.isEmpty()) {
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ import com.alibaba.csp.sentinel.node.metric.MetricTimerListener;
|
||||||
import com.alibaba.csp.sentinel.property.DynamicSentinelProperty;
|
import com.alibaba.csp.sentinel.property.DynamicSentinelProperty;
|
||||||
import com.alibaba.csp.sentinel.property.PropertyListener;
|
import com.alibaba.csp.sentinel.property.PropertyListener;
|
||||||
import com.alibaba.csp.sentinel.property.SentinelProperty;
|
import com.alibaba.csp.sentinel.property.SentinelProperty;
|
||||||
|
import com.alibaba.csp.sentinel.slots.block.RuleManager;
|
||||||
import com.alibaba.csp.sentinel.util.AssertUtil;
|
import com.alibaba.csp.sentinel.util.AssertUtil;
|
||||||
import com.alibaba.csp.sentinel.util.StringUtil;
|
import com.alibaba.csp.sentinel.util.StringUtil;
|
||||||
|
|
||||||
|
|
@ -48,7 +49,7 @@ import java.util.concurrent.TimeUnit;
|
||||||
*/
|
*/
|
||||||
public class FlowRuleManager {
|
public class FlowRuleManager {
|
||||||
|
|
||||||
private static volatile Map<String, List<FlowRule>> flowRules = new HashMap<>();
|
private static volatile RuleManager<FlowRule> flowRules = new RuleManager<>();
|
||||||
|
|
||||||
private static final FlowPropertyListener LISTENER = new FlowPropertyListener();
|
private static final FlowPropertyListener LISTENER = new FlowPropertyListener();
|
||||||
private static SentinelProperty<List<FlowRule>> currentProperty = new DynamicSentinelProperty<List<FlowRule>>();
|
private static SentinelProperty<List<FlowRule>> currentProperty = new DynamicSentinelProperty<List<FlowRule>>();
|
||||||
|
|
@ -105,11 +106,7 @@ public class FlowRuleManager {
|
||||||
* @return a new copy of the rules.
|
* @return a new copy of the rules.
|
||||||
*/
|
*/
|
||||||
public static List<FlowRule> getRules() {
|
public static List<FlowRule> getRules() {
|
||||||
List<FlowRule> rules = new ArrayList<FlowRule>();
|
return flowRules.getRules();
|
||||||
for (Map.Entry<String, List<FlowRule>> entry : flowRules.entrySet()) {
|
|
||||||
rules.addAll(entry.getValue());
|
|
||||||
}
|
|
||||||
return rules;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -121,12 +118,12 @@ public class FlowRuleManager {
|
||||||
currentProperty.updateValue(rules);
|
currentProperty.updateValue(rules);
|
||||||
}
|
}
|
||||||
|
|
||||||
static Map<String, List<FlowRule>> getFlowRuleMap() {
|
static List<FlowRule> getFlowRules(String resource) {
|
||||||
return flowRules;
|
return flowRules.getRules(resource);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean hasConfig(String resource) {
|
public static boolean hasConfig(String resource) {
|
||||||
return flowRules.containsKey(resource);
|
return flowRules.hasConfig(resource);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isOtherOrigin(String origin, String resourceName) {
|
public static boolean isOtherOrigin(String origin, String resourceName) {
|
||||||
|
|
@ -134,7 +131,7 @@ public class FlowRuleManager {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<FlowRule> rules = flowRules.get(resourceName);
|
List<FlowRule> rules = flowRules.getRules(resourceName);
|
||||||
|
|
||||||
if (rules != null) {
|
if (rules != null) {
|
||||||
for (FlowRule rule : rules) {
|
for (FlowRule rule : rules) {
|
||||||
|
|
@ -152,18 +149,14 @@ public class FlowRuleManager {
|
||||||
@Override
|
@Override
|
||||||
public synchronized void configUpdate(List<FlowRule> value) {
|
public synchronized void configUpdate(List<FlowRule> value) {
|
||||||
Map<String, List<FlowRule>> rules = FlowRuleUtil.buildFlowRuleMap(value);
|
Map<String, List<FlowRule>> rules = FlowRuleUtil.buildFlowRuleMap(value);
|
||||||
if (rules != null) {
|
flowRules.updateRules(rules);
|
||||||
flowRules = rules;
|
|
||||||
}
|
|
||||||
RecordLog.info("[FlowRuleManager] Flow rules received: {}", rules);
|
RecordLog.info("[FlowRuleManager] Flow rules received: {}", rules);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public synchronized void configLoad(List<FlowRule> conf) {
|
public synchronized void configLoad(List<FlowRule> conf) {
|
||||||
Map<String, List<FlowRule>> rules = FlowRuleUtil.buildFlowRuleMap(conf);
|
Map<String, List<FlowRule>> rules = FlowRuleUtil.buildFlowRuleMap(conf);
|
||||||
if (rules != null) {
|
flowRules.updateRules(rules);
|
||||||
flowRules = rules;
|
|
||||||
}
|
|
||||||
RecordLog.info("[FlowRuleManager] Flow rules loaded: {}", rules);
|
RecordLog.info("[FlowRuleManager] Flow rules loaded: {}", rules);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ package com.alibaba.csp.sentinel.slots.block.flow;
|
||||||
import com.alibaba.csp.sentinel.log.RecordLog;
|
import com.alibaba.csp.sentinel.log.RecordLog;
|
||||||
import com.alibaba.csp.sentinel.slots.block.ClusterRuleConstant;
|
import com.alibaba.csp.sentinel.slots.block.ClusterRuleConstant;
|
||||||
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
|
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
|
||||||
|
import com.alibaba.csp.sentinel.slots.block.RuleManager;
|
||||||
import com.alibaba.csp.sentinel.slots.block.flow.controller.DefaultController;
|
import com.alibaba.csp.sentinel.slots.block.flow.controller.DefaultController;
|
||||||
import com.alibaba.csp.sentinel.slots.block.flow.controller.ThrottlingController;
|
import com.alibaba.csp.sentinel.slots.block.flow.controller.ThrottlingController;
|
||||||
import com.alibaba.csp.sentinel.slots.block.flow.controller.WarmUpController;
|
import com.alibaba.csp.sentinel.slots.block.flow.controller.WarmUpController;
|
||||||
|
|
@ -170,6 +171,9 @@ public final class FlowRuleUtil {
|
||||||
if (!baseValid) {
|
if (!baseValid) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (!checkRegexField(rule)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
if (rule.getGrade() == RuleConstant.FLOW_GRADE_QPS) {
|
if (rule.getGrade() == RuleConstant.FLOW_GRADE_QPS) {
|
||||||
// Check strategy and control (shaping) behavior.
|
// Check strategy and control (shaping) behavior.
|
||||||
return checkClusterField(rule) && checkStrategyField(rule) && checkControlBehaviorField(rule);
|
return checkClusterField(rule) && checkStrategyField(rule) && checkControlBehaviorField(rule);
|
||||||
|
|
@ -237,6 +241,16 @@ public final class FlowRuleUtil {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static boolean checkRegexField(FlowRule rule) {
|
||||||
|
if (!RuleManager.checkRegexResourceField(rule)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (rule.isRegex()) {
|
||||||
|
return !rule.isClusterMode() && rule.getControlBehavior() == RuleConstant.CONTROL_BEHAVIOR_DEFAULT;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
private static boolean checkControlBehaviorField(/*@NonNull*/ FlowRule rule) {
|
private static boolean checkControlBehaviorField(/*@NonNull*/ FlowRule rule) {
|
||||||
switch (rule.getControlBehavior()) {
|
switch (rule.getControlBehavior()) {
|
||||||
case RuleConstant.CONTROL_BEHAVIOR_WARM_UP:
|
case RuleConstant.CONTROL_BEHAVIOR_WARM_UP:
|
||||||
|
|
|
||||||
|
|
@ -26,8 +26,6 @@ import com.alibaba.csp.sentinel.util.AssertUtil;
|
||||||
import com.alibaba.csp.sentinel.util.function.Function;
|
import com.alibaba.csp.sentinel.util.function.Function;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
|
|
@ -179,9 +177,7 @@ public class FlowSlot extends AbstractLinkedProcessorSlot<DefaultNode> {
|
||||||
private final Function<String, Collection<FlowRule>> ruleProvider = new Function<String, Collection<FlowRule>>() {
|
private final Function<String, Collection<FlowRule>> ruleProvider = new Function<String, Collection<FlowRule>>() {
|
||||||
@Override
|
@Override
|
||||||
public Collection<FlowRule> apply(String resource) {
|
public Collection<FlowRule> apply(String resource) {
|
||||||
// Flow rule map should not be null.
|
return FlowRuleManager.getFlowRules(resource);
|
||||||
Map<String, List<FlowRule>> flowRules = FlowRuleManager.getFlowRuleMap();
|
|
||||||
return flowRules.get(resource);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,4 +27,14 @@ public interface Function<T, R> {
|
||||||
* @return the function result
|
* @return the function result
|
||||||
*/
|
*/
|
||||||
R apply(T t);
|
R apply(T t);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a function that always returns its input argument.
|
||||||
|
*
|
||||||
|
* @param <T> the type of the input and output objects to the function
|
||||||
|
* @return a function that always returns its input argument
|
||||||
|
*/
|
||||||
|
static <T> Function<T, T> identity() {
|
||||||
|
return t -> t;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,112 @@
|
||||||
|
package com.alibaba.csp.sentinel.slots.block;
|
||||||
|
|
||||||
|
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
public class RuleManagerTest {
|
||||||
|
|
||||||
|
private RuleManager<FlowRule> ruleManager;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() throws Exception {
|
||||||
|
ruleManager = new RuleManager<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUpdateRules() throws Exception {
|
||||||
|
// Setup
|
||||||
|
final Map<String, List<FlowRule>> rulesMap = generateFlowRules(true);
|
||||||
|
|
||||||
|
// Run the test
|
||||||
|
ruleManager.updateRules(rulesMap);
|
||||||
|
|
||||||
|
// Verify the results
|
||||||
|
assertEquals(ruleManager.getRules().size(), 2);
|
||||||
|
Field regexRules = RuleManager.class.getDeclaredField("regexRules");
|
||||||
|
regexRules.setAccessible(true);
|
||||||
|
assertEquals(((Map)regexRules.get(ruleManager)).size(), 1);
|
||||||
|
Field simpleRules = RuleManager.class.getDeclaredField("simpleRules");
|
||||||
|
simpleRules.setAccessible(true);
|
||||||
|
assertEquals(((Map)simpleRules.get(ruleManager)).size(), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetRulesWithCache() throws Exception {
|
||||||
|
// Setup
|
||||||
|
final Map<String, List<FlowRule>> rulesMap = generateFlowRules(true);
|
||||||
|
|
||||||
|
// Run the test
|
||||||
|
ruleManager.updateRules(rulesMap);
|
||||||
|
|
||||||
|
// Verify the results
|
||||||
|
Field regexCacheRules = RuleManager.class.getDeclaredField("regexCacheRules");
|
||||||
|
regexCacheRules.setAccessible(true);
|
||||||
|
assertEquals(((Map)regexCacheRules.get(ruleManager)).size(), 0);
|
||||||
|
ruleManager.getRules("rule2");
|
||||||
|
assertEquals(((Map)regexCacheRules.get(ruleManager)).size(), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRebuildRulesWhenUpdateRules() throws Exception {
|
||||||
|
// Setup
|
||||||
|
final Map<String, List<FlowRule>> rulesMap = generateFlowRules(true);
|
||||||
|
|
||||||
|
// Run the test
|
||||||
|
ruleManager.updateRules(rulesMap);
|
||||||
|
ruleManager.getRules("rule2");
|
||||||
|
ruleManager.updateRules(generateFlowRules(true));
|
||||||
|
|
||||||
|
// Verify the results
|
||||||
|
Field regexCacheRules = RuleManager.class.getDeclaredField("regexCacheRules");
|
||||||
|
regexCacheRules.setAccessible(true);
|
||||||
|
assertEquals(((Map)regexCacheRules.get(ruleManager)).size(), 1);
|
||||||
|
|
||||||
|
// Clean up regular rules
|
||||||
|
ruleManager.updateRules(generateFlowRules(false));
|
||||||
|
// Verify the results
|
||||||
|
assertEquals(((Map)regexCacheRules.get(ruleManager)).size(), 0);
|
||||||
|
}
|
||||||
|
@Test
|
||||||
|
public void testValidRegexRule() {
|
||||||
|
// Setup
|
||||||
|
FlowRule flowRule = new FlowRule();
|
||||||
|
flowRule.setRegex(true);
|
||||||
|
flowRule.setResource("{}");
|
||||||
|
// Run the test and verify
|
||||||
|
Assert.assertFalse(RuleManager.checkRegexResourceField(flowRule));
|
||||||
|
|
||||||
|
flowRule.setResource(".*");
|
||||||
|
// Run the test and verify
|
||||||
|
Assert.assertTrue(RuleManager.checkRegexResourceField(flowRule));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testHasConfig() {
|
||||||
|
// Setup
|
||||||
|
final Map<String, List<FlowRule>> rulesMap = generateFlowRules(true);
|
||||||
|
|
||||||
|
// Run the test and verify the results
|
||||||
|
ruleManager.updateRules(rulesMap);
|
||||||
|
assertTrue(ruleManager.hasConfig("rule1"));
|
||||||
|
assertFalse(ruleManager.hasConfig("rule3"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String, List<FlowRule>> generateFlowRules(boolean withRegex) {
|
||||||
|
Map<String, List<FlowRule>> result = new HashMap<>(2);
|
||||||
|
FlowRule flowRule1 = new FlowRule("rule1");
|
||||||
|
flowRule1.setRegex(withRegex);
|
||||||
|
result.put(flowRule1.getResource(), Collections.singletonList(flowRule1));
|
||||||
|
FlowRule flowRule2 = new FlowRule("rule2");
|
||||||
|
result.put(flowRule2.getResource(), Collections.singletonList(flowRule2));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
package com.alibaba.csp.sentinel.slots.block.authority;
|
||||||
|
|
||||||
|
import com.alibaba.csp.sentinel.Entry;
|
||||||
|
import com.alibaba.csp.sentinel.SphU;
|
||||||
|
import com.alibaba.csp.sentinel.context.ContextUtil;
|
||||||
|
import com.alibaba.csp.sentinel.slots.block.BlockException;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author quguai
|
||||||
|
* @date 2023/10/27 11:48
|
||||||
|
*/
|
||||||
|
public class AuthorityPartialIntegrationTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRegex() {
|
||||||
|
AuthorityRule authorityRule = new AuthorityRule();
|
||||||
|
authorityRule.setRegex(true);
|
||||||
|
authorityRule.setStrategy(1);
|
||||||
|
authorityRule.setLimitApp("appA");
|
||||||
|
authorityRule.setResource(".*");
|
||||||
|
AuthorityRuleManager.loadRules(Collections.singletonList(authorityRule));
|
||||||
|
verifyFlow("testRegex_1", "appA", false);
|
||||||
|
verifyFlow("testRegex_2", "appA", false);
|
||||||
|
verifyFlow("testRegex_1", "appB", true);
|
||||||
|
verifyFlow("testRegex_2", "appB", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void verifyFlow(String resource, String origin, boolean shouldPass) {
|
||||||
|
ContextUtil.enter("a", origin);
|
||||||
|
Entry e = null;
|
||||||
|
try {
|
||||||
|
e = SphU.entry(resource);
|
||||||
|
assertTrue(shouldPass);
|
||||||
|
} catch (BlockException e1) {
|
||||||
|
assertFalse(shouldPass);
|
||||||
|
} finally {
|
||||||
|
if (e != null) {
|
||||||
|
e.exit();
|
||||||
|
}
|
||||||
|
ContextUtil.exit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,73 @@
|
||||||
|
package com.alibaba.csp.sentinel.slots.block.degrade;
|
||||||
|
|
||||||
|
import com.alibaba.csp.sentinel.Entry;
|
||||||
|
import com.alibaba.csp.sentinel.SphU;
|
||||||
|
import com.alibaba.csp.sentinel.Tracer;
|
||||||
|
import com.alibaba.csp.sentinel.context.ContextUtil;
|
||||||
|
import com.alibaba.csp.sentinel.slots.block.BlockException;
|
||||||
|
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author quguai
|
||||||
|
* @date 2023/10/27 13:56
|
||||||
|
*/
|
||||||
|
public class DegradePartialIntegrationTest {
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() throws Exception {
|
||||||
|
DegradeRuleManager.loadRules(new ArrayList<>());
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void tearDown() throws Exception {
|
||||||
|
DegradeRuleManager.loadRules(new ArrayList<>());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDegradeRegex() {
|
||||||
|
DegradeRule rule = new DegradeRule(".*")
|
||||||
|
.setCount(0.5d)
|
||||||
|
.setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_RATIO)
|
||||||
|
.setStatIntervalMs(20 * 1000)
|
||||||
|
.setTimeWindow(10)
|
||||||
|
.setMinRequestAmount(1);
|
||||||
|
rule.setRegex(true);
|
||||||
|
DegradeRuleManager.loadRules(Collections.singletonList(rule));
|
||||||
|
|
||||||
|
verifyDegradeFlow("testDegradeRegex_1", true, true);
|
||||||
|
verifyDegradeFlow("testDegradeRegex_1", true, false);
|
||||||
|
|
||||||
|
verifyDegradeFlow("testDegradeRegex_2", true, true);
|
||||||
|
verifyDegradeFlow("testDegradeRegex_2", true, false);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void verifyDegradeFlow(String resource, boolean error, boolean shouldPass) {
|
||||||
|
Entry entry = null;
|
||||||
|
try {
|
||||||
|
entry = SphU.entry(resource);
|
||||||
|
assertTrue(shouldPass);
|
||||||
|
if (error) {
|
||||||
|
int i = 10 / 0;
|
||||||
|
}
|
||||||
|
} catch (BlockException e1) {
|
||||||
|
assertFalse(shouldPass);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
Tracer.traceEntry(ex, entry);
|
||||||
|
} finally {
|
||||||
|
if (entry != null) {
|
||||||
|
entry.exit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -15,9 +15,6 @@
|
||||||
*/
|
*/
|
||||||
package com.alibaba.csp.sentinel.slots.block.flow;
|
package com.alibaba.csp.sentinel.slots.block.flow;
|
||||||
|
|
||||||
import static org.junit.Assert.assertTrue;
|
|
||||||
import static org.junit.Assert.fail;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
|
@ -32,6 +29,8 @@ import com.alibaba.csp.sentinel.context.ContextUtil;
|
||||||
import com.alibaba.csp.sentinel.slots.block.BlockException;
|
import com.alibaba.csp.sentinel.slots.block.BlockException;
|
||||||
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
|
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author jialiang.linjl
|
* @author jialiang.linjl
|
||||||
*/
|
*/
|
||||||
|
|
@ -114,6 +113,21 @@ public class FlowPartialIntegrationTest {
|
||||||
System.out.println("done");
|
System.out.println("done");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testQpsRegex() {
|
||||||
|
FlowRule flowRule = new FlowRule();
|
||||||
|
String resource = ".*";
|
||||||
|
flowRule.setResource(resource);
|
||||||
|
flowRule.setGrade(RuleConstant.FLOW_GRADE_QPS);
|
||||||
|
flowRule.setRegex(true);
|
||||||
|
flowRule.setCount(1);
|
||||||
|
FlowRuleManager.loadRules(Collections.singletonList(flowRule));
|
||||||
|
verifyFlow("testQpsRegex_1", true);
|
||||||
|
verifyFlow("testQpsRegex_2", true);
|
||||||
|
verifyFlow("testQpsRegex_1", false);
|
||||||
|
verifyFlow("testQpsRegex_2", false);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testOriginFlowRule() {
|
public void testOriginFlowRule() {
|
||||||
String RESOURCE_NAME = "testOriginFlowRule";
|
String RESOURCE_NAME = "testOriginFlowRule";
|
||||||
|
|
@ -255,4 +269,18 @@ public class FlowPartialIntegrationTest {
|
||||||
|
|
||||||
ContextUtil.exit();
|
ContextUtil.exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void verifyFlow(String resource, boolean shouldPass) {
|
||||||
|
Entry e = null;
|
||||||
|
try {
|
||||||
|
e = SphU.entry(resource);
|
||||||
|
assertTrue(shouldPass);
|
||||||
|
} catch (BlockException e1) {
|
||||||
|
assertFalse(shouldPass);
|
||||||
|
} finally {
|
||||||
|
if (e != null) {
|
||||||
|
e.exit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ import com.alibaba.csp.sentinel.log.RecordLog;
|
||||||
import com.alibaba.csp.sentinel.property.DynamicSentinelProperty;
|
import com.alibaba.csp.sentinel.property.DynamicSentinelProperty;
|
||||||
import com.alibaba.csp.sentinel.property.PropertyListener;
|
import com.alibaba.csp.sentinel.property.PropertyListener;
|
||||||
import com.alibaba.csp.sentinel.property.SentinelProperty;
|
import com.alibaba.csp.sentinel.property.SentinelProperty;
|
||||||
|
import com.alibaba.csp.sentinel.slots.block.RuleManager;
|
||||||
import com.alibaba.csp.sentinel.util.AssertUtil;
|
import com.alibaba.csp.sentinel.util.AssertUtil;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -35,8 +36,7 @@ import com.alibaba.csp.sentinel.util.AssertUtil;
|
||||||
*/
|
*/
|
||||||
public final class ParamFlowRuleManager {
|
public final class ParamFlowRuleManager {
|
||||||
|
|
||||||
private static final Map<String, List<ParamFlowRule>> PARAM_FLOW_RULES = new ConcurrentHashMap<>();
|
private static final RuleManager<ParamFlowRule> PARAM_FLOW_RULES = new RuleManager<>();
|
||||||
|
|
||||||
private final static RulePropertyListener PROPERTY_LISTENER = new RulePropertyListener();
|
private final static RulePropertyListener PROPERTY_LISTENER = new RulePropertyListener();
|
||||||
private static SentinelProperty<List<ParamFlowRule>> currentProperty = new DynamicSentinelProperty<>();
|
private static SentinelProperty<List<ParamFlowRule>> currentProperty = new DynamicSentinelProperty<>();
|
||||||
|
|
||||||
|
|
@ -75,12 +75,11 @@ public final class ParamFlowRuleManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<ParamFlowRule> getRulesOfResource(String resourceName) {
|
public static List<ParamFlowRule> getRulesOfResource(String resourceName) {
|
||||||
return new ArrayList<>(PARAM_FLOW_RULES.get(resourceName));
|
return new ArrayList<>(PARAM_FLOW_RULES.getRules(resourceName));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean hasRules(String resourceName) {
|
public static boolean hasRules(String resourceName) {
|
||||||
List<ParamFlowRule> rules = PARAM_FLOW_RULES.get(resourceName);
|
return PARAM_FLOW_RULES.hasConfig(resourceName);
|
||||||
return rules != null && !rules.isEmpty();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -89,11 +88,7 @@ public final class ParamFlowRuleManager {
|
||||||
* @return a new copy of the rules.
|
* @return a new copy of the rules.
|
||||||
*/
|
*/
|
||||||
public static List<ParamFlowRule> getRules() {
|
public static List<ParamFlowRule> getRules() {
|
||||||
List<ParamFlowRule> rules = new ArrayList<>();
|
return PARAM_FLOW_RULES.getRules();
|
||||||
for (Map.Entry<String, List<ParamFlowRule>> entry : PARAM_FLOW_RULES.entrySet()) {
|
|
||||||
rules.addAll(entry.getValue());
|
|
||||||
}
|
|
||||||
return rules;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static class RulePropertyListener implements PropertyListener<List<ParamFlowRule>> {
|
static class RulePropertyListener implements PropertyListener<List<ParamFlowRule>> {
|
||||||
|
|
@ -101,20 +96,14 @@ public final class ParamFlowRuleManager {
|
||||||
@Override
|
@Override
|
||||||
public void configUpdate(List<ParamFlowRule> list) {
|
public void configUpdate(List<ParamFlowRule> list) {
|
||||||
Map<String, List<ParamFlowRule>> rules = aggregateAndPrepareParamRules(list);
|
Map<String, List<ParamFlowRule>> rules = aggregateAndPrepareParamRules(list);
|
||||||
if (rules != null) {
|
PARAM_FLOW_RULES.updateRules(rules);
|
||||||
PARAM_FLOW_RULES.clear();
|
|
||||||
PARAM_FLOW_RULES.putAll(rules);
|
|
||||||
}
|
|
||||||
RecordLog.info("[ParamFlowRuleManager] Parameter flow rules received: {}", PARAM_FLOW_RULES);
|
RecordLog.info("[ParamFlowRuleManager] Parameter flow rules received: {}", PARAM_FLOW_RULES);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void configLoad(List<ParamFlowRule> list) {
|
public void configLoad(List<ParamFlowRule> list) {
|
||||||
Map<String, List<ParamFlowRule>> rules = aggregateAndPrepareParamRules(list);
|
Map<String, List<ParamFlowRule>> rules = aggregateAndPrepareParamRules(list);
|
||||||
if (rules != null) {
|
PARAM_FLOW_RULES.updateRules(rules);
|
||||||
PARAM_FLOW_RULES.clear();
|
|
||||||
PARAM_FLOW_RULES.putAll(rules);
|
|
||||||
}
|
|
||||||
RecordLog.info("[ParamFlowRuleManager] Parameter flow rules received: {}", PARAM_FLOW_RULES);
|
RecordLog.info("[ParamFlowRuleManager] Parameter flow rules received: {}", PARAM_FLOW_RULES);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -128,7 +117,7 @@ public final class ParamFlowRuleManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clear unused parameter metrics.
|
// Clear unused parameter metrics.
|
||||||
for (Map.Entry<String, List<ParamFlowRule>> entry : PARAM_FLOW_RULES.entrySet()) {
|
for (Map.Entry<String, List<ParamFlowRule>> entry : PARAM_FLOW_RULES.getOriginalRules().entrySet()) {
|
||||||
String resource = entry.getKey();
|
String resource = entry.getKey();
|
||||||
if (!newRuleMap.containsKey(resource)) {
|
if (!newRuleMap.containsKey(resource)) {
|
||||||
ParameterMetricStorage.clearParamMetricForResource(resource);
|
ParameterMetricStorage.clearParamMetricForResource(resource);
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
import com.alibaba.csp.sentinel.log.RecordLog;
|
import com.alibaba.csp.sentinel.log.RecordLog;
|
||||||
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
|
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
|
||||||
|
import com.alibaba.csp.sentinel.slots.block.RuleManager;
|
||||||
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleUtil;
|
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleUtil;
|
||||||
import com.alibaba.csp.sentinel.util.AssertUtil;
|
import com.alibaba.csp.sentinel.util.AssertUtil;
|
||||||
import com.alibaba.csp.sentinel.util.StringUtil;
|
import com.alibaba.csp.sentinel.util.StringUtil;
|
||||||
|
|
@ -48,7 +49,7 @@ public final class ParamFlowRuleUtil {
|
||||||
&& rule.getGrade() >= 0 && rule.getParamIdx() != null
|
&& rule.getGrade() >= 0 && rule.getParamIdx() != null
|
||||||
&& rule.getBurstCount() >= 0 && rule.getControlBehavior() >= 0
|
&& rule.getBurstCount() >= 0 && rule.getControlBehavior() >= 0
|
||||||
&& rule.getDurationInSec() > 0 && rule.getMaxQueueingTimeMs() >= 0
|
&& rule.getDurationInSec() > 0 && rule.getMaxQueueingTimeMs() >= 0
|
||||||
&& checkCluster(rule);
|
&& checkCluster(rule) & checkRegexField(rule);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean checkCluster(/*@PreChecked*/ ParamFlowRule rule) {
|
private static boolean checkCluster(/*@PreChecked*/ ParamFlowRule rule) {
|
||||||
|
|
@ -65,6 +66,16 @@ public final class ParamFlowRuleUtil {
|
||||||
return validClusterRuleId(clusterConfig.getFlowId());
|
return validClusterRuleId(clusterConfig.getFlowId());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static boolean checkRegexField(ParamFlowRule rule) {
|
||||||
|
if (!RuleManager.checkRegexResourceField(rule)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (rule.isRegex()) {
|
||||||
|
return !rule.isClusterMode() && rule.getControlBehavior() == RuleConstant.CONTROL_BEHAVIOR_DEFAULT;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
public static boolean validClusterRuleId(Long id) {
|
public static boolean validClusterRuleId(Long id) {
|
||||||
return id != null && id > 0;
|
return id != null && id > 0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,68 @@
|
||||||
|
package com.alibaba.csp.sentinel.slots.block.flow.param;
|
||||||
|
|
||||||
|
import com.alibaba.csp.sentinel.Entry;
|
||||||
|
import com.alibaba.csp.sentinel.EntryType;
|
||||||
|
import com.alibaba.csp.sentinel.SphU;
|
||||||
|
import com.alibaba.csp.sentinel.slots.block.BlockException;
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author quguai
|
||||||
|
* @date 2023/10/27 13:44
|
||||||
|
*/
|
||||||
|
public class ParamFlowPartialIntegrationTest {
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() throws Exception {
|
||||||
|
ParamFlowRuleManager.loadRules(new ArrayList<>());
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void tearDown() throws Exception {
|
||||||
|
ParamFlowRuleManager.loadRules(new ArrayList<>());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testParamFlowRegex() {
|
||||||
|
ParamFlowRule rule = new ParamFlowRule(".*")
|
||||||
|
.setParamIdx(0)
|
||||||
|
.setCount(1);
|
||||||
|
rule.setRegex(true);
|
||||||
|
ParamFlowRuleManager.loadRules(Collections.singletonList(rule));
|
||||||
|
verifyFlow("testParamFlowRegex_1", true, "args");
|
||||||
|
verifyFlow("testParamFlowRegex_1", true, "args_1");
|
||||||
|
|
||||||
|
verifyFlow("testParamFlowRegex_1", false, "args");
|
||||||
|
verifyFlow("testParamFlowRegex_1", false, "args_1");
|
||||||
|
|
||||||
|
verifyFlow("testParamFlowRegex_2", true, "args");
|
||||||
|
verifyFlow("testParamFlowRegex_2", true, "args_1");
|
||||||
|
|
||||||
|
verifyFlow("testParamFlowRegex_2", false, "args");
|
||||||
|
verifyFlow("testParamFlowRegex_2", false, "args_1");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void verifyFlow(String resource, boolean shouldPass, String... args) {
|
||||||
|
Entry e = null;
|
||||||
|
try {
|
||||||
|
e = SphU.entry(resource, 1, EntryType.IN, args);
|
||||||
|
assertTrue(shouldPass);
|
||||||
|
} catch (BlockException e1) {
|
||||||
|
assertFalse(shouldPass);
|
||||||
|
} finally {
|
||||||
|
if (e != null) {
|
||||||
|
e.exit(1, args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue