-
Notifications
You must be signed in to change notification settings - Fork 8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: resource rules (flow/degrade/param/authority) support regex mat…
…ching Closes #3247
- Loading branch information
Showing
16 changed files
with
641 additions
and
104 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
186 changes: 186 additions & 0 deletions
186
sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/RuleManager.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.