This is an automated email from the ASF dual-hosted git repository.
xiaoyu pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/shenyu.git
The following commit(s) were added to refs/heads/master by this push:
new 99cafc620 [type:feat] add trie match cache (#4222)
99cafc620 is described below
commit 99cafc6202c1fa74f4146247b230a87996cdcb8a
Author: moremind <[email protected]>
AuthorDate: Sat Dec 31 20:54:30 2022 -0600
[type:feat] add trie match cache (#4222)
* [type:feature] add trie match cache
* [type:feat] add shenyu trie
* [type:feat] add shenyu trie
* [type:feat] add trie match
* [type:feat] add shenyu trie
* [type:feat] add shenyu trie
* [type:feat] add shenyu trie
* [type:feat] add shenyu trie
* [type:feat] add shenyu trie
* [type:feat] add shenyu trie
* [type:feat] add shenyu trie
* [type:feat] add shenyu trie
* [type:feat] add shenyu trie
* [type:feat] add shenyu trie
* [type:feat] add shenyu trie
* fix checkstyle
* fix checkstyle
* Update AbstractShenyuPlugin.java
* fix checkstyle
* fix checkstyle
* fix checkstyle
* [type:feat] add trie
* [type:feat] add trie
* [type:feat] add trie
* [type:feat] add trie
* [type:feat] add trie
* [type:feat] add trie
* [type:feat] add trie
* [type:feat] add trie
* [type:feat] add trie
* add trie
* add trie
* [type:feat] add trie
* [type:feat] add trie
* [type:feat] add trie
Co-authored-by: xiaoyu <[email protected]>
---
.../shenyu/admin/service/impl/RuleServiceImpl.java | 2 +-
.../src/main/resources/application.yml | 6 +-
.../apache/shenyu/common/config/ShenyuConfig.java | 131 ++++--
.../shenyu/common/enums/RuleTrieEventEnum.java | 27 +-
.../shenyu/common/enums/TrieMatchModeEvent.java | 35 +-
shenyu-plugin/shenyu-plugin-base/pom.xml | 4 +
.../shenyu/plugin/base/AbstractShenyuPlugin.java | 71 ++--
.../base/cache/CommonPluginDataSubscriber.java | 12 +-
.../shenyu/plugin/base/cache/MatchDataCache.java | 40 --
.../plugin/base/cache/PluginHandlerEvent.java | 2 +
.../RuleTrieEvent.java} | 33 +-
.../apache/shenyu/plugin/base/trie/ShenyuTrie.java | 457 +++++++++++++++++++++
.../shenyu/plugin/base/trie/ShenyuTrieNode.java | 286 +++++++++++++
.../plugin/base/trie/ShenyuTrieRuleListener.java | 59 +++
.../plugin/base/AbstractShenyuPluginTest.java | 3 +
.../base/cache/CommonPluginDataSubscriberTest.java | 13 +
.../plugin/base/cache/MatchDataCacheTest.java | 32 --
.../starter/gateway/ShenyuConfiguration.java | 24 ++
.../web/controller/LocalPluginController.java | 19 +-
.../web/controller/LocalPluginControllerTest.java | 12 +
20 files changed, 1056 insertions(+), 212 deletions(-)
diff --git
a/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/impl/RuleServiceImpl.java
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/impl/RuleServiceImpl.java
index 756dbf15b..0c20995e7 100644
---
a/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/impl/RuleServiceImpl.java
+++
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/impl/RuleServiceImpl.java
@@ -273,7 +273,7 @@ public class RuleServiceImpl implements RuleService {
.orElseGet(ArrayList::new)
.stream()
.filter(Objects::nonNull)
- .collect(Collectors.toMap(RuleConditionDO::getRuleId,
ruleConditionDO ->
ListUtil.of(ConditionTransfer.INSTANCE.mapToRuleDO(ruleConditionDO)),
ListUtil::merge));
+ .collect(Collectors.toMap(RuleConditionDO::getRuleId,
ruleConditionDO ->
ListUtil.list(ConditionTransfer.INSTANCE.mapToRuleDO(ruleConditionDO)),
ListUtil::merge));
return ruleDOList.stream()
.filter(Objects::nonNull)
diff --git a/shenyu-bootstrap/src/main/resources/application.yml
b/shenyu-bootstrap/src/main/resources/application.yml
index 235ac1755..08dcbadbb 100644
--- a/shenyu-bootstrap/src/main/resources/application.yml
+++ b/shenyu-bootstrap/src/main/resources/application.yml
@@ -66,11 +66,13 @@ management:
enabled: false
shenyu:
+ trie:
+ trieChildrenSize: 10000
+ pathRuleCacheSize: 1000
+ matchMode: antPathMatch
matchCache:
selectorEnabled: false
- ruleEnabled: true
maxSelectorFreeMemory: 256 # 256MB
- maxRuleFreeMemory: 256 # 256MB
netty:
http:
# set to false, user can custom the netty tcp server config.
diff --git
a/shenyu-common/src/main/java/org/apache/shenyu/common/config/ShenyuConfig.java
b/shenyu-common/src/main/java/org/apache/shenyu/common/config/ShenyuConfig.java
index bd4d8bed3..7d319ecae 100644
---
a/shenyu-common/src/main/java/org/apache/shenyu/common/config/ShenyuConfig.java
+++
b/shenyu-common/src/main/java/org/apache/shenyu/common/config/ShenyuConfig.java
@@ -19,6 +19,7 @@ package org.apache.shenyu.common.config;
import org.apache.commons.lang3.StringUtils;
import org.apache.shenyu.common.concurrent.MemoryLimitCalculator;
+import org.apache.shenyu.common.enums.TrieMatchModeEvent;
import java.util.ArrayList;
import java.util.HashSet;
@@ -62,6 +63,8 @@ public class ShenyuConfig {
private SharedPool sharedPool = new SharedPool();
private MetricsConfig metrics = new MetricsConfig();
+
+ private ShenyuTrieConfig trie = new ShenyuTrieConfig();
/**
* Gets health.
@@ -332,7 +335,25 @@ public class ShenyuConfig {
public void setCross(final CrossFilterConfig cross) {
this.cross = cross;
}
-
+
+ /**
+ * get shenyu trie config.
+ *
+ * @return shenyu trie config
+ */
+ public ShenyuTrieConfig getTrie() {
+ return trie;
+ }
+
+ /**
+ * set shenyu trie config.
+ *
+ * @param trie trie config
+ */
+ public void setTrie(final ShenyuTrieConfig trie) {
+ this.trie = trie;
+ }
+
/**
* The type Scheduler.
*/
@@ -513,15 +534,11 @@ public class ShenyuConfig {
private boolean selectorEnabled;
- private boolean ruleEnabled;
-
/**
* Max free memory, unit mb.
*/
private Integer maxSelectorFreeMemory = 256;
- private Integer maxRuleFreeMemory = 256;
-
/**
* Get selector cache enabled.
*
@@ -540,23 +557,6 @@ public class ShenyuConfig {
this.selectorEnabled = selectorEnabled;
}
- /**
- * get rule cache enabled.
- *
- * @return rule enabled
- */
- public boolean getRuleEnabled() {
- return ruleEnabled;
- }
-
- /**
- * set rule cache enabled.
- *
- * @param ruleEnabled rule cache enabled
- */
- public void setRuleEnabled(final boolean ruleEnabled) {
- this.ruleEnabled = ruleEnabled;
- }
/**
* Gets maxFreeMemory.
@@ -575,24 +575,6 @@ public class ShenyuConfig {
public void setMaxSelectorFreeMemory(final Integer
maxSelectorFreeMemory) {
this.maxSelectorFreeMemory = maxSelectorFreeMemory;
}
-
- /**
- * Gets maxFreeMemory.
- *
- * @return the maxFreeMemory
- */
- public Integer getMaxRuleFreeMemory() {
- return maxRuleFreeMemory;
- }
-
- /**
- * Sets maxFreeMemory.
- *
- * @param maxRuleFreeMemory the maxFreeMemory
- */
- public void setMaxRuleFreeMemory(final Integer maxRuleFreeMemory) {
- this.maxRuleFreeMemory = maxRuleFreeMemory;
- }
}
/**
@@ -1706,4 +1688,73 @@ public class ShenyuConfig {
this.props = props;
}
}
+
+ /**
+ * shenyu trie config.
+ */
+ public static class ShenyuTrieConfig {
+ private Long trieChildrenSize = 10000L;
+
+ private Long pathRuleCacheSize = 1000L;
+
+ /**
+ * match mode.
+ * @see TrieMatchModeEvent
+ */
+ private String matchMode =
TrieMatchModeEvent.ANT_PATH_MATCH.getMatchMode();
+
+ /**
+ * get trie children size.
+ *
+ * @return trie children size
+ */
+ public Long getTrieChildrenSize() {
+ return trieChildrenSize;
+ }
+
+ /**
+ * set trie children size.
+ *
+ * @param trieChildrenSize trie children size
+ */
+ public void setTrieChildrenSize(final Long trieChildrenSize) {
+ this.trieChildrenSize = trieChildrenSize;
+ }
+
+ /**
+ * get path rule cache size.
+ *
+ * @return path rule cache size
+ */
+ public Long getPathRuleCacheSize() {
+ return pathRuleCacheSize;
+ }
+
+ /**
+ * set path rule cache size.
+ *
+ * @param pathRuleCacheSize path rule cache size
+ */
+ public void setPathRuleCacheSize(final Long pathRuleCacheSize) {
+ this.pathRuleCacheSize = pathRuleCacheSize;
+ }
+
+ /**
+ * get match mode.
+ *
+ * @return motch mode
+ */
+ public String getMatchMode() {
+ return matchMode;
+ }
+
+ /**
+ * set match mode.
+ *
+ * @param matchMode match mode
+ */
+ public void setMatchMode(final String matchMode) {
+ this.matchMode = matchMode;
+ }
+ }
}
diff --git
a/shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/cache/PluginHandlerEvent.java
b/shenyu-common/src/main/java/org/apache/shenyu/common/enums/RuleTrieEventEnum.java
similarity index 53%
copy from
shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/cache/PluginHandlerEvent.java
copy to
shenyu-common/src/main/java/org/apache/shenyu/common/enums/RuleTrieEventEnum.java
index acd3b4c65..3ebc22f38 100644
---
a/shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/cache/PluginHandlerEvent.java
+++
b/shenyu-common/src/main/java/org/apache/shenyu/common/enums/RuleTrieEventEnum.java
@@ -15,29 +15,20 @@
* limitations under the License.
*/
-package org.apache.shenyu.plugin.base.cache;
-
-import org.apache.shenyu.common.dto.PluginData;
-import org.apache.shenyu.common.enums.PluginHandlerEventEnum;
-import org.springframework.context.ApplicationEvent;
+package org.apache.shenyu.common.enums;
/**
- * event of sort plugin.
+ * Rule trie event enum.
*/
-public class PluginHandlerEvent extends ApplicationEvent {
-
- private final PluginHandlerEventEnum pluginHandlerEventEnum;
+public enum RuleTrieEventEnum {
- public PluginHandlerEvent(final PluginHandlerEventEnum
pluginHandlerEventEnum, final PluginData source) {
- super(source);
- this.pluginHandlerEventEnum = pluginHandlerEventEnum;
- }
+ /**
+ * Shenyu Trie insert event.
+ */
+ INSERT,
/**
- * get plugin handler.
- * @return plugin handler event
+ * Shenyu Trie remove event.
*/
- public PluginHandlerEventEnum getPluginStateEnums() {
- return pluginHandlerEventEnum;
- }
+ REMOVE;
}
diff --git
a/shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/cache/PluginHandlerEvent.java
b/shenyu-common/src/main/java/org/apache/shenyu/common/enums/TrieMatchModeEvent.java
similarity index 54%
copy from
shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/cache/PluginHandlerEvent.java
copy to
shenyu-common/src/main/java/org/apache/shenyu/common/enums/TrieMatchModeEvent.java
index acd3b4c65..8bc51d374 100644
---
a/shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/cache/PluginHandlerEvent.java
+++
b/shenyu-common/src/main/java/org/apache/shenyu/common/enums/TrieMatchModeEvent.java
@@ -15,29 +15,34 @@
* limitations under the License.
*/
-package org.apache.shenyu.plugin.base.cache;
-
-import org.apache.shenyu.common.dto.PluginData;
-import org.apache.shenyu.common.enums.PluginHandlerEventEnum;
-import org.springframework.context.ApplicationEvent;
+package org.apache.shenyu.common.enums;
/**
- * event of sort plugin.
+ * Shenyu match mode event.
*/
-public class PluginHandlerEvent extends ApplicationEvent {
+public enum TrieMatchModeEvent {
+ /**
+ * ant path match.
+ */
+ ANT_PATH_MATCH("antPathMatch"),
+
+ /**
+ * path pattern.
+ */
+ PATH_PATTERN("pathPattern");
- private final PluginHandlerEventEnum pluginHandlerEventEnum;
+ private final String matchMode;
- public PluginHandlerEvent(final PluginHandlerEventEnum
pluginHandlerEventEnum, final PluginData source) {
- super(source);
- this.pluginHandlerEventEnum = pluginHandlerEventEnum;
+ TrieMatchModeEvent(final String matchMode) {
+ this.matchMode = matchMode;
}
/**
- * get plugin handler.
- * @return plugin handler event
+ * get trie match mode.
+ *
+ * @return match mode
*/
- public PluginHandlerEventEnum getPluginStateEnums() {
- return pluginHandlerEventEnum;
+ public String getMatchMode() {
+ return matchMode;
}
}
diff --git a/shenyu-plugin/shenyu-plugin-base/pom.xml
b/shenyu-plugin/shenyu-plugin-base/pom.xml
index 0eb63c443..1dc8c5c6e 100644
--- a/shenyu-plugin/shenyu-plugin-base/pom.xml
+++ b/shenyu-plugin/shenyu-plugin-base/pom.xml
@@ -26,6 +26,10 @@
<artifactId>shenyu-plugin-base</artifactId>
<dependencies>
+ <dependency>
+ <groupId>com.github.ben-manes.caffeine</groupId>
+ <artifactId>caffeine</artifactId>
+ </dependency>
<dependency>
<groupId>org.apache.shenyu</groupId>
<artifactId>shenyu-plugin-api</artifactId>
diff --git
a/shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/AbstractShenyuPlugin.java
b/shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/AbstractShenyuPlugin.java
index f080ea95e..334d2715c 100644
---
a/shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/AbstractShenyuPlugin.java
+++
b/shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/AbstractShenyuPlugin.java
@@ -40,6 +40,8 @@ import org.apache.shenyu.plugin.api.utils.SpringBeanUtils;
import org.apache.shenyu.plugin.base.cache.BaseDataCache;
import org.apache.shenyu.plugin.base.cache.MatchDataCache;
import org.apache.shenyu.plugin.base.condition.strategy.MatchStrategyFactory;
+import org.apache.shenyu.plugin.base.trie.ShenyuTrie;
+import org.apache.shenyu.plugin.base.trie.ShenyuTrieNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.server.ServerWebExchange;
@@ -115,31 +117,31 @@ public abstract class AbstractShenyuPlugin implements
ShenyuPlugin {
// if continued, not match rules
return doExecute(exchange, chain, selectorData,
defaultRuleData(selectorData));
}
- // handle Rule
- RuleData ruleData = obtainRuleDataCacheIfEnabled(path);
List<RuleData> rules =
BaseDataCache.getInstance().obtainRuleData(selectorData.getId());
- if (Objects.nonNull(ruleData) && Objects.isNull(ruleData.getId())) {
- return handleRuleIfNull(pluginName, exchange, chain);
- }
if (CollectionUtils.isEmpty(rules)) {
return handleRuleIfNull(pluginName, exchange, chain);
}
+ RuleData ruleData;
if (selectorData.getType() == SelectorTypeEnum.FULL_FLOW.getCode()) {
//get last
RuleData rule = rules.get(rules.size() - 1);
printLog(rule, pluginName);
return doExecute(exchange, chain, selectorData, rule);
} else {
- Pair<Boolean, RuleData> matchRuleData = matchRule(exchange, rules);
- ruleData = matchRuleData.getRight();
+ // match path with rule uri condition
+ ShenyuTrieNode matchTrieNode =
SpringBeanUtils.getInstance().getBean(ShenyuTrie.class).match(path,
selectorData.getId());
+ if (Objects.nonNull(matchTrieNode)) {
+ List<RuleData> ruleDataList =
matchTrieNode.getPathRuleCache().getIfPresent(selectorData.getId());
+ if (CollectionUtils.isNotEmpty(ruleDataList)) {
+ ruleData = genericMatchRule(exchange, ruleDataList);
+ } else {
+ ruleData = genericMatchRule(exchange, rules);
+ }
+ } else {
+ ruleData = genericMatchRule(exchange, rules);
+ }
if (Objects.isNull(ruleData)) {
- this.cacheRuleData(pluginName, path, matchRuleData);
return handleRuleIfNull(pluginName, exchange, chain);
- } else {
- // if match success, cache rule.
- if (matchCacheConfig.getRuleEnabled() &&
matchRuleData.getLeft()) {
- cacheRuleData(path, ruleData);
- }
}
}
printLog(ruleData, pluginName);
@@ -166,45 +168,14 @@ public abstract class AbstractShenyuPlugin implements
ShenyuPlugin {
}
}
- private void cacheRuleData(final String pluginName, final String path,
final Pair<Boolean, RuleData> matchRuleData) {
- if (matchCacheConfig.getRuleEnabled() && matchRuleData.getLeft()) {
- // cache rule
- RuleData cacheRuleData = new RuleData();
- cacheRuleData.setPluginName(pluginName);
- cacheRuleData(path, cacheRuleData);
- }
- }
-
- private void cacheRuleData(final String path, final RuleData ruleData) {
- if (StringUtils.isBlank(ruleData.getId())) {
- MatchDataCache.getInstance().cacheRuleData(path, ruleData,
getRuleMaxFreeMemory());
- return;
- }
- List<ConditionData> conditionList = ruleData.getConditionDataList();
- if (CollectionUtils.isNotEmpty(conditionList)) {
- boolean isUriCondition = conditionList.stream().allMatch(v ->
URI_CONDITION_TYPE.equals(v.getParamType()));
- if (isUriCondition) {
- MatchDataCache.getInstance().cacheRuleData(path, ruleData,
getRuleMaxFreeMemory());
- }
- }
- }
-
private Integer getSelectorMaxFreeMemory() {
return matchCacheConfig.getMaxSelectorFreeMemory() * 1024 * 1024;
}
- private Integer getRuleMaxFreeMemory() {
- return matchCacheConfig.getMaxRuleFreeMemory() * 1024 * 1024;
- }
-
private SelectorData obtainSelectorDataCacheIfEnabled(final String path) {
return matchCacheConfig.getSelectorEnabled() ?
MatchDataCache.getInstance().obtainSelectorData(named(), path) : null;
}
- private RuleData obtainRuleDataCacheIfEnabled(final String path) {
- return matchCacheConfig.getRuleEnabled() ?
MatchDataCache.getInstance().obtainRuleData(named(), path) : null;
- }
-
protected RuleData defaultRuleData(final SelectorData selectorData) {
RuleData ruleData = new RuleData();
ruleData.setSelectorId(selectorData.getId());
@@ -259,14 +230,22 @@ public abstract class AbstractShenyuPlugin implements
ShenyuPlugin {
return true;
}
+ private RuleData genericMatchRule(final ServerWebExchange exchange, final
Collection<RuleData> rules) {
+ Pair<Boolean, RuleData> genericMatchRule = this.matchRule(exchange,
rules);
+ if (genericMatchRule.getLeft()) {
+ return genericMatchRule.getRight();
+ } else {
+ return null;
+ }
+ }
+
private Pair<Boolean, RuleData> matchRule(final ServerWebExchange
exchange, final Collection<RuleData> rules) {
- // RuleData ruleDataList = rules.stream().filter(rule ->
filterRule(rule, exchange)).findFirst().orElse(null);
List<RuleData> filterRuleData = rules.stream()
.filter(rule -> filterRule(rule, exchange))
.distinct()
.collect(Collectors.toList());
if (filterRuleData.size() > 1) {
- return Pair.of(Boolean.FALSE, manyMatchRule(filterRuleData));
+ return Pair.of(Boolean.TRUE, manyMatchRule(filterRuleData));
} else {
return Pair.of(Boolean.TRUE,
filterRuleData.stream().findFirst().orElse(null));
}
diff --git
a/shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/cache/CommonPluginDataSubscriber.java
b/shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/cache/CommonPluginDataSubscriber.java
index f967dc338..04e0a0240 100644
---
a/shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/cache/CommonPluginDataSubscriber.java
+++
b/shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/cache/CommonPluginDataSubscriber.java
@@ -23,7 +23,11 @@ import org.apache.shenyu.common.dto.RuleData;
import org.apache.shenyu.common.dto.SelectorData;
import org.apache.shenyu.common.enums.DataEventTypeEnum;
import org.apache.shenyu.common.enums.PluginHandlerEventEnum;
+import org.apache.shenyu.common.enums.RuleTrieEventEnum;
+import org.apache.shenyu.plugin.api.utils.SpringBeanUtils;
+import org.apache.shenyu.plugin.base.event.RuleTrieEvent;
import org.apache.shenyu.plugin.base.handler.PluginDataHandler;
+import org.apache.shenyu.plugin.base.trie.ShenyuTrie;
import org.apache.shenyu.sync.data.api.PluginDataSubscriber;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -146,7 +150,7 @@ public class CommonPluginDataSubscriber implements
PluginDataSubscriber {
@Override
public void refreshRuleDataAll() {
BaseDataCache.getInstance().cleanRuleData();
- MatchDataCache.getInstance().cleanRuleData();
+ SpringBeanUtils.getInstance().getBean(ShenyuTrie.class).clear();
}
@Override
@@ -198,10 +202,9 @@ public class CommonPluginDataSubscriber implements
PluginDataSubscriber {
} else if (data instanceof RuleData) {
RuleData ruleData = (RuleData) data;
BaseDataCache.getInstance().cacheRuleData(ruleData);
-
MatchDataCache.getInstance().removeRuleData(ruleData.getPluginName());
Optional.ofNullable(handlerMap.get(ruleData.getPluginName()))
.ifPresent(handler -> handler.handlerRule(ruleData));
-
+ eventPublisher.publishEvent(new
RuleTrieEvent(RuleTrieEventEnum.INSERT, ruleData));
}
}
@@ -244,10 +247,9 @@ public class CommonPluginDataSubscriber implements
PluginDataSubscriber {
} else if (data instanceof RuleData) {
RuleData ruleData = (RuleData) data;
BaseDataCache.getInstance().removeRuleData(ruleData);
-
MatchDataCache.getInstance().removeRuleData(ruleData.getPluginName());
Optional.ofNullable(handlerMap.get(ruleData.getPluginName()))
.ifPresent(handler -> handler.removeRule(ruleData));
-
+ eventPublisher.publishEvent(new
RuleTrieEvent(RuleTrieEventEnum.REMOVE, ruleData));
}
}
}
diff --git
a/shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/cache/MatchDataCache.java
b/shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/cache/MatchDataCache.java
index 79f58ce2f..5b84b1e1a 100644
---
a/shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/cache/MatchDataCache.java
+++
b/shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/cache/MatchDataCache.java
@@ -95,44 +95,4 @@ public final class MatchDataCache {
final Map<String, SelectorData> lruMap =
SELECTOR_DATA_MAP.get(pluginName);
return Optional.ofNullable(lruMap).orElse(Maps.newHashMap()).get(path);
}
-
- /**
- * remove rule data from RULE_DATA_MAP.
- *
- * @param pluginName plugin name
- */
- public void removeRuleData(final String pluginName) {
- RULE_DATA_MAP.remove(pluginName);
- }
-
- /**
- * clean rule data.
- */
- public void cleanRuleData() {
- RULE_DATA_MAP.clear();
- }
-
- /**
- * cache rule data to RULE_DATA_MAP, and put (key=pluginName,
value=ruleData) to {@linkplain MemorySafeWindowTinyLFUMap}.
- * {@linkplain MemorySafeWindowTinyLFUMap} memory decide by yourself, you
can config in shenyu-bootstrap application.yml.
- *
- * @param path uri path
- * @param ruleData rule data
- * @param maxMemory maxMemory
- */
- public void cacheRuleData(final String path, final RuleData ruleData,
final Integer maxMemory) {
- RULE_DATA_MAP.computeIfAbsent(ruleData.getPluginName(), map -> new
MemorySafeWindowTinyLFUMap<>(maxMemory, 1 << 16)).put(path, ruleData);
- }
-
- /**
- * get rule data from RULE_DATA_MAP.
- *
- * @param pluginName plugin name
- * @param path path
- * @return {@linkplain RuleData}
- */
- public RuleData obtainRuleData(final String pluginName, final String path)
{
- final Map<String, RuleData> lruMap = RULE_DATA_MAP.get(pluginName);
- return Optional.ofNullable(lruMap).orElse(Maps.newHashMap()).get(path);
- }
}
diff --git
a/shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/cache/PluginHandlerEvent.java
b/shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/cache/PluginHandlerEvent.java
index acd3b4c65..8dbe333dc 100644
---
a/shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/cache/PluginHandlerEvent.java
+++
b/shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/cache/PluginHandlerEvent.java
@@ -26,6 +26,8 @@ import org.springframework.context.ApplicationEvent;
*/
public class PluginHandlerEvent extends ApplicationEvent {
+ private static final long serialVersionUID = 3880398095608858961L;
+
private final PluginHandlerEventEnum pluginHandlerEventEnum;
public PluginHandlerEvent(final PluginHandlerEventEnum
pluginHandlerEventEnum, final PluginData source) {
diff --git
a/shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/cache/PluginHandlerEvent.java
b/shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/event/RuleTrieEvent.java
similarity index 52%
copy from
shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/cache/PluginHandlerEvent.java
copy to
shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/event/RuleTrieEvent.java
index acd3b4c65..91173ecd0 100644
---
a/shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/cache/PluginHandlerEvent.java
+++
b/shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/event/RuleTrieEvent.java
@@ -15,29 +15,38 @@
* limitations under the License.
*/
-package org.apache.shenyu.plugin.base.cache;
+package org.apache.shenyu.plugin.base.event;
-import org.apache.shenyu.common.dto.PluginData;
-import org.apache.shenyu.common.enums.PluginHandlerEventEnum;
+import org.apache.shenyu.common.dto.RuleData;
+import org.apache.shenyu.common.enums.RuleTrieEventEnum;
import org.springframework.context.ApplicationEvent;
/**
- * event of sort plugin.
+ * Rule trie event.
*/
-public class PluginHandlerEvent extends ApplicationEvent {
+public class RuleTrieEvent extends ApplicationEvent {
- private final PluginHandlerEventEnum pluginHandlerEventEnum;
+ private static final long serialVersionUID = -6616858497711197175L;
- public PluginHandlerEvent(final PluginHandlerEventEnum
pluginHandlerEventEnum, final PluginData source) {
+ private final RuleTrieEventEnum ruleTrieEventEnum;
+
+ /**
+ * shenyu trie event.
+ *
+ * @param ruleTrieEventEnum ruleTrieEventEnum
+ * @param source data
+ */
+ public RuleTrieEvent(final RuleTrieEventEnum ruleTrieEventEnum, final
RuleData source) {
super(source);
- this.pluginHandlerEventEnum = pluginHandlerEventEnum;
+ this.ruleTrieEventEnum = ruleTrieEventEnum;
}
/**
- * get plugin handler.
- * @return plugin handler event
+ * get rule trie build or remove event.
+ *
+ * @return {@linkplain RuleTrieEventEnum} include insert and remove event
*/
- public PluginHandlerEventEnum getPluginStateEnums() {
- return pluginHandlerEventEnum;
+ public RuleTrieEventEnum getRuleTrieEvent() {
+ return ruleTrieEventEnum;
}
}
diff --git
a/shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/trie/ShenyuTrie.java
b/shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/trie/ShenyuTrie.java
new file mode 100644
index 000000000..9ca3a3959
--- /dev/null
+++
b/shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/trie/ShenyuTrie.java
@@ -0,0 +1,457 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.shenyu.plugin.base.trie;
+
+import com.github.benmanes.caffeine.cache.Cache;
+import com.github.benmanes.caffeine.cache.Caffeine;
+import com.google.common.collect.Lists;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.shenyu.common.dto.RuleData;
+import org.apache.shenyu.common.enums.TrieMatchModeEvent;
+
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+public class ShenyuTrie {
+
+ private static final String WILDCARD = "*";
+
+ private static final String MATCH_ALL = "**";
+
+ private final ShenyuTrieNode root;
+
+ private final Long childrenSize;
+
+ private final Long pathRuleCacheSize;
+
+ private final Object lock = new Object();
+
+ /**
+ * the mode includes antPathMatch and pathPattern, please see {@linkplain
TrieMatchModeEvent}.
+ * antPathMatch means all full match, pathPattern is used in web.
+ */
+ private final String matchMode;
+
+ public ShenyuTrie(final Long pathRuleCacheSize, final Long childrenSize,
final String matchMode) {
+ this.root = new ShenyuTrieNode("/", "/", false, pathRuleCacheSize);
+ this.childrenSize = childrenSize;
+ this.pathRuleCacheSize = pathRuleCacheSize;
+ this.matchMode = matchMode;
+ }
+
+ /**
+ * clear the trie.
+ */
+ public void clear() {
+ cleanup(this.root.getChildren());
+ cleanup(this.root.getPathRuleCache());
+ cleanup(this.root.getPathVariablesSet());
+ this.root.setPathVariableNode(null);
+ }
+
+ /**
+ * judge the trie is empty.
+ *
+ * @param shenyuTrie trie
+ * @return status
+ */
+ public boolean isEmpty(final ShenyuTrie shenyuTrie) {
+ return shenyuTrie.root.getChildren().estimatedSize() == 0 &&
"/".equals(shenyuTrie.root.getMatchStr());
+ }
+
+ /**
+ * put node to trie.
+ *
+ * @param uriPath uri path
+ * @param ruleData rule data
+ * @param bizInfo biz info
+ */
+ public void putNode(final String uriPath, final RuleData ruleData, final
Object bizInfo) {
+ if (StringUtils.isNotBlank(uriPath)) {
+ String strippedPath = StringUtils.strip(uriPath, "/");
+ String[] pathParts = StringUtils.split(strippedPath, "/");
+ if (pathParts.length > 0) {
+ ShenyuTrieNode node = root;
+ for (int i = 0; i < pathParts.length; i++) {
+ boolean endOfPath = isMatchAllOrWildcard(pathParts[i]) &&
judgeEqual(i, pathParts.length - 1);
+ node = putNode0(pathParts[i], node, matchMode, endOfPath);
+ }
+ // after insert node, set full path and end of path
+ node.setFullPath(uriPath);
+ node.setEndOfPath(true);
+ node.setBizInfo(bizInfo);
+ if (Objects.isNull(node.getPathRuleCache())) {
+
node.setPathRuleCache(Caffeine.newBuilder().maximumSize(pathRuleCacheSize).build());
+ }
+ List<RuleData> ruleDataList = getVal(node.getPathRuleCache(),
ruleData.getSelectorId());
+ if (CollectionUtils.isNotEmpty(ruleDataList)) {
+ // synchronized list
+ synchronized (lock) {
+ ruleDataList.add(ruleData);
+ final List<RuleData> collect =
ruleDataList.stream().sorted(Comparator.comparing(RuleData::getSort)).collect(Collectors.toList());
+ node.getPathRuleCache().put(ruleData.getSelectorId(),
collect);
+ }
+ } else {
+ node.getPathRuleCache().put(ruleData.getSelectorId(),
Lists.newArrayList(ruleData));
+ }
+ }
+ }
+ }
+
+ /**
+ * put node to trie.
+ *
+ * @param segment current string
+ * @param shenyuTrieNode current trie node
+ * @param matchMode match mode
+ * @param isPathEnd end path
+ * @return {@linkplain ShenyuTrieNode}
+ */
+ private ShenyuTrieNode putNode0(final String segment, final ShenyuTrieNode
shenyuTrieNode,
+ final String matchMode, final boolean
isPathEnd) {
+ // if match mode is path pattern, when segment is * and **, return
current node
+ if (TrieMatchModeEvent.PATH_PATTERN.getMatchMode().equals(matchMode)) {
+ if (isMatchAll(segment)) {
+ // put node, and return node
+ return this.put(segment, shenyuTrieNode, true);
+ }
+ if (isMatchWildcard(segment)) {
+ ShenyuTrieNode wildcardNode = this.put(segment,
shenyuTrieNode, true);
+ wildcardNode.setWildcard(true);
+ return wildcardNode;
+ }
+ }
+ if
(TrieMatchModeEvent.ANT_PATH_MATCH.getMatchMode().equals(matchMode)) {
+ if (isMatchAll(segment) && isPathEnd) {
+ return this.put(segment, shenyuTrieNode, true);
+ }
+ if (isMatchWildcard(segment) && isPathEnd) {
+ ShenyuTrieNode wildcardNode = this.put(segment,
shenyuTrieNode, true);
+ wildcardNode.setWildcard(true);
+ return wildcardNode;
+ }
+ }
+ // dynamic route
+ if (segment.startsWith("{") && segment.endsWith("}")) {
+ ShenyuTrieNode childNode;
+ // contains key, get current pathVariable node
+ if (containsKey(shenyuTrieNode.getPathVariablesSet(), segment)) {
+ childNode = getVal(shenyuTrieNode.getPathVariablesSet(),
segment);
+ } else {
+ childNode = new ShenyuTrieNode();
+ childNode.setMatchStr(segment);
+ childNode.setEndOfPath(false);
+ if (Objects.isNull(shenyuTrieNode.getPathVariablesSet())) {
+
shenyuTrieNode.setPathVariablesSet(Caffeine.newBuilder().maximumSize(childrenSize).build());
+ }
+ shenyuTrieNode.getPathVariablesSet().put(segment, childNode);
+ shenyuTrieNode.setPathVariableNode(childNode);
+ }
+ return childNode;
+ }
+ return this.put(segment, shenyuTrieNode, false);
+ }
+
+ /**
+ * put node.
+ *
+ * @param segment segment
+ * @param shenyuTrieNode shenyu trie node
+ * @param endOfPath end of path
+ * @return ShenyuTrieNode
+ */
+ private ShenyuTrieNode put(final String segment, final ShenyuTrieNode
shenyuTrieNode, final boolean endOfPath) {
+ if (Objects.isNull(shenyuTrieNode.getChildren())) {
+
shenyuTrieNode.setChildren(Caffeine.newBuilder().maximumSize(childrenSize).build());
+ }
+ ShenyuTrieNode childrenNode;
+ if (containsKey(shenyuTrieNode.getChildren(), segment)) {
+ childrenNode = getVal(shenyuTrieNode.getChildren(), segment);
+ } else {
+ childrenNode = new ShenyuTrieNode();
+ childrenNode.setMatchStr(segment);
+ childrenNode.setEndOfPath(endOfPath);
+ shenyuTrieNode.getChildren().put(segment, childrenNode);
+ }
+ return childrenNode;
+ }
+
+ /**
+ * match trie, trie exist and match the path will return current node.
+ *
+ * @param uriPath uri path
+ * @param selectorId selectorId
+ * @return {@linkplain ShenyuTrieNode}
+ */
+ public ShenyuTrieNode match(final String uriPath, final String selectorId)
{
+ Objects.requireNonNull(selectorId);
+ if (!StringUtils.isEmpty(uriPath)) {
+ String strippedPath = StringUtils.strip(uriPath, "/");
+ String[] pathParts = StringUtils.split(strippedPath, "/");
+ if (pathParts.length > 0) {
+ ShenyuTrieNode currentNode = root;
+ for (int i = 0; i < pathParts.length; i++) {
+ String path = pathParts[i];
+ currentNode = matchNode(path, currentNode);
+ if (Objects.nonNull(currentNode)) {
+ boolean endPath = judgeEqual(i, pathParts.length - 1);
+ // path is not end, continue to execute
+ if (checkChildrenNotNull(currentNode) &&
!currentNode.getEndOfPath()) {
+ continue;
+ }
+ // include path variable node, general node, wildcard
node
+ if (endPath && checkPathRuleNotNull(currentNode)
+ &&
CollectionUtils.isNotEmpty(getVal(currentNode.getPathRuleCache(), selectorId)))
{
+ break;
+ }
+ // path is end and the match str is **, means match all
+ if (isMatchAll(currentNode.getMatchStr()) &&
currentNode.getEndOfPath()
+ && checkPathRuleNotNull(currentNode)
+ &&
CollectionUtils.isNotEmpty(getVal(currentNode.getPathRuleCache(), selectorId)))
{
+ break;
+ }
+ } else {
+ return null;
+ }
+ }
+ if (currentNode.getEndOfPath() ||
(Objects.nonNull(currentNode.getPathVariableNode()) &&
currentNode.getPathVariableNode().getEndOfPath())) {
+ return currentNode;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * match node.
+ * <p> priority: path > * > ** > pathVariableNode </p>
+ *
+ * @param segment path segment
+ * @param node node
+ * @return {@linkplain ShenyuTrieNode}
+ */
+ private ShenyuTrieNode matchNode(final String segment, final
ShenyuTrieNode node) {
+ if (Objects.nonNull(node)) {
+ // node exist in children,first find path, avoid A plug have
/http/**, B plug have /http/order/**
+ if (checkChildrenNotNull(node) && containsKey(node.getChildren(),
segment)) {
+ return getVal(node.getChildren(), segment);
+ }
+ if (checkChildrenNotNull(node) && containsKey(node.getChildren(),
WILDCARD)) {
+ return getVal(node.getChildren(), WILDCARD);
+ }
+ if (checkChildrenNotNull(node) && containsKey(node.getChildren(),
MATCH_ALL)) {
+ return getVal(node.getChildren(), MATCH_ALL);
+ }
+ // if node is path variable node
+ if (Objects.nonNull(node.getPathVariableNode())) {
+ return node.getPathVariableNode();
+ }
+ }
+ return null;
+ }
+
+ /**
+ * remove trie node.
+ * remove rules: query node of the current path, if the node exists,
+ * checks whether the current node is mapped to multiple plug-in rules.
+ * if the plug-in rules have only on mapping, remove the node from parent.
+ * if current node exists multi mappings, remove the mapping.
+ *
+ * @param path path
+ * @param selectorId selectorId
+ * @param ruleId ruleId
+ */
+ public void remove(final String path, final String selectorId, final
String ruleId) {
+ if (StringUtils.isNotBlank(path)) {
+ String strippedPath = StringUtils.strip(path, "/");
+ String[] pathParts = StringUtils.split(strippedPath, "/");
+ String key = pathParts[pathParts.length - 1];
+ ShenyuTrieNode currentNode = this.getNode(path);
+ // node is not null, judge exist many plugin mapping
+ if (Objects.nonNull(currentNode) &&
Objects.nonNull(currentNode.getPathRuleCache())) {
+ // check current mapping
+ currentNode.getPathRuleCache().cleanUp();
+ if (currentNode.getPathRuleCache().estimatedSize() == 1 &&
Objects.isNull(currentNode.getChildren())) {
+ // remove current node from parent node
+ String[] parentPathArray = Arrays.copyOfRange(pathParts,
0, pathParts.length - 1);
+ String parentPath = String.join("/", parentPathArray);
+ ShenyuTrieNode parentNode = this.getNode(parentPath);
+ parentNode.getChildren().invalidate(key);
+ parentNode.getChildren().cleanUp();
+ } else {
+ // remove plugin mapping
+ List<RuleData> delRuleData =
getVal(currentNode.getPathRuleCache(), selectorId);
+ if (CollectionUtils.isNotEmpty(delRuleData)) {
+ synchronized (lock) {
+ delRuleData.removeIf(rule ->
rule.getId().equals(ruleId));
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * get node from trie.
+ *
+ * @param uriPath uri path
+ * @return {@linkplain ShenyuTrieNode}
+ */
+ public ShenyuTrieNode getNode(final String uriPath) {
+ if (StringUtils.isNotBlank(uriPath)) {
+ String strippedPath = StringUtils.strip(uriPath, "/");
+ String[] pathParts = StringUtils.split(strippedPath, "/");
+ if (pathParts.length > 0) {
+ ShenyuTrieNode currentNode = root;
+ return getNode0(currentNode, pathParts);
+ } else {
+ return null;
+ }
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * get node.
+ *
+ * @param node node
+ * @param pathParts path parts
+ * @return {@linkplain ShenyuTrieNode}
+ */
+ private ShenyuTrieNode getNode0(final ShenyuTrieNode node, final String[]
pathParts) {
+ // get key in path part arrays
+ String key = pathParts[0];
+ String[] slice = Arrays.copyOfRange(pathParts, 1, pathParts.length);
+ // if exist one path
+ if (slice.length == 0) {
+ if (isPathVariable(key)) {
+ if (Objects.isNull(node.getPathVariableNode()) ||
!node.getPathVariableNode().getEndOfPath()) {
+ return null;
+ }
+ return node.getPathVariableNode();
+ } else if (isMatchAllOrWildcard(key)) {
+ return node;
+ } else {
+ if (Objects.isNull(node) || !checkChildrenNotNull(node)) {
+ return null;
+ }
+ return getVal(node.getChildren(), key);
+ }
+ } else {
+ if (isPathVariable(key)) {
+ if (Objects.isNull(node.getPathVariableNode())) {
+ return null;
+ }
+ return this.getNode0(node.getPathVariableNode(), slice);
+ } else {
+ if (Objects.isNull(node) || Objects.isNull(node.getChildren())
|| Objects.isNull(getVal(node.getChildren(), key))) {
+ return null;
+ } else {
+ return this.getNode0(getVal(node.getChildren(), key),
slice);
+ }
+ }
+ }
+ }
+
+ /**
+ * get current node biz info.
+ *
+ * @param trieNode trie
+ * @return biz info
+ */
+ private Object getBizInfo(final ShenyuTrieNode trieNode) {
+ return trieNode.getBizInfo();
+ }
+
+ /**
+ * match all, when the path is /ab/c/**, that means /a/b/c/d can be
matched.
+ *
+ * @param key key
+ * @return match result
+ */
+ private static boolean isMatchAll(final String key) {
+ return MATCH_ALL.equals(key);
+ }
+
+ /**
+ * match wildcard, when the path is /a/b/*, the matched path maybe /a/b/c
or /a/b/d and so on.
+ *
+ * @param key key
+ * @return match result
+ */
+ private static boolean isMatchWildcard(final String key) {
+ return WILDCARD.equals(key);
+ }
+
+ /**
+ * determines whether the string is * or **.
+ *
+ * @param key the path key
+ * @return true or false
+ */
+ private static boolean isMatchAllOrWildcard(final String key) {
+ return isMatchAll(key) || isMatchWildcard(key);
+ }
+
+ /**
+ * determines whether the string is path variable.
+ *
+ * @param key path string
+ * @return true or false
+ */
+ private static boolean isPathVariable(final String key) {
+ return Objects.nonNull(key) && key.startsWith("{") &&
key.endsWith("}");
+ }
+
+ private static <V> boolean containsKey(final Cache<String, V> cache, final
String key) {
+ V ret = getVal(cache, key);
+ return Objects.nonNull(ret);
+ }
+
+ private static <V> V getVal(final Cache<String, V> cache, final String
key) {
+ if (Objects.nonNull(cache)) {
+ return cache.getIfPresent(key);
+ } else {
+ return null;
+ }
+ }
+
+ private static <V> void cleanup(final Cache<String, V> cache) {
+ if (Objects.nonNull(cache)) {
+ cache.invalidateAll();
+ cache.cleanUp();
+ }
+ }
+
+ private static boolean checkChildrenNotNull(final ShenyuTrieNode
shenyuTrieNode) {
+ return Objects.nonNull(shenyuTrieNode) &&
Objects.nonNull(shenyuTrieNode.getChildren());
+ }
+
+ private static boolean checkPathRuleNotNull(final ShenyuTrieNode
shenyuTrieNode) {
+ return Objects.nonNull(shenyuTrieNode) &&
Objects.nonNull(shenyuTrieNode.getPathRuleCache());
+ }
+
+ private static boolean judgeEqual(final int param, final int actual) {
+ return param == actual;
+ }
+}
diff --git
a/shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/trie/ShenyuTrieNode.java
b/shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/trie/ShenyuTrieNode.java
new file mode 100644
index 000000000..b88fb825e
--- /dev/null
+++
b/shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/trie/ShenyuTrieNode.java
@@ -0,0 +1,286 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.shenyu.plugin.base.trie;
+
+import com.github.benmanes.caffeine.cache.Cache;
+import com.github.benmanes.caffeine.cache.Caffeine;
+import org.apache.shenyu.common.dto.RuleData;
+
+import java.io.Serializable;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * ShenyuTrieNode.
+ */
+public class ShenyuTrieNode implements Serializable {
+
+ private static final long serialVersionUID = -2347426887850566364L;
+
+ /**
+ * abc match abc, :a match all words as a variable names a, * match all
words ,** match all words and children.
+ */
+ private String matchStr;
+
+ /**
+ * full path.
+ */
+ private String fullPath;
+
+ /**
+ * in path /a/b/c, b is child of a, c is child of b.
+ */
+ private Cache<String, ShenyuTrieNode> children;
+
+ /**
+ * path variables.
+ */
+ private Cache<String, ShenyuTrieNode> pathVariablesSet;
+
+ /**
+ * path variable node.
+ */
+ private ShenyuTrieNode pathVariableNode;
+
+ /**
+ * isWildcard, match all nodes, /a/b/** /** is a match all Node.
+ */
+ private boolean isWildcard;
+
+ /**
+ * if true means a real path exists, /a/b/c/d only node of d is true,
a,b,c is false.
+ */
+ private boolean endOfPath;
+
+ /**
+ * selectorId mapping to RuleData.
+ */
+ private Cache<String, List<RuleData>> pathRuleCache;
+
+ /**
+ * biz info, route info and any other info store here, e.g. ruleId,
selectorId and so on.
+ */
+ private Object bizInfo;
+
+ public ShenyuTrieNode() {
+ }
+
+ public ShenyuTrieNode(final String matchStr, final String fullPath, final
boolean endOfPath, final Long size) {
+ this.matchStr = matchStr;
+ this.fullPath = fullPath;
+ this.endOfPath = endOfPath;
+ this.pathRuleCache = Caffeine.newBuilder().maximumSize(size).build();
+ this.pathVariablesSet =
Caffeine.newBuilder().maximumSize(size).build();
+ }
+
+ /**
+ * get match str.
+ *
+ * @return matched string
+ */
+ public String getMatchStr() {
+ return matchStr;
+ }
+
+ /**
+ * set match str.
+ *
+ * @param matchStr match string
+ */
+ public void setMatchStr(final String matchStr) {
+ this.matchStr = matchStr;
+ }
+
+ /**
+ * get full path.
+ *
+ * @return full path
+ */
+ public String getFullPath() {
+ return fullPath;
+ }
+
+ /**
+ * set full path.
+ *
+ * @param fullPath full path
+ */
+ public void setFullPath(final String fullPath) {
+ this.fullPath = fullPath;
+ }
+
+ /**
+ * get trie children.
+ *
+ * @return trie children cache
+ */
+ public Cache<String, ShenyuTrieNode> getChildren() {
+ return children;
+ }
+
+ /**
+ * get pathVariable set.
+ *
+ * @return path variable
+ */
+ public Cache<String, ShenyuTrieNode> getPathVariablesSet() {
+ return pathVariablesSet;
+ }
+
+ /**
+ * set pathVariable.
+ *
+ * @param pathVariablesSet pathVariablesSet
+ */
+ public void setPathVariablesSet(final Cache<String, ShenyuTrieNode>
pathVariablesSet) {
+ this.pathVariablesSet = pathVariablesSet;
+ }
+
+ /**
+ * get pathVariable node.
+ *
+ * @return ShenyuTrieNode
+ */
+ public ShenyuTrieNode getPathVariableNode() {
+ return pathVariableNode;
+ }
+
+ /**
+ * set pathVariable node.
+ * @param pathVariableNode pathVariableNode
+ */
+ public void setPathVariableNode(final ShenyuTrieNode pathVariableNode) {
+ this.pathVariableNode = pathVariableNode;
+ }
+
+ /**
+ * set children cache.
+ *
+ * @param children children
+ */
+ public void setChildren(final Cache<String, ShenyuTrieNode> children) {
+ this.children = children;
+ }
+
+ /**
+ * match all.
+ *
+ * @return match all will return true
+ */
+ public boolean getWildcard() {
+ return isWildcard;
+ }
+
+ /**
+ * set wildcard.
+ *
+ * @param wildcard wildcard
+ */
+ public void setWildcard(final boolean wildcard) {
+ isWildcard = wildcard;
+ }
+
+ /**
+ * this path is end of path.
+ *
+ * @return true means match success
+ */
+ public boolean getEndOfPath() {
+ return endOfPath;
+ }
+
+ /**
+ * set end of path.
+ *
+ * @param endOfPath end of path
+ */
+ public void setEndOfPath(final boolean endOfPath) {
+ this.endOfPath = endOfPath;
+ }
+
+ /**
+ * get current path biz info.
+ *
+ * @return biz info
+ */
+ public Object getBizInfo() {
+ return bizInfo;
+ }
+
+ /**
+ * set biz info.
+ *
+ * @param bizInfo bizInfo
+ */
+ public void setBizInfo(final Object bizInfo) {
+ this.bizInfo = bizInfo;
+ }
+
+ /**
+ * get path rule cache.
+ *
+ * @return rule cache
+ */
+ public Cache<String, List<RuleData>> getPathRuleCache() {
+ return pathRuleCache;
+ }
+
+ /**
+ * set path rule cache.
+ *
+ * @param pathRuleCache path rule cache
+ */
+ public void setPathRuleCache(final Cache<String, List<RuleData>>
pathRuleCache) {
+ this.pathRuleCache = pathRuleCache;
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ ShenyuTrieNode that = (ShenyuTrieNode) o;
+ return isWildcard == that.isWildcard && endOfPath == that.endOfPath &&
matchStr.equals(that.matchStr)
+ && fullPath.equals(that.fullPath) &&
children.equals(that.children)
+ && pathVariablesSet.equals(that.pathVariablesSet) &&
pathVariableNode.equals(that.pathVariableNode)
+ && pathRuleCache.equals(that.pathRuleCache) &&
bizInfo.equals(that.bizInfo);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(matchStr, fullPath, children, pathVariablesSet,
pathVariableNode, isWildcard, endOfPath, pathRuleCache, bizInfo);
+ }
+
+ @Override
+ public String toString() {
+ return "ShenyuTrieNode{"
+ + "matchStr='" + matchStr + '\''
+ + ", fullPath='" + fullPath + '\''
+ + ", children=" + children
+ + ", pathVariablesSet=" + pathVariablesSet
+ + ", pathVariableNode=" + pathVariableNode
+ + ", isWildcard=" + isWildcard
+ + ", endOfPath=" + endOfPath
+ + ", pathRuleCache=" + pathRuleCache
+ + ", bizInfo=" + bizInfo
+ + '}';
+ }
+}
diff --git
a/shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/trie/ShenyuTrieRuleListener.java
b/shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/trie/ShenyuTrieRuleListener.java
new file mode 100644
index 000000000..eebbab554
--- /dev/null
+++
b/shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/trie/ShenyuTrieRuleListener.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.shenyu.plugin.base.trie;
+
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.shenyu.common.dto.ConditionData;
+import org.apache.shenyu.common.dto.RuleData;
+import org.apache.shenyu.common.enums.ParamTypeEnum;
+import org.apache.shenyu.common.enums.RuleTrieEventEnum;
+import org.apache.shenyu.plugin.api.utils.SpringBeanUtils;
+import org.apache.shenyu.plugin.base.event.RuleTrieEvent;
+import org.springframework.context.ApplicationListener;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * shenyu trie rule change listener.
+ */
+public class ShenyuTrieRuleListener implements
ApplicationListener<RuleTrieEvent> {
+
+ @Override
+ public void onApplicationEvent(final RuleTrieEvent event) {
+ RuleTrieEventEnum eventEnum = event.getRuleTrieEvent();
+ RuleData ruleData = (RuleData) event.getSource();
+ List<ConditionData> conditionDataList =
ruleData.getConditionDataList();
+ List<ConditionData> filterConditions = conditionDataList.stream()
+ .filter(conditionData ->
ParamTypeEnum.URI.getName().equals(conditionData.getParamType()))
+ .collect(Collectors.toList());
+ if (CollectionUtils.isNotEmpty(filterConditions)) {
+ List<String> uriPaths =
filterConditions.stream().map(ConditionData::getParamValue).collect(Collectors.toList());
+ switch (eventEnum) {
+ case INSERT:
+ uriPaths.forEach(path ->
SpringBeanUtils.getInstance().getBean(ShenyuTrie.class).putNode(path, ruleData,
null));
+ break;
+ case REMOVE:
+ uriPaths.forEach(path ->
SpringBeanUtils.getInstance().getBean(ShenyuTrie.class).remove(path,
ruleData.getSelectorId(), ruleData.getId()));
+ break;
+ default:
+ throw new IllegalStateException("Unexpected value: " +
event.getRuleTrieEvent());
+ }
+ }
+ }
+}
diff --git
a/shenyu-plugin/shenyu-plugin-base/src/test/java/org/apache/shenyu/plugin/base/AbstractShenyuPluginTest.java
b/shenyu-plugin/shenyu-plugin-base/src/test/java/org/apache/shenyu/plugin/base/AbstractShenyuPluginTest.java
index 6213fb146..a23680cf6 100644
---
a/shenyu-plugin/shenyu-plugin-base/src/test/java/org/apache/shenyu/plugin/base/AbstractShenyuPluginTest.java
+++
b/shenyu-plugin/shenyu-plugin-base/src/test/java/org/apache/shenyu/plugin/base/AbstractShenyuPluginTest.java
@@ -23,9 +23,11 @@ import org.apache.shenyu.common.dto.PluginData;
import org.apache.shenyu.common.dto.RuleData;
import org.apache.shenyu.common.dto.SelectorData;
import org.apache.shenyu.common.enums.SelectorTypeEnum;
+import org.apache.shenyu.common.enums.TrieMatchModeEvent;
import org.apache.shenyu.plugin.api.ShenyuPluginChain;
import org.apache.shenyu.plugin.api.utils.SpringBeanUtils;
import org.apache.shenyu.plugin.base.cache.BaseDataCache;
+import org.apache.shenyu.plugin.base.trie.ShenyuTrie;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.context.ConfigurableApplicationContext;
@@ -169,6 +171,7 @@ public final class AbstractShenyuPluginTest {
private void mockShenyuConfig() {
ConfigurableApplicationContext context =
mock(ConfigurableApplicationContext.class);
when(context.getBean(ShenyuConfig.class)).thenReturn(new
ShenyuConfig());
+ when(context.getBean(ShenyuTrie.class)).thenReturn(new
ShenyuTrie(100L, 100L, TrieMatchModeEvent.ANT_PATH_MATCH.getMatchMode()));
SpringBeanUtils.getInstance().setApplicationContext(context);
}
diff --git
a/shenyu-plugin/shenyu-plugin-base/src/test/java/org/apache/shenyu/plugin/base/cache/CommonPluginDataSubscriberTest.java
b/shenyu-plugin/shenyu-plugin-base/src/test/java/org/apache/shenyu/plugin/base/cache/CommonPluginDataSubscriberTest.java
index a520cd0ff..b15599223 100644
---
a/shenyu-plugin/shenyu-plugin-base/src/test/java/org/apache/shenyu/plugin/base/cache/CommonPluginDataSubscriberTest.java
+++
b/shenyu-plugin/shenyu-plugin-base/src/test/java/org/apache/shenyu/plugin/base/cache/CommonPluginDataSubscriberTest.java
@@ -21,7 +21,10 @@ import com.google.common.collect.Lists;
import org.apache.shenyu.common.dto.PluginData;
import org.apache.shenyu.common.dto.RuleData;
import org.apache.shenyu.common.dto.SelectorData;
+import org.apache.shenyu.common.enums.TrieMatchModeEvent;
+import org.apache.shenyu.plugin.api.utils.SpringBeanUtils;
import org.apache.shenyu.plugin.base.handler.PluginDataHandler;
+import org.apache.shenyu.plugin.base.trie.ShenyuTrie;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
@@ -30,6 +33,7 @@ import org.mockito.junit.jupiter.MockitoExtension;
import org.mockito.junit.jupiter.MockitoSettings;
import org.mockito.quality.Strictness;
import org.springframework.context.ApplicationEventPublisher;
+import org.springframework.context.ConfigurableApplicationContext;
import java.util.ArrayList;
import java.util.List;
@@ -37,6 +41,8 @@ import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
/**
* Test cases for CommonPluginDataSubscriber.
@@ -66,6 +72,7 @@ public final class CommonPluginDataSubscriberTest {
@BeforeEach
public void setup() {
+ this.mockShenyuTrieConfig();
ArrayList<PluginDataHandler> pluginDataHandlerList =
Lists.newArrayList();
commonPluginDataSubscriber = new
CommonPluginDataSubscriber(pluginDataHandlerList, eventPublisher);
baseDataCache = BaseDataCache.getInstance();
@@ -223,4 +230,10 @@ public final class CommonPluginDataSubscriberTest {
assertEquals(Lists.newArrayList(),
baseDataCache.obtainRuleData(firstCachedRuleData.getSelectorId()));
assertEquals(Lists.newArrayList(secondCachedRuleData),
baseDataCache.obtainRuleData(secondCachedRuleData.getSelectorId()));
}
+
+ private void mockShenyuTrieConfig() {
+ ConfigurableApplicationContext context =
mock(ConfigurableApplicationContext.class);
+ when(context.getBean(ShenyuTrie.class)).thenReturn(new
ShenyuTrie(100L, 100L, TrieMatchModeEvent.ANT_PATH_MATCH.getMatchMode()));
+ SpringBeanUtils.getInstance().setApplicationContext(context);
+ }
}
diff --git
a/shenyu-plugin/shenyu-plugin-base/src/test/java/org/apache/shenyu/plugin/base/cache/MatchDataCacheTest.java
b/shenyu-plugin/shenyu-plugin-base/src/test/java/org/apache/shenyu/plugin/base/cache/MatchDataCacheTest.java
index 22e36a7a5..086d0f0d9 100644
---
a/shenyu-plugin/shenyu-plugin-base/src/test/java/org/apache/shenyu/plugin/base/cache/MatchDataCacheTest.java
+++
b/shenyu-plugin/shenyu-plugin-base/src/test/java/org/apache/shenyu/plugin/base/cache/MatchDataCacheTest.java
@@ -18,7 +18,6 @@
package org.apache.shenyu.plugin.base.cache;
import org.apache.shenyu.common.cache.MemorySafeWindowTinyLFUMap;
-import org.apache.shenyu.common.dto.RuleData;
import org.apache.shenyu.common.dto.SelectorData;
import org.junit.jupiter.api.Test;
@@ -69,37 +68,6 @@ public final class MatchDataCacheTest {
selectorMap.clear();
}
- @Test
- public void testCacheRuleData() throws NoSuchFieldException,
IllegalAccessException {
- RuleData cacheRuleData =
RuleData.builder().id("1").pluginName(mockPluginName1).sort(1).build();
- MatchDataCache.getInstance().cacheRuleData(path1, cacheRuleData, 5 *
1024);
- ConcurrentHashMap<String, MemorySafeWindowTinyLFUMap<String,
RuleData>> ruleMap = getFieldByName(ruleMapStr);
- assertEquals(cacheRuleData, ruleMap.get(mockPluginName1).get(path1));
- ruleMap.clear();
- }
-
- @Test
- public void testObtainRuleData() throws NoSuchFieldException,
IllegalAccessException {
- RuleData cacheRuleData =
RuleData.builder().id("1").pluginName(mockPluginName1).sort(1).build();
- ConcurrentHashMap<String, MemorySafeWindowTinyLFUMap<String,
RuleData>> ruleMap = getFieldByName(ruleMapStr);
- ruleMap.put(mockPluginName1, new MemorySafeWindowTinyLFUMap<>(5 *
1024, 16));
- ruleMap.get(mockPluginName1).put(path1, cacheRuleData);
- RuleData firstRuleDataCache =
MatchDataCache.getInstance().obtainRuleData(mockPluginName1, path1);
- assertEquals(cacheRuleData, firstRuleDataCache);
- ruleMap.clear();
- }
-
- @Test
- public void testRemoveRuleData() throws NoSuchFieldException,
IllegalAccessException {
-
- RuleData cacheRuleData =
RuleData.builder().id("1").pluginName(mockPluginName1).sort(1).build();
- MatchDataCache.getInstance().cacheRuleData(path1, cacheRuleData, 5 *
1024);
-
MatchDataCache.getInstance().removeRuleData(cacheRuleData.getPluginName());
- ConcurrentHashMap<String, MemorySafeWindowTinyLFUMap<String,
RuleData>> ruleMap = getFieldByName(ruleMapStr);
- assertNull(ruleMap.get(mockPluginName1));
- ruleMap.clear();
- }
-
@SuppressWarnings("rawtypes")
private ConcurrentHashMap getFieldByName(final String name) throws
NoSuchFieldException, IllegalAccessException {
MatchDataCache matchDataCache = MatchDataCache.getInstance();
diff --git
a/shenyu-spring-boot-starter/shenyu-spring-boot-starter-gateway/src/main/java/org/apache/shenyu/springboot/starter/gateway/ShenyuConfiguration.java
b/shenyu-spring-boot-starter/shenyu-spring-boot-starter-gateway/src/main/java/org/apache/shenyu/springboot/starter/gateway/ShenyuConfiguration.java
index 2294f2f89..2cf34b8c2 100644
---
a/shenyu-spring-boot-starter/shenyu-spring-boot-starter-gateway/src/main/java/org/apache/shenyu/springboot/starter/gateway/ShenyuConfiguration.java
+++
b/shenyu-spring-boot-starter/shenyu-spring-boot-starter-gateway/src/main/java/org/apache/shenyu/springboot/starter/gateway/ShenyuConfiguration.java
@@ -25,6 +25,8 @@ import
org.apache.shenyu.plugin.base.cache.CommonMetaDataSubscriber;
import org.apache.shenyu.plugin.base.cache.CommonPluginDataSubscriber;
import org.apache.shenyu.plugin.base.handler.MetaDataHandler;
import org.apache.shenyu.plugin.base.handler.PluginDataHandler;
+import org.apache.shenyu.plugin.base.trie.ShenyuTrie;
+import org.apache.shenyu.plugin.base.trie.ShenyuTrieRuleListener;
import org.apache.shenyu.sync.data.api.MetaDataSubscriber;
import org.apache.shenyu.sync.data.api.PluginDataSubscriber;
import org.apache.shenyu.web.configuration.ErrorHandlerConfiguration;
@@ -256,4 +258,26 @@ public class ShenyuConfiguration {
public ShenyuConfig shenyuConfig() {
return new ShenyuConfig();
}
+
+ /**
+ * shenyu trie config.
+ *
+ * @param shenyuConfig shenyu trie config
+ * @return ShenyuTrie
+ */
+ @Bean
+ public ShenyuTrie shenyuTrie(final ShenyuConfig shenyuConfig) {
+ return new ShenyuTrie(shenyuConfig.getTrie().getPathRuleCacheSize(),
shenyuConfig.getTrie().getTrieChildrenSize(),
+ shenyuConfig.getTrie().getMatchMode());
+ }
+
+ /**
+ * shenyu trie listener.
+ *
+ * @return ShenyuTrieRuleListener
+ */
+ @Bean
+ public ShenyuTrieRuleListener shenyuTrieRuleListener() {
+ return new ShenyuTrieRuleListener();
+ }
}
diff --git
a/shenyu-web/src/main/java/org/apache/shenyu/web/controller/LocalPluginController.java
b/shenyu-web/src/main/java/org/apache/shenyu/web/controller/LocalPluginController.java
index 5c68dcf9c..6acf9a52d 100644
---
a/shenyu-web/src/main/java/org/apache/shenyu/web/controller/LocalPluginController.java
+++
b/shenyu-web/src/main/java/org/apache/shenyu/web/controller/LocalPluginController.java
@@ -26,11 +26,14 @@ import org.apache.shenyu.common.dto.PluginData;
import org.apache.shenyu.common.dto.RuleData;
import org.apache.shenyu.common.dto.SelectorData;
import org.apache.shenyu.common.enums.MatchModeEnum;
+import org.apache.shenyu.common.enums.ParamTypeEnum;
import org.apache.shenyu.common.enums.SelectorTypeEnum;
import org.apache.shenyu.common.utils.JsonUtils;
import org.apache.shenyu.common.utils.UUIDUtils;
+import org.apache.shenyu.plugin.api.utils.SpringBeanUtils;
import org.apache.shenyu.plugin.base.cache.BaseDataCache;
import org.apache.shenyu.plugin.base.cache.MatchDataCache;
+import org.apache.shenyu.plugin.base.trie.ShenyuTrie;
import org.apache.shenyu.sync.data.api.PluginDataSubscriber;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -95,10 +98,24 @@ public class LocalPluginController {
final List<String> selectorIds =
selectorData.stream().map(SelectorData::getId).collect(Collectors.toList());
BaseDataCache.getInstance().removeSelectDataByPluginName(name);
MatchDataCache.getInstance().removeSelectorData(name);
- MatchDataCache.getInstance().removeRuleData(name);
for (String selectorId : selectorIds) {
BaseDataCache.getInstance().removeRuleDataBySelectorId(selectorId);
}
+ selectorIds.forEach(item -> {
+ List<RuleData> ruleDataList =
BaseDataCache.getInstance().obtainRuleData(item);
+ if (CollectionUtils.isNotEmpty(ruleDataList)) {
+ ruleDataList.forEach(rule -> {
+ List<ConditionData> conditionDataList =
rule.getConditionDataList();
+ List<ConditionData> filterConditions =
conditionDataList.stream()
+ .filter(conditionData ->
ParamTypeEnum.URI.getName().equals(conditionData.getParamType()))
+ .collect(Collectors.toList());
+ if (CollectionUtils.isNotEmpty(filterConditions)) {
+ List<String> uriPaths =
filterConditions.stream().map(ConditionData::getParamValue).collect(Collectors.toList());
+ uriPaths.forEach(path ->
SpringBeanUtils.getInstance().getBean(ShenyuTrie.class).remove(path, item,
rule.getId()));
+ }
+ });
+ }
+ });
return Mono.just(Constants.SUCCESS);
}
diff --git
a/shenyu-web/src/test/java/org/apache/shenyu/web/controller/LocalPluginControllerTest.java
b/shenyu-web/src/test/java/org/apache/shenyu/web/controller/LocalPluginControllerTest.java
index 1c6820a4c..29d79f1a6 100644
---
a/shenyu-web/src/test/java/org/apache/shenyu/web/controller/LocalPluginControllerTest.java
+++
b/shenyu-web/src/test/java/org/apache/shenyu/web/controller/LocalPluginControllerTest.java
@@ -29,11 +29,14 @@ import org.apache.shenyu.common.enums.LoadBalanceEnum;
import org.apache.shenyu.common.enums.OperatorEnum;
import org.apache.shenyu.common.enums.ParamTypeEnum;
import org.apache.shenyu.common.enums.PluginEnum;
+import org.apache.shenyu.common.enums.TrieMatchModeEvent;
import org.apache.shenyu.common.utils.GsonUtils;
import org.apache.shenyu.common.utils.JsonUtils;
+import org.apache.shenyu.plugin.api.utils.SpringBeanUtils;
import org.apache.shenyu.plugin.base.cache.BaseDataCache;
import org.apache.shenyu.plugin.base.cache.CommonPluginDataSubscriber;
import org.apache.shenyu.plugin.base.handler.PluginDataHandler;
+import org.apache.shenyu.plugin.base.trie.ShenyuTrie;
import org.apache.shenyu.sync.data.api.PluginDataSubscriber;
import org.apache.shenyu.web.controller.LocalPluginController.SelectorRuleData;
import org.junit.jupiter.api.Assertions;
@@ -45,6 +48,7 @@ import org.mockito.junit.jupiter.MockitoExtension;
import org.mockito.junit.jupiter.MockitoSettings;
import org.mockito.quality.Strictness;
import org.springframework.context.ApplicationEventPublisher;
+import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.mock.web.MockHttpServletResponse;
@@ -63,6 +67,7 @@ import java.util.stream.Stream;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
import static
org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@@ -84,6 +89,7 @@ public final class LocalPluginControllerTest {
@BeforeEach
public void setup() {
+ this.mockShenyuTrieConfig();
ArrayList<PluginDataHandler> pluginDataHandlerList =
Lists.newArrayList();
subscriber = new CommonPluginDataSubscriber(pluginDataHandlerList,
eventPublisher);
mockMvc = MockMvcBuilders.standaloneSetup(new
LocalPluginController(subscriber))
@@ -481,4 +487,10 @@ public final class LocalPluginControllerTest {
buildDefaultRuleData.invoke(localPluginController,
RuleData.builder().sort(1).enabled(true).loged(true).build());
}
+
+ private void mockShenyuTrieConfig() {
+ ConfigurableApplicationContext context =
mock(ConfigurableApplicationContext.class);
+ when(context.getBean(ShenyuTrie.class)).thenReturn(new
ShenyuTrie(100L, 100L, TrieMatchModeEvent.ANT_PATH_MATCH.getMatchMode()));
+ SpringBeanUtils.getInstance().setApplicationContext(context);
+ }
}