feat: skip regex resource matching when exact match exists (default disabled, configurable)(#3565)

* Simple rules should have higher priority than regular expression rules

* Add optional switch to skip regex matching when simple rules already match, default: false, keeps backward compatibility.

* fix RuleManagerTest unit test failure
This commit is contained in:
gaoyf 2025-11-03 17:57:14 +08:00 committed by GitHub
parent d75a6687b8
commit e60f0d0eee
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 15 additions and 2 deletions

View File

@ -61,6 +61,7 @@ public final class SentinelConfig {
public static final String STATISTIC_MAX_RT = "csp.sentinel.statistic.max.rt"; public static final String STATISTIC_MAX_RT = "csp.sentinel.statistic.max.rt";
public static final String SPI_CLASSLOADER = "csp.sentinel.spi.classloader"; public static final String SPI_CLASSLOADER = "csp.sentinel.spi.classloader";
public static final String METRIC_FLUSH_INTERVAL = "csp.sentinel.metric.flush.interval"; public static final String METRIC_FLUSH_INTERVAL = "csp.sentinel.metric.flush.interval";
public static final String SKIP_REGEX_IF_SIMPLE_RULE_MATCHED_KEY = "csp.sentinel.rule.regex.skip.if.simple.matched";
public static final String DEFAULT_CHARSET = "UTF-8"; public static final String DEFAULT_CHARSET = "UTF-8";
public static final long DEFAULT_SINGLE_METRIC_FILE_SIZE = 1024 * 1024 * 50; public static final long DEFAULT_SINGLE_METRIC_FILE_SIZE = 1024 * 1024 * 50;
@ -68,6 +69,7 @@ public final class SentinelConfig {
public static final int DEFAULT_COLD_FACTOR = 3; public static final int DEFAULT_COLD_FACTOR = 3;
public static final int DEFAULT_STATISTIC_MAX_RT = 5000; public static final int DEFAULT_STATISTIC_MAX_RT = 5000;
public static final long DEFAULT_METRIC_FLUSH_INTERVAL = 1L; public static final long DEFAULT_METRIC_FLUSH_INTERVAL = 1L;
public static final String DEFAULT_SKIP_REGEX_IF_SIMPLE_RULE_MATCHED = "false";
static { static {
try { try {
@ -106,6 +108,7 @@ public final class SentinelConfig {
setConfig(COLD_FACTOR, String.valueOf(DEFAULT_COLD_FACTOR)); setConfig(COLD_FACTOR, String.valueOf(DEFAULT_COLD_FACTOR));
setConfig(STATISTIC_MAX_RT, String.valueOf(DEFAULT_STATISTIC_MAX_RT)); setConfig(STATISTIC_MAX_RT, String.valueOf(DEFAULT_STATISTIC_MAX_RT));
setConfig(METRIC_FLUSH_INTERVAL, String.valueOf(DEFAULT_METRIC_FLUSH_INTERVAL)); setConfig(METRIC_FLUSH_INTERVAL, String.valueOf(DEFAULT_METRIC_FLUSH_INTERVAL));
setConfig(SKIP_REGEX_IF_SIMPLE_RULE_MATCHED_KEY, DEFAULT_SKIP_REGEX_IF_SIMPLE_RULE_MATCHED);
} }
private static void loadProps() { private static void loadProps() {
@ -341,5 +344,13 @@ public final class SentinelConfig {
return CLASSLOADER_CONTEXT.equalsIgnoreCase(classloaderConf); return CLASSLOADER_CONTEXT.equalsIgnoreCase(classloaderConf);
} }
/**
* Return whether to skip regex matching when simple rules already matched.
* Default: false (keeps backward compatibility).
*/
public static boolean shouldSkipRegexIfSimpleRuleMatched() {
return Boolean.parseBoolean(getConfig(SKIP_REGEX_IF_SIMPLE_RULE_MATCHED_KEY));
}
private SentinelConfig() {} private SentinelConfig() {}
} }

View File

@ -17,6 +17,7 @@ package com.alibaba.csp.sentinel.slots.block;
import com.alibaba.csp.sentinel.util.function.Function; import com.alibaba.csp.sentinel.util.function.Function;
import com.alibaba.csp.sentinel.util.function.Predicate; import com.alibaba.csp.sentinel.util.function.Predicate;
import com.alibaba.csp.sentinel.config.SentinelConfig;
import java.util.*; import java.util.*;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@ -86,7 +87,7 @@ public class RuleManager<R> {
*/ */
public List<R> getRules(String resource) { public List<R> getRules(String resource) {
List<R> result = new ArrayList<>(simpleRules.getOrDefault(resource, Collections.emptyList())); List<R> result = new ArrayList<>(simpleRules.getOrDefault(resource, Collections.emptyList()));
if (regexRules.isEmpty()) { if (regexRules.isEmpty() || (SentinelConfig.shouldSkipRegexIfSimpleRuleMatched() && !result.isEmpty())) {
return result; return result;
} }
if (regexCacheRules.containsKey(resource)) { if (regexCacheRules.containsKey(resource)) {

View File

@ -26,6 +26,7 @@ public class SentinelConfigTest {
assertEquals(SentinelConfig.DEFAULT_TOTAL_METRIC_FILE_COUNT, SentinelConfig.totalMetricFileCount()); assertEquals(SentinelConfig.DEFAULT_TOTAL_METRIC_FILE_COUNT, SentinelConfig.totalMetricFileCount());
assertEquals(SentinelConfig.DEFAULT_COLD_FACTOR, SentinelConfig.coldFactor()); assertEquals(SentinelConfig.DEFAULT_COLD_FACTOR, SentinelConfig.coldFactor());
assertEquals(SentinelConfig.DEFAULT_STATISTIC_MAX_RT, SentinelConfig.statisticMaxRt()); assertEquals(SentinelConfig.DEFAULT_STATISTIC_MAX_RT, SentinelConfig.statisticMaxRt());
assertEquals(false, SentinelConfig.shouldSkipRegexIfSimpleRuleMatched());
} }
// add JVM parameter // add JVM parameter

View File

@ -51,7 +51,7 @@ public class RuleManagerTest {
Field regexCacheRules = RuleManager.class.getDeclaredField("regexCacheRules"); Field regexCacheRules = RuleManager.class.getDeclaredField("regexCacheRules");
regexCacheRules.setAccessible(true); regexCacheRules.setAccessible(true);
assertEquals(((Map)regexCacheRules.get(ruleManager)).size(), 0); assertEquals(((Map)regexCacheRules.get(ruleManager)).size(), 0);
ruleManager.getRules("rule2"); ruleManager.getRules("rule");
assertEquals(((Map)regexCacheRules.get(ruleManager)).size(), 1); assertEquals(((Map)regexCacheRules.get(ruleManager)).size(), 1);
} }