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;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Abstract rule entity.
|
||||
*
|
||||
|
|
@ -44,6 +46,11 @@ public abstract class AbstractRule implements Rule {
|
|||
*/
|
||||
private String limitApp;
|
||||
|
||||
/**
|
||||
* Whether to match resource names according to regular rules
|
||||
*/
|
||||
private boolean regex;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
|
@ -72,6 +79,15 @@ public abstract class AbstractRule implements Rule {
|
|||
return this;
|
||||
}
|
||||
|
||||
public boolean isRegex() {
|
||||
return regex;
|
||||
}
|
||||
|
||||
public AbstractRule setRegex(boolean regex) {
|
||||
this.regex = regex;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
|
|
@ -83,7 +99,10 @@ public abstract class AbstractRule implements Rule {
|
|||
|
||||
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;
|
||||
}
|
||||
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)) {
|
||||
result = 31 * result + limitApp.hashCode();
|
||||
}
|
||||
result = 31 * result + (regex ? 1 : 0);
|
||||
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.slots.block.RuleConstant;
|
||||
import com.alibaba.csp.sentinel.slots.block.RuleManager;
|
||||
import com.alibaba.csp.sentinel.util.AssertUtil;
|
||||
import com.alibaba.csp.sentinel.util.StringUtil;
|
||||
import com.alibaba.csp.sentinel.property.DynamicSentinelProperty;
|
||||
|
|
@ -39,7 +40,7 @@ import com.alibaba.csp.sentinel.property.SentinelProperty;
|
|||
*/
|
||||
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 SentinelProperty<List<AuthorityRule>> currentProperty = new DynamicSentinelProperty<>();
|
||||
|
|
@ -70,7 +71,7 @@ public final class AuthorityRuleManager {
|
|||
}
|
||||
|
||||
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.
|
||||
*/
|
||||
public static List<AuthorityRule> getRules() {
|
||||
List<AuthorityRule> rules = new ArrayList<>();
|
||||
if (authorityRules == null) {
|
||||
return rules;
|
||||
}
|
||||
for (Map.Entry<String, Set<AuthorityRule>> entry : authorityRules.entrySet()) {
|
||||
rules.addAll(entry.getValue());
|
||||
}
|
||||
return rules;
|
||||
return authorityRules.getRules();
|
||||
}
|
||||
|
||||
private static class RulePropertyListener implements PropertyListener<List<AuthorityRule>> {
|
||||
|
||||
@Override
|
||||
public synchronized void configLoad(List<AuthorityRule> value) {
|
||||
authorityRules = loadAuthorityConf(value);
|
||||
authorityRules.updateRules(loadAuthorityConf(value));
|
||||
|
||||
RecordLog.info("[AuthorityRuleManager] Authority rules loaded: {}", authorityRules);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void configUpdate(List<AuthorityRule> conf) {
|
||||
authorityRules = loadAuthorityConf(conf);
|
||||
authorityRules.updateRules(loadAuthorityConf(conf));
|
||||
|
||||
RecordLog.info("[AuthorityRuleManager] Authority rules received: {}", authorityRules);
|
||||
}
|
||||
|
||||
private Map<String, Set<AuthorityRule>> loadAuthorityConf(List<AuthorityRule> list) {
|
||||
Map<String, Set<AuthorityRule>> newRuleMap = new ConcurrentHashMap<>();
|
||||
private Map<String, List<AuthorityRule>> loadAuthorityConf(List<AuthorityRule> list) {
|
||||
Map<String, List<AuthorityRule>> newRuleMap = new ConcurrentHashMap<>();
|
||||
|
||||
if (list == null || list.isEmpty()) {
|
||||
return newRuleMap;
|
||||
|
|
@ -123,10 +117,10 @@ public final class AuthorityRuleManager {
|
|||
}
|
||||
|
||||
String identity = rule.getResource();
|
||||
Set<AuthorityRule> ruleSet = newRuleMap.get(identity);
|
||||
List<AuthorityRule> ruleSet = newRuleMap.get(identity);
|
||||
// putIfAbsent
|
||||
if (ruleSet == null) {
|
||||
ruleSet = new HashSet<>();
|
||||
ruleSet = new ArrayList<>();
|
||||
ruleSet.add(rule);
|
||||
newRuleMap.put(identity, ruleSet);
|
||||
} else {
|
||||
|
|
@ -140,12 +134,12 @@ public final class AuthorityRuleManager {
|
|||
|
||||
}
|
||||
|
||||
static Map<String, Set<AuthorityRule>> getAuthorityRules() {
|
||||
return authorityRules;
|
||||
static List<AuthorityRule> getRules(String resource) {
|
||||
return authorityRules.getRules(resource);
|
||||
}
|
||||
|
||||
public static boolean isValidRule(AuthorityRule rule) {
|
||||
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;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.List;
|
||||
|
||||
import com.alibaba.csp.sentinel.Constants;
|
||||
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 {
|
||||
Map<String, Set<AuthorityRule>> authorityRules = AuthorityRuleManager.getAuthorityRules();
|
||||
|
||||
if (authorityRules == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Set<AuthorityRule> rules = authorityRules.get(resource.getName());
|
||||
List<AuthorityRule> rules = AuthorityRuleManager.getRules(resource.getName());
|
||||
if (rules == null) {
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,12 +21,14 @@ import java.util.HashSet;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
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.RuleManager;
|
||||
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;
|
||||
|
|
@ -42,8 +44,8 @@ import com.alibaba.csp.sentinel.util.StringUtil;
|
|||
*/
|
||||
public final class DegradeRuleManager {
|
||||
|
||||
private static volatile Map<String, List<CircuitBreaker>> circuitBreakers = new HashMap<>();
|
||||
private static volatile Map<String, Set<DegradeRule>> ruleMap = new HashMap<>();
|
||||
private static volatile RuleManager<CircuitBreaker> circuitBreakers = new RuleManager<>(DegradeRuleManager::generateCbs, cb -> cb.getRule().isRegex());
|
||||
private static volatile RuleManager<DegradeRule> ruleMap = new RuleManager<>();
|
||||
|
||||
private static final RulePropertyListener LISTENER = new RulePropertyListener();
|
||||
private static SentinelProperty<List<DegradeRule>> currentProperty
|
||||
|
|
@ -70,14 +72,11 @@ public final class DegradeRuleManager {
|
|||
}
|
||||
|
||||
static List<CircuitBreaker> getCircuitBreakers(String resourceName) {
|
||||
return circuitBreakers.get(resourceName);
|
||||
return circuitBreakers.getRules(resourceName);
|
||||
}
|
||||
|
||||
public static boolean hasConfig(String resource) {
|
||||
if (resource == null) {
|
||||
return false;
|
||||
}
|
||||
return circuitBreakers.containsKey(resource);
|
||||
return circuitBreakers.hasConfig(resource);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -88,16 +87,11 @@ public final class DegradeRuleManager {
|
|||
* @return list of existing circuit breaking rules, or empty list if no rules were loaded
|
||||
*/
|
||||
public static List<DegradeRule> getRules() {
|
||||
List<DegradeRule> rules = new ArrayList<>();
|
||||
for (Map.Entry<String, Set<DegradeRule>> entry : ruleMap.entrySet()) {
|
||||
rules.addAll(entry.getValue());
|
||||
return ruleMap.getRules();
|
||||
}
|
||||
return rules;
|
||||
}
|
||||
|
||||
public static Set<DegradeRule> getRulesOfResource(String resource) {
|
||||
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) {
|
||||
AssertUtil.notEmpty(resourceName, "resourceName cannot be empty");
|
||||
try {
|
||||
Map<String, Set<DegradeRule>> newRuleMap = new HashMap<>(ruleMap);
|
||||
Map<String, List<DegradeRule>> newRuleMap = ruleMap.getOriginalRules();
|
||||
if (rules == null) {
|
||||
newRuleMap.remove(resourceName);
|
||||
} else {
|
||||
|
|
@ -134,10 +128,10 @@ public final class DegradeRuleManager {
|
|||
newSet.add(rule);
|
||||
}
|
||||
}
|
||||
newRuleMap.put(resourceName, newSet);
|
||||
newRuleMap.put(resourceName, new ArrayList<>(newSet));
|
||||
}
|
||||
List<DegradeRule> allRules = new ArrayList<>();
|
||||
for (Set<DegradeRule> set : newRuleMap.values()) {
|
||||
for (List<DegradeRule> set : newRuleMap.values()) {
|
||||
allRules.addAll(set);
|
||||
}
|
||||
return currentProperty.updateValue(allRules);
|
||||
|
|
@ -189,6 +183,9 @@ public final class DegradeRuleManager {
|
|||
if (rule.getMinRequestAmount() <= 0 || rule.getStatIntervalMs() <= 0) {
|
||||
return false;
|
||||
}
|
||||
if (!RuleManager.checkRegexResourceField(rule)) {
|
||||
return false;
|
||||
}
|
||||
switch (rule.getGrade()) {
|
||||
case RuleConstant.DEGRADE_GRADE_RT:
|
||||
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 synchronized void reloadFrom(List<DegradeRule> list) {
|
||||
Map<String, List<CircuitBreaker>> cbs = buildCircuitBreakers(list);
|
||||
Map<String, Set<DegradeRule>> rm = new HashMap<>(cbs.size());
|
||||
|
||||
for (Map.Entry<String, List<CircuitBreaker>> e : cbs.entrySet()) {
|
||||
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;
|
||||
Map<String, List<DegradeRule>> rules = buildCircuitBreakerRules(cbs);
|
||||
circuitBreakers.updateRules(cbs);
|
||||
ruleMap.updateRules(rules);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -233,6 +223,16 @@ public final class DegradeRuleManager {
|
|||
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) {
|
||||
Map<String, List<CircuitBreaker>> cbMap = new HashMap<>(8);
|
||||
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.PropertyListener;
|
||||
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.StringUtil;
|
||||
|
||||
|
|
@ -48,7 +49,7 @@ import java.util.concurrent.TimeUnit;
|
|||
*/
|
||||
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 SentinelProperty<List<FlowRule>> currentProperty = new DynamicSentinelProperty<List<FlowRule>>();
|
||||
|
|
@ -105,11 +106,7 @@ public class FlowRuleManager {
|
|||
* @return a new copy of the rules.
|
||||
*/
|
||||
public static List<FlowRule> getRules() {
|
||||
List<FlowRule> rules = new ArrayList<FlowRule>();
|
||||
for (Map.Entry<String, List<FlowRule>> entry : flowRules.entrySet()) {
|
||||
rules.addAll(entry.getValue());
|
||||
}
|
||||
return rules;
|
||||
return flowRules.getRules();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -121,12 +118,12 @@ public class FlowRuleManager {
|
|||
currentProperty.updateValue(rules);
|
||||
}
|
||||
|
||||
static Map<String, List<FlowRule>> getFlowRuleMap() {
|
||||
return flowRules;
|
||||
static List<FlowRule> getFlowRules(String resource) {
|
||||
return flowRules.getRules(resource);
|
||||
}
|
||||
|
||||
public static boolean hasConfig(String resource) {
|
||||
return flowRules.containsKey(resource);
|
||||
return flowRules.hasConfig(resource);
|
||||
}
|
||||
|
||||
public static boolean isOtherOrigin(String origin, String resourceName) {
|
||||
|
|
@ -134,7 +131,7 @@ public class FlowRuleManager {
|
|||
return false;
|
||||
}
|
||||
|
||||
List<FlowRule> rules = flowRules.get(resourceName);
|
||||
List<FlowRule> rules = flowRules.getRules(resourceName);
|
||||
|
||||
if (rules != null) {
|
||||
for (FlowRule rule : rules) {
|
||||
|
|
@ -152,18 +149,14 @@ public class FlowRuleManager {
|
|||
@Override
|
||||
public synchronized void configUpdate(List<FlowRule> value) {
|
||||
Map<String, List<FlowRule>> rules = FlowRuleUtil.buildFlowRuleMap(value);
|
||||
if (rules != null) {
|
||||
flowRules = rules;
|
||||
}
|
||||
flowRules.updateRules(rules);
|
||||
RecordLog.info("[FlowRuleManager] Flow rules received: {}", rules);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void configLoad(List<FlowRule> conf) {
|
||||
Map<String, List<FlowRule>> rules = FlowRuleUtil.buildFlowRuleMap(conf);
|
||||
if (rules != null) {
|
||||
flowRules = rules;
|
||||
}
|
||||
flowRules.updateRules(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.slots.block.ClusterRuleConstant;
|
||||
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.ThrottlingController;
|
||||
import com.alibaba.csp.sentinel.slots.block.flow.controller.WarmUpController;
|
||||
|
|
@ -170,6 +171,9 @@ public final class FlowRuleUtil {
|
|||
if (!baseValid) {
|
||||
return false;
|
||||
}
|
||||
if (!checkRegexField(rule)) {
|
||||
return false;
|
||||
}
|
||||
if (rule.getGrade() == RuleConstant.FLOW_GRADE_QPS) {
|
||||
// Check strategy and control (shaping) behavior.
|
||||
return checkClusterField(rule) && checkStrategyField(rule) && checkControlBehaviorField(rule);
|
||||
|
|
@ -237,6 +241,16 @@ public final class FlowRuleUtil {
|
|||
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) {
|
||||
switch (rule.getControlBehavior()) {
|
||||
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 java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
|
|
@ -179,9 +177,7 @@ public class FlowSlot extends AbstractLinkedProcessorSlot<DefaultNode> {
|
|||
private final Function<String, Collection<FlowRule>> ruleProvider = new Function<String, Collection<FlowRule>>() {
|
||||
@Override
|
||||
public Collection<FlowRule> apply(String resource) {
|
||||
// Flow rule map should not be null.
|
||||
Map<String, List<FlowRule>> flowRules = FlowRuleManager.getFlowRuleMap();
|
||||
return flowRules.get(resource);
|
||||
return FlowRuleManager.getFlowRules(resource);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,4 +27,14 @@ public interface Function<T, R> {
|
|||
* @return the function result
|
||||
*/
|
||||
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;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
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.RuleConstant;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* @author jialiang.linjl
|
||||
*/
|
||||
|
|
@ -114,6 +113,21 @@ public class FlowPartialIntegrationTest {
|
|||
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
|
||||
public void testOriginFlowRule() {
|
||||
String RESOURCE_NAME = "testOriginFlowRule";
|
||||
|
|
@ -255,4 +269,18 @@ public class FlowPartialIntegrationTest {
|
|||
|
||||
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.PropertyListener;
|
||||
import com.alibaba.csp.sentinel.property.SentinelProperty;
|
||||
import com.alibaba.csp.sentinel.slots.block.RuleManager;
|
||||
import com.alibaba.csp.sentinel.util.AssertUtil;
|
||||
|
||||
/**
|
||||
|
|
@ -35,8 +36,7 @@ import com.alibaba.csp.sentinel.util.AssertUtil;
|
|||
*/
|
||||
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 static SentinelProperty<List<ParamFlowRule>> currentProperty = new DynamicSentinelProperty<>();
|
||||
|
||||
|
|
@ -75,12 +75,11 @@ public final class ParamFlowRuleManager {
|
|||
}
|
||||
|
||||
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) {
|
||||
List<ParamFlowRule> rules = PARAM_FLOW_RULES.get(resourceName);
|
||||
return rules != null && !rules.isEmpty();
|
||||
return PARAM_FLOW_RULES.hasConfig(resourceName);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -89,11 +88,7 @@ public final class ParamFlowRuleManager {
|
|||
* @return a new copy of the rules.
|
||||
*/
|
||||
public static List<ParamFlowRule> getRules() {
|
||||
List<ParamFlowRule> rules = new ArrayList<>();
|
||||
for (Map.Entry<String, List<ParamFlowRule>> entry : PARAM_FLOW_RULES.entrySet()) {
|
||||
rules.addAll(entry.getValue());
|
||||
}
|
||||
return rules;
|
||||
return PARAM_FLOW_RULES.getRules();
|
||||
}
|
||||
|
||||
static class RulePropertyListener implements PropertyListener<List<ParamFlowRule>> {
|
||||
|
|
@ -101,20 +96,14 @@ public final class ParamFlowRuleManager {
|
|||
@Override
|
||||
public void configUpdate(List<ParamFlowRule> list) {
|
||||
Map<String, List<ParamFlowRule>> rules = aggregateAndPrepareParamRules(list);
|
||||
if (rules != null) {
|
||||
PARAM_FLOW_RULES.clear();
|
||||
PARAM_FLOW_RULES.putAll(rules);
|
||||
}
|
||||
PARAM_FLOW_RULES.updateRules(rules);
|
||||
RecordLog.info("[ParamFlowRuleManager] Parameter flow rules received: {}", PARAM_FLOW_RULES);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configLoad(List<ParamFlowRule> list) {
|
||||
Map<String, List<ParamFlowRule>> rules = aggregateAndPrepareParamRules(list);
|
||||
if (rules != null) {
|
||||
PARAM_FLOW_RULES.clear();
|
||||
PARAM_FLOW_RULES.putAll(rules);
|
||||
}
|
||||
PARAM_FLOW_RULES.updateRules(rules);
|
||||
RecordLog.info("[ParamFlowRuleManager] Parameter flow rules received: {}", PARAM_FLOW_RULES);
|
||||
}
|
||||
|
||||
|
|
@ -128,7 +117,7 @@ public final class ParamFlowRuleManager {
|
|||
}
|
||||
|
||||
// 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();
|
||||
if (!newRuleMap.containsKey(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.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.util.AssertUtil;
|
||||
import com.alibaba.csp.sentinel.util.StringUtil;
|
||||
|
|
@ -48,7 +49,7 @@ public final class ParamFlowRuleUtil {
|
|||
&& rule.getGrade() >= 0 && rule.getParamIdx() != null
|
||||
&& rule.getBurstCount() >= 0 && rule.getControlBehavior() >= 0
|
||||
&& rule.getDurationInSec() > 0 && rule.getMaxQueueingTimeMs() >= 0
|
||||
&& checkCluster(rule);
|
||||
&& checkCluster(rule) & checkRegexField(rule);
|
||||
}
|
||||
|
||||
private static boolean checkCluster(/*@PreChecked*/ ParamFlowRule rule) {
|
||||
|
|
@ -65,6 +66,16 @@ public final class ParamFlowRuleUtil {
|
|||
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) {
|
||||
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