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);
+    }
 }

Reply via email to