This is an automated email from the ASF dual-hosted git repository.

hefengen 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 54f233df0 [type:feature] add rule cache (#4164)
54f233df0 is described below

commit 54f233df0d788477db36efc357c6fc2c57bd3a52
Author: moremind <[email protected]>
AuthorDate: Tue Nov 8 22:23:39 2022 +0800

    [type:feature] add rule cache (#4164)
    
    * [ISSUE #4120] rule cache
    
    * add rule cache
    
    * add rule cache
    
    * add rule cache
    
    * add rule cache
    
    * add rule cache
    
    * [type:feature] add rule cache
    
    * [type:feature] add rule cache
---
 .../src/main/resources/application.yml             |   6 +-
 .../apache/shenyu/common/config/ShenyuConfig.java  |  68 +++++++++---
 .../shenyu/plugin/base/AbstractShenyuPlugin.java   | 123 +++++++++++++++++----
 .../base/cache/CommonPluginDataSubscriber.java     |   3 +
 .../shenyu/plugin/base/cache/MatchDataCache.java   |  47 ++++++++
 .../plugin/base/cache/MatchDataCacheTest.java      |  34 ++++++
 .../web/controller/LocalPluginController.java      |   3 +-
 7 files changed, 243 insertions(+), 41 deletions(-)

diff --git a/shenyu-bootstrap/src/main/resources/application.yml 
b/shenyu-bootstrap/src/main/resources/application.yml
index f61c5bcdd..533916b13 100644
--- a/shenyu-bootstrap/src/main/resources/application.yml
+++ b/shenyu-bootstrap/src/main/resources/application.yml
@@ -68,8 +68,10 @@ management:
 
 shenyu:
   matchCache:
-    enabled: false
-    maxFreeMemory: 256 # 256MB
+    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 eca75a02a..bd4d8bed3 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
@@ -511,29 +511,51 @@ public class ShenyuConfig {
      */
     public static class MatchCache {
 
-        private boolean enabled;
+        private boolean selectorEnabled;
+
+        private boolean ruleEnabled;
 
         /**
          * Max free memory, unit mb.
          */
-        private Integer maxFreeMemory = 256;
+        private Integer maxSelectorFreeMemory = 256;
+
+        private Integer maxRuleFreeMemory = 256;
 
         /**
-         * Gets enabled.
+         * Get selector cache enabled.
          *
          * @return the enabled
          */
-        public boolean getEnabled() {
-            return enabled;
+        public boolean getSelectorEnabled() {
+            return selectorEnabled;
         }
 
         /**
-         * Sets enabled.
+         * Set selector enabled.
          *
-         * @param enabled the enabled
+         * @param selectorEnabled the enabled
          */
-        public void setEnabled(final boolean enabled) {
-            this.enabled = enabled;
+        public void setSelectorEnabled(final boolean selectorEnabled) {
+            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;
         }
 
         /**
@@ -541,17 +563,35 @@ public class ShenyuConfig {
          *
          * @return the maxFreeMemory
          */
-        public Integer getMaxFreeMemory() {
-            return maxFreeMemory;
+        public Integer getMaxSelectorFreeMemory() {
+            return maxSelectorFreeMemory;
         }
 
         /**
          * Sets maxFreeMemory.
          *
-         * @param maxFreeMemory the maxFreeMemory
+         * @param maxSelectorFreeMemory the maxFreeMemory
          */
-        public void setMaxFreeMemory(final Integer maxFreeMemory) {
-            this.maxFreeMemory = maxFreeMemory;
+        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;
         }
     }
     
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 197929d45..5a56c0a28 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
@@ -92,14 +92,14 @@ public abstract class AbstractShenyuPlugin implements 
ShenyuPlugin {
                 Pair<Boolean, SelectorData> matchSelectorData = 
matchSelector(exchange, selectors);
                 selectorData = matchSelectorData.getRight();
                 if (Objects.isNull(selectorData)) {
-                    if (matchCacheConfig.getEnabled() && 
matchSelectorData.getLeft()) {
+                    if (matchCacheConfig.getSelectorEnabled() && 
matchSelectorData.getLeft()) {
                         selectorData = new SelectorData();
                         selectorData.setPluginName(named());
                         cacheSelectorData(path, selectorData);
                     }
                     return handleSelectorIfNull(pluginName, exchange, chain);
                 } else {
-                    if (matchCacheConfig.getEnabled() && 
matchSelectorData.getLeft()) {
+                    if (matchCacheConfig.getSelectorEnabled() && 
matchSelectorData.getLeft()) {
                         cacheSelectorData(path, selectorData);
                     }
                 }
@@ -113,22 +113,40 @@ public abstract class AbstractShenyuPlugin implements 
ShenyuPlugin {
                 // if continued, not match rules
                 return doExecute(exchange, chain, selectorData, 
defaultRuleData(selectorData));
             }
-            List<RuleData> rules = 
BaseDataCache.getInstance().obtainRuleData(selectorData.getId());
-            if (CollectionUtils.isEmpty(rules)) {
-                return handleRuleIfNull(pluginName, exchange, chain);
-            }
-            RuleData rule;
-            if (selectorData.getType() == 
SelectorTypeEnum.FULL_FLOW.getCode()) {
-                //get last
-                rule = rules.get(rules.size() - 1);
+            // handle rule
+            RuleData ruleData = obtainRuleDataCacheIfEnabled(exchange);
+            if (Objects.isNull(ruleData)) {
+                // execute no cache
+                List<RuleData> rules = 
BaseDataCache.getInstance().obtainRuleData(selectorData.getId());
+                if (CollectionUtils.isEmpty(rules)) {
+                    return handleRuleIfNull(pluginName, exchange, chain);
+                }
+                if (selectorData.getType() == 
SelectorTypeEnum.FULL_FLOW.getCode()) {
+                    //get last
+                    RuleData rule = rules.get(rules.size() - 1);
+                    ruleLog(rule, pluginName);
+                    return doExecute(exchange, chain, selectorData, rule);
+                } else {
+                    Pair<Boolean, RuleData> matchRuleData = 
matchRule(exchange, rules);
+                    ruleData = matchRuleData.getRight();
+                    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);
+                        }
+                    }
+                }
             } else {
-                rule = matchRule(exchange, rules);
-            }
-            if (Objects.isNull(rule)) {
-                return handleRuleIfNull(pluginName, exchange, chain);
+                if (Objects.isNull(ruleData.getId())) {
+                    // handle rule not match
+                    return handleRuleIfNull(pluginName, exchange, chain);
+                }
             }
-            ruleLog(rule, pluginName);
-            return doExecute(exchange, chain, selectorData, rule);
+            ruleLog(ruleData, pluginName);
+            return doExecute(exchange, chain, selectorData, ruleData);
         }
         return chain.execute(exchange);
     }
@@ -141,24 +159,55 @@ public abstract class AbstractShenyuPlugin implements 
ShenyuPlugin {
 
     private void cacheSelectorData(final String path, final SelectorData 
selectorData) {
         if (StringUtils.isBlank(selectorData.getId())) {
-            MatchDataCache.getInstance().cacheSelectorData(path, selectorData, 
getMaxFreeMemory());
+            MatchDataCache.getInstance().cacheSelectorData(path, selectorData, 
getSelectorMaxFreeMemory());
             return;
         }
         List<ConditionData> conditionList = selectorData.getConditionList();
         if (CollectionUtils.isNotEmpty(conditionList)) {
             boolean isUriCondition = conditionList.stream().allMatch(v -> 
URI_CONDITION_TYPE.equals(v.getParamType()));
             if (isUriCondition) {
-                MatchDataCache.getInstance().cacheSelectorData(path, 
selectorData, getMaxFreeMemory());
+                MatchDataCache.getInstance().cacheSelectorData(path, 
selectorData, getSelectorMaxFreeMemory());
+            }
+        }
+    }
+
+    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 getMaxFreeMemory() {
-        return matchCacheConfig.getMaxFreeMemory() * 1024 * 1024;
+    private Integer getSelectorMaxFreeMemory() {
+        return matchCacheConfig.getMaxSelectorFreeMemory() * 1024 * 1024;
+    }
+
+    private Integer getRuleMaxFreeMemory() {
+        return matchCacheConfig.getMaxRuleFreeMemory() * 1024 * 1024;
     }
 
     private SelectorData obtainSelectorDataCacheIfEnabled(final 
ServerWebExchange exchange) {
-        return matchCacheConfig.getEnabled() ? 
MatchDataCache.getInstance().obtainSelectorData(named(), 
exchange.getRequest().getURI().getPath()) : null;
+        return matchCacheConfig.getSelectorEnabled() ? 
MatchDataCache.getInstance().obtainSelectorData(named(), 
exchange.getRequest().getURI().getPath()) : null;
+    }
+
+    private RuleData obtainRuleDataCacheIfEnabled(final ServerWebExchange 
exchange) {
+        return matchCacheConfig.getRuleEnabled() ? 
MatchDataCache.getInstance().obtainRuleData(named(), 
exchange.getRequest().getURI().getPath()) : null;
     }
     
     protected RuleData defaultRuleData(final SelectorData selectorData) {
@@ -178,7 +227,9 @@ public abstract class AbstractShenyuPlugin implements 
ShenyuPlugin {
 
     private Pair<Boolean, SelectorData> matchSelector(final ServerWebExchange 
exchange, final Collection<SelectorData> selectors) {
         List<SelectorData> filterCollectors = selectors.stream()
-                .filter(selector -> selector.getEnabled() && 
filterSelector(selector, exchange)).collect(Collectors.toList());
+                .filter(selector -> selector.getEnabled() && 
filterSelector(selector, exchange))
+                .distinct()
+                .collect(Collectors.toList());
         if (filterCollectors.size() > 1) {
             return Pair.of(Boolean.FALSE, manyMatchSelector(filterCollectors));
         } else {
@@ -213,8 +264,32 @@ public abstract class AbstractShenyuPlugin implements 
ShenyuPlugin {
         return true;
     }
 
-    private RuleData matchRule(final ServerWebExchange exchange, final 
Collection<RuleData> rules) {
-        return rules.stream().filter(rule -> filterRule(rule, 
exchange)).findFirst().orElse(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));
+        } else {
+            return Pair.of(Boolean.TRUE, 
filterRuleData.stream().findFirst().orElse(null));
+        }
+    }
+
+    private RuleData manyMatchRule(final List<RuleData> filterRuleData) {
+        Map<Integer, List<Pair<Integer, RuleData>>> collect =
+                filterRuleData.stream().map(rule -> {
+                    boolean match = MatchModeEnum.match(rule.getMatchMode(), 
MatchModeEnum.AND);
+                    int sort = 0;
+                    if (match) {
+                        sort = rule.getConditionDataList().size();
+                    }
+                    return Pair.of(sort, rule);
+                }).collect(Collectors.groupingBy(Pair::getLeft));
+        Integer max = Collections.max(collect.keySet());
+        List<Pair<Integer, RuleData>> pairs = collect.get(max);
+        return 
pairs.stream().map(Pair::getRight).min(Comparator.comparing(RuleData::getSort)).orElse(null);
     }
 
     private Boolean filterRule(final RuleData ruleData, final 
ServerWebExchange exchange) {
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 018d679f1..f967dc338 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
@@ -146,6 +146,7 @@ public class CommonPluginDataSubscriber implements 
PluginDataSubscriber {
     @Override
     public void refreshRuleDataAll() {
         BaseDataCache.getInstance().cleanRuleData();
+        MatchDataCache.getInstance().cleanRuleData();
     }
     
     @Override
@@ -197,6 +198,7 @@ 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));
             
@@ -242,6 +244,7 @@ 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));
             
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 00c1ae932..79f58ce2f 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
@@ -19,6 +19,7 @@ package org.apache.shenyu.plugin.base.cache;
 
 import com.google.common.collect.Maps;
 import org.apache.shenyu.common.cache.MemorySafeWindowTinyLFUMap;
+import org.apache.shenyu.common.dto.RuleData;
 import org.apache.shenyu.common.dto.SelectorData;
 
 import java.util.Map;
@@ -38,6 +39,12 @@ public final class MatchDataCache {
      */
     private static final ConcurrentMap<String, Map<String, SelectorData>> 
SELECTOR_DATA_MAP = Maps.newConcurrentMap();
 
+    /**
+     * plugin name -> LRU Map.
+     * LRU Map: path -> rule data.
+     */
+    private static final ConcurrentMap<String, Map<String, RuleData>> 
RULE_DATA_MAP = Maps.newConcurrentMap();
+
     private MatchDataCache() {
     }
 
@@ -88,4 +95,44 @@ 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/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 2084b6057..22e36a7a5 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,6 +18,7 @@
 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;
 
@@ -32,6 +33,8 @@ public final class MatchDataCacheTest {
 
     private final String selectorMapStr = "SELECTOR_DATA_MAP";
 
+    private final String ruleMapStr = "RULE_DATA_MAP";
+
     private final String mockPluginName1 = "MOCK_PLUGIN_NAME_1";
 
     private final String path1 = "/http/abc";
@@ -66,6 +69,37 @@ 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-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 d88d7feff..6e75463b3 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
@@ -92,9 +92,10 @@ public class LocalPluginController {
         LOG.info("clean apache shenyu local plugin for {}", name);
         BaseDataCache.getInstance().removePluginDataByPluginName(name);
         List<SelectorData> selectorData = 
BaseDataCache.getInstance().obtainSelectorData(name);
-        List<String> selectorIds = 
selectorData.stream().map(SelectorData::getId).collect(Collectors.toList());
+        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);
         }

Reply via email to