You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@shenyu.apache.org by xi...@apache.org on 2022/06/27 08:53:34 UTC
[incubator-shenyu] branch master updated: [ISSUE #3367] optimize selector match (#3440)
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/incubator-shenyu.git
The following commit(s) were added to refs/heads/master by this push:
new 23b0ce652 [ISSUE #3367] optimize selector match (#3440)
23b0ce652 is described below
commit 23b0ce652f63f9a2ef532617bf9b8fa89d6009c9
Author: zouchangfu <50...@users.noreply.github.com>
AuthorDate: Mon Jun 27 16:53:25 2022 +0800
[ISSUE #3367] optimize selector match (#3440)
* [type: refactor] optimeze selector match
* [type: refactor] optimeze rule match
* fix code style
* [type: refactor] use the thread security list
* [type: refactor] obtain first selector and first rule
* [type: refactor] add cache switch and memory size
* [type: refactor] fix code style
* add MatchDataCacheTest
* [type: refactor] modify maxMemory to maxFreeMemory
* [type: refactor] fix code style
* [type: refactor] modify method name
* [type: refactor] fix code style
* [type: refactor] mock shenyuConfig
* [type: refactor] fix test bug
* [type: refactor] If selector match is empty or rule match is empty, cache the empty collection
* [type: refactor] fix check style
* [type: refactor] close cache switch
* [type: bug] fix match bug
* [type: bug] fix match bug
* [type: refactor] fix code style
* [type: refactor] optimize selector
* [type: refactor] fix code style
* [type: refactor] fix code style
* [type: refactor] only uri condition cache
* [type: refactor] Remove redundant code
* [type: refactor] fix code style
* [type: style] fix code
* [type: feat] only cache one selector
* [type: feat] fix cache bug
* [type: fix] fix cache bug
---
.../src/main/resources/application.yml | 4 +
.../apache/shenyu/common/config/ShenyuConfig.java | 66 +++++++++++++
.../shenyu/plugin/base/AbstractShenyuPlugin.java | 103 +++++++++++++++++----
.../base/cache/CommonPluginDataSubscriber.java | 3 +
.../shenyu/plugin/base/cache/MatchDataCache.java | 91 ++++++++++++++++++
.../plugin/base/AbstractShenyuPluginTest.java | 12 ++-
.../plugin/base/cache/MatchDataCacheTest.java | 75 +++++++++++++++
.../web/controller/LocalPluginController.java | 2 +
8 files changed, 336 insertions(+), 20 deletions(-)
diff --git a/shenyu-bootstrap/src/main/resources/application.yml b/shenyu-bootstrap/src/main/resources/application.yml
index f86cd8964..b5c2ea5a9 100644
--- a/shenyu-bootstrap/src/main/resources/application.yml
+++ b/shenyu-bootstrap/src/main/resources/application.yml
@@ -70,6 +70,10 @@ management:
enabled: false
shenyu:
+ matchCache:
+ enabled: true
+ # 256 * 1024 * 1024 = 256MB
+ maxFreeMemory: 268435456
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 45001ef53..925e572be 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
@@ -44,6 +44,8 @@ public class ShenyuConfig {
private FallbackPath fallback = new FallbackPath();
private ExtPlugin extPlugin = new ExtPlugin();
+
+ private MatchCache matchCache = new MatchCache();
private Scheduler scheduler = new Scheduler();
@@ -224,6 +226,24 @@ public class ShenyuConfig {
public void setExtPlugin(final ExtPlugin extPlugin) {
this.extPlugin = extPlugin;
}
+
+ /**
+ * Gets match cache.
+ *
+ * @return the match cache
+ */
+ public MatchCache getMatchCache() {
+ return matchCache;
+ }
+
+ /**
+ * Sets match cache.
+ *
+ * @param matchCache the match cache
+ */
+ public void setMatchCache(final MatchCache matchCache) {
+ this.matchCache = matchCache;
+ }
/**
* Gets file.
@@ -505,6 +525,52 @@ public class ShenyuConfig {
this.scheduleDelay = scheduleDelay;
}
}
+
+ /**
+ * the match cache.
+ */
+ public static class MatchCache {
+
+ private boolean enabled;
+
+ private Integer maxFreeMemory = 256 * 1024 * 1024;
+
+ /**
+ * Gets enabled.
+ *
+ * @return the enabled
+ */
+ public boolean getEnabled() {
+ return enabled;
+ }
+
+ /**
+ * Sets enabled.
+ *
+ * @param enabled the enabled
+ */
+ public void setEnabled(final boolean enabled) {
+ this.enabled = enabled;
+ }
+
+ /**
+ * Gets maxFreeMemory.
+ *
+ * @return the maxFreeMemory
+ */
+ public Integer getMaxFreeMemory() {
+ return maxFreeMemory;
+ }
+
+ /**
+ * Sets maxFreeMemory.
+ *
+ * @param maxFreeMemory the maxFreeMemory
+ */
+ public void setMaxFreeMemory(final Integer maxFreeMemory) {
+ this.maxFreeMemory = maxFreeMemory;
+ }
+ }
/**
* The type Exclude path.
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 20b9211b1..ff2e62e44 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
@@ -18,7 +18,10 @@
package org.apache.shenyu.plugin.base;
import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
+import org.apache.shenyu.common.config.ShenyuConfig;
+import org.apache.shenyu.common.dto.ConditionData;
import org.apache.shenyu.common.dto.PluginData;
import org.apache.shenyu.common.dto.RuleData;
import org.apache.shenyu.common.dto.SelectorData;
@@ -26,7 +29,9 @@ import org.apache.shenyu.common.enums.MatchModeEnum;
import org.apache.shenyu.common.enums.SelectorTypeEnum;
import org.apache.shenyu.plugin.api.ShenyuPlugin;
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.cache.MatchDataCache;
import org.apache.shenyu.plugin.base.condition.strategy.MatchStrategyFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -45,9 +50,19 @@ import java.util.stream.Collectors;
* abstract shenyu plugin please extends.
*/
public abstract class AbstractShenyuPlugin implements ShenyuPlugin {
-
+
private static final Logger LOG = LoggerFactory.getLogger(AbstractShenyuPlugin.class);
-
+
+ private static final String URI_CONDITION_TYPE = "uri";
+
+ private ShenyuConfig.MatchCache matchCacheConfig;
+
+ private SelectorData defaultSelectorData = new SelectorData();
+
+ {
+ defaultSelectorData.setPluginName(named());
+ }
+
/**
* this is Template Method child has Implement your own logic.
*
@@ -58,7 +73,7 @@ public abstract class AbstractShenyuPlugin implements ShenyuPlugin {
* @return {@code Mono<Void>} to indicate when request handling is complete
*/
protected abstract Mono<Void> doExecute(ServerWebExchange exchange, ShenyuPluginChain chain, SelectorData selector, RuleData rule);
-
+
/**
* Process the Web request and (optionally) delegate to the next
* {@code ShenyuPlugin} through the given {@link ShenyuPluginChain}.
@@ -69,18 +84,29 @@ public abstract class AbstractShenyuPlugin implements ShenyuPlugin {
*/
@Override
public Mono<Void> execute(final ServerWebExchange exchange, final ShenyuPluginChain chain) {
+ initMatchCacheConfig();
String pluginName = named();
PluginData pluginData = BaseDataCache.getInstance().obtainPluginData(pluginName);
if (pluginData != null && pluginData.getEnabled()) {
- final Collection<SelectorData> selectors = BaseDataCache.getInstance().obtainSelectorData(pluginName);
- if (CollectionUtils.isEmpty(selectors)) {
- return handleSelectorIfNull(pluginName, exchange, chain);
+ final String path = exchange.getRequest().getURI().getPath();
+ Pair<Boolean, SelectorData> resultSelectorData = obtainSelectorDataCacheIfEnabled(exchange);
+ SelectorData selectorData = resultSelectorData.getRight();
+ if (Boolean.TRUE.equals(resultSelectorData.getLeft())) {
+ List<SelectorData> selectors = BaseDataCache.getInstance().obtainSelectorData(pluginName);
+ if (CollectionUtils.isEmpty(selectors)) {
+ return handleSelectorIfNull(pluginName, exchange, chain);
+ }
+ Pair<Boolean, SelectorData> matchSelectorData = matchSelector(exchange, selectors);
+ selectorData = matchSelectorData.getRight();
+ if (matchSelectorData.getLeft()) {
+ cacheSelectorDataIfEnabled(path, selectorData);
+ }
}
- SelectorData selectorData = matchSelector(exchange, selectors);
if (Objects.isNull(selectorData)) {
return handleSelectorIfNull(pluginName, exchange, chain);
}
selectorLog(selectorData, pluginName);
+
List<RuleData> rules = BaseDataCache.getInstance().obtainRuleData(selectorData.getId());
if (CollectionUtils.isEmpty(rules)) {
return handleRuleIfNull(pluginName, exchange, chain);
@@ -100,25 +126,64 @@ public abstract class AbstractShenyuPlugin implements ShenyuPlugin {
}
return chain.execute(exchange);
}
-
+
+ private void initMatchCacheConfig() {
+ if (Objects.isNull(matchCacheConfig)) {
+ matchCacheConfig = SpringBeanUtils.getInstance().getBean(ShenyuConfig.class).getMatchCache();
+ }
+ }
+
+ private void cacheSelectorDataIfEnabled(final String path, final SelectorData selectorData) {
+ if (matchCacheConfig.getEnabled()) {
+ if (Objects.isNull(selectorData)) {
+ MatchDataCache.getInstance().cacheSelectorData(path, defaultSelectorData, matchCacheConfig.getMaxFreeMemory());
+ } else {
+ 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, matchCacheConfig.getMaxFreeMemory());
+ }
+ }
+ }
+ }
+ }
+
+ private Pair<Boolean, SelectorData> obtainSelectorDataCacheIfEnabled(final ServerWebExchange exchange) {
+ if (matchCacheConfig.getEnabled()) {
+ SelectorData selectorData = MatchDataCache.getInstance().obtainSelectorData(named(), exchange.getRequest().getURI().getPath());
+
+ if (Objects.isNull(selectorData)) {
+ return Pair.of(Boolean.TRUE, null);
+ }
+
+ if (StringUtils.isBlank(selectorData.getId())) {
+ return Pair.of(Boolean.FALSE, null);
+ }
+
+ return Pair.of(Boolean.FALSE, selectorData);
+ }
+ return Pair.of(Boolean.TRUE, null);
+ }
+
protected Mono<Void> handleSelectorIfNull(final String pluginName, final ServerWebExchange exchange, final ShenyuPluginChain chain) {
return chain.execute(exchange);
}
-
+
protected Mono<Void> handleRuleIfNull(final String pluginName, final ServerWebExchange exchange, final ShenyuPluginChain chain) {
return chain.execute(exchange);
}
-
- private SelectorData matchSelector(final ServerWebExchange exchange, final Collection<SelectorData> selectors) {
+
+ 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());
if (filterCollectors.size() > 1) {
- return manyMatchSelector(filterCollectors);
+ return Pair.of(Boolean.FALSE, manyMatchSelector(filterCollectors));
} else {
- return filterCollectors.stream().findFirst().orElse(null);
+ return Pair.of(Boolean.TRUE, filterCollectors.stream().findFirst().orElse(null));
}
}
-
+
private SelectorData manyMatchSelector(final List<SelectorData> filterCollectors) {
//What needs to be dealt with here is the and condition. If the number of and conditions is the same and is matched at the same time,
// it will be sorted by the sort field.
@@ -135,7 +200,7 @@ public abstract class AbstractShenyuPlugin implements ShenyuPlugin {
List<Pair<Integer, SelectorData>> pairs = collect.get(max);
return pairs.stream().map(Pair::getRight).min(Comparator.comparing(SelectorData::getSort)).orElse(null);
}
-
+
private Boolean filterSelector(final SelectorData selector, final ServerWebExchange exchange) {
if (selector.getType() == SelectorTypeEnum.CUSTOM_FLOW.getCode()) {
if (CollectionUtils.isEmpty(selector.getConditionList())) {
@@ -145,21 +210,21 @@ 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 Boolean filterRule(final RuleData ruleData, final ServerWebExchange exchange) {
return ruleData.getEnabled() && MatchStrategyFactory.match(ruleData.getMatchMode(), ruleData.getConditionDataList(), exchange);
}
-
+
private void selectorLog(final SelectorData selectorData, final String pluginName) {
if (selectorData.getLogged()) {
LOG.info("{} selector success match , selector name :{}", pluginName, selectorData.getName());
}
}
-
+
private void ruleLog(final RuleData ruleData, final String pluginName) {
if (ruleData.getLoged()) {
LOG.info("{} rule success match , rule name :{}", pluginName, ruleData.getName());
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 9a4c7831a..018d679f1 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
@@ -122,6 +122,7 @@ public class CommonPluginDataSubscriber implements PluginDataSubscriber {
@Override
public void refreshSelectorDataAll() {
BaseDataCache.getInstance().cleanSelectorData();
+ MatchDataCache.getInstance().cleanSelectorData();
}
@Override
@@ -189,6 +190,7 @@ public class CommonPluginDataSubscriber implements PluginDataSubscriber {
} else if (data instanceof SelectorData) {
SelectorData selectorData = (SelectorData) data;
BaseDataCache.getInstance().cacheSelectData(selectorData);
+ MatchDataCache.getInstance().removeSelectorData(selectorData.getPluginName());
Optional.ofNullable(handlerMap.get(selectorData.getPluginName()))
.ifPresent(handler -> handler.handlerSelector(selectorData));
@@ -233,6 +235,7 @@ public class CommonPluginDataSubscriber implements PluginDataSubscriber {
} else if (data instanceof SelectorData) {
SelectorData selectorData = (SelectorData) data;
BaseDataCache.getInstance().removeSelectData(selectorData);
+ MatchDataCache.getInstance().removeSelectorData(selectorData.getPluginName());
Optional.ofNullable(handlerMap.get(selectorData.getPluginName()))
.ifPresent(handler -> handler.removeSelector(selectorData));
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
new file mode 100644
index 000000000..aa1065c6d
--- /dev/null
+++ b/shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/cache/MatchDataCache.java
@@ -0,0 +1,91 @@
+/*
+ * 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.cache;
+
+import com.google.common.collect.Maps;
+import org.apache.shenyu.common.cache.MemorySafeLRUMap;
+import org.apache.shenyu.common.dto.SelectorData;
+
+import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.ConcurrentMap;
+
+
+/**
+ * The match data cache.
+ */
+public final class MatchDataCache {
+
+ private static final MatchDataCache INSTANCE = new MatchDataCache();
+
+ /**
+ * pluginName -> LRUMap.
+ */
+ private static final ConcurrentMap<String, Map<String, SelectorData>> SELECTOR_DATA_MAP = Maps.newConcurrentMap();
+
+ private MatchDataCache() {
+ }
+
+ /**
+ * Gets instance.
+ *
+ * @return the instance
+ */
+ public static MatchDataCache getInstance() {
+ return INSTANCE;
+ }
+
+ /**
+ * Remove selector data.
+ *
+ * @param pluginName the pluginName
+ */
+ public void removeSelectorData(final String pluginName) {
+ SELECTOR_DATA_MAP.remove(pluginName);
+ }
+
+ /**
+ * Clean selector data.
+ */
+ public void cleanSelectorData() {
+ SELECTOR_DATA_MAP.clear();
+ }
+
+ /**
+ * Cache selector data.
+ *
+ * @param path the path
+ * @param selectorData the selector data
+ * @param maxMemory the max memory
+ */
+ public void cacheSelectorData(final String path, final SelectorData selectorData, final Integer maxMemory) {
+ SELECTOR_DATA_MAP.computeIfAbsent(selectorData.getPluginName(), map -> new MemorySafeLRUMap<>(maxMemory, 1 << 16)).put(path, selectorData);
+ }
+
+ /**
+ * Obtain selector data.
+ *
+ * @param pluginName the pluginName
+ * @param path the path
+ * @return the selector data
+ */
+ public SelectorData obtainSelectorData(final String pluginName, final String path) {
+ final Map<String, SelectorData> lruMap = SELECTOR_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/AbstractShenyuPluginTest.java b/shenyu-plugin/shenyu-plugin-base/src/test/java/org/apache/shenyu/plugin/base/AbstractShenyuPluginTest.java
index a2bb7afc2..2dfbbd64f 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
@@ -17,20 +17,23 @@
package org.apache.shenyu.plugin.base;
+import org.apache.shenyu.common.config.ShenyuConfig;
import org.apache.shenyu.common.dto.ConditionData;
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.plugin.api.ShenyuPluginChain;
+import org.apache.shenyu.plugin.api.utils.SpringBeanUtils;
import org.apache.shenyu.plugin.base.cache.BaseDataCache;
import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.mock.http.server.reactive.MockServerHttpRequest;
import org.springframework.mock.web.server.MockServerWebExchange;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
-import org.junit.jupiter.api.Test;
import java.util.Collections;
import java.util.List;
@@ -59,6 +62,7 @@ public final class AbstractShenyuPluginTest {
@BeforeEach
public void setUp() {
+ mockShenyuConfig();
this.ruleData = RuleData.builder().id("1")
.selectorId("1").enabled(true)
.loged(true).sort(1).build();
@@ -153,6 +157,12 @@ public final class AbstractShenyuPluginTest {
StepVerifier.create(testShenyuPlugin.execute(exchange, shenyuPluginChain)).expectSubscription().verifyComplete();
}
+ private void mockShenyuConfig() {
+ ConfigurableApplicationContext context = mock(ConfigurableApplicationContext.class);
+ when(context.getBean(ShenyuConfig.class)).thenReturn(new ShenyuConfig());
+ SpringBeanUtils.getInstance().setApplicationContext(context);
+ }
+
static class TestShenyuPlugin extends AbstractShenyuPlugin {
@Override
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
new file mode 100644
index 000000000..132b8717e
--- /dev/null
+++ b/shenyu-plugin/shenyu-plugin-base/src/test/java/org/apache/shenyu/plugin/base/cache/MatchDataCacheTest.java
@@ -0,0 +1,75 @@
+/*
+ * 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.cache;
+
+import org.apache.shenyu.common.cache.MemorySafeLRUMap;
+import org.apache.shenyu.common.dto.SelectorData;
+import org.junit.jupiter.api.Test;
+
+import java.lang.reflect.Field;
+import java.util.concurrent.ConcurrentHashMap;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+@SuppressWarnings("unchecked")
+public final class MatchDataCacheTest {
+
+ private final String selectorMapStr = "SELECTOR_DATA_MAP";
+
+ private final String mockPluginName1 = "MOCK_PLUGIN_NAME_1";
+
+ private final String path1 = "/http/abc";
+
+ @Test
+ public void testCacheSelectorData() throws NoSuchFieldException, IllegalAccessException {
+ SelectorData firstCachedSelectorData = SelectorData.builder().id("1").pluginName(mockPluginName1).sort(1).build();
+ MatchDataCache.getInstance().cacheSelectorData(path1, firstCachedSelectorData, 5 * 1024);
+ ConcurrentHashMap<String, MemorySafeLRUMap<String, SelectorData>> selectorMap = getFieldByName(selectorMapStr);
+ assertEquals(firstCachedSelectorData, selectorMap.get(mockPluginName1).get(path1));
+ selectorMap.clear();
+ }
+
+ @Test
+ public void testObtainSelectorData() throws NoSuchFieldException, IllegalAccessException {
+ SelectorData firstSelectorData = SelectorData.builder().id("1").pluginName(mockPluginName1).sort(1).build();
+ ConcurrentHashMap<String, MemorySafeLRUMap<String, SelectorData>> selectorMap = getFieldByName(selectorMapStr);
+ selectorMap.put(mockPluginName1, new MemorySafeLRUMap<>(5 * 1024, 16));
+ selectorMap.get(mockPluginName1).put(path1, firstSelectorData);
+ SelectorData firstSelectorDataCache = MatchDataCache.getInstance().obtainSelectorData(mockPluginName1, path1);
+ assertEquals(firstSelectorData, firstSelectorDataCache);
+ selectorMap.clear();
+ }
+
+ @Test
+ public void testRemoveSelectorData() throws NoSuchFieldException, IllegalAccessException {
+ SelectorData firstCachedSelectorData = SelectorData.builder().id("1").pluginName(mockPluginName1).sort(1).build();
+ MatchDataCache.getInstance().cacheSelectorData(path1, firstCachedSelectorData, 5 * 1024);
+ MatchDataCache.getInstance().removeSelectorData(firstCachedSelectorData.getPluginName());
+ ConcurrentHashMap<String, MemorySafeLRUMap<String, SelectorData>> selectorMap = getFieldByName(selectorMapStr);
+ assertEquals(null, selectorMap.get(mockPluginName1));
+ selectorMap.clear();
+ }
+
+ @SuppressWarnings("rawtypes")
+ private ConcurrentHashMap getFieldByName(final String name) throws NoSuchFieldException, IllegalAccessException {
+ MatchDataCache matchDataCache = MatchDataCache.getInstance();
+ Field pluginMapField = matchDataCache.getClass().getDeclaredField(name);
+ pluginMapField.setAccessible(true);
+ return (ConcurrentHashMap) pluginMapField.get(matchDataCache);
+ }
+}
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 2851116ae..61a153e96 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
@@ -30,6 +30,7 @@ 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.base.cache.BaseDataCache;
+import org.apache.shenyu.plugin.base.cache.MatchDataCache;
import org.apache.shenyu.sync.data.api.PluginDataSubscriber;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -93,6 +94,7 @@ public class LocalPluginController {
List<SelectorData> selectorData = BaseDataCache.getInstance().obtainSelectorData(name);
List<String> selectorIds = selectorData.stream().map(SelectorData::getId).collect(Collectors.toList());
BaseDataCache.getInstance().removeSelectDataByPluginName(name);
+ MatchDataCache.getInstance().removeSelectorData(name);
for (String selectorId : selectorIds) {
BaseDataCache.getInstance().removeRuleDataBySelectorId(selectorId);
}