From 2eb2ded297973bc6ac2a6c12c68a181c1dbdc216 Mon Sep 17 00:00:00 2001 From: "zunfei.lzf" Date: Fri, 10 Jan 2025 11:49:24 +0800 Subject: [PATCH 1/8] pattern match over limit optimize --- .../alibaba/nacos/api/common/Constants.java | 6 + .../request/ConfigFuzzyWatchSyncRequest.java | 12 +- .../impl/ConfigFuzzyWatchGroupKeyHolder.java | 28 ++- .../NamingFuzzyWatchServiceListHolder.java | 21 ++- .../NamingFuzzyWatchNotifyRequestHandler.java | 7 + .../configuration/ConfigCommonConfig.java | 24 ++- .../event/ConfigCancelFuzzyWatchEvent.java | 76 -------- .../ConfigFuzzyWatchRequestHandler.java | 21 ++- .../remote/ConfigFuzzyWatchSyncNotifier.java | 19 +- .../ConfigFuzzyWatchContextService.java | 56 +++--- .../ConfigFuzzyWatchContextServiceTest.java | 165 ++++++++++++++++++ .../alibaba/nacos/core/utils/StringPool.java | 4 - .../v2/event/client/ClientOperationEvent.java | 27 --- .../index/NamingFuzzyWatchContextService.java | 74 +++++--- .../nacos/naming/misc/GlobalConfig.java | 9 + .../push/NamingFuzzyWatchSyncNotifier.java | 18 +- .../NamingFuzzyWatchRequestHandler.java | 18 +- 17 files changed, 374 insertions(+), 211 deletions(-) delete mode 100644 config/src/main/java/com/alibaba/nacos/config/server/model/event/ConfigCancelFuzzyWatchEvent.java create mode 100644 config/src/test/java/com/alibaba/nacos/config/server/service/ConfigFuzzyWatchContextServiceTest.java diff --git a/api/src/main/java/com/alibaba/nacos/api/common/Constants.java b/api/src/main/java/com/alibaba/nacos/api/common/Constants.java index 0faa1bc469d..22a723daa6e 100644 --- a/api/src/main/java/com/alibaba/nacos/api/common/Constants.java +++ b/api/src/main/java/com/alibaba/nacos/api/common/Constants.java @@ -298,6 +298,12 @@ public static class Naming { */ public static final String FUZZY_WATCH_RESOURCE_CHANGED = "FUZZY_WATCH_RESOURCE_CHANGED"; + + /** + * fuzzy watch sync type of watch init notify finish. + */ + public static final String FUZZY_WATCH_MATCH_RESOURCE_OVER_LIMIT = "FUZZY_WATCH_MATCH_RESOURCE_OVER_LIMIT"; + /** * watch type of watch. */ diff --git a/api/src/main/java/com/alibaba/nacos/api/config/remote/request/ConfigFuzzyWatchSyncRequest.java b/api/src/main/java/com/alibaba/nacos/api/config/remote/request/ConfigFuzzyWatchSyncRequest.java index e6ead123c78..4c1a383a965 100644 --- a/api/src/main/java/com/alibaba/nacos/api/config/remote/request/ConfigFuzzyWatchSyncRequest.java +++ b/api/src/main/java/com/alibaba/nacos/api/config/remote/request/ConfigFuzzyWatchSyncRequest.java @@ -109,7 +109,7 @@ public static ConfigFuzzyWatchSyncRequest buildSyncRequest(String syncType, Set< } /** - * Builds a final FuzzyListenNotifyDiffRequest with the specified group key pattern. + * Builds fuzzy watch init finish requst * * @param groupKeyPattern The pattern used to match group keys for the configurations * @return A final FuzzyListenNotifyDiffRequest @@ -118,6 +118,16 @@ public static ConfigFuzzyWatchSyncRequest buildInitFinishRequest(String groupKey return new ConfigFuzzyWatchSyncRequest(Constants.FINISH_FUZZY_WATCH_INIT_NOTIFY, groupKeyPattern, null, 0, 0); } + /** + * Builds a final FUZZY_WATCH_MATCH_RESOURCE_OVER_LIMIT with the specified group key pattern. + * + * @param groupKeyPattern The pattern used to match group keys for the configurations + * @return A final FuzzyListenNotifyDiffRequest + */ + public static ConfigFuzzyWatchSyncRequest buildOverLimitRequest(String groupKeyPattern) { + return new ConfigFuzzyWatchSyncRequest(Constants.FUZZY_WATCH_MATCH_RESOURCE_OVER_LIMIT, groupKeyPattern, null, 0, 0); + } + public String getGroupKeyPattern() { return groupKeyPattern; } diff --git a/client/src/main/java/com/alibaba/nacos/client/config/impl/ConfigFuzzyWatchGroupKeyHolder.java b/client/src/main/java/com/alibaba/nacos/client/config/impl/ConfigFuzzyWatchGroupKeyHolder.java index f64a8ccbff0..458de242df0 100644 --- a/client/src/main/java/com/alibaba/nacos/client/config/impl/ConfigFuzzyWatchGroupKeyHolder.java +++ b/client/src/main/java/com/alibaba/nacos/client/config/impl/ConfigFuzzyWatchGroupKeyHolder.java @@ -56,6 +56,7 @@ import static com.alibaba.nacos.api.common.Constants.FUZZY_WATCH_RESOURCE_CHANGED; import static com.alibaba.nacos.api.common.Constants.WATCH_TYPE_CANCEL_WATCH; import static com.alibaba.nacos.api.common.Constants.WATCH_TYPE_WATCH; +import static com.alibaba.nacos.api.model.v2.ErrorCode.FUZZY_WATCH_PATTERN_OVER_LIMIT; /** * config fuzzy watch context holder. @@ -213,6 +214,12 @@ ConfigFuzzyWatchSyncResponse handleFuzzyWatchNotifyDiffRequest(ConfigFuzzyWatchS context.markInitializationComplete(); return new ConfigFuzzyWatchSyncResponse(); } + + if (Constants.FUZZY_WATCH_MATCH_RESOURCE_OVER_LIMIT.equals(request.getSyncType())) { + LOGGER.info("[{}] [fuzzy-watch] pattern match config config count reach to up limit,pattern ->{}, received keys count {}", + agent.getName(), request.getGroupKeyPattern(), context.getReceivedGroupKeys().size()); + return new ConfigFuzzyWatchSyncResponse(); + } LOGGER.info( "[{}] [fuzzy-watch-diff-sync-push] pattern ->{},syncType={},,syncCount={},totalBatch={},currentBatch={}", @@ -406,15 +413,20 @@ private void doExecuteConfigFuzzyListen(List contextLis } } catch (NacosException e) { - // Log error and retry after a short delay - LOGGER.error("Execute batch fuzzy listen config change error.", e); - try { - Thread.sleep(50L); - } catch (InterruptedException interruptedException) { - // Ignore interruption + + if (FUZZY_WATCH_PATTERN_OVER_LIMIT.getCode()==e.getErrCode()){ + LOGGER.error(" fuzzy watch pattern over limit,pattern ->{} ,fuzzy watch will be suppressed",entry.getGroupKeyPattern()); + } else { + // Log error and retry after a short delay + LOGGER.error("Execute batch fuzzy listen config change error.", e); + try { + Thread.sleep(50L); + } catch (InterruptedException interruptedException) { + // Ignore interruption + } + // Retry notification + notifyFuzzyWatchSync(); } - // Retry notification - notifyFuzzyWatchSync(); } }); listenFutures.add(future); diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/cache/NamingFuzzyWatchServiceListHolder.java b/client/src/main/java/com/alibaba/nacos/client/naming/cache/NamingFuzzyWatchServiceListHolder.java index 64fcc984eec..88f84aef69b 100644 --- a/client/src/main/java/com/alibaba/nacos/client/naming/cache/NamingFuzzyWatchServiceListHolder.java +++ b/client/src/main/java/com/alibaba/nacos/client/naming/cache/NamingFuzzyWatchServiceListHolder.java @@ -45,6 +45,7 @@ import static com.alibaba.nacos.api.common.Constants.ServiceChangedType.ADD_SERVICE; import static com.alibaba.nacos.api.common.Constants.WATCH_TYPE_CANCEL_WATCH; import static com.alibaba.nacos.api.common.Constants.WATCH_TYPE_WATCH; +import static com.alibaba.nacos.api.model.v2.ErrorCode.FUZZY_WATCH_PATTERN_OVER_LIMIT; /** * Naming client fuzzy watch service list holder. @@ -273,18 +274,24 @@ private void doExecuteNamingFuzzyWatch(List contextList } else { entry.setConsistentWithServer(true); } - } + } catch (NacosException e) { // Log error and retry after a short delay LOGGER.error(" fuzzy watch request fail.", e); - try { - Thread.sleep(500L); - } catch (InterruptedException interruptedException) { - // Ignore interruption + + if (FUZZY_WATCH_PATTERN_OVER_LIMIT.getCode()==e.getErrCode()){ + LOGGER.error(" fuzzy watch pattern over limit,pattern ->{} ,fuzzy watch will be suppressed",entry.getGroupKeyPattern()); + } else { + try { + Thread.sleep(100L); + } catch (InterruptedException interruptedException) { + // Ignore interruption + } + // Retry notification + notifyFuzzyWatchSync(); } - // Retry notification - notifyFuzzyWatchSync(); + } } diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/NamingFuzzyWatchNotifyRequestHandler.java b/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/NamingFuzzyWatchNotifyRequestHandler.java index aa40d048a01..268d104e713 100644 --- a/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/NamingFuzzyWatchNotifyRequestHandler.java +++ b/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/NamingFuzzyWatchNotifyRequestHandler.java @@ -26,14 +26,17 @@ import com.alibaba.nacos.client.naming.cache.NamingFuzzyWatchContext; import com.alibaba.nacos.client.naming.cache.NamingFuzzyWatchServiceListHolder; import com.alibaba.nacos.client.naming.event.NamingFuzzyWatchNotifyEvent; +import com.alibaba.nacos.client.utils.LogUtils; import com.alibaba.nacos.common.notify.NotifyCenter; import com.alibaba.nacos.common.remote.client.Connection; import com.alibaba.nacos.common.remote.client.ServerRequestHandler; import com.alibaba.nacos.common.utils.FuzzyGroupKeyPattern; +import org.slf4j.Logger; import java.util.Collection; import static com.alibaba.nacos.api.common.Constants.FUZZY_WATCH_RESOURCE_CHANGED; +import static com.alibaba.nacos.client.utils.LogUtils.NAMING_LOGGER; /** * handle fuzzy watch request from server. @@ -41,6 +44,7 @@ */ public class NamingFuzzyWatchNotifyRequestHandler implements ServerRequestHandler { + NamingFuzzyWatchServiceListHolder namingFuzzyWatchServiceListHolder; public NamingFuzzyWatchNotifyRequestHandler(NamingFuzzyWatchServiceListHolder namingFuzzyWatchServiceListHolder) { @@ -70,6 +74,9 @@ public Response requestReply(Request request, Connection connection) { } } else if (watchNotifySyncRequest.getSyncType().equals(Constants.FINISH_FUZZY_WATCH_INIT_NOTIFY)) { namingFuzzyWatchContext.markInitializationComplete(); + } else if(watchNotifySyncRequest.getSyncType().equals(Constants.FUZZY_WATCH_MATCH_RESOURCE_OVER_LIMIT)) { + NAMING_LOGGER.warn("[{}] [fuzzy-watch] pattern matched service count reach to up limit,pattern ->{}, received keys count {}", + namingFuzzyWatchServiceListHolder.getNotifierEventScope(), watchNotifySyncRequest.getGroupKeyPattern(), namingFuzzyWatchContext.getReceivedServiceKeys().size()); } } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/configuration/ConfigCommonConfig.java b/config/src/main/java/com/alibaba/nacos/config/server/configuration/ConfigCommonConfig.java index b6af4a6f805..c418bcdf380 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/configuration/ConfigCommonConfig.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/configuration/ConfigCommonConfig.java @@ -39,6 +39,11 @@ public class ConfigCommonConfig extends AbstractDynamicConfig { private boolean derbyOpsEnabled = false; + private int maxPatternCount=20; + + private int maxMatchedConfigCount=500; + + private ConfigCommonConfig() { super(CONFIG_COMMON); resetConfig(); @@ -60,18 +65,10 @@ public long getPushTimeout() { return pushTimeout; } - public void setPushTimeout(long pushTimeout) { - this.pushTimeout = pushTimeout; - } - public int getBatchSize() { return batchSize; } - public void setBatchSize(int batchSize) { - this.batchSize = batchSize; - } - public boolean isDerbyOpsEnabled() { return derbyOpsEnabled; } @@ -80,12 +77,23 @@ public void setDerbyOpsEnabled(boolean derbyOpsEnabled) { this.derbyOpsEnabled = derbyOpsEnabled; } + public int getMaxPatternCount() { + return maxPatternCount; + } + + public int getMaxMatchedConfigCount() { + return maxMatchedConfigCount; + } + @Override protected void getConfigFromEnv() { maxPushRetryTimes = EnvUtil.getProperty("nacos.config.push.maxRetryTime", Integer.class, 50); pushTimeout = EnvUtil.getProperty("nacos.config.push.timeout", Long.class, 3000L); batchSize = EnvUtil.getProperty("nacos.config.push.batchSize", Integer.class, 20); derbyOpsEnabled = EnvUtil.getProperty("nacos.config.derby.ops.enabled", Boolean.class, false); + + maxPatternCount = EnvUtil.getProperty("nacos.config.fuzzy.watch.max.pattern.count", Integer.class, 20); + maxMatchedConfigCount = EnvUtil.getProperty("nacos.config.fuzzy.watch.max.pattern.match.config.count", Integer.class, 500); } @Override diff --git a/config/src/main/java/com/alibaba/nacos/config/server/model/event/ConfigCancelFuzzyWatchEvent.java b/config/src/main/java/com/alibaba/nacos/config/server/model/event/ConfigCancelFuzzyWatchEvent.java deleted file mode 100644 index 77fbace8cc4..00000000000 --- a/config/src/main/java/com/alibaba/nacos/config/server/model/event/ConfigCancelFuzzyWatchEvent.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright 1999-2023 Alibaba Group Holding Ltd. - * - * Licensed 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 com.alibaba.nacos.config.server.model.event; - -import com.alibaba.nacos.common.notify.Event; - -/** - * This event represents a batch fuzzy listening event for configurations. It is used to notify the server about a batch - * of fuzzy listening requests from clients. Each request contains a client ID, a set of existing group keys associated - * with the client, a key group pattern, and a flag indicating whether the client is initializing. - * - * @author stone-98 - * @date 2024/3/5 - */ -public class ConfigCancelFuzzyWatchEvent extends Event { - - private static final long serialVersionUID = 1953965691384930209L; - - /** - * ID of the client making the request. - */ - private String connectionId; - - /** - * Pattern for matching group keys. - */ - private String groupKeyPattern; - - /** - * Constructs a new ConfigBatchFuzzyListenEvent with the specified parameters. - * - * @param connectionId ID of the client making the request - * @param groupKeyPattern Pattern for matching group keys - */ - public ConfigCancelFuzzyWatchEvent(String connectionId, String groupKeyPattern) { - this.connectionId = connectionId; - this.groupKeyPattern = groupKeyPattern; - } - - /** - * Get the ID of the client making the request. - * - * @return The client ID - */ - public String getConnectionId() { - return connectionId; - } - - public String getGroupKeyPattern() { - return groupKeyPattern; - } - - /** - * Set the ID of the client making the request. - * - * @param connectionId The client ID to be set - */ - public void setConnectionId(String connectionId) { - this.connectionId = connectionId; - } - -} diff --git a/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigFuzzyWatchRequestHandler.java b/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigFuzzyWatchRequestHandler.java index 5426b5accb2..d93552ee103 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigFuzzyWatchRequestHandler.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigFuzzyWatchRequestHandler.java @@ -22,7 +22,6 @@ import com.alibaba.nacos.api.remote.request.RequestMeta; import com.alibaba.nacos.auth.annotation.Secured; import com.alibaba.nacos.common.notify.NotifyCenter; -import com.alibaba.nacos.config.server.model.event.ConfigCancelFuzzyWatchEvent; import com.alibaba.nacos.config.server.model.event.ConfigFuzzyWatchEvent; import com.alibaba.nacos.config.server.service.ConfigFuzzyWatchContextService; import com.alibaba.nacos.core.control.TpsControl; @@ -80,13 +79,21 @@ public ConfigFuzzyWatchResponse handle(ConfigFuzzyWatchRequest request, RequestM String groupKeyPattern = request.getGroupKeyPattern(); if (WATCH_TYPE_WATCH.equals(request.getWatchType())) { // Add client to the fuzzy listening context - configFuzzyWatchContextService.addFuzzyListen(groupKeyPattern, connectionId); - // Get existing group keys for the client and publish initialization event - Set clientExistingGroupKeys = request.getReceivedGroupKeys(); - NotifyCenter.publishEvent(new ConfigFuzzyWatchEvent(connectionId, clientExistingGroupKeys, groupKeyPattern, - request.isInitializing())); + try { + configFuzzyWatchContextService.addFuzzyWatch(groupKeyPattern, connectionId); + // Get existing group keys for the client and publish initialization event + Set clientExistingGroupKeys = request.getReceivedGroupKeys(); + NotifyCenter.publishEvent(new ConfigFuzzyWatchEvent(connectionId, clientExistingGroupKeys, groupKeyPattern, + request.isInitializing())); + }catch(NacosException nacosException){ + ConfigFuzzyWatchResponse configFuzzyWatchResponse = new ConfigFuzzyWatchResponse(); + configFuzzyWatchResponse.setErrorCode(nacosException.getErrCode()); + configFuzzyWatchResponse.setMessage(nacosException.getErrMsg()); + return configFuzzyWatchResponse; + } + } else if (WATCH_TYPE_CANCEL_WATCH.equals(request.getWatchType())) { - NotifyCenter.publishEvent(new ConfigCancelFuzzyWatchEvent(connectionId, groupKeyPattern)); + configFuzzyWatchContextService.removeFuzzyListen(groupKeyPattern,connectionId); } // Return response diff --git a/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigFuzzyWatchSyncNotifier.java b/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigFuzzyWatchSyncNotifier.java index 4710cddb3a2..28c3ea1830c 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigFuzzyWatchSyncNotifier.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigFuzzyWatchSyncNotifier.java @@ -26,7 +26,6 @@ import com.alibaba.nacos.common.utils.CollectionUtils; import com.alibaba.nacos.common.utils.FuzzyGroupKeyPattern; import com.alibaba.nacos.config.server.configuration.ConfigCommonConfig; -import com.alibaba.nacos.config.server.model.event.ConfigCancelFuzzyWatchEvent; import com.alibaba.nacos.config.server.model.event.ConfigFuzzyWatchEvent; import com.alibaba.nacos.config.server.service.ConfigFuzzyWatchContextService; import com.alibaba.nacos.config.server.utils.ConfigExecutor; @@ -128,6 +127,7 @@ public void handleFuzzyWatchEvent(ConfigFuzzyWatchEvent event) { if (CollectionUtils.isEmpty(configStates)) { if (event.isInitializing()) { + ConfigFuzzyWatchSyncRequest request = ConfigFuzzyWatchSyncRequest.buildInitFinishRequest( event.getGroupKeyPattern()); int maxPushRetryTimes = ConfigCommonConfig.getInstance().getMaxPushRetryTimes(); @@ -135,6 +135,15 @@ public void handleFuzzyWatchEvent(ConfigFuzzyWatchEvent event) { FuzzyWatchRpcPushTask fuzzyWatchRpcPushTask = new FuzzyWatchRpcPushTask(request, null, maxPushRetryTimes, event.getConnectionId()); push(fuzzyWatchRpcPushTask, connectionManager); + }else if(matchGroupKeys.size()>=ConfigCommonConfig.getInstance().getMaxMatchedConfigCount()){ + // no diff but + ConfigFuzzyWatchSyncRequest request = ConfigFuzzyWatchSyncRequest.buildOverLimitRequest( + event.getGroupKeyPattern()); + int maxPushRetryTimes = ConfigCommonConfig.getInstance().getMaxPushRetryTimes(); + // Create RPC push task and push the request to the client + FuzzyWatchRpcPushTask fuzzyWatchRpcPushTask = new FuzzyWatchRpcPushTask(request, null, + maxPushRetryTimes, event.getConnectionId()); + push(fuzzyWatchRpcPushTask, connectionManager); } } else { @@ -175,7 +184,6 @@ public void handleFuzzyWatchEvent(ConfigFuzzyWatchEvent event) { public List> subscribeTypes() { List> result = new LinkedList<>(); result.add(ConfigFuzzyWatchEvent.class); - result.add(ConfigCancelFuzzyWatchEvent.class); return result; } @@ -184,13 +192,6 @@ public void onEvent(Event event) { if (event instanceof ConfigFuzzyWatchEvent) { handleFuzzyWatchEvent((ConfigFuzzyWatchEvent) event); } - - if (event instanceof ConfigCancelFuzzyWatchEvent) { - // Remove client from the fuzzy listening context - configFuzzyWatchContextService.removeFuzzyListen(((ConfigCancelFuzzyWatchEvent) event).getGroupKeyPattern(), - ((ConfigCancelFuzzyWatchEvent) event).getConnectionId()); - } - } /** diff --git a/config/src/main/java/com/alibaba/nacos/config/server/service/ConfigFuzzyWatchContextService.java b/config/src/main/java/com/alibaba/nacos/config/server/service/ConfigFuzzyWatchContextService.java index bccc74d5152..6f904e615a7 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/service/ConfigFuzzyWatchContextService.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/service/ConfigFuzzyWatchContextService.java @@ -16,15 +16,17 @@ package com.alibaba.nacos.config.server.service; -import com.alibaba.nacos.api.exception.runtime.NacosRuntimeException; +import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.common.utils.CollectionUtils; import com.alibaba.nacos.common.utils.FuzzyGroupKeyPattern; +import com.alibaba.nacos.config.server.configuration.ConfigCommonConfig; import com.alibaba.nacos.config.server.utils.GroupKey; import com.alibaba.nacos.config.server.utils.GroupKey2; import com.alibaba.nacos.config.server.utils.LogUtil; import com.alibaba.nacos.core.utils.GlobalExecutor; import org.springframework.stereotype.Component; +import javax.annotation.PostConstruct; import java.util.HashSet; import java.util.Iterator; import java.util.Map; @@ -34,7 +36,6 @@ import static com.alibaba.nacos.api.common.Constants.ConfigChangedType.ADD_CONFIG; import static com.alibaba.nacos.api.common.Constants.ConfigChangedType.CONFIG_CHANGED; import static com.alibaba.nacos.api.common.Constants.ConfigChangedType.DELETE_CONFIG; -import static com.alibaba.nacos.api.model.v2.ErrorCode.FUZZY_WATCH_PATTERN_MATCH_GROUP_KEY_OVER_LIMIT; import static com.alibaba.nacos.api.model.v2.ErrorCode.FUZZY_WATCH_PATTERN_OVER_LIMIT; /** @@ -55,13 +56,13 @@ public class ConfigFuzzyWatchContextService { */ private final Map> matchedGroupKeys = new ConcurrentHashMap<>(); - private static final int FUZZY_WATCH_MAX_PATTERN_COUNT = 50; - - private static final int FUZZY_WATCH_MAX_PATTERN_MATCHED_GROUP_KEY_COUNT = 1000; public ConfigFuzzyWatchContextService() { - - GlobalExecutor.scheduleWithFixDelayByCommon(() -> trimFuzzyWatchContext(), 30000); + } + + @PostConstruct + public void init() { + GlobalExecutor.scheduleWithFixDelayByCommon(() -> trimFuzzyWatchContext(), 60000); } /** @@ -69,22 +70,26 @@ public ConfigFuzzyWatchContextService() { * if watchedClients is null. pattern matchedServiceKeys will be removed in second period to avoid frequently * matchedServiceKeys init. */ - private void trimFuzzyWatchContext() { + void trimFuzzyWatchContext() { try { Iterator>> iterator = matchedGroupKeys.entrySet().iterator(); while (iterator.hasNext()) { - Map.Entry> next = iterator.next(); - Set watchedClients = this.watchedClients.get(next.getKey()); + Map.Entry> matchedGroupKeys = iterator.next(); + Set watchedClients = this.watchedClients.get(matchedGroupKeys.getKey()); if (watchedClients == null) { iterator.remove(); LogUtil.DEFAULT_LOG.info( "[fuzzy-watch] no watchedClients context for pattern {},remove matchedGroupKeys context", - next.getKey()); + matchedGroupKeys.getKey()); } else if (watchedClients.isEmpty()) { LogUtil.DEFAULT_LOG.info("[fuzzy-watch] no client watched pattern {},remove watchedClients context", - next.getKey()); - this.watchedClients.remove(next.getKey()); + matchedGroupKeys.getKey()); + this.watchedClients.remove(matchedGroupKeys.getKey()); + } else if(matchedGroupKeys.getValue().size()>=ConfigCommonConfig.getInstance().getMaxMatchedConfigCount()){ + LogUtil.DEFAULT_LOG.warn( + "[fuzzy-watch] pattern {} matched groupKey count is reach to upper limit {}, fuzzy watch notify may be suppressed ", + matchedGroupKeys.getKey(),matchedGroupKeys.getValue().size()); } } } catch (Throwable throwable) { @@ -140,40 +145,39 @@ public boolean syncGroupKeyContext(String groupKey, String changedType) { * @param groupKeyPattern The pattern to match group keys. * @return A set of group keys that match the pattern and are effective for the client. */ - private void initMatchGroupKeys(String groupKeyPattern) { + private void initMatchGroupKeys(String groupKeyPattern) throws NacosException { if (matchedGroupKeys.containsKey(groupKeyPattern)) { return; } - if (matchedGroupKeys.size() >= FUZZY_WATCH_MAX_PATTERN_COUNT) { + if (matchedGroupKeys.size() >= ConfigCommonConfig.getInstance().getMaxPatternCount()) { LogUtil.DEFAULT_LOG.warn( "[fuzzy-watch] pattern count is over limit ,pattern {} init fail,current count is {}", groupKeyPattern, matchedGroupKeys.size()); - throw new NacosRuntimeException(FUZZY_WATCH_PATTERN_OVER_LIMIT.getCode(), - FUZZY_WATCH_PATTERN_OVER_LIMIT.getMsg()); + throw new NacosException(FUZZY_WATCH_PATTERN_OVER_LIMIT.getCode(), FUZZY_WATCH_PATTERN_OVER_LIMIT.getMsg()); } matchedGroupKeys.computeIfAbsent(groupKeyPattern, k -> new HashSet<>()); Set matchedGroupKeys = this.matchedGroupKeys.get(groupKeyPattern); long matchBeginTime = System.currentTimeMillis(); + boolean overMatchCount = false; for (String groupKey : ConfigCacheService.CACHE.keySet()) { String[] groupKeyItems = GroupKey.parseKey(groupKey); if (FuzzyGroupKeyPattern.matchPattern(groupKeyPattern, groupKeyItems[0], groupKeyItems[1], groupKeyItems[2])) { - if (matchedGroupKeys.size() >= FUZZY_WATCH_MAX_PATTERN_MATCHED_GROUP_KEY_COUNT) { + if (matchedGroupKeys.size() >= ConfigCommonConfig.getInstance().getMaxMatchedConfigCount()) { LogUtil.DEFAULT_LOG.warn("[fuzzy-watch] pattern matched service count is over limit , " + "other services will stop notify for pattern {} ,current count is {}", groupKeyPattern, matchedGroupKeys.size()); - - throw new NacosRuntimeException(FUZZY_WATCH_PATTERN_MATCH_GROUP_KEY_OVER_LIMIT.getCode(), - FUZZY_WATCH_PATTERN_MATCH_GROUP_KEY_OVER_LIMIT.getMsg()); + overMatchCount = true; + break; } matchedGroupKeys.add(groupKey); } } - LogUtil.DEFAULT_LOG.info("[fuzzy-watch] pattern {} match {} group keys, cost {}ms", groupKeyPattern, - matchedGroupKeys.size(), System.currentTimeMillis() - matchBeginTime); + LogUtil.DEFAULT_LOG.info("[fuzzy-watch] pattern {} match {} group keys,overMatchCount={}, cost {}ms", + groupKeyPattern, matchedGroupKeys.size(), overMatchCount, System.currentTimeMillis() - matchBeginTime); } @@ -184,12 +188,12 @@ private void initMatchGroupKeys(String groupKeyPattern) { * * @param groupKeyPattern The group key pattern to associate with the listen connection. * @param connectId The connection ID to be added. + * @throws NacosException over max pattern count. */ - public synchronized void addFuzzyListen(String groupKeyPattern, String connectId) { - + public synchronized void addFuzzyWatch(String groupKeyPattern, String connectId) throws NacosException { + watchedClients.computeIfAbsent(groupKeyPattern, k -> new HashSet<>()); initMatchGroupKeys(groupKeyPattern); // Add the connection ID to the set associated with the key pattern in keyPatternContext - watchedClients.computeIfAbsent(groupKeyPattern, k -> new HashSet<>()); watchedClients.get(groupKeyPattern).add(connectId); } diff --git a/config/src/test/java/com/alibaba/nacos/config/server/service/ConfigFuzzyWatchContextServiceTest.java b/config/src/test/java/com/alibaba/nacos/config/server/service/ConfigFuzzyWatchContextServiceTest.java new file mode 100644 index 00000000000..1972d61a370 --- /dev/null +++ b/config/src/test/java/com/alibaba/nacos/config/server/service/ConfigFuzzyWatchContextServiceTest.java @@ -0,0 +1,165 @@ +/* + * Copyright 1999-2023 Alibaba Group Holding Ltd. + * + * Licensed 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 com.alibaba.nacos.config.server.service; + +import com.alibaba.nacos.api.exception.NacosException; +import com.alibaba.nacos.common.utils.CollectionUtils; +import com.alibaba.nacos.common.utils.FuzzyGroupKeyPattern; + +import com.alibaba.nacos.config.server.utils.GroupKey; +import com.alibaba.nacos.sys.env.EnvUtil; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +import static com.alibaba.nacos.api.common.Constants.ConfigChangedType.ADD_CONFIG; +import static com.alibaba.nacos.api.common.Constants.ConfigChangedType.DELETE_CONFIG; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import java.util.Set; + +@ExtendWith(SpringExtension.class) +public class ConfigFuzzyWatchContextServiceTest { + + MockedStatic envUtilMockedStatic ; + + @BeforeEach + public void before(){ + envUtilMockedStatic = Mockito.mockStatic( + EnvUtil.class); + envUtilMockedStatic.when(() -> EnvUtil.getProperty(eq( "nacos.config.cache.type"),anyString())).thenReturn("nacos"); + } + + @AfterEach + public void after(){ + envUtilMockedStatic.close(); + } + + @Test + public void testTrimFuzzyWatchContext() throws NacosException { + + ConfigCacheService.dump("data124","group","12345","content",System.currentTimeMillis(),null,null); + ConfigFuzzyWatchContextService configFuzzyWatchContextService=new ConfigFuzzyWatchContextService(); + String groupKey = GroupKey.getKeyTenant("data124", "group", "12345"); + configFuzzyWatchContextService.syncGroupKeyContext(groupKey,ADD_CONFIG); + //init + String collectionId="id"; + String groupKeyPattern= FuzzyGroupKeyPattern.generatePattern("data*","group","12345"); + + configFuzzyWatchContextService.addFuzzyWatch(groupKeyPattern,collectionId); + + //test + Set matchedClients = configFuzzyWatchContextService.getMatchedClients(groupKey); + Assertions.assertTrue(matchedClients.size()==1); + + Set notMatchedClients = configFuzzyWatchContextService.getMatchedClients( + GroupKey.getKeyTenant("da124", "group", "12345")); + Assertions.assertTrue(notMatchedClients.size()==0); + + Set matchedGroupKeys = configFuzzyWatchContextService.matchGroupKeys(groupKeyPattern); + + Assertions.assertTrue(matchedGroupKeys.size()>0); + Assertions.assertTrue(matchedGroupKeys.contains(groupKey)); + + // remove connection is watch + configFuzzyWatchContextService.clearFuzzyWatchContext(collectionId); + + //trim once, matchedClients2 is empty,matchedGroupKeys2 is not empty + configFuzzyWatchContextService.trimFuzzyWatchContext(); + Set matchedClients2 = configFuzzyWatchContextService.getMatchedClients(groupKey); + Assertions.assertTrue(matchedClients2!=null&&matchedClients2.isEmpty()); + + Set matchedGroupKeys2 = configFuzzyWatchContextService.matchGroupKeys(groupKeyPattern); + Assertions.assertTrue(matchedGroupKeys2!=null&&matchedGroupKeys2.contains(groupKey)); + + //trim twice, matchedGroupKeys2 is empty + configFuzzyWatchContextService.trimFuzzyWatchContext(); + Set matchedGroupKeys3 = configFuzzyWatchContextService.matchGroupKeys(groupKeyPattern); + Assertions.assertTrue(matchedGroupKeys3==null); + } + + @Test + public void testSyncGroupKeyContext() throws NacosException { + ConfigFuzzyWatchContextService configFuzzyWatchContextService=new ConfigFuzzyWatchContextService(); + + //init + String collectionId="id"; + String groupKeyPattern= FuzzyGroupKeyPattern.generatePattern("data*","group","12345"); + configFuzzyWatchContextService.addFuzzyWatch(groupKeyPattern,collectionId); + String keyTenant = GroupKey.getKeyTenant("data1245", "group", "12345"); + boolean needNotify1=configFuzzyWatchContextService.syncGroupKeyContext(keyTenant,ADD_CONFIG); + Assertions.assertTrue(needNotify1); + boolean needNotify2=configFuzzyWatchContextService.syncGroupKeyContext(keyTenant,ADD_CONFIG); + Assertions.assertFalse(needNotify2); + + boolean needNotify3=configFuzzyWatchContextService.syncGroupKeyContext(keyTenant,DELETE_CONFIG); + Assertions.assertTrue(needNotify3); + + + boolean needNotify4=configFuzzyWatchContextService.syncGroupKeyContext(keyTenant,DELETE_CONFIG); + Assertions.assertFalse(needNotify4); + + } + + @Test + public void testFuzzyWatch() throws NacosException { + ConfigFuzzyWatchContextService configFuzzyWatchContextService=new ConfigFuzzyWatchContextService(); + + //init + String collectionId="id"; + String groupKeyPattern= FuzzyGroupKeyPattern.generatePattern("data*","group","12345"); + configFuzzyWatchContextService.addFuzzyWatch(groupKeyPattern,collectionId); + String groupKey = GroupKey.getKeyTenant("data1245", "group", "12345"); + + boolean needNotify=configFuzzyWatchContextService.syncGroupKeyContext(groupKey,ADD_CONFIG); + Assertions.assertTrue(needNotify); + + Set matchedClients1 = configFuzzyWatchContextService.getMatchedClients(groupKey); + Assertions.assertTrue(matchedClients1.contains(collectionId)); + + configFuzzyWatchContextService.removeFuzzyListen(groupKeyPattern,collectionId); + Set matchedClients = configFuzzyWatchContextService.getMatchedClients(groupKey); + + Assertions.assertTrue(CollectionUtils.isEmpty(matchedClients)); + + } + + @Test + public void testFuzzyWatchOverLimit() throws NacosException { + ConfigFuzzyWatchContextService configFuzzyWatchContextService=new ConfigFuzzyWatchContextService(); + + //init + String collectionId="id"; + String groupKeyPattern= FuzzyGroupKeyPattern.generatePattern("data*","group","12345"); + configFuzzyWatchContextService.addFuzzyWatch(groupKeyPattern,collectionId); + String groupKey = GroupKey.getKeyTenant("data1245", "group", "12345"); + boolean needNotify=configFuzzyWatchContextService.syncGroupKeyContext(groupKey,ADD_CONFIG); + + Assertions.assertTrue(needNotify); + + configFuzzyWatchContextService.removeFuzzyListen(groupKeyPattern,collectionId); + Set matchedClients = configFuzzyWatchContextService.getMatchedClients(groupKey); + + Assertions.assertTrue(CollectionUtils.isEmpty(matchedClients)); + + } +} diff --git a/core/src/main/java/com/alibaba/nacos/core/utils/StringPool.java b/core/src/main/java/com/alibaba/nacos/core/utils/StringPool.java index 69920b9e0cd..9f77100a414 100644 --- a/core/src/main/java/com/alibaba/nacos/core/utils/StringPool.java +++ b/core/src/main/java/com/alibaba/nacos/core/utils/StringPool.java @@ -33,10 +33,6 @@ public class StringPool { private static Cache groupKeyCache = CacheBuilder.newBuilder().maximumSize(5000000) .expireAfterAccess(60, TimeUnit.SECONDS).build(); - static { - GlobalExecutor.scheduleWithFixDelayByCommon(() -> groupKeyCache.cleanUp(), 30000); - } - /** * get singleton string value from the pool. * diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/event/client/ClientOperationEvent.java b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/event/client/ClientOperationEvent.java index b08d2d0c496..8c5cd7f804b 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/event/client/ClientOperationEvent.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/event/client/ClientOperationEvent.java @@ -134,36 +134,9 @@ public Set getClientReceivedServiceKeys() { return clientReceivedServiceKeys; } - public void setClientReceivedServiceKeys(Set clientReceivedServiceKeys) { - this.clientReceivedServiceKeys = clientReceivedServiceKeys; - } - public boolean isInitializing() { return isInitializing; } - - public void setInitializing(boolean initializing) { - isInitializing = initializing; - } - } - - /** - * Client cancel fuzzy watch service event. - */ - public static class ClientCancelFuzzyWatchEvent extends ClientOperationEvent { - - private static final long serialVersionUID = -4518919987813223118L; - - private final String pattern; - - public ClientCancelFuzzyWatchEvent(String pattern, String clientId) { - super(clientId, null); - this.pattern = pattern; - } - - public String getPattern() { - return pattern; - } } public static class ClientReleaseEvent extends ClientOperationEvent { diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/index/NamingFuzzyWatchContextService.java b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/index/NamingFuzzyWatchContextService.java index 6f0b0cd5c55..b9fcecfc1b7 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/index/NamingFuzzyWatchContextService.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/index/NamingFuzzyWatchContextService.java @@ -16,7 +16,7 @@ package com.alibaba.nacos.naming.core.v2.index; -import com.alibaba.nacos.api.exception.runtime.NacosRuntimeException; +import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.naming.utils.NamingUtils; import com.alibaba.nacos.common.notify.Event; import com.alibaba.nacos.common.notify.NotifyCenter; @@ -27,9 +27,11 @@ import com.alibaba.nacos.naming.core.v2.ServiceManager; import com.alibaba.nacos.naming.core.v2.event.client.ClientOperationEvent; import com.alibaba.nacos.naming.core.v2.pojo.Service; +import com.alibaba.nacos.naming.misc.GlobalConfig; import com.alibaba.nacos.naming.misc.Loggers; import org.springframework.stereotype.Component; +import javax.annotation.PostConstruct; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; @@ -41,7 +43,6 @@ import static com.alibaba.nacos.api.common.Constants.ServiceChangedType.ADD_SERVICE; import static com.alibaba.nacos.api.common.Constants.ServiceChangedType.DELETE_SERVICE; -import static com.alibaba.nacos.api.model.v2.ErrorCode.FUZZY_WATCH_PATTERN_MATCH_GROUP_KEY_OVER_LIMIT; import static com.alibaba.nacos.api.model.v2.ErrorCode.FUZZY_WATCH_PATTERN_OVER_LIMIT; import static com.alibaba.nacos.common.utils.FuzzyGroupKeyPattern.getNamespaceFromPattern; @@ -65,11 +66,14 @@ public class NamingFuzzyWatchContextService extends SmartSubscriber { */ private final ConcurrentMap> matchedServiceKeys = new ConcurrentHashMap<>(); - private static final int FUZZY_WATCH_MAX_PATTERN_COUNT = 50; + GlobalConfig globalConfig; - private static final int FUZZY_WATCH_MAX_PATTERN_MATCHED_GROUP_KEY_COUNT = 200; + public NamingFuzzyWatchContextService(GlobalConfig globalConfig) { + this.globalConfig = globalConfig; + } - public NamingFuzzyWatchContextService() { + @PostConstruct + public void init() { GlobalExecutor.scheduleWithFixDelayByCommon(() -> trimFuzzyWatchContext(), 30000); NotifyCenter.registerSubscriber(this); } @@ -86,9 +90,18 @@ private void trimFuzzyWatchContext() { Map.Entry> next = iterator.next(); Set watchedClients = this.watchedClients.get(next.getKey()); if (watchedClients == null) { + Loggers.SRV_LOG.info( + "[fuzzy-watch] no watchedClients context for pattern {},remove matchedGroupKeys context", + next.getKey()); iterator.remove(); } else if (watchedClients.isEmpty()) { + Loggers.SRV_LOG.info("[fuzzy-watch] no client watched pattern {},remove watchedClients context", + next.getKey()); this.watchedClients.remove(next.getKey()); + } else if (next.getValue().size() >= globalConfig.getMaxMatchedServiceCount()) { + Loggers.SRV_LOG.warn( + "[fuzzy-watch] pattern {} matched serviceKey count is reach to upper limit {}, fuzzy watch notify may be suppressed ", + next.getKey(), next.getValue().size()); } } } catch (Throwable throwable) { @@ -106,7 +119,6 @@ public List> subscribeTypes() { @Override public void onEvent(Event event) { - //handle client disconnected event. if (event instanceof ClientOperationEvent.ClientReleaseEvent) { removeFuzzyWatchContext(((ClientOperationEvent.ClientReleaseEvent) event).getClientId()); } @@ -147,7 +159,7 @@ public boolean syncServiceContext(Service changedService, String changedType) { String serviceKey = NamingUtils.getServiceKey(changedService.getNamespace(), changedService.getGroup(), changedService.getName()); - Loggers.SRV_LOG.warn("FUZZY_WATCH: service change matched,service key {},changed type {} ", serviceKey, + Loggers.SRV_LOG.warn("[fuzzy-watch] service change matched,service key {},changed type {} ", serviceKey, changedType); Iterator>> iterator = matchedServiceKeys.entrySet().iterator(); @@ -159,21 +171,21 @@ public boolean syncServiceContext(Service changedService, String changedType) { Set matchedServiceKeys = next.getValue(); if (changedType.equals(ADD_SERVICE) && !matchedServiceKeys.contains(serviceKey)) { - if (matchedServiceKeys.size() >= FUZZY_WATCH_MAX_PATTERN_MATCHED_GROUP_KEY_COUNT) { - Loggers.SRV_LOG.warn("FUZZY_WATCH: pattern matched service count is over limit , " + if (matchedServiceKeys.size() >= globalConfig.getMaxMatchedServiceCount()) { + Loggers.SRV_LOG.warn("[fuzzy-watch] pattern matched service count is over limit , " + "current service will be ignore for pattern {} ,current count is {}", next.getKey(), matchedServiceKeys.size()); continue; } if (matchedServiceKeys.add(serviceKey)) { - Loggers.SRV_LOG.info("FUZZY_WATCH: pattern {} matched service keys count changed to {}", + Loggers.SRV_LOG.info("[fuzzy-watch] pattern {} matched service keys count changed to {}", next.getKey(), matchedServiceKeys.size()); needNotify = true; } } else if (changedType.equals(DELETE_SERVICE) && matchedServiceKeys.contains(serviceKey)) { if (matchedServiceKeys.remove(serviceKey)) { - Loggers.SRV_LOG.info("FUZZY_WATCH: pattern {} matched service keys count changed to {}", + Loggers.SRV_LOG.info("[fuzzy-watch] pattern {} matched service keys count changed to {}", next.getKey(), matchedServiceKeys.size()); needNotify = true; } @@ -190,10 +202,21 @@ public boolean syncServiceContext(Service changedService, String changedType) { * @param clientId client id. * @return */ - public Set syncFuzzyWatcherContext(String groupKeyPattern, String clientId) { - watchedClients.computeIfAbsent(groupKeyPattern, key -> new ConcurrentHashSet<>()).add(clientId); - Set matchedServiceKeys = initWatchMatchService(groupKeyPattern); - return matchedServiceKeys; + public void syncFuzzyWatcherContext(String groupKeyPattern, String clientId) throws NacosException { + //init empty watchedClients first,when pattern is not over limit,then add clientId. + watchedClients.computeIfAbsent(groupKeyPattern, key -> new ConcurrentHashSet<>()); + initWatchMatchService(groupKeyPattern); + watchedClients.get(groupKeyPattern).add(clientId); + } + + /** + * get matched exist group keys with the groupKeyPattern. return null if not matched. + * + * @param groupKeyPattern groupKeyPattern. + * @return + */ + public Set matchServiceKeys(String groupKeyPattern) { + return matchedServiceKeys.get(groupKeyPattern); } private void removeFuzzyWatchContext(String clientId) { @@ -222,14 +245,14 @@ public void removeFuzzyWatchContext(String groupKeyPattern, String clientId) { * @param completedPattern the completed pattern of watch (with namespace id). * @return a copy set of matched service keys in Nacos server */ - public Set initWatchMatchService(String completedPattern) { + public Set initWatchMatchService(String completedPattern) throws NacosException { if (!matchedServiceKeys.containsKey(completedPattern)) { - if (matchedServiceKeys.size() >= FUZZY_WATCH_MAX_PATTERN_COUNT) { + if (matchedServiceKeys.size() >= globalConfig.getMaxPatternCount()) { Loggers.SRV_LOG.warn( "FUZZY_WATCH: fuzzy watch pattern count is over limit ,pattern {} init fail,current count is {}", completedPattern, matchedServiceKeys.size()); - throw new NacosRuntimeException(FUZZY_WATCH_PATTERN_OVER_LIMIT.getCode(), + throw new NacosException(FUZZY_WATCH_PATTERN_OVER_LIMIT.getCode(), FUZZY_WATCH_PATTERN_OVER_LIMIT.getMsg()); } @@ -237,25 +260,26 @@ public Set initWatchMatchService(String completedPattern) { Set namespaceServices = ServiceManager.getInstance() .getSingletons(getNamespaceFromPattern(completedPattern)); Set matchedServices = matchedServiceKeys.computeIfAbsent(completedPattern, k -> new HashSet<>()); - + boolean overMatchCount = false; for (Service service : namespaceServices) { if (FuzzyGroupKeyPattern.matchPattern(completedPattern, service.getName(), service.getGroup(), service.getNamespace())) { - if (matchedServices.size() >= FUZZY_WATCH_MAX_PATTERN_MATCHED_GROUP_KEY_COUNT) { + if (matchedServices.size() >= globalConfig.getMaxMatchedServiceCount()) { - Loggers.SRV_LOG.warn("FUZZY_WATCH: pattern matched service count is over limit , " + Loggers.SRV_LOG.warn("[fuzzy-watch] pattern matched service count is over limit , " + "other services will stop notify for pattern {} ,current count is {}", completedPattern, matchedServices.size()); - throw new NacosRuntimeException(FUZZY_WATCH_PATTERN_MATCH_GROUP_KEY_OVER_LIMIT.getCode(), - FUZZY_WATCH_PATTERN_MATCH_GROUP_KEY_OVER_LIMIT.getMsg()); + overMatchCount = true; + break; } matchedServices.add( NamingUtils.getServiceKey(service.getNamespace(), service.getGroup(), service.getName())); } } matchedServiceKeys.putIfAbsent(completedPattern, matchedServices); - Loggers.SRV_LOG.info("FUZZY_WATCH: pattern {} match {} services, cost {}ms", completedPattern, - matchedServices.size(), System.currentTimeMillis() - matchBeginTime); + Loggers.SRV_LOG.info("FUZZY_WATCH: pattern {} match {} services, overMatchCount={},cost {}ms", + completedPattern, matchedServices.size(), overMatchCount, + System.currentTimeMillis() - matchBeginTime); } diff --git a/naming/src/main/java/com/alibaba/nacos/naming/misc/GlobalConfig.java b/naming/src/main/java/com/alibaba/nacos/naming/misc/GlobalConfig.java index b5c1770b30c..2e81e23779e 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/misc/GlobalConfig.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/misc/GlobalConfig.java @@ -58,5 +58,14 @@ public static Long getExpiredMetadataCleanInterval() { public static Long getExpiredMetadataExpiredTime() { return EnvUtil.getProperty(EXPIRED_METADATA_EXPIRED_TIME, Long.class, 60000L); } + + public static int getMaxPatternCount() { + return EnvUtil.getProperty("nacos.naming.fuzzy.watch.max.pattern.count", Integer.class, 20); + } + + public static int getMaxMatchedServiceCount() { + return EnvUtil.getProperty("nacos.naming.fuzzy.watch.max.pattern.match.service.count", Integer.class, 500); + + } } diff --git a/naming/src/main/java/com/alibaba/nacos/naming/push/NamingFuzzyWatchSyncNotifier.java b/naming/src/main/java/com/alibaba/nacos/naming/push/NamingFuzzyWatchSyncNotifier.java index c9e8f897257..45277921cac 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/push/NamingFuzzyWatchSyncNotifier.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/push/NamingFuzzyWatchSyncNotifier.java @@ -39,6 +39,7 @@ import static com.alibaba.nacos.api.common.Constants.FINISH_FUZZY_WATCH_INIT_NOTIFY; import static com.alibaba.nacos.api.common.Constants.FUZZY_WATCH_DIFF_SYNC_NOTIFY; import static com.alibaba.nacos.api.common.Constants.FUZZY_WATCH_INIT_NOTIFY; +import static com.alibaba.nacos.api.common.Constants.FUZZY_WATCH_MATCH_RESOURCE_OVER_LIMIT; import static com.alibaba.nacos.api.common.Constants.ServiceChangedType.ADD_SERVICE; import static com.alibaba.nacos.api.common.Constants.ServiceChangedType.DELETE_SERVICE; @@ -66,7 +67,6 @@ public NamingFuzzyWatchSyncNotifier(NamingFuzzyWatchContextService namingFuzzyWa public List> subscribeTypes() { List> result = new LinkedList<>(); result.add(ClientOperationEvent.ClientFuzzyWatchEvent.class); - result.add(ClientOperationEvent.ClientCancelFuzzyWatchEvent.class); return result; } @@ -77,21 +77,15 @@ public void onEvent(Event event) { //fuzzy watch event ClientOperationEvent.ClientFuzzyWatchEvent clientFuzzyWatchEvent = (ClientOperationEvent.ClientFuzzyWatchEvent) event; handleClientFuzzyWatchEvent(clientFuzzyWatchEvent); - } else if (event instanceof ClientOperationEvent.ClientCancelFuzzyWatchEvent) { - //handle cancel fuzzy watch event for a client cancel a fuzzy pattern - String completedPattern = ((ClientOperationEvent.ClientCancelFuzzyWatchEvent) event).getPattern(); - namingFuzzyWatchContextService.removeFuzzyWatchContext(completedPattern, - ((ClientOperationEvent.ClientCancelFuzzyWatchEvent) event).getClientId()); } - } private void handleClientFuzzyWatchEvent(ClientOperationEvent.ClientFuzzyWatchEvent clientFuzzyWatchEvent) { String completedPattern = clientFuzzyWatchEvent.getGroupKeyPattern(); //sync fuzzy watch context - Set patternMatchedServiceKeys = namingFuzzyWatchContextService.syncFuzzyWatcherContext(completedPattern, - clientFuzzyWatchEvent.getClientId()); + Set patternMatchedServiceKeys = namingFuzzyWatchContextService.matchServiceKeys(completedPattern); + Set clientReceivedGroupKeys = clientFuzzyWatchEvent.getClientReceivedServiceKeys(); List groupKeyStates = FuzzyGroupKeyPattern.diffGroupKeys( patternMatchedServiceKeys, clientReceivedGroupKeys); @@ -121,6 +115,12 @@ private void handleClientFuzzyWatchEvent(ClientOperationEvent.ClientFuzzyWatchEv fuzzyWatchPushDelayTaskEngine.addTask(FuzzyWatchPushDelayTaskEngine.getTaskKey(fuzzyWatchSyncNotifyTask), fuzzyWatchSyncNotifyTask); + }else if(FUZZY_WATCH_DIFF_SYNC_NOTIFY.equals(syncType)){ + FuzzyWatchSyncNotifyTask fuzzyWatchSyncNotifyTask = new FuzzyWatchSyncNotifyTask( + clientFuzzyWatchEvent.getClientId(), completedPattern, FUZZY_WATCH_MATCH_RESOURCE_OVER_LIMIT, null, + PushConfig.getInstance().getPushTaskRetryDelay()); + fuzzyWatchPushDelayTaskEngine.addTask(FuzzyWatchPushDelayTaskEngine.getTaskKey(fuzzyWatchSyncNotifyTask), + fuzzyWatchSyncNotifyTask); } } diff --git a/naming/src/main/java/com/alibaba/nacos/naming/remote/rpc/handler/NamingFuzzyWatchRequestHandler.java b/naming/src/main/java/com/alibaba/nacos/naming/remote/rpc/handler/NamingFuzzyWatchRequestHandler.java index 782038fdac8..45948da9f36 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/remote/rpc/handler/NamingFuzzyWatchRequestHandler.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/remote/rpc/handler/NamingFuzzyWatchRequestHandler.java @@ -24,6 +24,7 @@ import com.alibaba.nacos.common.notify.NotifyCenter; import com.alibaba.nacos.core.remote.RequestHandler; import com.alibaba.nacos.naming.core.v2.event.client.ClientOperationEvent; +import com.alibaba.nacos.naming.core.v2.index.NamingFuzzyWatchContextService; import com.alibaba.nacos.plugin.auth.constant.ActionTypes; import org.springframework.stereotype.Component; @@ -38,9 +39,11 @@ @Component("namingFuzzyWatchRequestHandler") public class NamingFuzzyWatchRequestHandler extends RequestHandler { - public NamingFuzzyWatchRequestHandler() { + private NamingFuzzyWatchContextService namingFuzzyWatchContextService; + + public NamingFuzzyWatchRequestHandler(NamingFuzzyWatchContextService namingFuzzyWatchContextService) { + this.namingFuzzyWatchContextService = namingFuzzyWatchContextService; NotifyCenter.registerToPublisher(ClientOperationEvent.ClientFuzzyWatchEvent.class, 1000); - NotifyCenter.registerToPublisher(ClientOperationEvent.ClientCancelFuzzyWatchEvent.class, 1000); } @@ -51,13 +54,20 @@ public NamingFuzzyWatchResponse handle(NamingFuzzyWatchRequest request, RequestM String groupKeyPattern = request.getGroupKeyPattern(); switch (request.getWatchType()) { case WATCH_TYPE_WATCH: + try { + namingFuzzyWatchContextService.syncFuzzyWatcherContext(groupKeyPattern, meta.getConnectionId()); + } catch (NacosException nacosException) { + NamingFuzzyWatchResponse namingFuzzyWatchResponse = new NamingFuzzyWatchResponse(); + namingFuzzyWatchResponse.setErrorCode(nacosException.getErrCode()); + namingFuzzyWatchResponse.setMessage(nacosException.getErrMsg()); + return namingFuzzyWatchResponse; + } NotifyCenter.publishEvent( new ClientOperationEvent.ClientFuzzyWatchEvent(groupKeyPattern, meta.getConnectionId(), request.getReceivedGroupKeys(), request.isInitializing())); return NamingFuzzyWatchResponse.buildSuccessResponse(); case WATCH_TYPE_CANCEL_WATCH: - NotifyCenter.publishEvent( - new ClientOperationEvent.ClientCancelFuzzyWatchEvent(groupKeyPattern, meta.getConnectionId())); + namingFuzzyWatchContextService.removeFuzzyWatchContext(groupKeyPattern, meta.getConnectionId()); return NamingFuzzyWatchResponse.buildSuccessResponse(); default: throw new NacosException(NacosException.INVALID_PARAM, From 59c7ba394084665023d57a63b9673d93782f88c4 Mon Sep 17 00:00:00 2001 From: "zunfei.lzf" Date: Fri, 10 Jan 2025 12:02:34 +0800 Subject: [PATCH 2/8] pattern match over limit optimize --- .../alibaba/nacos/api/common/Constants.java | 1 - .../request/ConfigFuzzyWatchSyncRequest.java | 2 +- .../impl/ConfigFuzzyWatchGroupKeyHolder.java | 19 ++-- .../NamingFuzzyWatchServiceListHolder.java | 9 +- .../NamingFuzzyWatchNotifyRequestHandler.java | 14 +-- .../configuration/ConfigCommonConfig.java | 10 +- .../ConfigFuzzyWatchRequestHandler.java | 9 +- .../remote/ConfigFuzzyWatchSyncNotifier.java | 2 +- .../ConfigFuzzyWatchContextService.java | 8 +- .../ConfigFuzzyWatchContextServiceTest.java | 102 +++++++++--------- .../push/NamingFuzzyWatchSyncNotifier.java | 3 +- 11 files changed, 95 insertions(+), 84 deletions(-) diff --git a/api/src/main/java/com/alibaba/nacos/api/common/Constants.java b/api/src/main/java/com/alibaba/nacos/api/common/Constants.java index 22a723daa6e..466c912420f 100644 --- a/api/src/main/java/com/alibaba/nacos/api/common/Constants.java +++ b/api/src/main/java/com/alibaba/nacos/api/common/Constants.java @@ -297,7 +297,6 @@ public static class Naming { * fuzzy watch sync type of watch resource changed. */ public static final String FUZZY_WATCH_RESOURCE_CHANGED = "FUZZY_WATCH_RESOURCE_CHANGED"; - /** * fuzzy watch sync type of watch init notify finish. diff --git a/api/src/main/java/com/alibaba/nacos/api/config/remote/request/ConfigFuzzyWatchSyncRequest.java b/api/src/main/java/com/alibaba/nacos/api/config/remote/request/ConfigFuzzyWatchSyncRequest.java index 4c1a383a965..e3d53458a5e 100644 --- a/api/src/main/java/com/alibaba/nacos/api/config/remote/request/ConfigFuzzyWatchSyncRequest.java +++ b/api/src/main/java/com/alibaba/nacos/api/config/remote/request/ConfigFuzzyWatchSyncRequest.java @@ -109,7 +109,7 @@ public static ConfigFuzzyWatchSyncRequest buildSyncRequest(String syncType, Set< } /** - * Builds fuzzy watch init finish requst + * Builds fuzzy watch init finish request. * * @param groupKeyPattern The pattern used to match group keys for the configurations * @return A final FuzzyListenNotifyDiffRequest diff --git a/client/src/main/java/com/alibaba/nacos/client/config/impl/ConfigFuzzyWatchGroupKeyHolder.java b/client/src/main/java/com/alibaba/nacos/client/config/impl/ConfigFuzzyWatchGroupKeyHolder.java index 458de242df0..afcf1e80163 100644 --- a/client/src/main/java/com/alibaba/nacos/client/config/impl/ConfigFuzzyWatchGroupKeyHolder.java +++ b/client/src/main/java/com/alibaba/nacos/client/config/impl/ConfigFuzzyWatchGroupKeyHolder.java @@ -60,6 +60,7 @@ /** * config fuzzy watch context holder. + * * @author shiyiyue */ public class ConfigFuzzyWatchGroupKeyHolder { @@ -162,8 +163,8 @@ public void removeFuzzyListenContext(String groupKeyPattern) { /** * register fuzzy watcher. * - * @param dataIdPattern dataIdPattern. - * @param groupPattern groupPattern. + * @param dataIdPattern dataIdPattern. + * @param groupPattern groupPattern. * @param fuzzyWatchEventWatcher fuzzyWatchEventWatcher. * @return */ @@ -183,7 +184,7 @@ public ConfigFuzzyWatchContext registerFuzzyWatcher(String dataIdPattern, String } return configFuzzyWatchContext; } - + /** * Retrieves the FuzzyListenContext for the given data ID pattern and group. * @@ -214,9 +215,10 @@ ConfigFuzzyWatchSyncResponse handleFuzzyWatchNotifyDiffRequest(ConfigFuzzyWatchS context.markInitializationComplete(); return new ConfigFuzzyWatchSyncResponse(); } - + if (Constants.FUZZY_WATCH_MATCH_RESOURCE_OVER_LIMIT.equals(request.getSyncType())) { - LOGGER.info("[{}] [fuzzy-watch] pattern match config config count reach to up limit,pattern ->{}, received keys count {}", + LOGGER.info( + "[{}] [fuzzy-watch] pattern match config config count reach to up limit,pattern ->{}, received keys count {}", agent.getName(), request.getGroupKeyPattern(), context.getReceivedGroupKeys().size()); return new ConfigFuzzyWatchSyncResponse(); } @@ -413,9 +415,10 @@ private void doExecuteConfigFuzzyListen(List contextLis } } catch (NacosException e) { - - if (FUZZY_WATCH_PATTERN_OVER_LIMIT.getCode()==e.getErrCode()){ - LOGGER.error(" fuzzy watch pattern over limit,pattern ->{} ,fuzzy watch will be suppressed",entry.getGroupKeyPattern()); + + if (FUZZY_WATCH_PATTERN_OVER_LIMIT.getCode() == e.getErrCode()) { + LOGGER.error(" fuzzy watch pattern over limit,pattern ->{} ,fuzzy watch will be suppressed", + entry.getGroupKeyPattern()); } else { // Log error and retry after a short delay LOGGER.error("Execute batch fuzzy listen config change error.", e); diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/cache/NamingFuzzyWatchServiceListHolder.java b/client/src/main/java/com/alibaba/nacos/client/naming/cache/NamingFuzzyWatchServiceListHolder.java index 88f84aef69b..3137202bb5b 100644 --- a/client/src/main/java/com/alibaba/nacos/client/naming/cache/NamingFuzzyWatchServiceListHolder.java +++ b/client/src/main/java/com/alibaba/nacos/client/naming/cache/NamingFuzzyWatchServiceListHolder.java @@ -155,6 +155,7 @@ public NamingFuzzyWatchContext registerFuzzyWatcher(String groupKeyPattern, Fuzz /** * init fuzzy watch context. + * * @param groupKeyPattern groupKeyPattern. * @return fuzzy context. */ @@ -174,7 +175,8 @@ public NamingFuzzyWatchContext initFuzzyWatchContextIfNeed(String groupKeyPatter } /** - * remove fuzzy watch context for pattern. + * remove fuzzy watch context for pattern. + * * @param groupKeyPattern group key pattern. */ public synchronized void removePatternMatchCache(String groupKeyPattern) { @@ -280,8 +282,9 @@ private void doExecuteNamingFuzzyWatch(List contextList // Log error and retry after a short delay LOGGER.error(" fuzzy watch request fail.", e); - if (FUZZY_WATCH_PATTERN_OVER_LIMIT.getCode()==e.getErrCode()){ - LOGGER.error(" fuzzy watch pattern over limit,pattern ->{} ,fuzzy watch will be suppressed",entry.getGroupKeyPattern()); + if (FUZZY_WATCH_PATTERN_OVER_LIMIT.getCode() == e.getErrCode()) { + LOGGER.error(" fuzzy watch pattern over limit,pattern ->{} ,fuzzy watch will be suppressed", + entry.getGroupKeyPattern()); } else { try { Thread.sleep(100L); diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/NamingFuzzyWatchNotifyRequestHandler.java b/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/NamingFuzzyWatchNotifyRequestHandler.java index 268d104e713..6650613c966 100644 --- a/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/NamingFuzzyWatchNotifyRequestHandler.java +++ b/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/NamingFuzzyWatchNotifyRequestHandler.java @@ -26,12 +26,10 @@ import com.alibaba.nacos.client.naming.cache.NamingFuzzyWatchContext; import com.alibaba.nacos.client.naming.cache.NamingFuzzyWatchServiceListHolder; import com.alibaba.nacos.client.naming.event.NamingFuzzyWatchNotifyEvent; -import com.alibaba.nacos.client.utils.LogUtils; import com.alibaba.nacos.common.notify.NotifyCenter; import com.alibaba.nacos.common.remote.client.Connection; import com.alibaba.nacos.common.remote.client.ServerRequestHandler; import com.alibaba.nacos.common.utils.FuzzyGroupKeyPattern; -import org.slf4j.Logger; import java.util.Collection; @@ -40,11 +38,11 @@ /** * handle fuzzy watch request from server. + * * @author shiyiyue */ public class NamingFuzzyWatchNotifyRequestHandler implements ServerRequestHandler { - NamingFuzzyWatchServiceListHolder namingFuzzyWatchServiceListHolder; public NamingFuzzyWatchNotifyRequestHandler(NamingFuzzyWatchServiceListHolder namingFuzzyWatchServiceListHolder) { @@ -74,9 +72,13 @@ public Response requestReply(Request request, Connection connection) { } } else if (watchNotifySyncRequest.getSyncType().equals(Constants.FINISH_FUZZY_WATCH_INIT_NOTIFY)) { namingFuzzyWatchContext.markInitializationComplete(); - } else if(watchNotifySyncRequest.getSyncType().equals(Constants.FUZZY_WATCH_MATCH_RESOURCE_OVER_LIMIT)) { - NAMING_LOGGER.warn("[{}] [fuzzy-watch] pattern matched service count reach to up limit,pattern ->{}, received keys count {}", - namingFuzzyWatchServiceListHolder.getNotifierEventScope(), watchNotifySyncRequest.getGroupKeyPattern(), namingFuzzyWatchContext.getReceivedServiceKeys().size()); + } else if (watchNotifySyncRequest.getSyncType() + .equals(Constants.FUZZY_WATCH_MATCH_RESOURCE_OVER_LIMIT)) { + NAMING_LOGGER.warn( + "[{}] [fuzzy-watch] pattern matched service count reach to up limit,pattern ->{}, received keys count {}", + namingFuzzyWatchServiceListHolder.getNotifierEventScope(), + watchNotifySyncRequest.getGroupKeyPattern(), + namingFuzzyWatchContext.getReceivedServiceKeys().size()); } } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/configuration/ConfigCommonConfig.java b/config/src/main/java/com/alibaba/nacos/config/server/configuration/ConfigCommonConfig.java index c418bcdf380..7fc76d355b9 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/configuration/ConfigCommonConfig.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/configuration/ConfigCommonConfig.java @@ -39,10 +39,9 @@ public class ConfigCommonConfig extends AbstractDynamicConfig { private boolean derbyOpsEnabled = false; - private int maxPatternCount=20; - - private int maxMatchedConfigCount=500; + private int maxPatternCount = 20; + private int maxMatchedConfigCount = 500; private ConfigCommonConfig() { super(CONFIG_COMMON); @@ -91,9 +90,10 @@ protected void getConfigFromEnv() { pushTimeout = EnvUtil.getProperty("nacos.config.push.timeout", Long.class, 3000L); batchSize = EnvUtil.getProperty("nacos.config.push.batchSize", Integer.class, 20); derbyOpsEnabled = EnvUtil.getProperty("nacos.config.derby.ops.enabled", Boolean.class, false); - + maxPatternCount = EnvUtil.getProperty("nacos.config.fuzzy.watch.max.pattern.count", Integer.class, 20); - maxMatchedConfigCount = EnvUtil.getProperty("nacos.config.fuzzy.watch.max.pattern.match.config.count", Integer.class, 500); + maxMatchedConfigCount = EnvUtil.getProperty("nacos.config.fuzzy.watch.max.pattern.match.config.count", + Integer.class, 500); } @Override diff --git a/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigFuzzyWatchRequestHandler.java b/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigFuzzyWatchRequestHandler.java index d93552ee103..d6a9b7b60c2 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigFuzzyWatchRequestHandler.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigFuzzyWatchRequestHandler.java @@ -83,9 +83,10 @@ public ConfigFuzzyWatchResponse handle(ConfigFuzzyWatchRequest request, RequestM configFuzzyWatchContextService.addFuzzyWatch(groupKeyPattern, connectionId); // Get existing group keys for the client and publish initialization event Set clientExistingGroupKeys = request.getReceivedGroupKeys(); - NotifyCenter.publishEvent(new ConfigFuzzyWatchEvent(connectionId, clientExistingGroupKeys, groupKeyPattern, - request.isInitializing())); - }catch(NacosException nacosException){ + NotifyCenter.publishEvent( + new ConfigFuzzyWatchEvent(connectionId, clientExistingGroupKeys, groupKeyPattern, + request.isInitializing())); + } catch (NacosException nacosException) { ConfigFuzzyWatchResponse configFuzzyWatchResponse = new ConfigFuzzyWatchResponse(); configFuzzyWatchResponse.setErrorCode(nacosException.getErrCode()); configFuzzyWatchResponse.setMessage(nacosException.getErrMsg()); @@ -93,7 +94,7 @@ public ConfigFuzzyWatchResponse handle(ConfigFuzzyWatchRequest request, RequestM } } else if (WATCH_TYPE_CANCEL_WATCH.equals(request.getWatchType())) { - configFuzzyWatchContextService.removeFuzzyListen(groupKeyPattern,connectionId); + configFuzzyWatchContextService.removeFuzzyListen(groupKeyPattern, connectionId); } // Return response diff --git a/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigFuzzyWatchSyncNotifier.java b/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigFuzzyWatchSyncNotifier.java index 28c3ea1830c..b598ec65acb 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigFuzzyWatchSyncNotifier.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigFuzzyWatchSyncNotifier.java @@ -135,7 +135,7 @@ public void handleFuzzyWatchEvent(ConfigFuzzyWatchEvent event) { FuzzyWatchRpcPushTask fuzzyWatchRpcPushTask = new FuzzyWatchRpcPushTask(request, null, maxPushRetryTimes, event.getConnectionId()); push(fuzzyWatchRpcPushTask, connectionManager); - }else if(matchGroupKeys.size()>=ConfigCommonConfig.getInstance().getMaxMatchedConfigCount()){ + } else if (matchGroupKeys.size() >= ConfigCommonConfig.getInstance().getMaxMatchedConfigCount()) { // no diff but ConfigFuzzyWatchSyncRequest request = ConfigFuzzyWatchSyncRequest.buildOverLimitRequest( event.getGroupKeyPattern()); diff --git a/config/src/main/java/com/alibaba/nacos/config/server/service/ConfigFuzzyWatchContextService.java b/config/src/main/java/com/alibaba/nacos/config/server/service/ConfigFuzzyWatchContextService.java index 6f904e615a7..1f88b32bcdc 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/service/ConfigFuzzyWatchContextService.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/service/ConfigFuzzyWatchContextService.java @@ -56,7 +56,6 @@ public class ConfigFuzzyWatchContextService { */ private final Map> matchedGroupKeys = new ConcurrentHashMap<>(); - public ConfigFuzzyWatchContextService() { } @@ -86,10 +85,11 @@ void trimFuzzyWatchContext() { LogUtil.DEFAULT_LOG.info("[fuzzy-watch] no client watched pattern {},remove watchedClients context", matchedGroupKeys.getKey()); this.watchedClients.remove(matchedGroupKeys.getKey()); - } else if(matchedGroupKeys.getValue().size()>=ConfigCommonConfig.getInstance().getMaxMatchedConfigCount()){ + } else if (matchedGroupKeys.getValue().size() >= ConfigCommonConfig.getInstance() + .getMaxMatchedConfigCount()) { LogUtil.DEFAULT_LOG.warn( "[fuzzy-watch] pattern {} matched groupKey count is reach to upper limit {}, fuzzy watch notify may be suppressed ", - matchedGroupKeys.getKey(),matchedGroupKeys.getValue().size()); + matchedGroupKeys.getKey(), matchedGroupKeys.getValue().size()); } } } catch (Throwable throwable) { @@ -188,7 +188,7 @@ private void initMatchGroupKeys(String groupKeyPattern) throws NacosException { * * @param groupKeyPattern The group key pattern to associate with the listen connection. * @param connectId The connection ID to be added. - * @throws NacosException over max pattern count. + * @throws NacosException over max pattern count. */ public synchronized void addFuzzyWatch(String groupKeyPattern, String connectId) throws NacosException { watchedClients.computeIfAbsent(groupKeyPattern, k -> new HashSet<>()); diff --git a/config/src/test/java/com/alibaba/nacos/config/server/service/ConfigFuzzyWatchContextServiceTest.java b/config/src/test/java/com/alibaba/nacos/config/server/service/ConfigFuzzyWatchContextServiceTest.java index 1972d61a370..ab21189c0f8 100644 --- a/config/src/test/java/com/alibaba/nacos/config/server/service/ConfigFuzzyWatchContextServiceTest.java +++ b/config/src/test/java/com/alibaba/nacos/config/server/service/ConfigFuzzyWatchContextServiceTest.java @@ -19,7 +19,6 @@ import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.common.utils.CollectionUtils; import com.alibaba.nacos.common.utils.FuzzyGroupKeyPattern; - import com.alibaba.nacos.config.server.utils.GroupKey; import com.alibaba.nacos.sys.env.EnvUtil; import org.junit.jupiter.api.AfterEach; @@ -31,53 +30,57 @@ import org.mockito.Mockito; import org.springframework.test.context.junit.jupiter.SpringExtension; +import java.util.Set; + import static com.alibaba.nacos.api.common.Constants.ConfigChangedType.ADD_CONFIG; import static com.alibaba.nacos.api.common.Constants.ConfigChangedType.DELETE_CONFIG; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; -import java.util.Set; @ExtendWith(SpringExtension.class) public class ConfigFuzzyWatchContextServiceTest { - MockedStatic envUtilMockedStatic ; + MockedStatic envUtilMockedStatic; + /** + * before. + */ @BeforeEach - public void before(){ - envUtilMockedStatic = Mockito.mockStatic( - EnvUtil.class); - envUtilMockedStatic.when(() -> EnvUtil.getProperty(eq( "nacos.config.cache.type"),anyString())).thenReturn("nacos"); + public void before() { + envUtilMockedStatic = Mockito.mockStatic(EnvUtil.class); + envUtilMockedStatic.when(() -> EnvUtil.getProperty(eq("nacos.config.cache.type"), anyString())) + .thenReturn("nacos"); } @AfterEach - public void after(){ + public void after() { envUtilMockedStatic.close(); } @Test public void testTrimFuzzyWatchContext() throws NacosException { - ConfigCacheService.dump("data124","group","12345","content",System.currentTimeMillis(),null,null); - ConfigFuzzyWatchContextService configFuzzyWatchContextService=new ConfigFuzzyWatchContextService(); + ConfigCacheService.dump("data124", "group", "12345", "content", System.currentTimeMillis(), null, null); + ConfigFuzzyWatchContextService configFuzzyWatchContextService = new ConfigFuzzyWatchContextService(); String groupKey = GroupKey.getKeyTenant("data124", "group", "12345"); - configFuzzyWatchContextService.syncGroupKeyContext(groupKey,ADD_CONFIG); + configFuzzyWatchContextService.syncGroupKeyContext(groupKey, ADD_CONFIG); //init - String collectionId="id"; - String groupKeyPattern= FuzzyGroupKeyPattern.generatePattern("data*","group","12345"); + String collectionId = "id"; + String groupKeyPattern = FuzzyGroupKeyPattern.generatePattern("data*", "group", "12345"); - configFuzzyWatchContextService.addFuzzyWatch(groupKeyPattern,collectionId); + configFuzzyWatchContextService.addFuzzyWatch(groupKeyPattern, collectionId); //test Set matchedClients = configFuzzyWatchContextService.getMatchedClients(groupKey); - Assertions.assertTrue(matchedClients.size()==1); - + Assertions.assertTrue(matchedClients.size() == 1); + Set notMatchedClients = configFuzzyWatchContextService.getMatchedClients( GroupKey.getKeyTenant("da124", "group", "12345")); - Assertions.assertTrue(notMatchedClients.size()==0); - + Assertions.assertTrue(notMatchedClients.size() == 0); + Set matchedGroupKeys = configFuzzyWatchContextService.matchGroupKeys(groupKeyPattern); - - Assertions.assertTrue(matchedGroupKeys.size()>0); + + Assertions.assertTrue(matchedGroupKeys.size() > 0); Assertions.assertTrue(matchedGroupKeys.contains(groupKey)); // remove connection is watch @@ -86,77 +89,76 @@ public void testTrimFuzzyWatchContext() throws NacosException { //trim once, matchedClients2 is empty,matchedGroupKeys2 is not empty configFuzzyWatchContextService.trimFuzzyWatchContext(); Set matchedClients2 = configFuzzyWatchContextService.getMatchedClients(groupKey); - Assertions.assertTrue(matchedClients2!=null&&matchedClients2.isEmpty()); - + Assertions.assertTrue(matchedClients2 != null && matchedClients2.isEmpty()); + Set matchedGroupKeys2 = configFuzzyWatchContextService.matchGroupKeys(groupKeyPattern); - Assertions.assertTrue(matchedGroupKeys2!=null&&matchedGroupKeys2.contains(groupKey)); + Assertions.assertTrue(matchedGroupKeys2 != null && matchedGroupKeys2.contains(groupKey)); //trim twice, matchedGroupKeys2 is empty configFuzzyWatchContextService.trimFuzzyWatchContext(); Set matchedGroupKeys3 = configFuzzyWatchContextService.matchGroupKeys(groupKeyPattern); - Assertions.assertTrue(matchedGroupKeys3==null); + Assertions.assertTrue(matchedGroupKeys3 == null); } @Test public void testSyncGroupKeyContext() throws NacosException { - ConfigFuzzyWatchContextService configFuzzyWatchContextService=new ConfigFuzzyWatchContextService(); - + ConfigFuzzyWatchContextService configFuzzyWatchContextService = new ConfigFuzzyWatchContextService(); + //init - String collectionId="id"; - String groupKeyPattern= FuzzyGroupKeyPattern.generatePattern("data*","group","12345"); - configFuzzyWatchContextService.addFuzzyWatch(groupKeyPattern,collectionId); + String collectionId = "id"; + String groupKeyPattern = FuzzyGroupKeyPattern.generatePattern("data*", "group", "12345"); + configFuzzyWatchContextService.addFuzzyWatch(groupKeyPattern, collectionId); String keyTenant = GroupKey.getKeyTenant("data1245", "group", "12345"); - boolean needNotify1=configFuzzyWatchContextService.syncGroupKeyContext(keyTenant,ADD_CONFIG); + boolean needNotify1 = configFuzzyWatchContextService.syncGroupKeyContext(keyTenant, ADD_CONFIG); Assertions.assertTrue(needNotify1); - boolean needNotify2=configFuzzyWatchContextService.syncGroupKeyContext(keyTenant,ADD_CONFIG); + boolean needNotify2 = configFuzzyWatchContextService.syncGroupKeyContext(keyTenant, ADD_CONFIG); Assertions.assertFalse(needNotify2); - - boolean needNotify3=configFuzzyWatchContextService.syncGroupKeyContext(keyTenant,DELETE_CONFIG); + + boolean needNotify3 = configFuzzyWatchContextService.syncGroupKeyContext(keyTenant, DELETE_CONFIG); Assertions.assertTrue(needNotify3); - - - boolean needNotify4=configFuzzyWatchContextService.syncGroupKeyContext(keyTenant,DELETE_CONFIG); + + boolean needNotify4 = configFuzzyWatchContextService.syncGroupKeyContext(keyTenant, DELETE_CONFIG); Assertions.assertFalse(needNotify4); - + } @Test public void testFuzzyWatch() throws NacosException { - ConfigFuzzyWatchContextService configFuzzyWatchContextService=new ConfigFuzzyWatchContextService(); + ConfigFuzzyWatchContextService configFuzzyWatchContextService = new ConfigFuzzyWatchContextService(); //init - String collectionId="id"; - String groupKeyPattern= FuzzyGroupKeyPattern.generatePattern("data*","group","12345"); - configFuzzyWatchContextService.addFuzzyWatch(groupKeyPattern,collectionId); + String collectionId = "id"; + String groupKeyPattern = FuzzyGroupKeyPattern.generatePattern("data*", "group", "12345"); + configFuzzyWatchContextService.addFuzzyWatch(groupKeyPattern, collectionId); String groupKey = GroupKey.getKeyTenant("data1245", "group", "12345"); - boolean needNotify=configFuzzyWatchContextService.syncGroupKeyContext(groupKey,ADD_CONFIG); + boolean needNotify = configFuzzyWatchContextService.syncGroupKeyContext(groupKey, ADD_CONFIG); Assertions.assertTrue(needNotify); Set matchedClients1 = configFuzzyWatchContextService.getMatchedClients(groupKey); Assertions.assertTrue(matchedClients1.contains(collectionId)); - configFuzzyWatchContextService.removeFuzzyListen(groupKeyPattern,collectionId); + configFuzzyWatchContextService.removeFuzzyListen(groupKeyPattern, collectionId); Set matchedClients = configFuzzyWatchContextService.getMatchedClients(groupKey); - + Assertions.assertTrue(CollectionUtils.isEmpty(matchedClients)); } @Test public void testFuzzyWatchOverLimit() throws NacosException { - ConfigFuzzyWatchContextService configFuzzyWatchContextService=new ConfigFuzzyWatchContextService(); + ConfigFuzzyWatchContextService configFuzzyWatchContextService = new ConfigFuzzyWatchContextService(); //init - String collectionId="id"; - String groupKeyPattern= FuzzyGroupKeyPattern.generatePattern("data*","group","12345"); - configFuzzyWatchContextService.addFuzzyWatch(groupKeyPattern,collectionId); + String collectionId = "id"; + String groupKeyPattern = FuzzyGroupKeyPattern.generatePattern("data*", "group", "12345"); + configFuzzyWatchContextService.addFuzzyWatch(groupKeyPattern, collectionId); String groupKey = GroupKey.getKeyTenant("data1245", "group", "12345"); - boolean needNotify=configFuzzyWatchContextService.syncGroupKeyContext(groupKey,ADD_CONFIG); + boolean needNotify = configFuzzyWatchContextService.syncGroupKeyContext(groupKey, ADD_CONFIG); Assertions.assertTrue(needNotify); - configFuzzyWatchContextService.removeFuzzyListen(groupKeyPattern,collectionId); + configFuzzyWatchContextService.removeFuzzyListen(groupKeyPattern, collectionId); Set matchedClients = configFuzzyWatchContextService.getMatchedClients(groupKey); Assertions.assertTrue(CollectionUtils.isEmpty(matchedClients)); diff --git a/naming/src/main/java/com/alibaba/nacos/naming/push/NamingFuzzyWatchSyncNotifier.java b/naming/src/main/java/com/alibaba/nacos/naming/push/NamingFuzzyWatchSyncNotifier.java index 45277921cac..9cb55d50e33 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/push/NamingFuzzyWatchSyncNotifier.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/push/NamingFuzzyWatchSyncNotifier.java @@ -45,6 +45,7 @@ /** * fuzzy watch event for fuzzy watch. + * * @author shiyiyue */ @Service @@ -115,7 +116,7 @@ private void handleClientFuzzyWatchEvent(ClientOperationEvent.ClientFuzzyWatchEv fuzzyWatchPushDelayTaskEngine.addTask(FuzzyWatchPushDelayTaskEngine.getTaskKey(fuzzyWatchSyncNotifyTask), fuzzyWatchSyncNotifyTask); - }else if(FUZZY_WATCH_DIFF_SYNC_NOTIFY.equals(syncType)){ + } else if (FUZZY_WATCH_DIFF_SYNC_NOTIFY.equals(syncType)) { FuzzyWatchSyncNotifyTask fuzzyWatchSyncNotifyTask = new FuzzyWatchSyncNotifyTask( clientFuzzyWatchEvent.getClientId(), completedPattern, FUZZY_WATCH_MATCH_RESOURCE_OVER_LIMIT, null, PushConfig.getInstance().getPushTaskRetryDelay()); From 8ee897cea70f955beb999add3ea84f3c7ab3c517 Mon Sep 17 00:00:00 2001 From: "zunfei.lzf" Date: Mon, 13 Jan 2025 14:32:05 +0800 Subject: [PATCH 3/8] pattern match over limit optimize --- .../alibaba/nacos/api/common/Constants.java | 5 - .../AbstractFuzzyWatchEventWatcher.java | 12 +- .../listener/FuzzyWatchLoadWatcher.java | 35 +++++ .../request/ConfigFuzzyWatchSyncRequest.java | 10 -- .../alibaba/nacos/api/model/v2/ErrorCode.java | 2 +- .../AbstractFuzzyWatchEventWatcher.java | 12 +- .../listener/FuzzyWatchLoadWatcher.java | 35 +++++ .../src/main/resources/application.properties | 8 ++ .../ClientFuzzyWatchNotifyRequestHandler.java | 2 +- .../config/impl/ConfigFuzzyWatchContext.java | 55 +++++++- .../impl/ConfigFuzzyWatchGroupKeyHolder.java | 110 +++++++++------ .../impl/ConfigFuzzyWatchLoadEvent.java | 76 ++++++++++ .../impl/ConfigFuzzyWatchNotifyEvent.java | 6 +- .../impl/ConfigFuzzyWatcherWrapper.java | 7 +- .../cache/FuzzyWatchEventWatcherWrapper.java | 28 ++++ .../naming/cache/NamingFuzzyWatchContext.java | 46 +++++++ .../NamingFuzzyWatchServiceListHolder.java | 76 ++++++---- .../event/NamingFuzzyWatchLoadEvent.java | 82 +++++++++++ .../NamingFuzzyWatchNotifyRequestHandler.java | 8 -- .../ConfigFuzzyWatchRequestHandler.java | 12 +- .../remote/ConfigFuzzyWatchSyncNotifier.java | 22 ++- .../ConfigFuzzyWatchContextService.java | 100 ++++++++++---- .../index/NamingFuzzyWatchContextService.java | 130 ++++++++++++------ .../push/NamingFuzzyWatchSyncNotifier.java | 16 +-- .../NamingFuzzyWatchRequestHandler.java | 19 ++- 25 files changed, 725 insertions(+), 189 deletions(-) create mode 100644 api/src/main/java/com/alibaba/nacos/api/config/listener/FuzzyWatchLoadWatcher.java create mode 100644 api/src/main/java/com/alibaba/nacos/api/naming/listener/FuzzyWatchLoadWatcher.java create mode 100644 client/src/main/java/com/alibaba/nacos/client/config/impl/ConfigFuzzyWatchLoadEvent.java create mode 100644 client/src/main/java/com/alibaba/nacos/client/naming/event/NamingFuzzyWatchLoadEvent.java diff --git a/api/src/main/java/com/alibaba/nacos/api/common/Constants.java b/api/src/main/java/com/alibaba/nacos/api/common/Constants.java index 466c912420f..0faa1bc469d 100644 --- a/api/src/main/java/com/alibaba/nacos/api/common/Constants.java +++ b/api/src/main/java/com/alibaba/nacos/api/common/Constants.java @@ -297,11 +297,6 @@ public static class Naming { * fuzzy watch sync type of watch resource changed. */ public static final String FUZZY_WATCH_RESOURCE_CHANGED = "FUZZY_WATCH_RESOURCE_CHANGED"; - - /** - * fuzzy watch sync type of watch init notify finish. - */ - public static final String FUZZY_WATCH_MATCH_RESOURCE_OVER_LIMIT = "FUZZY_WATCH_MATCH_RESOURCE_OVER_LIMIT"; /** * watch type of watch. diff --git a/api/src/main/java/com/alibaba/nacos/api/config/listener/AbstractFuzzyWatchEventWatcher.java b/api/src/main/java/com/alibaba/nacos/api/config/listener/AbstractFuzzyWatchEventWatcher.java index b62cca652af..5039d0c820a 100644 --- a/api/src/main/java/com/alibaba/nacos/api/config/listener/AbstractFuzzyWatchEventWatcher.java +++ b/api/src/main/java/com/alibaba/nacos/api/config/listener/AbstractFuzzyWatchEventWatcher.java @@ -25,7 +25,7 @@ * @author stone-98 * @date 2024/3/4 */ -public abstract class AbstractFuzzyWatchEventWatcher implements FuzzyWatchEventWatcher { +public abstract class AbstractFuzzyWatchEventWatcher implements FuzzyWatchEventWatcher, FuzzyWatchLoadWatcher { /** * Get executor for execute this receive. @@ -35,4 +35,14 @@ public abstract class AbstractFuzzyWatchEventWatcher implements FuzzyWatchEventW public Executor getExecutor() { return null; } + + @Override + public void onPatternOverLimit() { + // do nothing default + } + + @Override + public void onConfigReachUpLimit() { + // do nothing default + } } diff --git a/api/src/main/java/com/alibaba/nacos/api/config/listener/FuzzyWatchLoadWatcher.java b/api/src/main/java/com/alibaba/nacos/api/config/listener/FuzzyWatchLoadWatcher.java new file mode 100644 index 00000000000..17fd4159a37 --- /dev/null +++ b/api/src/main/java/com/alibaba/nacos/api/config/listener/FuzzyWatchLoadWatcher.java @@ -0,0 +1,35 @@ +/* + * Copyright 1999-2023 Alibaba Group Holding Ltd. + * + * Licensed 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 com.alibaba.nacos.api.config.listener; + +/** + * config fuzzy watch watcher that triggered when loader over limit. + * @author shiyiyue + */ +public interface FuzzyWatchLoadWatcher { + + /** + * triggered when server pattern count over limit. + */ + void onPatternOverLimit(); + + /** + * triggered when pattern match config count over limit. + */ + void onConfigReachUpLimit(); + +} diff --git a/api/src/main/java/com/alibaba/nacos/api/config/remote/request/ConfigFuzzyWatchSyncRequest.java b/api/src/main/java/com/alibaba/nacos/api/config/remote/request/ConfigFuzzyWatchSyncRequest.java index e3d53458a5e..1b4bde72448 100644 --- a/api/src/main/java/com/alibaba/nacos/api/config/remote/request/ConfigFuzzyWatchSyncRequest.java +++ b/api/src/main/java/com/alibaba/nacos/api/config/remote/request/ConfigFuzzyWatchSyncRequest.java @@ -118,16 +118,6 @@ public static ConfigFuzzyWatchSyncRequest buildInitFinishRequest(String groupKey return new ConfigFuzzyWatchSyncRequest(Constants.FINISH_FUZZY_WATCH_INIT_NOTIFY, groupKeyPattern, null, 0, 0); } - /** - * Builds a final FUZZY_WATCH_MATCH_RESOURCE_OVER_LIMIT with the specified group key pattern. - * - * @param groupKeyPattern The pattern used to match group keys for the configurations - * @return A final FuzzyListenNotifyDiffRequest - */ - public static ConfigFuzzyWatchSyncRequest buildOverLimitRequest(String groupKeyPattern) { - return new ConfigFuzzyWatchSyncRequest(Constants.FUZZY_WATCH_MATCH_RESOURCE_OVER_LIMIT, groupKeyPattern, null, 0, 0); - } - public String getGroupKeyPattern() { return groupKeyPattern; } diff --git a/api/src/main/java/com/alibaba/nacos/api/model/v2/ErrorCode.java b/api/src/main/java/com/alibaba/nacos/api/model/v2/ErrorCode.java index c810a75440c..5916e9c1328 100644 --- a/api/src/main/java/com/alibaba/nacos/api/model/v2/ErrorCode.java +++ b/api/src/main/java/com/alibaba/nacos/api/model/v2/ErrorCode.java @@ -225,7 +225,7 @@ public enum ErrorCode { FUZZY_WATCH_PATTERN_OVER_LIMIT(50310, "fuzzy watch pattern over limit"), - FUZZY_WATCH_PATTERN_MATCH_GROUP_KEY_OVER_LIMIT(50311, "fuzzy watch pattern matched group key over limit"); + FUZZY_WATCH_PATTERN_MATCH_COUNT_OVER_LIMIT(50311, "fuzzy watch pattern matched count over limit"); private final Integer code; diff --git a/api/src/main/java/com/alibaba/nacos/api/naming/listener/AbstractFuzzyWatchEventWatcher.java b/api/src/main/java/com/alibaba/nacos/api/naming/listener/AbstractFuzzyWatchEventWatcher.java index fa10d05f1ea..ed6dbdf224f 100644 --- a/api/src/main/java/com/alibaba/nacos/api/naming/listener/AbstractFuzzyWatchEventWatcher.java +++ b/api/src/main/java/com/alibaba/nacos/api/naming/listener/AbstractFuzzyWatchEventWatcher.java @@ -23,10 +23,20 @@ * * @author tanyongquan */ -public abstract class AbstractFuzzyWatchEventWatcher implements FuzzyWatchEventWatcher { +public abstract class AbstractFuzzyWatchEventWatcher implements FuzzyWatchEventWatcher, FuzzyWatchLoadWatcher { @Override public Executor getExecutor() { return null; } + + @Override + public void onPatternOverLimit() { + //do nothing default + } + + @Override + public void onServiceReachUpLimit() { + //do nothing default + } } diff --git a/api/src/main/java/com/alibaba/nacos/api/naming/listener/FuzzyWatchLoadWatcher.java b/api/src/main/java/com/alibaba/nacos/api/naming/listener/FuzzyWatchLoadWatcher.java new file mode 100644 index 00000000000..a737bddcdce --- /dev/null +++ b/api/src/main/java/com/alibaba/nacos/api/naming/listener/FuzzyWatchLoadWatcher.java @@ -0,0 +1,35 @@ +/* + * Copyright 1999-2023 Alibaba Group Holding Ltd. + * + * Licensed 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 com.alibaba.nacos.api.naming.listener; + +/** + * naming fuzzy watch watcher that triggered when loader over limit. + * @author shiyiyue + */ +public interface FuzzyWatchLoadWatcher { + + /** + * triggered when server pattern count over limit. + */ + void onPatternOverLimit(); + + /** + * triggered when pattern match service count over limit. + */ + void onServiceReachUpLimit(); + +} diff --git a/bootstrap/src/main/resources/application.properties b/bootstrap/src/main/resources/application.properties index e45a7a015d9..f10a42809cb 100644 --- a/bootstrap/src/main/resources/application.properties +++ b/bootstrap/src/main/resources/application.properties @@ -292,3 +292,11 @@ nacos.k8s.sync.enabled=false ### Sets the deployment type: 'merged' for joint deployment, 'server' for separate deployment server only, 'console' for separate deployment console only. nacos.deployment.type=merged + + +#*************** Fuzzy Watch Configuration ***************# +nacos.config.fuzzy.watch.max.pattern.count=20 +nacos.config.fuzzy.watch.max.pattern.match.config.count=500 + +nacos.naming.fuzzy.watch.max.pattern.count=20 +nacos.naming.fuzzy.watch.max.pattern.match.service.count=500 diff --git a/client/src/main/java/com/alibaba/nacos/client/config/impl/ClientFuzzyWatchNotifyRequestHandler.java b/client/src/main/java/com/alibaba/nacos/client/config/impl/ClientFuzzyWatchNotifyRequestHandler.java index f6b9a934afd..d8980592a43 100644 --- a/client/src/main/java/com/alibaba/nacos/client/config/impl/ClientFuzzyWatchNotifyRequestHandler.java +++ b/client/src/main/java/com/alibaba/nacos/client/config/impl/ClientFuzzyWatchNotifyRequestHandler.java @@ -40,7 +40,7 @@ public ClientFuzzyWatchNotifyRequestHandler(ConfigFuzzyWatchGroupKeyHolder confi public Response requestReply(Request request, Connection connection) { //fuzzy watch diff reconciliation sync if (request instanceof ConfigFuzzyWatchSyncRequest) { - return configFuzzyWatchGroupKeyHolder.handleFuzzyWatchNotifyDiffRequest( + return configFuzzyWatchGroupKeyHolder.handleFuzzyWatchSyncNotifyRequest( (ConfigFuzzyWatchSyncRequest) request); } //fuzzy watch changed notify for a single config. include config changed or config delete. diff --git a/client/src/main/java/com/alibaba/nacos/client/config/impl/ConfigFuzzyWatchContext.java b/client/src/main/java/com/alibaba/nacos/client/config/impl/ConfigFuzzyWatchContext.java index e9a14ee1cf4..e06dc9bd1cb 100644 --- a/client/src/main/java/com/alibaba/nacos/client/config/impl/ConfigFuzzyWatchContext.java +++ b/client/src/main/java/com/alibaba/nacos/client/config/impl/ConfigFuzzyWatchContext.java @@ -18,6 +18,7 @@ import com.alibaba.nacos.api.config.listener.ConfigFuzzyWatchChangeEvent; import com.alibaba.nacos.api.config.listener.FuzzyWatchEventWatcher; +import com.alibaba.nacos.api.config.listener.FuzzyWatchLoadWatcher; import com.alibaba.nacos.client.config.common.GroupKey; import com.alibaba.nacos.client.utils.LogUtils; import com.alibaba.nacos.common.utils.ConcurrentHashSet; @@ -40,9 +41,11 @@ import static com.alibaba.nacos.api.common.Constants.ConfigChangedType.DELETE_CONFIG; import static com.alibaba.nacos.api.common.Constants.FUZZY_WATCH_DIFF_SYNC_NOTIFY; import static com.alibaba.nacos.api.common.Constants.FUZZY_WATCH_INIT_NOTIFY; +import static com.alibaba.nacos.api.model.v2.ErrorCode.FUZZY_WATCH_PATTERN_MATCH_COUNT_OVER_LIMIT; +import static com.alibaba.nacos.api.model.v2.ErrorCode.FUZZY_WATCH_PATTERN_OVER_LIMIT; /** - * fuzzy wather context for a single group key pattern. + * fuzzy watcher context for a single group key pattern. * *

This class manages the context information for fuzzy listening, including environment name, task ID, data ID * pattern, group, tenant, listener set, and other related information. @@ -90,6 +93,22 @@ public class ConfigFuzzyWatchContext { */ private volatile boolean isDiscard = false; + long patternLimitTs = 0; + + private static final long SUPPRESSED_PERIOD = 60 * 1000L; + + boolean patternLimitSuppressed() { + return patternLimitTs > 0 && System.currentTimeMillis() - patternLimitTs < SUPPRESSED_PERIOD; + } + + public void clearOverLimitTs() { + this.patternLimitTs = 0; + } + + public void refreshOverLimitTs() { + this.patternLimitTs = System.currentTimeMillis(); + } + /** * Set of listeners associated with the context. */ @@ -151,6 +170,35 @@ private void doNotifyWatchers(final String groupKey, final String changedType, f } } + /** + * notify loader watcher. + * @param code over limit code,FUZZY_WATCH_PATTERN_MATCH_COUNT_OVER_LIMIT or FUZZY_WATCH_PATTERN_OVER_LIMIT. + */ + public void notifyLoaderWatcher(int code) { + + if (this.patternLimitSuppressed()) { + return; + } + boolean notify = false; + + for (ConfigFuzzyWatcherWrapper configFuzzyWatcherWrapper : calculateListenersToNotify(null)) { + if (configFuzzyWatcherWrapper.fuzzyWatchEventWatcher instanceof FuzzyWatchLoadWatcher) { + + if (FUZZY_WATCH_PATTERN_MATCH_COUNT_OVER_LIMIT.getCode().equals(code)) { + ((FuzzyWatchLoadWatcher) configFuzzyWatcherWrapper.fuzzyWatchEventWatcher).onConfigReachUpLimit(); + notify = true; + } + if (FUZZY_WATCH_PATTERN_OVER_LIMIT.getCode().equals(code)) { + ((FuzzyWatchLoadWatcher) configFuzzyWatcherWrapper.fuzzyWatchEventWatcher).onPatternOverLimit(); + notify = true; + } + } + } + if (notify) { + this.refreshOverLimitTs(); + } + } + private void doNotifyWatcher(final String groupKey, final String changedType, final String syncType, ConfigFuzzyWatcherWrapper configFuzzyWatcher) { @@ -396,10 +444,11 @@ void syncFuzzyWatchers() { /** * creat a new future of this context. + * * @return */ public Future> createNewFuture() { - Future> completableFuture = new Future>() { + Future> future = new Future>() { @Override public boolean cancel(boolean mayInterruptIfRunning) { throw new UnsupportedOperationException("not support to cancel fuzzy watch"); @@ -441,7 +490,7 @@ public Set get(long timeout, TimeUnit unit) throws InterruptedException, } }; - return completableFuture; + return future; } } diff --git a/client/src/main/java/com/alibaba/nacos/client/config/impl/ConfigFuzzyWatchGroupKeyHolder.java b/client/src/main/java/com/alibaba/nacos/client/config/impl/ConfigFuzzyWatchGroupKeyHolder.java index afcf1e80163..301693703e5 100644 --- a/client/src/main/java/com/alibaba/nacos/client/config/impl/ConfigFuzzyWatchGroupKeyHolder.java +++ b/client/src/main/java/com/alibaba/nacos/client/config/impl/ConfigFuzzyWatchGroupKeyHolder.java @@ -56,6 +56,7 @@ import static com.alibaba.nacos.api.common.Constants.FUZZY_WATCH_RESOURCE_CHANGED; import static com.alibaba.nacos.api.common.Constants.WATCH_TYPE_CANCEL_WATCH; import static com.alibaba.nacos.api.common.Constants.WATCH_TYPE_WATCH; +import static com.alibaba.nacos.api.model.v2.ErrorCode.FUZZY_WATCH_PATTERN_MATCH_COUNT_OVER_LIMIT; import static com.alibaba.nacos.api.model.v2.ErrorCode.FUZZY_WATCH_PATTERN_OVER_LIMIT; /** @@ -93,10 +94,9 @@ public class ConfigFuzzyWatchGroupKeyHolder { public ConfigFuzzyWatchGroupKeyHolder(ClientWorker.ConfigRpcTransportClient agent, String clientUuid) { this.clientUuid = clientUuid; this.agent = agent; - NotifyCenter.registerSubscriber(new Subscriber() { + NotifyCenter.registerSubscriber(new Subscriber() { @Override - public void onEvent(Event event) { - ConfigFuzzyWatchNotifyEvent configFuzzyWatchNotifyEvent = (ConfigFuzzyWatchNotifyEvent) event; + public void onEvent(ConfigFuzzyWatchNotifyEvent configFuzzyWatchNotifyEvent) { //instance check if (!configFuzzyWatchNotifyEvent.getClientUuid().equals(clientUuid)) { @@ -119,6 +119,30 @@ public Class subscribeType() { return ConfigFuzzyWatchNotifyEvent.class; } }); + + NotifyCenter.registerSubscriber(new Subscriber() { + @Override + public void onEvent(ConfigFuzzyWatchLoadEvent event) { + + //instance check + if (!event.getClientUuid().equals(clientUuid)) { + return; + } + + ConfigFuzzyWatchContext context = fuzzyListenContextMap.get().get(event.getGroupKeyPattern()); + if (context == null) { + return; + } + + context.notifyLoaderWatcher(event.getCode()); + } + + @Override + public Class subscribeType() { + return ConfigFuzzyWatchLoadEvent.class; + } + }); + } /** @@ -175,7 +199,7 @@ public ConfigFuzzyWatchContext registerFuzzyWatcher(String dataIdPattern, String if (configFuzzyWatchContext.addWatcher(configFuzzyWatcherWrapper)) { if (configFuzzyWatchContext.getReceivedGroupKeys() != null) { for (String groupKey : configFuzzyWatchContext.getReceivedGroupKeys()) { - ConfigFuzzyWatchNotifyEvent configFuzzyWatchNotifyEvent = ConfigFuzzyWatchNotifyEvent.buildNotifyPatternAllListenersEvent( + ConfigFuzzyWatchNotifyEvent configFuzzyWatchNotifyEvent = ConfigFuzzyWatchNotifyEvent.buildEvent( groupKey, configFuzzyWatchContext.getGroupKeyPattern(), ADD_CONFIG, FUZZY_WATCH_INIT_NOTIFY, configFuzzyWatcherWrapper.getUuid()); NotifyCenter.publishEvent(configFuzzyWatchNotifyEvent); @@ -206,7 +230,7 @@ public ConfigFuzzyWatchContext getFuzzyListenContext(String dataIdPattern, Strin * @param request The fuzzy listen init notify request to handle. * @return A {@link ConfigFuzzyWatchSyncResponse} indicating the result of handling the request. */ - ConfigFuzzyWatchSyncResponse handleFuzzyWatchNotifyDiffRequest(ConfigFuzzyWatchSyncRequest request) { + ConfigFuzzyWatchSyncResponse handleFuzzyWatchSyncNotifyRequest(ConfigFuzzyWatchSyncRequest request) { String groupKeyPattern = request.getGroupKeyPattern(); ConfigFuzzyWatchContext context = fuzzyListenContextMap.get().get(groupKeyPattern); if (Constants.FINISH_FUZZY_WATCH_INIT_NOTIFY.equals(request.getSyncType())) { @@ -216,15 +240,8 @@ ConfigFuzzyWatchSyncResponse handleFuzzyWatchNotifyDiffRequest(ConfigFuzzyWatchS return new ConfigFuzzyWatchSyncResponse(); } - if (Constants.FUZZY_WATCH_MATCH_RESOURCE_OVER_LIMIT.equals(request.getSyncType())) { - LOGGER.info( - "[{}] [fuzzy-watch] pattern match config config count reach to up limit,pattern ->{}, received keys count {}", - agent.getName(), request.getGroupKeyPattern(), context.getReceivedGroupKeys().size()); - return new ConfigFuzzyWatchSyncResponse(); - } - LOGGER.info( - "[{}] [fuzzy-watch-diff-sync-push] pattern ->{},syncType={},,syncCount={},totalBatch={},currentBatch={}", + "[{}] [fuzzy-watch] sync notify , pattern ->{},syncType={},,syncCount={},totalBatch={},currentBatch={}", agent.getName(), request.getGroupKeyPattern(), request.getSyncType(), request.getContexts().size(), request.getTotalBatch(), request.getCurrentBatch()); @@ -235,9 +252,9 @@ ConfigFuzzyWatchSyncResponse handleFuzzyWatchNotifyDiffRequest(ConfigFuzzyWatchS LOGGER.info("[{}] [fuzzy-watch-diff-sync-push] local match group key added ,pattern ->{}, " + "group key ->{},publish fuzzy watch notify event", agent.getName(), request.getGroupKeyPattern(), requestContext.getGroupKey()); - NotifyCenter.publishEvent(ConfigFuzzyWatchNotifyEvent.buildNotifyPatternAllListenersEvent( - requestContext.getGroupKey(), request.getGroupKeyPattern(), - requestContext.getChangedType(), request.getSyncType(), this.clientUuid)); + NotifyCenter.publishEvent(ConfigFuzzyWatchNotifyEvent.buildEvent(requestContext.getGroupKey(), + request.getGroupKeyPattern(), requestContext.getChangedType(), request.getSyncType(), + this.clientUuid)); } break; case DELETE_CONFIG: @@ -245,9 +262,9 @@ ConfigFuzzyWatchSyncResponse handleFuzzyWatchNotifyDiffRequest(ConfigFuzzyWatchS LOGGER.info("[{}] [fuzzy-watch-diff-sync-push] local match group key remove ,pattern ->{}, " + "group key ->{},publish fuzzy watch notify event", agent.getName(), request.getGroupKeyPattern(), requestContext.getGroupKey()); - NotifyCenter.publishEvent(ConfigFuzzyWatchNotifyEvent.buildNotifyPatternAllListenersEvent( - requestContext.getGroupKey(), request.getGroupKeyPattern(), - requestContext.getChangedType(), request.getSyncType(), this.clientUuid)); + NotifyCenter.publishEvent(ConfigFuzzyWatchNotifyEvent.buildEvent(requestContext.getGroupKey(), + request.getGroupKeyPattern(), requestContext.getChangedType(), request.getSyncType(), + this.clientUuid)); } break; default: @@ -305,15 +322,13 @@ ConfigFuzzyWatchChangeNotifyResponse handlerFuzzyWatchChangeNotifyRequest( agent.getName(), request.getChangeType(), request.getGroupKey()); NotifyCenter.publishEvent( - ConfigFuzzyWatchNotifyEvent.buildNotifyPatternAllListenersEvent(request.getGroupKey(), - matchedPattern, ADD_CONFIG, FUZZY_WATCH_RESOURCE_CHANGED, this.clientUuid)); + ConfigFuzzyWatchNotifyEvent.buildEvent(request.getGroupKey(), matchedPattern, ADD_CONFIG, + FUZZY_WATCH_RESOURCE_CHANGED, this.clientUuid)); } } else if (DELETE_CONFIG.equals(request.getChangeType()) && context.removeReceivedGroupKey( request.getGroupKey())) { - NotifyCenter.publishEvent( - ConfigFuzzyWatchNotifyEvent.buildNotifyPatternAllListenersEvent(request.getGroupKey(), - matchedPattern, Constants.ConfigChangedType.DELETE_CONFIG, FUZZY_WATCH_RESOURCE_CHANGED, - this.clientUuid)); + NotifyCenter.publishEvent(ConfigFuzzyWatchNotifyEvent.buildEvent(request.getGroupKey(), matchedPattern, + Constants.ConfigChangedType.DELETE_CONFIG, FUZZY_WATCH_RESOURCE_CHANGED, this.clientUuid)); } } @@ -413,23 +428,29 @@ private void doExecuteConfigFuzzyListen(List contextLis entry.setConsistentWithServer(true); } + entry.clearOverLimitTs(); + } else if (listenResponse != null) { + if (handleOverLoadEvent(entry.getGroupKeyPattern(), listenResponse.getErrorCode())) { + return; + } + LOGGER.error("Execute fuzzy watch config change error,code={},msg={}", + listenResponse.getErrorCode(), listenResponse.getMessage()); } - } catch (NacosException e) { - if (FUZZY_WATCH_PATTERN_OVER_LIMIT.getCode() == e.getErrCode()) { - LOGGER.error(" fuzzy watch pattern over limit,pattern ->{} ,fuzzy watch will be suppressed", - entry.getGroupKeyPattern()); - } else { - // Log error and retry after a short delay - LOGGER.error("Execute batch fuzzy listen config change error.", e); - try { - Thread.sleep(50L); - } catch (InterruptedException interruptedException) { - // Ignore interruption - } - // Retry notification - notifyFuzzyWatchSync(); + } catch (NacosException e) { + if (handleOverLoadEvent(entry.getGroupKeyPattern(), e.getErrCode())) { + return; } + // Log error and retry after a short delay + LOGGER.error("Execute fuzzy watch config change error.", e); + try { + Thread.sleep(50L); + } catch (InterruptedException interruptedException) { + // Ignore interruption + } + // Retry notification + notifyFuzzyWatchSync(); + } }); listenFutures.add(future); @@ -446,6 +467,16 @@ private void doExecuteConfigFuzzyListen(List contextLis } } + private boolean handleOverLoadEvent(String pattern, int errorCode) { + if (FUZZY_WATCH_PATTERN_OVER_LIMIT.getCode() == errorCode + || FUZZY_WATCH_PATTERN_MATCH_COUNT_OVER_LIMIT.getCode() == errorCode) { + LOGGER.warn(" fuzzy watch pattern over limit,pattern ->{} ,fuzzy watch will be suppressed", pattern); + NotifyCenter.publishEvent(ConfigFuzzyWatchLoadEvent.buildEvent(errorCode, pattern, this.clientUuid)); + return true; + } + return false; + } + /** * Builds a request for fuzzy listen configuration. * @@ -482,7 +513,6 @@ private ConfigFuzzyWatchContext initFuzzyWatchContextIfAbsent(String dataIdPatte } else { String groupKeyPattern = FuzzyGroupKeyPattern.generatePattern(dataIdPattern, groupPattern, agent.getTenant()); - context = new ConfigFuzzyWatchContext(agent.getName(), groupKeyPattern); context.setConsistentWithServer(false); Map copy = new HashMap<>(fuzzyListenContextMap.get()); diff --git a/client/src/main/java/com/alibaba/nacos/client/config/impl/ConfigFuzzyWatchLoadEvent.java b/client/src/main/java/com/alibaba/nacos/client/config/impl/ConfigFuzzyWatchLoadEvent.java new file mode 100644 index 00000000000..11396da3822 --- /dev/null +++ b/client/src/main/java/com/alibaba/nacos/client/config/impl/ConfigFuzzyWatchLoadEvent.java @@ -0,0 +1,76 @@ +/* + * Copyright 1999-2023 Alibaba Group Holding Ltd. + * + * Licensed 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 com.alibaba.nacos.client.config.impl; + +import com.alibaba.nacos.common.notify.Event; + +/** + * event published when a fuzzy watch pattern being suppressed because of pattern count or pattern matched config count is over limit. + * @author shiyiyue + * @date 2025/01/13 + */ +public class ConfigFuzzyWatchLoadEvent extends Event { + + private String clientUuid; + + /** + * The groupKeyPattern of configuration. + */ + private String groupKeyPattern; + + private int code; + + /** + * Constructs a new ConfigFuzzyWatchLoadEvent. + */ + public ConfigFuzzyWatchLoadEvent() { + } + + /** + * Constructs a new FuzzyListenNotifyEvent with the specified group, dataId, and type. + * + * @param code The type of notification. + * @param groupKeyPattern The groupKeyPattern of notification. + */ + private ConfigFuzzyWatchLoadEvent(int code, String groupKeyPattern, String clientUuid) { + this.code = code; + this.groupKeyPattern = groupKeyPattern; + this.clientUuid = clientUuid; + } + + /** + * Builds a new FuzzyListenNotifyEvent with the specified group, dataId, and type. + * + * @param groupKeyPattern The groupKey of the configuration. + * @return A new FuzzyListenNotifyEvent instance. + */ + public static ConfigFuzzyWatchLoadEvent buildEvent(int code, String groupKeyPattern, String clientUuid) { + return new ConfigFuzzyWatchLoadEvent(code, groupKeyPattern, clientUuid); + } + + public String getClientUuid() { + return clientUuid; + } + + public String getGroupKeyPattern() { + return groupKeyPattern; + } + + public int getCode() { + return code; + } +} diff --git a/client/src/main/java/com/alibaba/nacos/client/config/impl/ConfigFuzzyWatchNotifyEvent.java b/client/src/main/java/com/alibaba/nacos/client/config/impl/ConfigFuzzyWatchNotifyEvent.java index 3489e77ed40..c4005d265c4 100644 --- a/client/src/main/java/com/alibaba/nacos/client/config/impl/ConfigFuzzyWatchNotifyEvent.java +++ b/client/src/main/java/com/alibaba/nacos/client/config/impl/ConfigFuzzyWatchNotifyEvent.java @@ -79,9 +79,9 @@ private ConfigFuzzyWatchNotifyEvent(String groupKey, String changedType, String * @param groupKey The groupKey of the configuration. * @return A new FuzzyListenNotifyEvent instance. */ - public static ConfigFuzzyWatchNotifyEvent buildNotifyPatternAllListenersEvent(String groupKey, + public static ConfigFuzzyWatchNotifyEvent buildEvent(String groupKey, String groupKeyPattern, String changedType, String syncType, String clientUuid) { - return buildNotifyPatternAllListenersEvent(groupKey, groupKeyPattern, changedType, syncType, clientUuid, null); + return buildEvent(groupKey, groupKeyPattern, changedType, syncType, clientUuid, null); } /** @@ -90,7 +90,7 @@ public static ConfigFuzzyWatchNotifyEvent buildNotifyPatternAllListenersEvent(St * @param groupKey The groupKey of the configuration. * @return A new FuzzyListenNotifyEvent instance. */ - public static ConfigFuzzyWatchNotifyEvent buildNotifyPatternAllListenersEvent(String groupKey, + public static ConfigFuzzyWatchNotifyEvent buildEvent(String groupKey, String groupKeyPattern, String changedType, String syncType, String clientUuid, String watcherUuid) { ConfigFuzzyWatchNotifyEvent configFuzzyWatchNotifyEvent = new ConfigFuzzyWatchNotifyEvent(groupKey, changedType, syncType, groupKeyPattern, clientUuid, watcherUuid); diff --git a/client/src/main/java/com/alibaba/nacos/client/config/impl/ConfigFuzzyWatcherWrapper.java b/client/src/main/java/com/alibaba/nacos/client/config/impl/ConfigFuzzyWatcherWrapper.java index 2d7ade95b71..2de22641cce 100644 --- a/client/src/main/java/com/alibaba/nacos/client/config/impl/ConfigFuzzyWatcherWrapper.java +++ b/client/src/main/java/com/alibaba/nacos/client/config/impl/ConfigFuzzyWatcherWrapper.java @@ -24,11 +24,12 @@ import java.util.UUID; /** - * ConfigFuzzyWatcherWrapper. + * ConfigFuzzyWatcherWrapper. + * * @author shiyiyue */ public class ConfigFuzzyWatcherWrapper { - + FuzzyWatchEventWatcher fuzzyWatchEventWatcher; public ConfigFuzzyWatcherWrapper(FuzzyWatchEventWatcher fuzzyWatchEventWatcher) { @@ -58,7 +59,7 @@ public boolean equals(Object o) { public int hashCode() { return Objects.hash(fuzzyWatchEventWatcher, uuid); } - + Set getSyncGroupKeys() { return syncGroupKeys; } diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/cache/FuzzyWatchEventWatcherWrapper.java b/client/src/main/java/com/alibaba/nacos/client/naming/cache/FuzzyWatchEventWatcherWrapper.java index c607c8e9a44..964bb5c66d2 100644 --- a/client/src/main/java/com/alibaba/nacos/client/naming/cache/FuzzyWatchEventWatcherWrapper.java +++ b/client/src/main/java/com/alibaba/nacos/client/naming/cache/FuzzyWatchEventWatcherWrapper.java @@ -25,10 +25,38 @@ /** * fuzzy watcher wrapper. + * * @author shiyiyue */ public class FuzzyWatchEventWatcherWrapper { + long patternLimitTs = 0; + + long matchCountLimitTs = 0; + + static final long SUPPRESSED_PERIOD = 60 * 1000L; + + boolean patternLimitNotifySuppressed() { + boolean suppressed = patternLimitTs > 0 && System.currentTimeMillis() - patternLimitTs < SUPPRESSED_PERIOD; + if (!suppressed) { + patternLimitTs = System.currentTimeMillis(); + } + return suppressed; + } + + boolean matchCountLimitNotifySuppressed() { + boolean suppressed = System.currentTimeMillis() - matchCountLimitTs < SUPPRESSED_PERIOD; + if (!suppressed) { + matchCountLimitTs = System.currentTimeMillis(); + } + return suppressed; + } + + public void clearSuppressedTs() { + patternLimitTs = 0; + matchCountLimitTs = 0; + } + FuzzyWatchEventWatcher fuzzyWatchEventWatcher; public FuzzyWatchEventWatcherWrapper(FuzzyWatchEventWatcher fuzzyWatchEventWatcher) { diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/cache/NamingFuzzyWatchContext.java b/client/src/main/java/com/alibaba/nacos/client/naming/cache/NamingFuzzyWatchContext.java index e5173614bda..75bda1a68d3 100644 --- a/client/src/main/java/com/alibaba/nacos/client/naming/cache/NamingFuzzyWatchContext.java +++ b/client/src/main/java/com/alibaba/nacos/client/naming/cache/NamingFuzzyWatchContext.java @@ -18,6 +18,7 @@ import com.alibaba.nacos.api.naming.listener.FuzzyWatchChangeEvent; import com.alibaba.nacos.api.naming.listener.FuzzyWatchEventWatcher; +import com.alibaba.nacos.api.naming.listener.FuzzyWatchLoadWatcher; import com.alibaba.nacos.api.naming.pojo.ListView; import com.alibaba.nacos.api.naming.utils.NamingUtils; import com.alibaba.nacos.client.utils.LogUtils; @@ -38,10 +39,13 @@ import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicBoolean; import java.util.stream.Collectors; + import static com.alibaba.nacos.api.common.Constants.FUZZY_WATCH_DIFF_SYNC_NOTIFY; import static com.alibaba.nacos.api.common.Constants.FUZZY_WATCH_INIT_NOTIFY; import static com.alibaba.nacos.api.common.Constants.ServiceChangedType.ADD_SERVICE; import static com.alibaba.nacos.api.common.Constants.ServiceChangedType.DELETE_SERVICE; +import static com.alibaba.nacos.api.model.v2.ErrorCode.FUZZY_WATCH_PATTERN_MATCH_COUNT_OVER_LIMIT; +import static com.alibaba.nacos.api.model.v2.ErrorCode.FUZZY_WATCH_PATTERN_OVER_LIMIT; /** * fuzzy wather context for a single group key pattern. @@ -92,6 +96,22 @@ public class NamingFuzzyWatchContext { */ private final Set fuzzyWatchEventWatcherWrappers = new HashSet<>(); + long patternLimitTs = 0; + + private static final long SUPPRESSED_PERIOD = 60 * 1000L; + + boolean patternLimitSuppressed() { + return patternLimitTs > 0 && System.currentTimeMillis() - patternLimitTs < SUPPRESSED_PERIOD; + } + + public void clearOverLimitTs() { + this.patternLimitTs = 0; + } + + public void refreshOverLimitTs() { + this.patternLimitTs = System.currentTimeMillis(); + } + /** * Constructor with environment name, data ID pattern, and group. * @@ -300,6 +320,31 @@ void notifyFuzzyWatchers(String serviceKey, String changedType, String syncType, } } + void notifyOverLimitWatchers(int code) { + + if (this.patternLimitSuppressed()) { + return; + } + boolean notify = false; + + for (FuzzyWatchEventWatcherWrapper namingFuzzyWatcherWrapper : filterWatchers(null)) { + if (namingFuzzyWatcherWrapper.fuzzyWatchEventWatcher instanceof FuzzyWatchLoadWatcher) { + + if (FUZZY_WATCH_PATTERN_MATCH_COUNT_OVER_LIMIT.getCode().equals(code)) { + ((FuzzyWatchLoadWatcher) namingFuzzyWatcherWrapper.fuzzyWatchEventWatcher).onServiceReachUpLimit(); + notify = true; + } + if (FUZZY_WATCH_PATTERN_OVER_LIMIT.getCode().equals(code)) { + ((FuzzyWatchLoadWatcher) namingFuzzyWatcherWrapper.fuzzyWatchEventWatcher).onPatternOverLimit(); + notify = true; + } + } + } + if (notify) { + this.refreshOverLimitTs(); + } + } + private Set filterWatchers(String uuid) { if (StringUtils.isBlank(uuid) || CollectionUtils.isEmpty(getFuzzyWatchEventWatcherWrappers())) { return getFuzzyWatchEventWatcherWrappers(); @@ -311,6 +356,7 @@ private Set filterWatchers(String uuid) { /** * create a new future of this context. + * * @return */ public Future> createNewFuture() { diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/cache/NamingFuzzyWatchServiceListHolder.java b/client/src/main/java/com/alibaba/nacos/client/naming/cache/NamingFuzzyWatchServiceListHolder.java index 3137202bb5b..d395e62f337 100644 --- a/client/src/main/java/com/alibaba/nacos/client/naming/cache/NamingFuzzyWatchServiceListHolder.java +++ b/client/src/main/java/com/alibaba/nacos/client/naming/cache/NamingFuzzyWatchServiceListHolder.java @@ -21,16 +21,18 @@ import com.alibaba.nacos.api.naming.remote.request.NamingFuzzyWatchRequest; import com.alibaba.nacos.api.naming.remote.response.NamingFuzzyWatchResponse; import com.alibaba.nacos.client.naming.event.NamingFuzzyWatchNotifyEvent; +import com.alibaba.nacos.client.naming.event.NamingFuzzyWatchLoadEvent; import com.alibaba.nacos.client.naming.remote.gprc.NamingGrpcClientProxy; import com.alibaba.nacos.client.utils.LogUtils; import com.alibaba.nacos.common.executor.NameThreadFactory; import com.alibaba.nacos.common.notify.Event; import com.alibaba.nacos.common.notify.NotifyCenter; -import com.alibaba.nacos.common.notify.listener.Subscriber; +import com.alibaba.nacos.common.notify.listener.SmartSubscriber; import com.alibaba.nacos.common.utils.CollectionUtils; import org.slf4j.Logger; import java.util.ArrayList; +import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.concurrent.ArrayBlockingQueue; @@ -45,6 +47,7 @@ import static com.alibaba.nacos.api.common.Constants.ServiceChangedType.ADD_SERVICE; import static com.alibaba.nacos.api.common.Constants.WATCH_TYPE_CANCEL_WATCH; import static com.alibaba.nacos.api.common.Constants.WATCH_TYPE_WATCH; +import static com.alibaba.nacos.api.model.v2.ErrorCode.FUZZY_WATCH_PATTERN_MATCH_COUNT_OVER_LIMIT; import static com.alibaba.nacos.api.model.v2.ErrorCode.FUZZY_WATCH_PATTERN_OVER_LIMIT; /** @@ -52,7 +55,7 @@ * * @author tanyongquan */ -public class NamingFuzzyWatchServiceListHolder extends Subscriber { +public class NamingFuzzyWatchServiceListHolder extends SmartSubscriber { private static final Logger LOGGER = LogUtils.logger(NamingFuzzyWatchServiceListHolder.class); @@ -276,18 +279,25 @@ private void doExecuteNamingFuzzyWatch(List contextList } else { entry.setConsistentWithServer(true); } + entry.clearOverLimitTs(); } } catch (NacosException e) { // Log error and retry after a short delay - LOGGER.error(" fuzzy watch request fail.", e); - if (FUZZY_WATCH_PATTERN_OVER_LIMIT.getCode() == e.getErrCode()) { - LOGGER.error(" fuzzy watch pattern over limit,pattern ->{} ,fuzzy watch will be suppressed", - entry.getGroupKeyPattern()); + if (FUZZY_WATCH_PATTERN_OVER_LIMIT.getCode() == e.getErrCode() + || FUZZY_WATCH_PATTERN_MATCH_COUNT_OVER_LIMIT.getCode() == e.getErrCode()) { + LOGGER.error(" fuzzy watch pattern over limit,pattern ->{} ,fuzzy watch will be suppressed,msg={}", + entry.getGroupKeyPattern(), e.getErrMsg()); + NamingFuzzyWatchLoadEvent namingFuzzyWatchLoadEvent = NamingFuzzyWatchLoadEvent.buildEvent( + e.getErrCode(), entry.getGroupKeyPattern(), notifierEventScope); + NotifyCenter.publishEvent(namingFuzzyWatchLoadEvent); + } else { + LOGGER.error(" fuzzy watch request fail.", e); + try { - Thread.sleep(100L); + Thread.sleep(1000L); } catch (InterruptedException interruptedException) { // Ignore interruption } @@ -320,28 +330,48 @@ public Map getFuzzyMatchContextMap() { } @Override - public void onEvent(NamingFuzzyWatchNotifyEvent event) { - if (!event.scope().equals(notifierEventScope)) { - return; - } + public void onEvent(Event event) { - String changedType = event.getChangedType(); - String syncType = event.getSyncType(); - - String serviceKey = event.getServiceKey(); - String pattern = event.getPattern(); - String watchUuid = event.getWatcherUuid(); - NamingFuzzyWatchContext namingFuzzyWatchContext = fuzzyMatchContextMap.get(pattern); - if (namingFuzzyWatchContext == null) { - return; + if (event instanceof NamingFuzzyWatchNotifyEvent) { + if (!event.scope().equals(notifierEventScope)) { + return; + } + NamingFuzzyWatchNotifyEvent watchNotifyEvent = (NamingFuzzyWatchNotifyEvent) event; + String changedType = watchNotifyEvent.getChangedType(); + String syncType = watchNotifyEvent.getSyncType(); + + String serviceKey = watchNotifyEvent.getServiceKey(); + String pattern = watchNotifyEvent.getPattern(); + String watchUuid = watchNotifyEvent.getWatcherUuid(); + NamingFuzzyWatchContext namingFuzzyWatchContext = fuzzyMatchContextMap.get(pattern); + if (namingFuzzyWatchContext == null) { + return; + } + namingFuzzyWatchContext.notifyFuzzyWatchers(serviceKey, changedType, syncType, watchUuid); + } + if (event instanceof NamingFuzzyWatchLoadEvent) { + if (!event.scope().equals(notifierEventScope)) { + return; + } + + NamingFuzzyWatchLoadEvent overLimitEvent = (NamingFuzzyWatchLoadEvent) event; + NamingFuzzyWatchContext namingFuzzyWatchContext = fuzzyMatchContextMap.get( + overLimitEvent.getGroupKeyPattern()); + if (namingFuzzyWatchContext == null) { + return; + } + + namingFuzzyWatchContext.notifyOverLimitWatchers(overLimitEvent.getCode()); } - namingFuzzyWatchContext.notifyFuzzyWatchers(serviceKey, changedType, syncType, watchUuid); } @Override - public Class subscribeType() { - return NamingFuzzyWatchNotifyEvent.class; + public List> subscribeTypes() { + List> result = new LinkedList<>(); + result.add(NamingFuzzyWatchNotifyEvent.class); + result.add(NamingFuzzyWatchLoadEvent.class); + return result; } public String getNotifierEventScope() { diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/event/NamingFuzzyWatchLoadEvent.java b/client/src/main/java/com/alibaba/nacos/client/naming/event/NamingFuzzyWatchLoadEvent.java new file mode 100644 index 00000000000..d4218585994 --- /dev/null +++ b/client/src/main/java/com/alibaba/nacos/client/naming/event/NamingFuzzyWatchLoadEvent.java @@ -0,0 +1,82 @@ +/* + * Copyright 1999-2023 Alibaba Group Holding Ltd. + * + * Licensed 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 com.alibaba.nacos.client.naming.event; + +import com.alibaba.nacos.common.notify.Event; + +/** + * Event class for fuzzy listen notifications. + * + *

This class represents an event used for notifying fuzzy listen changes. It extends {@link Event}, indicating + * that it may be processed asynchronously. The event contains information about the group, dataId, type, and UUID of + * the notification. + * + * @author stone-98 + * @date 2024/3/4 + */ +public class NamingFuzzyWatchLoadEvent extends Event { + + private String eventScope; + + /** + * The groupKeyPattern of configuration. + */ + private String groupKeyPattern; + + private int code; + + /** + * Constructs a new FuzzyListenNotifyEvent. + */ + public NamingFuzzyWatchLoadEvent() { + } + + /** + * Constructs a new FuzzyListenNotifyEvent with the specified group, dataId, and type. + * + * @param code The type of notification. + * @param groupKeyPattern The groupKeyPattern of notification. + */ + private NamingFuzzyWatchLoadEvent(int code, String groupKeyPattern, String eventScope) { + this.code = code; + this.groupKeyPattern = groupKeyPattern; + this.eventScope = eventScope; + } + + /** + * Builds a new FuzzyListenNotifyEvent with the specified group, dataId, and type. + * + * @param groupKeyPattern The groupKey of the configuration. + * @return A new FuzzyListenNotifyEvent instance. + */ + public static NamingFuzzyWatchLoadEvent buildEvent(int code, String groupKeyPattern, String scope) { + return new NamingFuzzyWatchLoadEvent(code, groupKeyPattern, scope); + } + + @Override + public String scope() { + return eventScope; + } + + public String getGroupKeyPattern() { + return groupKeyPattern; + } + + public int getCode() { + return code; + } +} diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/NamingFuzzyWatchNotifyRequestHandler.java b/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/NamingFuzzyWatchNotifyRequestHandler.java index 6650613c966..649d4f007eb 100644 --- a/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/NamingFuzzyWatchNotifyRequestHandler.java +++ b/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/NamingFuzzyWatchNotifyRequestHandler.java @@ -34,7 +34,6 @@ import java.util.Collection; import static com.alibaba.nacos.api.common.Constants.FUZZY_WATCH_RESOURCE_CHANGED; -import static com.alibaba.nacos.client.utils.LogUtils.NAMING_LOGGER; /** * handle fuzzy watch request from server. @@ -72,13 +71,6 @@ public Response requestReply(Request request, Connection connection) { } } else if (watchNotifySyncRequest.getSyncType().equals(Constants.FINISH_FUZZY_WATCH_INIT_NOTIFY)) { namingFuzzyWatchContext.markInitializationComplete(); - } else if (watchNotifySyncRequest.getSyncType() - .equals(Constants.FUZZY_WATCH_MATCH_RESOURCE_OVER_LIMIT)) { - NAMING_LOGGER.warn( - "[{}] [fuzzy-watch] pattern matched service count reach to up limit,pattern ->{}, received keys count {}", - namingFuzzyWatchServiceListHolder.getNotifierEventScope(), - watchNotifySyncRequest.getGroupKeyPattern(), - namingFuzzyWatchContext.getReceivedServiceKeys().size()); } } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigFuzzyWatchRequestHandler.java b/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigFuzzyWatchRequestHandler.java index d6a9b7b60c2..fc01519931a 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigFuzzyWatchRequestHandler.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigFuzzyWatchRequestHandler.java @@ -37,6 +37,7 @@ import static com.alibaba.nacos.api.common.Constants.WATCH_TYPE_CANCEL_WATCH; import static com.alibaba.nacos.api.common.Constants.WATCH_TYPE_WATCH; +import static com.alibaba.nacos.api.model.v2.ErrorCode.FUZZY_WATCH_PATTERN_MATCH_COUNT_OVER_LIMIT; /** * Handler for processing batch fuzzy listen requests. @@ -88,8 +89,15 @@ public ConfigFuzzyWatchResponse handle(ConfigFuzzyWatchRequest request, RequestM request.isInitializing())); } catch (NacosException nacosException) { ConfigFuzzyWatchResponse configFuzzyWatchResponse = new ConfigFuzzyWatchResponse(); - configFuzzyWatchResponse.setErrorCode(nacosException.getErrCode()); - configFuzzyWatchResponse.setMessage(nacosException.getErrMsg()); + configFuzzyWatchResponse.setErrorInfo(nacosException.getErrCode(), nacosException.getErrMsg()); + return configFuzzyWatchResponse; + } + + boolean reachToUpLimit = configFuzzyWatchContextService.reachToUpLimit(groupKeyPattern); + if (reachToUpLimit) { + ConfigFuzzyWatchResponse configFuzzyWatchResponse = new ConfigFuzzyWatchResponse(); + configFuzzyWatchResponse.setErrorInfo(FUZZY_WATCH_PATTERN_MATCH_COUNT_OVER_LIMIT.getCode(), + FUZZY_WATCH_PATTERN_MATCH_COUNT_OVER_LIMIT.getMsg()); return configFuzzyWatchResponse; } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigFuzzyWatchSyncNotifier.java b/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigFuzzyWatchSyncNotifier.java index b598ec65acb..e4c9b24c283 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigFuzzyWatchSyncNotifier.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigFuzzyWatchSyncNotifier.java @@ -87,7 +87,7 @@ public ConfigFuzzyWatchSyncNotifier(ConnectionManager connectionManager, RpcPush * @param retryTask The retry task containing the RPC push request * @param connectionManager The connection manager for managing client connections */ - private static void push(FuzzyWatchRpcPushTask retryTask, ConnectionManager connectionManager) { + static void push(FuzzyWatchRpcPushTask retryTask, ConnectionManager connectionManager) { ConfigFuzzyWatchSyncRequest notifyRequest = retryTask.notifyRequest; // Check if the maximum retry times have been reached if (retryTask.isOverTimes()) { @@ -126,20 +126,10 @@ public void handleFuzzyWatchEvent(ConfigFuzzyWatchEvent event) { clientExistingGroupKeys); if (CollectionUtils.isEmpty(configStates)) { + int maxPushRetryTimes = ConfigCommonConfig.getInstance().getMaxPushRetryTimes(); if (event.isInitializing()) { - ConfigFuzzyWatchSyncRequest request = ConfigFuzzyWatchSyncRequest.buildInitFinishRequest( event.getGroupKeyPattern()); - int maxPushRetryTimes = ConfigCommonConfig.getInstance().getMaxPushRetryTimes(); - // Create RPC push task and push the request to the client - FuzzyWatchRpcPushTask fuzzyWatchRpcPushTask = new FuzzyWatchRpcPushTask(request, null, - maxPushRetryTimes, event.getConnectionId()); - push(fuzzyWatchRpcPushTask, connectionManager); - } else if (matchGroupKeys.size() >= ConfigCommonConfig.getInstance().getMaxMatchedConfigCount()) { - // no diff but - ConfigFuzzyWatchSyncRequest request = ConfigFuzzyWatchSyncRequest.buildOverLimitRequest( - event.getGroupKeyPattern()); - int maxPushRetryTimes = ConfigCommonConfig.getInstance().getMaxPushRetryTimes(); // Create RPC push task and push the request to the client FuzzyWatchRpcPushTask fuzzyWatchRpcPushTask = new FuzzyWatchRpcPushTask(request, null, maxPushRetryTimes, event.getConnectionId()); @@ -147,6 +137,12 @@ public void handleFuzzyWatchEvent(ConfigFuzzyWatchEvent event) { } } else { + + // delete notify protection when pattern match count over limit because matchGroupKeys may not a full set. + if (configFuzzyWatchContextService.reachToUpLimit(event.getGroupKeyPattern())) { + configStates.removeIf(item -> !item.isExist()); + } + String syncType = event.isInitializing() ? FUZZY_WATCH_INIT_NOTIFY : FUZZY_WATCH_DIFF_SYNC_NOTIFY; int batchSize = ConfigCommonConfig.getInstance().getBatchSize(); @@ -344,9 +340,9 @@ public void onSuccess() { connectionManager); } } - } + /** * Handles the failure of the RPC push operation. * diff --git a/config/src/main/java/com/alibaba/nacos/config/server/service/ConfigFuzzyWatchContextService.java b/config/src/main/java/com/alibaba/nacos/config/server/service/ConfigFuzzyWatchContextService.java index 1f88b32bcdc..fe99189fbc5 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/service/ConfigFuzzyWatchContextService.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/service/ConfigFuzzyWatchContextService.java @@ -49,19 +49,19 @@ public class ConfigFuzzyWatchContextService { /** * groupKeyPattern -> watched client id set. */ - private final Map> watchedClients = new ConcurrentHashMap<>(); + private final Map> watchedClientsMap = new ConcurrentHashMap<>(); /** * groupKeyPattern -> matched groupKeys set. */ - private final Map> matchedGroupKeys = new ConcurrentHashMap<>(); + private final Map> matchedGroupKeysMap = new ConcurrentHashMap<>(); public ConfigFuzzyWatchContextService() { } @PostConstruct public void init() { - GlobalExecutor.scheduleWithFixDelayByCommon(() -> trimFuzzyWatchContext(), 60000); + GlobalExecutor.scheduleWithFixDelayByCommon(() -> trimFuzzyWatchContext(), 30000); } /** @@ -71,10 +71,10 @@ public void init() { */ void trimFuzzyWatchContext() { try { - Iterator>> iterator = matchedGroupKeys.entrySet().iterator(); + Iterator>> iterator = matchedGroupKeysMap.entrySet().iterator(); while (iterator.hasNext()) { Map.Entry> matchedGroupKeys = iterator.next(); - Set watchedClients = this.watchedClients.get(matchedGroupKeys.getKey()); + Set watchedClients = this.watchedClientsMap.get(matchedGroupKeys.getKey()); if (watchedClients == null) { iterator.remove(); @@ -84,11 +84,15 @@ void trimFuzzyWatchContext() { } else if (watchedClients.isEmpty()) { LogUtil.DEFAULT_LOG.info("[fuzzy-watch] no client watched pattern {},remove watchedClients context", matchedGroupKeys.getKey()); - this.watchedClients.remove(matchedGroupKeys.getKey()); - } else if (matchedGroupKeys.getValue().size() >= ConfigCommonConfig.getInstance() - .getMaxMatchedConfigCount()) { + this.watchedClientsMap.remove(matchedGroupKeys.getKey()); + } else if (reachToUpLimit(matchedGroupKeys.getValue().size())) { LogUtil.DEFAULT_LOG.warn( - "[fuzzy-watch] pattern {} matched groupKey count is reach to upper limit {}, fuzzy watch notify may be suppressed ", + "[fuzzy-watch] pattern {} matched config count has reached to upper limit {}, fuzzy watch has been suppressed ", + matchedGroupKeys.getKey(), matchedGroupKeys.getValue().size()); + } else if (reachToUpLimit((int) (matchedGroupKeys.getValue().size() * 1.25))) { + LogUtil.DEFAULT_LOG.warn( + "[fuzzy-watch] pattern {} matched config count has reached to 80% of the upper limit {} " + + ",it may has a risk of notify suppressed in the near further", matchedGroupKeys.getKey(), matchedGroupKeys.getValue().size()); } } @@ -104,7 +108,7 @@ void trimFuzzyWatchContext() { * @return */ public Set matchGroupKeys(String groupKeyPattern) { - return matchedGroupKeys.get(groupKeyPattern); + return matchedGroupKeysMap.get(groupKeyPattern); } /** @@ -122,23 +126,73 @@ public boolean syncGroupKeyContext(String groupKey, String changedType) { String dataId = groupKeyItems[0]; String group = groupKeyItems[1]; String namespace = groupKeyItems[2]; - Iterator>> iterator = matchedGroupKeys.entrySet().iterator(); boolean tryAdd = changedType.equals(ADD_CONFIG) || changedType.equals(CONFIG_CHANGED); boolean tryRemove = changedType.equals(DELETE_CONFIG); + + Iterator>> iterator = matchedGroupKeysMap.entrySet().iterator(); while (iterator.hasNext()) { Map.Entry> entry = iterator.next(); if (FuzzyGroupKeyPattern.matchPattern(entry.getKey(), dataId, group, namespace)) { - if (tryAdd && entry.getValue().add(groupKey)) { + boolean containsAlready = entry.getValue().contains(groupKey); + boolean reachToUpLimit = reachToUpLimit(entry.getValue().size()); + if (tryAdd && !containsAlready && reachToUpLimit) { + LogUtil.DEFAULT_LOG.warn("[fuzzy-watch] pattern matched config count is over limit , " + + "current config will be ignored for pattern {} ,current count is {}", entry.getKey(), + entry.getValue().size()); + continue; + } + + if (tryAdd && !containsAlready && entry.getValue().add(groupKey)) { needNotify = true; } - if (tryRemove && entry.getValue().remove(groupKey)) { + if (tryRemove && containsAlready && entry.getValue().remove(groupKey)) { needNotify = true; + if (reachToUpLimit) { + makeupMatchedGroupKeys(entry.getKey()); + } } } } return needNotify; } + /** + * make matched group key when deleted configs on loa protection model. + * + * @param groupKeyPattern group key pattern. + */ + public void makeupMatchedGroupKeys(String groupKeyPattern) { + + Set matchedGroupKeys = matchedGroupKeysMap.get(groupKeyPattern); + if (matchedGroupKeys == null || reachToUpLimit(matchedGroupKeys.size())) { + return; + } + + for (String groupKey : ConfigCacheService.CACHE.keySet()) { + String[] groupKeyItems = GroupKey.parseKey(groupKey); + if (FuzzyGroupKeyPattern.matchPattern(groupKeyPattern, groupKeyItems[0], groupKeyItems[1], groupKeyItems[2]) + && !matchedGroupKeys.contains(groupKey)) { + matchedGroupKeys.add(groupKey); + LogUtil.DEFAULT_LOG.info("[fuzzy-watch] pattern {} makeup group key {}", groupKeyPattern, groupKey); + if (reachToUpLimit(matchedGroupKeys.size())) { + LogUtil.DEFAULT_LOG.warn( + "[fuzzy-watch] pattern {] matched config count is over limit ,makeup group keys skip.", + groupKeyPattern); + return; + } + } + } + } + + private boolean reachToUpLimit(int size) { + return size >= ConfigCommonConfig.getInstance().getMaxMatchedConfigCount(); + } + + public boolean reachToUpLimit(String groupKeyPattern) { + Set strings = matchedGroupKeysMap.get(groupKeyPattern); + return strings != null && (reachToUpLimit(strings.size())); + } + /** * Matches the client effective group keys based on the specified group key pattern, client IP, and tag. * @@ -146,19 +200,19 @@ public boolean syncGroupKeyContext(String groupKey, String changedType) { * @return A set of group keys that match the pattern and are effective for the client. */ private void initMatchGroupKeys(String groupKeyPattern) throws NacosException { - if (matchedGroupKeys.containsKey(groupKeyPattern)) { + if (matchedGroupKeysMap.containsKey(groupKeyPattern)) { return; } - if (matchedGroupKeys.size() >= ConfigCommonConfig.getInstance().getMaxPatternCount()) { + if (matchedGroupKeysMap.size() >= ConfigCommonConfig.getInstance().getMaxPatternCount()) { LogUtil.DEFAULT_LOG.warn( "[fuzzy-watch] pattern count is over limit ,pattern {} init fail,current count is {}", - groupKeyPattern, matchedGroupKeys.size()); + groupKeyPattern, matchedGroupKeysMap.size()); throw new NacosException(FUZZY_WATCH_PATTERN_OVER_LIMIT.getCode(), FUZZY_WATCH_PATTERN_OVER_LIMIT.getMsg()); } - matchedGroupKeys.computeIfAbsent(groupKeyPattern, k -> new HashSet<>()); - Set matchedGroupKeys = this.matchedGroupKeys.get(groupKeyPattern); + matchedGroupKeysMap.computeIfAbsent(groupKeyPattern, k -> new HashSet<>()); + Set matchedGroupKeys = this.matchedGroupKeysMap.get(groupKeyPattern); long matchBeginTime = System.currentTimeMillis(); boolean overMatchCount = false; for (String groupKey : ConfigCacheService.CACHE.keySet()) { @@ -191,10 +245,10 @@ private void initMatchGroupKeys(String groupKeyPattern) throws NacosException { * @throws NacosException over max pattern count. */ public synchronized void addFuzzyWatch(String groupKeyPattern, String connectId) throws NacosException { - watchedClients.computeIfAbsent(groupKeyPattern, k -> new HashSet<>()); + watchedClientsMap.computeIfAbsent(groupKeyPattern, k -> new HashSet<>()); initMatchGroupKeys(groupKeyPattern); // Add the connection ID to the set associated with the key pattern in keyPatternContext - watchedClients.get(groupKeyPattern).add(connectId); + watchedClientsMap.get(groupKeyPattern).add(connectId); } /** @@ -208,7 +262,7 @@ public synchronized void addFuzzyWatch(String groupKeyPattern, String connectId) */ public synchronized void removeFuzzyListen(String groupKeyPattern, String connectionId) { // Retrieve the set of connection IDs associated with the group key pattern - Set connectIds = watchedClients.get(groupKeyPattern); + Set connectIds = watchedClientsMap.get(groupKeyPattern); if (CollectionUtils.isNotEmpty(connectIds)) { // Remove the connection ID from the set if it exists connectIds.remove(connectionId); @@ -221,7 +275,7 @@ public synchronized void removeFuzzyListen(String groupKeyPattern, String connec * @param connectionId connection id. */ public void clearFuzzyWatchContext(String connectionId) { - for (Map.Entry> keyPatternContextEntry : watchedClients.entrySet()) { + for (Map.Entry> keyPatternContextEntry : watchedClientsMap.entrySet()) { Set connectionIds = keyPatternContextEntry.getValue(); if (CollectionUtils.isNotEmpty(connectionIds)) { connectionIds.remove(connectionId); @@ -239,7 +293,7 @@ public Set getMatchedClients(String groupKey) { // Initialize a set to store the matched connection IDs Set connectIds = new HashSet<>(); // Iterate over each key pattern in the context - Iterator>> watchClientIterator = watchedClients.entrySet().iterator(); + Iterator>> watchClientIterator = watchedClientsMap.entrySet().iterator(); String[] groupItems = GroupKey2.parseKey(groupKey); diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/index/NamingFuzzyWatchContextService.java b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/index/NamingFuzzyWatchContextService.java index b9fcecfc1b7..027ed2f76f1 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/index/NamingFuzzyWatchContextService.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/index/NamingFuzzyWatchContextService.java @@ -57,14 +57,14 @@ public class NamingFuzzyWatchContextService extends SmartSubscriber { /** * watched client ids of a pattern, {fuzzy watch pattern -> Set[watched clientID]}. */ - private final ConcurrentMap> watchedClients = new ConcurrentHashMap<>(); + private final ConcurrentMap> watchedClientsMap = new ConcurrentHashMap<>(); /** * The pattern matched service keys for pattern.{fuzzy watch pattern -> Set[matched service keys]}. initialized a * new entry pattern when a client register a new pattern. destroyed a new entry pattern by task when no clients * watch pattern in max 30s delay. */ - private final ConcurrentMap> matchedServiceKeys = new ConcurrentHashMap<>(); + private final ConcurrentMap> matchedServiceKeysMap = new ConcurrentHashMap<>(); GlobalConfig globalConfig; @@ -85,10 +85,12 @@ public void init() { */ private void trimFuzzyWatchContext() { try { - Iterator>> iterator = matchedServiceKeys.entrySet().iterator(); + Iterator>> iterator = matchedServiceKeysMap.entrySet().iterator(); while (iterator.hasNext()) { Map.Entry> next = iterator.next(); - Set watchedClients = this.watchedClients.get(next.getKey()); + Set watchedClients = this.watchedClientsMap.get(next.getKey()); + + int serviceKeysCount = next.getValue().size(); if (watchedClients == null) { Loggers.SRV_LOG.info( "[fuzzy-watch] no watchedClients context for pattern {},remove matchedGroupKeys context", @@ -97,11 +99,16 @@ private void trimFuzzyWatchContext() { } else if (watchedClients.isEmpty()) { Loggers.SRV_LOG.info("[fuzzy-watch] no client watched pattern {},remove watchedClients context", next.getKey()); - this.watchedClients.remove(next.getKey()); - } else if (next.getValue().size() >= globalConfig.getMaxMatchedServiceCount()) { + this.watchedClientsMap.remove(next.getKey()); + } else if (reachToUpLimit(serviceKeysCount)) { Loggers.SRV_LOG.warn( "[fuzzy-watch] pattern {} matched serviceKey count is reach to upper limit {}, fuzzy watch notify may be suppressed ", next.getKey(), next.getValue().size()); + } else if (reachToUpLimit((int) (serviceKeysCount * 1.25))) { + Loggers.SRV_LOG.warn( + "[fuzzy-watch] pattern {} matched serviceKey count has reached to 80% of the upper limit {} ," + + "it may has a risk of notify suppressed in the near further", next.getKey(), + serviceKeysCount); } } } catch (Throwable throwable) { @@ -132,7 +139,7 @@ public void onEvent(Event event) { */ public Set getFuzzyWatchedClients(Service service) { Set matchedClients = new HashSet<>(); - Iterator>> iterator = watchedClients.entrySet().iterator(); + Iterator>> iterator = watchedClientsMap.entrySet().iterator(); while (iterator.hasNext()) { Map.Entry> entry = iterator.next(); if (FuzzyGroupKeyPattern.matchPattern(entry.getKey(), service.getName(), service.getGroup(), @@ -162,32 +169,36 @@ public boolean syncServiceContext(Service changedService, String changedType) { Loggers.SRV_LOG.warn("[fuzzy-watch] service change matched,service key {},changed type {} ", serviceKey, changedType); - Iterator>> iterator = matchedServiceKeys.entrySet().iterator(); - + Iterator>> iterator = matchedServiceKeysMap.entrySet().iterator(); + boolean tryAdd = changedType.equals(ADD_SERVICE); + boolean tryRemove = changedType.equals(DELETE_SERVICE); while (iterator.hasNext()) { Map.Entry> next = iterator.next(); if (FuzzyGroupKeyPattern.matchPattern(next.getKey(), changedService.getName(), changedService.getGroup(), changedService.getNamespace())) { - Set matchedServiceKeys = next.getValue(); - if (changedType.equals(ADD_SERVICE) && !matchedServiceKeys.contains(serviceKey)) { - if (matchedServiceKeys.size() >= globalConfig.getMaxMatchedServiceCount()) { - Loggers.SRV_LOG.warn("[fuzzy-watch] pattern matched service count is over limit , " - + "current service will be ignore for pattern {} ,current count is {}", next.getKey(), - matchedServiceKeys.size()); - continue; - } - if (matchedServiceKeys.add(serviceKey)) { - Loggers.SRV_LOG.info("[fuzzy-watch] pattern {} matched service keys count changed to {}", - next.getKey(), matchedServiceKeys.size()); - needNotify = true; - } + boolean reachToUpLimit = reachToUpLimit(matchedServiceKeys.size()); + boolean containsAlready = matchedServiceKeys.contains(serviceKey); + + if (tryAdd && !containsAlready && reachToUpLimit) { + Loggers.SRV_LOG.warn("[fuzzy-watch] pattern matched service count is over limit , " + + "current service will be ignore for pattern {} ,current count is {}", next.getKey(), + matchedServiceKeys.size()); + continue; + } + + if (tryAdd && !containsAlready && matchedServiceKeys.add(serviceKey)) { + Loggers.SRV_LOG.info("[fuzzy-watch] pattern {} matched service keys count changed to {}", + next.getKey(), matchedServiceKeys.size()); + needNotify = true; - } else if (changedType.equals(DELETE_SERVICE) && matchedServiceKeys.contains(serviceKey)) { - if (matchedServiceKeys.remove(serviceKey)) { - Loggers.SRV_LOG.info("[fuzzy-watch] pattern {} matched service keys count changed to {}", - next.getKey(), matchedServiceKeys.size()); - needNotify = true; + } + if (tryRemove && containsAlready && matchedServiceKeys.remove(serviceKey)) { + Loggers.SRV_LOG.info("[fuzzy-watch] pattern {} matched service keys count changed to {}", + next.getKey(), matchedServiceKeys.size()); + needNotify = true; + if (reachToUpLimit) { + makeupMatchedGroupKeys(next.getKey()); } } } @@ -195,6 +206,47 @@ public boolean syncServiceContext(Service changedService, String changedType) { return needNotify; } + private boolean reachToUpLimit(int size) { + return size >= globalConfig.getMaxMatchedServiceCount(); + } + + public boolean reachToUpLimit(String groupKeyPattern) { + Set strings = matchedServiceKeysMap.get(groupKeyPattern); + return strings != null && (reachToUpLimit(strings.size())); + } + + /** + * make matched group key when deleted configs on loa protection model. + * + * @param groupKeyPattern group key pattern. + */ + public void makeupMatchedGroupKeys(String groupKeyPattern) { + + Set matchedGroupKeys = matchedServiceKeysMap.get(groupKeyPattern); + if (matchedGroupKeys == null || reachToUpLimit(matchedGroupKeys.size())) { + return; + } + Set namespaceServices = ServiceManager.getInstance() + .getSingletons(getNamespaceFromPattern(groupKeyPattern)); + for (Service service : namespaceServices) { + String serviceKey = NamingUtils.getServiceKey(service.getNamespace(), service.getGroup(), + service.getName()); + if (FuzzyGroupKeyPattern.matchPattern(groupKeyPattern, service.getName(), service.getGroup(), + service.getNamespace()) && !matchedGroupKeys.contains(serviceKey)) { + if (matchedGroupKeys.add(serviceKey)) { + Loggers.SRV_LOG.info("[fuzzy-watch] pattern {} makeup service key {}", groupKeyPattern, serviceKey); + if (reachToUpLimit(matchedGroupKeys.size())) { + Loggers.SRV_LOG.warn( + "[fuzzy-watch] pattern {} matched service count reach to up limit ,makeup group keys skip.", + groupKeyPattern); + return; + } + } + + } + } + } + /** * sync fuzzy watch context. * @@ -204,9 +256,9 @@ public boolean syncServiceContext(Service changedService, String changedType) { */ public void syncFuzzyWatcherContext(String groupKeyPattern, String clientId) throws NacosException { //init empty watchedClients first,when pattern is not over limit,then add clientId. - watchedClients.computeIfAbsent(groupKeyPattern, key -> new ConcurrentHashSet<>()); + watchedClientsMap.computeIfAbsent(groupKeyPattern, key -> new ConcurrentHashSet<>()); initWatchMatchService(groupKeyPattern); - watchedClients.get(groupKeyPattern).add(clientId); + watchedClientsMap.get(groupKeyPattern).add(clientId); } /** @@ -216,11 +268,11 @@ public void syncFuzzyWatcherContext(String groupKeyPattern, String clientId) thr * @return */ public Set matchServiceKeys(String groupKeyPattern) { - return matchedServiceKeys.get(groupKeyPattern); + return matchedServiceKeysMap.get(groupKeyPattern); } private void removeFuzzyWatchContext(String clientId) { - Iterator>> iterator = watchedClients.entrySet().iterator(); + Iterator>> iterator = watchedClientsMap.entrySet().iterator(); while (iterator.hasNext()) { Map.Entry> next = iterator.next(); next.getValue().remove(clientId); @@ -234,8 +286,8 @@ private void removeFuzzyWatchContext(String clientId) { * @param clientId client id. */ public void removeFuzzyWatchContext(String groupKeyPattern, String clientId) { - if (watchedClients.containsKey(groupKeyPattern)) { - watchedClients.get(groupKeyPattern).remove(clientId); + if (watchedClientsMap.containsKey(groupKeyPattern)) { + watchedClientsMap.get(groupKeyPattern).remove(clientId); } } @@ -247,11 +299,11 @@ public void removeFuzzyWatchContext(String groupKeyPattern, String clientId) { */ public Set initWatchMatchService(String completedPattern) throws NacosException { - if (!matchedServiceKeys.containsKey(completedPattern)) { - if (matchedServiceKeys.size() >= globalConfig.getMaxPatternCount()) { + if (!matchedServiceKeysMap.containsKey(completedPattern)) { + if (matchedServiceKeysMap.size() >= globalConfig.getMaxPatternCount()) { Loggers.SRV_LOG.warn( "FUZZY_WATCH: fuzzy watch pattern count is over limit ,pattern {} init fail,current count is {}", - completedPattern, matchedServiceKeys.size()); + completedPattern, matchedServiceKeysMap.size()); throw new NacosException(FUZZY_WATCH_PATTERN_OVER_LIMIT.getCode(), FUZZY_WATCH_PATTERN_OVER_LIMIT.getMsg()); } @@ -259,7 +311,7 @@ public Set initWatchMatchService(String completedPattern) throws NacosEx long matchBeginTime = System.currentTimeMillis(); Set namespaceServices = ServiceManager.getInstance() .getSingletons(getNamespaceFromPattern(completedPattern)); - Set matchedServices = matchedServiceKeys.computeIfAbsent(completedPattern, k -> new HashSet<>()); + Set matchedServices = matchedServiceKeysMap.computeIfAbsent(completedPattern, k -> new HashSet<>()); boolean overMatchCount = false; for (Service service : namespaceServices) { if (FuzzyGroupKeyPattern.matchPattern(completedPattern, service.getName(), service.getGroup(), @@ -276,14 +328,14 @@ public Set initWatchMatchService(String completedPattern) throws NacosEx NamingUtils.getServiceKey(service.getNamespace(), service.getGroup(), service.getName())); } } - matchedServiceKeys.putIfAbsent(completedPattern, matchedServices); + matchedServiceKeysMap.putIfAbsent(completedPattern, matchedServices); Loggers.SRV_LOG.info("FUZZY_WATCH: pattern {} match {} services, overMatchCount={},cost {}ms", completedPattern, matchedServices.size(), overMatchCount, System.currentTimeMillis() - matchBeginTime); } - return new HashSet(matchedServiceKeys.get(completedPattern)); + return new HashSet(matchedServiceKeysMap.get(completedPattern)); } } diff --git a/naming/src/main/java/com/alibaba/nacos/naming/push/NamingFuzzyWatchSyncNotifier.java b/naming/src/main/java/com/alibaba/nacos/naming/push/NamingFuzzyWatchSyncNotifier.java index 9cb55d50e33..2a607f66e4f 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/push/NamingFuzzyWatchSyncNotifier.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/push/NamingFuzzyWatchSyncNotifier.java @@ -39,7 +39,6 @@ import static com.alibaba.nacos.api.common.Constants.FINISH_FUZZY_WATCH_INIT_NOTIFY; import static com.alibaba.nacos.api.common.Constants.FUZZY_WATCH_DIFF_SYNC_NOTIFY; import static com.alibaba.nacos.api.common.Constants.FUZZY_WATCH_INIT_NOTIFY; -import static com.alibaba.nacos.api.common.Constants.FUZZY_WATCH_MATCH_RESOURCE_OVER_LIMIT; import static com.alibaba.nacos.api.common.Constants.ServiceChangedType.ADD_SERVICE; import static com.alibaba.nacos.api.common.Constants.ServiceChangedType.DELETE_SERVICE; @@ -90,10 +89,17 @@ private void handleClientFuzzyWatchEvent(ClientOperationEvent.ClientFuzzyWatchEv Set clientReceivedGroupKeys = clientFuzzyWatchEvent.getClientReceivedServiceKeys(); List groupKeyStates = FuzzyGroupKeyPattern.diffGroupKeys( patternMatchedServiceKeys, clientReceivedGroupKeys); - Set syncContext = convert(groupKeyStates); + + // delete notify protection when pattern match count over limit because patternMatchedServiceKeys may not full set. + if (namingFuzzyWatchContextService.reachToUpLimit(completedPattern)) { + groupKeyStates.removeIf(item -> !item.isExist()); + } + String syncType = clientFuzzyWatchEvent.isInitializing() ? FUZZY_WATCH_INIT_NOTIFY : FUZZY_WATCH_DIFF_SYNC_NOTIFY; + Set syncContext = convert(groupKeyStates); + if (CollectionUtils.isNotEmpty(groupKeyStates)) { Set> dividedServices = divideServiceByBatch(syncContext); BatchTaskCounter batchTaskCounter = new BatchTaskCounter(dividedServices.size()); @@ -116,12 +122,6 @@ private void handleClientFuzzyWatchEvent(ClientOperationEvent.ClientFuzzyWatchEv fuzzyWatchPushDelayTaskEngine.addTask(FuzzyWatchPushDelayTaskEngine.getTaskKey(fuzzyWatchSyncNotifyTask), fuzzyWatchSyncNotifyTask); - } else if (FUZZY_WATCH_DIFF_SYNC_NOTIFY.equals(syncType)) { - FuzzyWatchSyncNotifyTask fuzzyWatchSyncNotifyTask = new FuzzyWatchSyncNotifyTask( - clientFuzzyWatchEvent.getClientId(), completedPattern, FUZZY_WATCH_MATCH_RESOURCE_OVER_LIMIT, null, - PushConfig.getInstance().getPushTaskRetryDelay()); - fuzzyWatchPushDelayTaskEngine.addTask(FuzzyWatchPushDelayTaskEngine.getTaskKey(fuzzyWatchSyncNotifyTask), - fuzzyWatchSyncNotifyTask); } } diff --git a/naming/src/main/java/com/alibaba/nacos/naming/remote/rpc/handler/NamingFuzzyWatchRequestHandler.java b/naming/src/main/java/com/alibaba/nacos/naming/remote/rpc/handler/NamingFuzzyWatchRequestHandler.java index 45948da9f36..f21844059c9 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/remote/rpc/handler/NamingFuzzyWatchRequestHandler.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/remote/rpc/handler/NamingFuzzyWatchRequestHandler.java @@ -30,6 +30,7 @@ import static com.alibaba.nacos.api.common.Constants.WATCH_TYPE_CANCEL_WATCH; import static com.alibaba.nacos.api.common.Constants.WATCH_TYPE_WATCH; +import static com.alibaba.nacos.api.model.v2.ErrorCode.FUZZY_WATCH_PATTERN_MATCH_COUNT_OVER_LIMIT; /** * Fuzzy watch service request handler. @@ -56,15 +57,23 @@ public NamingFuzzyWatchResponse handle(NamingFuzzyWatchRequest request, RequestM case WATCH_TYPE_WATCH: try { namingFuzzyWatchContextService.syncFuzzyWatcherContext(groupKeyPattern, meta.getConnectionId()); + NotifyCenter.publishEvent( + new ClientOperationEvent.ClientFuzzyWatchEvent(groupKeyPattern, meta.getConnectionId(), + request.getReceivedGroupKeys(), request.isInitializing())); } catch (NacosException nacosException) { NamingFuzzyWatchResponse namingFuzzyWatchResponse = new NamingFuzzyWatchResponse(); - namingFuzzyWatchResponse.setErrorCode(nacosException.getErrCode()); - namingFuzzyWatchResponse.setMessage(nacosException.getErrMsg()); + namingFuzzyWatchResponse.setErrorInfo(nacosException.getErrCode(), nacosException.getErrMsg()); return namingFuzzyWatchResponse; } - NotifyCenter.publishEvent( - new ClientOperationEvent.ClientFuzzyWatchEvent(groupKeyPattern, meta.getConnectionId(), - request.getReceivedGroupKeys(), request.isInitializing())); + + boolean reachToUpLimit = namingFuzzyWatchContextService.reachToUpLimit(groupKeyPattern); + if (reachToUpLimit) { + NamingFuzzyWatchResponse namingFuzzyWatchResponse = new NamingFuzzyWatchResponse(); + namingFuzzyWatchResponse.setErrorInfo(FUZZY_WATCH_PATTERN_MATCH_COUNT_OVER_LIMIT.getCode(), + FUZZY_WATCH_PATTERN_MATCH_COUNT_OVER_LIMIT.getMsg()); + return namingFuzzyWatchResponse; + } + return NamingFuzzyWatchResponse.buildSuccessResponse(); case WATCH_TYPE_CANCEL_WATCH: namingFuzzyWatchContextService.removeFuzzyWatchContext(groupKeyPattern, meta.getConnectionId()); From afd07bb7b331e9b2620e92cac90c2b3c68d00bf2 Mon Sep 17 00:00:00 2001 From: "zunfei.lzf" Date: Wed, 15 Jan 2025 17:21:50 +0800 Subject: [PATCH 4/8] pattern match over load optimize test case --- .../NamingFuzzyWatchChangeNotifyRequest.java | 6 +- .../client/config/impl/ClientWorker.java | 21 +- .../config/impl/ConfigFuzzyWatchContext.java | 53 ++- .../impl/ConfigFuzzyWatchGroupKeyHolder.java | 192 ++++----- .../impl/ConfigFuzzyWatcherWrapper.java | 4 +- .../config/impl/ConfigTransportClient.java | 12 +- .../cache/FuzzyWatchEventWatcherWrapper.java | 31 +- .../naming/cache/NamingFuzzyWatchContext.java | 57 ++- .../NamingFuzzyWatchServiceListHolder.java | 34 +- .../client/config/impl/ClientWorkerTest.java | 2 +- .../ConfigFuzzyWatchGroupKeyHolderTest.java | 383 ++++++++++++++++++ ...NamingFuzzyWatchServiceListHolderTest.java | 354 ++++++++++++++++ .../ConfigCachePostProcessorDelegate.java | 4 +- .../ConfigFuzzyWatchChangeNotifier.java | 128 +----- .../remote/ConfigFuzzyWatchSyncNotifier.java | 207 +--------- .../remote/ConfigQueryRequestHandler.java | 2 - .../remote/FuzzyWatchChangeNotifyTask.java | 126 ++++++ .../remote/FuzzyWatchSyncNotifyCallback.java | 93 +++++ .../remote/FuzzyWatchSyncNotifyTask.java | 127 ++++++ .../ConfigFuzzyWatchContextService.java | 2 +- .../handler/ConfigChainEntryHandler.java | 8 +- .../ConfigCachePostProcessorDelegateTest.java | 4 - .../ConfigFuzzyWatchChangeNotifierTest.java | 128 ++++++ .../ConfigFuzzyWatchSyncNotifierTest.java | 141 +++++++ .../remote/ConfigQueryRequestHandlerTest.java | 8 +- .../FuzzyWatchSyncNotifyCallbackTest.java | 115 ++++++ .../ConfigFuzzyWatchContextServiceTest.java | 89 +++- .../service/dump/DumpProcessorTest.java | 11 +- .../dump/DumpProcessorUserRwaDiskTest.java | 2 +- .../utils/Md5ComparatorDelegateTest.java | 4 - .../FuzzyWatchChangeNotifyExecuteTask.java | 2 +- 31 files changed, 1819 insertions(+), 531 deletions(-) create mode 100644 client/src/test/java/com/alibaba/nacos/client/config/impl/ConfigFuzzyWatchGroupKeyHolderTest.java create mode 100644 client/src/test/java/com/alibaba/nacos/client/naming/cache/NamingFuzzyWatchServiceListHolderTest.java create mode 100644 config/src/main/java/com/alibaba/nacos/config/server/remote/FuzzyWatchChangeNotifyTask.java create mode 100644 config/src/main/java/com/alibaba/nacos/config/server/remote/FuzzyWatchSyncNotifyCallback.java create mode 100644 config/src/main/java/com/alibaba/nacos/config/server/remote/FuzzyWatchSyncNotifyTask.java create mode 100644 config/src/test/java/com/alibaba/nacos/config/server/remote/ConfigFuzzyWatchChangeNotifierTest.java create mode 100644 config/src/test/java/com/alibaba/nacos/config/server/remote/ConfigFuzzyWatchSyncNotifierTest.java create mode 100644 config/src/test/java/com/alibaba/nacos/config/server/remote/FuzzyWatchSyncNotifyCallbackTest.java diff --git a/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/NamingFuzzyWatchChangeNotifyRequest.java b/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/NamingFuzzyWatchChangeNotifyRequest.java index 3abade19bc2..0ad6b7f34f6 100644 --- a/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/NamingFuzzyWatchChangeNotifyRequest.java +++ b/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/NamingFuzzyWatchChangeNotifyRequest.java @@ -16,6 +16,8 @@ package com.alibaba.nacos.api.naming.remote.request; +import static com.alibaba.nacos.api.common.Constants.FUZZY_WATCH_RESOURCE_CHANGED; + /** * Nacos fuzzy watch notify service change request, use it when one of the services changes. * @@ -31,8 +33,8 @@ public NamingFuzzyWatchChangeNotifyRequest() { } - public NamingFuzzyWatchChangeNotifyRequest(String serviceKey, String changedType, String syncType) { - super(syncType); + public NamingFuzzyWatchChangeNotifyRequest(String serviceKey, String changedType) { + super(FUZZY_WATCH_RESOURCE_CHANGED); this.serviceKey = serviceKey; this.changedType = changedType; } diff --git a/client/src/main/java/com/alibaba/nacos/client/config/impl/ClientWorker.java b/client/src/main/java/com/alibaba/nacos/client/config/impl/ClientWorker.java index fe04c17623e..a954daee303 100644 --- a/client/src/main/java/com/alibaba/nacos/client/config/impl/ClientWorker.java +++ b/client/src/main/java/com/alibaba/nacos/client/config/impl/ClientWorker.java @@ -134,20 +134,16 @@ public class ClientWorker implements Closeable { private ConfigFuzzyWatchGroupKeyHolder configFuzzyWatchGroupKeyHolder; - private Map appLables = new HashMap<>(); + private Map appLabels = new HashMap<>(); private final ConfigFilterChainManager configFilterChainManager; private final String uuid = UUID.randomUUID().toString(); - private long timeout; - private long requestTimeout; private final ConfigRpcTransportClient agent; - private int taskPenaltyTime; - private boolean enableRemoteSyncConfig = false; private static final int MIN_THREAD_NUM = 2; @@ -528,7 +524,7 @@ public ClientWorker(final ConfigFilterChainManager configFilterChainManager, } void initAppLabels(Properties properties) { - this.appLables = ConnLabelsUtils.addPrefixForEachKey(defaultLabelsCollectorManager.getLabels(properties), + this.appLabels = ConnLabelsUtils.addPrefixForEachKey(defaultLabelsCollectorManager.getLabels(properties), APP_CONN_PREFIX); } @@ -546,12 +542,6 @@ private void init(NacosClientProperties properties) { requestTimeout = ConvertUtils.toLong(properties.getProperty(PropertyKeyConst.CONFIG_REQUEST_TIMEOUT, "-1")); - timeout = Math.max(ConvertUtils.toInt(properties.getProperty(PropertyKeyConst.CONFIG_LONG_POLL_TIMEOUT), - Constants.CONFIG_LONG_POLL_TIMEOUT), Constants.MIN_CONFIG_LONG_POLL_TIMEOUT); - - taskPenaltyTime = ConvertUtils.toInt(properties.getProperty(PropertyKeyConst.CONFIG_RETRY_TIME), - Constants.CONFIG_RETRY_TIME); - this.enableRemoteSyncConfig = Boolean.parseBoolean( properties.getProperty(PropertyKeyConst.ENABLE_REMOTE_SYNC_CONFIG)); initAppLabels(properties.getProperties(SourceType.PROPERTIES)); @@ -663,8 +653,8 @@ public void shutdown() throws NacosException { } } - LOGGER.info("Shutdown executor {}", executor); - executor.shutdown(); + LOGGER.info("Shutdown executor {}", agent.getExecutor()); + agent.getExecutor().shutdown(); Map stringCacheDataMap = cacheMap.get(); for (Map.Entry entry : stringCacheDataMap.entrySet()) { entry.getValue().setConsistentWithServer(false); @@ -692,7 +682,7 @@ private Map getLabels() { labels.put(Constants.LOCATION_TAG, EnvUtil.getSelfLocationTag()); } - labels.putAll(appLables); + labels.putAll(appLabels); return labels; } @@ -814,6 +804,7 @@ public Class subscribeType() { @Override public void startInternal() { + ScheduledExecutorService executor = getExecutor(); executor.schedule(() -> { while (!executor.isShutdown() && !executor.isTerminated()) { try { diff --git a/client/src/main/java/com/alibaba/nacos/client/config/impl/ConfigFuzzyWatchContext.java b/client/src/main/java/com/alibaba/nacos/client/config/impl/ConfigFuzzyWatchContext.java index e06dc9bd1cb..877317ac6a4 100644 --- a/client/src/main/java/com/alibaba/nacos/client/config/impl/ConfigFuzzyWatchContext.java +++ b/client/src/main/java/com/alibaba/nacos/client/config/impl/ConfigFuzzyWatchContext.java @@ -21,6 +21,7 @@ import com.alibaba.nacos.api.config.listener.FuzzyWatchLoadWatcher; import com.alibaba.nacos.client.config.common.GroupKey; import com.alibaba.nacos.client.utils.LogUtils; +import com.alibaba.nacos.common.utils.CollectionUtils; import com.alibaba.nacos.common.utils.ConcurrentHashSet; import com.alibaba.nacos.common.utils.FuzzyGroupKeyPattern; import com.alibaba.nacos.common.utils.StringUtils; @@ -78,6 +79,8 @@ public class ConfigFuzzyWatchContext { */ private Set receivedGroupKeys = new ConcurrentHashSet<>(); + long syncVersion = 0; + /** * Flag indicating whether the context is consistent with the server. */ @@ -172,6 +175,7 @@ private void doNotifyWatchers(final String groupKey, final String changedType, f /** * notify loader watcher. + * * @param code over limit code,FUZZY_WATCH_PATTERN_MATCH_COUNT_OVER_LIMIT or FUZZY_WATCH_PATTERN_OVER_LIMIT. */ public void notifyLoaderWatcher(int code) { @@ -381,6 +385,10 @@ public boolean isInitializing() { return !initializationCompleted.get(); } + public int getReceivedGroupKeysCount() { + return receivedGroupKeys.size(); + } + /** * Get the set of data IDs associated with the context. zw * @@ -390,12 +398,34 @@ public Set getReceivedGroupKeys() { return Collections.unmodifiableSet(receivedGroupKeys); } + public void refreshSyncVersion() { + this.syncVersion = System.currentTimeMillis(); + } + + /** + * add receive group key. + * @param groupKey group key. + * @return + */ public boolean addReceivedGroupKey(String groupKey) { - return receivedGroupKeys.add(groupKey); + boolean added = receivedGroupKeys.add(groupKey); + if (added) { + refreshSyncVersion(); + } + return added; } + /** + * remove receive group key. + * @param groupKey group key. + * @return + */ public boolean removeReceivedGroupKey(String groupKey) { - return receivedGroupKeys.remove(groupKey); + boolean removed = receivedGroupKeys.remove(groupKey); + if (removed) { + refreshSyncVersion(); + } + return removed; } /** @@ -429,14 +459,23 @@ public boolean isAsync() { void syncFuzzyWatchers() { for (ConfigFuzzyWatcherWrapper configFuzzyWatcher : configFuzzyWatcherWrappers) { - Set receivedGroupKeysContext = receivedGroupKeys; + + if (configFuzzyWatcher.syncVersion == this.syncVersion) { + continue; + } + + Set receivedGroupKeysContext = new HashSet<>(getReceivedGroupKeys()); Set syncGroupKeys = configFuzzyWatcher.getSyncGroupKeys(); List groupKeyStates = FuzzyGroupKeyPattern.diffGroupKeys( receivedGroupKeysContext, syncGroupKeys); - for (FuzzyGroupKeyPattern.GroupKeyState groupKeyState : groupKeyStates) { - String changedType = groupKeyState.isExist() ? ADD_CONFIG : DELETE_CONFIG; - doNotifyWatcher(groupKeyState.getGroupKey(), changedType, FUZZY_WATCH_DIFF_SYNC_NOTIFY, - configFuzzyWatcher); + if (CollectionUtils.isEmpty(groupKeyStates)) { + configFuzzyWatcher.syncVersion = this.syncVersion; + } else { + for (FuzzyGroupKeyPattern.GroupKeyState groupKeyState : groupKeyStates) { + String changedType = groupKeyState.isExist() ? ADD_CONFIG : DELETE_CONFIG; + doNotifyWatcher(groupKeyState.getGroupKey(), changedType, FUZZY_WATCH_DIFF_SYNC_NOTIFY, + configFuzzyWatcher); + } } } diff --git a/client/src/main/java/com/alibaba/nacos/client/config/impl/ConfigFuzzyWatchGroupKeyHolder.java b/client/src/main/java/com/alibaba/nacos/client/config/impl/ConfigFuzzyWatchGroupKeyHolder.java index 301693703e5..090509162ec 100644 --- a/client/src/main/java/com/alibaba/nacos/client/config/impl/ConfigFuzzyWatchGroupKeyHolder.java +++ b/client/src/main/java/com/alibaba/nacos/client/config/impl/ConfigFuzzyWatchGroupKeyHolder.java @@ -29,7 +29,7 @@ import com.alibaba.nacos.client.utils.LogUtils; import com.alibaba.nacos.common.notify.Event; import com.alibaba.nacos.common.notify.NotifyCenter; -import com.alibaba.nacos.common.notify.listener.Subscriber; +import com.alibaba.nacos.common.notify.listener.SmartSubscriber; import com.alibaba.nacos.common.remote.client.RpcClient; import com.alibaba.nacos.common.utils.CollectionUtils; import com.alibaba.nacos.common.utils.FuzzyGroupKeyPattern; @@ -38,6 +38,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; +import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; @@ -45,6 +46,7 @@ import java.util.concurrent.BlockingQueue; import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; +import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicReference; @@ -64,7 +66,7 @@ * * @author shiyiyue */ -public class ConfigFuzzyWatchGroupKeyHolder { +public class ConfigFuzzyWatchGroupKeyHolder extends SmartSubscriber { private static final Logger LOGGER = LogUtils.logger(ClientWorker.class); @@ -94,80 +96,33 @@ public class ConfigFuzzyWatchGroupKeyHolder { public ConfigFuzzyWatchGroupKeyHolder(ClientWorker.ConfigRpcTransportClient agent, String clientUuid) { this.clientUuid = clientUuid; this.agent = agent; - NotifyCenter.registerSubscriber(new Subscriber() { - @Override - public void onEvent(ConfigFuzzyWatchNotifyEvent configFuzzyWatchNotifyEvent) { - - //instance check - if (!configFuzzyWatchNotifyEvent.getClientUuid().equals(clientUuid)) { - return; - } - - ConfigFuzzyWatchContext context = fuzzyListenContextMap.get() - .get(configFuzzyWatchNotifyEvent.getGroupKeyPattern()); - if (context == null) { - return; - } - - context.notifyWatcher(configFuzzyWatchNotifyEvent.getGroupKey(), - configFuzzyWatchNotifyEvent.getChangedType(), configFuzzyWatchNotifyEvent.getSyncType(), - configFuzzyWatchNotifyEvent.getWatcherUuid()); - } - - @Override - public Class subscribeType() { - return ConfigFuzzyWatchNotifyEvent.class; - } - }); - - NotifyCenter.registerSubscriber(new Subscriber() { - @Override - public void onEvent(ConfigFuzzyWatchLoadEvent event) { - - //instance check - if (!event.getClientUuid().equals(clientUuid)) { - return; - } - - ConfigFuzzyWatchContext context = fuzzyListenContextMap.get().get(event.getGroupKeyPattern()); - if (context == null) { - return; - } - - context.notifyLoaderWatcher(event.getCode()); - } - - @Override - public Class subscribeType() { - return ConfigFuzzyWatchLoadEvent.class; - } - }); - + NotifyCenter.registerSubscriber(this); } /** * start. */ public void start() { - agent.executor.schedule(() -> { - while (!agent.executor.isShutdown() && !agent.executor.isTerminated()) { + ScheduledExecutorService agentExecutor = agent.getExecutor(); + agentExecutor.submit(() -> { + while (!agentExecutor.isShutdown() && !agentExecutor.isTerminated()) { try { fuzzyListenExecuteBell.poll(5L, TimeUnit.SECONDS); - if (agent.executor.isShutdown() || agent.executor.isTerminated()) { + if (agentExecutor.isShutdown() || agentExecutor.isTerminated()) { continue; } executeConfigFuzzyListen(); } catch (Throwable e) { LOGGER.error("[rpc-fuzzy-listen-execute] rpc fuzzy listen exception", e); try { - Thread.sleep(50L); + Thread.sleep(500L); } catch (InterruptedException interruptedException) { //ignore } notifyFuzzyWatchSync(); } } - }, 0L, TimeUnit.MILLISECONDS); + }); } /** @@ -235,7 +190,7 @@ ConfigFuzzyWatchSyncResponse handleFuzzyWatchSyncNotifyRequest(ConfigFuzzyWatchS ConfigFuzzyWatchContext context = fuzzyListenContextMap.get().get(groupKeyPattern); if (Constants.FINISH_FUZZY_WATCH_INIT_NOTIFY.equals(request.getSyncType())) { LOGGER.info("[{}] [fuzzy-watch] init-notify-finished, pattern ->{}, match group keys count {}", - agent.getName(), request.getGroupKeyPattern(), context.getReceivedGroupKeys().size()); + agent.getName(), request.getGroupKeyPattern(), context.getReceivedGroupKeysCount()); context.markInitializationComplete(); return new ConfigFuzzyWatchSyncResponse(); } @@ -411,47 +366,11 @@ private void doExecuteConfigFuzzyListen(List contextLis RpcClient rpcClient = agent.ensureRpcClient(taskId); // Iterate through the context map and submit tasks for execution - for (ConfigFuzzyWatchContext entry : contextLists) { - ExecutorService executorService = agent.executor; + for (ConfigFuzzyWatchContext context : contextLists) { + ExecutorService executorService = agent.getExecutor(); // Submit task for execution Future future = executorService.submit(() -> { - ConfigFuzzyWatchRequest configFuzzyWatchRequest = buildFuzzyListenConfigRequest(entry); - try { - // Execute the fuzzy listen operation - ConfigFuzzyWatchResponse listenResponse = (ConfigFuzzyWatchResponse) agent.requestProxy(rpcClient, - configFuzzyWatchRequest); - if (listenResponse != null && listenResponse.isSuccess()) { - - if (entry.isDiscard()) { - removeFuzzyListenContext(entry.getGroupKeyPattern()); - } else { - entry.setConsistentWithServer(true); - } - - entry.clearOverLimitTs(); - } else if (listenResponse != null) { - if (handleOverLoadEvent(entry.getGroupKeyPattern(), listenResponse.getErrorCode())) { - return; - } - LOGGER.error("Execute fuzzy watch config change error,code={},msg={}", - listenResponse.getErrorCode(), listenResponse.getMessage()); - } - - } catch (NacosException e) { - if (handleOverLoadEvent(entry.getGroupKeyPattern(), e.getErrCode())) { - return; - } - // Log error and retry after a short delay - LOGGER.error("Execute fuzzy watch config change error.", e); - try { - Thread.sleep(50L); - } catch (InterruptedException interruptedException) { - // Ignore interruption - } - // Retry notification - notifyFuzzyWatchSync(); - - } + executeFuzzyWatchRequest(context, rpcClient); }); listenFutures.add(future); } @@ -467,6 +386,41 @@ private void doExecuteConfigFuzzyListen(List contextLis } } + void executeFuzzyWatchRequest(ConfigFuzzyWatchContext context, RpcClient rpcClient) { + ConfigFuzzyWatchRequest configFuzzyWatchRequest = buildFuzzyListenConfigRequest(context); + try { + // Execute the fuzzy listen operation + ConfigFuzzyWatchResponse listenResponse = (ConfigFuzzyWatchResponse) agent.requestProxy(rpcClient, + configFuzzyWatchRequest); + if (listenResponse != null && listenResponse.isSuccess()) { + + if (context.isDiscard()) { + removeFuzzyListenContext(context.getGroupKeyPattern()); + } else { + context.setConsistentWithServer(true); + } + + context.clearOverLimitTs(); + } else if (listenResponse != null) { + if (handleOverLoadEvent(context.getGroupKeyPattern(), listenResponse.getErrorCode())) { + return; + } + LOGGER.error("Execute fuzzy watch config change error,code={},msg={}", listenResponse.getErrorCode(), + listenResponse.getMessage()); + } + + } catch (NacosException e) { + if (handleOverLoadEvent(context.getGroupKeyPattern(), e.getErrCode())) { + return; + } + // Log error and retry after a short delay + LOGGER.error("Execute fuzzy watch config change error.", e); + // Retry notification + notifyFuzzyWatchSync(); + + } + } + private boolean handleOverLoadEvent(String pattern, int errorCode) { if (FUZZY_WATCH_PATTERN_OVER_LIMIT.getCode() == errorCode || FUZZY_WATCH_PATTERN_MATCH_COUNT_OVER_LIMIT.getCode() == errorCode) { @@ -527,4 +481,50 @@ private ConfigFuzzyWatchContext initFuzzyWatchContextIfAbsent(String dataIdPatte return context; } + @Override + public List> subscribeTypes() { + List> result = new LinkedList<>(); + result.add(ConfigFuzzyWatchNotifyEvent.class); + result.add(ConfigFuzzyWatchLoadEvent.class); + return result; + } + + @Override + public void onEvent(Event event) { + + if (event instanceof ConfigFuzzyWatchNotifyEvent) { + + ConfigFuzzyWatchNotifyEvent configFuzzyWatchNotifyEvent = (ConfigFuzzyWatchNotifyEvent) event; + if (!configFuzzyWatchNotifyEvent.getClientUuid().equals(clientUuid)) { + return; + } + + ConfigFuzzyWatchContext context = fuzzyListenContextMap.get() + .get(configFuzzyWatchNotifyEvent.getGroupKeyPattern()); + if (context == null) { + return; + } + + context.notifyWatcher(configFuzzyWatchNotifyEvent.getGroupKey(), + configFuzzyWatchNotifyEvent.getChangedType(), configFuzzyWatchNotifyEvent.getSyncType(), + configFuzzyWatchNotifyEvent.getWatcherUuid()); + } + + if (event instanceof ConfigFuzzyWatchLoadEvent) { + ConfigFuzzyWatchLoadEvent loadEvent = (ConfigFuzzyWatchLoadEvent) event; + + //instance check + if (!loadEvent.getClientUuid().equals(clientUuid)) { + return; + } + + ConfigFuzzyWatchContext context = fuzzyListenContextMap.get().get(loadEvent.getGroupKeyPattern()); + if (context == null) { + return; + } + + context.notifyLoaderWatcher(loadEvent.getCode()); + } + + } } diff --git a/client/src/main/java/com/alibaba/nacos/client/config/impl/ConfigFuzzyWatcherWrapper.java b/client/src/main/java/com/alibaba/nacos/client/config/impl/ConfigFuzzyWatcherWrapper.java index 2de22641cce..4a8f53294e2 100644 --- a/client/src/main/java/com/alibaba/nacos/client/config/impl/ConfigFuzzyWatcherWrapper.java +++ b/client/src/main/java/com/alibaba/nacos/client/config/impl/ConfigFuzzyWatcherWrapper.java @@ -29,7 +29,9 @@ * @author shiyiyue */ public class ConfigFuzzyWatcherWrapper { - + + long syncVersion = 0; + FuzzyWatchEventWatcher fuzzyWatchEventWatcher; public ConfigFuzzyWatcherWrapper(FuzzyWatchEventWatcher fuzzyWatchEventWatcher) { diff --git a/client/src/main/java/com/alibaba/nacos/client/config/impl/ConfigTransportClient.java b/client/src/main/java/com/alibaba/nacos/client/config/impl/ConfigTransportClient.java index 1bd5a8e10c5..6f66c8dd246 100644 --- a/client/src/main/java/com/alibaba/nacos/client/config/impl/ConfigTransportClient.java +++ b/client/src/main/java/com/alibaba/nacos/client/config/impl/ConfigTransportClient.java @@ -19,14 +19,14 @@ import com.alibaba.nacos.api.PropertyKeyConst; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.exception.NacosException; -import com.alibaba.nacos.client.env.NacosClientProperties; -import com.alibaba.nacos.plugin.auth.api.RequestResource; import com.alibaba.nacos.client.config.filter.impl.ConfigResponse; +import com.alibaba.nacos.client.env.NacosClientProperties; import com.alibaba.nacos.client.security.SecurityProxy; +import com.alibaba.nacos.client.utils.ParamUtil; import com.alibaba.nacos.common.utils.ConvertUtils; import com.alibaba.nacos.common.utils.MD5Utils; import com.alibaba.nacos.common.utils.StringUtils; -import com.alibaba.nacos.client.utils.ParamUtil; +import com.alibaba.nacos.plugin.auth.api.RequestResource; import java.util.HashMap; import java.util.Map; @@ -51,7 +51,7 @@ public abstract class ConfigTransportClient { String tenant; - ScheduledExecutorService executor; + private ScheduledExecutorService executor; final ConfigServerListManager serverListManager; @@ -126,6 +126,10 @@ public void setExecutor(ScheduledExecutorService executor) { this.executor = executor; } + public ScheduledExecutorService getExecutor() { + return this.executor; + } + /** * base start client. */ diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/cache/FuzzyWatchEventWatcherWrapper.java b/client/src/main/java/com/alibaba/nacos/client/naming/cache/FuzzyWatchEventWatcherWrapper.java index 964bb5c66d2..c50f24f8fd5 100644 --- a/client/src/main/java/com/alibaba/nacos/client/naming/cache/FuzzyWatchEventWatcherWrapper.java +++ b/client/src/main/java/com/alibaba/nacos/client/naming/cache/FuzzyWatchEventWatcherWrapper.java @@ -30,41 +30,16 @@ */ public class FuzzyWatchEventWatcherWrapper { - long patternLimitTs = 0; - - long matchCountLimitTs = 0; - - static final long SUPPRESSED_PERIOD = 60 * 1000L; - - boolean patternLimitNotifySuppressed() { - boolean suppressed = patternLimitTs > 0 && System.currentTimeMillis() - patternLimitTs < SUPPRESSED_PERIOD; - if (!suppressed) { - patternLimitTs = System.currentTimeMillis(); - } - return suppressed; - } - - boolean matchCountLimitNotifySuppressed() { - boolean suppressed = System.currentTimeMillis() - matchCountLimitTs < SUPPRESSED_PERIOD; - if (!suppressed) { - matchCountLimitTs = System.currentTimeMillis(); - } - return suppressed; - } - - public void clearSuppressedTs() { - patternLimitTs = 0; - matchCountLimitTs = 0; - } + long syncVersion = 0; FuzzyWatchEventWatcher fuzzyWatchEventWatcher; + String uuid = UUID.randomUUID().toString(); + public FuzzyWatchEventWatcherWrapper(FuzzyWatchEventWatcher fuzzyWatchEventWatcher) { this.fuzzyWatchEventWatcher = fuzzyWatchEventWatcher; } - String uuid = UUID.randomUUID().toString(); - private Set syncServiceKeys = new HashSet<>(); final String getUuid() { diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/cache/NamingFuzzyWatchContext.java b/client/src/main/java/com/alibaba/nacos/client/naming/cache/NamingFuzzyWatchContext.java index 75bda1a68d3..725cf5a7dfc 100644 --- a/client/src/main/java/com/alibaba/nacos/client/naming/cache/NamingFuzzyWatchContext.java +++ b/client/src/main/java/com/alibaba/nacos/client/naming/cache/NamingFuzzyWatchContext.java @@ -76,6 +76,8 @@ public class NamingFuzzyWatchContext { */ private Set receivedServiceKeys = new ConcurrentHashSet<>(); + private long syncVersion = 0; + /** * Flag indicating whether the context is consistent with the server. */ @@ -112,6 +114,10 @@ public void refreshOverLimitTs() { this.patternLimitTs = System.currentTimeMillis(); } + public void refreshSyncVersion() { + this.syncVersion = System.currentTimeMillis(); + } + /** * Constructor with environment name, data ID pattern, and group. * @@ -199,7 +205,7 @@ public void markInitializationComplete() { * * @param watcher watcher to be removed */ - public void removeWatcher(FuzzyWatchEventWatcher watcher) { + public synchronized void removeWatcher(FuzzyWatchEventWatcher watcher) { Iterator iterator = fuzzyWatchEventWatcherWrappers.iterator(); while (iterator.hasNext()) { FuzzyWatchEventWatcherWrapper next = iterator.next(); @@ -209,7 +215,10 @@ public void removeWatcher(FuzzyWatchEventWatcher watcher) { this.groupKeyPattern, watcher, next.getUuid()); } } - + if (fuzzyWatchEventWatcherWrappers.isEmpty()) { + this.setConsistentWithServer(false); + this.setDiscard(true); + } } /** @@ -283,12 +292,33 @@ public Set getReceivedServiceKeys() { return Collections.unmodifiableSet(receivedServiceKeys); } + /** + * add received service key. + * + * @param serviceKey service key. + * @return + */ public boolean addReceivedServiceKey(String serviceKey) { - return receivedServiceKeys.add(serviceKey); + boolean added = receivedServiceKeys.add(serviceKey); + if (added) { + refreshSyncVersion(); + } + return added; } + /** + * remove received service key. + * + * @param serviceKey service key. + * @return + */ public boolean removeReceivedServiceKey(String serviceKey) { - return receivedServiceKeys.remove(serviceKey); + + boolean removed = receivedServiceKeys.remove(serviceKey); + if (removed) { + refreshSyncVersion(); + } + return removed; } /** @@ -302,14 +332,23 @@ public Set getFuzzyWatchEventWatcherWrappers() { void syncFuzzyWatchers() { for (FuzzyWatchEventWatcherWrapper namingFuzzyWatcher : fuzzyWatchEventWatcherWrappers) { - Set receivedServiceKeysContext = this.getReceivedServiceKeys(); + + if (namingFuzzyWatcher.syncVersion == this.syncVersion) { + continue; + } + + Set receivedServiceKeysContext = new HashSet<>(this.getReceivedServiceKeys()); Set syncGroupKeys = namingFuzzyWatcher.getSyncServiceKeys(); List groupKeyStates = FuzzyGroupKeyPattern.diffGroupKeys( receivedServiceKeysContext, syncGroupKeys); - for (FuzzyGroupKeyPattern.GroupKeyState groupKeyState : groupKeyStates) { - String changedType = groupKeyState.isExist() ? ADD_SERVICE : DELETE_SERVICE; - doNotifyWatcher(groupKeyState.getGroupKey(), changedType, FUZZY_WATCH_DIFF_SYNC_NOTIFY, - namingFuzzyWatcher); + if (CollectionUtils.isEmpty(groupKeyStates)) { + namingFuzzyWatcher.syncVersion = this.syncVersion; + } else { + for (FuzzyGroupKeyPattern.GroupKeyState groupKeyState : groupKeyStates) { + String changedType = groupKeyState.isExist() ? ADD_SERVICE : DELETE_SERVICE; + doNotifyWatcher(groupKeyState.getGroupKey(), changedType, FUZZY_WATCH_DIFF_SYNC_NOTIFY, + namingFuzzyWatcher); + } } } } diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/cache/NamingFuzzyWatchServiceListHolder.java b/client/src/main/java/com/alibaba/nacos/client/naming/cache/NamingFuzzyWatchServiceListHolder.java index d395e62f337..af6811b913a 100644 --- a/client/src/main/java/com/alibaba/nacos/client/naming/cache/NamingFuzzyWatchServiceListHolder.java +++ b/client/src/main/java/com/alibaba/nacos/client/naming/cache/NamingFuzzyWatchServiceListHolder.java @@ -20,8 +20,8 @@ import com.alibaba.nacos.api.naming.listener.FuzzyWatchEventWatcher; import com.alibaba.nacos.api.naming.remote.request.NamingFuzzyWatchRequest; import com.alibaba.nacos.api.naming.remote.response.NamingFuzzyWatchResponse; -import com.alibaba.nacos.client.naming.event.NamingFuzzyWatchNotifyEvent; import com.alibaba.nacos.client.naming.event.NamingFuzzyWatchLoadEvent; +import com.alibaba.nacos.client.naming.event.NamingFuzzyWatchNotifyEvent; import com.alibaba.nacos.client.naming.remote.gprc.NamingGrpcClientProxy; import com.alibaba.nacos.client.utils.LogUtils; import com.alibaba.nacos.common.executor.NameThreadFactory; @@ -35,6 +35,7 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ConcurrentHashMap; @@ -139,20 +140,24 @@ public NamingFuzzyWatchContext getFuzzyWatchContext(String groupKeyPattern) { */ public NamingFuzzyWatchContext registerFuzzyWatcher(String groupKeyPattern, FuzzyWatchEventWatcher watcher) { NamingFuzzyWatchContext namingFuzzyWatchContext = initFuzzyWatchContextIfNeed(groupKeyPattern); - - FuzzyWatchEventWatcherWrapper fuzzyWatchEventWatcherWrapper = new FuzzyWatchEventWatcherWrapper(watcher); - if (namingFuzzyWatchContext.getFuzzyWatchEventWatcherWrappers().add(fuzzyWatchEventWatcherWrapper)) { - LOGGER.info(" [add-watcher-ok] groupKeyPattern={}, watcher={},uuid={} ", groupKeyPattern, watcher, - fuzzyWatchEventWatcherWrapper.getUuid()); - if (CollectionUtils.isNotEmpty(namingFuzzyWatchContext.getReceivedServiceKeys())) { - for (String serviceKey : namingFuzzyWatchContext.getReceivedServiceKeys()) { - NamingFuzzyWatchNotifyEvent namingFuzzyWatchNotifyEvent = NamingFuzzyWatchNotifyEvent.build( - notifierEventScope, groupKeyPattern, serviceKey, ADD_SERVICE, FUZZY_WATCH_INIT_NOTIFY, - fuzzyWatchEventWatcherWrapper.getUuid()); - NotifyCenter.publishEvent(namingFuzzyWatchNotifyEvent); + namingFuzzyWatchContext.setDiscard(false); + synchronized (namingFuzzyWatchContext) { + FuzzyWatchEventWatcherWrapper fuzzyWatchEventWatcherWrapper = new FuzzyWatchEventWatcherWrapper(watcher); + if (namingFuzzyWatchContext.getFuzzyWatchEventWatcherWrappers().add(fuzzyWatchEventWatcherWrapper)) { + LOGGER.info(" [add-watcher-ok] groupKeyPattern={}, watcher={},uuid={} ", groupKeyPattern, watcher, + fuzzyWatchEventWatcherWrapper.getUuid()); + Set receivedServiceKeys = namingFuzzyWatchContext.getReceivedServiceKeys(); + if (CollectionUtils.isNotEmpty(receivedServiceKeys)) { + for (String serviceKey :receivedServiceKeys) { + NamingFuzzyWatchNotifyEvent namingFuzzyWatchNotifyEvent = NamingFuzzyWatchNotifyEvent.build( + notifierEventScope, groupKeyPattern, serviceKey, ADD_SERVICE, FUZZY_WATCH_INIT_NOTIFY, + fuzzyWatchEventWatcherWrapper.getUuid()); + NotifyCenter.publishEvent(namingFuzzyWatchNotifyEvent); + } } } } + return namingFuzzyWatchContext; } @@ -225,9 +230,8 @@ public void executeNamingFuzzyWatch() throws NacosException { if (context.isConsistentWithServer()) { // Skip if a full synchronization is not needed if (!needAllSync) { - continue; - } else { context.syncFuzzyWatchers(); + continue; } } @@ -295,7 +299,7 @@ private void doExecuteNamingFuzzyWatch(List contextList } else { LOGGER.error(" fuzzy watch request fail.", e); - + try { Thread.sleep(1000L); } catch (InterruptedException interruptedException) { diff --git a/client/src/test/java/com/alibaba/nacos/client/config/impl/ClientWorkerTest.java b/client/src/test/java/com/alibaba/nacos/client/config/impl/ClientWorkerTest.java index 5659c2c3684..f01d353092f 100644 --- a/client/src/test/java/com/alibaba/nacos/client/config/impl/ClientWorkerTest.java +++ b/client/src/test/java/com/alibaba/nacos/client/config/impl/ClientWorkerTest.java @@ -486,7 +486,7 @@ void testShutdown() throws NacosException, NoSuchFieldException, IllegalAccessEx Field agent1 = ClientWorker.class.getDeclaredField("agent"); agent1.setAccessible(true); ConfigTransportClient o = (ConfigTransportClient) agent1.get(clientWorker); - assertTrue(o.executor.isShutdown()); + assertTrue(o.getExecutor().isShutdown()); agent1.setAccessible(false); assertNull(clientWorker.getAgentName()); diff --git a/client/src/test/java/com/alibaba/nacos/client/config/impl/ConfigFuzzyWatchGroupKeyHolderTest.java b/client/src/test/java/com/alibaba/nacos/client/config/impl/ConfigFuzzyWatchGroupKeyHolderTest.java new file mode 100644 index 00000000000..ebeba68641f --- /dev/null +++ b/client/src/test/java/com/alibaba/nacos/client/config/impl/ConfigFuzzyWatchGroupKeyHolderTest.java @@ -0,0 +1,383 @@ +/* + * Copyright 1999-2018 Alibaba Group Holding Ltd. + * + * Licensed 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 com.alibaba.nacos.client.config.impl; + +import com.alibaba.nacos.api.config.listener.AbstractFuzzyWatchEventWatcher; +import com.alibaba.nacos.api.config.listener.ConfigFuzzyWatchChangeEvent; +import com.alibaba.nacos.api.config.remote.request.ConfigFuzzyWatchChangeNotifyRequest; +import com.alibaba.nacos.api.config.remote.request.ConfigFuzzyWatchRequest; +import com.alibaba.nacos.api.config.remote.request.ConfigFuzzyWatchSyncRequest; +import com.alibaba.nacos.api.config.remote.response.ConfigFuzzyWatchResponse; +import com.alibaba.nacos.api.exception.NacosException; +import com.alibaba.nacos.client.config.common.GroupKey; +import com.alibaba.nacos.common.remote.client.RpcClient; +import com.alibaba.nacos.common.utils.CollectionUtils; +import com.alibaba.nacos.common.utils.FuzzyGroupKeyPattern; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.ArgumentMatchers; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.HashSet; +import java.util.Set; +import java.util.concurrent.Future; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; + +import static com.alibaba.nacos.api.common.Constants.ConfigChangedType.ADD_CONFIG; +import static com.alibaba.nacos.api.common.Constants.ConfigChangedType.DELETE_CONFIG; +import static com.alibaba.nacos.api.common.Constants.FUZZY_WATCH_DIFF_SYNC_NOTIFY; +import static com.alibaba.nacos.api.common.Constants.FUZZY_WATCH_INIT_NOTIFY; +import static com.alibaba.nacos.api.model.v2.ErrorCode.FUZZY_WATCH_PATTERN_MATCH_COUNT_OVER_LIMIT; +import static com.alibaba.nacos.api.model.v2.ErrorCode.FUZZY_WATCH_PATTERN_OVER_LIMIT; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +public class ConfigFuzzyWatchGroupKeyHolderTest { + + ConfigFuzzyWatchGroupKeyHolder configFuzzyWatchGroupKeyHolder; + + @Mock + ClientWorker.ConfigRpcTransportClient rpcTransportClient; + + String clientId = "conn" + System.currentTimeMillis(); + + String tenant = "t1"; + + @BeforeEach + void before() { + configFuzzyWatchGroupKeyHolder = new ConfigFuzzyWatchGroupKeyHolder(rpcTransportClient, clientId); + } + + @AfterEach + void after() { + + } + + @Test + void testRegisterFuzzyWatcherAndNotify() throws InterruptedException { + when(rpcTransportClient.getTenant()).thenReturn(tenant); + + String dataId = "dataId"; + String group = "group"; + + AtomicInteger watcher1Flag = new AtomicInteger(0); + AtomicInteger watcher2Flag = new AtomicInteger(0); + configFuzzyWatchGroupKeyHolder.registerFuzzyWatcher(dataId + "*", group, new AbstractFuzzyWatchEventWatcher() { + @Override + public void onEvent(ConfigFuzzyWatchChangeEvent event) { + watcher1Flag.incrementAndGet(); + } + }); + + configFuzzyWatchGroupKeyHolder.registerFuzzyWatcher(dataId + "*", group, new AbstractFuzzyWatchEventWatcher() { + @Override + public void onEvent(ConfigFuzzyWatchChangeEvent event) { + watcher2Flag.incrementAndGet(); + } + }); + + String groupKey1 = GroupKey.getKeyTenant(dataId + 1, group, tenant); + //build init notify add + Set contexts = new HashSet<>(); + contexts.add(ConfigFuzzyWatchSyncRequest.Context.build(groupKey1, ADD_CONFIG)); + String groupKeyPattern = FuzzyGroupKeyPattern.generatePattern(dataId + "*", group, tenant); + ConfigFuzzyWatchSyncRequest initNotifyRequest = ConfigFuzzyWatchSyncRequest.buildSyncRequest( + FUZZY_WATCH_INIT_NOTIFY, contexts, groupKeyPattern, 1, 1); + configFuzzyWatchGroupKeyHolder.handleFuzzyWatchSyncNotifyRequest(initNotifyRequest); + //check watcher notified + Thread.sleep(100L); + Assertions.assertTrue(watcher1Flag.get() == 1); + Assertions.assertTrue(watcher1Flag.get() == 1); + + //build change notify add + String changedGroupKey2Add = GroupKey.getKeyTenant(dataId + 2, group, tenant); + ConfigFuzzyWatchChangeNotifyRequest changedNotifyRequest = new ConfigFuzzyWatchChangeNotifyRequest( + changedGroupKey2Add, ADD_CONFIG); + configFuzzyWatchGroupKeyHolder.handlerFuzzyWatchChangeNotifyRequest(changedNotifyRequest); + + //check watcher notified + Thread.sleep(100L); + Assertions.assertTrue(watcher1Flag.get() == 2); + Assertions.assertTrue(watcher1Flag.get() == 2); + + //check not complete future timeout + ConfigFuzzyWatchContext configFuzzyWatchContext = configFuzzyWatchGroupKeyHolder.getFuzzyListenContext( + dataId + "*", group); + Future> newFutureNotFinish = configFuzzyWatchContext.createNewFuture(); + try { + newFutureNotFinish.get(1000L, TimeUnit.MILLISECONDS); + Assertions.assertFalse(true); + } catch (TimeoutException e) { + Assertions.assertTrue(true); + } catch (Throwable throwable) { + Assertions.assertFalse(true); + } + + // build init finish notify + ConfigFuzzyWatchSyncRequest configFuzzyWatchSyncRequest = ConfigFuzzyWatchSyncRequest.buildInitFinishRequest( + groupKeyPattern); + configFuzzyWatchGroupKeyHolder.handleFuzzyWatchSyncNotifyRequest(configFuzzyWatchSyncRequest); + + //check a completed future. + Future> newFutureFinish = configFuzzyWatchContext.createNewFuture(); + try { + Set groupKeys = newFutureFinish.get(10L, TimeUnit.MILLISECONDS); + Assertions.assertTrue( + groupKeys != null && groupKeys.contains(groupKey1) && groupKeys.contains(changedGroupKey2Add)); + } catch (Exception e) { + Assertions.assertTrue(false); + } + + //check watcher notified delete + Thread.sleep(100L); + Assertions.assertTrue(watcher1Flag.get() == 2); + Assertions.assertTrue(watcher1Flag.get() == 2); + + //build change notify + String changedGroupKey2Delete = changedGroupKey2Add; + + ConfigFuzzyWatchChangeNotifyRequest changedNotifyRequestDelete = new ConfigFuzzyWatchChangeNotifyRequest( + changedGroupKey2Delete, DELETE_CONFIG); + configFuzzyWatchGroupKeyHolder.handlerFuzzyWatchChangeNotifyRequest(changedNotifyRequestDelete); + //check watcher notified delete + Thread.sleep(100L); + Assertions.assertTrue(watcher1Flag.get() == 3); + Assertions.assertTrue(watcher1Flag.get() == 3); + + Future> newFuture = configFuzzyWatchContext.createNewFuture(); + + try { + Set groupKeys = newFuture.get(10L, TimeUnit.MILLISECONDS); + Assertions.assertTrue( + groupKeys != null && groupKeys.contains(groupKey1) && !groupKeys.contains(changedGroupKey2Delete)); + } catch (Exception e) { + Assertions.assertTrue(false); + } + + //build sync delete + String groupKey1Delete = groupKey1; + //build init notify add + Set contextsDelete = new HashSet<>(); + contextsDelete.add(ConfigFuzzyWatchSyncRequest.Context.build(groupKey1Delete, DELETE_CONFIG)); + ConfigFuzzyWatchSyncRequest deleteNotifyRequest = ConfigFuzzyWatchSyncRequest.buildSyncRequest( + FUZZY_WATCH_DIFF_SYNC_NOTIFY, contextsDelete, groupKeyPattern, 1, 1); + configFuzzyWatchGroupKeyHolder.handleFuzzyWatchSyncNotifyRequest(deleteNotifyRequest); + + //check watcher notified delete + Thread.sleep(100L); + Assertions.assertTrue(watcher1Flag.get() == 4); + Assertions.assertTrue(watcher1Flag.get() == 4); + + Future> newFutureEmpty = configFuzzyWatchContext.createNewFuture(); + + try { + Set groupKeys = newFutureEmpty.get(10L, TimeUnit.MILLISECONDS); + Assertions.assertTrue(CollectionUtils.isEmpty(groupKeys)); + } catch (Exception e) { + Assertions.assertTrue(false); + } + + configFuzzyWatchGroupKeyHolder.resetConsistenceStatus(); + Assertions.assertFalse(configFuzzyWatchContext.isConsistentWithServer()); + } + + @Test + void testExecuteConfigFuzzyListen() throws NacosException { + when(rpcTransportClient.getTenant()).thenReturn(tenant); + + ConfigFuzzyWatchContext configFuzzyWatchContext = configFuzzyWatchGroupKeyHolder.registerFuzzyWatcher("da1*", + "group*", new AbstractFuzzyWatchEventWatcher() { + @Override + public void onEvent(ConfigFuzzyWatchChangeEvent event) { + + } + }); + configFuzzyWatchContext.setConsistentWithServer(true); + + configFuzzyWatchGroupKeyHolder.registerFuzzyWatcher("da2*", "group*", new AbstractFuzzyWatchEventWatcher() { + @Override + public void onEvent(ConfigFuzzyWatchChangeEvent event) { + + } + }); + + configFuzzyWatchGroupKeyHolder.registerFuzzyWatcher("da3*", "group*", new AbstractFuzzyWatchEventWatcher() { + @Override + public void onEvent(ConfigFuzzyWatchChangeEvent event) { + + } + }); + + RpcClient rpcClient = Mockito.mock(RpcClient.class); + when(rpcTransportClient.ensureRpcClient(eq("0"))).thenReturn(rpcClient); + ScheduledExecutorService scheduledExecutorService = Mockito.mock(ScheduledExecutorService.class); + when(rpcTransportClient.getExecutor()).thenReturn(scheduledExecutorService); + when(scheduledExecutorService.submit(any(Runnable.class))).thenReturn(Mockito.mock(Future.class)); + configFuzzyWatchGroupKeyHolder.executeConfigFuzzyListen(); + + verify(scheduledExecutorService, times(2)).submit(ArgumentMatchers.any(Runnable.class)); + + } + + @Test + void testExecuteFuzzyWatchRequestNormal() throws NacosException { + String envName = "name"; + String groupKeyPattern = "pattern"; + ConfigFuzzyWatchContext configFuzzyWatchContext = new ConfigFuzzyWatchContext(envName, groupKeyPattern); + configFuzzyWatchContext.refreshOverLimitTs(); + RpcClient rpcClient = Mockito.mock(RpcClient.class); + + when(rpcTransportClient.requestProxy(eq(rpcClient), any(ConfigFuzzyWatchRequest.class))).thenReturn( + new ConfigFuzzyWatchResponse()); + configFuzzyWatchGroupKeyHolder.executeFuzzyWatchRequest(configFuzzyWatchContext, rpcClient); + + Assertions.assertTrue(!configFuzzyWatchContext.patternLimitSuppressed()); + Assertions.assertTrue(configFuzzyWatchContext.isConsistentWithServer()); + + } + + @Test + void testExecuteFuzzyWatchRequestRemove() throws NacosException { + + AbstractFuzzyWatchEventWatcher abstractFuzzyWatchEventWatcher = new AbstractFuzzyWatchEventWatcher() { + @Override + public void onEvent(ConfigFuzzyWatchChangeEvent event) { + + } + }; + configFuzzyWatchGroupKeyHolder.registerFuzzyWatcher("*", "*", abstractFuzzyWatchEventWatcher); + configFuzzyWatchGroupKeyHolder.removeFuzzyWatcher("*", "*", abstractFuzzyWatchEventWatcher); + + RpcClient rpcClient = Mockito.mock(RpcClient.class); + when(rpcTransportClient.requestProxy(eq(rpcClient), any(ConfigFuzzyWatchRequest.class))).thenReturn( + new ConfigFuzzyWatchResponse()); + configFuzzyWatchGroupKeyHolder.executeFuzzyWatchRequest( + configFuzzyWatchGroupKeyHolder.getFuzzyListenContext("*", "*"), rpcClient); + + Assertions.assertTrue(configFuzzyWatchGroupKeyHolder.getFuzzyListenContext("*", "*") == null); + + } + + @Test + void testExecuteFuzzyWatchRequestOverLoad() throws NacosException, InterruptedException { + + AtomicBoolean patternOvrFlag = new AtomicBoolean(false); + AtomicBoolean configCountOverFlag = new AtomicBoolean(false); + + AbstractFuzzyWatchEventWatcher abstractFuzzyWatchEventWatcher = new AbstractFuzzyWatchEventWatcher() { + @Override + public void onEvent(ConfigFuzzyWatchChangeEvent event) { + + } + + @Override + public void onPatternOverLimit() { + patternOvrFlag.set(true); + } + + @Override + public void onConfigReachUpLimit() { + configCountOverFlag.set(true); + } + }; + ConfigFuzzyWatchContext configFuzzyWatchContext = configFuzzyWatchGroupKeyHolder.registerFuzzyWatcher("*", "*", + abstractFuzzyWatchEventWatcher); + + RpcClient rpcClient = Mockito.mock(RpcClient.class); + + //test pattern over load + ConfigFuzzyWatchResponse overloadResponse = new ConfigFuzzyWatchResponse(); + overloadResponse.setErrorInfo(FUZZY_WATCH_PATTERN_OVER_LIMIT.getCode(), + FUZZY_WATCH_PATTERN_OVER_LIMIT.getMsg()); + when(rpcTransportClient.requestProxy(eq(rpcClient), any(ConfigFuzzyWatchRequest.class))).thenReturn( + overloadResponse); + configFuzzyWatchGroupKeyHolder.executeFuzzyWatchRequest(configFuzzyWatchContext, rpcClient); + Thread.sleep(100L); + Assertions.assertTrue(configFuzzyWatchContext.patternLimitSuppressed()); + Assertions.assertTrue(!configFuzzyWatchContext.isConsistentWithServer()); + Assertions.assertTrue(patternOvrFlag.get()); + Assertions.assertFalse(configCountOverFlag.get()); + + configFuzzyWatchContext.clearOverLimitTs(); + ConfigFuzzyWatchResponse countOverloadResponse = new ConfigFuzzyWatchResponse(); + countOverloadResponse.setErrorInfo(FUZZY_WATCH_PATTERN_MATCH_COUNT_OVER_LIMIT.getCode(), + FUZZY_WATCH_PATTERN_MATCH_COUNT_OVER_LIMIT.getMsg()); + when(rpcTransportClient.requestProxy(eq(rpcClient), any(ConfigFuzzyWatchRequest.class))).thenReturn( + countOverloadResponse); + configFuzzyWatchGroupKeyHolder.executeFuzzyWatchRequest(configFuzzyWatchContext, rpcClient); + Thread.sleep(100L); + Assertions.assertTrue(configFuzzyWatchContext.patternLimitSuppressed()); + Assertions.assertTrue(!configFuzzyWatchContext.isConsistentWithServer()); + Assertions.assertTrue(configCountOverFlag.get()); + + } + + @Test + void testSyncWhenWatcherFail() throws NacosException { + when(rpcTransportClient.getTenant()).thenReturn(tenant); + + String groupKey = GroupKey.getKeyTenant("dataIdName124", "group", tenant); + + AtomicInteger watcherFlag = new AtomicInteger(0); + + AbstractFuzzyWatchEventWatcher abstractFuzzyWatchEventWatcher = new AbstractFuzzyWatchEventWatcher() { + + @Override + public void onEvent(ConfigFuzzyWatchChangeEvent event) { + int get = watcherFlag.incrementAndGet(); + if (get < 2) { + System.out.println("times " + get + " fail"); + throw new RuntimeException("mock exception"); + } else { + System.out.println("times " + get + " success"); + + } + } + }; + + ConfigFuzzyWatchContext configFuzzyWatchContext = configFuzzyWatchGroupKeyHolder.registerFuzzyWatcher( + "dataIdName*", "group", abstractFuzzyWatchEventWatcher); + + ConfigFuzzyWatchChangeNotifyRequest configFuzzyWatchChangeNotifyRequest = new ConfigFuzzyWatchChangeNotifyRequest( + groupKey, ADD_CONFIG); + configFuzzyWatchGroupKeyHolder.handlerFuzzyWatchChangeNotifyRequest(configFuzzyWatchChangeNotifyRequest); + + //notify 1, fail + configFuzzyWatchContext.syncFuzzyWatchers(); + //notify 2,success + configFuzzyWatchContext.syncFuzzyWatchers(); + //notify 3 +, will not trigger watchers. + configFuzzyWatchContext.syncFuzzyWatchers(); + configFuzzyWatchContext.syncFuzzyWatchers(); + configFuzzyWatchContext.syncFuzzyWatchers(); + //expect 2 times notified + Assertions.assertEquals(2, watcherFlag.get()); + } +} diff --git a/client/src/test/java/com/alibaba/nacos/client/naming/cache/NamingFuzzyWatchServiceListHolderTest.java b/client/src/test/java/com/alibaba/nacos/client/naming/cache/NamingFuzzyWatchServiceListHolderTest.java new file mode 100644 index 00000000000..3f0dc8ac633 --- /dev/null +++ b/client/src/test/java/com/alibaba/nacos/client/naming/cache/NamingFuzzyWatchServiceListHolderTest.java @@ -0,0 +1,354 @@ +/* + * Copyright 1999-2018 Alibaba Group Holding Ltd. + * + * Licensed 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 com.alibaba.nacos.client.naming.cache; + +import com.alibaba.nacos.api.exception.NacosException; +import com.alibaba.nacos.api.naming.listener.AbstractFuzzyWatchEventWatcher; +import com.alibaba.nacos.api.naming.listener.FuzzyWatchChangeEvent; +import com.alibaba.nacos.api.naming.pojo.ListView; +import com.alibaba.nacos.api.naming.remote.request.NamingFuzzyWatchChangeNotifyRequest; +import com.alibaba.nacos.api.naming.remote.request.NamingFuzzyWatchRequest; +import com.alibaba.nacos.api.naming.remote.request.NamingFuzzyWatchSyncRequest; +import com.alibaba.nacos.api.naming.remote.response.NamingFuzzyWatchResponse; +import com.alibaba.nacos.api.naming.utils.NamingUtils; +import com.alibaba.nacos.api.remote.response.Response; +import com.alibaba.nacos.client.naming.event.NamingFuzzyWatchLoadEvent; +import com.alibaba.nacos.client.naming.remote.gprc.NamingFuzzyWatchNotifyRequestHandler; +import com.alibaba.nacos.client.naming.remote.gprc.NamingGrpcClientProxy; +import com.alibaba.nacos.common.remote.client.Connection; +import com.alibaba.nacos.common.utils.FuzzyGroupKeyPattern; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.HashSet; +import java.util.Set; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.atomic.AtomicInteger; + +import static com.alibaba.nacos.api.common.Constants.FINISH_FUZZY_WATCH_INIT_NOTIFY; +import static com.alibaba.nacos.api.common.Constants.FUZZY_WATCH_INIT_NOTIFY; +import static com.alibaba.nacos.api.common.Constants.ServiceChangedType.ADD_SERVICE; +import static com.alibaba.nacos.api.common.Constants.ServiceChangedType.DELETE_SERVICE; +import static com.alibaba.nacos.api.model.v2.ErrorCode.FUZZY_WATCH_PATTERN_MATCH_COUNT_OVER_LIMIT; +import static com.alibaba.nacos.api.model.v2.ErrorCode.FUZZY_WATCH_PATTERN_OVER_LIMIT; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +public class NamingFuzzyWatchServiceListHolderTest { + + NamingFuzzyWatchServiceListHolder namingFuzzyWatchServiceListHolder; + + NamingFuzzyWatchNotifyRequestHandler namingFuzzyWatchNotifyRequestHandler; + + String eventScope = "scope" + System.currentTimeMillis(); + + @Mock + NamingGrpcClientProxy namingGrpcClientProxy; + + @Mock + Connection connection; + + @BeforeEach + void before() { + namingFuzzyWatchServiceListHolder = new NamingFuzzyWatchServiceListHolder(eventScope); + namingFuzzyWatchServiceListHolder.registerNamingGrpcClientProxy(namingGrpcClientProxy); + namingFuzzyWatchNotifyRequestHandler = new NamingFuzzyWatchNotifyRequestHandler( + namingFuzzyWatchServiceListHolder); + } + + @AfterEach + void after() { + + } + + @Test + void testOnEventWatchNotify() throws InterruptedException { + + String serviceKey = NamingUtils.getServiceKey("namespace123", "group", "serviceName124"); + String generatePattern = FuzzyGroupKeyPattern.generatePattern("serviceName124*", "group", "namespace123"); + + AtomicInteger watcherFlag = new AtomicInteger(0); + namingFuzzyWatchServiceListHolder.registerFuzzyWatcher(generatePattern, new AbstractFuzzyWatchEventWatcher() { + @Override + public void onEvent(FuzzyWatchChangeEvent event) { + watcherFlag.incrementAndGet(); + } + }); + + NamingFuzzyWatchChangeNotifyRequest namingFuzzyWatchChangeNotifyRequest = new NamingFuzzyWatchChangeNotifyRequest( + serviceKey, ADD_SERVICE); + Response response = namingFuzzyWatchNotifyRequestHandler.requestReply(namingFuzzyWatchChangeNotifyRequest, + connection); + Assertions.assertNotNull(response); + Thread.sleep(100L); + Assertions.assertTrue(watcherFlag.get() == 1); + + Response duplicatedResponse = namingFuzzyWatchNotifyRequestHandler.requestReply( + namingFuzzyWatchChangeNotifyRequest, connection); + Assertions.assertNotNull(duplicatedResponse); + Thread.sleep(100L); + Assertions.assertTrue(watcherFlag.get() == 1); + + namingFuzzyWatchChangeNotifyRequest.setChangedType(DELETE_SERVICE); + Response deleteResponse = namingFuzzyWatchNotifyRequestHandler.requestReply(namingFuzzyWatchChangeNotifyRequest, + connection); + Assertions.assertNotNull(deleteResponse); + Thread.sleep(100L); + Assertions.assertTrue(watcherFlag.get() == 2); + + } + + @Test + void testOnEventWatchSync() throws InterruptedException { + + String generatePattern = FuzzyGroupKeyPattern.generatePattern("serviceName124*", "group", "namespace123"); + + AtomicInteger watcherFlag = new AtomicInteger(0); + NamingFuzzyWatchContext namingFuzzyWatchContext = namingFuzzyWatchServiceListHolder.registerFuzzyWatcher( + generatePattern, new AbstractFuzzyWatchEventWatcher() { + @Override + public void onEvent(FuzzyWatchChangeEvent event) { + watcherFlag.incrementAndGet(); + } + }); + + String serviceKey1 = NamingUtils.getServiceKey("namespace123", "group", "serviceName124"); + String serviceKey2 = NamingUtils.getServiceKey("namespace123", "group", "serviceName124234"); + + Set contexts = new HashSet<>(); + contexts.add(NamingFuzzyWatchSyncRequest.Context.build(serviceKey1, ADD_SERVICE)); + contexts.add(NamingFuzzyWatchSyncRequest.Context.build(serviceKey2, ADD_SERVICE)); + + //init notify + NamingFuzzyWatchSyncRequest namingFuzzyWatchSyncRequest = new NamingFuzzyWatchSyncRequest(generatePattern, + FUZZY_WATCH_INIT_NOTIFY, contexts); + Response responseInitNotify = namingFuzzyWatchNotifyRequestHandler.requestReply(namingFuzzyWatchSyncRequest, + connection); + Assertions.assertNotNull(responseInitNotify); + Thread.sleep(100L); + Assertions.assertTrue(watcherFlag.get() == 2); + try { + Future> newFuture = namingFuzzyWatchContext.createNewFuture(); + newFuture.get(100L, TimeUnit.MILLISECONDS); + Assertions.assertTrue(false); + } catch (TimeoutException timeoutException) { + Assertions.assertTrue(true); + } catch (ExecutionException e) { + throw new RuntimeException(e); + } + //init finish notify + NamingFuzzyWatchSyncRequest namingFuzzyWatchSyncRequestFinish = new NamingFuzzyWatchSyncRequest(generatePattern, + FINISH_FUZZY_WATCH_INIT_NOTIFY, null); + Response responseInitNotifyFinish = namingFuzzyWatchNotifyRequestHandler.requestReply( + namingFuzzyWatchSyncRequestFinish, connection); + + Assertions.assertNotNull(responseInitNotifyFinish); + Thread.sleep(100L); + Assertions.assertTrue(watcherFlag.get() == 2); + try { + Future> newFuture = namingFuzzyWatchContext.createNewFuture(); + ListView stringListView = newFuture.get(); + Assertions.assertTrue( + stringListView.getData().contains(serviceKey1) && stringListView.getData().contains(serviceKey2)); + + Assertions.assertFalse(newFuture.isCancelled()); + Assertions.assertTrue(newFuture.isDone()); + try { + newFuture.cancel(true); + Assertions.assertTrue(false); + } catch (UnsupportedOperationException unsupportedOperationException) { + Assertions.assertTrue(true); + } + } catch (Exception timeoutException) { + Assertions.assertTrue(false); + } + + namingFuzzyWatchServiceListHolder.registerFuzzyWatcher(generatePattern, new AbstractFuzzyWatchEventWatcher() { + @Override + public void onEvent(FuzzyWatchChangeEvent event) { + watcherFlag.incrementAndGet(); + } + }); + Thread.sleep(100L); + Assertions.assertEquals(4, watcherFlag.get()); + } + + @Test + void testOnEventLoadEvent() throws InterruptedException { + + String generatePattern = FuzzyGroupKeyPattern.generatePattern("serviceName124*", "group", "namespace123"); + + AtomicInteger watcherPatternOverFlag = new AtomicInteger(0); + AtomicInteger watcherServiceOverFlag = new AtomicInteger(0); + + namingFuzzyWatchServiceListHolder.registerFuzzyWatcher(generatePattern, new AbstractFuzzyWatchEventWatcher() { + @Override + public void onEvent(FuzzyWatchChangeEvent event) { + + } + + @Override + public void onPatternOverLimit() { + watcherPatternOverFlag.incrementAndGet(); + } + + @Override + public void onServiceReachUpLimit() { + watcherServiceOverFlag.incrementAndGet(); + } + }); + + NamingFuzzyWatchLoadEvent namingFuzzyWatchLoadEvent = NamingFuzzyWatchLoadEvent.buildEvent( + FUZZY_WATCH_PATTERN_OVER_LIMIT.getCode(), generatePattern, eventScope); + namingFuzzyWatchServiceListHolder.onEvent(namingFuzzyWatchLoadEvent); + Assertions.assertTrue(watcherPatternOverFlag.get() == 1); + Assertions.assertTrue(watcherServiceOverFlag.get() == 0); + + NamingFuzzyWatchLoadEvent namingFuzzyWatchLoadEventDup = NamingFuzzyWatchLoadEvent.buildEvent( + FUZZY_WATCH_PATTERN_MATCH_COUNT_OVER_LIMIT.getCode(), generatePattern, eventScope); + namingFuzzyWatchServiceListHolder.onEvent(namingFuzzyWatchLoadEventDup); + Assertions.assertTrue(watcherPatternOverFlag.get() == 1); + Assertions.assertTrue(watcherServiceOverFlag.get() == 0); + NamingFuzzyWatchContext namingFuzzyWatchContext = namingFuzzyWatchServiceListHolder.getFuzzyWatchContext( + generatePattern); + namingFuzzyWatchContext.clearOverLimitTs(); + NamingFuzzyWatchLoadEvent namingFuzzyWatchLoadEvent2 = NamingFuzzyWatchLoadEvent.buildEvent( + FUZZY_WATCH_PATTERN_MATCH_COUNT_OVER_LIMIT.getCode(), generatePattern, eventScope); + namingFuzzyWatchServiceListHolder.onEvent(namingFuzzyWatchLoadEvent2); + Assertions.assertTrue(watcherPatternOverFlag.get() == 1); + Assertions.assertTrue(watcherServiceOverFlag.get() == 1); + } + + @Test + void testExecuteNamingFuzzyWatch() throws NacosException, InterruptedException { + String generatePattern = FuzzyGroupKeyPattern.generatePattern("serviceName124*", "group", "namespace123"); + + AtomicInteger watcherPatternOverFlag = new AtomicInteger(0); + AtomicInteger watcherServiceOverFlag = new AtomicInteger(0); + + AbstractFuzzyWatchEventWatcher abstractFuzzyWatchEventWatcher = new AbstractFuzzyWatchEventWatcher() { + @Override + public void onEvent(FuzzyWatchChangeEvent event) { + + } + + @Override + public void onPatternOverLimit() { + watcherPatternOverFlag.incrementAndGet(); + } + + @Override + public void onServiceReachUpLimit() { + watcherServiceOverFlag.incrementAndGet(); + } + + }; + + NamingFuzzyWatchContext namingFuzzyWatchContext = namingFuzzyWatchServiceListHolder.registerFuzzyWatcher( + generatePattern, abstractFuzzyWatchEventWatcher); + Assertions.assertFalse(namingFuzzyWatchContext.isConsistentWithServer()); + + //check success fuzzy watch + when(namingGrpcClientProxy.fuzzyWatchRequest(any(NamingFuzzyWatchRequest.class))).thenReturn( + NamingFuzzyWatchResponse.buildSuccessResponse()); + namingFuzzyWatchServiceListHolder.executeNamingFuzzyWatch(); + Assertions.assertTrue(namingFuzzyWatchContext.isConsistentWithServer()); + + //check sync skip + namingFuzzyWatchServiceListHolder.executeNamingFuzzyWatch(); + + namingFuzzyWatchServiceListHolder.resetConsistenceStatus(); + //check over fuzzy watch pattern count + when(namingGrpcClientProxy.fuzzyWatchRequest(any(NamingFuzzyWatchRequest.class))).thenThrow( + new NacosException(FUZZY_WATCH_PATTERN_OVER_LIMIT.getCode(), FUZZY_WATCH_PATTERN_OVER_LIMIT.getMsg())); + namingFuzzyWatchServiceListHolder.executeNamingFuzzyWatch(); + Thread.sleep(100L); + Assertions.assertTrue(watcherPatternOverFlag.get() == 1); + Assertions.assertTrue(watcherServiceOverFlag.get() == 0); + + namingFuzzyWatchContext.clearOverLimitTs(); + //check over fuzzy watch service count + when(namingGrpcClientProxy.fuzzyWatchRequest(any(NamingFuzzyWatchRequest.class))).thenThrow( + new NacosException(FUZZY_WATCH_PATTERN_MATCH_COUNT_OVER_LIMIT.getCode(), + FUZZY_WATCH_PATTERN_MATCH_COUNT_OVER_LIMIT.getMsg())); + namingFuzzyWatchServiceListHolder.executeNamingFuzzyWatch(); + Thread.sleep(100L); + Assertions.assertTrue(watcherPatternOverFlag.get() == 1); + Assertions.assertTrue(watcherServiceOverFlag.get() == 1); + + when(namingGrpcClientProxy.fuzzyWatchRequest(any(NamingFuzzyWatchRequest.class))).thenThrow( + new NacosException(500, "unknow")); + namingFuzzyWatchServiceListHolder.executeNamingFuzzyWatch(); + + //check cancel fuzzy watch + namingFuzzyWatchContext.removeWatcher(abstractFuzzyWatchEventWatcher); + when(namingGrpcClientProxy.fuzzyWatchRequest(any(NamingFuzzyWatchRequest.class))).thenReturn( + NamingFuzzyWatchResponse.buildSuccessResponse()); + namingFuzzyWatchServiceListHolder.executeNamingFuzzyWatch(); + Assertions.assertTrue(namingFuzzyWatchServiceListHolder.getFuzzyWatchContext(generatePattern) == null); + + } + + @Test + void testSyncWhenWatcherFail() throws NacosException { + + String serviceKey = NamingUtils.getServiceKey("namespace123", "group", "serviceName124"); + + String generatePattern = FuzzyGroupKeyPattern.generatePattern("serviceName124*", "group", "namespace123"); + + AtomicInteger watcherFlag = new AtomicInteger(0); + + AbstractFuzzyWatchEventWatcher abstractFuzzyWatchEventWatcher = new AbstractFuzzyWatchEventWatcher() { + @Override + public void onEvent(FuzzyWatchChangeEvent event) { + int get = watcherFlag.incrementAndGet(); + if (get < 2) { + System.out.println("times " + get + " fail"); + throw new RuntimeException("mock exception"); + } else { + System.out.println("times " + get + " success"); + } + } + }; + + NamingFuzzyWatchContext namingFuzzyWatchContext = namingFuzzyWatchServiceListHolder.registerFuzzyWatcher( + generatePattern, abstractFuzzyWatchEventWatcher); + + NamingFuzzyWatchChangeNotifyRequest namingFuzzyWatchChangeNotifyRequest = new NamingFuzzyWatchChangeNotifyRequest( + serviceKey, ADD_SERVICE); + namingFuzzyWatchNotifyRequestHandler.requestReply(namingFuzzyWatchChangeNotifyRequest, connection); + //notify 1, fail + namingFuzzyWatchContext.syncFuzzyWatchers(); + //notify 2,success + namingFuzzyWatchContext.syncFuzzyWatchers(); + //notify 3 +, will not trigger watchers. + namingFuzzyWatchContext.syncFuzzyWatchers(); + namingFuzzyWatchContext.syncFuzzyWatchers(); + namingFuzzyWatchContext.syncFuzzyWatchers(); + //expect 2 times notified + Assertions.assertEquals(2, watcherFlag.get()); + } +} diff --git a/config/src/main/java/com/alibaba/nacos/config/server/model/ConfigCachePostProcessorDelegate.java b/config/src/main/java/com/alibaba/nacos/config/server/model/ConfigCachePostProcessorDelegate.java index 981e5352eb6..70c85c423ee 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/model/ConfigCachePostProcessorDelegate.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/model/ConfigCachePostProcessorDelegate.java @@ -33,7 +33,7 @@ public class ConfigCachePostProcessorDelegate { private static final Logger LOGGER = LoggerFactory.getLogger(ConfigCacheFactoryDelegate.class); - private static final ConfigCachePostProcessorDelegate INSTANCE = new ConfigCachePostProcessorDelegate(); + private static ConfigCachePostProcessorDelegate instance = new ConfigCachePostProcessorDelegate(); private String configCacheMd5PostProcessorType = EnvUtil.getProperty("nacos.config.cache.type", "nacos"); @@ -67,7 +67,7 @@ private ConfigCachePostProcessorDelegate() { } public static ConfigCachePostProcessorDelegate getInstance() { - return INSTANCE; + return instance; } public void postProcess(ConfigCache configCache, String content) { diff --git a/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigFuzzyWatchChangeNotifier.java b/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigFuzzyWatchChangeNotifier.java index 05b9200172c..582e5e62ab9 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigFuzzyWatchChangeNotifier.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigFuzzyWatchChangeNotifier.java @@ -17,7 +17,6 @@ package com.alibaba.nacos.config.server.remote; import com.alibaba.nacos.api.config.remote.request.ConfigFuzzyWatchChangeNotifyRequest; -import com.alibaba.nacos.api.remote.AbstractPushCallBack; import com.alibaba.nacos.common.notify.Event; import com.alibaba.nacos.common.notify.NotifyCenter; import com.alibaba.nacos.common.notify.listener.Subscriber; @@ -25,19 +24,12 @@ import com.alibaba.nacos.config.server.model.event.LocalDataChangeEvent; import com.alibaba.nacos.config.server.service.ConfigCacheService; import com.alibaba.nacos.config.server.service.ConfigFuzzyWatchContextService; -import com.alibaba.nacos.config.server.utils.ConfigExecutor; import com.alibaba.nacos.core.remote.Connection; import com.alibaba.nacos.core.remote.ConnectionManager; -import com.alibaba.nacos.core.remote.ConnectionMeta; import com.alibaba.nacos.core.remote.RpcPushService; import com.alibaba.nacos.core.utils.Loggers; -import com.alibaba.nacos.plugin.control.ControlManagerCenter; -import com.alibaba.nacos.plugin.control.tps.TpsControlManager; -import com.alibaba.nacos.plugin.control.tps.request.TpsCheckRequest; import org.springframework.stereotype.Component; -import java.util.concurrent.TimeUnit; - import static com.alibaba.nacos.api.common.Constants.ConfigChangedType.CONFIG_CHANGED; import static com.alibaba.nacos.api.common.Constants.ConfigChangedType.DELETE_CONFIG; @@ -51,17 +43,9 @@ @Component public class ConfigFuzzyWatchChangeNotifier extends Subscriber { - private static final String POINT_FUZZY_WATCH_CONFIG_PUSH = "POINT_FUZZY_WATCH_CONFIG_PUSH"; - - private static final String POINT_FUZZY_WATCH_CONFIG_PUSH_SUCCESS = "POINT_FUZZY_WATCH_CONFIG_PUSH_SUCCESS"; - - private static final String POINT_FUZZY_WATCH_CONFIG_PUSH_FAIL = "POINT_FUZZY_WATCH_CONFIG_PUSH_FAIL"; - private final ConnectionManager connectionManager; - private final RpcPushService rpcPushService; - - private final TpsControlManager tpsControlManager; + private RpcPushService rpcPushService; private final ConfigFuzzyWatchContextService configFuzzyWatchContextService; @@ -73,9 +57,8 @@ public class ConfigFuzzyWatchChangeNotifier extends Subscriber subscribeType() { return LocalDataChangeEvent.class; } - /** - * Pushes the notification to remote clients. - * - * @param retryTask The task for retrying to push notification. - */ - private void push(RpcPushTask retryTask) { - ConfigFuzzyWatchChangeNotifyRequest notifyRequest = retryTask.notifyRequest; - if (retryTask.isOverTimes()) { - Loggers.REMOTE_PUSH.warn( - "push callback retry fail over times.groupKey={},,clientId={}, will unregister client.", - notifyRequest.getGroupKey(), retryTask.connectionId); - connectionManager.unregister(retryTask.connectionId); - } else if (connectionManager.getConnection(retryTask.connectionId) != null) { - // First time: delay 0s; Second time: delay 2s; Third time: delay 4s - ConfigExecutor.getClientConfigNotifierServiceExecutor() - .schedule(retryTask, retryTask.tryTimes * 2L, TimeUnit.SECONDS); - } else { - // Client is already offline, ignore the task. - Loggers.REMOTE_PUSH.warn( - "Client is already offline, ignore the task. dataId={},groupKey={},tenant={},clientId={}", - notifyRequest.getGroupKey(), retryTask.connectionId); - } - } - - /** - * Represents a task for pushing notification to remote clients. - */ - class RpcPushTask implements Runnable { - - ConfigFuzzyWatchChangeNotifyRequest notifyRequest; - - int maxRetryTimes; - - int tryTimes = 0; - - String connectionId; - - String clientIp; - - String appName; - - /** - * Constructs a RpcPushTask with the specified parameters. - * - * @param notifyRequest The notification request to be sent. - * @param maxRetryTimes The maximum number of retry times. - * @param connectionId The ID of the connection. - * @param clientIp The IP address of the client. - * @param appName The name of the application. - */ - public RpcPushTask(ConfigFuzzyWatchChangeNotifyRequest notifyRequest, int maxRetryTimes, String connectionId, - String clientIp, String appName) { - this.notifyRequest = notifyRequest; - this.maxRetryTimes = maxRetryTimes; - this.connectionId = connectionId; - this.clientIp = clientIp; - this.appName = appName; - } - - /** - * Checks if the number of retry times exceeds the maximum limit. - * - * @return {@code true} if the number of retry times exceeds the maximum limit; otherwise, {@code false}. - */ - public boolean isOverTimes() { - return maxRetryTimes > 0 && this.tryTimes >= maxRetryTimes; - } - - @Override - public void run() { - tryTimes++; - TpsCheckRequest tpsCheckRequest = new TpsCheckRequest(); - tpsCheckRequest.setPointName(POINT_FUZZY_WATCH_CONFIG_PUSH); - if (!tpsControlManager.check(tpsCheckRequest).isSuccess()) { - push(this); - } else { - long timeout = ConfigCommonConfig.getInstance().getPushTimeout(); - rpcPushService.pushWithCallback(connectionId, notifyRequest, new AbstractPushCallBack(timeout) { - @Override - public void onSuccess() { - TpsCheckRequest tpsCheckRequest = new TpsCheckRequest(); - tpsCheckRequest.setPointName(POINT_FUZZY_WATCH_CONFIG_PUSH_SUCCESS); - tpsControlManager.check(tpsCheckRequest); - } - - @Override - public void onFail(Throwable e) { - TpsCheckRequest tpsCheckRequest = new TpsCheckRequest(); - tpsCheckRequest.setPointName(POINT_FUZZY_WATCH_CONFIG_PUSH_FAIL); - tpsControlManager.check(tpsCheckRequest); - Loggers.REMOTE_PUSH.warn("Push fail, groupKey={}, clientId={}", notifyRequest.getGroupKey(), - connectionId, e); - push(RpcPushTask.this); - } - - }, ConfigExecutor.getClientConfigNotifierServiceExecutor()); - } - } - } } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigFuzzyWatchSyncNotifier.java b/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigFuzzyWatchSyncNotifier.java index e4c9b24c283..adaee1195e4 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigFuzzyWatchSyncNotifier.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigFuzzyWatchSyncNotifier.java @@ -18,7 +18,6 @@ import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.config.remote.request.ConfigFuzzyWatchSyncRequest; -import com.alibaba.nacos.api.remote.AbstractPushCallBack; import com.alibaba.nacos.common.notify.Event; import com.alibaba.nacos.common.notify.NotifyCenter; import com.alibaba.nacos.common.notify.listener.SmartSubscriber; @@ -28,13 +27,8 @@ import com.alibaba.nacos.config.server.configuration.ConfigCommonConfig; import com.alibaba.nacos.config.server.model.event.ConfigFuzzyWatchEvent; import com.alibaba.nacos.config.server.service.ConfigFuzzyWatchContextService; -import com.alibaba.nacos.config.server.utils.ConfigExecutor; import com.alibaba.nacos.core.remote.ConnectionManager; import com.alibaba.nacos.core.remote.RpcPushService; -import com.alibaba.nacos.core.utils.Loggers; -import com.alibaba.nacos.plugin.control.ControlManagerCenter; -import com.alibaba.nacos.plugin.control.tps.TpsControlManager; -import com.alibaba.nacos.plugin.control.tps.request.TpsCheckRequest; import org.springframework.stereotype.Component; import java.util.ArrayList; @@ -42,7 +36,6 @@ import java.util.LinkedList; import java.util.List; import java.util.Set; -import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; @@ -58,16 +51,8 @@ @Component(value = "configFuzzyWatchSyncNotifier") public class ConfigFuzzyWatchSyncNotifier extends SmartSubscriber { - private static final String FUZZY_LISTEN_CONFIG_DIFF_PUSH = "FUZZY_LISTEN_CONFIG_DIFF_PUSH_COUNT"; - - private static final String FUZZY_LISTEN_CONFIG_DIFF_PUSH_SUCCESS = "FUZZY_LISTEN_CONFIG_DIFF_PUSH_SUCCESS"; - - private static final String FUZZY_LISTEN_CONFIG_DIFF_PUSH_FAIL = "FUZZY_LISTEN_CONFIG_DIFF_PUSH_FAIL"; - private final ConnectionManager connectionManager; - private final TpsControlManager tpsControlManager; - private final RpcPushService rpcPushService; private final ConfigFuzzyWatchContextService configFuzzyWatchContextService; @@ -75,38 +60,11 @@ public class ConfigFuzzyWatchSyncNotifier extends SmartSubscriber { public ConfigFuzzyWatchSyncNotifier(ConnectionManager connectionManager, RpcPushService rpcPushService, ConfigFuzzyWatchContextService configFuzzyWatchContextService) { this.connectionManager = connectionManager; - this.tpsControlManager = ControlManagerCenter.getInstance().getTpsControlManager(); this.rpcPushService = rpcPushService; this.configFuzzyWatchContextService = configFuzzyWatchContextService; NotifyCenter.registerSubscriber(this); } - /** - * Pushes the retry task to the client connection manager for retrying the RPC push operation. - * - * @param retryTask The retry task containing the RPC push request - * @param connectionManager The connection manager for managing client connections - */ - static void push(FuzzyWatchRpcPushTask retryTask, ConnectionManager connectionManager) { - ConfigFuzzyWatchSyncRequest notifyRequest = retryTask.notifyRequest; - // Check if the maximum retry times have been reached - if (retryTask.isOverTimes()) { - // If over the maximum retry times, log a warning and unregister the client connection - Loggers.REMOTE_PUSH.warn( - "Push callback retry failed over times. groupKeyPattern={}, clientId={}, will unregister client.", - notifyRequest.getGroupKeyPattern(), retryTask.connectionId); - connectionManager.unregister(retryTask.connectionId); - } else if (connectionManager.getConnection(retryTask.connectionId) != null) { - // Schedule a retry task with an increasing delay based on the number of retries - // First time: delay 0s; second time: delay 2s; third time: delay 4s, and so on - ConfigExecutor.scheduleClientConfigNotifier(retryTask, retryTask.tryTimes * 2L, TimeUnit.SECONDS); - } else { - // If the client is already offline, ignore the task - Loggers.REMOTE_PUSH.warn("Client is already offline, ignore the task. groupKeyPattern={}, clientId={}", - notifyRequest.getGroupKeyPattern(), retryTask.connectionId); - } - } - /** * Handles the ConfigBatchFuzzyListenEvent. This method is responsible for processing batch fuzzy listen events and * pushing corresponding notifications to clients. @@ -131,9 +89,9 @@ public void handleFuzzyWatchEvent(ConfigFuzzyWatchEvent event) { ConfigFuzzyWatchSyncRequest request = ConfigFuzzyWatchSyncRequest.buildInitFinishRequest( event.getGroupKeyPattern()); // Create RPC push task and push the request to the client - FuzzyWatchRpcPushTask fuzzyWatchRpcPushTask = new FuzzyWatchRpcPushTask(request, null, - maxPushRetryTimes, event.getConnectionId()); - push(fuzzyWatchRpcPushTask, connectionManager); + FuzzyWatchSyncNotifyTask fuzzyWatchSyncNotifyTask = new FuzzyWatchSyncNotifyTask(connectionManager, + rpcPushService, request, null, maxPushRetryTimes, event.getConnectionId()); + fuzzyWatchSyncNotifyTask.scheduleSelf(); } } else { @@ -167,9 +125,9 @@ public void handleFuzzyWatchEvent(ConfigFuzzyWatchEvent event) { event.getGroupKeyPattern(), totalBatch, currentBatch); int maxPushRetryTimes = ConfigCommonConfig.getInstance().getMaxPushRetryTimes(); // Create RPC push task and push the request to the client - FuzzyWatchRpcPushTask fuzzyWatchRpcPushTask = new FuzzyWatchRpcPushTask(request, batchTaskCounter, - maxPushRetryTimes, event.getConnectionId()); - push(fuzzyWatchRpcPushTask, connectionManager); + FuzzyWatchSyncNotifyTask fuzzyWatchSyncNotifyTask = new FuzzyWatchSyncNotifyTask(connectionManager, + rpcPushService, request, batchTaskCounter, maxPushRetryTimes, event.getConnectionId()); + fuzzyWatchSyncNotifyTask.scheduleSelf(); currentBatch++; } } @@ -208,157 +166,4 @@ private List> divideConfigStatesIntoBatches(Collection configStat .values()); } - /** - * Represents a task for pushing FuzzyListenNotifyDiffRequest to clients. - */ - class FuzzyWatchRpcPushTask implements Runnable { - - /** - * The FuzzyListenNotifyDiffRequest to be pushed. - */ - ConfigFuzzyWatchSyncRequest notifyRequest; - - /** - * The maximum number of times to retry pushing the request. - */ - int maxRetryTimes; - - /** - * The current number of attempts made to push the request. - */ - int tryTimes = 0; - - /** - * The ID of the connection associated with the client. - */ - String connectionId; - - BatchTaskCounter batchTaskCounter; - - /** - * Constructs a new RpcPushTask with the specified parameters. - * - * @param notifyRequest The FuzzyListenNotifyDiffRequest to be pushed - * @param batchTaskCounter The batchTaskCounter counter for tracking the number of finished push batches - * @param maxRetryTimes The maximum number of times to retry pushing the request - * @param connectionId The ID of the connection associated with the client - */ - public FuzzyWatchRpcPushTask(ConfigFuzzyWatchSyncRequest notifyRequest, BatchTaskCounter batchTaskCounter, - int maxRetryTimes, String connectionId) { - this.notifyRequest = notifyRequest; - this.batchTaskCounter = batchTaskCounter; - this.maxRetryTimes = maxRetryTimes; - this.connectionId = connectionId; - } - - /** - * Checks if the maximum number of retry times has been reached. - * - * @return true if the maximum number of retry times has been reached, otherwise false - */ - public boolean isOverTimes() { - return maxRetryTimes > 0 && this.tryTimes >= maxRetryTimes; - } - - /** - * Executes the task, attempting to push the request to the client. - */ - @Override - public void run() { - tryTimes++; - TpsCheckRequest tpsCheckRequest = new TpsCheckRequest(); - - tpsCheckRequest.setPointName(FUZZY_LISTEN_CONFIG_DIFF_PUSH); - if (!tpsControlManager.check(tpsCheckRequest).isSuccess()) { - push(this, connectionManager); - } else { - rpcPushService.pushWithCallback(connectionId, notifyRequest, - new FuzzyWatchRpcPushCallback(this, tpsControlManager, connectionManager, batchTaskCounter), - ConfigExecutor.getClientConfigNotifierServiceExecutor()); - } - } - } - - /** - * Represents a callback for handling the result of an RPC push operation. - */ - class FuzzyWatchRpcPushCallback extends AbstractPushCallBack { - - /** - * The RpcPushTask associated with the callback. - */ - FuzzyWatchRpcPushTask fuzzyWatchRpcPushTask; - - /** - * The TpsControlManager for checking TPS limits. - */ - TpsControlManager tpsControlManager; - - /** - * The ConnectionManager for managing client connections. - */ - ConnectionManager connectionManager; - - BatchTaskCounter batchTaskCounter; - - /** - * Constructs a new RpcPushCallback with the specified parameters. - * - * @param fuzzyWatchRpcPushTask The RpcPushTask associated with the callback - * @param tpsControlManager The TpsControlManager for checking TPS limits - * @param connectionManager The ConnectionManager for managing client connections - * @param batchTaskCounter The batchTaskCounter counter - */ - public FuzzyWatchRpcPushCallback(FuzzyWatchRpcPushTask fuzzyWatchRpcPushTask, - TpsControlManager tpsControlManager, ConnectionManager connectionManager, - BatchTaskCounter batchTaskCounter) { - super(3000L); - this.fuzzyWatchRpcPushTask = fuzzyWatchRpcPushTask; - this.tpsControlManager = tpsControlManager; - this.connectionManager = connectionManager; - this.batchTaskCounter = batchTaskCounter; - - } - - /** - * Handles the successful completion of the RPC push operation. - */ - @Override - public void onSuccess() { - // Check TPS limits - TpsCheckRequest tpsCheckRequest = new TpsCheckRequest(); - tpsCheckRequest.setPointName(FUZZY_LISTEN_CONFIG_DIFF_PUSH_SUCCESS); - tpsControlManager.check(tpsCheckRequest); - - if (batchTaskCounter != null) { - batchTaskCounter.batchSuccess(fuzzyWatchRpcPushTask.notifyRequest.getCurrentBatch()); - if (batchTaskCounter.batchCompleted() && fuzzyWatchRpcPushTask.notifyRequest.getSyncType() - .equals(FUZZY_WATCH_INIT_NOTIFY)) { - ConfigFuzzyWatchSyncRequest request = ConfigFuzzyWatchSyncRequest.buildInitFinishRequest( - fuzzyWatchRpcPushTask.notifyRequest.getGroupKeyPattern()); - push(new FuzzyWatchRpcPushTask(request, null, 50, fuzzyWatchRpcPushTask.connectionId), - connectionManager); - } - } - } - - - /** - * Handles the failure of the RPC push operation. - * - * @param e The exception thrown during the operation - */ - @Override - public void onFail(Throwable e) { - // Check TPS limits - TpsCheckRequest tpsCheckRequest = new TpsCheckRequest(); - tpsCheckRequest.setPointName(FUZZY_LISTEN_CONFIG_DIFF_PUSH_FAIL); - tpsControlManager.check(tpsCheckRequest); - - // Log the failure and retry the task - Loggers.REMOTE_PUSH.warn("Push fail, groupKeyPattern={}, clientId={}", - fuzzyWatchRpcPushTask.notifyRequest.getGroupKeyPattern(), fuzzyWatchRpcPushTask.connectionId, e); - push(fuzzyWatchRpcPushTask, connectionManager); - } - } } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigQueryRequestHandler.java b/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigQueryRequestHandler.java index 696748b81be..9f86ae93c85 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigQueryRequestHandler.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigQueryRequestHandler.java @@ -22,7 +22,6 @@ import com.alibaba.nacos.api.remote.request.RequestMeta; import com.alibaba.nacos.api.remote.response.ResponseCode; import com.alibaba.nacos.auth.annotation.Secured; -import com.alibaba.nacos.common.utils.NamespaceUtil; import com.alibaba.nacos.config.server.model.ConfigCacheGray; import com.alibaba.nacos.config.server.model.gray.BetaGrayRule; import com.alibaba.nacos.config.server.model.gray.TagGrayRule; @@ -76,7 +75,6 @@ public ConfigQueryResponse handle(ConfigQueryRequest request, RequestMeta meta) String dataId = request.getDataId(); String group = request.getGroup(); String tenant = request.getTenant(); - tenant = NamespaceUtil.processNamespaceParameter(tenant); String groupKey = GroupKey2.getKey(dataId, group, tenant); boolean notify = request.isNotify(); diff --git a/config/src/main/java/com/alibaba/nacos/config/server/remote/FuzzyWatchChangeNotifyTask.java b/config/src/main/java/com/alibaba/nacos/config/server/remote/FuzzyWatchChangeNotifyTask.java new file mode 100644 index 00000000000..6ce16e0a496 --- /dev/null +++ b/config/src/main/java/com/alibaba/nacos/config/server/remote/FuzzyWatchChangeNotifyTask.java @@ -0,0 +1,126 @@ +/* + * Copyright 1999-2023 Alibaba Group Holding Ltd. + * + * Licensed 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 com.alibaba.nacos.config.server.remote; + +import com.alibaba.nacos.api.config.remote.request.ConfigFuzzyWatchChangeNotifyRequest; +import com.alibaba.nacos.api.remote.AbstractPushCallBack; +import com.alibaba.nacos.config.server.configuration.ConfigCommonConfig; +import com.alibaba.nacos.config.server.utils.ConfigExecutor; +import com.alibaba.nacos.core.remote.ConnectionManager; +import com.alibaba.nacos.core.remote.RpcPushService; +import com.alibaba.nacos.core.utils.Loggers; +import com.alibaba.nacos.plugin.control.ControlManagerCenter; +import com.alibaba.nacos.plugin.control.tps.request.TpsCheckRequest; + +import java.util.concurrent.TimeUnit; + +/** + * Represents a task for pushing notification to remote clients. + */ +class FuzzyWatchChangeNotifyTask implements Runnable { + + private static final String POINT_FUZZY_WATCH_CONFIG_PUSH = "POINT_FUZZY_WATCH_CONFIG_PUSH"; + + private static final String POINT_FUZZY_WATCH_CONFIG_PUSH_SUCCESS = "POINT_FUZZY_WATCH_CONFIG_PUSH_SUCCESS"; + + private static final String POINT_FUZZY_WATCH_CONFIG_PUSH_FAIL = "POINT_FUZZY_WATCH_CONFIG_PUSH_FAIL"; + + ConfigFuzzyWatchChangeNotifyRequest notifyRequest; + + final ConnectionManager connectionManager; + + final RpcPushService rpcPushService; + + int maxRetryTimes; + + int tryTimes = 0; + + String connectionId; + + /** + * Constructs a RpcPushTask with the specified parameters. + * + * @param notifyRequest The notification request to be sent. + * @param maxRetryTimes The maximum number of retry times. + * @param connectionId The ID of the connection. + */ + public FuzzyWatchChangeNotifyTask(ConnectionManager connectionManager, RpcPushService rpcPushService, + ConfigFuzzyWatchChangeNotifyRequest notifyRequest, int maxRetryTimes, String connectionId) { + this.connectionManager = connectionManager; + this.rpcPushService = rpcPushService; + this.notifyRequest = notifyRequest; + this.maxRetryTimes = maxRetryTimes; + this.connectionId = connectionId; + } + + /** + * Checks if the number of retry times exceeds the maximum limit. + * + * @return {@code true} if the number of retry times exceeds the maximum limit; otherwise, {@code false}. + */ + public boolean isOverTimes() { + return maxRetryTimes > 0 && this.tryTimes >= maxRetryTimes; + } + + @Override + public void run() { + + if (isOverTimes()) { + Loggers.REMOTE_PUSH.warn( + "push callback retry fail over times.groupKey={},,clientId={}, will unregister client.", + notifyRequest.getGroupKey(), connectionId); + connectionManager.unregister(connectionId); + } else if (connectionManager.getConnection(connectionId) == null) { + // Client is already offline, ignore the task. + Loggers.REMOTE_PUSH.warn( + "Client is already offline, ignore the task. dataId={},groupKey={},tenant={},clientId={}", + notifyRequest.getGroupKey(), connectionId); + return; + } + + TpsCheckRequest tpsCheckRequest = new TpsCheckRequest(); + tpsCheckRequest.setPointName(POINT_FUZZY_WATCH_CONFIG_PUSH); + if (!ControlManagerCenter.getInstance().getTpsControlManager().check(tpsCheckRequest).isSuccess()) { + scheduleSelf(); + } else { + long timeout = ConfigCommonConfig.getInstance().getPushTimeout(); + rpcPushService.pushWithCallback(connectionId, notifyRequest, new AbstractPushCallBack(timeout) { + @Override + public void onSuccess() { + TpsCheckRequest tpsCheckRequest = new TpsCheckRequest(); + tpsCheckRequest.setPointName(POINT_FUZZY_WATCH_CONFIG_PUSH_SUCCESS); + ControlManagerCenter.getInstance().getTpsControlManager().check(tpsCheckRequest); + } + + @Override + public void onFail(Throwable e) { + TpsCheckRequest tpsCheckRequest = new TpsCheckRequest(); + tpsCheckRequest.setPointName(POINT_FUZZY_WATCH_CONFIG_PUSH_FAIL); + ControlManagerCenter.getInstance().getTpsControlManager().check(tpsCheckRequest); + Loggers.REMOTE_PUSH.warn("Push fail, groupKey={}, clientId={}", notifyRequest.getGroupKey(), + connectionId, e); + FuzzyWatchChangeNotifyTask.this.scheduleSelf(); + } + + }, ConfigExecutor.getClientConfigNotifierServiceExecutor()); + } + } + + void scheduleSelf() { + ConfigExecutor.scheduleClientConfigNotifier(this, tryTimes * 2L, TimeUnit.SECONDS); + } +} diff --git a/config/src/main/java/com/alibaba/nacos/config/server/remote/FuzzyWatchSyncNotifyCallback.java b/config/src/main/java/com/alibaba/nacos/config/server/remote/FuzzyWatchSyncNotifyCallback.java new file mode 100644 index 00000000000..15e5ab17495 --- /dev/null +++ b/config/src/main/java/com/alibaba/nacos/config/server/remote/FuzzyWatchSyncNotifyCallback.java @@ -0,0 +1,93 @@ +/* + * Copyright 1999-2023 Alibaba Group Holding Ltd. + * + * Licensed 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 com.alibaba.nacos.config.server.remote; + +import com.alibaba.nacos.api.config.remote.request.ConfigFuzzyWatchSyncRequest; +import com.alibaba.nacos.api.remote.AbstractPushCallBack; +import com.alibaba.nacos.core.utils.Loggers; +import com.alibaba.nacos.plugin.control.ControlManagerCenter; +import com.alibaba.nacos.plugin.control.tps.request.TpsCheckRequest; + +import static com.alibaba.nacos.api.common.Constants.FUZZY_WATCH_INIT_NOTIFY; + +/** + * Represents a callback for handling the result of an RPC push operation. + * + * @author stone-98 + */ +class FuzzyWatchSyncNotifyCallback extends AbstractPushCallBack { + + /** + * The RpcPushTask associated with the callback. + */ + FuzzyWatchSyncNotifyTask fuzzyWatchSyncNotifyTask; + + /** + * Constructs a new RpcPushCallback with the specified parameters. + * + * @param fuzzyWatchSyncNotifyTask The RpcPushTask associated with the callback + */ + public FuzzyWatchSyncNotifyCallback(FuzzyWatchSyncNotifyTask fuzzyWatchSyncNotifyTask) { + super(3000L); + this.fuzzyWatchSyncNotifyTask = fuzzyWatchSyncNotifyTask; + } + + /** + * Handles the successful completion of the RPC push operation. + */ + @Override + public void onSuccess() { + // Check TPS limits + TpsCheckRequest tpsCheckRequest = new TpsCheckRequest(); + tpsCheckRequest.setPointName(FuzzyWatchSyncNotifyTask.CONFIG_FUZZY_WATCH_CONFIG_SYNC_SUCCESS); + ControlManagerCenter.getInstance().getTpsControlManager().check(tpsCheckRequest); + + if (fuzzyWatchSyncNotifyTask.batchTaskCounter != null) { + fuzzyWatchSyncNotifyTask.batchTaskCounter.batchSuccess( + fuzzyWatchSyncNotifyTask.notifyRequest.getCurrentBatch()); + if (fuzzyWatchSyncNotifyTask.batchTaskCounter.batchCompleted() + && fuzzyWatchSyncNotifyTask.notifyRequest.getSyncType().equals(FUZZY_WATCH_INIT_NOTIFY)) { + ConfigFuzzyWatchSyncRequest request = ConfigFuzzyWatchSyncRequest.buildInitFinishRequest( + fuzzyWatchSyncNotifyTask.notifyRequest.getGroupKeyPattern()); + + // Create RPC push task and push the request to the client + FuzzyWatchSyncNotifyTask fuzzyWatchSyncNotifyTaskFinish = new FuzzyWatchSyncNotifyTask( + fuzzyWatchSyncNotifyTask.connectionManager, fuzzyWatchSyncNotifyTask.rpcPushService, request, + null, fuzzyWatchSyncNotifyTask.maxRetryTimes, fuzzyWatchSyncNotifyTask.connectionId); + fuzzyWatchSyncNotifyTaskFinish.scheduleSelf(); + } + } + } + + /** + * Handles the failure of the RPC push operation. + * + * @param e The exception thrown during the operation + */ + @Override + public void onFail(Throwable e) { + // Check TPS limits + TpsCheckRequest tpsCheckRequest = new TpsCheckRequest(); + tpsCheckRequest.setPointName(FuzzyWatchSyncNotifyTask.CONFIG_FUZZY_WATCH_CONFIG_SYNC_FAIL); + ControlManagerCenter.getInstance().getTpsControlManager().check(tpsCheckRequest); + + // Log the failure and retry the task + Loggers.REMOTE_PUSH.warn("Push fail, groupKeyPattern={}, clientId={}", + fuzzyWatchSyncNotifyTask.notifyRequest.getGroupKeyPattern(), fuzzyWatchSyncNotifyTask.connectionId, e); + fuzzyWatchSyncNotifyTask.scheduleSelf(); + } +} diff --git a/config/src/main/java/com/alibaba/nacos/config/server/remote/FuzzyWatchSyncNotifyTask.java b/config/src/main/java/com/alibaba/nacos/config/server/remote/FuzzyWatchSyncNotifyTask.java new file mode 100644 index 00000000000..8cdfbbbded1 --- /dev/null +++ b/config/src/main/java/com/alibaba/nacos/config/server/remote/FuzzyWatchSyncNotifyTask.java @@ -0,0 +1,127 @@ +/* + * Copyright 1999-2023 Alibaba Group Holding Ltd. + * + * Licensed 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 com.alibaba.nacos.config.server.remote; + +import com.alibaba.nacos.api.config.remote.request.ConfigFuzzyWatchSyncRequest; +import com.alibaba.nacos.common.task.BatchTaskCounter; +import com.alibaba.nacos.config.server.utils.ConfigExecutor; +import com.alibaba.nacos.core.remote.ConnectionManager; +import com.alibaba.nacos.core.remote.RpcPushService; +import com.alibaba.nacos.core.utils.Loggers; +import com.alibaba.nacos.plugin.control.ControlManagerCenter; +import com.alibaba.nacos.plugin.control.tps.request.TpsCheckRequest; + +import java.util.concurrent.TimeUnit; + +/** + * Represents a task for pushing FuzzyListenNotifyDiffRequest to clients. + * + * @author stone-98 + */ +class FuzzyWatchSyncNotifyTask implements Runnable { + + static final String CONFIG_FUZZY_WATCH_CONFIG_SYNC = "FUZZY_WATCH_CONFIG_SYNC_PUSH"; + + static final String CONFIG_FUZZY_WATCH_CONFIG_SYNC_SUCCESS = "CONFIG_FUZZY_WATCH_CONFIG_SYNC_SUCCESS"; + + static final String CONFIG_FUZZY_WATCH_CONFIG_SYNC_FAIL = "CONFIG_FUZZY_WATCH_CONFIG_SYNC_FAIL"; + + final ConnectionManager connectionManager; + + final RpcPushService rpcPushService; + + /** + * The FuzzyListenNotifyDiffRequest to be pushed. + */ + ConfigFuzzyWatchSyncRequest notifyRequest; + + /** + * The maximum number of times to retry pushing the request. + */ + int maxRetryTimes; + + /** + * The current number of attempts made to push the request. + */ + int tryTimes = 0; + + /** + * The ID of the connection associated with the client. + */ + String connectionId; + + BatchTaskCounter batchTaskCounter; + + /** + * Constructs a new RpcPushTask with the specified parameters. + * + * @param notifyRequest The FuzzyListenNotifyDiffRequest to be pushed + * @param batchTaskCounter The batchTaskCounter counter for tracking the number of finished push batches + * @param maxRetryTimes The maximum number of times to retry pushing the request + * @param connectionId The ID of the connection associated with the client + */ + public FuzzyWatchSyncNotifyTask(ConnectionManager connectionManager, RpcPushService rpcPushService, + ConfigFuzzyWatchSyncRequest notifyRequest, BatchTaskCounter batchTaskCounter, int maxRetryTimes, + String connectionId) { + this.connectionManager = connectionManager; + this.rpcPushService = rpcPushService; + this.notifyRequest = notifyRequest; + this.batchTaskCounter = batchTaskCounter; + this.maxRetryTimes = maxRetryTimes; + this.connectionId = connectionId; + } + + /** + * Checks if the maximum number of retry times has been reached. + * + * @return true if the maximum number of retry times has been reached, otherwise false + */ + public boolean isOverTimes() { + return maxRetryTimes > 0 && this.tryTimes >= maxRetryTimes; + } + + /** + * Executes the task, attempting to push the request to the client. + */ + @Override + public void run() { + + if (isOverTimes()) { + // If over the maximum retry times, log a warning and unregister the client connection + Loggers.REMOTE_PUSH.warn( + "Push callback retry failed over times. groupKeyPattern={}, clientId={}, will unregister client.", + notifyRequest.getGroupKeyPattern(), connectionId); + connectionManager.unregister(connectionId); + } else if (connectionManager.getConnection(connectionId) != null) { + tryTimes++; + TpsCheckRequest tpsCheckRequest = new TpsCheckRequest(); + + tpsCheckRequest.setPointName(CONFIG_FUZZY_WATCH_CONFIG_SYNC); + if (!ControlManagerCenter.getInstance().getTpsControlManager().check(tpsCheckRequest).isSuccess()) { + scheduleSelf(); + } else { + rpcPushService.pushWithCallback(connectionId, notifyRequest, new FuzzyWatchSyncNotifyCallback(this), + ConfigExecutor.getClientConfigNotifierServiceExecutor()); + } + } + + } + + void scheduleSelf() { + ConfigExecutor.scheduleClientConfigNotifier(this, tryTimes * 2L, TimeUnit.SECONDS); + } +} diff --git a/config/src/main/java/com/alibaba/nacos/config/server/service/ConfigFuzzyWatchContextService.java b/config/src/main/java/com/alibaba/nacos/config/server/service/ConfigFuzzyWatchContextService.java index fe99189fbc5..3214af4b736 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/service/ConfigFuzzyWatchContextService.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/service/ConfigFuzzyWatchContextService.java @@ -220,7 +220,7 @@ private void initMatchGroupKeys(String groupKeyPattern) throws NacosException { if (FuzzyGroupKeyPattern.matchPattern(groupKeyPattern, groupKeyItems[0], groupKeyItems[1], groupKeyItems[2])) { - if (matchedGroupKeys.size() >= ConfigCommonConfig.getInstance().getMaxMatchedConfigCount()) { + if (reachToUpLimit(matchedGroupKeys.size())) { LogUtil.DEFAULT_LOG.warn("[fuzzy-watch] pattern matched service count is over limit , " + "other services will stop notify for pattern {} ,current count is {}", groupKeyPattern, matchedGroupKeys.size()); diff --git a/config/src/main/java/com/alibaba/nacos/config/server/service/query/handler/ConfigChainEntryHandler.java b/config/src/main/java/com/alibaba/nacos/config/server/service/query/handler/ConfigChainEntryHandler.java index a87e92a1482..2684a5973a5 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/service/query/handler/ConfigChainEntryHandler.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/service/query/handler/ConfigChainEntryHandler.java @@ -16,6 +16,7 @@ package com.alibaba.nacos.config.server.service.query.handler; +import com.alibaba.nacos.common.utils.NamespaceUtil; import com.alibaba.nacos.config.server.model.CacheItem; import com.alibaba.nacos.config.server.service.ConfigCacheService; import com.alibaba.nacos.config.server.service.query.model.ConfigQueryChainRequest; @@ -47,6 +48,8 @@ public String getName() { @Override public ConfigQueryChainResponse handle(ConfigQueryChainRequest request) throws IOException { + + request.setTenant(NamespaceUtil.processNamespaceParameter(request.getTenant())); String groupKey = GroupKey2.getKey(request.getDataId(), request.getGroup(), request.getTenant()); int lockResult = ConfigCacheService.tryConfigReadLock(groupKey); CacheItem cacheItem = ConfigCacheService.getContentCache(groupKey); @@ -78,9 +81,4 @@ public ConfigQueryChainResponse handle(ConfigQueryChainRequest request) throws I public static CacheItem getThreadLocalCacheItem() { return CACHE_ITEM_THREAD_LOCAL.get(); } - - public static void removeThreadLocalCacheItem() { - CACHE_ITEM_THREAD_LOCAL.remove(); - } - } \ No newline at end of file diff --git a/config/src/test/java/com/alibaba/nacos/config/server/model/ConfigCachePostProcessorDelegateTest.java b/config/src/test/java/com/alibaba/nacos/config/server/model/ConfigCachePostProcessorDelegateTest.java index a68d78d5b40..0b2908fbc13 100644 --- a/config/src/test/java/com/alibaba/nacos/config/server/model/ConfigCachePostProcessorDelegateTest.java +++ b/config/src/test/java/com/alibaba/nacos/config/server/model/ConfigCachePostProcessorDelegateTest.java @@ -30,7 +30,6 @@ import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Modifier; import java.util.Collections; import static org.mockito.Mockito.doNothing; @@ -83,10 +82,7 @@ void test2() Constructor constructor = ConfigCachePostProcessorDelegate.class.getDeclaredConstructor(); constructor.setAccessible(true); Field field = ConfigCachePostProcessorDelegate.class.getDeclaredField("INSTANCE"); - Field modifiersField = Field.class.getDeclaredField("modifiers"); - modifiersField.setAccessible(true); field.setAccessible(true); - modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL); ConfigCachePostProcessorDelegate delegate = (ConfigCachePostProcessorDelegate) constructor.newInstance(); field.set(null, delegate); ConfigCachePostProcessorDelegate.getInstance().postProcess(null, null); diff --git a/config/src/test/java/com/alibaba/nacos/config/server/remote/ConfigFuzzyWatchChangeNotifierTest.java b/config/src/test/java/com/alibaba/nacos/config/server/remote/ConfigFuzzyWatchChangeNotifierTest.java new file mode 100644 index 00000000000..f6acfa8ee19 --- /dev/null +++ b/config/src/test/java/com/alibaba/nacos/config/server/remote/ConfigFuzzyWatchChangeNotifierTest.java @@ -0,0 +1,128 @@ +/* + * Copyright 1999-2018 Alibaba Group Holding Ltd. + * + * Licensed 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 com.alibaba.nacos.config.server.remote; + +import com.alibaba.nacos.config.server.model.CacheItem; +import com.alibaba.nacos.config.server.model.event.LocalDataChangeEvent; +import com.alibaba.nacos.config.server.service.ConfigCacheService; +import com.alibaba.nacos.config.server.service.ConfigFuzzyWatchContextService; +import com.alibaba.nacos.config.server.utils.ConfigExecutor; +import com.alibaba.nacos.config.server.utils.GroupKey; +import com.alibaba.nacos.core.remote.ConnectionManager; +import com.alibaba.nacos.core.remote.RpcPushService; +import com.alibaba.nacos.core.remote.grpc.GrpcConnection; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.io.IOException; +import java.util.Collections; +import java.util.concurrent.TimeUnit; + +import static com.alibaba.nacos.api.common.Constants.ConfigChangedType.CONFIG_CHANGED; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +public class ConfigFuzzyWatchChangeNotifierTest { + + ConfigFuzzyWatchChangeNotifier configFuzzyWatchChangeNotifier; + + @Mock + ConnectionManager connectionManager; + + @Mock + RpcPushService rpcPushService; + + @Mock + ConfigFuzzyWatchContextService configFuzzyWatchContextService; + + MockedStatic tMockedStatic; + + MockedStatic configCacheServiceMockedStatic; + + @AfterEach + void after() { + tMockedStatic.close(); + configCacheServiceMockedStatic.close(); + } + + @BeforeEach + void setUp() throws IOException { + tMockedStatic = Mockito.mockStatic(ConfigExecutor.class); + configCacheServiceMockedStatic = Mockito.mockStatic(ConfigCacheService.class); + configFuzzyWatchChangeNotifier = new ConfigFuzzyWatchChangeNotifier(connectionManager, rpcPushService, + configFuzzyWatchContextService); + } + + @Test + void testOnConfigAdd() { + + String groupKey = GroupKey.getKeyTenant("data1234", "group", "tnnt1234"); + when(configFuzzyWatchContextService.syncGroupKeyContext(eq(groupKey), eq(CONFIG_CHANGED))).thenReturn(true); + + CacheItem cacheItem = Mockito.mock(CacheItem.class); + configCacheServiceMockedStatic.when(() -> ConfigCacheService.getContentCache(eq(groupKey))) + .thenReturn(cacheItem); + + String connectionId = "123456"; + when(configFuzzyWatchContextService.getMatchedClients(eq(groupKey))).thenReturn( + Collections.singleton(connectionId)); + GrpcConnection grpcConnection = Mockito.mock(GrpcConnection.class); + when(connectionManager.getConnection(eq(connectionId))).thenReturn(grpcConnection); + + LocalDataChangeEvent localDataChangeEvent = new LocalDataChangeEvent(groupKey); + configFuzzyWatchChangeNotifier.onEvent(localDataChangeEvent); + + tMockedStatic.verify( + () -> ConfigExecutor.scheduleClientConfigNotifier(any(FuzzyWatchChangeNotifyTask.class), eq(0L), + eq(TimeUnit.SECONDS)), times(1)); + + } + + @Test + void testOnEmptyConnection() { + + String groupKey = GroupKey.getKeyTenant("data1234", "group", "tnnt1234"); + when(configFuzzyWatchContextService.syncGroupKeyContext(eq(groupKey), eq(CONFIG_CHANGED))).thenReturn(true); + + CacheItem cacheItem = Mockito.mock(CacheItem.class); + configCacheServiceMockedStatic.when(() -> ConfigCacheService.getContentCache(eq(groupKey))) + .thenReturn(cacheItem); + + String connectionId = "123456"; + when(configFuzzyWatchContextService.getMatchedClients(eq(groupKey))).thenReturn( + Collections.singleton(connectionId)); + when(connectionManager.getConnection(eq(connectionId))).thenReturn(null); + + LocalDataChangeEvent localDataChangeEvent = new LocalDataChangeEvent(groupKey); + configFuzzyWatchChangeNotifier.onEvent(localDataChangeEvent); + + tMockedStatic.verify( + () -> ConfigExecutor.scheduleClientConfigNotifier(any(FuzzyWatchChangeNotifyTask.class), eq(0L), + eq(TimeUnit.SECONDS)), times(0)); + + } + +} diff --git a/config/src/test/java/com/alibaba/nacos/config/server/remote/ConfigFuzzyWatchSyncNotifierTest.java b/config/src/test/java/com/alibaba/nacos/config/server/remote/ConfigFuzzyWatchSyncNotifierTest.java new file mode 100644 index 00000000000..762d6552f33 --- /dev/null +++ b/config/src/test/java/com/alibaba/nacos/config/server/remote/ConfigFuzzyWatchSyncNotifierTest.java @@ -0,0 +1,141 @@ +/* + * Copyright 1999-2018 Alibaba Group Holding Ltd. + * + * Licensed 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 com.alibaba.nacos.config.server.remote; + +import com.alibaba.nacos.common.utils.FuzzyGroupKeyPattern; +import com.alibaba.nacos.config.server.configuration.ConfigCommonConfig; +import com.alibaba.nacos.config.server.model.event.ConfigFuzzyWatchEvent; +import com.alibaba.nacos.config.server.service.ConfigFuzzyWatchContextService; +import com.alibaba.nacos.config.server.utils.ConfigExecutor; +import com.alibaba.nacos.config.server.utils.GroupKey; +import com.alibaba.nacos.core.remote.ConnectionManager; +import com.alibaba.nacos.core.remote.RpcPushService; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.io.IOException; +import java.util.HashSet; +import java.util.Set; +import java.util.concurrent.TimeUnit; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +public class ConfigFuzzyWatchSyncNotifierTest { + + ConfigFuzzyWatchSyncNotifier configFuzzyWatchSyncNotifier; + + @Mock + ConnectionManager connectionManager; + + @Mock + RpcPushService rpcPushService; + + @Mock + ConfigFuzzyWatchContextService configFuzzyWatchContextService; + + MockedStatic tMockedStatic; + + @AfterEach + void after() { + tMockedStatic.close(); + } + + @BeforeEach + void setUp() throws IOException { + tMockedStatic = Mockito.mockStatic(ConfigExecutor.class); + configFuzzyWatchSyncNotifier = new ConfigFuzzyWatchSyncNotifier(connectionManager, rpcPushService, + configFuzzyWatchContextService); + } + + @Test + void testInitNotifyWithoutMatchGroupKeys() { + String connectionId = "conn12345678"; + String groupKeyPattern = FuzzyGroupKeyPattern.generatePattern("dataId*", "group", "tnnt1234"); + ConfigFuzzyWatchEvent configFuzzyWatchEvent = new ConfigFuzzyWatchEvent(connectionId, null, groupKeyPattern, + true); + configFuzzyWatchSyncNotifier.onEvent(configFuzzyWatchEvent); + tMockedStatic.verify( + () -> ConfigExecutor.scheduleClientConfigNotifier(any(FuzzyWatchSyncNotifyTask.class), eq(0L), + eq(TimeUnit.SECONDS)), times(1)); + + } + + @Test + void testInitNotifyWithMatchGroupKeys() { + + String groupKeyPattern = FuzzyGroupKeyPattern.generatePattern("dataId*", "group", "tnnt1234"); + Set matchGroupKeys = new HashSet<>(); + int batchSize = ConfigCommonConfig.getInstance().getBatchSize(); + for (int i = batchSize; i < batchSize * 2; i++) { + matchGroupKeys.add(GroupKey.getKeyTenant("dataId" + i, "group", "tnnt1234")); + } + Set clientMatchGroupKeys = new HashSet<>(); + for (int i = 0; i < batchSize; i++) { + clientMatchGroupKeys.add(GroupKey.getKeyTenant("dataId" + i, "group", "tnnt1234")); + } + + when(configFuzzyWatchContextService.matchGroupKeys(groupKeyPattern)).thenReturn(matchGroupKeys); + when(configFuzzyWatchContextService.reachToUpLimit(eq(groupKeyPattern))).thenReturn(false); + String connectionId = "conn12345678"; + + ConfigFuzzyWatchEvent configFuzzyWatchEvent = new ConfigFuzzyWatchEvent(connectionId, clientMatchGroupKeys, + groupKeyPattern, true); + configFuzzyWatchSyncNotifier.onEvent(configFuzzyWatchEvent); + tMockedStatic.verify( + () -> ConfigExecutor.scheduleClientConfigNotifier(any(FuzzyWatchSyncNotifyTask.class), eq(0L), + eq(TimeUnit.SECONDS)), times(2)); + + } + + @Test + void testInitNotifyWithMatchGroupKeysOnDeleteProtection() { + + String groupKeyPattern = FuzzyGroupKeyPattern.generatePattern("dataId*", "group", "tnnt1234"); + + Set matchGroupKeys = new HashSet<>(); + int batchSize = ConfigCommonConfig.getInstance().getBatchSize(); + for (int i = batchSize; i < batchSize * 2; i++) { + matchGroupKeys.add(GroupKey.getKeyTenant("dataId" + i, "group", "tnnt1234")); + } + Set clientMatchGroupKeys = new HashSet<>(); + for (int i = 0; i < batchSize; i++) { + clientMatchGroupKeys.add(GroupKey.getKeyTenant("dataId" + i, "group", "tnnt1234")); + } + + when(configFuzzyWatchContextService.matchGroupKeys(groupKeyPattern)).thenReturn(matchGroupKeys); + when(configFuzzyWatchContextService.reachToUpLimit(eq(groupKeyPattern))).thenReturn(true); + String connectionId = "conn12345678"; + ConfigFuzzyWatchEvent configFuzzyWatchEvent = new ConfigFuzzyWatchEvent(connectionId, clientMatchGroupKeys, + groupKeyPattern, true); + configFuzzyWatchSyncNotifier.onEvent(configFuzzyWatchEvent); + tMockedStatic.verify( + () -> ConfigExecutor.scheduleClientConfigNotifier(any(FuzzyWatchSyncNotifyTask.class), eq(0L), + eq(TimeUnit.SECONDS)), times(1)); + + } + +} diff --git a/config/src/test/java/com/alibaba/nacos/config/server/remote/ConfigQueryRequestHandlerTest.java b/config/src/test/java/com/alibaba/nacos/config/server/remote/ConfigQueryRequestHandlerTest.java index f0eeb784681..9601feb0032 100644 --- a/config/src/test/java/com/alibaba/nacos/config/server/remote/ConfigQueryRequestHandlerTest.java +++ b/config/src/test/java/com/alibaba/nacos/config/server/remote/ConfigQueryRequestHandlerTest.java @@ -27,16 +27,15 @@ import com.alibaba.nacos.config.server.model.gray.ConfigGrayPersistInfo; import com.alibaba.nacos.config.server.model.gray.GrayRuleManager; import com.alibaba.nacos.config.server.model.gray.TagGrayRule; -import com.alibaba.nacos.config.server.service.query.ConfigQueryChainService; import com.alibaba.nacos.config.server.service.ConfigCacheService; import com.alibaba.nacos.config.server.service.dump.disk.ConfigDiskServiceFactory; import com.alibaba.nacos.config.server.service.dump.disk.ConfigRocksDbDiskService; +import com.alibaba.nacos.config.server.service.query.ConfigQueryChainService; import com.alibaba.nacos.config.server.utils.GroupKey2; import com.alibaba.nacos.config.server.utils.PropertyUtil; import com.alibaba.nacos.sys.env.EnvUtil; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.MockedStatic; @@ -57,7 +56,6 @@ // todo open the test case @ExtendWith(MockitoExtension.class) -@Disabled class ConfigQueryRequestHandlerTest { static MockedStatic configCacheServiceMockedStatic; @@ -94,7 +92,6 @@ void setUp() throws IOException { final String groupKey = GroupKey2.getKey(dataId, group, Constants.DEFAULT_NAMESPACE_ID); when(ConfigCacheService.tryConfigReadLock(groupKey)).thenReturn(1); propertyUtilMockedStatic.when(PropertyUtil::getMaxContent).thenReturn(1024 * 1000); - } /** @@ -121,7 +118,8 @@ void testGetNormal() throws Exception { RequestMeta requestMeta = new RequestMeta(); requestMeta.setClientIp("127.0.0.1"); - when(configRocksDbDiskService.getContent(eq(dataId), eq(group), eq(Constants.DEFAULT_NAMESPACE_ID))).thenReturn(content); + when(configRocksDbDiskService.getContent(eq(dataId), eq(group), eq(Constants.DEFAULT_NAMESPACE_ID))).thenReturn( + content); ConfigQueryResponse response = configQueryRequestHandler.handle(configQueryRequest, requestMeta); assertEquals(content, response.getContent()); assertEquals(MD5Utils.md5Hex(content, "UTF-8"), response.getMd5()); diff --git a/config/src/test/java/com/alibaba/nacos/config/server/remote/FuzzyWatchSyncNotifyCallbackTest.java b/config/src/test/java/com/alibaba/nacos/config/server/remote/FuzzyWatchSyncNotifyCallbackTest.java new file mode 100644 index 00000000000..35aecd202d8 --- /dev/null +++ b/config/src/test/java/com/alibaba/nacos/config/server/remote/FuzzyWatchSyncNotifyCallbackTest.java @@ -0,0 +1,115 @@ +/* + * Copyright 1999-2018 Alibaba Group Holding Ltd. + * + * Licensed 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 com.alibaba.nacos.config.server.remote; + +import com.alibaba.nacos.api.config.remote.request.ConfigFuzzyWatchSyncRequest; +import com.alibaba.nacos.common.task.BatchTaskCounter; +import com.alibaba.nacos.config.server.utils.ConfigExecutor; +import com.alibaba.nacos.core.remote.ConnectionManager; +import com.alibaba.nacos.core.remote.RpcPushService; +import com.alibaba.nacos.plugin.control.ControlManagerCenter; +import com.alibaba.nacos.plugin.control.tps.TpsControlManager; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.io.IOException; +import java.util.concurrent.TimeUnit; + +import static com.alibaba.nacos.api.common.Constants.FUZZY_WATCH_INIT_NOTIFY; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +public class FuzzyWatchSyncNotifyCallbackTest { + + @Mock + ConnectionManager connectionManager; + + @Mock + ControlManagerCenter controlManagerCenter; + + @Mock + TpsControlManager tpsControlManager; + + MockedStatic controlManagerCenterMockedStatic; + + @Mock + RpcPushService rpcPushService; + + MockedStatic tMockedStatic; + + FuzzyWatchSyncNotifyCallback fuzzyWatchSyncNotifyCallback; + + BatchTaskCounter taskCounter; + + @Mock + ConfigFuzzyWatchSyncRequest configFuzzyWatchSyncRequest; + + @AfterEach + void after() { + tMockedStatic.close(); + controlManagerCenterMockedStatic.close(); + } + + @BeforeEach + void setUp() throws IOException { + taskCounter = new BatchTaskCounter(5); + tMockedStatic = Mockito.mockStatic(ConfigExecutor.class); + controlManagerCenterMockedStatic = Mockito.mockStatic(ControlManagerCenter.class); + Mockito.when(ControlManagerCenter.getInstance()).thenReturn(controlManagerCenter); + Mockito.when(ControlManagerCenter.getInstance().getTpsControlManager()).thenReturn(tpsControlManager); + FuzzyWatchSyncNotifyTask fuzzyWatchSyncNotifyTask = new FuzzyWatchSyncNotifyTask(connectionManager, + rpcPushService, configFuzzyWatchSyncRequest, taskCounter, 5, "con1"); + fuzzyWatchSyncNotifyCallback = new FuzzyWatchSyncNotifyCallback(fuzzyWatchSyncNotifyTask); + } + + @Test + void testOnSuccess() { + + when(configFuzzyWatchSyncRequest.getSyncType()).thenReturn(FUZZY_WATCH_INIT_NOTIFY); + when(configFuzzyWatchSyncRequest.getCurrentBatch()).thenReturn(5); + + for (int i = 1; i < 5; i++) { + taskCounter.batchSuccess(i); + } + fuzzyWatchSyncNotifyCallback.fuzzyWatchSyncNotifyTask.tryTimes++; + fuzzyWatchSyncNotifyCallback.onSuccess(); + //create a new init finish task; + tMockedStatic.verify( + () -> ConfigExecutor.scheduleClientConfigNotifier(any(FuzzyWatchSyncNotifyTask.class), eq(0L), + eq(TimeUnit.SECONDS)), times(1)); + } + + @Test + void testOnFail() { + + fuzzyWatchSyncNotifyCallback.fuzzyWatchSyncNotifyTask.tryTimes++; + fuzzyWatchSyncNotifyCallback.onFail(new RuntimeException()); + // schedule self ,after 2 sec. + tMockedStatic.verify( + () -> ConfigExecutor.scheduleClientConfigNotifier(any(FuzzyWatchSyncNotifyTask.class), eq(2L), + eq(TimeUnit.SECONDS)), times(1)); + } +} diff --git a/config/src/test/java/com/alibaba/nacos/config/server/service/ConfigFuzzyWatchContextServiceTest.java b/config/src/test/java/com/alibaba/nacos/config/server/service/ConfigFuzzyWatchContextServiceTest.java index ab21189c0f8..0accb389ee0 100644 --- a/config/src/test/java/com/alibaba/nacos/config/server/service/ConfigFuzzyWatchContextServiceTest.java +++ b/config/src/test/java/com/alibaba/nacos/config/server/service/ConfigFuzzyWatchContextServiceTest.java @@ -19,6 +19,7 @@ import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.common.utils.CollectionUtils; import com.alibaba.nacos.common.utils.FuzzyGroupKeyPattern; +import com.alibaba.nacos.config.server.configuration.ConfigCommonConfig; import com.alibaba.nacos.config.server.utils.GroupKey; import com.alibaba.nacos.sys.env.EnvUtil; import org.junit.jupiter.api.AfterEach; @@ -34,14 +35,22 @@ import static com.alibaba.nacos.api.common.Constants.ConfigChangedType.ADD_CONFIG; import static com.alibaba.nacos.api.common.Constants.ConfigChangedType.DELETE_CONFIG; +import static com.alibaba.nacos.api.model.v2.ErrorCode.FUZZY_WATCH_PATTERN_OVER_LIMIT; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.when; @ExtendWith(SpringExtension.class) public class ConfigFuzzyWatchContextServiceTest { MockedStatic envUtilMockedStatic; + MockedStatic configCommonConfigMockedStatic; + + private static int mocMaxPattern = 5; + + private static int mocMaxPatternConfigCount = 10; + /** * before. */ @@ -50,25 +59,33 @@ public void before() { envUtilMockedStatic = Mockito.mockStatic(EnvUtil.class); envUtilMockedStatic.when(() -> EnvUtil.getProperty(eq("nacos.config.cache.type"), anyString())) .thenReturn("nacos"); + + configCommonConfigMockedStatic = Mockito.mockStatic(ConfigCommonConfig.class); + + ConfigCommonConfig configCommonConfig = Mockito.mock(ConfigCommonConfig.class); + when(configCommonConfig.getMaxPatternCount()).thenReturn(mocMaxPattern); + when(configCommonConfig.getMaxMatchedConfigCount()).thenReturn(mocMaxPatternConfigCount); + + configCommonConfigMockedStatic.when(() -> ConfigCommonConfig.getInstance()).thenReturn(configCommonConfig); } @AfterEach public void after() { envUtilMockedStatic.close(); + configCommonConfigMockedStatic.close(); } @Test public void testTrimFuzzyWatchContext() throws NacosException { - ConfigCacheService.dump("data124", "group", "12345", "content", System.currentTimeMillis(), null, null); ConfigFuzzyWatchContextService configFuzzyWatchContextService = new ConfigFuzzyWatchContextService(); String groupKey = GroupKey.getKeyTenant("data124", "group", "12345"); - configFuzzyWatchContextService.syncGroupKeyContext(groupKey, ADD_CONFIG); //init String collectionId = "id"; String groupKeyPattern = FuzzyGroupKeyPattern.generatePattern("data*", "group", "12345"); configFuzzyWatchContextService.addFuzzyWatch(groupKeyPattern, collectionId); + configFuzzyWatchContextService.syncGroupKeyContext(groupKey, ADD_CONFIG); //test Set matchedClients = configFuzzyWatchContextService.getMatchedClients(groupKey); @@ -122,6 +139,74 @@ public void testSyncGroupKeyContext() throws NacosException { } + @Test + public void testMakeupGroupKeyContext() throws NacosException { + + ConfigFuzzyWatchContextService configFuzzyWatchContextService = new ConfigFuzzyWatchContextService(); + + //init + String collectionId = "id"; + String groupKeyPattern = FuzzyGroupKeyPattern.generatePattern("data*", "group", "12345"); + configFuzzyWatchContextService.addFuzzyWatch(groupKeyPattern, collectionId); + + for (int i = 0; i <= mocMaxPatternConfigCount; i++) { + String keyTenant = GroupKey.getKeyTenant("data1" + i, "group", "12345"); + boolean needNotify1 = configFuzzyWatchContextService.syncGroupKeyContext(keyTenant, ADD_CONFIG); + Assertions.assertEquals(i < mocMaxPatternConfigCount ? true : false, needNotify1); + } + + String overLimitKey = GroupKey.getKeyTenant("data1" + mocMaxPatternConfigCount, "group", "12345"); + Assertions.assertFalse(configFuzzyWatchContextService.matchGroupKeys(groupKeyPattern).contains(overLimitKey)); + + //sync init cache service + ConfigCacheService.dump("data1" + mocMaxPatternConfigCount, "group", "12345", "content", + System.currentTimeMillis(), null, null); + + String deletedKey = GroupKey.getKeyTenant("data1" + 0, "group", "12345"); + + configFuzzyWatchContextService.syncGroupKeyContext(deletedKey, DELETE_CONFIG); + + Assertions.assertTrue(configFuzzyWatchContextService.matchGroupKeys(groupKeyPattern).contains(overLimitKey)); + + } + + @Test + public void testInitGroupKeyContext() throws NacosException { + + ConfigFuzzyWatchContextService configFuzzyWatchContextService = new ConfigFuzzyWatchContextService(); + String dataIdPrefix = "testinitD"; + // init config + for (int i = 0; i <= mocMaxPatternConfigCount; i++) { + ConfigCacheService.dump(dataIdPrefix + i, "group", "12345", "content", System.currentTimeMillis(), null, + null); + } + + String collectionId = "id"; + String groupKeyPattern = FuzzyGroupKeyPattern.generatePattern(dataIdPrefix + "*", "group", "12345"); + + // test init config + configFuzzyWatchContextService.addFuzzyWatch(groupKeyPattern, collectionId); + Assertions.assertEquals(mocMaxPatternConfigCount, + configFuzzyWatchContextService.matchGroupKeys(groupKeyPattern).size()); + + for (int i = 1; i < mocMaxPattern; i++) { + String groupKeyPattern0 = FuzzyGroupKeyPattern.generatePattern(dataIdPrefix + "*" + i, "group", "12345"); + configFuzzyWatchContextService.addFuzzyWatch(groupKeyPattern0, collectionId); + } + + try { + String groupKeyPatternOver = FuzzyGroupKeyPattern.generatePattern(dataIdPrefix + "*" + mocMaxPattern, + "group", "12345"); + + configFuzzyWatchContextService.addFuzzyWatch(groupKeyPatternOver, collectionId); + Assertions.assertTrue(false); + } catch (NacosException nacosException) { + Assertions.assertEquals(FUZZY_WATCH_PATTERN_OVER_LIMIT.getCode(), nacosException.getErrCode()); + Assertions.assertEquals(FUZZY_WATCH_PATTERN_OVER_LIMIT.getMsg(), nacosException.getErrMsg()); + } + + } + @Test public void testFuzzyWatch() throws NacosException { ConfigFuzzyWatchContextService configFuzzyWatchContextService = new ConfigFuzzyWatchContextService(); diff --git a/config/src/test/java/com/alibaba/nacos/config/server/service/dump/DumpProcessorTest.java b/config/src/test/java/com/alibaba/nacos/config/server/service/dump/DumpProcessorTest.java index e3d8ccf93d8..8fcc7865566 100644 --- a/config/src/test/java/com/alibaba/nacos/config/server/service/dump/DumpProcessorTest.java +++ b/config/src/test/java/com/alibaba/nacos/config/server/service/dump/DumpProcessorTest.java @@ -105,12 +105,19 @@ protected ConfigDiskService createDiskService() { } @AfterEach - void after() { + void after() throws Exception { dynamicDataSourceMockedStatic.close(); envUtilMockedStatic.close(); ConfigDiskServiceFactory.getInstance().clearAll(); ConfigDiskServiceFactory.getInstance().clearAllGray(); - + + Field[] declaredFields = ConfigDiskServiceFactory.class.getDeclaredFields(); + for (Field filed : declaredFields) { + if (filed.getName().equals("configDiskService")) { + filed.setAccessible(true); + filed.set(null, null); + } + } } @Test diff --git a/config/src/test/java/com/alibaba/nacos/config/server/service/dump/DumpProcessorUserRwaDiskTest.java b/config/src/test/java/com/alibaba/nacos/config/server/service/dump/DumpProcessorUserRwaDiskTest.java index 41f464686ce..e2d6bf6d89d 100644 --- a/config/src/test/java/com/alibaba/nacos/config/server/service/dump/DumpProcessorUserRwaDiskTest.java +++ b/config/src/test/java/com/alibaba/nacos/config/server/service/dump/DumpProcessorUserRwaDiskTest.java @@ -40,7 +40,7 @@ protected ConfigDiskService createDiskService() { } @AfterEach - public void after() { + public void after() throws Exception { super.after(); } diff --git a/config/src/test/java/com/alibaba/nacos/config/server/utils/Md5ComparatorDelegateTest.java b/config/src/test/java/com/alibaba/nacos/config/server/utils/Md5ComparatorDelegateTest.java index 640aad0f184..548f4c9ad4d 100644 --- a/config/src/test/java/com/alibaba/nacos/config/server/utils/Md5ComparatorDelegateTest.java +++ b/config/src/test/java/com/alibaba/nacos/config/server/utils/Md5ComparatorDelegateTest.java @@ -31,7 +31,6 @@ import java.lang.reflect.Constructor; import java.lang.reflect.Field; -import java.lang.reflect.Modifier; import java.util.Collections; import java.util.HashMap; @@ -90,10 +89,7 @@ public void test2() throws Exception { Constructor constructor = Md5ComparatorDelegate.class.getDeclaredConstructor(); constructor.setAccessible(true); Field field = Md5ComparatorDelegate.class.getDeclaredField("INSTANCE"); - Field modifiersField = Field.class.getDeclaredField("modifiers"); - modifiersField.setAccessible(true); field.setAccessible(true); - modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL); Md5ComparatorDelegate delegate = (Md5ComparatorDelegate) constructor.newInstance(); field.set(null, delegate); MockHttpServletRequest request = new MockHttpServletRequest(); diff --git a/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchChangeNotifyExecuteTask.java b/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchChangeNotifyExecuteTask.java index 46cabbec2e7..bfe9cfd3cab 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchChangeNotifyExecuteTask.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchChangeNotifyExecuteTask.java @@ -52,7 +52,7 @@ public FuzzyWatchChangeNotifyExecuteTask(FuzzyWatchPushDelayTaskEngine delayTask public void run() { delayTaskEngine.getPushExecutor().doFuzzyWatchNotifyPushWithCallBack(clientId, - new NamingFuzzyWatchChangeNotifyRequest(serviceKey, changedType, FUZZY_WATCH_RESOURCE_CHANGED), + new NamingFuzzyWatchChangeNotifyRequest(serviceKey, changedType), new FuzzyWatchChangeNotifyCallback(clientId, serviceKey, changedType)); } From c54a5f3d7dd6412f9d867fec508577ed34ac0752 Mon Sep 17 00:00:00 2001 From: "zunfei.lzf" Date: Wed, 15 Jan 2025 17:32:17 +0800 Subject: [PATCH 5/8] pattern match over load optimize test case --- .../client/config/impl/ConfigFuzzyWatchGroupKeyHolder.java | 3 +-- .../client/naming/cache/NamingFuzzyWatchServiceListHolder.java | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/client/src/main/java/com/alibaba/nacos/client/config/impl/ConfigFuzzyWatchGroupKeyHolder.java b/client/src/main/java/com/alibaba/nacos/client/config/impl/ConfigFuzzyWatchGroupKeyHolder.java index 090509162ec..adb62346d6c 100644 --- a/client/src/main/java/com/alibaba/nacos/client/config/impl/ConfigFuzzyWatchGroupKeyHolder.java +++ b/client/src/main/java/com/alibaba/nacos/client/config/impl/ConfigFuzzyWatchGroupKeyHolder.java @@ -317,11 +317,10 @@ public void executeConfigFuzzyListen() throws NacosException { for (ConfigFuzzyWatchContext context : fuzzyListenContextMap.get().values()) { // Check if the context is consistent with the server if (context.isConsistentWithServer()) { + context.syncFuzzyWatchers(); // Skip if a full synchronization is not needed if (!needAllSync) { continue; - } else { - context.syncFuzzyWatchers(); } } diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/cache/NamingFuzzyWatchServiceListHolder.java b/client/src/main/java/com/alibaba/nacos/client/naming/cache/NamingFuzzyWatchServiceListHolder.java index af6811b913a..f42deb4c922 100644 --- a/client/src/main/java/com/alibaba/nacos/client/naming/cache/NamingFuzzyWatchServiceListHolder.java +++ b/client/src/main/java/com/alibaba/nacos/client/naming/cache/NamingFuzzyWatchServiceListHolder.java @@ -228,9 +228,9 @@ public void executeNamingFuzzyWatch() throws NacosException { for (NamingFuzzyWatchContext context : fuzzyMatchContextMap.values()) { // Check if the context is consistent with the server if (context.isConsistentWithServer()) { + context.syncFuzzyWatchers(); // Skip if a full synchronization is not needed if (!needAllSync) { - context.syncFuzzyWatchers(); continue; } } From 3b3aca859a88f71a4633699a80eb4be810e03d40 Mon Sep 17 00:00:00 2001 From: "zunfei.lzf" Date: Wed, 15 Jan 2025 17:33:13 +0800 Subject: [PATCH 6/8] pattern match over load optimize test case --- .../nacos/client/naming/event/NamingFuzzyWatchLoadEvent.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/event/NamingFuzzyWatchLoadEvent.java b/client/src/main/java/com/alibaba/nacos/client/naming/event/NamingFuzzyWatchLoadEvent.java index d4218585994..2844131225b 100644 --- a/client/src/main/java/com/alibaba/nacos/client/naming/event/NamingFuzzyWatchLoadEvent.java +++ b/client/src/main/java/com/alibaba/nacos/client/naming/event/NamingFuzzyWatchLoadEvent.java @@ -25,8 +25,8 @@ * that it may be processed asynchronously. The event contains information about the group, dataId, type, and UUID of * the notification. * - * @author stone-98 - * @date 2024/3/4 + * @author shiyiyue + * @date 2025/01/13 */ public class NamingFuzzyWatchLoadEvent extends Event { From 55b1eeac42ef0be17e4d931f9e2c668d416916b0 Mon Sep 17 00:00:00 2001 From: "zunfei.lzf" Date: Thu, 16 Jan 2025 14:44:31 +0800 Subject: [PATCH 7/8] test case --- .../request/NamingFuzzyWatchSyncRequest.java | 8 +- .../ConfigFuzzyWatchContextService.java | 3 +- .../index/NamingFuzzyWatchContextService.java | 19 +- .../push/NamingFuzzyWatchSyncNotifier.java | 4 +- .../FuzzyWatchChangeNotifyExecuteTask.java | 12 +- .../v2/task/FuzzyWatchSyncNotifyCallback.java | 103 ++++++++ .../task/FuzzyWatchSyncNotifyExecuteTask.java | 105 +------- .../v2/task/FuzzyWatchSyncNotifyTask.java | 9 +- .../NamingFuzzyWatchContextServiceTest.java | 247 ++++++++++++++++++ .../NamingFuzzyWatchChangeNotifierTest.java | 83 ++++++ .../NamingFuzzyWatchSyncNotifierTest.java | 165 ++++++++++++ .../FuzzyWatchSyncNotifyExecuteTaskTest.java | 134 ++++++++++ 12 files changed, 760 insertions(+), 132 deletions(-) create mode 100644 naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchSyncNotifyCallback.java create mode 100644 naming/src/test/java/com/alibaba/nacos/naming/core/v2/index/NamingFuzzyWatchContextServiceTest.java create mode 100644 naming/src/test/java/com/alibaba/nacos/naming/push/NamingFuzzyWatchChangeNotifierTest.java create mode 100644 naming/src/test/java/com/alibaba/nacos/naming/push/NamingFuzzyWatchSyncNotifierTest.java create mode 100644 naming/src/test/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchSyncNotifyExecuteTaskTest.java diff --git a/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/NamingFuzzyWatchSyncRequest.java b/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/NamingFuzzyWatchSyncRequest.java index c7ac6ca52a4..29492113c70 100644 --- a/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/NamingFuzzyWatchSyncRequest.java +++ b/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/NamingFuzzyWatchSyncRequest.java @@ -16,15 +16,13 @@ package com.alibaba.nacos.api.naming.remote.request; -import com.alibaba.nacos.api.common.Constants; - -import java.util.HashSet; import java.util.Set; import static com.alibaba.nacos.api.common.Constants.Naming.NAMING_MODULE; /** * fuzzy watch sync request from Nacos server. + * * @author shiyiyue */ public class NamingFuzzyWatchSyncRequest extends AbstractFuzzyWatchNotifyRequest { @@ -69,10 +67,6 @@ public void setCurrentBatch(int currentBatch) { this.currentBatch = currentBatch; } - public static NamingFuzzyWatchSyncRequest buildInitNotifyFinishRequest(String pattern) { - return new NamingFuzzyWatchSyncRequest(pattern, Constants.FINISH_FUZZY_WATCH_INIT_NOTIFY, new HashSet<>(1)); - } - /** * byuld SyncNotifyRequest. * diff --git a/config/src/main/java/com/alibaba/nacos/config/server/service/ConfigFuzzyWatchContextService.java b/config/src/main/java/com/alibaba/nacos/config/server/service/ConfigFuzzyWatchContextService.java index 3214af4b736..a1f76ac27c0 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/service/ConfigFuzzyWatchContextService.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/service/ConfigFuzzyWatchContextService.java @@ -108,7 +108,8 @@ void trimFuzzyWatchContext() { * @return */ public Set matchGroupKeys(String groupKeyPattern) { - return matchedGroupKeysMap.get(groupKeyPattern); + Set stringSet = matchedGroupKeysMap.get(groupKeyPattern); + return stringSet == null ? new HashSet<>() : new HashSet<>(matchedGroupKeysMap.get(groupKeyPattern)); } /** diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/index/NamingFuzzyWatchContextService.java b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/index/NamingFuzzyWatchContextService.java index 027ed2f76f1..bfacb13ddfb 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/index/NamingFuzzyWatchContextService.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/index/NamingFuzzyWatchContextService.java @@ -66,10 +66,7 @@ public class NamingFuzzyWatchContextService extends SmartSubscriber { */ private final ConcurrentMap> matchedServiceKeysMap = new ConcurrentHashMap<>(); - GlobalConfig globalConfig; - - public NamingFuzzyWatchContextService(GlobalConfig globalConfig) { - this.globalConfig = globalConfig; + public NamingFuzzyWatchContextService() { } @PostConstruct @@ -83,7 +80,7 @@ public void init() { * if watchedClients is null. pattern matchedServiceKeys will be removed in second period to avoid frequently * matchedServiceKeys init. */ - private void trimFuzzyWatchContext() { + void trimFuzzyWatchContext() { try { Iterator>> iterator = matchedServiceKeysMap.entrySet().iterator(); while (iterator.hasNext()) { @@ -125,7 +122,6 @@ public List> subscribeTypes() { @Override public void onEvent(Event event) { - if (event instanceof ClientOperationEvent.ClientReleaseEvent) { removeFuzzyWatchContext(((ClientOperationEvent.ClientReleaseEvent) event).getClientId()); } @@ -207,7 +203,7 @@ public boolean syncServiceContext(Service changedService, String changedType) { } private boolean reachToUpLimit(int size) { - return size >= globalConfig.getMaxMatchedServiceCount(); + return size >= GlobalConfig.getMaxMatchedServiceCount(); } public boolean reachToUpLimit(String groupKeyPattern) { @@ -262,13 +258,14 @@ public void syncFuzzyWatcherContext(String groupKeyPattern, String clientId) thr } /** - * get matched exist group keys with the groupKeyPattern. return null if not matched. + * get matched exist group keys with the groupKeyPattern. * * @param groupKeyPattern groupKeyPattern. * @return */ public Set matchServiceKeys(String groupKeyPattern) { - return matchedServiceKeysMap.get(groupKeyPattern); + Set stringSet = matchedServiceKeysMap.get(groupKeyPattern); + return stringSet == null ? new HashSet<>() : new HashSet<>(stringSet); } private void removeFuzzyWatchContext(String clientId) { @@ -300,7 +297,7 @@ public void removeFuzzyWatchContext(String groupKeyPattern, String clientId) { public Set initWatchMatchService(String completedPattern) throws NacosException { if (!matchedServiceKeysMap.containsKey(completedPattern)) { - if (matchedServiceKeysMap.size() >= globalConfig.getMaxPatternCount()) { + if (matchedServiceKeysMap.size() >= GlobalConfig.getMaxPatternCount()) { Loggers.SRV_LOG.warn( "FUZZY_WATCH: fuzzy watch pattern count is over limit ,pattern {} init fail,current count is {}", completedPattern, matchedServiceKeysMap.size()); @@ -316,7 +313,7 @@ public Set initWatchMatchService(String completedPattern) throws NacosEx for (Service service : namespaceServices) { if (FuzzyGroupKeyPattern.matchPattern(completedPattern, service.getName(), service.getGroup(), service.getNamespace())) { - if (matchedServices.size() >= globalConfig.getMaxMatchedServiceCount()) { + if (matchedServices.size() >= GlobalConfig.getMaxMatchedServiceCount()) { Loggers.SRV_LOG.warn("[fuzzy-watch] pattern matched service count is over limit , " + "other services will stop notify for pattern {} ,current count is {}", diff --git a/naming/src/main/java/com/alibaba/nacos/naming/push/NamingFuzzyWatchSyncNotifier.java b/naming/src/main/java/com/alibaba/nacos/naming/push/NamingFuzzyWatchSyncNotifier.java index 2a607f66e4f..6e2b501e58d 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/push/NamingFuzzyWatchSyncNotifier.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/push/NamingFuzzyWatchSyncNotifier.java @@ -54,7 +54,7 @@ public class NamingFuzzyWatchSyncNotifier extends SmartSubscriber { private FuzzyWatchPushDelayTaskEngine fuzzyWatchPushDelayTaskEngine; - private static final int BATCH_SIZE = 10; + static final int BATCH_SIZE = 10; public NamingFuzzyWatchSyncNotifier(NamingFuzzyWatchContextService namingFuzzyWatchContextService, FuzzyWatchPushDelayTaskEngine fuzzyWatchPushDelayTaskEngine) { @@ -86,7 +86,7 @@ private void handleClientFuzzyWatchEvent(ClientOperationEvent.ClientFuzzyWatchEv //sync fuzzy watch context Set patternMatchedServiceKeys = namingFuzzyWatchContextService.matchServiceKeys(completedPattern); - Set clientReceivedGroupKeys = clientFuzzyWatchEvent.getClientReceivedServiceKeys(); + Set clientReceivedGroupKeys = new HashSet<>(clientFuzzyWatchEvent.getClientReceivedServiceKeys()); List groupKeyStates = FuzzyGroupKeyPattern.diffGroupKeys( patternMatchedServiceKeys, clientReceivedGroupKeys); diff --git a/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchChangeNotifyExecuteTask.java b/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchChangeNotifyExecuteTask.java index bfe9cfd3cab..7af4ab52404 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchChangeNotifyExecuteTask.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchChangeNotifyExecuteTask.java @@ -23,7 +23,7 @@ import com.alibaba.nacos.naming.push.v2.NoRequiredRetryException; import com.alibaba.nacos.naming.push.v2.PushConfig; -import static com.alibaba.nacos.api.common.Constants.FUZZY_WATCH_RESOURCE_CHANGED; +import static com.alibaba.nacos.naming.push.v2.task.FuzzyWatchPushDelayTaskEngine.getTaskKey; /** * Nacos naming fuzzy watch notify service change push delay task. @@ -78,7 +78,7 @@ public long getTimeout() { @Override public void onSuccess() { - Loggers.PUSH.info("[FUZZY-WATCH] change notify success ,clientId {}, serviceKey {] ,changedType {} ", + Loggers.PUSH.info("[fuzzy watch] change notify success ,clientId {}, serviceKey {] ,changedType {} ", clientId, clientId, changedType); } @@ -86,13 +86,13 @@ public void onSuccess() { @Override public void onFail(Throwable e) { - Loggers.PUSH.warn("[FUZZY-WATCH] change notify fail ,clientId {}, serviceKey {] ,changedType {} ", clientId, + Loggers.PUSH.warn("[fuzzy watch] change notify fail ,clientId {}, serviceKey {] ,changedType {} ", clientId, clientId, changedType, e); if (!(e instanceof NoRequiredRetryException)) { - delayTaskEngine.addTask(System.currentTimeMillis(), - new FuzzyWatchChangeNotifyTask(serviceKey, changedType, clientId, - PushConfig.getInstance().getPushTaskRetryDelay())); + FuzzyWatchChangeNotifyTask fuzzyWatchChangeNotifyTask = new FuzzyWatchChangeNotifyTask(serviceKey, + changedType, clientId, PushConfig.getInstance().getPushTaskRetryDelay()); + delayTaskEngine.addTask(getTaskKey(fuzzyWatchChangeNotifyTask), fuzzyWatchChangeNotifyTask); } } } diff --git a/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchSyncNotifyCallback.java b/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchSyncNotifyCallback.java new file mode 100644 index 00000000000..7bf75f5bc92 --- /dev/null +++ b/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchSyncNotifyCallback.java @@ -0,0 +1,103 @@ +/* + * Copyright 1999-2023 Alibaba Group Holding Ltd. + * + * Licensed 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 com.alibaba.nacos.naming.push.v2.task; + +import com.alibaba.nacos.api.remote.PushCallBack; +import com.alibaba.nacos.common.task.BatchTaskCounter; +import com.alibaba.nacos.naming.misc.Loggers; +import com.alibaba.nacos.naming.push.v2.NoRequiredRetryException; +import com.alibaba.nacos.naming.push.v2.PushConfig; + +import static com.alibaba.nacos.api.common.Constants.FINISH_FUZZY_WATCH_INIT_NOTIFY; +import static com.alibaba.nacos.api.common.Constants.FUZZY_WATCH_INIT_NOTIFY; +import static com.alibaba.nacos.naming.push.v2.task.FuzzyWatchPushDelayTaskEngine.getTaskKey; + +class FuzzyWatchSyncNotifyCallback implements PushCallBack { + + private FuzzyWatchSyncNotifyTask fuzzyWatchSyncNotifyTask; + + private BatchTaskCounter batchTaskCounter; + + private FuzzyWatchPushDelayTaskEngine fuzzyWatchPushDelayTaskEngine; + + FuzzyWatchSyncNotifyCallback(FuzzyWatchSyncNotifyTask fuzzyWatchSyncNotifyTask, BatchTaskCounter batchTaskCounter, + FuzzyWatchPushDelayTaskEngine fuzzyWatchPushDelayTaskEngine) { + this.batchTaskCounter = batchTaskCounter; + this.fuzzyWatchPushDelayTaskEngine = fuzzyWatchPushDelayTaskEngine; + this.fuzzyWatchSyncNotifyTask = fuzzyWatchSyncNotifyTask; + } + + @Override + public long getTimeout() { + return PushConfig.getInstance().getPushTaskTimeout(); + } + + @Override + public void onSuccess() { + + long now = System.currentTimeMillis(); + if (isFinishInitTask()) { + Loggers.PUSH.info( + "[fuzzy watch] init notify finish push success ,clientId={}, pattern ={},total cost time={}ms", + fuzzyWatchSyncNotifyTask.getClientId(), fuzzyWatchSyncNotifyTask.getPattern(), + (now - fuzzyWatchSyncNotifyTask.getExecuteStartTime())); + } else { + Loggers.PUSH.info( + "[fuzzy watch] sync notify task success, pattern {}, syncType={},clientId={},current batch size {},currentBatch={},totalBatch={}", + fuzzyWatchSyncNotifyTask.getPattern(), fuzzyWatchSyncNotifyTask.getSyncType(), + fuzzyWatchSyncNotifyTask.getClientId(), fuzzyWatchSyncNotifyTask.getSyncServiceKeys().size(), + fuzzyWatchSyncNotifyTask.getCurrentBatch()); + // if total batch is success sync to client send + if (isInitNotifyTask()) { + Loggers.PUSH.info( + "[fuzzy watch] init notify push success ,clientId={}, pattern ={} ,currentBatch={},totalBatch={}", + fuzzyWatchSyncNotifyTask.getClientId(), fuzzyWatchSyncNotifyTask.getPattern(), + fuzzyWatchSyncNotifyTask.getCurrentBatch(), fuzzyWatchSyncNotifyTask.getTotalBatch()); + batchTaskCounter.batchSuccess(fuzzyWatchSyncNotifyTask.getCurrentBatch()); + if (batchTaskCounter.batchCompleted()) { + Loggers.PUSH.info( + "[fuzzy watch] init notify all batch finish ,clientId={}, pattern ={},start notify init finish task", + fuzzyWatchSyncNotifyTask.getClientId(), fuzzyWatchSyncNotifyTask.getPattern()); + FuzzyWatchSyncNotifyTask fuzzyWatchSyncNotifyTaskFinish = new FuzzyWatchSyncNotifyTask( + fuzzyWatchSyncNotifyTask.getClientId(), fuzzyWatchSyncNotifyTask.getPattern(), + FINISH_FUZZY_WATCH_INIT_NOTIFY, null, PushConfig.getInstance().getPushTaskDelay()); + fuzzyWatchPushDelayTaskEngine.addTask(getTaskKey(fuzzyWatchSyncNotifyTaskFinish), + fuzzyWatchSyncNotifyTaskFinish); + } + } + } + } + + private boolean isFinishInitTask() { + return FINISH_FUZZY_WATCH_INIT_NOTIFY.equals(fuzzyWatchSyncNotifyTask.getSyncType()); + } + + private boolean isInitNotifyTask() { + return FUZZY_WATCH_INIT_NOTIFY.equals(fuzzyWatchSyncNotifyTask.getSyncType()); + } + + @Override + public void onFail(Throwable e) { + Loggers.PUSH.warn("[fuzzy watch] sync notify fail, pattern {} ,clientId ={},currentBatch={},totalBatch={}", + fuzzyWatchSyncNotifyTask.getPattern(), fuzzyWatchSyncNotifyTask.getClientId(), + fuzzyWatchSyncNotifyTask.getCurrentBatch(), fuzzyWatchSyncNotifyTask.getTotalBatch(), e); + if (!(e instanceof NoRequiredRetryException)) { + Loggers.PUSH.warn("[fuzzy watch] reschedule this task to engine"); + fuzzyWatchPushDelayTaskEngine.addTask(getTaskKey(fuzzyWatchSyncNotifyTask), fuzzyWatchSyncNotifyTask); + } + } +} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchSyncNotifyExecuteTask.java b/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchSyncNotifyExecuteTask.java index 243cb6336a8..cbdddb30f53 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchSyncNotifyExecuteTask.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchSyncNotifyExecuteTask.java @@ -17,15 +17,7 @@ package com.alibaba.nacos.naming.push.v2.task; import com.alibaba.nacos.api.naming.remote.request.NamingFuzzyWatchSyncRequest; -import com.alibaba.nacos.api.remote.PushCallBack; import com.alibaba.nacos.common.task.AbstractExecuteTask; -import com.alibaba.nacos.common.task.BatchTaskCounter; -import com.alibaba.nacos.naming.misc.Loggers; -import com.alibaba.nacos.naming.push.v2.NoRequiredRetryException; -import com.alibaba.nacos.naming.push.v2.PushConfig; - -import static com.alibaba.nacos.api.common.Constants.FINISH_FUZZY_WATCH_INIT_NOTIFY; -import static com.alibaba.nacos.api.common.Constants.FUZZY_WATCH_INIT_NOTIFY; /** * Nacos naming fuzzy watch initial push execute task. @@ -42,23 +34,12 @@ public class FuzzyWatchSyncNotifyExecuteTask extends AbstractExecuteTask { private final FuzzyWatchSyncNotifyTask delayTask; - /** - * Fuzzy watch origin push matched service size, if there is no failure while executing push, - * {@code originSize == latch}. just use to record log after finish all push. - */ - private final int originSize; - - /** - * TODO set batch size from config. - */ - public FuzzyWatchSyncNotifyExecuteTask(String clientId, String pattern, FuzzyWatchPushDelayTaskEngine fuzzyWatchPushDelayTaskEngine, FuzzyWatchSyncNotifyTask delayTask) { this.clientId = clientId; this.pattern = pattern; this.fuzzyWatchPushDelayTaskEngine = fuzzyWatchPushDelayTaskEngine; this.delayTask = delayTask; - this.originSize = delayTask.getSyncServiceKeys().size(); } @Override @@ -69,90 +50,8 @@ public void run() { delayTask.getCurrentBatch()); fuzzyWatchPushDelayTaskEngine.getPushExecutor() .doFuzzyWatchNotifyPushWithCallBack(clientId, namingFuzzyWatchSyncRequest, - new FuzzyWatchSyncNotifyExecuteTask.FuzzyWatchSyncNotifyCallback(namingFuzzyWatchSyncRequest, - delayTask.getBatchTaskCounter())); - } - - private class FuzzyWatchSyncNotifyCallback implements PushCallBack { - - private NamingFuzzyWatchSyncRequest namingFuzzyWatchSyncRequest; - - /** - * Record the push task execute start time. - */ - private final long executeStartTime; - - private BatchTaskCounter batchTaskCounter; - - private FuzzyWatchSyncNotifyCallback(NamingFuzzyWatchSyncRequest namingFuzzyWatchSyncRequest, - BatchTaskCounter batchTaskCounter) { - this.namingFuzzyWatchSyncRequest = namingFuzzyWatchSyncRequest; - this.executeStartTime = System.currentTimeMillis(); - this.batchTaskCounter = batchTaskCounter; - } - - @Override - public long getTimeout() { - return PushConfig.getInstance().getPushTaskTimeout(); - } - - @Override - public void onSuccess() { - long pushFinishTime = System.currentTimeMillis(); - long pushCostTimeForNetWork = pushFinishTime - executeStartTime; - long pushCostTimeForAll = pushFinishTime - delayTask.getLastProcessTime(); - - if (isFinishInitTask()) { - Loggers.PUSH.info( - "[FUZZY-WATCH-SYNC-COMPLETE] {}ms, all delay time {}ms for client {} watch init push finish," - + " pattern {}, all push service size {}", pushCostTimeForNetWork, pushCostTimeForAll, - clientId, pattern, originSize); - } else { - Loggers.PUSH.info( - "[FUZZY-WATCH-PUSH-SUCC] {}ms, all delay time {}ms for client {}, pattern {}, syncType={},push size {},currentBatch={}", - pushCostTimeForNetWork, pushCostTimeForAll, clientId, pattern, - namingFuzzyWatchSyncRequest.getSyncType(), namingFuzzyWatchSyncRequest.getContexts().size(), - namingFuzzyWatchSyncRequest.getCurrentBatch()); - // if total batch is success sync to client send - if (isInitNotifyTask()) { - batchTaskCounter.batchSuccess(namingFuzzyWatchSyncRequest.getCurrentBatch()); - if (batchTaskCounter.batchCompleted()) { - fuzzyWatchPushDelayTaskEngine.addTask(System.currentTimeMillis(), - new FuzzyWatchSyncNotifyTask(clientId, pattern, FINISH_FUZZY_WATCH_INIT_NOTIFY, null, - PushConfig.getInstance().getPushTaskDelay())); - } - } - - } - } - - private boolean isFinishInitTask() { - return FINISH_FUZZY_WATCH_INIT_NOTIFY.equals(namingFuzzyWatchSyncRequest.getSyncType()); - } - - private boolean isInitNotifyTask() { - return FUZZY_WATCH_INIT_NOTIFY.equals(namingFuzzyWatchSyncRequest.getSyncType()); - } - - @Override - public void onFail(Throwable e) { - long pushCostTime = System.currentTimeMillis() - executeStartTime; - Loggers.PUSH.error( - "[FUZZY-WATCH-SYNC-PUSH-FAIL] {}ms, pattern {} match {} service: {}, currentBatch={},reason={}, client={}", - pushCostTime, pattern, namingFuzzyWatchSyncRequest.getContexts().size(), - namingFuzzyWatchSyncRequest.getCurrentBatch(), e.getMessage(), clientId); - if (!(e instanceof NoRequiredRetryException)) { - Loggers.PUSH.error("Reason detail: ", e); - //resend request only - FuzzyWatchSyncNotifyTask fuzzyWatchSyncNotifyTask = new FuzzyWatchSyncNotifyTask(clientId, pattern, - delayTask.getSyncType(), namingFuzzyWatchSyncRequest.getContexts(), - PushConfig.getInstance().getPushTaskRetryDelay()); - fuzzyWatchSyncNotifyTask.setBatchTaskCounter(batchTaskCounter); - - fuzzyWatchPushDelayTaskEngine.addTask(System.currentTimeMillis(), fuzzyWatchSyncNotifyTask); - - } - } + new FuzzyWatchSyncNotifyCallback(delayTask, delayTask.getBatchTaskCounter(), + fuzzyWatchPushDelayTaskEngine)); } } \ No newline at end of file diff --git a/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchSyncNotifyTask.java b/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchSyncNotifyTask.java index fa4c633f206..f9c4943f84d 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchSyncNotifyTask.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchSyncNotifyTask.java @@ -39,12 +39,14 @@ public class FuzzyWatchSyncNotifyTask extends AbstractDelayTask { private final String syncType; - private int totalBatch; + private int totalBatch = 1; - private int currentBatch; + private int currentBatch = 1; private BatchTaskCounter batchTaskCounter; + private long executeStartTime = System.currentTimeMillis(); + public FuzzyWatchSyncNotifyTask(String clientId, String pattern, String syncType, Set syncServiceKeys, long delay) { this.clientId = clientId; @@ -113,4 +115,7 @@ public void setBatchTaskCounter(BatchTaskCounter batchTaskCounter) { this.batchTaskCounter = batchTaskCounter; } + public long getExecuteStartTime() { + return executeStartTime; + } } diff --git a/naming/src/test/java/com/alibaba/nacos/naming/core/v2/index/NamingFuzzyWatchContextServiceTest.java b/naming/src/test/java/com/alibaba/nacos/naming/core/v2/index/NamingFuzzyWatchContextServiceTest.java new file mode 100644 index 00000000000..37debe44b2a --- /dev/null +++ b/naming/src/test/java/com/alibaba/nacos/naming/core/v2/index/NamingFuzzyWatchContextServiceTest.java @@ -0,0 +1,247 @@ +/* + * Copyright 1999-2018 Alibaba Group Holding Ltd. + * + * Licensed 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 com.alibaba.nacos.naming.core.v2.index; + +import com.alibaba.nacos.api.exception.NacosException; +import com.alibaba.nacos.api.naming.utils.NamingUtils; +import com.alibaba.nacos.common.utils.FuzzyGroupKeyPattern; +import com.alibaba.nacos.naming.core.v2.ServiceManager; +import com.alibaba.nacos.naming.core.v2.client.impl.ConnectionBasedClient; +import com.alibaba.nacos.naming.core.v2.event.client.ClientOperationEvent; +import com.alibaba.nacos.naming.core.v2.pojo.Service; +import com.alibaba.nacos.naming.misc.GlobalConfig; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.MockedStatic; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.HashSet; +import java.util.Set; + +import static com.alibaba.nacos.api.common.Constants.ServiceChangedType.ADD_SERVICE; +import static com.alibaba.nacos.api.common.Constants.ServiceChangedType.DELETE_SERVICE; +import static com.alibaba.nacos.api.model.v2.ErrorCode.FUZZY_WATCH_PATTERN_OVER_LIMIT; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +public class NamingFuzzyWatchContextServiceTest { + + NamingFuzzyWatchContextService namingFuzzyWatchContextService; + + @Mock + MockedStatic tMockedStatic; + + @Mock + MockedStatic serviceManagerMockedStatic; + + @Mock + ServiceManager serviceManager; + + @BeforeEach + void before() { + namingFuzzyWatchContextService = new NamingFuzzyWatchContextService(); + + serviceManagerMockedStatic.when(() -> ServiceManager.getInstance()).thenReturn(serviceManager); + } + + @AfterEach + void after() { + tMockedStatic.close(); + serviceManagerMockedStatic.close(); + } + + @Test + void testInitWatchMatchServiceNormal() throws NacosException { + tMockedStatic.when(() -> GlobalConfig.getMaxPatternCount()).thenReturn(20); + tMockedStatic.when(() -> GlobalConfig.getMaxMatchedServiceCount()).thenReturn(500); + + //mock services + Set serviceSet = new HashSet<>(); + for (int i = 0; i < 10; i++) { + Service service = Service.newService("namespace", "group" + i, "service" + i, true); + serviceSet.add(service); + } + serviceSet.add(Service.newService("namespace", "group", "12service", true)); + + when(serviceManager.getSingletons(eq("namespace"))).thenReturn(serviceSet); + String groupKeyPattern = FuzzyGroupKeyPattern.generatePattern("service*", "group*", "namespace"); + Set strings = namingFuzzyWatchContextService.initWatchMatchService(groupKeyPattern); + for (int i = 0; i < 10; i++) { + Assertions.assertTrue(strings.contains(NamingUtils.getServiceKey("namespace", "group" + i, "service" + i))); + } + Assertions.assertFalse(strings.contains(NamingUtils.getServiceKey("namespace", "group", "12service"))); + + } + + @Test + void testInitWatchMatchServiceOverLoadPatternCount() throws NacosException { + tMockedStatic.when(() -> GlobalConfig.getMaxPatternCount()).thenReturn(5); + + for (int i = 0; i < 10; i++) { + String groupKeyPattern = FuzzyGroupKeyPattern.generatePattern("service*" + i, "group*", "namespace"); + + if (i < 5) { + int size = namingFuzzyWatchContextService.initWatchMatchService(groupKeyPattern).size(); + Assertions.assertEquals(0, size); + } else { + try { + namingFuzzyWatchContextService.initWatchMatchService(groupKeyPattern).size(); + Assertions.assertFalse(true); + } catch (Exception nacosException) { + Assertions.assertTrue(nacosException instanceof NacosException); + Assertions.assertTrue( + ((NacosException) nacosException).getErrCode() == FUZZY_WATCH_PATTERN_OVER_LIMIT.getCode()); + } + } + } + } + + @Test + void testInitWatchMatchServiceOverLoadServiceCount() throws NacosException { + tMockedStatic.when(() -> GlobalConfig.getMaxPatternCount()).thenReturn(20); + tMockedStatic.when(() -> GlobalConfig.getMaxMatchedServiceCount()).thenReturn(5); + + //mock services + Set serviceSet = new HashSet<>(); + for (int i = 0; i < 10; i++) { + Service service = Service.newService("namespace", "group" + i, "service" + i, true); + serviceSet.add(service); + } + serviceSet.add(Service.newService("namespace", "group", "12service", true)); + when(serviceManager.getSingletons(eq("namespace"))).thenReturn(serviceSet); + String groupKeyPattern = FuzzyGroupKeyPattern.generatePattern("service*", "group*", "namespace"); + Set strings = namingFuzzyWatchContextService.initWatchMatchService(groupKeyPattern); + Assertions.assertTrue(strings.size() == 5); + Assertions.assertFalse(strings.contains(NamingUtils.getServiceKey("namespace", "group", "12service"))); + } + + @Test + void testSyncServiceContext() throws NacosException { + tMockedStatic.when(() -> GlobalConfig.getMaxPatternCount()).thenReturn(20); + tMockedStatic.when(() -> GlobalConfig.getMaxMatchedServiceCount()).thenReturn(500); + + //init group key context. + + String groupKeyPattern = FuzzyGroupKeyPattern.generatePattern("1service*", "2group*", "3namespace"); + String connectionId = "conn1234"; + namingFuzzyWatchContextService.syncFuzzyWatcherContext(groupKeyPattern, connectionId); + + //match service add + Service service = Service.newService("3namespace", "2group", "1service", true); + boolean needNotify = namingFuzzyWatchContextService.syncServiceContext(service, ADD_SERVICE); + Assertions.assertTrue(needNotify); + boolean needNotify2 = namingFuzzyWatchContextService.syncServiceContext(service, ADD_SERVICE); + Assertions.assertFalse(needNotify2); + + //check matched client and services + Set fuzzyWatchedClients = namingFuzzyWatchContextService.getFuzzyWatchedClients(service); + Assertions.assertTrue(fuzzyWatchedClients.contains(connectionId)); + Set matchServiceKeys = namingFuzzyWatchContextService.matchServiceKeys(groupKeyPattern); + Assertions.assertTrue(matchServiceKeys.contains( + NamingUtils.getServiceKey(service.getNamespace(), service.getGroup(), service.getName()))); + + //not match service add + Service serviceNotMatch = Service.newService("345namespace", "2group", "1service", true); + boolean needNotifyNotMatch = namingFuzzyWatchContextService.syncServiceContext(serviceNotMatch, ADD_SERVICE); + Assertions.assertFalse(needNotifyNotMatch); + + //match service add + Service serviceDRemove = service; + boolean needNotifyRemove = namingFuzzyWatchContextService.syncServiceContext(serviceDRemove, DELETE_SERVICE); + Assertions.assertTrue(needNotifyRemove); + boolean needNotify2Remove = namingFuzzyWatchContextService.syncServiceContext(serviceDRemove, DELETE_SERVICE); + Assertions.assertFalse(needNotify2Remove); + + } + + @Test + void testMakeupContextOnOverLoad() throws NacosException { + + tMockedStatic.when(() -> GlobalConfig.getMaxPatternCount()).thenReturn(20); + tMockedStatic.when(() -> GlobalConfig.getMaxMatchedServiceCount()).thenReturn(10); + + //mock services + Set serviceSet = new HashSet<>(); + for (int i = 0; i < 11; i++) { + Service service = Service.newService("namespace", "group" + i, "service" + i, true); + serviceSet.add(service); + } + serviceSet.add(Service.newService("namespace", "group", "12service", true)); + when(serviceManager.getSingletons(eq("namespace"))).thenReturn(serviceSet); + + String connectionId = "connection"; + String groupKeyPattern = FuzzyGroupKeyPattern.generatePattern("service*", "group*", "namespace"); + namingFuzzyWatchContextService.syncFuzzyWatcherContext(groupKeyPattern, connectionId); + Set matchServiceKeys = namingFuzzyWatchContextService.matchServiceKeys(groupKeyPattern); + Assertions.assertEquals(10, matchServiceKeys.size()); + + String serviceKeyToRemove = (String) (matchServiceKeys.toArray()[0]); + String[] parseServiceKey = NamingUtils.parseServiceKey(serviceKeyToRemove); + Service serviceToRemove = Service.newService(parseServiceKey[0], parseServiceKey[1], parseServiceKey[2]); + + Set serviceSet2 = new HashSet<>(serviceSet); + serviceSet2.remove(serviceToRemove); + when(serviceManager.getSingletons(eq("namespace"))).thenReturn(serviceSet2); + //delete on over load + boolean needNotify = namingFuzzyWatchContextService.syncServiceContext(serviceToRemove, DELETE_SERVICE); + Assertions.assertTrue(needNotify); + Set matchServiceKeys2 = namingFuzzyWatchContextService.matchServiceKeys(groupKeyPattern); + Assertions.assertFalse(matchServiceKeys2.contains(serviceKeyToRemove)); + Assertions.assertEquals(10, matchServiceKeys2.size()); + + } + + @Test + void testTrimContext() throws NacosException { + tMockedStatic.when(() -> GlobalConfig.getMaxPatternCount()).thenReturn(20); + tMockedStatic.when(() -> GlobalConfig.getMaxMatchedServiceCount()).thenReturn(5); + String groupKeyPattern = FuzzyGroupKeyPattern.generatePattern("service*", "group*", "namespace"); + + //mock services + Set serviceSet = new HashSet<>(); + for (int i = 0; i < 10; i++) { + Service service = Service.newService("namespace", "group" + i, "service" + i, true); + serviceSet.add(service); + } + when(serviceManager.getSingletons(eq("namespace"))).thenReturn(serviceSet); + String connectionId = "connection"; + namingFuzzyWatchContextService.syncFuzzyWatcherContext(groupKeyPattern, connectionId); + String connectionId2 = "connection22"; + namingFuzzyWatchContextService.syncFuzzyWatcherContext(groupKeyPattern, connectionId2); + Set matchServiceKeys = namingFuzzyWatchContextService.matchServiceKeys(groupKeyPattern); + // + namingFuzzyWatchContextService.trimFuzzyWatchContext(); + Set matchServiceKeys2 = namingFuzzyWatchContextService.matchServiceKeys(groupKeyPattern); + Assertions.assertEquals(matchServiceKeys, matchServiceKeys2); + + namingFuzzyWatchContextService.removeFuzzyWatchContext(groupKeyPattern, connectionId2); + namingFuzzyWatchContextService.onEvent( + new ClientOperationEvent.ClientReleaseEvent(new ConnectionBasedClient(connectionId, true, 0L), true)); + namingFuzzyWatchContextService.trimFuzzyWatchContext(); + Set matchServiceKeys3 = namingFuzzyWatchContextService.matchServiceKeys(groupKeyPattern); + Assertions.assertEquals(matchServiceKeys3, matchServiceKeys2); + namingFuzzyWatchContextService.trimFuzzyWatchContext(); + Set matchServiceKeys4 = namingFuzzyWatchContextService.matchServiceKeys(groupKeyPattern); + Assertions.assertEquals(0, matchServiceKeys4.size()); + + } +} diff --git a/naming/src/test/java/com/alibaba/nacos/naming/push/NamingFuzzyWatchChangeNotifierTest.java b/naming/src/test/java/com/alibaba/nacos/naming/push/NamingFuzzyWatchChangeNotifierTest.java new file mode 100644 index 00000000000..34874c6a5dd --- /dev/null +++ b/naming/src/test/java/com/alibaba/nacos/naming/push/NamingFuzzyWatchChangeNotifierTest.java @@ -0,0 +1,83 @@ +/* + * Copyright 1999-2020 Alibaba Group Holding Ltd. + * + * Licensed 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 com.alibaba.nacos.naming.push; + +import com.alibaba.nacos.naming.core.v2.event.service.ServiceEvent; +import com.alibaba.nacos.naming.core.v2.index.NamingFuzzyWatchContextService; +import com.alibaba.nacos.naming.core.v2.pojo.Service; +import com.alibaba.nacos.naming.push.v2.task.FuzzyWatchChangeNotifyTask; +import com.alibaba.nacos.naming.push.v2.task.FuzzyWatchPushDelayTaskEngine; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.HashSet; +import java.util.Set; + +import static com.alibaba.nacos.api.common.Constants.ServiceChangedType.ADD_SERVICE; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +public class NamingFuzzyWatchChangeNotifierTest { + + @Mock + private NamingFuzzyWatchContextService namingFuzzyWatchContextService; + + @Mock + private FuzzyWatchPushDelayTaskEngine fuzzyWatchPushDelayTaskEngine; + + NamingFuzzyWatchChangeNotifier namingFuzzyWatchChangeNotifier; + + @BeforeEach + void before() { + namingFuzzyWatchChangeNotifier = new NamingFuzzyWatchChangeNotifier(namingFuzzyWatchContextService, + fuzzyWatchPushDelayTaskEngine); + } + + @AfterEach + void after() { + } + + @Test + void testServiceChangedEvent() { + Service service = Service.newService("namespace12", "group", "service12345"); + + when(namingFuzzyWatchContextService.syncServiceContext(eq(service), eq(ADD_SERVICE))).thenReturn(true); + + Set set = new HashSet(); + set.add("2345123"); + set.add("23453"); + set.add("234535"); + + when(namingFuzzyWatchContextService.getFuzzyWatchedClients(eq(service))).thenReturn(set); + + ServiceEvent.ServiceChangedEvent serviceChangedEvent = new ServiceEvent.ServiceChangedEvent(service, + ADD_SERVICE); + namingFuzzyWatchChangeNotifier.onEvent(serviceChangedEvent); + + verify(fuzzyWatchPushDelayTaskEngine, times(set.size())).addTask(anyString(), + any(FuzzyWatchChangeNotifyTask.class)); + } +} diff --git a/naming/src/test/java/com/alibaba/nacos/naming/push/NamingFuzzyWatchSyncNotifierTest.java b/naming/src/test/java/com/alibaba/nacos/naming/push/NamingFuzzyWatchSyncNotifierTest.java new file mode 100644 index 00000000000..2022f038438 --- /dev/null +++ b/naming/src/test/java/com/alibaba/nacos/naming/push/NamingFuzzyWatchSyncNotifierTest.java @@ -0,0 +1,165 @@ +/* + * Copyright 1999-2020 Alibaba Group Holding Ltd. + * + * Licensed 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 com.alibaba.nacos.naming.push; + +import com.alibaba.nacos.api.naming.utils.NamingUtils; +import com.alibaba.nacos.common.utils.FuzzyGroupKeyPattern; +import com.alibaba.nacos.naming.core.v2.event.client.ClientOperationEvent; +import com.alibaba.nacos.naming.core.v2.index.NamingFuzzyWatchContextService; +import com.alibaba.nacos.naming.push.v2.task.FuzzyWatchPushDelayTaskEngine; +import com.alibaba.nacos.naming.push.v2.task.FuzzyWatchSyncNotifyTask; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.HashSet; +import java.util.Set; + +import static com.alibaba.nacos.naming.push.NamingFuzzyWatchSyncNotifier.BATCH_SIZE; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +public class NamingFuzzyWatchSyncNotifierTest { + + @Mock + private NamingFuzzyWatchContextService namingFuzzyWatchContextService; + + @Mock + private FuzzyWatchPushDelayTaskEngine fuzzyWatchPushDelayTaskEngine; + + NamingFuzzyWatchSyncNotifier namingFuzzyWatchSyncNotifier; + + @BeforeEach + void before() { + namingFuzzyWatchSyncNotifier = new NamingFuzzyWatchSyncNotifier(namingFuzzyWatchContextService, + fuzzyWatchPushDelayTaskEngine); + } + + @AfterEach + void after() { + } + + @Test + void testOnClientFuzzyWatchEventInit() { + + String groupKeyPattern = FuzzyGroupKeyPattern.generatePattern("service*", "group123", "namespace"); + + Set clientReceivedServiceKeys = new HashSet<>(); + for (int i = 0; i < BATCH_SIZE * 2; i++) { + clientReceivedServiceKeys.add(NamingUtils.getServiceKey("namespace", "group123" + i, "service" + i)); + } + Set matchedServiceKeys = new HashSet<>(); + for (int i = BATCH_SIZE; i < BATCH_SIZE * 3; i++) { + matchedServiceKeys.add(NamingUtils.getServiceKey("namespace", "group123" + i, "service" + i)); + } + + when(namingFuzzyWatchContextService.matchServiceKeys(eq(groupKeyPattern))).thenReturn(matchedServiceKeys); + + when(namingFuzzyWatchContextService.reachToUpLimit(eq(groupKeyPattern))).thenReturn(false); + String clientId = "onn1234"; + boolean isInitializing = true; + ClientOperationEvent.ClientFuzzyWatchEvent clientFuzzyWatchEvent = new ClientOperationEvent.ClientFuzzyWatchEvent( + groupKeyPattern, clientId, clientReceivedServiceKeys, isInitializing); + namingFuzzyWatchSyncNotifier.onEvent(clientFuzzyWatchEvent); + + verify(fuzzyWatchPushDelayTaskEngine, times(2)).addTask(anyString(), any(FuzzyWatchSyncNotifyTask.class)); + } + + @Test + void testOnClientFuzzyWatchEventInitFinish() { + + String groupKeyPattern = FuzzyGroupKeyPattern.generatePattern("service*", "group123", "namespace"); + + String clientId = "onn1234"; + Set clientReceivedServiceKeys = new HashSet<>(); + + boolean isInitializing = true; + + Set matchedServiceKeys = new HashSet<>(); + + when(namingFuzzyWatchContextService.matchServiceKeys(eq(groupKeyPattern))).thenReturn(matchedServiceKeys); + + when(namingFuzzyWatchContextService.reachToUpLimit(eq(groupKeyPattern))).thenReturn(false); + ClientOperationEvent.ClientFuzzyWatchEvent clientFuzzyWatchEvent = new ClientOperationEvent.ClientFuzzyWatchEvent( + groupKeyPattern, clientId, clientReceivedServiceKeys, isInitializing); + namingFuzzyWatchSyncNotifier.onEvent(clientFuzzyWatchEvent); + + verify(fuzzyWatchPushDelayTaskEngine, times(1)).addTask(anyString(), any(FuzzyWatchSyncNotifyTask.class)); + } + + @Test + void testOnClientFuzzyWatchEventDiffSync() { + + String groupKeyPattern = FuzzyGroupKeyPattern.generatePattern("service*", "group123", "namespace"); + + Set clientReceivedServiceKeys = new HashSet<>(); + for (int i = 0; i < BATCH_SIZE * 2; i++) { + clientReceivedServiceKeys.add(NamingUtils.getServiceKey("namespace", "group123" + i, "service" + i)); + } + + Set matchedServiceKeys = new HashSet<>(); + for (int i = BATCH_SIZE; i < BATCH_SIZE * 3; i++) { + matchedServiceKeys.add(NamingUtils.getServiceKey("namespace", "group123" + i, "service" + i)); + } + + when(namingFuzzyWatchContextService.matchServiceKeys(eq(groupKeyPattern))).thenReturn(matchedServiceKeys); + when(namingFuzzyWatchContextService.reachToUpLimit(eq(groupKeyPattern))).thenReturn(false); + String clientId = "onn1234"; + boolean isInitializing = false; + ClientOperationEvent.ClientFuzzyWatchEvent clientFuzzyWatchEvent = new ClientOperationEvent.ClientFuzzyWatchEvent( + groupKeyPattern, clientId, clientReceivedServiceKeys, isInitializing); + namingFuzzyWatchSyncNotifier.onEvent(clientFuzzyWatchEvent); + verify(fuzzyWatchPushDelayTaskEngine, times(2)).addTask(anyString(), any(FuzzyWatchSyncNotifyTask.class)); + + } + + @Test + void testOnClientFuzzyWatchEventWhenOverLoadModel() { + + String groupKeyPattern = FuzzyGroupKeyPattern.generatePattern("service*", "group123", "namespace"); + + Set clientReceivedServiceKeys = new HashSet<>(); + for (int i = 0; i < BATCH_SIZE * 2; i++) { + clientReceivedServiceKeys.add(NamingUtils.getServiceKey("namespace", "group123" + i, "service" + i)); + } + + Set matchedServiceKeys = new HashSet<>(); + for (int i = BATCH_SIZE; i < BATCH_SIZE * 3; i++) { + matchedServiceKeys.add(NamingUtils.getServiceKey("namespace", "group123" + i, "service" + i)); + } + + when(namingFuzzyWatchContextService.matchServiceKeys(eq(groupKeyPattern))).thenReturn(matchedServiceKeys); + + when(namingFuzzyWatchContextService.reachToUpLimit(eq(groupKeyPattern))).thenReturn(true); + String clientId = "onn1234"; + boolean isInitializing = false; + + ClientOperationEvent.ClientFuzzyWatchEvent clientFuzzyWatchEvent = new ClientOperationEvent.ClientFuzzyWatchEvent( + groupKeyPattern, clientId, clientReceivedServiceKeys, isInitializing); + namingFuzzyWatchSyncNotifier.onEvent(clientFuzzyWatchEvent); + verify(fuzzyWatchPushDelayTaskEngine, times(1)).addTask(anyString(), any(FuzzyWatchSyncNotifyTask.class)); + + } +} diff --git a/naming/src/test/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchSyncNotifyExecuteTaskTest.java b/naming/src/test/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchSyncNotifyExecuteTaskTest.java new file mode 100644 index 00000000000..2f254377c73 --- /dev/null +++ b/naming/src/test/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchSyncNotifyExecuteTaskTest.java @@ -0,0 +1,134 @@ +/* + * Copyright 1999-2023 Alibaba Group Holding Ltd. + * + * Licensed 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 com.alibaba.nacos.naming.push.v2.task; + +import com.alibaba.nacos.api.exception.runtime.NacosRuntimeException; +import com.alibaba.nacos.api.naming.remote.request.NamingFuzzyWatchSyncRequest; +import com.alibaba.nacos.api.naming.utils.NamingUtils; +import com.alibaba.nacos.common.task.BatchTaskCounter; +import com.alibaba.nacos.common.utils.FuzzyGroupKeyPattern; +import com.alibaba.nacos.naming.push.v2.executor.PushExecutor; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.HashSet; +import java.util.Set; + +import static com.alibaba.nacos.api.common.Constants.FINISH_FUZZY_WATCH_INIT_NOTIFY; +import static com.alibaba.nacos.api.common.Constants.FUZZY_WATCH_DIFF_SYNC_NOTIFY; +import static com.alibaba.nacos.api.common.Constants.FUZZY_WATCH_INIT_NOTIFY; +import static com.alibaba.nacos.api.common.Constants.ServiceChangedType.ADD_SERVICE; +import static com.alibaba.nacos.naming.push.v2.task.FuzzyWatchPushDelayTaskEngine.getTaskKey; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +public class FuzzyWatchSyncNotifyExecuteTaskTest { + + @Mock + FuzzyWatchPushDelayTaskEngine fuzzyWatchPushDelayTaskEngine; + + @Mock + PushExecutor pushExecutor; + + @Test + void testSyncNotifyRun() { + + when(fuzzyWatchPushDelayTaskEngine.getPushExecutor()).thenReturn(pushExecutor); + + String groupKeyPattern = FuzzyGroupKeyPattern.generatePattern("service*", "group123", "namespace"); + Set matchedServiceKeys = new HashSet<>(); + for (int i = 0; i < 5; i++) { + matchedServiceKeys.add(NamingFuzzyWatchSyncRequest.Context.build( + NamingUtils.getServiceKey("namespace", "group123" + i, "service" + i), ADD_SERVICE)); + } + String clientId = "conntion1234"; + + FuzzyWatchSyncNotifyTask fuzzyWatchSyncNotifyTask = new FuzzyWatchSyncNotifyTask(clientId, groupKeyPattern, + FUZZY_WATCH_DIFF_SYNC_NOTIFY, matchedServiceKeys, 0L); + FuzzyWatchSyncNotifyExecuteTask fuzzyWatchSyncNotifyExecuteTask = new FuzzyWatchSyncNotifyExecuteTask(clientId, + groupKeyPattern, fuzzyWatchPushDelayTaskEngine, fuzzyWatchSyncNotifyTask); + fuzzyWatchSyncNotifyExecuteTask.run(); + verify(pushExecutor, times(1)).doFuzzyWatchNotifyPushWithCallBack(eq(clientId), + any(NamingFuzzyWatchSyncRequest.class), any(FuzzyWatchSyncNotifyCallback.class)); + } + + @Test + void testCallbackSuccessForInitNotify() { + + String groupKeyPattern = FuzzyGroupKeyPattern.generatePattern("service*", "group123", "namespace"); + Set matchedServiceKeys = new HashSet<>(); + for (int i = 0; i < 5; i++) { + matchedServiceKeys.add(NamingFuzzyWatchSyncRequest.Context.build( + NamingUtils.getServiceKey("namespace", "group123" + i, "service" + i), ADD_SERVICE)); + } + String clientId = "conntion1234"; + + BatchTaskCounter batchTaskCounter = new BatchTaskCounter(5); + for (int i = 1; i <= 5; i++) { + FuzzyWatchSyncNotifyTask fuzzyWatchSyncNotifyTask = new FuzzyWatchSyncNotifyTask(clientId, groupKeyPattern, + FUZZY_WATCH_INIT_NOTIFY, matchedServiceKeys, 0L); + fuzzyWatchSyncNotifyTask.setTotalBatch(5); + fuzzyWatchSyncNotifyTask.setCurrentBatch(i); + FuzzyWatchSyncNotifyCallback fuzzyWatchSyncNotifyCallback = new FuzzyWatchSyncNotifyCallback( + fuzzyWatchSyncNotifyTask, batchTaskCounter, fuzzyWatchPushDelayTaskEngine); + fuzzyWatchSyncNotifyCallback.onSuccess(); + } + //check batch completed + Assertions.assertEquals(true, batchTaskCounter.batchCompleted()); + //check add init notify finish task. + verify(fuzzyWatchPushDelayTaskEngine, times(1)).addTask(anyString(), any(FuzzyWatchSyncNotifyTask.class)); + } + + @Test + void testCallbackSuccessForInitFinishNotify() { + + String groupKeyPattern = FuzzyGroupKeyPattern.generatePattern("service*", "group123", "namespace"); + String clientId = "conntion1234"; + FuzzyWatchSyncNotifyTask fuzzyWatchSyncNotifyTask = new FuzzyWatchSyncNotifyTask(clientId, groupKeyPattern, + FINISH_FUZZY_WATCH_INIT_NOTIFY, null, 0L); + FuzzyWatchSyncNotifyCallback fuzzyWatchSyncNotifyCallback = new FuzzyWatchSyncNotifyCallback( + fuzzyWatchSyncNotifyTask, null, fuzzyWatchPushDelayTaskEngine); + + fuzzyWatchSyncNotifyCallback.onSuccess(); + //check add init notify finish task. + verify(fuzzyWatchPushDelayTaskEngine, times(0)).addTask(anyString(), any(FuzzyWatchSyncNotifyTask.class)); + } + + @Test + void testCallbackFail() { + + String groupKeyPattern = FuzzyGroupKeyPattern.generatePattern("service*", "group123", "namespace"); + String clientId = "conntion1234"; + FuzzyWatchSyncNotifyTask fuzzyWatchSyncNotifyTask = new FuzzyWatchSyncNotifyTask(clientId, groupKeyPattern, + FINISH_FUZZY_WATCH_INIT_NOTIFY, null, 0L); + FuzzyWatchSyncNotifyCallback fuzzyWatchSyncNotifyCallback = new FuzzyWatchSyncNotifyCallback( + fuzzyWatchSyncNotifyTask, null, fuzzyWatchPushDelayTaskEngine); + + fuzzyWatchSyncNotifyCallback.onFail(new NacosRuntimeException(500, "exception")); + //check add init notify finish task. + verify(fuzzyWatchPushDelayTaskEngine, times(1)).addTask(eq(getTaskKey(fuzzyWatchSyncNotifyTask)), + eq(fuzzyWatchSyncNotifyTask)); + } +} From 6acef3a84380544e8c260fe514a4af62003b4617 Mon Sep 17 00:00:00 2001 From: "zunfei.lzf" Date: Thu, 16 Jan 2025 15:16:10 +0800 Subject: [PATCH 8/8] pmd fix --- naming/src/main/java/com/alibaba/nacos/naming/core/Operator.java | 1 + 1 file changed, 1 insertion(+) diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/Operator.java b/naming/src/main/java/com/alibaba/nacos/naming/core/Operator.java index 0ea46af2693..fd503d31964 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/core/Operator.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/core/Operator.java @@ -21,6 +21,7 @@ /** * Operator service. + * @author Metthew */ public interface Operator {