From 17bd04eae758d5c04d2e15daadb057e3c4068c3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=98=BF=E9=AD=81?= <670569467@qq.com> Date: Fri, 27 Dec 2024 10:21:15 +0800 Subject: [PATCH 01/15] Config fuzzy watch support (#11856) Nacos config supports fuzzy watch. by 670569467@qq.com --- .../alibaba/nacos/api/common/Constants.java | 16 + .../nacos/api/config/ConfigService.java | 86 ++- .../listener/AbstractFuzzyListenListener.java | 98 +++ .../FuzzyListenConfigChangeEvent.java | 109 +++ .../AbstractFuzzyListenNotifyRequest.java | 52 ++ .../ConfigBatchFuzzyListenRequest.java | 233 +++++++ .../FuzzyListenNotifyChangeRequest.java | 113 +++ .../request/FuzzyListenNotifyDiffRequest.java | 191 ++++++ .../ConfigBatchFuzzyListenResponse.java | 29 + .../FuzzyListenNotifyChangeResponse.java | 29 + .../FuzzyListenNotifyDiffResponse.java | 29 + .../com.alibaba.nacos.api.remote.Payload | 8 +- .../client/config/NacosConfigService.java | 106 ++- .../client/config/impl/ClientWorker.java | 646 +++++++++++++++++- .../config/impl/ConfigTransportClient.java | 27 +- .../config/impl/FuzzyListenContext.java | 447 ++++++++++++ .../config/impl/FuzzyListenNotifyEvent.java | 200 ++++++ .../alibaba/nacos/client/utils/ParamUtil.java | 35 +- .../client/config/NacosConfigServiceTest.java | 43 +- .../alibaba/nacos/common/utils/GroupKey.java | 140 ++++ .../nacos/common/utils/GroupKeyPattern.java | 259 +++++++ .../common/utils/GroupKeyPatternTest.java | 136 ++++ .../configuration/ConfigCommonConfig.java | 19 + .../nacos/config/server/model/CacheItem.java | 16 + .../event/ConfigBatchFuzzyListenEvent.java | 142 ++++ .../ConfigBatchFuzzyListenRequestHandler.java | 107 +++ .../remote/ConfigChangeListenContext.java | 107 ++- .../RpcFuzzyListenConfigChangeNotifier.java | 216 ++++++ .../RpcFuzzyListenConfigDiffNotifier.java | 488 +++++++++++++ .../server/service/ConfigCacheService.java | 41 +- ...atchFuzzyListenRequestParamsExtractor.java | 73 ++ .../nacos/example/FuzzyListenExample.java | 98 +++ 32 files changed, 4270 insertions(+), 69 deletions(-) create mode 100644 api/src/main/java/com/alibaba/nacos/api/config/listener/AbstractFuzzyListenListener.java create mode 100644 api/src/main/java/com/alibaba/nacos/api/config/listener/FuzzyListenConfigChangeEvent.java create mode 100644 api/src/main/java/com/alibaba/nacos/api/config/remote/request/AbstractFuzzyListenNotifyRequest.java create mode 100644 api/src/main/java/com/alibaba/nacos/api/config/remote/request/ConfigBatchFuzzyListenRequest.java create mode 100644 api/src/main/java/com/alibaba/nacos/api/config/remote/request/FuzzyListenNotifyChangeRequest.java create mode 100644 api/src/main/java/com/alibaba/nacos/api/config/remote/request/FuzzyListenNotifyDiffRequest.java create mode 100644 api/src/main/java/com/alibaba/nacos/api/config/remote/response/ConfigBatchFuzzyListenResponse.java create mode 100644 api/src/main/java/com/alibaba/nacos/api/config/remote/response/FuzzyListenNotifyChangeResponse.java create mode 100644 api/src/main/java/com/alibaba/nacos/api/config/remote/response/FuzzyListenNotifyDiffResponse.java create mode 100644 client/src/main/java/com/alibaba/nacos/client/config/impl/FuzzyListenContext.java create mode 100644 client/src/main/java/com/alibaba/nacos/client/config/impl/FuzzyListenNotifyEvent.java create mode 100644 common/src/main/java/com/alibaba/nacos/common/utils/GroupKey.java create mode 100644 common/src/main/java/com/alibaba/nacos/common/utils/GroupKeyPattern.java create mode 100644 common/src/test/java/com/alibaba/nacos/common/utils/GroupKeyPatternTest.java create mode 100644 config/src/main/java/com/alibaba/nacos/config/server/model/event/ConfigBatchFuzzyListenEvent.java create mode 100644 config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigBatchFuzzyListenRequestHandler.java create mode 100644 config/src/main/java/com/alibaba/nacos/config/server/remote/RpcFuzzyListenConfigChangeNotifier.java create mode 100644 config/src/main/java/com/alibaba/nacos/config/server/remote/RpcFuzzyListenConfigDiffNotifier.java create mode 100644 core/src/main/java/com/alibaba/nacos/core/paramcheck/impl/ConfigBatchFuzzyListenRequestParamsExtractor.java create mode 100644 example/src/main/java/com/alibaba/nacos/example/FuzzyListenExample.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 8cd9f940c61..509972f07f0 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 @@ -99,6 +99,10 @@ public class Constants { public static final Integer CLUSTER_GRPC_PORT_DEFAULT_OFFSET = 1001; + public static final String NAMESPACE_ID_SPLITTER = ">>"; + + public static final String DATA_ID_SPLITTER = "@@"; + /** * second. */ @@ -208,6 +212,8 @@ public class Constants { public static final String ALL_PATTERN = "*"; + public static final String FUZZY_LISTEN_PATTERN_WILDCARD = "*"; + public static final String COLON = ":"; public static final String LINE_BREAK = "\n"; @@ -231,6 +237,16 @@ public class Constants { public static final int DEFAULT_REDO_THREAD_COUNT = 1; + public static class ConfigChangeType { + + public static final String ADD_CONFIG = "ADD_CONFIG"; + + public static final String DELETE_CONFIG = "DELETE_CONFIG"; + + public static final String FINISH_LISTEN_INIT = "FINISH_LISTEN_INIT"; + + public static final String LISTEN_INIT = "LISTEN_INIT"; + } public static final String APP_CONN_LABELS_KEY = "nacos.app.conn.labels"; public static final String DOT = "."; diff --git a/api/src/main/java/com/alibaba/nacos/api/config/ConfigService.java b/api/src/main/java/com/alibaba/nacos/api/config/ConfigService.java index 3adab5aa4a3..a1017860f61 100644 --- a/api/src/main/java/com/alibaba/nacos/api/config/ConfigService.java +++ b/api/src/main/java/com/alibaba/nacos/api/config/ConfigService.java @@ -17,9 +17,13 @@ package com.alibaba.nacos.api.config; import com.alibaba.nacos.api.config.filter.IConfigFilter; +import com.alibaba.nacos.api.config.listener.AbstractFuzzyListenListener; import com.alibaba.nacos.api.config.listener.Listener; import com.alibaba.nacos.api.exception.NacosException; +import java.util.Collection; +import java.util.concurrent.CompletableFuture; + /** * Config Service Interface. * @@ -59,8 +63,8 @@ String getConfigAndSignListener(String dataId, String group, long timeoutMs, Lis /** * Add a listener to the configuration, after the server modified the configuration, the client will use the * incoming listener callback. Recommended asynchronous processing, the application can implement the getExecutor - * method in the ManagerListener, provide a thread pool of execution. If not provided, use the main thread callback, May - * block other configurations or be blocked by other configurations. + * method in the ManagerListener, provide a thread pool of execution. If not provided, use the main thread callback, + * May block other configurations or be blocked by other configurations. * * @param dataId dataId * @param group group @@ -69,6 +73,78 @@ String getConfigAndSignListener(String dataId, String group, long timeoutMs, Lis */ void addListener(String dataId, String group, Listener listener) throws NacosException; + /** + * Add a fuzzy listener to the configuration. After the server modifies the configuration matching the specified + * fixed group name, the client will utilize the incoming fuzzy listener callback. Fuzzy listeners allow for + * pattern-based subscription to configurations, where the fixed group name represents the group and dataId patterns + * specified for subscription. + * + * @param fixedGroupName The fixed group name representing the group and dataId patterns to subscribe to. + * @param listener The fuzzy listener to be added. + * @throws NacosException NacosException + */ + void addFuzzyListener(String fixedGroupName, AbstractFuzzyListenListener listener) throws NacosException; + + /** + * Add a fuzzy listener to the configuration. After the server modifies the configuration matching the specified + * dataId pattern and fixed group name, the client will utilize the incoming fuzzy listener callback. Fuzzy + * listeners allow for pattern-based subscription to configurations. + * + * @param dataIdPattern The pattern to match dataIds for subscription. + * @param fixedGroupName The fixed group name representing the group and dataId patterns to subscribe to. + * @param listener The fuzzy listener to be added. + * @throws NacosException NacosException + */ + void addFuzzyListener(String dataIdPattern, String fixedGroupName, AbstractFuzzyListenListener listener) + throws NacosException; + + /** + * Add a fuzzy listener to the configuration and retrieve all configs that match the specified fixed group name. + * Fuzzy listeners allow for pattern-based subscription to configs, where the fixed group name represents the group + * and dataId patterns specified for subscription. + * + * @param fixedGroupName The fixed group name representing the group and dataId patterns to subscribe to. + * @param listener The fuzzy listener to be added. + * @return CompletableFuture containing collection of configs that match the specified fixed group name. + * @throws NacosException NacosException + */ + CompletableFuture> addFuzzyListenerAndGetConfigs(String fixedGroupName, + AbstractFuzzyListenListener listener) throws NacosException; + + /** + * Add a fuzzy listener to the configuration and retrieve all configs that match the specified dataId pattern and + * fixed group name. Fuzzy listeners allow for pattern-based subscription to configs. + * + * @param dataIdPattern The pattern to match dataIds for subscription. + * @param fixedGroupName The fixed group name representing the group and dataId patterns to subscribe to. + * @param listener The fuzzy listener to be added. + * @return CompletableFuture containing collection of configs that match the specified dataId pattern and fixed + * group name. + * @throws NacosException NacosException + */ + CompletableFuture> addFuzzyListenerAndGetConfigs(String dataIdPattern, String fixedGroupName, + AbstractFuzzyListenListener listener) throws NacosException; + + /** + * Cancel fuzzy listen and remove the event listener for a specified fixed group name. + * + * @param fixedGroupName The fixed group name for fuzzy watch. + * @param listener The event listener to be removed. + * @throws NacosException If an error occurs during the cancellation process. + */ + void cancelFuzzyListen(String fixedGroupName, AbstractFuzzyListenListener listener) throws NacosException; + + /** + * Cancel fuzzy listen and remove the event listener for a specified service name pattern and fixed group name. + * + * @param dataIdPatter The pattern to match dataId for fuzzy watch. + * @param fixedGroupName The fixed group name for fuzzy watch. + * @param listener The event listener to be removed. + * @throws NacosException If an error occurs during the cancellation process. + */ + void cancelFuzzyListen(String dataIdPatter, String fixedGroupName, AbstractFuzzyListenListener listener) + throws NacosException; + /** * Publish config. * @@ -144,10 +220,10 @@ boolean publishConfigCas(String dataId, String group, String content, String cas * @return whether health */ String getServerStatus(); - + /** - * add config filter. - * It is recommended to use {@link com.alibaba.nacos.api.config.filter.AbstractConfigFilter} to expand the filter. + * add config filter. It is recommended to use {@link com.alibaba.nacos.api.config.filter.AbstractConfigFilter} to + * expand the filter. * * @param configFilter filter * @since 2.3.0 diff --git a/api/src/main/java/com/alibaba/nacos/api/config/listener/AbstractFuzzyListenListener.java b/api/src/main/java/com/alibaba/nacos/api/config/listener/AbstractFuzzyListenListener.java new file mode 100644 index 00000000000..bd317884a48 --- /dev/null +++ b/api/src/main/java/com/alibaba/nacos/api/config/listener/AbstractFuzzyListenListener.java @@ -0,0 +1,98 @@ +/* + * 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; + +import java.util.Objects; + +/** + * AbstractFuzzyListenListener is an abstract class that provides basic functionality for listening to fuzzy + * configuration changes in Nacos. + * + * @author stone-98 + * @date 2024/3/4 + */ +public abstract class AbstractFuzzyListenListener extends AbstractListener { + + /** + * Unique identifier for the listener. + */ + private String uuid; + + /** + * Get the UUID (Unique Identifier) of the listener. + * + * @return The UUID of the listener + */ + public String getUuid() { + return uuid; + } + + /** + * Set the UUID (Unique Identifier) of the listener. + * + * @param uuid The UUID to be set + */ + public void setUuid(String uuid) { + this.uuid = uuid; + } + + /** + * Callback method invoked when a fuzzy configuration change event occurs. + * + * @param event The fuzzy configuration change event + */ + public abstract void onEvent(FuzzyListenConfigChangeEvent event); + + /** + * Receive the configuration information. This method is overridden but does nothing in this abstract class. + * + * @param configInfo The configuration information + */ + @Override + public void receiveConfigInfo(String configInfo) { + // Do nothing by default + } + + /** + * Compute the hash code for this listener based on its UUID. + * + * @return The hash code value for this listener + */ + @Override + public int hashCode() { + return Objects.hashCode(uuid); + } + + /** + * Compare this listener to the specified object for equality. Two listeners are considered equal if they have the + * same UUID. + * + * @param o The object to compare to + * @return true if the specified object is equal to this listener, false otherwise + */ + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + AbstractFuzzyListenListener that = (AbstractFuzzyListenListener) o; + return Objects.equals(uuid, that.uuid); + } +} diff --git a/api/src/main/java/com/alibaba/nacos/api/config/listener/FuzzyListenConfigChangeEvent.java b/api/src/main/java/com/alibaba/nacos/api/config/listener/FuzzyListenConfigChangeEvent.java new file mode 100644 index 00000000000..6e2c3e319ca --- /dev/null +++ b/api/src/main/java/com/alibaba/nacos/api/config/listener/FuzzyListenConfigChangeEvent.java @@ -0,0 +1,109 @@ +/* + * 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; + +/** + * Represents a fuzzy listening configuration change event. + * + *

This event indicates that a change has occurred in a configuration that matches a fuzzy listening pattern. + * + * @author stone-98 + * @date 2024/3/12 + */ +public class FuzzyListenConfigChangeEvent { + + /** + * The group of the configuration that has changed. + */ + private String group; + + /** + * The data ID of the configuration that has changed. + */ + private String dataId; + + /** + * The type of change that has occurred (e.g., "ADD_CONFIG", "DELETE_CONFIG"). + */ + private String type; + + /** + * Constructs an empty FuzzyListenConfigChangeEvent. + */ + public FuzzyListenConfigChangeEvent() { + } + + /** + * Constructs a FuzzyListenConfigChangeEvent with the specified parameters. + * + * @param group The group of the configuration that has changed + * @param dataId The data ID of the configuration that has changed + * @param type The type of change that has occurred + */ + public FuzzyListenConfigChangeEvent(String group, String dataId, String type) { + this.group = group; + this.dataId = dataId; + this.type = type; + } + + /** + * Constructs and returns a new FuzzyListenConfigChangeEvent with the specified parameters. + * + * @param group The group of the configuration that has changed + * @param dataId The data ID of the configuration that has changed + * @param type The type of change that has occurred + * @return A new FuzzyListenConfigChangeEvent instance + */ + public static FuzzyListenConfigChangeEvent build(String group, String dataId, String type) { + return new FuzzyListenConfigChangeEvent(group, dataId, type); + } + + public String getGroup() { + return group; + } + + public void setGroup(String group) { + this.group = group; + } + + public String getDataId() { + return dataId; + } + + public void setDataId(String dataId) { + this.dataId = dataId; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + /** + * Returns a string representation of the FuzzyListenConfigChangeEvent. + * + * @return A string representation of the event + */ + @Override + public String toString() { + return "FuzzyListenConfigChangeEvent{" + "group='" + group + '\'' + ", dataId='" + dataId + '\'' + ", type='" + + type + '\'' + '}'; + } +} diff --git a/api/src/main/java/com/alibaba/nacos/api/config/remote/request/AbstractFuzzyListenNotifyRequest.java b/api/src/main/java/com/alibaba/nacos/api/config/remote/request/AbstractFuzzyListenNotifyRequest.java new file mode 100644 index 00000000000..1ba2b7bda14 --- /dev/null +++ b/api/src/main/java/com/alibaba/nacos/api/config/remote/request/AbstractFuzzyListenNotifyRequest.java @@ -0,0 +1,52 @@ +/* + * 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.remote.request; + +import com.alibaba.nacos.api.remote.request.ServerRequest; + +import static com.alibaba.nacos.api.common.Constants.Config.CONFIG_MODULE; + +/** + * AbstractFuzzyListenNotifyRequest. + * + * @author stone-98 + * @date 2024/3/14 + */ +public abstract class AbstractFuzzyListenNotifyRequest extends ServerRequest { + + private String serviceChangedType; + + public AbstractFuzzyListenNotifyRequest() { + } + + public AbstractFuzzyListenNotifyRequest(String serviceChangedType) { + this.serviceChangedType = serviceChangedType; + } + + public String getServiceChangedType() { + return serviceChangedType; + } + + public void setServiceChangedType(String serviceChangedType) { + this.serviceChangedType = serviceChangedType; + } + + @Override + public String getModule() { + return CONFIG_MODULE; + } +} diff --git a/api/src/main/java/com/alibaba/nacos/api/config/remote/request/ConfigBatchFuzzyListenRequest.java b/api/src/main/java/com/alibaba/nacos/api/config/remote/request/ConfigBatchFuzzyListenRequest.java new file mode 100644 index 00000000000..fd5ce81e1cb --- /dev/null +++ b/api/src/main/java/com/alibaba/nacos/api/config/remote/request/ConfigBatchFuzzyListenRequest.java @@ -0,0 +1,233 @@ +/* + * 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.remote.request; + +import com.alibaba.nacos.api.common.Constants; +import com.alibaba.nacos.api.remote.request.Request; +import com.alibaba.nacos.api.utils.StringUtils; + +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; + +/** + * Represents a request for batch fuzzy listening configurations. + * + *

This request is used to request batch fuzzy listening configurations from the server. It contains a set of + * contexts, each representing a fuzzy listening context. + * + * @author stone-98 + * @date 2024/3/4 + */ +public class ConfigBatchFuzzyListenRequest extends Request { + + /** + * Set of fuzzy listening contexts. + */ + private Set contexts = new HashSet<>(); + + /** + * Constructs an empty ConfigBatchFuzzyListenRequest. + */ + public ConfigBatchFuzzyListenRequest() { + } + + /** + * Adds a new context to the request. + * + * @param tenant The namespace or tenant associated with the configurations + * @param group The group associated with the configurations + * @param dataIdPattern The pattern for matching data IDs + * @param dataIds Set of data IDs + * @param listen Flag indicating whether to listen for changes + * @param isInitializing Flag indicating whether the client is initializing + */ + public void addContext(String tenant, String group, String dataIdPattern, Set dataIds, boolean listen, + boolean isInitializing) { + contexts.add( + new Context(StringUtils.isEmpty(tenant) ? Constants.DEFAULT_NAMESPACE_ID : tenant, group, dataIdPattern, + dataIds, listen, isInitializing)); + } + + /** + * Get the set of fuzzy listening contexts. + * + * @return The set of contexts + */ + public Set getContexts() { + return contexts; + } + + /** + * Set the set of fuzzy listening contexts. + * + * @param contexts The set of contexts to be set + */ + public void setContexts(Set contexts) { + this.contexts = contexts; + } + + /** + * Get the module name for this request. + * + * @return The module name + */ + @Override + public String getModule() { + return Constants.Config.CONFIG_MODULE; + } + + /** + * Represents a fuzzy listening context. + */ + public static class Context { + + /** + * The namespace or tenant associated with the configurations. + */ + private String tenant; + + /** + * The group associated with the configurations. + */ + private String group; + + /** + * The pattern for matching data IDs. + */ + private String dataIdPattern; + + /** + * Set of data IDs. + */ + private Set dataIds; + + /** + * Flag indicating whether to listen for changes. + */ + private boolean listen; + + /** + * Flag indicating whether the client is initializing. + */ + private boolean isInitializing; + + /** + * Constructs an empty Context. + */ + public Context() { + } + + /** + * Constructs a Context with the specified parameters. + * + * @param tenant The namespace or tenant associated with the configurations + * @param group The group associated with the configurations + * @param dataIdPattern The pattern for matching data IDs + * @param dataIds Set of data IDs + * @param listen Flag indicating whether to listen for changes + * @param isInitializing Flag indicating whether the client is initializing + */ + public Context(String tenant, String group, String dataIdPattern, Set dataIds, boolean listen, + boolean isInitializing) { + this.tenant = tenant; + this.group = group; + this.dataIdPattern = dataIdPattern; + this.dataIds = dataIds; + this.listen = listen; + this.isInitializing = isInitializing; + } + + public String getTenant() { + return tenant; + } + + public void setTenant(String tenant) { + this.tenant = tenant; + } + + public String getGroup() { + return group; + } + + public void setGroup(String group) { + this.group = group; + } + + public String getDataIdPattern() { + return dataIdPattern; + } + + public void setDataIdPattern(String dataIdPattern) { + this.dataIdPattern = dataIdPattern; + } + + public Set getDataIds() { + return dataIds; + } + + public void setDataIds(Set dataIds) { + this.dataIds = dataIds; + } + + public boolean isListen() { + return listen; + } + + public void setListen(boolean listen) { + this.listen = listen; + } + + public boolean isInitializing() { + return isInitializing; + } + + public void setInitializing(boolean initializing) { + isInitializing = initializing; + } + + /** + * Indicates whether some other object is "equal to" this one. + * + * @param o The reference object with which to compare + * @return True if this object is the same as the obj argument, false otherwise + */ + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Context that = (Context) o; + return Objects.equals(tenant, that.tenant) && Objects.equals(group, that.group) && Objects.equals( + dataIdPattern, that.dataIdPattern) && Objects.equals(dataIds, that.dataIds) && Objects.equals( + listen, that.listen) && Objects.equals(isInitializing, that.isInitializing); + } + + /** + * Returns a hash code value for the object. + * + * @return A hash code value for this object + */ + @Override + public int hashCode() { + return Objects.hash(tenant, group, dataIdPattern, dataIds, listen, isInitializing); + } + } +} diff --git a/api/src/main/java/com/alibaba/nacos/api/config/remote/request/FuzzyListenNotifyChangeRequest.java b/api/src/main/java/com/alibaba/nacos/api/config/remote/request/FuzzyListenNotifyChangeRequest.java new file mode 100644 index 00000000000..b149d70689e --- /dev/null +++ b/api/src/main/java/com/alibaba/nacos/api/config/remote/request/FuzzyListenNotifyChangeRequest.java @@ -0,0 +1,113 @@ +/* + * 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.remote.request; + +/** + * Represents a request to notify changes in a fuzzy listening configuration. + * + *

This request is used to notify clients about changes in configurations that match fuzzy listening patterns. + * + * @author stone-98 + * @date 2024/3/13 + */ +public class FuzzyListenNotifyChangeRequest extends AbstractFuzzyListenNotifyRequest { + + /** + * The tenant of the configuration that has changed. + */ + private String tenant; + + /** + * The group of the configuration that has changed. + */ + private String group; + + /** + * The data ID of the configuration that has changed. + */ + private String dataId; + + /** + * Indicates whether the configuration exists or not. + */ + private boolean isExist; + + /** + * Constructs an empty FuzzyListenNotifyChangeRequest. + */ + public FuzzyListenNotifyChangeRequest() { + } + + /** + * Constructs a FuzzyListenNotifyChangeRequest with the specified parameters. + * + * @param tenant The tenant of the configuration that has changed + * @param group The group of the configuration that has changed + * @param dataId The data ID of the configuration that has changed + * @param isExist Indicates whether the configuration exists or not + */ + public FuzzyListenNotifyChangeRequest(String tenant, String group, String dataId, boolean isExist) { + this.tenant = tenant; + this.group = group; + this.dataId = dataId; + this.isExist = isExist; + } + + public String getTenant() { + return tenant; + } + + public void setTenant(String tenant) { + this.tenant = tenant; + } + + public String getGroup() { + return group; + } + + public void setGroup(String group) { + this.group = group; + } + + public String getDataId() { + return dataId; + } + + public void setDataId(String dataId) { + this.dataId = dataId; + } + + public boolean isExist() { + return isExist; + } + + public void setExist(boolean exist) { + isExist = exist; + } + + /** + * Returns a string representation of the FuzzyListenNotifyChangeRequest. + * + * @return A string representation of the request + */ + @Override + public String toString() { + return "FuzzyListenNotifyChangeRequest{" + "tenant='" + tenant + '\'' + ", group='" + group + '\'' + + ", dataId='" + dataId + '\'' + ", isExist=" + isExist + '}'; + } + +} diff --git a/api/src/main/java/com/alibaba/nacos/api/config/remote/request/FuzzyListenNotifyDiffRequest.java b/api/src/main/java/com/alibaba/nacos/api/config/remote/request/FuzzyListenNotifyDiffRequest.java new file mode 100644 index 00000000000..0c925a3dad9 --- /dev/null +++ b/api/src/main/java/com/alibaba/nacos/api/config/remote/request/FuzzyListenNotifyDiffRequest.java @@ -0,0 +1,191 @@ +/* + * 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.remote.request; + +import com.alibaba.nacos.api.common.Constants; + +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; + +/** + * Represents a request to notify the difference in configurations for fuzzy listening. + * + *

This request is used to notify clients about the difference in configurations that match fuzzy listening + * patterns. + * + * @author stone-98 + * @date 2024/3/6 + */ +public class FuzzyListenNotifyDiffRequest extends AbstractFuzzyListenNotifyRequest { + + /** + * The pattern used to match group keys for the configurations. + */ + private String groupKeyPattern; + + /** + * The set of contexts containing information about the configurations. + */ + private Set contexts; + + /** + * Constructs an empty FuzzyListenNotifyDiffRequest. + */ + public FuzzyListenNotifyDiffRequest() { + } + + /** + * Constructs a FuzzyListenNotifyDiffRequest with the specified parameters. + * + * @param serviceChangedType The type of service change + * @param groupKeyPattern The pattern used to match group keys for the configurations + * @param contexts The set of contexts containing information about the configurations + */ + public FuzzyListenNotifyDiffRequest(String serviceChangedType, String groupKeyPattern, Set contexts) { + super(serviceChangedType); + this.groupKeyPattern = groupKeyPattern; + this.contexts = contexts; + } + + /** + * Builds an initial FuzzyListenNotifyDiffRequest with the specified set of contexts and group key pattern. + * + * @param contexts The set of contexts containing information about the configurations + * @param groupKeyPattern The pattern used to match group keys for the configurations + * @return An initial FuzzyListenNotifyDiffRequest + */ + public static FuzzyListenNotifyDiffRequest buildInitRequest(Set contexts, String groupKeyPattern) { + return new FuzzyListenNotifyDiffRequest(Constants.ConfigChangeType.LISTEN_INIT, groupKeyPattern, contexts); + } + + /** + * Builds a final FuzzyListenNotifyDiffRequest with the specified group key pattern. + * + * @param groupKeyPattern The pattern used to match group keys for the configurations + * @return A final FuzzyListenNotifyDiffRequest + */ + public static FuzzyListenNotifyDiffRequest buildInitFinishRequest(String groupKeyPattern) { + return new FuzzyListenNotifyDiffRequest(Constants.ConfigChangeType.FINISH_LISTEN_INIT, groupKeyPattern, + new HashSet<>()); + } + + public String getGroupKeyPattern() { + return groupKeyPattern; + } + + public void setGroupKeyPattern(String groupKeyPattern) { + this.groupKeyPattern = groupKeyPattern; + } + + public Set getContexts() { + return contexts; + } + + public void setContexts(Set contexts) { + this.contexts = contexts; + } + + /** + * Represents context information about a configuration. + */ + public static class Context { + + private String tenant; + + private String group; + + private String dataId; + + private String type; + + /** + * Constructs an empty Context object. + */ + public Context() { + } + + /** + * Builds a new context object with the provided parameters. + * + * @param tenant The tenant associated with the configuration. + * @param group The group associated with the configuration. + * @param dataId The data ID of the configuration. + * @param type The type of the configuration change event. + * @return A new context object initialized with the provided parameters. + */ + public static Context build(String tenant, String group, String dataId, String type) { + Context context = new Context(); + context.setTenant(tenant); + context.setGroup(group); + context.setDataId(dataId); + context.setType(type); + return context; + } + + @Override + public int hashCode() { + return Objects.hash(tenant, group, dataId, tenant); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Context that = (Context) o; + return Objects.equals(tenant, that.tenant) && Objects.equals(group, that.group) && Objects.equals(dataId, + that.dataId) && Objects.equals(type, that.type); + } + + public String getTenant() { + return tenant; + } + + public void setTenant(String tenant) { + this.tenant = tenant; + } + + public String getGroup() { + return group; + } + + public void setGroup(String group) { + this.group = group; + } + + public String getDataId() { + return dataId; + } + + public void setDataId(String dataId) { + this.dataId = dataId; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + } + +} diff --git a/api/src/main/java/com/alibaba/nacos/api/config/remote/response/ConfigBatchFuzzyListenResponse.java b/api/src/main/java/com/alibaba/nacos/api/config/remote/response/ConfigBatchFuzzyListenResponse.java new file mode 100644 index 00000000000..f1eca4df7c5 --- /dev/null +++ b/api/src/main/java/com/alibaba/nacos/api/config/remote/response/ConfigBatchFuzzyListenResponse.java @@ -0,0 +1,29 @@ +/* + * 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.remote.response; + +import com.alibaba.nacos.api.remote.response.Response; + +/** + * ConfigBatchFuzzyListenResponse. + * + * @author stone-98 + * @date 2024/3/4 + */ +public class ConfigBatchFuzzyListenResponse extends Response { + +} diff --git a/api/src/main/java/com/alibaba/nacos/api/config/remote/response/FuzzyListenNotifyChangeResponse.java b/api/src/main/java/com/alibaba/nacos/api/config/remote/response/FuzzyListenNotifyChangeResponse.java new file mode 100644 index 00000000000..1e58ec3f7dd --- /dev/null +++ b/api/src/main/java/com/alibaba/nacos/api/config/remote/response/FuzzyListenNotifyChangeResponse.java @@ -0,0 +1,29 @@ +/* + * 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.remote.response; + +import com.alibaba.nacos.api.remote.response.Response; + +/** + * FuzzyListenNotifyChangeResponse. + * + * @author stone-98 + * @date 2024/3/18 + */ +public class FuzzyListenNotifyChangeResponse extends Response { + +} diff --git a/api/src/main/java/com/alibaba/nacos/api/config/remote/response/FuzzyListenNotifyDiffResponse.java b/api/src/main/java/com/alibaba/nacos/api/config/remote/response/FuzzyListenNotifyDiffResponse.java new file mode 100644 index 00000000000..672d8dfc800 --- /dev/null +++ b/api/src/main/java/com/alibaba/nacos/api/config/remote/response/FuzzyListenNotifyDiffResponse.java @@ -0,0 +1,29 @@ +/* + * 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.remote.response; + +import com.alibaba.nacos.api.remote.response.Response; + +/** + * FuzzyListenNotifyDiffResponse. + * + * @author stone-98 + * @date 2024/3/18 + */ +public class FuzzyListenNotifyDiffResponse extends Response { + +} diff --git a/api/src/main/resources/META-INF/services/com.alibaba.nacos.api.remote.Payload b/api/src/main/resources/META-INF/services/com.alibaba.nacos.api.remote.Payload index cbd1e87502a..eba07c73d27 100644 --- a/api/src/main/resources/META-INF/services/com.alibaba.nacos.api.remote.Payload +++ b/api/src/main/resources/META-INF/services/com.alibaba.nacos.api.remote.Payload @@ -57,4 +57,10 @@ com.alibaba.nacos.api.naming.remote.response.InstanceResponse com.alibaba.nacos.api.naming.remote.response.NotifySubscriberResponse com.alibaba.nacos.api.naming.remote.response.QueryServiceResponse com.alibaba.nacos.api.naming.remote.response.ServiceListResponse -com.alibaba.nacos.api.naming.remote.response.SubscribeServiceResponse \ No newline at end of file +com.alibaba.nacos.api.naming.remote.response.SubscribeServiceResponse +com.alibaba.nacos.api.config.remote.request.ConfigBatchFuzzyListenRequest +com.alibaba.nacos.api.config.remote.response.ConfigBatchFuzzyListenResponse +com.alibaba.nacos.api.config.remote.request.FuzzyListenNotifyChangeRequest +com.alibaba.nacos.api.config.remote.response.FuzzyListenNotifyChangeResponse +com.alibaba.nacos.api.config.remote.request.FuzzyListenNotifyDiffRequest +com.alibaba.nacos.api.config.remote.response.FuzzyListenNotifyDiffResponse diff --git a/client/src/main/java/com/alibaba/nacos/client/config/NacosConfigService.java b/client/src/main/java/com/alibaba/nacos/client/config/NacosConfigService.java index ba17c03bb1a..7e921e45ed7 100644 --- a/client/src/main/java/com/alibaba/nacos/client/config/NacosConfigService.java +++ b/client/src/main/java/com/alibaba/nacos/client/config/NacosConfigService.java @@ -21,6 +21,7 @@ import com.alibaba.nacos.api.config.ConfigService; import com.alibaba.nacos.api.config.ConfigType; import com.alibaba.nacos.api.config.filter.IConfigFilter; +import com.alibaba.nacos.api.config.listener.AbstractFuzzyListenListener; import com.alibaba.nacos.api.config.listener.Listener; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.client.config.filter.impl.ConfigFilterChainManager; @@ -28,6 +29,7 @@ import com.alibaba.nacos.client.config.filter.impl.ConfigResponse; import com.alibaba.nacos.client.config.http.ServerHttpAgent; import com.alibaba.nacos.client.config.impl.ClientWorker; +import com.alibaba.nacos.client.config.impl.FuzzyListenContext; import com.alibaba.nacos.client.config.impl.ConfigServerListManager; import com.alibaba.nacos.client.config.impl.LocalConfigInfoProcessor; import com.alibaba.nacos.client.config.impl.LocalEncryptedDataKeyProcessor; @@ -40,8 +42,13 @@ import com.alibaba.nacos.common.utils.StringUtils; import org.slf4j.Logger; +import java.util.Collection; import java.util.Collections; import java.util.Properties; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; + +import static com.alibaba.nacos.api.common.Constants.FUZZY_LISTEN_PATTERN_WILDCARD; /** * Config Impl. @@ -125,6 +132,87 @@ public void addListener(String dataId, String group, Listener listener) throws N worker.addTenantListeners(dataId, group, Collections.singletonList(listener)); } + @Override + public void addFuzzyListener(String fixedGroupName, AbstractFuzzyListenListener listener) throws NacosException { + doFuzzyListen(FUZZY_LISTEN_PATTERN_WILDCARD, fixedGroupName, listener); + } + + @Override + public void addFuzzyListener(String dataIdPattern, String fixedGroupName, AbstractFuzzyListenListener listener) + throws NacosException { + // only support prefix match right now + if (!dataIdPattern.endsWith(FUZZY_LISTEN_PATTERN_WILDCARD)) { + if (dataIdPattern.startsWith(FUZZY_LISTEN_PATTERN_WILDCARD)) { + throw new UnsupportedOperationException("Suffix matching for dataId is not supported yet." + + " It will be supported in future updates if needed."); + } else { + throw new UnsupportedOperationException( + "Illegal dataId pattern, please read the documentation and pass a valid pattern."); + } + } + doFuzzyListen(dataIdPattern, fixedGroupName, listener); + } + + @Override + public CompletableFuture> addFuzzyListenerAndGetConfigs(String fixedGroupName, + AbstractFuzzyListenListener listener) throws NacosException { + return doAddFuzzyListenerAndGetConfigs(FUZZY_LISTEN_PATTERN_WILDCARD, fixedGroupName, listener); + } + + @Override + public CompletableFuture> addFuzzyListenerAndGetConfigs(String dataIdPattern, + String fixedGroupName, AbstractFuzzyListenListener listener) throws NacosException { + return doAddFuzzyListenerAndGetConfigs(dataIdPattern, fixedGroupName, listener); + } + + private CompletableFuture> doAddFuzzyListenerAndGetConfigs(String dataIdPattern, + String fixedGroupName, AbstractFuzzyListenListener listener) throws NacosException { + CompletableFuture> future = new CompletableFuture<>(); + if (listener == null) { + future.completeExceptionally(new IllegalArgumentException("Listener cannot be null")); + return future; + } + addFuzzyListener(dataIdPattern, fixedGroupName, listener); + FuzzyListenContext context = worker.getFuzzyListenContext(dataIdPattern, fixedGroupName); + if (context == null) { + future.complete(Collections.emptyList()); + return future; + } + return context.waitForInitializationComplete(future); + } + + private void doFuzzyListen(String dataIdPattern, String fixedGroupName, AbstractFuzzyListenListener listener) + throws NacosException { + if (listener == null) { + return; + } + listener.setUuid(UUID.randomUUID().toString()); + if (!worker.containsPatternMatchCache(dataIdPattern, fixedGroupName)) { + worker.addTenantFuzzyListenListens(dataIdPattern, fixedGroupName, Collections.singletonList(listener)); + } else { + worker.duplicateFuzzyListenInit(dataIdPattern, fixedGroupName, listener); + } + } + + @Override + public void cancelFuzzyListen(String fixedGroupName, AbstractFuzzyListenListener listener) throws NacosException { + cancelFuzzyListen(FUZZY_LISTEN_PATTERN_WILDCARD, fixedGroupName, listener); + } + + @Override + public void cancelFuzzyListen(String dataIdPattern, String fixedGroupName, AbstractFuzzyListenListener listener) + throws NacosException { + doCancelFuzzyListen(dataIdPattern, fixedGroupName, listener); + } + + private void doCancelFuzzyListen(String dataIdPattern, String groupNamePattern, + AbstractFuzzyListenListener listener) throws NacosException { + if (null == listener) { + return; + } + worker.removeFuzzyListenListener(dataIdPattern, groupNamePattern, listener); + } + @Override public boolean publishConfig(String dataId, String group, String content) throws NacosException { return publishConfig(dataId, group, content, ConfigType.getDefaultType().getType()); @@ -176,8 +264,8 @@ private String getConfigInner(String tenant, String dataId, String group, long t LOGGER.warn("[{}] [get-config] get failover ok, dataId={}, group={}, tenant={}", worker.getAgentName(), dataId, group, tenant); cr.setContent(content); - String encryptedDataKey = LocalEncryptedDataKeyProcessor - .getEncryptDataKeyFailover(agent.getName(), dataId, group, tenant); + String encryptedDataKey = LocalEncryptedDataKeyProcessor.getEncryptDataKeyFailover(agent.getName(), dataId, + group, tenant); cr.setEncryptedDataKey(encryptedDataKey); configFilterChainManager.doFilter(null, cr); content = cr.getContent(); @@ -199,15 +287,15 @@ private String getConfigInner(String tenant, String dataId, String group, long t LOGGER.warn("[{}] [get-config] get from server error, dataId={}, group={}, tenant={}, msg={}", worker.getAgentName(), dataId, group, tenant, ioe.toString()); } - + content = LocalConfigInfoProcessor.getSnapshot(worker.getAgentName(), dataId, group, tenant); if (content != null) { LOGGER.warn("[{}] [get-config] get snapshot ok, dataId={}, group={}, tenant={}", worker.getAgentName(), dataId, group, tenant); } cr.setContent(content); - String encryptedDataKey = LocalEncryptedDataKeyProcessor - .getEncryptDataKeySnapshot(agent.getName(), dataId, group, tenant); + String encryptedDataKey = LocalEncryptedDataKeyProcessor.getEncryptDataKeySnapshot(agent.getName(), dataId, + group, tenant); cr.setEncryptedDataKey(encryptedDataKey); configFilterChainManager.doFilter(null, cr); content = cr.getContent(); @@ -239,8 +327,8 @@ private boolean publishConfigInner(String tenant, String dataId, String group, S content = cr.getContent(); String encryptedDataKey = cr.getEncryptedDataKey(); - return worker - .publishConfig(dataId, group, tenant, appName, tag, betaIps, content, encryptedDataKey, casMd5, type); + return worker.publishConfig(dataId, group, tenant, appName, tag, betaIps, content, encryptedDataKey, casMd5, + type); } @Override @@ -251,12 +339,12 @@ public String getServerStatus() { return DOWN; } } - + @Override public void addConfigFilter(IConfigFilter configFilter) { configFilterChainManager.addFilter(configFilter); } - + @Override public void shutDown() throws NacosException { worker.shutdown(); 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 a8629ecb0dc..b2ab88f7774 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 @@ -19,19 +19,26 @@ import com.alibaba.nacos.api.PropertyKeyConst; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.config.ConfigType; +import com.alibaba.nacos.api.config.listener.AbstractFuzzyListenListener; import com.alibaba.nacos.api.config.listener.Listener; import com.alibaba.nacos.api.config.remote.request.ClientConfigMetricRequest; +import com.alibaba.nacos.api.config.remote.request.ConfigBatchFuzzyListenRequest; import com.alibaba.nacos.api.config.remote.request.ConfigBatchListenRequest; import com.alibaba.nacos.api.config.remote.request.ConfigChangeNotifyRequest; import com.alibaba.nacos.api.config.remote.request.ConfigPublishRequest; import com.alibaba.nacos.api.config.remote.request.ConfigQueryRequest; import com.alibaba.nacos.api.config.remote.request.ConfigRemoveRequest; +import com.alibaba.nacos.api.config.remote.request.FuzzyListenNotifyChangeRequest; +import com.alibaba.nacos.api.config.remote.request.FuzzyListenNotifyDiffRequest; import com.alibaba.nacos.api.config.remote.response.ClientConfigMetricResponse; +import com.alibaba.nacos.api.config.remote.response.ConfigBatchFuzzyListenResponse; import com.alibaba.nacos.api.config.remote.response.ConfigChangeBatchListenResponse; import com.alibaba.nacos.api.config.remote.response.ConfigChangeNotifyResponse; import com.alibaba.nacos.api.config.remote.response.ConfigPublishResponse; import com.alibaba.nacos.api.config.remote.response.ConfigQueryResponse; import com.alibaba.nacos.api.config.remote.response.ConfigRemoveResponse; +import com.alibaba.nacos.api.config.remote.response.FuzzyListenNotifyChangeResponse; +import com.alibaba.nacos.api.config.remote.response.FuzzyListenNotifyDiffResponse; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.remote.RemoteConstants; import com.alibaba.nacos.api.remote.request.Request; @@ -65,6 +72,7 @@ import com.alibaba.nacos.common.remote.client.ServerListFactory; import com.alibaba.nacos.common.utils.ConnLabelsUtils; import com.alibaba.nacos.common.utils.ConvertUtils; +import com.alibaba.nacos.common.utils.GroupKeyPattern; import com.alibaba.nacos.common.utils.JacksonUtils; import com.alibaba.nacos.common.utils.MD5Utils; import com.alibaba.nacos.common.utils.StringUtils; @@ -84,6 +92,7 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Properties; import java.util.Set; import java.util.UUID; @@ -129,6 +138,11 @@ public class ClientWorker implements Closeable { */ private final AtomicReference> cacheMap = new AtomicReference<>(new HashMap<>()); + /** + * fuzzyListenGroupKey -> fuzzyListenContext. + */ + private final AtomicReference> fuzzyListenContextMap = new AtomicReference<>( + new HashMap<>()); private final DefaultLabelsCollectorManager defaultLabelsCollectorManager = new DefaultLabelsCollectorManager(); private Map appLables = new HashMap<>(); @@ -156,6 +170,49 @@ public class ClientWorker implements Closeable { */ private final List taskIdCacheCountList = new ArrayList<>(); + /** + * index(taskId)-> total context count for this taskId. + */ + private final List taskIdContextCountList = new ArrayList<>(); + + @SuppressWarnings("PMD.ThreadPoolCreationRule") + public ClientWorker(final ConfigFilterChainManager configFilterChainManager, ServerListManager serverListManager, + final NacosClientProperties properties) throws NacosException { + this.configFilterChainManager = configFilterChainManager; + + init(properties); + + agent = new ConfigRpcTransportClient(properties, serverListManager); + ScheduledExecutorService executorService = Executors.newScheduledThreadPool(initWorkerThreadCount(properties), + new NameThreadFactory("com.alibaba.nacos.client.Worker")); + agent.setExecutor(executorService); + agent.start(); + + } + + /** + * Adds a list of fuzzy listen listeners for the specified data ID pattern and group. + * + * @param dataIdPattern The pattern of the data ID to listen for. + * @param group The group of the configuration. + * @param listeners The list of listeners to add. + * @throws NacosException If an error occurs while adding the listeners. + */ + public void addTenantFuzzyListenListens(String dataIdPattern, String group, + List listeners) throws NacosException { + group = blank2defaultGroup(group); + FuzzyListenContext context = addFuzzyListenContextIfAbsent(dataIdPattern, group); + synchronized (context) { + for (AbstractFuzzyListenListener listener : listeners) { + context.addListener(listener); + } + context.setInitializing(true); + context.setDiscard(false); + context.getIsConsistentWithServer().set(false); + agent.notifyFuzzyListenConfig(); + } + } + /** * Add listeners for data. * @@ -286,19 +343,72 @@ public void removeTenantListener(String dataId, String group, Listener listener) } } - void removeCache(String dataId, String group, String tenant) { - String groupKey = GroupKey.getKeyTenant(dataId, group, tenant); - synchronized (cacheMap) { - Map copy = new HashMap<>(cacheMap.get()); - CacheData remove = copy.remove(groupKey); - if (remove != null) { - decreaseTaskIdCount(remove.getTaskId()); + /** + * Initializes a duplicate fuzzy listen for the specified data ID pattern, group, and listener. + * + * @param dataIdPattern The pattern of the data ID to listen for. + * @param group The group of the configuration. + * @param listener The listener to add. + */ + public void duplicateFuzzyListenInit(String dataIdPattern, String group, AbstractFuzzyListenListener listener) { + String groupKeyPattern = GroupKeyPattern.generateFuzzyListenGroupKeyPattern(dataIdPattern, group); + Map contextMap = fuzzyListenContextMap.get(); + FuzzyListenContext context = contextMap.get(groupKeyPattern); + if (Objects.isNull(context)) { + return; + } + synchronized (context) { + context.addListener(listener); + + for (String dataId : context.getDataIds()) { + NotifyCenter.publishEvent(FuzzyListenNotifyEvent.buildNotifyPatternSpecificListenerEvent(group, dataId, + Constants.ConfigChangeType.ADD_CONFIG, groupKeyPattern, listener.getUuid())); + } + } + } + + /** + * Removes a fuzzy listen listener for the specified data ID pattern, group, and listener. + * + * @param dataIdPattern The pattern of the data ID. + * @param group The group of the configuration. + * @param listener The listener to remove. + * @throws NacosException If an error occurs while removing the listener. + */ + public void removeFuzzyListenListener(String dataIdPattern, String group, AbstractFuzzyListenListener listener) + throws NacosException { + group = blank2defaultGroup(group); + FuzzyListenContext fuzzyListenContext = getFuzzyListenContext(dataIdPattern, group); + if (fuzzyListenContext != null) { + synchronized (fuzzyListenContext) { + fuzzyListenContext.removeListener(listener); + if (fuzzyListenContext.getListeners().isEmpty()) { + fuzzyListenContext.setDiscard(true); + fuzzyListenContext.getIsConsistentWithServer().set(false); + agent.removeFuzzyListenContext(dataIdPattern, group); + } } - cacheMap.set(copy); } - LOGGER.info("[{}] [unsubscribe] {}", agent.getName(), groupKey); - - MetricsMonitor.getListenConfigCountMonitor().set(cacheMap.get().size()); + } + + /** + * Removes the fuzzy listen context for the specified data ID pattern and group. + * + * @param dataIdPattern The pattern of the data ID. + * @param group The group of the configuration. + */ + public void removeFuzzyListenContext(String dataIdPattern, String group) { + String groupKeyPattern = GroupKeyPattern.generateFuzzyListenGroupKeyPattern(dataIdPattern, group); + synchronized (fuzzyListenContextMap) { + Map copy = new HashMap<>(fuzzyListenContextMap.get()); + FuzzyListenContext removedContext = copy.remove(groupKeyPattern); + if (removedContext != null) { + decreaseContextTaskIdCount(removedContext.getTaskId()); + } + fuzzyListenContextMap.set(copy); + } + LOGGER.info("[{}] [fuzzy-listen-unsubscribe] {}", agent.getName(), groupKeyPattern); + // TODO: Record metric for fuzzy listen unsubscribe. } /** @@ -425,6 +535,28 @@ public CacheData addCacheDataIfAbsent(String dataId, String group, String tenant return cache; } + /** + * Removes the cache entry associated with the given data ID, group, and tenant. + * + * @param dataId The data ID. + * @param group The group name. + * @param tenant The tenant. + */ + public void removeCache(String dataId, String group, String tenant) { + String groupKey = GroupKey.getKeyTenant(dataId, group, tenant); + synchronized (cacheMap) { + Map copy = new HashMap<>(cacheMap.get()); + CacheData remove = copy.remove(groupKey); + if (remove != null) { + decreaseTaskIdCount(remove.getTaskId()); + } + cacheMap.set(copy); + } + LOGGER.info("[{}] [unsubscribe] {}", agent.getName(), groupKey); + + MetricsMonitor.getListenConfigCountMonitor().set(cacheMap.get().size()); + } + /** * Put cache. * @@ -439,23 +571,114 @@ private void putCache(String key, CacheData cache) { } } + /** + * Adds a fuzzy listen context if it doesn't already exist for the specified data ID pattern and group. If the + * context already exists, returns the existing context. + * + * @param dataIdPattern The pattern of the data ID. + * @param group The group of the configuration. + * @return The fuzzy listen context for the specified data ID pattern and group. + */ + public FuzzyListenContext addFuzzyListenContextIfAbsent(String dataIdPattern, String group) { + FuzzyListenContext context = getFuzzyListenContext(dataIdPattern, group); + if (context != null) { + return context; + } + synchronized (fuzzyListenContextMap) { + FuzzyListenContext contextFromMap = getFuzzyListenContext(dataIdPattern, group); + if (contextFromMap != null) { + context = contextFromMap; + context.getIsConsistentWithServer().set(false); + } else { + context = new FuzzyListenContext(agent.getName(), dataIdPattern, group); + int taskId = calculateContextTaskId(); + increaseContextTaskIdCount(taskId); + context.setTaskId(taskId); + } + } + + Map copy = new HashMap<>(fuzzyListenContextMap.get()); + String groupKeyPattern = GroupKeyPattern.generateFuzzyListenGroupKeyPattern(dataIdPattern, group); + copy.put(groupKeyPattern, context); + fuzzyListenContextMap.set(copy); + + // TODO: Record metrics + + return context; + } + + /** + * Increases the count for the specified task ID in the given count list. + * + * @param taskId The ID of the task for which the count needs to be increased. + */ private void increaseTaskIdCount(int taskId) { - taskIdCacheCountList.get(taskId).incrementAndGet(); + increaseCount(taskId, taskIdCacheCountList); } + /** + * Decreases the count for the specified task ID in the given count list. + * + * @param taskId The ID of the task for which the count needs to be decreased. + */ private void decreaseTaskIdCount(int taskId) { - taskIdCacheCountList.get(taskId).decrementAndGet(); + decreaseCount(taskId, taskIdCacheCountList); } + /** + * Increases the context task ID count in the corresponding list. + * + * @param taskId The ID of the context task for which the count needs to be increased. + */ + private void increaseContextTaskIdCount(int taskId) { + increaseCount(taskId, taskIdContextCountList); + } + + /** + * Decreases the context task ID count in the corresponding list. + * + * @param taskId The ID of the context task for which the count needs to be decreased. + */ + private void decreaseContextTaskIdCount(int taskId) { + decreaseCount(taskId, taskIdContextCountList); + } + + /** + * Calculates the task ID based on the configuration size. + * + * @return The calculated task ID. + */ private int calculateTaskId() { - int perTaskSize = (int) ParamUtil.getPerTaskConfigSize(); - for (int index = 0; index < taskIdCacheCountList.size(); index++) { - if (taskIdCacheCountList.get(index).get() < perTaskSize) { - return index; - } - } - taskIdCacheCountList.add(new AtomicInteger(0)); - return taskIdCacheCountList.size() - 1; + return calculateId(taskIdCacheCountList, (long) ParamUtil.getPerTaskConfigSize()); + } + + /** + * Calculates the context task ID based on the configuration size. + * + * @return The calculated context task ID. + */ + private int calculateContextTaskId() { + return calculateId(taskIdContextCountList, (long) ParamUtil.getPerTaskContextSize()); + } + + /** + * Increases the count for the specified task ID in the given count list. + * + * @param taskId The ID of the task for which the count needs to be increased. + * @param countList The list containing the counts for different task IDs. + */ + private void increaseCount(int taskId, List countList) { + countList.get(taskId).incrementAndGet(); + } + + /** + * Decreases the count for the specified task ID in the given count list. + * + * @param taskId The ID of the task for which the count needs to be decreased. + * @param countList The list containing the counts for different task IDs. + */ + private void decreaseCount(int taskId, List countList) { + countList.get(taskId).decrementAndGet(); } public CacheData getCache(String dataId, String group) { @@ -469,6 +692,35 @@ public CacheData getCache(String dataId, String group, String tenant) { return cacheMap.get().get(GroupKey.getKeyTenant(dataId, group, tenant)); } + /** + * Calculates the task ID based on the provided count list and per-task size. + * + * @param countList The list containing the counts for different task IDs. + * @param perTaskSize The size of each task. + * @return The calculated task ID. + */ + private int calculateId(List countList, long perTaskSize) { + for (int index = 0; index < countList.size(); index++) { + if (countList.get(index).get() < perTaskSize) { + return index; + } + } + countList.add(new AtomicInteger(0)); + return countList.size() - 1; + } + + /** + * Retrieves the FuzzyListenContext for the given data ID pattern and group. + * + * @param dataIdPattern The data ID pattern. + * @param group The group name. + * @return The corresponding FuzzyListenContext, or null if not found. + */ + public FuzzyListenContext getFuzzyListenContext(String dataIdPattern, String group) { + return fuzzyListenContextMap.get() + .get(GroupKeyPattern.generateFuzzyListenGroupKeyPattern(dataIdPattern, group)); + } + public ConfigResponse getServerConfig(String dataId, String group, String tenant, long readTimeout, boolean notify) throws NacosException { if (StringUtils.isBlank(group)) { @@ -481,6 +733,19 @@ private String blank2defaultGroup(String group) { return StringUtils.isBlank(group) ? Constants.DEFAULT_GROUP : group.trim(); } + /** + * Checks if the pattern match cache contains an entry for the specified data ID pattern and group. + * + * @param dataIdPattern The data ID pattern. + * @param group The group name. + * @return True if the cache contains an entry, false otherwise. + */ + public boolean containsPatternMatchCache(String dataIdPattern, String group) { + Map contextMap = fuzzyListenContextMap.get(); + String groupKeyPattern = GroupKeyPattern.generateFuzzyListenGroupKeyPattern(dataIdPattern, group); + return contextMap.containsKey(groupKeyPattern); + } + @SuppressWarnings("PMD.ThreadPoolCreationRule") public ClientWorker(final ConfigFilterChainManager configFilterChainManager, ConfigServerListManager serverListManager, final NacosClientProperties properties) throws NacosException { @@ -587,14 +852,28 @@ public boolean isHealthServer() { public class ConfigRpcTransportClient extends ConfigTransportClient { - Map multiTaskExecutor = new HashMap<>(); + /** + * 5 minutes to check all fuzzy listen context. + */ + private static final long FUZZY_LISTEN_ALL_SYNC_INTERNAL = 5 * 60 * 1000L; + + private final String configListenerTaskPrefix = "nacos.client.config.listener.task"; + + private final String fuzzyListenerTaskPrefix = "nacos.client.config.fuzzyListener.task"; private final BlockingQueue listenExecutebell = new ArrayBlockingQueue<>(1); + private final Map multiTaskExecutor = new HashMap<>(); + private final Object bellItem = new Object(); private long lastAllSyncTime = System.currentTimeMillis(); + /** + * fuzzyListenExecuteBell. + */ + private final BlockingQueue fuzzyListenExecuteBell = new ArrayBlockingQueue<>(1); + Subscriber subscriber = null; /** @@ -602,6 +881,11 @@ public class ConfigRpcTransportClient extends ConfigTransportClient { */ private static final long ALL_SYNC_INTERNAL = 3 * 60 * 1000L; + /** + * fuzzyListenLastAllSyncTime. + */ + private long fuzzyListenLastAllSyncTime = System.currentTimeMillis(); + public ConfigRpcTransportClient(NacosClientProperties properties, ConfigServerListManager serverListManager) { super(properties, serverListManager); } @@ -665,6 +949,85 @@ private Map getLabels() { return labels; } + /** + * Handles a fuzzy listen init notify request. + * + *

This method processes the incoming fuzzy listen init notify request from a client. It updates the fuzzy + * listen context based on the request's information, and publishes events if necessary. + * + * @param request The fuzzy listen init notify request to handle. + * @param clientName The name of the client sending the request. + * @return A {@link FuzzyListenNotifyDiffResponse} indicating the result of handling the request. + */ + private FuzzyListenNotifyDiffResponse handleFuzzyListenNotifyDiffRequest(FuzzyListenNotifyDiffRequest request, + String clientName) { + LOGGER.info("[{}] [fuzzy-listen-config-push] config init.", clientName); + String groupKeyPattern = request.getGroupKeyPattern(); + FuzzyListenContext context = fuzzyListenContextMap.get().get(groupKeyPattern); + if (Constants.ConfigChangeType.FINISH_LISTEN_INIT.equals(request.getServiceChangedType())) { + context.markInitializationComplete(); + return new FuzzyListenNotifyDiffResponse(); + } + for (FuzzyListenNotifyDiffRequest.Context requestContext : request.getContexts()) { + Set existsDataIds = context.getDataIds(); + switch (requestContext.getType()) { + case Constants.ConfigChangeType.LISTEN_INIT: + case Constants.ConfigChangeType.ADD_CONFIG: + if (existsDataIds.add(requestContext.getDataId())) { + NotifyCenter.publishEvent(FuzzyListenNotifyEvent.buildNotifyPatternAllListenersEvent( + requestContext.getGroup(), requestContext.getDataId(), request.getGroupKeyPattern(), + Constants.ConfigChangeType.ADD_CONFIG)); + } + break; + case Constants.ConfigChangeType.DELETE_CONFIG: + if (existsDataIds.remove(requestContext.getDataId())) { + NotifyCenter.publishEvent(FuzzyListenNotifyEvent.buildNotifyPatternAllListenersEvent( + requestContext.getGroup(), requestContext.getDataId(), request.getGroupKeyPattern(), + Constants.ConfigChangeType.DELETE_CONFIG)); + } + break; + default: + LOGGER.error("Invalid config change type: {}", requestContext.getType()); + break; + } + } + return new FuzzyListenNotifyDiffResponse(); + } + + /** + * Handles a fuzzy listen notify change request. + * + *

This method processes the incoming fuzzy listen notify change request from a client. It updates the fuzzy + * listen context based on the request's information, and publishes events if necessary. + * + * @param request The fuzzy listen notify change request to handle. + * @param clientName The name of the client sending the request. + */ + private FuzzyListenNotifyChangeResponse handlerFuzzyListenNotifyChangeRequest( + FuzzyListenNotifyChangeRequest request, String clientName) { + LOGGER.info("[{}] [fuzzy-listen-config-push] config changed.", clientName); + Map listenContextMap = fuzzyListenContextMap.get(); + Set matchedPatterns = GroupKeyPattern.getConfigMatchedPatternsWithoutNamespace(request.getDataId(), + request.getGroup(), listenContextMap.keySet()); + for (String matchedPattern : matchedPatterns) { + FuzzyListenContext context = listenContextMap.get(matchedPattern); + if (request.isExist()) { + if (context.getDataIds().add(request.getDataId())) { + NotifyCenter.publishEvent( + FuzzyListenNotifyEvent.buildNotifyPatternAllListenersEvent(request.getGroup(), + request.getDataId(), matchedPattern, Constants.ConfigChangeType.ADD_CONFIG)); + } + } else { + if (context.getDataIds().remove(request.getDataId())) { + NotifyCenter.publishEvent( + FuzzyListenNotifyEvent.buildNotifyPatternAllListenersEvent(request.getGroup(), + request.getDataId(), matchedPattern, Constants.ConfigChangeType.DELETE_CONFIG)); + } + } + } + return new FuzzyListenNotifyChangeResponse(); + } + ConfigChangeNotifyResponse handleConfigChangeNotifyRequest(ConfigChangeNotifyRequest configChangeNotifyRequest, String clientName) { LOGGER.info("[{}] [server-push] config changed. dataId={}, group={},tenant={}", clientName, @@ -700,6 +1063,14 @@ private void initRpcClientHandler(final RpcClient rpcClientInner) { return handleConfigChangeNotifyRequest((ConfigChangeNotifyRequest) request, rpcClientInner.getName()); } + if (request instanceof FuzzyListenNotifyDiffRequest) { + return handleFuzzyListenNotifyDiffRequest((FuzzyListenNotifyDiffRequest) request, + rpcClientInner.getName()); + } + if (request instanceof FuzzyListenNotifyChangeRequest) { + return handlerFuzzyListenNotifyChangeRequest((FuzzyListenNotifyChangeRequest) request, + rpcClientInner.getName()); + } return null; }); @@ -716,6 +1087,9 @@ private void initRpcClientHandler(final RpcClient rpcClientInner) { public void onConnected(Connection connection) { LOGGER.info("[{}] Connected,notify listen context...", rpcClientInner.getName()); notifyListenConfig(); + + LOGGER.info("[{}] Connected,notify fuzzy listen context...", rpcClientInner.getName()); + notifyFuzzyListenConfig(); } @Override @@ -733,6 +1107,18 @@ public void onDisConnect(Connection connection) { cacheData.setConsistentWithServer(false); } } + + Collection fuzzyListenContexts = fuzzyListenContextMap.get().values(); + + for (FuzzyListenContext context : fuzzyListenContexts) { + if (StringUtils.isNotBlank(taskId)) { + if (Integer.valueOf(taskId).equals(context.getTaskId())) { + context.getIsConsistentWithServer().set(false); + } + } else { + context.getIsConsistentWithServer().set(false); + } + } } }); @@ -769,6 +1155,25 @@ public Class subscribeType() { } }; NotifyCenter.registerSubscriber(subscriber); + + NotifyCenter.registerSubscriber(new Subscriber() { + @Override + public void onEvent(Event event) { + FuzzyListenNotifyEvent fuzzyListenNotifyEvent = (FuzzyListenNotifyEvent) event; + FuzzyListenContext context = fuzzyListenContextMap.get() + .get(fuzzyListenNotifyEvent.getGroupKeyPattern()); + if (context == null) { + return; + } + context.notifyListener(fuzzyListenNotifyEvent.getDataId(), fuzzyListenNotifyEvent.getType(), + fuzzyListenNotifyEvent.getUuid()); + } + + @Override + public Class subscribeType() { + return FuzzyListenNotifyEvent.class; + } + }); } @Override @@ -793,6 +1198,26 @@ public void startInternal() { } }, 0L, TimeUnit.MILLISECONDS); + executor.schedule(() -> { + while (!executor.isShutdown() && !executor.isTerminated()) { + try { + fuzzyListenExecuteBell.poll(5L, TimeUnit.SECONDS); + if (executor.isShutdown() || executor.isTerminated()) { + continue; + } + executeConfigFuzzyListen(); + } catch (Throwable e) { + LOGGER.error("[rpc-fuzzy-listen-execute] rpc fuzzy listen exception", e); + try { + Thread.sleep(50L); + } catch (InterruptedException interruptedException) { + //ignore + } + notifyFuzzyListenConfig(); + } + } + }, 0L, TimeUnit.MILLISECONDS); + } @Override @@ -805,6 +1230,11 @@ public void notifyListenConfig() { listenExecutebell.offer(bellItem); } + @Override + public void notifyFuzzyListenConfig() { + fuzzyListenExecuteBell.offer(bellItem); + } + @Override public void executeConfigListen() throws NacosException { @@ -860,6 +1290,118 @@ public void executeConfigListen() throws NacosException { } + /** + * Execute fuzzy listen configuration changes. + * + *

This method iterates through all fuzzy listen contexts and determines whether they need to be added or + * removed based on their consistency with the server and discard status. It then calls the appropriate method + * to execute the fuzzy listen operation. + * + * @throws NacosException If an error occurs during the execution of fuzzy listen configuration changes. + */ + @Override + public void executeConfigFuzzyListen() throws NacosException { + Map> needSyncContextMap = new HashMap<>(16); + + // Obtain the current timestamp + long now = System.currentTimeMillis(); + + // Determine whether a full synchronization is needed + boolean needAllSync = now - fuzzyListenLastAllSyncTime >= FUZZY_LISTEN_ALL_SYNC_INTERNAL; + + // Iterate through all fuzzy listen contexts + for (FuzzyListenContext context : fuzzyListenContextMap.get().values()) { + // Check if the context is consistent with the server + if (context.getIsConsistentWithServer().get()) { + // Skip if a full synchronization is not needed + if (!needAllSync) { + continue; + } + } + + List needSyncContexts = needSyncContextMap.computeIfAbsent( + String.valueOf(context.getTaskId()), k -> new LinkedList<>()); + needSyncContexts.add(context); + } + + // Execute fuzzy listen operation for addition + doExecuteConfigFuzzyListen(needSyncContextMap); + + // Update last all sync time if a full synchronization was performed + if (needAllSync) { + fuzzyListenLastAllSyncTime = now; + } + } + + /** + * Execute fuzzy listen configuration changes for a specific map of contexts. + * + *

This method submits tasks to execute fuzzy listen operations asynchronously for the provided contexts. It + * waits for all tasks to complete and logs any errors that occur. + * + * @param contextMap The map of contexts to execute fuzzy listen operations for. + * @throws NacosException If an error occurs during the execution of fuzzy listen configuration changes. + */ + private void doExecuteConfigFuzzyListen(Map> contextMap) + throws NacosException { + // Return if the context map is null or empty + if (contextMap == null || contextMap.isEmpty()) { + return; + } + + // List to hold futures for asynchronous tasks + List> listenFutures = new ArrayList<>(); + + // Iterate through the context map and submit tasks for execution + for (Map.Entry> entry : contextMap.entrySet()) { + String taskId = entry.getKey(); + List contexts = entry.getValue(); + RpcClient rpcClient = ensureRpcClient(taskId); + ExecutorService executorService = ensureSyncExecutor(fuzzyListenerTaskPrefix, taskId); + // Submit task for execution + Future future = executorService.submit(() -> { + ConfigBatchFuzzyListenRequest configBatchFuzzyListenRequest = buildFuzzyListenConfigRequest( + contexts); + try { + // Execute the fuzzy listen operation + ConfigBatchFuzzyListenResponse listenResponse = (ConfigBatchFuzzyListenResponse) requestProxy( + rpcClient, configBatchFuzzyListenRequest); + if (listenResponse != null && listenResponse.isSuccess()) { + for (FuzzyListenContext context : contexts) { + if (context.isDiscard()) { + ClientWorker.this.removeFuzzyListenContext(context.getDataIdPattern(), + context.getGroup()); + } else { + context.getIsConsistentWithServer().set(true); + } + } + } + } 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 + } + // Retry notification + notifyFuzzyListenConfig(); + } + }); + listenFutures.add(future); + } + + // Wait for all tasks to complete + for (Future future : listenFutures) { + try { + future.get(); + } catch (Throwable throwable) { + // Log async listen error + LOGGER.error("Async fuzzy listen config change error.", throwable); + } + } + } + /** * Checks and handles local configuration for a given CacheData object. This method evaluates the use of * failover files for local configuration storage and updates the CacheData accordingly. @@ -908,16 +1450,41 @@ public void checkLocalConfig(CacheData cacheData) { } } - private ExecutorService ensureSyncExecutor(String taskId) { - if (!multiTaskExecutor.containsKey(taskId)) { - multiTaskExecutor.put(taskId, + /** + * Ensure to create a synchronous executor for the given task prefix and task ID. If an executor for the given + * task doesn't exist yet, a new executor will be created. + * + * @param taskPrefix The prefix of the task identifier + * @param taskId The ID of the task + * @return The created or existing executor + */ + private ExecutorService ensureSyncExecutor(String taskPrefix, String taskId) { + // Generate the unique task identifier + String taskIdentifier = generateTaskIdentifier(taskPrefix, taskId); + + // If the task identifier doesn't exist in the existing executors, create a new executor and add it to the multiTaskExecutor map + if (!multiTaskExecutor.containsKey(taskIdentifier)) { + multiTaskExecutor.put(taskIdentifier, new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(), r -> { - Thread thread = new Thread(r, "nacos.client.config.listener.task-" + taskId); + Thread thread = new Thread(r, taskIdentifier); thread.setDaemon(true); return thread; })); } - return multiTaskExecutor.get(taskId); + + // Return the created or existing executor + return multiTaskExecutor.get(taskIdentifier); + } + + /** + * Generate a task identifier based on the task prefix and task ID. + * + * @param taskPrefix The prefix of the task identifier + * @param taskId The ID of the task + * @return The generated task identifier + */ + private String generateTaskIdentifier(String taskPrefix, String taskId) { + return taskPrefix + "-" + taskId; } private void refreshContentAndCheck(RpcClient rpcClient, String groupKey, boolean notify) { @@ -957,7 +1524,7 @@ private void checkRemoveListenCache(Map> removeListenCac String taskId = entry.getKey(); RpcClient rpcClient = ensureRpcClient(taskId); - ExecutorService executorService = ensureSyncExecutor(taskId); + ExecutorService executorService = ensureSyncExecutor(configListenerTaskPrefix, taskId); Future future = executorService.submit(() -> { List removeListenCaches = entry.getValue(); ConfigBatchListenRequest configChangeListenRequest = buildConfigRequest(removeListenCaches); @@ -1007,7 +1574,7 @@ private boolean checkListenCache(Map> listenCachesMap) t String taskId = entry.getKey(); RpcClient rpcClient = ensureRpcClient(taskId); - ExecutorService executorService = ensureSyncExecutor(taskId); + ExecutorService executorService = ensureSyncExecutor(configListenerTaskPrefix, taskId); Future future = executorService.submit(() -> { List listenCaches = entry.getValue(); //reset notify change flag. @@ -1124,12 +1691,33 @@ private ConfigBatchListenRequest buildConfigRequest(List caches) { return configChangeListenRequest; } + /** + * Builds a request for fuzzy listen configuration. + * + * @param contexts The list of fuzzy listen contexts. + * @return A {@code ConfigBatchFuzzyListenRequest} object representing the request. + */ + private ConfigBatchFuzzyListenRequest buildFuzzyListenConfigRequest(List contexts) { + ConfigBatchFuzzyListenRequest request = new ConfigBatchFuzzyListenRequest(); + for (FuzzyListenContext context : contexts) { + request.addContext(getTenant(), context.getGroup(), context.getDataIdPattern(), context.getDataIds(), + !context.isDiscard(), context.isInitializing()); + } + return request; + } + @Override public void removeCache(String dataId, String group) { // Notify to rpc un listen ,and remove cache if success. notifyListenConfig(); } + @Override + public void removeFuzzyListenContext(String dataIdPattern, String group) throws NacosException { + // Notify to rpc un fuzzy listen, and remove cache if success. + notifyFuzzyListenConfig(); + } + /** * send cancel listen config change request . * 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..28d49e59054 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; @@ -177,6 +177,11 @@ public String getTenant() { **/ public abstract void notifyListenConfig(); + /** + * notify fuzzy listen config. + */ + public abstract void notifyFuzzyListenConfig(); + /** * listen change . * @@ -184,6 +189,13 @@ public String getTenant() { */ public abstract void executeConfigListen() throws NacosException; + /** + * Fuzzy listen change. + * + * @throws NacosException nacos exception throws, should retry. + */ + public abstract void executeConfigFuzzyListen() throws NacosException; + /** * remove cache implements. * @@ -192,6 +204,15 @@ public String getTenant() { */ public abstract void removeCache(String dataId, String group); + /** + * Remove fuzzy listen context. + * + * @param dataIdPattern dataIdPattern + * @param group group + * @throws NacosException if an error occurs while removing the fuzzy listen context. + */ + public abstract void removeFuzzyListenContext(String dataIdPattern, String group) throws NacosException; + /** * query config. * diff --git a/client/src/main/java/com/alibaba/nacos/client/config/impl/FuzzyListenContext.java b/client/src/main/java/com/alibaba/nacos/client/config/impl/FuzzyListenContext.java new file mode 100644 index 00000000000..93ddbcfbb7a --- /dev/null +++ b/client/src/main/java/com/alibaba/nacos/client/config/impl/FuzzyListenContext.java @@ -0,0 +1,447 @@ +/* + * 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.api.config.listener.AbstractFuzzyListenListener; +import com.alibaba.nacos.api.config.listener.FuzzyListenConfigChangeEvent; +import com.alibaba.nacos.client.utils.LogUtils; +import com.alibaba.nacos.common.utils.ConcurrentHashSet; +import com.alibaba.nacos.common.utils.StringUtils; +import org.slf4j.Logger; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +/** + * Context for fuzzy listening. + * + *

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. + *

+ * + * @author stone-98 + * @date 2024/3/4 + */ +public class FuzzyListenContext { + + /** + * Logger for FuzzyListenContext. + */ + private static final Logger LOGGER = LogUtils.logger(FuzzyListenContext.class); + + /** + * Environment name. + */ + private String envName; + + /** + * Task ID. + */ + private int taskId; + + /** + * Data ID pattern. + */ + private String dataIdPattern; + + /** + * Group name. + */ + private String group; + + /** + * Tenant name. + */ + private String tenant; + + /** + * Flag indicating whether the context is consistent with the server. + */ + private final AtomicBoolean isConsistentWithServer = new AtomicBoolean(); + + /** + * Lock object for synchronization of initialization. + */ + private final Lock initializationLock = new ReentrantLock(); + + /** + * Condition object for waiting initialization completion. + */ + private final Condition initializationCompleted = initializationLock.newCondition(); + + /** + * Flag indicating whether the context is initializing. + */ + private boolean isInitializing = false; + + /** + * Flag indicating whether the context is discarded. + */ + private volatile boolean isDiscard = false; + + /** + * Set of data IDs associated with the context. + */ + private Set dataIds = new ConcurrentHashSet<>(); + + /** + * Set of listeners associated with the context. + */ + private Set listeners = new HashSet<>(); + + /** + * Constructor with environment name, data ID pattern, and group. + * + * @param envName Environment name + * @param dataIdPattern Data ID pattern + * @param group Group name + */ + public FuzzyListenContext(String envName, String dataIdPattern, String group) { + this.envName = envName; + this.dataIdPattern = dataIdPattern; + this.group = group; + } + + /** + * Calculate the listeners to notify based on the given UUID. + * + * @param uuid UUID to filter listeners + * @return Set of listeners to notify + */ + public Set calculateListenersToNotify(String uuid) { + Set listenersToNotify = new HashSet<>(); + if (StringUtils.isEmpty(uuid)) { + listenersToNotify = listeners; + } else { + for (AbstractFuzzyListenListener listener : listeners) { + if (uuid.equals(listener.getUuid())) { + listenersToNotify.add(listener); + } + } + } + return listenersToNotify; + } + + /** + * Notify the listener with the specified data ID, type, and UUID. + * + * @param dataId Data ID + * @param type Type of the event + * @param uuid UUID to filter listeners + */ + public void notifyListener(final String dataId, final String type, final String uuid) { + Set listenersToNotify = calculateListenersToNotify(uuid); + doNotifyListener(dataId, type, listenersToNotify); + } + + /** + * Perform the notification for the specified data ID, type, and listeners. + * + * @param dataId Data ID + * @param type Type of the event + * @param listenersToNotify Set of listeners to notify + */ + private void doNotifyListener(final String dataId, final String type, + Set listenersToNotify) { + for (AbstractFuzzyListenListener listener : listenersToNotify) { + AbstractFuzzyNotifyTask job = new AbstractFuzzyNotifyTask() { + @Override + public void run() { + long start = System.currentTimeMillis(); + FuzzyListenConfigChangeEvent event = FuzzyListenConfigChangeEvent.build(group, dataId, type); + if (listener != null) { + listener.onEvent(event); + } + LOGGER.info("[{}] [notify-ok] dataId={}, group={}, tenant={}, listener={}, job run cost={} millis.", + envName, dataId, group, tenant, listener, (System.currentTimeMillis() - start)); + } + }; + + try { + if (null != listener.getExecutor()) { + LOGGER.info( + "[{}] [notify-listener] task submitted to user executor, dataId={}, group={}, tenant={}, listener={}.", + envName, dataId, group, tenant, listener); + job.async = true; + listener.getExecutor().execute(job); + } else { + LOGGER.info( + "[{}] [notify-listener] task execute in nacos thread, dataId={}, group={}, tenant={}, listener={}.", + envName, dataId, group, tenant, listener); + job.run(); + } + } catch (Throwable t) { + LOGGER.error("[{}] [notify-listener-error] dataId={}, group={}, tenant={}, listener={}, throwable={}.", + envName, dataId, group, tenant, listener, t.getCause()); + } + } + } + + + /** + * Wait for initialization to be complete. + * + * @return CompletableFuture> Completes with the collection of data IDs if initialization is + * @return CompletableFuture> Completes with the collection of data IDs if initialization is + * complete, or completes exceptionally if an error occurs + */ + public CompletableFuture> waitForInitializationComplete( + CompletableFuture> future) { + initializationLock.lock(); + try { + while (isInitializing) { + initializationCompleted.await(); + } + future.complete(Collections.unmodifiableCollection(dataIds)); + } catch (InterruptedException e) { + future.completeExceptionally(e); + } finally { + initializationLock.unlock(); + } + return future; + } + + /** + * Mark initialization as complete and notify waiting threads. + */ + public void markInitializationComplete() { + initializationLock.lock(); + try { + isInitializing = false; + initializationCompleted.signalAll(); + } finally { + initializationLock.unlock(); + } + } + + /** + * Remove a listener from the context. + * + * @param listener Listener to be removed + */ + public void removeListener(AbstractFuzzyListenListener listener) { + listeners.remove(listener); + } + + /** + * Add a listener to the context. + * + * @param listener Listener to be added + */ + public void addListener(AbstractFuzzyListenListener listener) { + listeners.add(listener); + } + + /** + * Get the environment name. + * + * @return Environment name + */ + public String getEnvName() { + return envName; + } + + /** + * Set the environment name. + * + * @param envName Environment name to be set + */ + public void setEnvName(String envName) { + this.envName = envName; + } + + /** + * Get the task ID. + * + * @return Task ID + */ + public int getTaskId() { + return taskId; + } + + /** + * Set the task ID. + * + * @param taskId Task ID to be set + */ + public void setTaskId(int taskId) { + this.taskId = taskId; + } + + /** + * Get the data ID pattern. + * + * @return Data ID pattern + */ + public String getDataIdPattern() { + return dataIdPattern; + } + + /** + * Set the data ID pattern. + * + * @param dataIdPattern Data ID pattern to be set + */ + public void setDataIdPattern(String dataIdPattern) { + this.dataIdPattern = dataIdPattern; + } + + /** + * Get the group name. + * + * @return Group name + */ + public String getGroup() { + return group; + } + + /** + * Set the group name. + * + * @param group Group name to be set + */ + public void setGroup(String group) { + this.group = group; + } + + /** + * Get the tenant name. + * + * @return Tenant name + */ + public String getTenant() { + return tenant; + } + + /** + * Set the tenant name. + * + * @param tenant Tenant name to be set + */ + public void setTenant(String tenant) { + this.tenant = tenant; + } + + /** + * Get the flag indicating whether the context is consistent with the server. + * + * @return AtomicBoolean indicating whether the context is consistent with the server + */ + public AtomicBoolean getIsConsistentWithServer() { + return isConsistentWithServer; + } + + /** + * Check if the context is discarded. + * + * @return True if the context is discarded, otherwise false + */ + public boolean isDiscard() { + return isDiscard; + } + + /** + * Set the flag indicating whether the context is discarded. + * + * @param discard True to mark the context as discarded, otherwise false + */ + public void setDiscard(boolean discard) { + isDiscard = discard; + } + + /** + * Check if the context is initializing. + * + * @return True if the context is initializing, otherwise false + */ + public boolean isInitializing() { + return isInitializing; + } + + /** + * Set the flag indicating whether the context is initializing. + * + * @param initializing True to mark the context as initializing, otherwise false + */ + public void setInitializing(boolean initializing) { + isInitializing = initializing; + } + + /** + * Get the set of data IDs associated with the context. + * + * @return Set of data IDs + */ + public Set getDataIds() { + return Collections.unmodifiableSet(dataIds); + } + + /** + * Set the set of data IDs associated with the context. + * + * @param dataIds Set of data IDs to be set + */ + public void setDataIds(Set dataIds) { + this.dataIds = dataIds; + } + + /** + * Get the set of listeners associated with the context. + * + * @return Set of listeners + */ + public Set getListeners() { + return listeners; + } + + /** + * Set the set of listeners associated with the context. + * + * @param listeners Set of listeners to be set + */ + public void setListeners(Set listeners) { + this.listeners = listeners; + } + + /** + * Abstract task for fuzzy notification. + */ + abstract static class AbstractFuzzyNotifyTask implements Runnable { + + /** + * Flag indicating whether the task is asynchronous. + */ + boolean async = false; + + /** + * Check if the task is asynchronous. + * + * @return True if the task is asynchronous, otherwise false + */ + public boolean isAsync() { + return async; + } + } +} + diff --git a/client/src/main/java/com/alibaba/nacos/client/config/impl/FuzzyListenNotifyEvent.java b/client/src/main/java/com/alibaba/nacos/client/config/impl/FuzzyListenNotifyEvent.java new file mode 100644 index 00000000000..a710630c131 --- /dev/null +++ b/client/src/main/java/com/alibaba/nacos/client/config/impl/FuzzyListenNotifyEvent.java @@ -0,0 +1,200 @@ +/* + * 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 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 FuzzyListenNotifyEvent extends Event { + + /** + * The unique identifier for the listener. + */ + private String uuid; + + /** + * The groupKeyPattern of configuration. + */ + private String groupKeyPattern; + + /** + * The group of the configuration. + */ + private String group; + + /** + * The dataId of the configuration. + */ + private String dataId; + + /** + * The type of notification (e.g., ADD_CONFIG, DELETE_CONFIG). + */ + private String type; + + /** + * Constructs a new FuzzyListenNotifyEvent. + */ + public FuzzyListenNotifyEvent() { + } + + /** + * Constructs a new FuzzyListenNotifyEvent with the specified group, dataId, type, and UUID. + * + * @param group The group of the configuration. + * @param dataId The dataId of the configuration. + * @param type The type of notification. + * @param uuid The UUID (Unique Identifier) of the listener. + */ + public FuzzyListenNotifyEvent(String group, String dataId, String type, String groupKeyPattern, String uuid) { + this.group = group; + this.dataId = dataId; + this.type = type; + this.groupKeyPattern = groupKeyPattern; + this.uuid = uuid; + } + + /** + * Constructs a new FuzzyListenNotifyEvent with the specified group, dataId, and type. + * + * @param group The group of the configuration. + * @param dataId The dataId of the configuration. + * @param type The type of notification. + */ + public FuzzyListenNotifyEvent(String group, String dataId, String type, String groupKeyPattern) { + this.group = group; + this.dataId = dataId; + this.type = type; + this.groupKeyPattern = groupKeyPattern; + } + + /** + * Builds a new FuzzyListenNotifyEvent with the specified group, dataId, type, and UUID. + * + * @param group The group of the configuration. + * @param dataId The dataId of the configuration. + * @param type The type of notification. + * @param uuid The UUID (Unique Identifier) of the listener. + * @return A new FuzzyListenNotifyEvent instance. + */ + public static FuzzyListenNotifyEvent buildNotifyPatternSpecificListenerEvent(String group, String dataId, + String type, String groupKeyPattern, String uuid) { + return new FuzzyListenNotifyEvent(group, dataId, type, groupKeyPattern, uuid); + } + + /** + * Builds a new FuzzyListenNotifyEvent with the specified group, dataId, and type. + * + * @param group The group of the configuration. + * @param dataId The dataId of the configuration. + * @param type The type of notification. + * @return A new FuzzyListenNotifyEvent instance. + */ + public static FuzzyListenNotifyEvent buildNotifyPatternAllListenersEvent(String group, String dataId, + String groupKeyPattern, String type) { + return new FuzzyListenNotifyEvent(group, dataId, type, groupKeyPattern); + } + + /** + * Gets the UUID (Unique Identifier) of the listener. + * + * @return The UUID of the listener. + */ + public String getUuid() { + return uuid; + } + + /** + * Sets the UUID (Unique Identifier) of the listener. + * + * @param uuid The UUID to set. + */ + public void setUuid(String uuid) { + this.uuid = uuid; + } + + public String getGroupKeyPattern() { + return groupKeyPattern; + } + + public void setGroupKeyPattern(String groupKeyPattern) { + this.groupKeyPattern = groupKeyPattern; + } + + /** + * Gets the group of the configuration. + * + * @return The group of the configuration. + */ + public String getGroup() { + return group; + } + + /** + * Sets the group of the configuration. + * + * @param group The group to set. + */ + public void setGroup(String group) { + this.group = group; + } + + /** + * Gets the dataId of the configuration. + * + * @return The dataId of the configuration. + */ + public String getDataId() { + return dataId; + } + + /** + * Sets the dataId of the configuration. + * + * @param dataId The dataId to set. + */ + public void setDataId(String dataId) { + this.dataId = dataId; + } + + /** + * Gets the type of notification. + * + * @return The type of notification. + */ + public String getType() { + return type; + } + + /** + * Sets the type of notification. + * + * @param type The type to set. + */ + public void setType(String type) { + this.type = type; + } +} diff --git a/client/src/main/java/com/alibaba/nacos/client/utils/ParamUtil.java b/client/src/main/java/com/alibaba/nacos/client/utils/ParamUtil.java index 4325d60adcc..f6f8be2b4cc 100644 --- a/client/src/main/java/com/alibaba/nacos/client/utils/ParamUtil.java +++ b/client/src/main/java/com/alibaba/nacos/client/utils/ParamUtil.java @@ -62,6 +62,8 @@ public class ParamUtil { private static double perTaskConfigSize = 3000; + private static final String PER_TASK_CONTEXT_SIZE_KEY = "PER_TASK_CONTEXT_SIZE_KEY"; + private static final String NACOS_CLIENT_APP_KEY = "nacos.client.appKey"; private static final String BLANK_STR = ""; @@ -84,6 +86,9 @@ public class ParamUtil { private static final String DEFAULT_PER_TASK_CONFIG_SIZE_KEY = "3000"; + private static final String DEFAULT_PER_TASK_CONTEXT_SIZE_KEY = "3000"; + + private static double perTaskContextSize = 3000; private static final int DESENSITISE_PARAMETER_MIN_LENGTH = 2; private static final int DESENSITISE_PARAMETER_KEEP_ONE_CHAR_LENGTH = 8; @@ -110,6 +115,9 @@ public class ParamUtil { perTaskConfigSize = initPerTaskConfigSize(); LOGGER.info("PER_TASK_CONFIG_SIZE: {}", perTaskConfigSize); + + perTaskContextSize = initPerTaskContextSize(); + LOGGER.info("PER_TASK_CONTEXT_SIZE: {}", perTaskContextSize); } private static int initConnectionTimeout() { @@ -146,6 +154,16 @@ private static double initPerTaskConfigSize() { } } + private static double initPerTaskContextSize() { + try { + return Double.parseDouble(NacosClientProperties.PROTOTYPE.getProperty(PER_TASK_CONTEXT_SIZE_KEY, + DEFAULT_PER_TASK_CONTEXT_SIZE_KEY)); + } catch (NumberFormatException e) { + LOGGER.error("[PER_TASK_CONTEXT_SIZE] PER_TASK_CONTEXT_SIZE invalid", e); + throw new IllegalArgumentException("invalid PER_TASK_CONTEXT_SIZE, expected value type double", e); + } + } + public static String getAppKey() { return appKey; } @@ -202,6 +220,14 @@ public static void setPerTaskConfigSize(double perTaskConfigSize) { ParamUtil.perTaskConfigSize = perTaskConfigSize; } + public static double getPerTaskContextSize() { + return perTaskContextSize; + } + + public static void setPerTaskContextSize(double perTaskContextSize) { + ParamUtil.perTaskContextSize = perTaskContextSize; + } + public static String getDefaultServerPort() { return serverPort; } @@ -257,10 +283,10 @@ public static String parsingEndpointRule(String endpointUrl) { if (StringUtils.isNotBlank(endpointUrlSource)) { endpointUrl = endpointUrlSource; } - + return StringUtils.isNotBlank(endpointUrl) ? endpointUrl : ""; } - + endpointUrl = endpointUrl.substring(endpointUrl.indexOf("${") + 2, endpointUrl.lastIndexOf("}")); int defStartOf = endpointUrl.indexOf(":"); String defaultEndpointUrl = null; @@ -268,12 +294,13 @@ public static String parsingEndpointRule(String endpointUrl) { defaultEndpointUrl = endpointUrl.substring(defStartOf + 1); endpointUrl = endpointUrl.substring(0, defStartOf); } + String endpointUrlSource = TemplateUtils.stringBlankAndThenExecute( NacosClientProperties.PROTOTYPE.getProperty(endpointUrl), () -> NacosClientProperties.PROTOTYPE.getProperty( PropertyKeyConst.SystemEnv.ALIBABA_ALIWARE_ENDPOINT_URL)); - + if (StringUtils.isBlank(endpointUrlSource)) { if (StringUtils.isNotBlank(defaultEndpointUrl)) { endpointUrl = defaultEndpointUrl; @@ -281,7 +308,7 @@ public static String parsingEndpointRule(String endpointUrl) { } else { endpointUrl = endpointUrlSource; } - + return StringUtils.isNotBlank(endpointUrl) ? endpointUrl : ""; } diff --git a/client/src/test/java/com/alibaba/nacos/client/config/NacosConfigServiceTest.java b/client/src/test/java/com/alibaba/nacos/client/config/NacosConfigServiceTest.java index 7e56dde22d4..4cf62936894 100644 --- a/client/src/test/java/com/alibaba/nacos/client/config/NacosConfigServiceTest.java +++ b/client/src/test/java/com/alibaba/nacos/client/config/NacosConfigServiceTest.java @@ -38,7 +38,7 @@ import org.mockito.quality.Strictness; import java.lang.reflect.Field; -import java.util.Arrays; +import java.util.Collections; import java.util.Properties; import java.util.concurrent.Executor; @@ -157,6 +157,7 @@ void testGetConfig403() throws NacosException { .thenThrow(new NacosException(NacosException.NO_RIGHT, "no right")); try { nacosConfigService.getConfig(dataId, group, timeout); + Assert.fail(); assertTrue(false); } catch (NacosException e) { assertEquals(NacosException.NO_RIGHT, e.getErrCode()); @@ -195,27 +196,42 @@ public void receiveConfigInfo(String configInfo) { public void startInternal() throws NacosException { // NOOP } - + @Override public String getName() { return "TestConfigTransportClient"; } - + @Override public void notifyListenConfig() { // NOOP } - + + @Override + public void notifyFuzzyListenConfig() { + // NOOP + } + @Override public void executeConfigListen() { // NOOP } - + + @Override + public void executeConfigFuzzyListen() throws NacosException { + // NOOP + } + @Override public void removeCache(String dataId, String group) { // NOOP } - + + @Override + public void removeFuzzyListenContext(String dataIdPattern, String group) { + // NOOP + } + @Override public ConfigResponse queryConfig(String dataId, String group, String tenant, long readTimeous, boolean notify) throws NacosException { @@ -241,6 +257,10 @@ public boolean removeConfig(String dataId, String group, String tenant, String t Mockito.when(mockWoker.getAgent()).thenReturn(client); final String config = nacosConfigService.getConfigAndSignListener(dataId, group, timeout, listener); + Assert.assertEquals(content, config); + + Mockito.verify(mockWoker, Mockito.times(1)) + .addTenantListenersWithContent(dataId, group, content, null, Collections.singletonList(listener)); assertEquals(content, config); Mockito.verify(mockWoker, Mockito.times(1)).addTenantListenersWithContent(dataId, group, content, null, Arrays.asList(listener)); @@ -255,15 +275,16 @@ void testAddListener() throws NacosException { public Executor getExecutor() { return null; } - + @Override public void receiveConfigInfo(String configInfo) { - + } }; - + nacosConfigService.addListener(dataId, group, listener); - Mockito.verify(mockWoker, Mockito.times(1)).addTenantListeners(dataId, group, Arrays.asList(listener)); + Mockito.verify(mockWoker, Mockito.times(1)) + .addTenantListeners(dataId, group, Collections.singletonList(listener)); } @Test @@ -383,4 +404,4 @@ void testShutDown() { nacosConfigService.shutDown(); }); } -} \ No newline at end of file +} diff --git a/common/src/main/java/com/alibaba/nacos/common/utils/GroupKey.java b/common/src/main/java/com/alibaba/nacos/common/utils/GroupKey.java new file mode 100644 index 00000000000..c821876ed55 --- /dev/null +++ b/common/src/main/java/com/alibaba/nacos/common/utils/GroupKey.java @@ -0,0 +1,140 @@ +/* + * 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.common.utils; + +/** + * Synthesize the form of dataId+groupId. Escapes reserved characters in dataId and groupId. + * + * @author Nacos + */ +public class GroupKey { + + private static final char PLUS = '+'; + + private static final char PERCENT = '%'; + + private static final char TWO = '2'; + + private static final char B = 'B'; + + private static final char FIVE = '5'; + + public static String getKey(String dataId, String group) { + return getKey(dataId, group, ""); + } + + public static String getKey(String dataId, String group, String datumStr) { + return doGetKey(dataId, group, datumStr); + } + + public static String getKeyTenant(String dataId, String group, String tenant) { + return doGetKey(dataId, group, tenant); + } + + private static String doGetKey(String dataId, String group, String datumStr) { + if (StringUtils.isBlank(dataId)) { + throw new IllegalArgumentException("invalid dataId"); + } + if (StringUtils.isBlank(group)) { + throw new IllegalArgumentException("invalid group"); + } + StringBuilder sb = new StringBuilder(); + urlEncode(dataId, sb); + sb.append(PLUS); + urlEncode(group, sb); + if (StringUtils.isNotEmpty(datumStr)) { + sb.append(PLUS); + urlEncode(datumStr, sb); + } + + return sb.toString(); + } + + /** + * Parse key. + * + * @param groupKey group key + * @return parsed key + */ + public static String[] parseKey(String groupKey) { + StringBuilder sb = new StringBuilder(); + String dataId = null; + String group = null; + String tenant = null; + + for (int i = 0; i < groupKey.length(); ++i) { + char c = groupKey.charAt(i); + if (PLUS == c) { + if (null == dataId) { + dataId = sb.toString(); + sb.setLength(0); + } else if (null == group) { + group = sb.toString(); + sb.setLength(0); + } else { + throw new IllegalArgumentException("invalid groupkey:" + groupKey); + } + } else if (PERCENT == c) { + char next = groupKey.charAt(++i); + char nextnext = groupKey.charAt(++i); + if (TWO == next && B == nextnext) { + sb.append(PLUS); + } else if (TWO == next && FIVE == nextnext) { + sb.append(PERCENT); + } else { + throw new IllegalArgumentException("invalid groupkey:" + groupKey); + } + } else { + sb.append(c); + } + } + + if (group == null) { + group = sb.toString(); + } else { + tenant = sb.toString(); + } + + if (StringUtils.isBlank(dataId)) { + throw new IllegalArgumentException("invalid dataId"); + } + if (StringUtils.isBlank(group)) { + throw new IllegalArgumentException("invalid group"); + } + if (StringUtils.isBlank(tenant)) { + return new String[] {dataId, group}; + } + return new String[] {dataId, group, tenant}; + } + + /** + * + -> %2B % -> %25. + */ + static void urlEncode(String str, StringBuilder sb) { + for (int idx = 0; idx < str.length(); ++idx) { + char c = str.charAt(idx); + if (PLUS == c) { + sb.append("%2B"); + } else if (PERCENT == c) { + sb.append("%25"); + } else { + sb.append(c); + } + } + } + +} diff --git a/common/src/main/java/com/alibaba/nacos/common/utils/GroupKeyPattern.java b/common/src/main/java/com/alibaba/nacos/common/utils/GroupKeyPattern.java new file mode 100644 index 00000000000..f00d50e7db2 --- /dev/null +++ b/common/src/main/java/com/alibaba/nacos/common/utils/GroupKeyPattern.java @@ -0,0 +1,259 @@ +/* + * 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.common.utils; + +import com.alibaba.nacos.api.common.Constants; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; + +import static com.alibaba.nacos.api.common.Constants.DATA_ID_SPLITTER; +import static com.alibaba.nacos.api.common.Constants.FUZZY_LISTEN_PATTERN_WILDCARD; +import static com.alibaba.nacos.api.common.Constants.NAMESPACE_ID_SPLITTER; + +/** + * Utility class for matching group keys against a given pattern. + * + *

This class provides methods to match group keys based on a pattern specified. It supports matching based on + * dataId, group, and namespace components of the group key. + * + * @author stone-98 + * @date 2024/3/14 + */ +public class GroupKeyPattern { + + /** + * Generates a fuzzy listen group key pattern based on the given dataId pattern, group, and optional tenant. + * + *

This method generates a unique group key pattern for fuzzy listen based on the specified dataId pattern, + * group, and optional tenant. It concatenates the dataId pattern, group, and tenant (if provided) with a delimiter + * and returns the resulting string. The resulting string is interned to improve memory efficiency. + * + * @param dataIdPattern The pattern for matching dataIds. + * @param group The group associated with the dataIds. + * @param namespace (Optional) The tenant associated with the dataIds (can be null or empty). + * @return A unique group key pattern for fuzzy listen. + * @throws IllegalArgumentException If the dataId pattern or group is blank. + */ + public static String generateFuzzyListenGroupKeyPattern(final String dataIdPattern, final String group, + final String namespace) { + if (StringUtils.isBlank(dataIdPattern)) { + throw new IllegalArgumentException("Param 'dataIdPattern' is illegal, dataIdPattern is blank"); + } + if (StringUtils.isBlank(group)) { + throw new IllegalArgumentException("Param 'group' is illegal, group is blank"); + } + StringBuilder sb = new StringBuilder(); + if (StringUtils.isNotBlank(namespace)) { + sb.append(namespace); + } + sb.append(NAMESPACE_ID_SPLITTER); + sb.append(group); + sb.append(DATA_ID_SPLITTER); + sb.append(dataIdPattern); + return sb.toString().intern(); + } + + /** + * Generates a fuzzy listen group key pattern based on the given dataId pattern and group. + * + *

This method generates a unique group key pattern for fuzzy listen based on the specified dataId pattern and + * group. It concatenates the dataId pattern and group with a delimiter and returns the resulting string. The + * resulting string is interned to improve memory efficiency. + * + * @param dataIdPattern The pattern for matching dataIds. + * @param group The group associated with the dataIds. + * @return A unique group key pattern for fuzzy listen. + * @throws IllegalArgumentException If the dataId pattern or group is blank. + */ + public static String generateFuzzyListenGroupKeyPattern(final String dataIdPattern, final String group) { + if (StringUtils.isBlank(dataIdPattern)) { + throw new IllegalArgumentException("Param 'dataIdPattern' is illegal, dataIdPattern is blank"); + } + if (StringUtils.isBlank(group)) { + throw new IllegalArgumentException("Param 'group' is illegal, group is blank"); + } + final String fuzzyListenGroupKey = group + DATA_ID_SPLITTER + dataIdPattern; + return fuzzyListenGroupKey.intern(); + } + + /** + * Checks whether a group key matches the specified pattern. + * + * @param groupKey The group key to match. + * @param groupKeyPattern The pattern to match against. + * @return {@code true} if the group key matches the pattern, otherwise {@code false}. + */ + public static boolean isMatchPatternWithNamespace(String groupKey, String groupKeyPattern) { + String[] parseKey = GroupKey.parseKey(groupKey); + String dataId = parseKey[0]; + String group = parseKey[1]; + String namespace = parseKey.length > 2 ? parseKey[2] : Constants.DEFAULT_NAMESPACE_ID; + + String namespacePattern = getNamespace(groupKeyPattern); + String groupPattern = getGroup(groupKeyPattern); + String dataIdPattern = getDataIdPattern(groupKeyPattern); + + if (dataIdPattern.equals(FUZZY_LISTEN_PATTERN_WILDCARD)) { + return namespace.equals(namespacePattern) && group.equals(groupPattern); + } + + if (dataIdPattern.endsWith(FUZZY_LISTEN_PATTERN_WILDCARD)) { + String dataIdPrefix = dataIdPattern.substring(0, dataIdPattern.length() - 1); + return namespace.equals(namespacePattern) && groupPattern.equals(group) && dataId.startsWith(dataIdPrefix); + } + + return namespace.equals(namespacePattern) && group.equals(groupPattern) && dataId.equals(dataIdPattern); + } + + /** + * Checks whether a group key matches the specified pattern. + * + * @param groupKey The group key to match. + * @param groupKeyPattern The pattern to match against. + * @return {@code true} if the group key matches the pattern, otherwise {@code false}. + */ + public static boolean isMatchPatternWithoutNamespace(String groupKey, String groupKeyPattern) { + String[] parseKey = GroupKey.parseKey(groupKey); + String dataId = parseKey[0]; + String group = parseKey[1]; + + String groupPattern = getGroup(groupKeyPattern); + String dataIdPattern = getDataIdPattern(groupKeyPattern); + + if (dataIdPattern.equals(FUZZY_LISTEN_PATTERN_WILDCARD)) { + return group.equals(groupPattern); + } + + if (dataIdPattern.endsWith(FUZZY_LISTEN_PATTERN_WILDCARD)) { + String dataIdPrefix = dataIdPattern.substring(0, dataIdPattern.length() - 1); + return groupPattern.equals(group) && dataId.startsWith(dataIdPrefix); + } + + return group.equals(groupPattern) && dataId.equals(dataIdPattern); + } + + /** + * Given a dataId, group, dataId pattern, and group pattern, determines whether it can match. + * + * @param dataId The dataId to match. + * @param group The group to match. + * @param dataIdPattern The dataId pattern to match against. + * @param groupPattern The group pattern to match against. + * @return {@code true} if the dataId and group match the patterns, otherwise {@code false}. + */ + public static boolean isMatchPatternWithoutNamespace(String dataId, String group, String dataIdPattern, + String groupPattern) { + String groupKey = GroupKey.getKey(dataId, group); + String groupKeyPattern = generateFuzzyListenGroupKeyPattern(dataIdPattern, groupPattern); + return isMatchPatternWithoutNamespace(groupKey, groupKeyPattern); + } + + /** + * Given a dataId, group, and a collection of completed group key patterns, returns the patterns that match. + * + * @param dataId The dataId to match. + * @param group The group to match. + * @param groupKeyPatterns The collection of completed group key patterns to match against. + * @return A set of patterns that match the dataId and group. + */ + public static Set getConfigMatchedPatternsWithoutNamespace(String dataId, String group, + Collection groupKeyPatterns) { + if (CollectionUtils.isEmpty(groupKeyPatterns)) { + return new HashSet<>(1); + } + Set matchedPatternList = new HashSet<>(); + for (String keyPattern : groupKeyPatterns) { + if (isMatchPatternWithoutNamespace(dataId, group, getDataIdPattern(keyPattern), getGroup(keyPattern))) { + matchedPatternList.add(keyPattern); + } + } + return matchedPatternList; + } + + /** + * Extracts the namespace from the given group key pattern. + * + * @param groupKeyPattern The group key pattern from which to extract the namespace. + * @return The namespace extracted from the group key pattern. + */ + public static String getNamespace(final String groupKeyPattern) { + if (StringUtils.isBlank(groupKeyPattern)) { + return StringUtils.EMPTY; + } + if (!groupKeyPattern.contains(NAMESPACE_ID_SPLITTER)) { + return StringUtils.EMPTY; + } + return groupKeyPattern.split(NAMESPACE_ID_SPLITTER)[0]; + } + + /** + * Extracts the group from the given group key pattern. + * + * @param groupKeyPattern The group key pattern from which to extract the group. + * @return The group extracted from the group key pattern. + */ + public static String getGroup(final String groupKeyPattern) { + if (StringUtils.isBlank(groupKeyPattern)) { + return StringUtils.EMPTY; + } + String groupWithNamespace; + if (!groupKeyPattern.contains(DATA_ID_SPLITTER)) { + groupWithNamespace = groupKeyPattern; + } else { + groupWithNamespace = groupKeyPattern.split(DATA_ID_SPLITTER)[0]; + } + + if (!groupKeyPattern.contains(NAMESPACE_ID_SPLITTER)) { + return groupWithNamespace; + } + return groupWithNamespace.split(NAMESPACE_ID_SPLITTER)[1]; + } + + /** + * Extracts the dataId pattern from the given group key pattern. + * + * @param groupKeyPattern The group key pattern from which to extract the dataId pattern. + * @return The dataId pattern extracted from the group key pattern. + */ + public static String getDataIdPattern(final String groupKeyPattern) { + if (StringUtils.isBlank(groupKeyPattern)) { + return StringUtils.EMPTY; + } + if (!groupKeyPattern.contains(DATA_ID_SPLITTER)) { + return StringUtils.EMPTY; + } + return groupKeyPattern.split(DATA_ID_SPLITTER)[1]; + } + + /** + * Given a completed pattern, removes the namespace. + * + * @param completedPattern The completed pattern from which to remove the namespace. + * @return The pattern with the namespace removed. + */ + public static String getPatternRemovedNamespace(String completedPattern) { + if (StringUtils.isBlank(completedPattern)) { + return StringUtils.EMPTY; + } + if (!completedPattern.contains(NAMESPACE_ID_SPLITTER)) { + return completedPattern; + } + return completedPattern.split(NAMESPACE_ID_SPLITTER)[1]; + } +} diff --git a/common/src/test/java/com/alibaba/nacos/common/utils/GroupKeyPatternTest.java b/common/src/test/java/com/alibaba/nacos/common/utils/GroupKeyPatternTest.java new file mode 100644 index 00000000000..a16e222ecbc --- /dev/null +++ b/common/src/test/java/com/alibaba/nacos/common/utils/GroupKeyPatternTest.java @@ -0,0 +1,136 @@ +/* + * 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.common.utils; + +import org.junit.Assert; +import org.junit.Test; + +import java.util.HashSet; +import java.util.Set; + +/** + * GroupKeyPatternUtilsTest. + * + * @author stone-98 + * @date 2024/3/19 + */ +public class GroupKeyPatternTest { + + @Test + public void testGetGroupKeyPatternWithNamespace() { + String dataIdPattern = "examplePattern*"; + String group = "exampleGroup"; + String namespace = "exampleNamespace"; + + String groupKeyPattern = GroupKeyPattern.generateFuzzyListenGroupKeyPattern(dataIdPattern, group, namespace); + + Assert.assertEquals("exampleNamespace>>exampleGroup@@examplePattern*", groupKeyPattern); + } + + @Test + public void testGetGroupKeyPatternWithoutNamespace() { + String dataIdPattern = "examplePattern*"; + String group = "exampleGroup"; + + String groupKeyPattern = GroupKeyPattern.generateFuzzyListenGroupKeyPattern(dataIdPattern, group); + + Assert.assertEquals("exampleGroup@@examplePattern*", groupKeyPattern); + } + + @Test + public void testIsMatchPatternWithNamespace() { + String groupKey = "examplePattern+exampleGroup+exampleNamespace"; + String groupKeyPattern = "exampleNamespace>>exampleGroup@@examplePattern*"; + + boolean result = GroupKeyPattern.isMatchPatternWithNamespace(groupKey, groupKeyPattern); + + Assert.assertTrue(result); + } + + @Test + public void testIsMatchPatternWithoutNamespace() { + String groupKey = "examplePattern+exampleGroup+exampleNamespace"; + String groupKeyPattern = "exampleNamespace>>exampleGroup@@*"; + + boolean result = GroupKeyPattern.isMatchPatternWithoutNamespace(groupKey, groupKeyPattern); + + Assert.assertTrue(result); + } + + @Test + public void testIsMatchPatternWithoutNamespaceWithDataIdPrefix() { + String groupKey = "examplePattern+exampleGroup+exampleNamespace"; + String groupKeyPattern = "exampleNamespace>>exampleGroup@@examplePattern*"; + + boolean result = GroupKeyPattern.isMatchPatternWithoutNamespace(groupKey, groupKeyPattern); + + Assert.assertTrue(result); + } + + @Test + public void testGetConfigMatchedPatternsWithoutNamespace() { + String dataId = "exampleDataId"; + String group = "exampleGroup"; + Set groupKeyPatterns = new HashSet<>(); + groupKeyPatterns.add("exampleGroup@@exampleDataId*"); + groupKeyPatterns.add("exampleGroup@@exampleDataI*"); + + Set matchedPatterns = GroupKeyPattern.getConfigMatchedPatternsWithoutNamespace(dataId, group, + groupKeyPatterns); + + Assert.assertEquals(2, matchedPatterns.size()); + Assert.assertTrue(matchedPatterns.contains("exampleGroup@@exampleDataId*")); + Assert.assertTrue(matchedPatterns.contains("exampleGroup@@exampleDataI*")); + } + + @Test + public void testGetNamespace() { + String groupKeyPattern = "exampleNamespace>>exampleGroup@@examplePattern"; + + String namespace = GroupKeyPattern.getNamespace(groupKeyPattern); + + Assert.assertEquals("exampleNamespace", namespace); + } + + @Test + public void testGetGroup() { + String groupKeyPattern = "exampleNamespace>>exampleGroup@@examplePattern"; + + String group = GroupKeyPattern.getGroup(groupKeyPattern); + + Assert.assertEquals("exampleGroup", group); + } + + @Test + public void testGetDataIdPattern() { + String groupKeyPattern = "exampleNamespace>>exampleGroup@@examplePattern"; + + String dataIdPattern = GroupKeyPattern.getDataIdPattern(groupKeyPattern); + + Assert.assertEquals("examplePattern", dataIdPattern); + } + + @Test + public void testGetPatternRemovedNamespace() { + String groupKeyPattern = "exampleNamespace>>exampleGroup@@examplePattern"; + + String patternRemovedNamespace = GroupKeyPattern.getPatternRemovedNamespace(groupKeyPattern); + + Assert.assertEquals("exampleGroup@@examplePattern", patternRemovedNamespace); + } +} + 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 ac6e286877d..722e5818493 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 @@ -33,6 +33,9 @@ public class ConfigCommonConfig extends AbstractDynamicConfig { private int maxPushRetryTimes = 50; + private long pushTimeout = 3000L; + + private int batchSize = 10; private boolean derbyOpsEnabled = false; private ConfigCommonConfig() { @@ -52,6 +55,20 @@ public void setMaxPushRetryTimes(int maxPushRetryTimes) { this.maxPushRetryTimes = maxPushRetryTimes; } + 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; } @@ -63,6 +80,8 @@ public void setDerbyOpsEnabled(boolean derbyOpsEnabled) { @Override protected void getConfigFromEnv() { maxPushRetryTimes = EnvUtil.getProperty("nacos.config.push.maxRetryTime", Integer.class, 50); + pushTimeout = EnvUtil.getProperty("nacos.config.push.timeout", Long.class, 3000L); + pushTimeout = EnvUtil.getProperty("nacos.config.push.batchSize", Integer.class, 10); derbyOpsEnabled = EnvUtil.getProperty("nacos.config.derby.ops.enabled", Boolean.class, false); } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/model/CacheItem.java b/config/src/main/java/com/alibaba/nacos/config/server/model/CacheItem.java index e0423f3daa7..836713a3be4 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/model/CacheItem.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/model/CacheItem.java @@ -16,6 +16,8 @@ package com.alibaba.nacos.config.server.model; +import com.alibaba.nacos.common.utils.CollectionUtils; +import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.config.server.utils.SimpleReadWriteLock; import com.alibaba.nacos.core.utils.StringPool; @@ -127,5 +129,19 @@ public void clearConfigGrays() { this.configCacheGray = null; this.sortedConfigCacheGrayList = null; } + + /** + * Checks if the configuration is effective for the specified client IP and tag. + * + * @param tag The tag associated with the configuration. + * @param clientIp The IP address of the client. + * @return true if the configuration is effective for the client, false otherwise. + */ + public boolean effectiveForClient(String tag, String clientIp) { + if (isBeta && CollectionUtils.isNotEmpty(ips4Beta) && !ips4Beta.contains(clientIp)) { + return false; + } + return StringUtils.isBlank(tag) || (getConfigCacheTags() != null && getConfigCacheTags().containsKey(tag)); + } } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/model/event/ConfigBatchFuzzyListenEvent.java b/config/src/main/java/com/alibaba/nacos/config/server/model/event/ConfigBatchFuzzyListenEvent.java new file mode 100644 index 00000000000..2e44763ee9e --- /dev/null +++ b/config/src/main/java/com/alibaba/nacos/config/server/model/event/ConfigBatchFuzzyListenEvent.java @@ -0,0 +1,142 @@ +/* + * 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; + +import java.util.Set; + +/** + * 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 ConfigBatchFuzzyListenEvent extends Event { + + private static final long serialVersionUID = 1953965691384930209L; + + /** + * ID of the client making the request. + */ + private String clientId; + + /** + * Pattern for matching group keys. + */ + private String keyGroupPattern; + + /** + * Set of existing group keys associated with the client. + */ + private Set clientExistingGroupKeys; + + /** + * Flag indicating whether the client is initializing. + */ + private boolean isInitializing; + + /** + * Constructs a new ConfigBatchFuzzyListenEvent with the specified parameters. + * + * @param clientId ID of the client making the request + * @param clientExistingGroupKeys Set of existing group keys associated with the client + * @param keyGroupPattern Pattern for matching group keys + * @param isInitializing Flag indicating whether the client is initializing + */ + public ConfigBatchFuzzyListenEvent(String clientId, Set clientExistingGroupKeys, String keyGroupPattern, + boolean isInitializing) { + this.clientId = clientId; + this.clientExistingGroupKeys = clientExistingGroupKeys; + this.keyGroupPattern = keyGroupPattern; + this.isInitializing = isInitializing; + } + + /** + * Get the ID of the client making the request. + * + * @return The client ID + */ + public String getClientId() { + return clientId; + } + + /** + * Set the ID of the client making the request. + * + * @param clientId The client ID to be set + */ + public void setClientId(String clientId) { + this.clientId = clientId; + } + + /** + * Get the pattern for matching group keys. + * + * @return The key group pattern + */ + public String getKeyGroupPattern() { + return keyGroupPattern; + } + + /** + * Set the pattern for matching group keys. + * + * @param keyGroupPattern The key group pattern to be set + */ + public void setKeyGroupPattern(String keyGroupPattern) { + this.keyGroupPattern = keyGroupPattern; + } + + /** + * Get the set of existing group keys associated with the client. + * + * @return The set of existing group keys + */ + public Set getClientExistingGroupKeys() { + return clientExistingGroupKeys; + } + + /** + * Set the set of existing group keys associated with the client. + * + * @param clientExistingGroupKeys The set of existing group keys to be set + */ + public void setClientExistingGroupKeys(Set clientExistingGroupKeys) { + this.clientExistingGroupKeys = clientExistingGroupKeys; + } + + /** + * Check whether the client is initializing. + * + * @return True if the client is initializing, otherwise false + */ + public boolean isInitializing() { + return isInitializing; + } + + /** + * Set the flag indicating whether the client is initializing. + * + * @param initializing True if the client is initializing, otherwise false + */ + public void setInitializing(boolean initializing) { + isInitializing = initializing; + } +} diff --git a/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigBatchFuzzyListenRequestHandler.java b/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigBatchFuzzyListenRequestHandler.java new file mode 100644 index 00000000000..0a344ba2123 --- /dev/null +++ b/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigBatchFuzzyListenRequestHandler.java @@ -0,0 +1,107 @@ +/* + * 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.ConfigBatchFuzzyListenRequest; +import com.alibaba.nacos.api.config.remote.response.ConfigBatchFuzzyListenResponse; +import com.alibaba.nacos.api.exception.NacosException; +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.common.utils.CollectionUtils; +import com.alibaba.nacos.common.utils.GroupKeyPattern; +import com.alibaba.nacos.config.server.model.event.ConfigBatchFuzzyListenEvent; +import com.alibaba.nacos.config.server.utils.GroupKey; +import com.alibaba.nacos.core.control.TpsControl; +import com.alibaba.nacos.core.paramcheck.ExtractorManager; +import com.alibaba.nacos.core.paramcheck.impl.ConfigBatchFuzzyListenRequestParamsExtractor; +import com.alibaba.nacos.core.remote.RequestHandler; +import com.alibaba.nacos.core.utils.StringPool; +import com.alibaba.nacos.plugin.auth.constant.ActionTypes; +import com.alibaba.nacos.plugin.auth.constant.SignType; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.Set; +import java.util.stream.Collectors; + +/** + * Handler for processing batch fuzzy listen requests. + *

+ * This handler is responsible for processing batch fuzzy listen requests sent by clients. It adds or removes clients + * from the fuzzy listening context based on the request, and publishes corresponding events to notify interested + * parties. + *

+ * + * @author stone-98 + * @date 2024/3/4 + */ +@Component +public class ConfigBatchFuzzyListenRequestHandler + extends RequestHandler { + + /** + * Context for managing fuzzy listen changes. + */ + @Autowired + private ConfigChangeListenContext configChangeListenContext; + + /** + * Handles the batch fuzzy listen request. + *

+ * This method processes the batch fuzzy listen request by adding or removing clients from the fuzzy listening + * context based on the request, and publishes corresponding events to notify interested parties. + *

+ * + * @param request The batch fuzzy listen request + * @param meta Request meta information + * @return The response to the batch fuzzy listen request + * @throws NacosException If an error occurs while processing the request + */ + @Override + @TpsControl(pointName = "ConfigFuzzyListen") + @Secured(action = ActionTypes.READ, signType = SignType.CONFIG) + @ExtractorManager.Extractor(rpcExtractor = ConfigBatchFuzzyListenRequestParamsExtractor.class) + public ConfigBatchFuzzyListenResponse handle(ConfigBatchFuzzyListenRequest request, RequestMeta meta) + throws NacosException { + String connectionId = StringPool.get(meta.getConnectionId()); + for (ConfigBatchFuzzyListenRequest.Context context : request.getContexts()) { + String groupKeyPattern = GroupKeyPattern.generateFuzzyListenGroupKeyPattern(context.getDataIdPattern(), + context.getGroup(), context.getTenant()); + groupKeyPattern = StringPool.get(groupKeyPattern); + if (context.isListen()) { + // Add client to the fuzzy listening context + configChangeListenContext.addFuzzyListen(groupKeyPattern, connectionId); + // Get existing group keys for the client and publish initialization event + Set clientExistingGroupKeys = null; + if (CollectionUtils.isNotEmpty(context.getDataIds())) { + clientExistingGroupKeys = context.getDataIds().stream() + .map(dataId -> GroupKey.getKeyTenant(dataId, context.getGroup(), context.getTenant())) + .collect(Collectors.toSet()); + } + NotifyCenter.publishEvent( + new ConfigBatchFuzzyListenEvent(connectionId, clientExistingGroupKeys, groupKeyPattern, + context.isInitializing())); + } else { + // Remove client from the fuzzy listening context + configChangeListenContext.removeFuzzyListen(groupKeyPattern, connectionId); + } + } + // Return response + return new ConfigBatchFuzzyListenResponse(); + } +} diff --git a/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigChangeListenContext.java b/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigChangeListenContext.java index adf7453558a..c71ef288f72 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigChangeListenContext.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigChangeListenContext.java @@ -17,6 +17,7 @@ package com.alibaba.nacos.config.server.remote; import com.alibaba.nacos.common.utils.CollectionUtils; +import com.alibaba.nacos.common.utils.GroupKeyPattern; import org.springframework.stereotype.Component; import java.util.Collection; @@ -36,18 +37,100 @@ @Component public class ConfigChangeListenContext { + /** + * groupKeyPattern -> connection set. + */ + private final Map> keyPatternContext = new ConcurrentHashMap<>(); + /** * groupKey-> connection set. */ - private ConcurrentHashMap> groupKeyContext = new ConcurrentHashMap<>(); + private final ConcurrentHashMap> groupKeyContext = new ConcurrentHashMap<>(); /** * connectionId-> group key set. */ - private ConcurrentHashMap> connectionIdContext = new ConcurrentHashMap<>(); + private final ConcurrentHashMap> connectionIdContext = new ConcurrentHashMap<>(); /** - * add listen. + * Adds a fuzzy listen connection ID associated with the specified group key pattern. If the key pattern does not + * exist in the context, a new entry will be created. If the key pattern already exists, the connection ID will be + * added to the existing set. + * + * @param groupKeyPattern The group key pattern to associate with the listen connection. + * @param connectId The connection ID to be added. + */ + public synchronized void addFuzzyListen(String groupKeyPattern, String connectId) { + // Add the connection ID to the set associated with the key pattern in keyPatternContext + keyPatternContext.computeIfAbsent(groupKeyPattern, k -> new HashSet<>()).add(connectId); + } + + /** + * Removes a fuzzy listen connection ID associated with the specified group key pattern. If the group key pattern + * exists in the context and the connection ID is found in the associated set, the connection ID will be removed + * from the set. If the set becomes empty after removal, the entry for the group key pattern will be removed from + * the context. + * + * @param groupKeyPattern The group key pattern associated with the listen connection to be removed. + * @param connectionId The connection ID to be removed. + */ + public synchronized void removeFuzzyListen(String groupKeyPattern, String connectionId) { + // Retrieve the set of connection IDs associated with the group key pattern + Set connectIds = keyPatternContext.get(groupKeyPattern); + if (CollectionUtils.isNotEmpty(connectIds)) { + // Remove the connection ID from the set if it exists + connectIds.remove(connectionId); + // Remove the entry for the group key pattern if the set becomes empty after removal + if (connectIds.isEmpty()) { + keyPatternContext.remove(groupKeyPattern); + } + } + } + + /** + * Retrieves the set of fuzzy listen connection IDs associated with the specified group key pattern. + * + * @param groupKeyPattern The group key pattern to retrieve the associated connection IDs. + * @return The set of connection IDs associated with the group key pattern, or null if no connections are found. + */ + public synchronized Set getFuzzyListeners(String groupKeyPattern) { + // Retrieve the set of connection IDs associated with the group key pattern + Set connectionIds = keyPatternContext.get(groupKeyPattern); + // If the set is not empty, create a new set and safely copy the connection IDs into it + if (CollectionUtils.isNotEmpty(connectionIds)) { + Set listenConnections = new HashSet<>(); + safeCopy(connectionIds, listenConnections); + return listenConnections; + } + // Return null if no connections are found for the specified group key pattern + return null; + } + + /** + * Retrieves the set of connection IDs matched with the specified group key. + * + * @param groupKey The group key to match with the key patterns. + * @return The set of connection IDs matched with the group key. + */ + public Set getConnectIdMatchedPatterns(String groupKey) { + // Initialize a set to store the matched connection IDs + Set connectIds = new HashSet<>(); + // Iterate over each key pattern in the context + for (String keyPattern : keyPatternContext.keySet()) { + // Check if the group key matches the current key pattern + if (GroupKeyPattern.isMatchPatternWithNamespace(groupKey, keyPattern)) { + // If matched, add the associated connection IDs to the set + Set connectIdSet = keyPatternContext.get(keyPattern); + if (CollectionUtils.isNotEmpty(connectIdSet)) { + connectIds.addAll(connectIdSet); + } + } + } + return connectIds; + } + + /** + * Add listen. * * @param groupKey groupKey. * @param connectionId connectionId. @@ -127,7 +210,7 @@ public synchronized void clearContextForConnectionId(final String connectionId) return; } for (Map.Entry groupKey : listenKeys.entrySet()) { - + Set connectionIds = groupKeyContext.get(groupKey.getKey()); if (CollectionUtils.isNotEmpty(connectionIds)) { connectionIds.remove(connectionId); @@ -137,9 +220,23 @@ public synchronized void clearContextForConnectionId(final String connectionId) } else { groupKeyContext.remove(groupKey.getKey()); } - + } connectionIdContext.remove(connectionId); + + // Remove any remaining fuzzy listen connections + for (Map.Entry> keyPatternContextEntry : keyPatternContext.entrySet()) { + String keyPattern = keyPatternContextEntry.getKey(); + Set connectionIds = keyPatternContextEntry.getValue(); + if (CollectionUtils.isEmpty(connectionIds)) { + keyPatternContext.remove(keyPattern); + } else { + connectionIds.remove(keyPattern); + if (CollectionUtils.isEmpty(connectionIds)) { + keyPatternContext.remove(keyPattern); + } + } + } } /** diff --git a/config/src/main/java/com/alibaba/nacos/config/server/remote/RpcFuzzyListenConfigChangeNotifier.java b/config/src/main/java/com/alibaba/nacos/config/server/remote/RpcFuzzyListenConfigChangeNotifier.java new file mode 100644 index 00000000000..3724a0eaf69 --- /dev/null +++ b/config/src/main/java/com/alibaba/nacos/config/server/remote/RpcFuzzyListenConfigChangeNotifier.java @@ -0,0 +1,216 @@ +/* + * 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.FuzzyListenNotifyChangeRequest; +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; +import com.alibaba.nacos.config.server.configuration.ConfigCommonConfig; +import com.alibaba.nacos.config.server.model.event.LocalDataChangeEvent; +import com.alibaba.nacos.config.server.service.ConfigCacheService; +import com.alibaba.nacos.config.server.utils.ConfigExecutor; +import com.alibaba.nacos.config.server.utils.GroupKey; +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; + +/** + * Notify remote clients about fuzzy listen configuration changes. Use subscriber mode to monitor local data changes, + * and push notifications to remote clients accordingly. + * + * @author stone-98 + * @date 2024/3/18 + */ +@Component(value = "rpcFuzzyListenConfigChangeNotifier") +public class RpcFuzzyListenConfigChangeNotifier extends Subscriber { + + private static final String POINT_FUZZY_LISTEN_CONFIG_PUSH = "POINT_FUZZY_LISTEN_CONFIG_PUSH"; + + private static final String POINT_FUZZY_LISTEN_CONFIG_PUSH_SUCCESS = "POINT_FUZZY_LISTEN_CONFIG_PUSH_SUCCESS"; + + private static final String POINT_FUZZY_LISTEN_CONFIG_PUSH_FAIL = "POINT_FUZZY_LISTEN_CONFIG_PUSH_FAIL"; + + private final ConfigChangeListenContext configChangeListenContext; + + private final ConnectionManager connectionManager; + + private final RpcPushService rpcPushService; + + private final TpsControlManager tpsControlManager; + + /** + * Constructs RpcFuzzyListenConfigChangeNotifier with the specified dependencies. + * + * @param configChangeListenContext The context for config change listening. + * @param connectionManager The manager for connections. + * @param rpcPushService The service for RPC push. + */ + public RpcFuzzyListenConfigChangeNotifier(ConfigChangeListenContext configChangeListenContext, + ConnectionManager connectionManager, RpcPushService rpcPushService) { + this.configChangeListenContext = configChangeListenContext; + this.connectionManager = connectionManager; + this.rpcPushService = rpcPushService; + this.tpsControlManager = ControlManagerCenter.getInstance().getTpsControlManager(); + NotifyCenter.registerSubscriber(this); + } + + @Override + public void onEvent(LocalDataChangeEvent event) { + String groupKey = event.groupKey; + String[] parseKey = GroupKey.parseKey(groupKey); + String dataId = parseKey[0]; + String group = parseKey[1]; + String tenant = parseKey.length > 2 ? parseKey[2] : ""; + + for (String clientId : configChangeListenContext.getConnectIdMatchedPatterns(groupKey)) { + Connection connection = connectionManager.getConnection(clientId); + if (null == connection) { + Loggers.REMOTE_PUSH.warn( + "clientId not found, Config change notification not sent. clientId={},keyGroupPattern={}", + clientId, event.groupKey); + continue; + } + ConnectionMeta metaInfo = connection.getMetaInfo(); + String clientIp = metaInfo.getClientIp(); + String clientTag = metaInfo.getTag(); + String appName = metaInfo.getAppName(); + boolean exists = ConfigCacheService.containsAndEffectiveForClient(groupKey, clientIp, clientTag); + FuzzyListenNotifyChangeRequest request = new FuzzyListenNotifyChangeRequest(tenant, group, dataId, exists); + int maxPushRetryTimes = ConfigCommonConfig.getInstance().getMaxPushRetryTimes(); + RpcPushTask rpcPushTask = new RpcPushTask(request, maxPushRetryTimes, clientId, clientIp, appName); + push(rpcPushTask); + } + } + + @Override + public Class subscribeType() { + return LocalDataChangeEvent.class; + } + + /** + * Pushes the notification to remote clients. + * + * @param retryTask The task for retrying to push notification. + */ + private void push(RpcPushTask retryTask) { + FuzzyListenNotifyChangeRequest notifyRequest = retryTask.notifyRequest; + if (retryTask.isOverTimes()) { + Loggers.REMOTE_PUSH.warn( + "push callback retry fail over times. dataId={},group={},tenant={},clientId={}, will unregister client.", + notifyRequest.getDataId(), notifyRequest.getGroup(), notifyRequest.getTenant(), + 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={},group={},tenant={},clientId={}", + notifyRequest.getDataId(), notifyRequest.getGroup(), notifyRequest.getTenant(), + retryTask.connectionId); + } + } + + /** + * Represents a task for pushing notification to remote clients. + */ + class RpcPushTask implements Runnable { + + FuzzyListenNotifyChangeRequest 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(FuzzyListenNotifyChangeRequest 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_LISTEN_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_LISTEN_CONFIG_PUSH_SUCCESS); + tpsControlManager.check(tpsCheckRequest); + } + + @Override + public void onFail(Throwable e) { + TpsCheckRequest tpsCheckRequest = new TpsCheckRequest(); + tpsCheckRequest.setPointName(POINT_FUZZY_LISTEN_CONFIG_PUSH_FAIL); + tpsControlManager.check(tpsCheckRequest); + Loggers.REMOTE_PUSH.warn("Push fail, dataId={}, group={}, tenant={}, clientId={}", + notifyRequest.getDataId(), notifyRequest.getGroup(), notifyRequest.getTenant(), + connectionId, e); + push(RpcPushTask.this); + } + + }, ConfigExecutor.getClientConfigNotifierServiceExecutor()); + } + } + } +} diff --git a/config/src/main/java/com/alibaba/nacos/config/server/remote/RpcFuzzyListenConfigDiffNotifier.java b/config/src/main/java/com/alibaba/nacos/config/server/remote/RpcFuzzyListenConfigDiffNotifier.java new file mode 100644 index 00000000000..67e7a46de42 --- /dev/null +++ b/config/src/main/java/com/alibaba/nacos/config/server/remote/RpcFuzzyListenConfigDiffNotifier.java @@ -0,0 +1,488 @@ +/* + * 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.common.Constants; +import com.alibaba.nacos.api.config.remote.request.FuzzyListenNotifyDiffRequest; +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; +import com.alibaba.nacos.common.utils.CollectionUtils; +import com.alibaba.nacos.common.utils.GroupKeyPattern; +import com.alibaba.nacos.config.server.configuration.ConfigCommonConfig; +import com.alibaba.nacos.config.server.model.event.ConfigBatchFuzzyListenEvent; +import com.alibaba.nacos.config.server.service.ConfigCacheService; +import com.alibaba.nacos.config.server.utils.ConfigExecutor; +import com.alibaba.nacos.config.server.utils.GroupKey; +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.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * Handles batch fuzzy listen events and pushes corresponding notifications to clients. + * + * @author stone-98 + * @date 2024/3/18 + */ +@Component(value = "rpcFuzzyListenConfigDiffNotifier") +public class RpcFuzzyListenConfigDiffNotifier extends Subscriber { + + 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; + + public RpcFuzzyListenConfigDiffNotifier(ConnectionManager connectionManager, RpcPushService rpcPushService) { + this.connectionManager = connectionManager; + this.tpsControlManager = ControlManagerCenter.getInstance().getTpsControlManager(); + this.rpcPushService = rpcPushService; + 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 + */ + private static void push(RpcPushTask retryTask, ConnectionManager connectionManager) { + FuzzyListenNotifyDiffRequest 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. + * + * @param event The ConfigBatchFuzzyListenEvent to handle + */ + @Override + public void onEvent(ConfigBatchFuzzyListenEvent event) { + // Get the connection for the client + Connection connection = connectionManager.getConnection(event.getClientId()); + if (connection == null) { + Loggers.REMOTE_PUSH.warn( + "clientId not found, Config diff notification not sent. clientId={},keyGroupPattern={}", + event.getClientId(), event.getKeyGroupPattern()); + // If connection is not available, return + return; + } + + // Retrieve meta information for the connection + ConnectionMeta metaInfo = connection.getMetaInfo(); + String clientIp = metaInfo.getClientIp(); + String clientTag = metaInfo.getTag(); + + // Match client effective group keys based on the event pattern, client IP, and tag + Set matchGroupKeys = ConfigCacheService.matchClientEffectiveGroupKeys(event.getKeyGroupPattern(), + clientIp, clientTag); + + // Retrieve existing group keys for the client from the event + Set clientExistingGroupKeys = event.getClientExistingGroupKeys(); + + // Check if both matched and existing group keys are empty, if so, return + if (CollectionUtils.isEmpty(matchGroupKeys) && CollectionUtils.isEmpty(clientExistingGroupKeys)) { + return; + } + + // Calculate and merge configuration states based on matched and existing group keys + List configStates = calculateAndMergeToConfigState(matchGroupKeys, clientExistingGroupKeys); + + // If no config states are available, return + if (CollectionUtils.isEmpty(configStates)) { + return; + } + + int batchSize = ConfigCommonConfig.getInstance().getBatchSize(); + // Divide config states into batches + List> divideConfigStatesIntoBatches = divideConfigStatesIntoBatches(configStates, batchSize); + + // Calculate the number of batches and initialize push batch finish count + int originBatchSize = divideConfigStatesIntoBatches.size(); + AtomicInteger pushBatchFinishCount = new AtomicInteger(0); + + // Iterate over each batch of config states + for (List configStateList : divideConfigStatesIntoBatches) { + // Map config states to FuzzyListenNotifyDiffRequest.Context objects + Set contexts = configStateList.stream().map(state -> { + String[] parseKey = GroupKey.parseKey(state.getGroupKey()); + String dataId = parseKey[0]; + String group = parseKey[1]; + String tenant = parseKey.length > 2 ? parseKey[2] : Constants.DEFAULT_NAMESPACE_ID; + String changeType = event.isInitializing() ? Constants.ConfigChangeType.LISTEN_INIT + : (state.isExist() ? Constants.ConfigChangeType.ADD_CONFIG + : Constants.ConfigChangeType.DELETE_CONFIG); + return FuzzyListenNotifyDiffRequest.Context.build(tenant, group, dataId, changeType); + }).collect(Collectors.toSet()); + + // Remove namespace from the pattern + String patternWithoutNameSpace = GroupKeyPattern.getPatternRemovedNamespace(event.getKeyGroupPattern()); + + // Build FuzzyListenNotifyDiffRequest with contexts and pattern + FuzzyListenNotifyDiffRequest request = FuzzyListenNotifyDiffRequest.buildInitRequest(contexts, + patternWithoutNameSpace); + + int maxPushRetryTimes = ConfigCommonConfig.getInstance().getMaxPushRetryTimes(); + // Create RPC push task and push the request to the client + RpcPushTask rpcPushTask = new RpcPushTask(request, pushBatchFinishCount, originBatchSize, maxPushRetryTimes, + event.getClientId(), clientIp, metaInfo.getAppName()); + push(rpcPushTask, connectionManager); + } + } + + /** + * Calculates and merges the differences between the matched group keys and the client's existing group keys into a + * list of ConfigState objects. + * + * @param matchGroupKeys The matched group keys set + * @param clientExistingGroupKeys The client's existing group keys set + * @return The merged list of ConfigState objects representing the states to be added or removed + */ + private List calculateAndMergeToConfigState(Set matchGroupKeys, + Set clientExistingGroupKeys) { + // Calculate the set of group keys to be added and removed + Set addGroupKeys = new HashSet<>(); + if (CollectionUtils.isNotEmpty(matchGroupKeys)) { + addGroupKeys.addAll(matchGroupKeys); + } + if (CollectionUtils.isNotEmpty(clientExistingGroupKeys)) { + addGroupKeys.removeAll(clientExistingGroupKeys); + } + + Set removeGroupKeys = new HashSet<>(); + if (CollectionUtils.isNotEmpty(clientExistingGroupKeys)) { + removeGroupKeys.addAll(clientExistingGroupKeys); + } + if (CollectionUtils.isNotEmpty(matchGroupKeys)) { + removeGroupKeys.removeAll(matchGroupKeys); + } + + // Convert the group keys to be added and removed into corresponding ConfigState objects and merge them into a list + return Stream.concat(addGroupKeys.stream().map(groupKey -> new ConfigState(groupKey, true)), + removeGroupKeys.stream().map(groupKey -> new ConfigState(groupKey, false))) + .collect(Collectors.toList()); + } + + @Override + public Class subscribeType() { + return ConfigBatchFuzzyListenEvent.class; + } + + /** + * Divides a collection of items into batches. + * + * @param configStates The collection of items to be divided into batches + * @param batchSize The size of each batch + * @param The type of items in the collection + * @return A list of batches, each containing a sublist of items + */ + private List> divideConfigStatesIntoBatches(Collection configStates, int batchSize) { + // Initialize an index to track the current batch number + AtomicInteger index = new AtomicInteger(); + + // Group the elements into batches based on their index divided by the batch size + return new ArrayList<>( + configStates.stream().collect(Collectors.groupingBy(e -> index.getAndIncrement() / batchSize)) + .values()); + } + + /** + * ConfigState. + */ + public static class ConfigState { + + /** + * The group key associated with the configuration. + */ + private String groupKey; + + /** + * Indicates whether the configuration exists or not. + */ + private boolean exist; + + /** + * Constructs a new ConfigState instance with the given group key and existence flag. + * + * @param groupKey The group key associated with the configuration. + * @param exist {@code true} if the configuration exists, {@code false} otherwise. + */ + public ConfigState(String groupKey, boolean exist) { + this.groupKey = groupKey; + this.exist = exist; + } + + /** + * Retrieves the group key associated with the configuration. + * + * @return The group key. + */ + public String getGroupKey() { + return groupKey; + } + + /** + * Sets the group key associated with the configuration. + * + * @param groupKey The group key to set. + */ + public void setGroupKey(String groupKey) { + this.groupKey = groupKey; + } + + /** + * Checks whether the configuration exists or not. + * + * @return {@code true} if the configuration exists, {@code false} otherwise. + */ + public boolean isExist() { + return exist; + } + + /** + * Sets the existence flag of the configuration. + * + * @param exist {@code true} if the configuration exists, {@code false} otherwise. + */ + public void setExist(boolean exist) { + this.exist = exist; + } + } + + /** + * Represents a task for pushing FuzzyListenNotifyDiffRequest to clients. + */ + class RpcPushTask implements Runnable { + + /** + * The FuzzyListenNotifyDiffRequest to be pushed. + */ + FuzzyListenNotifyDiffRequest 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; + + /** + * The IP address of the client. + */ + String clientIp; + + /** + * The name of the client's application. + */ + String appName; + + /** + * The counter for tracking the number of finished push batches. + */ + AtomicInteger pushBatchFinishCount; + + /** + * The original size of the batch before splitting. + */ + int originBatchSize; + + /** + * Constructs a new RpcPushTask with the specified parameters. + * + * @param notifyRequest The FuzzyListenNotifyDiffRequest to be pushed + * @param pushBatchFinishCount The counter for tracking the number of finished push batches + * @param originBatchSize The original size of the batch before splitting + * @param maxRetryTimes The maximum number of times to retry pushing the request + * @param connectionId The ID of the connection associated with the client + * @param clientIp The IP address of the client + * @param appName The name of the client's application + */ + public RpcPushTask(FuzzyListenNotifyDiffRequest notifyRequest, AtomicInteger pushBatchFinishCount, + int originBatchSize, int maxRetryTimes, String connectionId, String clientIp, String appName) { + this.notifyRequest = notifyRequest; + this.pushBatchFinishCount = pushBatchFinishCount; + this.originBatchSize = originBatchSize; + this.maxRetryTimes = maxRetryTimes; + this.connectionId = connectionId; + this.clientIp = clientIp; + this.appName = appName; + } + + /** + * 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 RpcPushCallback(this, tpsControlManager, connectionManager, pushBatchFinishCount, + originBatchSize), ConfigExecutor.getClientConfigNotifierServiceExecutor()); + } + } + } + + /** + * Represents a callback for handling the result of an RPC push operation. + */ + class RpcPushCallback extends AbstractPushCallBack { + + /** + * The RpcPushTask associated with the callback. + */ + RpcPushTask rpcPushTask; + + /** + * The TpsControlManager for checking TPS limits. + */ + TpsControlManager tpsControlManager; + + /** + * The ConnectionManager for managing client connections. + */ + ConnectionManager connectionManager; + + /** + * The counter for tracking the number of pushed batches. + */ + AtomicInteger pushBatchCount; + + /** + * The original size of the batch before splitting. + */ + int originBatchSize; + + /** + * Constructs a new RpcPushCallback with the specified parameters. + * + * @param rpcPushTask The RpcPushTask associated with the callback + * @param tpsControlManager The TpsControlManager for checking TPS limits + * @param connectionManager The ConnectionManager for managing client connections + * @param pushBatchCount The counter for tracking the number of pushed batches + * @param originBatchSize The original size of the batch before splitting + */ + public RpcPushCallback(RpcPushTask rpcPushTask, TpsControlManager tpsControlManager, + ConnectionManager connectionManager, AtomicInteger pushBatchCount, int originBatchSize) { + // Set the timeout for the callback + super(3000L); + this.rpcPushTask = rpcPushTask; + this.tpsControlManager = tpsControlManager; + this.connectionManager = connectionManager; + this.pushBatchCount = pushBatchCount; + this.originBatchSize = originBatchSize; + } + + /** + * 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 (pushBatchCount.get() < originBatchSize) { + pushBatchCount.incrementAndGet(); + } else if (pushBatchCount.get() == originBatchSize) { + FuzzyListenNotifyDiffRequest request = FuzzyListenNotifyDiffRequest.buildInitFinishRequest( + rpcPushTask.notifyRequest.getGroupKeyPattern()); + push(new RpcPushTask(request, pushBatchCount, originBatchSize, 50, rpcPushTask.connectionId, + rpcPushTask.clientIp, rpcPushTask.appName), 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={}", + rpcPushTask.notifyRequest.getGroupKeyPattern(), rpcPushTask.connectionId, e); + push(rpcPushTask, connectionManager); + } + } +} diff --git a/config/src/main/java/com/alibaba/nacos/config/server/service/ConfigCacheService.java b/config/src/main/java/com/alibaba/nacos/config/server/service/ConfigCacheService.java index da9ed9ef5f2..e6e0fe3f060 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/service/ConfigCacheService.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/service/ConfigCacheService.java @@ -17,6 +17,9 @@ package com.alibaba.nacos.config.server.service; import com.alibaba.nacos.common.notify.NotifyCenter; +import com.alibaba.nacos.common.utils.CollectionUtils; +import com.alibaba.nacos.common.utils.GroupKeyPattern; +import com.alibaba.nacos.common.utils.InternetAddressUtil; import com.alibaba.nacos.common.utils.MD5Utils; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.config.server.model.CacheItem; @@ -31,9 +34,14 @@ import com.alibaba.nacos.sys.env.EnvUtil; import java.io.IOException; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Set; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; import static com.alibaba.nacos.api.common.Constants.CLIENT_IP; import static com.alibaba.nacos.api.common.Constants.VIPSERVER_TAG; @@ -68,6 +76,37 @@ public static int groupCount() { return CACHE.size(); } + /** + * Matches the client effective group keys based on the specified group key pattern, client IP, and tag. + * + * @param groupKeyPattern The pattern to match group keys. + * @param clientIp The IP address of the client. + * @param tag The tag associated with the configuration. + * @return A set of group keys that match the pattern and are effective for the client. + */ + public static Set matchClientEffectiveGroupKeys(String groupKeyPattern, String clientIp, String tag) { + return CACHE.entrySet().stream() + .filter(entry -> GroupKeyPattern.isMatchPatternWithNamespace(entry.getKey(), groupKeyPattern)) + .filter(entry -> entry.getValue().effectiveForClient(tag, clientIp)).map(Map.Entry::getKey) + .collect(Collectors.toSet()); + } + + /** + * Checks if the specified group key is present in the cache and effective for the client. + * + * @param groupKey The group key to check. + * @param clientIp The IP address of the client. + * @param tag The tag associated with the configuration. + * @return true if the group key is present in the cache and effective for the client, false otherwise. + */ + public static boolean containsAndEffectiveForClient(String groupKey, String clientIp, String tag) { + if (!CACHE.containsKey(groupKey)) { + return false; + } + CacheItem cacheItem = CACHE.get(groupKey); + return cacheItem.effectiveForClient(tag, clientIp); + } + /** * Save config file and update md5 value in cache. * @@ -200,8 +239,6 @@ public static boolean dumpGray(String dataId, String group, String tenant, Strin //check timestamp long localGrayLastModifiedTs = ConfigCacheService.getGrayLastModifiedTs(groupKey, grayName); - - boolean timestampOutdated = lastModifiedTs < localGrayLastModifiedTs; if (timestampOutdated) { DUMP_LOG.warn("[dump-gray-ignore] timestamp is outdated,groupKey={}", groupKey); return true; diff --git a/core/src/main/java/com/alibaba/nacos/core/paramcheck/impl/ConfigBatchFuzzyListenRequestParamsExtractor.java b/core/src/main/java/com/alibaba/nacos/core/paramcheck/impl/ConfigBatchFuzzyListenRequestParamsExtractor.java new file mode 100644 index 00000000000..73c3c01b834 --- /dev/null +++ b/core/src/main/java/com/alibaba/nacos/core/paramcheck/impl/ConfigBatchFuzzyListenRequestParamsExtractor.java @@ -0,0 +1,73 @@ +/* + * 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.core.paramcheck.impl; + +import com.alibaba.nacos.api.config.remote.request.ConfigBatchFuzzyListenRequest; +import com.alibaba.nacos.api.exception.NacosException; +import com.alibaba.nacos.api.remote.request.Request; +import com.alibaba.nacos.common.paramcheck.ParamInfo; +import com.alibaba.nacos.common.utils.CollectionUtils; +import com.alibaba.nacos.core.paramcheck.AbstractRpcParamExtractor; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +/** + * Extractor for parameters of {@link ConfigBatchFuzzyListenRequest}. This extractor retrieves parameter information + * from the request object and constructs {@link ParamInfo} instances representing the namespace ID, group, and data IDs + * contained in the request's contexts. + * + * @author stone-98 + * @date 2024/3/5 + */ +public class ConfigBatchFuzzyListenRequestParamsExtractor extends AbstractRpcParamExtractor { + + /** + * Extracts parameter information from the given request. + * + * @param request The request object to extract parameter information from. + * @return A list of {@link ParamInfo} instances representing the extracted parameters. + * @throws NacosException If an error occurs while extracting parameter information. + */ + @Override + public List extractParam(Request request) throws NacosException { + ConfigBatchFuzzyListenRequest req = (ConfigBatchFuzzyListenRequest) request; + Set contexts = req.getContexts(); + List paramInfos = new ArrayList<>(); + if (contexts == null) { + return paramInfos; + } + for (ConfigBatchFuzzyListenRequest.Context context : contexts) { + // Extract namespace ID and group from the context + ParamInfo paramInfo1 = new ParamInfo(); + paramInfo1.setNamespaceId(context.getTenant()); + paramInfo1.setGroup(context.getGroup()); + paramInfos.add(paramInfo1); + + // Extract data IDs from the context if present + if (CollectionUtils.isNotEmpty(context.getDataIds())) { + for (String dataId : context.getDataIds()) { + ParamInfo paramInfo2 = new ParamInfo(); + paramInfo2.setDataId(dataId); + paramInfos.add(paramInfo2); + } + } + } + return paramInfos; + } +} diff --git a/example/src/main/java/com/alibaba/nacos/example/FuzzyListenExample.java b/example/src/main/java/com/alibaba/nacos/example/FuzzyListenExample.java new file mode 100644 index 00000000000..40622c4c4d7 --- /dev/null +++ b/example/src/main/java/com/alibaba/nacos/example/FuzzyListenExample.java @@ -0,0 +1,98 @@ +/* + * 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.example; + +import com.alibaba.nacos.api.config.ConfigFactory; +import com.alibaba.nacos.api.config.ConfigService; +import com.alibaba.nacos.api.config.listener.AbstractFuzzyListenListener; +import com.alibaba.nacos.api.config.listener.FuzzyListenConfigChangeEvent; +import com.alibaba.nacos.api.exception.NacosException; + +import java.util.Properties; + +/** + * Nacos config fuzzy listen example. + *

+ * Add the JVM parameter to run the NamingExample: + * {@code -DserverAddr=${nacos.server.ip}:${nacos.server.port} -Dnamespace=${namespaceId}} + *

+ *

+ * This example demonstrates how to use fuzzy listening for Nacos configuration. + *

+ *

+ * Fuzzy listening allows you to monitor configuration changes that match a specified pattern. + *

+ *

+ * In this example, we publish several configurations with names starting with "test", and then add a fuzzy listener to + * listen for changes to configurations with names starting with "test". + *

+ *

+ * After publishing the configurations, the example waits for a brief period, then cancels the fuzzy listening. + *

+ * + * @author stone-98 + * @date 2024/3/14 + */ +public class FuzzyListenExample { + + public static void main(String[] args) throws NacosException, InterruptedException { + // Set up properties for Nacos Config Service + Properties properties = new Properties(); + properties.setProperty("serverAddr", System.getProperty("serverAddr", "localhost")); + properties.setProperty("namespace", System.getProperty("namespace", "public")); + + // Create a Config Service instance + ConfigService configService = ConfigFactory.createConfigService(properties); + + int publicConfigNum = 10; + // Publish some configurations for testing + for (int i = 0; i < publicConfigNum; i++) { + boolean isPublishOk = configService.publishConfig("test" + i, "DEFAULT_GROUP", "content"); + System.out.println("[publish result] " + isPublishOk); + } + + // Define a fuzzy listener to handle configuration changes + AbstractFuzzyListenListener listener = new AbstractFuzzyListenListener() { + @Override + public void onEvent(FuzzyListenConfigChangeEvent event) { + System.out.println("[fuzzy listen config change]" + event.toString()); + } + }; + + // Add the fuzzy listener to monitor configurations starting with "test" + configService.addFuzzyListener("test*", "DEFAULT_GROUP", listener); + System.out.println("[Fuzzy listening started.]"); + + // Publish more configurations to trigger the listener + Thread.sleep(1000); + boolean isPublishOkOne = configService.publishConfig("test-one", "DEFAULT_GROUP", "content"); + System.out.println("[publish result] " + isPublishOkOne); + + boolean isPublishOkTwo = configService.publishConfig("nacos-test-two", "DEFAULT_GROUP", "content"); + System.out.println("[publish result] " + isPublishOkTwo); + + boolean isPublishOkThree = configService.publishConfig("test", "DEFAULT_GROUP", "content"); + System.out.println("[publish result] " + isPublishOkThree); + + // Wait briefly before canceling the fuzzy listening + Thread.sleep(1000); + System.out.println("Cancel fuzzy listen..."); + + // Sleep to keep the program running for observation + Thread.sleep(3000); + } +} From a2368832f3db0bc2a112b726e8ad5456d4646df1 Mon Sep 17 00:00:00 2001 From: shiyiyue1102 Date: Fri, 27 Dec 2024 10:36:45 +0800 Subject: [PATCH 02/15] Naming fuzzy watch support (#12997) Naming Fuzzy Watch by Chionanthus --- .../alibaba/nacos/api/common/Constants.java | 26 ++- .../nacos/api/naming/NamingService.java | 45 ++++ .../AbstractFuzzyWatchEventListener.java | 41 ++++ .../naming/listener/FuzzyWatchListener.java | 32 +++ .../listener/FuzzyWatchNotifyEvent.java | 55 +++++ .../nacos/api/naming/pojo/Service.java | 29 +++ .../naming/remote/NamingRemoteConstants.java | 4 + .../AbstractFuzzyWatchNotifyRequest.java | 61 +++++ .../FuzzyWatchNotifyChangeRequest.java | 55 +++++ .../request/FuzzyWatchNotifyInitRequest.java | 67 ++++++ .../remote/request/FuzzyWatchRequest.java | 43 ++++ .../remote/response/FuzzyWatchResponse.java | 61 +++++ .../response/NotifyFuzzyWatcherResponse.java | 28 +++ .../nacos/api/naming/utils/NamingUtils.java | 120 ++++++++++ .../com.alibaba.nacos.api.remote.Payload | 5 + .../client/naming/NacosNamingService.java | 70 +++++- .../cache/FuzzyWatchServiceListHolder.java | 142 ++++++++++++ .../naming/event/FuzzyWatchNotifyEvent.java | 85 +++++++ .../naming/event/ServicesChangeNotifier.java | 147 ++++++++++++ .../naming/remote/NamingClientProxy.java | 27 +++ .../remote/NamingClientProxyDelegate.java | 34 ++- .../remote/gprc/NamingGrpcClientProxy.java | 71 +++++- .../remote/gprc/NamingPushRequestHandler.java | 12 +- .../gprc/redo/NamingGrpcRedoService.java | 101 +++++++++ .../remote/gprc/redo/RedoScheduledTask.java | 39 ++++ .../gprc/redo/data/FuzzyWatcherRedoData.java | 33 +++ .../remote/http/NamingHttpClientProxy.java | 15 ++ .../nacos-client/reflect-config.json | 35 ++- .../client/naming/NacosNamingServiceTest.java | 1 - .../remote/AbstractNamingClientProxyTest.java | 15 ++ .../remote/NamingClientProxyDelegateTest.java | 24 +- .../gprc/NamingGrpcClientProxyTest.java | 13 +- .../gprc/NamingPushRequestHandlerTest.java | 6 +- .../nacos/example/FuzzyWatchExample.java | 115 ++++++++++ .../naming/core/v2/client/AbstractClient.java | 31 +++ .../nacos/naming/core/v2/client/Client.java | 31 +++ .../v2/event/client/ClientOperationEvent.java | 39 ++++ .../core/v2/event/service/ServiceEvent.java | 53 ++++- .../v2/index/ClientServiceIndexesManager.java | 131 ++++++++++- .../metadata/InstanceMetadataProcessor.java | 3 +- .../v2/service/ClientOperationService.java | 24 ++ .../service/ClientOperationServiceProxy.java | 10 + .../EphemeralClientOperationServiceImpl.java | 29 +++ .../PersistentClientOperationServiceImpl.java | 10 + .../heartbeat/ClientBeatProcessorV2.java | 3 +- .../heartbeat/UnhealthyInstanceChecker.java | 2 +- .../v2/NamingSubscriberServiceV2Impl.java | 38 ++++ .../naming/push/v2/executor/PushExecutor.java | 21 ++ .../v2/executor/PushExecutorDelegate.java | 15 ++ .../push/v2/executor/PushExecutorRpcImpl.java | 13 ++ .../push/v2/executor/PushExecutorUdpImpl.java | 12 + .../push/v2/task/FuzzyWatchInitDelayTask.java | 91 ++++++++ .../v2/task/FuzzyWatchInitExecuteTask.java | 209 ++++++++++++++++++ .../task/FuzzyWatchNotifyChangeDelayTask.java | 90 ++++++++ .../FuzzyWatchNotifyChangeExecuteTask.java | 145 ++++++++++++ .../task/FuzzyWatchPushDelayTaskEngine.java | 120 ++++++++++ .../rpc/handler/FuzzyWatchRequestHandler.java | 63 ++++++ .../ClientServiceIndexesManagerTest.java | 3 +- .../v2/NamingSubscriberServiceV2ImplTest.java | 5 +- .../push/v2/task/FixturePushExecutor.java | 17 ++ 60 files changed, 2825 insertions(+), 40 deletions(-) create mode 100644 api/src/main/java/com/alibaba/nacos/api/naming/listener/AbstractFuzzyWatchEventListener.java create mode 100644 api/src/main/java/com/alibaba/nacos/api/naming/listener/FuzzyWatchListener.java create mode 100644 api/src/main/java/com/alibaba/nacos/api/naming/listener/FuzzyWatchNotifyEvent.java create mode 100644 api/src/main/java/com/alibaba/nacos/api/naming/remote/request/AbstractFuzzyWatchNotifyRequest.java create mode 100644 api/src/main/java/com/alibaba/nacos/api/naming/remote/request/FuzzyWatchNotifyChangeRequest.java create mode 100644 api/src/main/java/com/alibaba/nacos/api/naming/remote/request/FuzzyWatchNotifyInitRequest.java create mode 100644 api/src/main/java/com/alibaba/nacos/api/naming/remote/request/FuzzyWatchRequest.java create mode 100644 api/src/main/java/com/alibaba/nacos/api/naming/remote/response/FuzzyWatchResponse.java create mode 100644 api/src/main/java/com/alibaba/nacos/api/naming/remote/response/NotifyFuzzyWatcherResponse.java create mode 100644 client/src/main/java/com/alibaba/nacos/client/naming/cache/FuzzyWatchServiceListHolder.java create mode 100644 client/src/main/java/com/alibaba/nacos/client/naming/event/FuzzyWatchNotifyEvent.java create mode 100644 client/src/main/java/com/alibaba/nacos/client/naming/event/ServicesChangeNotifier.java create mode 100644 client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/redo/data/FuzzyWatcherRedoData.java create mode 100644 example/src/main/java/com/alibaba/nacos/example/FuzzyWatchExample.java create mode 100644 naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchInitDelayTask.java create mode 100644 naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchInitExecuteTask.java create mode 100644 naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchNotifyChangeDelayTask.java create mode 100644 naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchNotifyChangeExecuteTask.java create mode 100644 naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchPushDelayTaskEngine.java create mode 100644 naming/src/main/java/com/alibaba/nacos/naming/remote/rpc/handler/FuzzyWatchRequestHandler.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 509972f07f0..b8c8e3eeaca 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 @@ -194,8 +194,12 @@ public class Constants { public static final int WRITE_REDIRECT_CODE = 307; + public static final String NAMESPACE_ID_SPLITER = ">>"; + public static final String SERVICE_INFO_SPLITER = "@@"; + public static final String MATCH_PATTERN_SPLITER = "##"; + public static final int SERVICE_INFO_SPLIT_COUNT = 2; public static final String NULL_STRING = "null"; @@ -212,7 +216,7 @@ public class Constants { public static final String ALL_PATTERN = "*"; - public static final String FUZZY_LISTEN_PATTERN_WILDCARD = "*"; + public static final String FUZZY_WATCH_PATTERN_WILDCARD = "*"; public static final String COLON = ":"; @@ -292,6 +296,26 @@ public static class Naming { public static final String CMDB_CONTEXT_TYPE = "CMDB"; } + /** + * The constants in fuzzy watch event type directory. + */ + public static class ServiceChangedType { + + public static final String ADD_SERVICE = "ADD_SERVICE"; + + public static final String DELETE_SERVICE = "DELETE_SERVICE"; + + public static final String INSTANCE_CHANGED = "INSTANCE_CHANGED"; + + public static final String HEART_BEAT = "HEART_BEAT"; + + public static final String SERVICE_SUBSCRIBED = "SERVICE_SUBSCRIBED"; + + public static final String WATCH_INITIAL_MATCH = "WATCH_INITIAL_MATCH"; + + public static final String FINISH_WATCH_INIT = "FINISH_WATCH_INIT"; + } + /** * The constants in remote directory. */ diff --git a/api/src/main/java/com/alibaba/nacos/api/naming/NamingService.java b/api/src/main/java/com/alibaba/nacos/api/naming/NamingService.java index f10c25d3029..6edc947f578 100644 --- a/api/src/main/java/com/alibaba/nacos/api/naming/NamingService.java +++ b/api/src/main/java/com/alibaba/nacos/api/naming/NamingService.java @@ -17,6 +17,7 @@ package com.alibaba.nacos.api.naming; import com.alibaba.nacos.api.exception.NacosException; +import com.alibaba.nacos.api.naming.listener.AbstractFuzzyWatchEventListener; import com.alibaba.nacos.api.naming.listener.EventListener; import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.api.naming.pojo.ListView; @@ -555,6 +556,50 @@ void subscribe(String serviceName, String groupName, NamingSelector selector, Ev void unsubscribe(String serviceName, String groupName, List clusters, EventListener listener) throws NacosException; + /** + * According to matching rules, watch services within a specific scope, and receive notifications when + * changes occur in the services within the scope. + * When given a fixed group name, watch changes in all services under this group. + * + * @param fixedGroupName fixed group name for fuzzy watch + * @param listener event listener + * @throws NacosException nacos exception + */ + void fuzzyWatch(String fixedGroupName, AbstractFuzzyWatchEventListener listener) throws NacosException; + + /** + * According to matching rules, watch services within a specific scope, and receive notifications when + * changes occur in the services within the scope. + * When provided with a fixed group name and pattern of service name, watch changes in services under + * this group that match the specified pattern. + * + * @param serviceNamePattern service name pattern for fuzzy watch + * @param fixedGroupName fixed group name for fuzzy watch + * @param listener event listener + * @throws NacosException nacos exception + */ + void fuzzyWatch(String serviceNamePattern, String fixedGroupName, + AbstractFuzzyWatchEventListener listener) throws NacosException; + + /** + * Cancel fuzzy watch, and remove event listener of a pattern. + * + * @param fixedGroupName fixed group name for fuzzy watch + * @param listener event listener + * @throws NacosException nacos exception + */ + void cancelFuzzyWatch(String fixedGroupName, AbstractFuzzyWatchEventListener listener) throws NacosException; + + /** + * Cancel fuzzy watch, and remove event listener of a pattern. + * + * @param serviceNamePattern service name pattern for fuzzy watch + * @param fixedGroupName fixed group name for fuzzy watch + * @param listener event listener + * @throws NacosException nacos exception + */ + void cancelFuzzyWatch(String serviceNamePattern, String fixedGroupName, AbstractFuzzyWatchEventListener listener) throws NacosException; + /** * Unsubscribe event listener of service. * diff --git a/api/src/main/java/com/alibaba/nacos/api/naming/listener/AbstractFuzzyWatchEventListener.java b/api/src/main/java/com/alibaba/nacos/api/naming/listener/AbstractFuzzyWatchEventListener.java new file mode 100644 index 00000000000..e4c23dbe43b --- /dev/null +++ b/api/src/main/java/com/alibaba/nacos/api/naming/listener/AbstractFuzzyWatchEventListener.java @@ -0,0 +1,41 @@ +/* + * 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; + +import java.util.concurrent.Executor; + +/** + * Abstract fuzzy watch event listener, to support handle event by user custom executor. + * + * @author tanyongquan + */ +public abstract class AbstractFuzzyWatchEventListener implements FuzzyWatchListener { + + String uuid; + + public Executor getExecutor() { + return null; + } + + public final void setUuid(String uuid) { + this.uuid = uuid; + } + + public final String getUuid() { + return uuid; + } +} diff --git a/api/src/main/java/com/alibaba/nacos/api/naming/listener/FuzzyWatchListener.java b/api/src/main/java/com/alibaba/nacos/api/naming/listener/FuzzyWatchListener.java new file mode 100644 index 00000000000..ffd2090a799 --- /dev/null +++ b/api/src/main/java/com/alibaba/nacos/api/naming/listener/FuzzyWatchListener.java @@ -0,0 +1,32 @@ +/* + * 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; + +/** + * Fuzzy Watch Listener. + * + * @author tanyongquan + */ +public interface FuzzyWatchListener { + + /** + * callback event. + * + * @param event event + */ + void onEvent(FuzzyWatchNotifyEvent event); +} diff --git a/api/src/main/java/com/alibaba/nacos/api/naming/listener/FuzzyWatchNotifyEvent.java b/api/src/main/java/com/alibaba/nacos/api/naming/listener/FuzzyWatchNotifyEvent.java new file mode 100644 index 00000000000..29f07309389 --- /dev/null +++ b/api/src/main/java/com/alibaba/nacos/api/naming/listener/FuzzyWatchNotifyEvent.java @@ -0,0 +1,55 @@ +/* + * 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; + +import com.alibaba.nacos.api.naming.pojo.Service; + +/** + * Fuzzy Watch Notify Event. + * + * @author tanyongquan + */ +public class FuzzyWatchNotifyEvent implements Event { + + private Service service; + + private String changeType; + + public FuzzyWatchNotifyEvent() { + } + + public FuzzyWatchNotifyEvent(Service service, String changeType) { + this.service = service; + this.changeType = changeType; + } + + public Service getService() { + return service; + } + + public void setService(Service service) { + this.service = service; + } + + public String getChangeType() { + return changeType; + } + + public void setChangeType(String changeType) { + this.changeType = changeType; + } +} diff --git a/api/src/main/java/com/alibaba/nacos/api/naming/pojo/Service.java b/api/src/main/java/com/alibaba/nacos/api/naming/pojo/Service.java index 08fa6d269ca..3c800dd0bda 100644 --- a/api/src/main/java/com/alibaba/nacos/api/naming/pojo/Service.java +++ b/api/src/main/java/com/alibaba/nacos/api/naming/pojo/Service.java @@ -16,9 +16,12 @@ package com.alibaba.nacos.api.naming.pojo; +import com.alibaba.nacos.api.naming.utils.NamingUtils; + import java.io.Serializable; import java.util.HashMap; import java.util.Map; +import java.util.Objects; /** * Service of Nacos. @@ -63,6 +66,11 @@ public Service(String name) { this.name = name; } + public Service(String name, String groupName) { + this.name = name; + this.groupName = groupName; + } + public String getName() { return name; } @@ -95,6 +103,10 @@ public void setGroupName(String groupName) { this.groupName = groupName; } + public String getGroupedServiceName() { + return NamingUtils.getGroupedName(name, groupName); + } + public Map getMetadata() { return metadata; } @@ -112,4 +124,21 @@ public String toString() { return "Service{" + "name='" + name + '\'' + ", protectThreshold=" + protectThreshold + ", appName='" + appName + '\'' + ", groupName='" + groupName + '\'' + ", metadata=" + metadata + '}'; } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Service service = (Service) o; + return name.equals(service.name) && groupName.equals(service.groupName); + } + + @Override + public int hashCode() { + return Objects.hash(name, groupName); + } } diff --git a/api/src/main/java/com/alibaba/nacos/api/naming/remote/NamingRemoteConstants.java b/api/src/main/java/com/alibaba/nacos/api/naming/remote/NamingRemoteConstants.java index 4eb9cccc30c..36ee9f64034 100644 --- a/api/src/main/java/com/alibaba/nacos/api/naming/remote/NamingRemoteConstants.java +++ b/api/src/main/java/com/alibaba/nacos/api/naming/remote/NamingRemoteConstants.java @@ -30,6 +30,10 @@ public class NamingRemoteConstants { public static final String DE_REGISTER_INSTANCE = "deregisterInstance"; + public static final String FUZZY_WATCH_SERVICE = "fuzzyWatchService"; + + public static final String CANCEL_FUZZY_WATCH_SERVICE = "cancelFuzzyWatchService"; + public static final String QUERY_SERVICE = "queryService"; public static final String SUBSCRIBE_SERVICE = "subscribeService"; diff --git a/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/AbstractFuzzyWatchNotifyRequest.java b/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/AbstractFuzzyWatchNotifyRequest.java new file mode 100644 index 00000000000..b1678a8001d --- /dev/null +++ b/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/AbstractFuzzyWatchNotifyRequest.java @@ -0,0 +1,61 @@ +/* + * 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.remote.request; + +import com.alibaba.nacos.api.remote.request.ServerRequest; + +import static com.alibaba.nacos.api.common.Constants.Naming.NAMING_MODULE; + +/** + * Abstract fuzzy watch notify request, including basic fuzzy watch notify information. + * + * @author tanyongquan + */ +public abstract class AbstractFuzzyWatchNotifyRequest extends ServerRequest { + private String namespace; + + private String serviceChangedType; + + public AbstractFuzzyWatchNotifyRequest(){ + } + + public AbstractFuzzyWatchNotifyRequest(String namespace, String serviceChangedType) { + this.namespace = namespace; + this.serviceChangedType = serviceChangedType; + } + + public String getNamespace() { + return namespace; + } + + public void setNamespace(String namespace) { + this.namespace = namespace; + } + + public String getServiceChangedType() { + return serviceChangedType; + } + + public void setServiceChangedType(String serviceChangedType) { + this.serviceChangedType = serviceChangedType; + } + + @Override + public String getModule() { + return NAMING_MODULE; + } +} diff --git a/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/FuzzyWatchNotifyChangeRequest.java b/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/FuzzyWatchNotifyChangeRequest.java new file mode 100644 index 00000000000..bf31ecb58cd --- /dev/null +++ b/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/FuzzyWatchNotifyChangeRequest.java @@ -0,0 +1,55 @@ +/* + * 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.remote.request; + +/** + * Nacos fuzzy watch notify service change request, use it when one of the services changes. + * + * @author tanyongquan + */ +public class FuzzyWatchNotifyChangeRequest extends AbstractFuzzyWatchNotifyRequest { + + String serviceName; + + String groupName; + + public FuzzyWatchNotifyChangeRequest() { + } + + public FuzzyWatchNotifyChangeRequest(String namespace, String serviceName, + String groupName, String serviceChangedType) { + super(namespace, serviceChangedType); + this.serviceName = serviceName; + this.groupName = groupName; + } + + public String getServiceName() { + return serviceName; + } + + public void setServiceName(String serviceName) { + this.serviceName = serviceName; + } + + public String getGroupName() { + return groupName; + } + + public void setGroupName(String groupName) { + this.groupName = groupName; + } +} diff --git a/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/FuzzyWatchNotifyInitRequest.java b/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/FuzzyWatchNotifyInitRequest.java new file mode 100644 index 00000000000..0983df802d7 --- /dev/null +++ b/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/FuzzyWatchNotifyInitRequest.java @@ -0,0 +1,67 @@ +/* + * 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.remote.request; + +import com.alibaba.nacos.api.common.Constants; + +import java.util.Collection; +import java.util.HashSet; + +/** + * Nacos fuzzy watch initial notify request, use it when init a watch request, push service by batch. + * + * @author tanyongquan + */ +public class FuzzyWatchNotifyInitRequest extends AbstractFuzzyWatchNotifyRequest { + + private String pattern; + + private Collection servicesName; + + public FuzzyWatchNotifyInitRequest() { + } + + private FuzzyWatchNotifyInitRequest(String namespace, String pattern, String serviceChangedType, Collection servicesName) { + super(namespace, serviceChangedType); + this.servicesName = servicesName; + this.pattern = pattern; + } + + public static FuzzyWatchNotifyInitRequest buildInitRequest(String namespace, String pattern, Collection servicesName) { + return new FuzzyWatchNotifyInitRequest(namespace, pattern, Constants.ServiceChangedType.WATCH_INITIAL_MATCH, servicesName); + } + + public static FuzzyWatchNotifyInitRequest buildInitFinishRequest(String namespace, String pattern) { + return new FuzzyWatchNotifyInitRequest(namespace, pattern, Constants.ServiceChangedType.FINISH_WATCH_INIT, new HashSet<>(1)); + } + + public String getPattern() { + return pattern; + } + + public void setPattern(String pattern) { + this.pattern = pattern; + } + + public Collection getServicesName() { + return servicesName; + } + + public void setServicesName(Collection servicesName) { + this.servicesName = servicesName; + } +} diff --git a/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/FuzzyWatchRequest.java b/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/FuzzyWatchRequest.java new file mode 100644 index 00000000000..dd5015abd8b --- /dev/null +++ b/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/FuzzyWatchRequest.java @@ -0,0 +1,43 @@ +/* + * 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.remote.request; + +/** + * Nacos naming fuzzy watch service request. + * + * @author tanyongquan + */ +public class FuzzyWatchRequest extends AbstractNamingRequest { + + private String type; + + public FuzzyWatchRequest() { + } + + public FuzzyWatchRequest(String namespace, String serviceNamePattern, String groupNamePattern, String type) { + super(namespace, serviceNamePattern, groupNamePattern); + this.type = type; + } + + public void setType(String type) { + this.type = type; + } + + public String getType() { + return this.type; + } +} diff --git a/api/src/main/java/com/alibaba/nacos/api/naming/remote/response/FuzzyWatchResponse.java b/api/src/main/java/com/alibaba/nacos/api/naming/remote/response/FuzzyWatchResponse.java new file mode 100644 index 00000000000..6abbad6ba54 --- /dev/null +++ b/api/src/main/java/com/alibaba/nacos/api/naming/remote/response/FuzzyWatchResponse.java @@ -0,0 +1,61 @@ +/* + * 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.remote.response; + +import com.alibaba.nacos.api.remote.response.Response; +import com.alibaba.nacos.api.remote.response.ResponseCode; + +/** + * Nacos naming fuzzy watch service response. + * + * @author tanyongquan + */ +public class FuzzyWatchResponse extends Response { + + private String type; + + public FuzzyWatchResponse(){ + } + + public FuzzyWatchResponse(String type) { + this.type = type; + } + + public static FuzzyWatchResponse buildSuccessResponse(String type) { + return new FuzzyWatchResponse(type); + } + + /** + * Build fail response. + * + * @param message error message + * @return fail response + */ + public static FuzzyWatchResponse buildFailResponse(String message) { + FuzzyWatchResponse result = new FuzzyWatchResponse(); + result.setErrorInfo(ResponseCode.FAIL.getCode(), message); + return result; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } +} diff --git a/api/src/main/java/com/alibaba/nacos/api/naming/remote/response/NotifyFuzzyWatcherResponse.java b/api/src/main/java/com/alibaba/nacos/api/naming/remote/response/NotifyFuzzyWatcherResponse.java new file mode 100644 index 00000000000..ac9d55298db --- /dev/null +++ b/api/src/main/java/com/alibaba/nacos/api/naming/remote/response/NotifyFuzzyWatcherResponse.java @@ -0,0 +1,28 @@ +/* + * 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.remote.response; + +import com.alibaba.nacos.api.remote.response.Response; + +/** + * Response for notify fuzzy watcher. + * + * @author tanyongquan + */ +public class NotifyFuzzyWatcherResponse extends Response { + +} diff --git a/api/src/main/java/com/alibaba/nacos/api/naming/utils/NamingUtils.java b/api/src/main/java/com/alibaba/nacos/api/naming/utils/NamingUtils.java index 074c41749e2..69176918c7d 100644 --- a/api/src/main/java/com/alibaba/nacos/api/naming/utils/NamingUtils.java +++ b/api/src/main/java/com/alibaba/nacos/api/naming/utils/NamingUtils.java @@ -23,12 +23,14 @@ import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.api.utils.StringUtils; +import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.regex.Pattern; import static com.alibaba.nacos.api.common.Constants.CLUSTER_NAME_PATTERN_STRING; +import static com.alibaba.nacos.api.common.Constants.FUZZY_WATCH_PATTERN_WILDCARD; import static com.alibaba.nacos.api.common.Constants.NUMBER_PATTERN_STRING; /** @@ -189,6 +191,124 @@ public static void batchCheckInstanceIsLegal(List instances) throws Na } } + public static String getPatternWithNamespace(final String namespaceId, final String groupedPattern) { + if (StringUtils.isBlank(namespaceId)) { + throw new IllegalArgumentException("Param 'namespaceId' is illegal, namespaceId is blank"); + } + if (StringUtils.isBlank(groupedPattern)) { + throw new IllegalArgumentException("Param 'groupedPattern' is illegal, groupedPattern is blank"); + } + final String resultGroupedPattern = namespaceId + Constants.NAMESPACE_ID_SPLITER + groupedPattern; + return resultGroupedPattern.intern(); + } + + public static String getNamespaceFromPattern(String completedPattern) { + if (StringUtils.isBlank(completedPattern)) { + return StringUtils.EMPTY; + } + if (!completedPattern.contains(Constants.NAMESPACE_ID_SPLITER)) { + return Constants.DEFAULT_NAMESPACE_ID; + } + return completedPattern.split(Constants.NAMESPACE_ID_SPLITER)[0]; + } + + public static String getPatternRemovedNamespace(String completedPattern) { + if (StringUtils.isBlank(completedPattern)) { + return StringUtils.EMPTY; + } + if (!completedPattern.contains(Constants.NAMESPACE_ID_SPLITER)) { + return completedPattern; + } + return completedPattern.split(Constants.NAMESPACE_ID_SPLITER)[1]; + } + + /** + * Get the pattern watched under given namespace id. + * + * @param namespaceId name space id + * @param completedPatterns a set of all watched pattern(with namespace id) + * @return filtered pattern set + */ + public static Set filterPatternWithNamespace(String namespaceId, Set completedPatterns) { + Set patternsOfGivenNamespace = new HashSet<>(); + for (String each : completedPatterns) { + String nameSpaceOfPattern = getNamespaceFromPattern(each); + if (namespaceId.equals(nameSpaceOfPattern)) { + patternsOfGivenNamespace.add(getPatternRemovedNamespace(each)); + } + } + return patternsOfGivenNamespace; + } + + /** + * Given a service, and a list of watched patterns, return the patterns that the service can match. + * + * @param serviceName service Name + * @param groupName group Name + * @param watchPattern a list of completed watch patterns + * @return the patterns list that the service can match. + */ + public static Set getServiceMatchedPatterns(String serviceName, String groupName, Collection watchPattern) { + if (watchPattern == null || watchPattern.isEmpty()) { + return new HashSet<>(1); + } + Set matchedPatternList = new HashSet<>(); + for (String eachPattern : watchPattern) { + if (isMatchPattern(serviceName, groupName, getServiceName(eachPattern), getGroupName(eachPattern))) { + matchedPatternList.add(eachPattern); + } + } + return matchedPatternList; + } + + /** + * Given a list of service's name, and a pattern to watch, return the services that can match the pattern. + * + * @param servicesList a list of service's name + * @param serviceNamePattern service name Pattern + * @param groupNamePattern group name Pattern + * @return the patterns list that the service can match. + */ + public static Set getPatternMatchedServices(Collection servicesList, String serviceNamePattern, + String groupNamePattern) { + if (servicesList == null || servicesList.isEmpty()) { + return new HashSet<>(1); + } + Set matchList = new HashSet<>(); + for (String eachService : servicesList) { + if (isMatchPattern(getServiceName(eachService), getGroupName(eachService), serviceNamePattern, groupNamePattern)) { + matchList.add(eachService); + } + } + return matchList; + } + + /** + * Given a service name and a pattern to match, determine whether it can match. + * TODO:If want to add a matching method, can implement in here. + * + * @param serviceName service name to judge + * @param groupName group name to judge + * @param serviceNamePattern service name Pattern + * @param groupNamePattern group name Pattern + * @return matching result + */ + public static boolean isMatchPattern(String serviceName, String groupName, String serviceNamePattern, String groupNamePattern) { + // Only support prefix match or all match service name right now + // Only support fixed group name right now + if (serviceNamePattern.equals(FUZZY_WATCH_PATTERN_WILDCARD)) { + return groupName.equals(groupNamePattern); + } else if (serviceNamePattern.endsWith(FUZZY_WATCH_PATTERN_WILDCARD)) { + String serviceMatchName = serviceNamePattern.substring(0, serviceNamePattern.length() - 1); + return prefixMatchWithFixedGroupName(serviceName, serviceMatchName, groupName, groupNamePattern); + } + return false; + } + + private static boolean prefixMatchWithFixedGroupName(String serviceName, String serviceNamePrefix, String groupName, String fixedGroupName) { + return groupName.equals(fixedGroupName) && serviceName.startsWith(serviceNamePrefix); + } + /** * Check string is a number or not. * diff --git a/api/src/main/resources/META-INF/services/com.alibaba.nacos.api.remote.Payload b/api/src/main/resources/META-INF/services/com.alibaba.nacos.api.remote.Payload index eba07c73d27..3b4927ec774 100644 --- a/api/src/main/resources/META-INF/services/com.alibaba.nacos.api.remote.Payload +++ b/api/src/main/resources/META-INF/services/com.alibaba.nacos.api.remote.Payload @@ -49,15 +49,20 @@ com.alibaba.nacos.api.naming.remote.request.BatchInstanceRequest com.alibaba.nacos.api.naming.remote.request.InstanceRequest com.alibaba.nacos.api.naming.remote.request.PersistentInstanceRequest com.alibaba.nacos.api.naming.remote.request.NotifySubscriberRequest +com.alibaba.nacos.api.naming.remote.request.FuzzyWatchNotifyChangeRequest +com.alibaba.nacos.api.naming.remote.request.FuzzyWatchNotifyInitRequest com.alibaba.nacos.api.naming.remote.request.ServiceListRequest com.alibaba.nacos.api.naming.remote.request.ServiceQueryRequest com.alibaba.nacos.api.naming.remote.request.SubscribeServiceRequest +com.alibaba.nacos.api.naming.remote.request.FuzzyWatchRequest com.alibaba.nacos.api.naming.remote.response.BatchInstanceResponse com.alibaba.nacos.api.naming.remote.response.InstanceResponse com.alibaba.nacos.api.naming.remote.response.NotifySubscriberResponse +com.alibaba.nacos.api.naming.remote.response.NotifyFuzzyWatcherResponse com.alibaba.nacos.api.naming.remote.response.QueryServiceResponse com.alibaba.nacos.api.naming.remote.response.ServiceListResponse com.alibaba.nacos.api.naming.remote.response.SubscribeServiceResponse +com.alibaba.nacos.api.naming.remote.response.FuzzyWatchResponse com.alibaba.nacos.api.config.remote.request.ConfigBatchFuzzyListenRequest com.alibaba.nacos.api.config.remote.response.ConfigBatchFuzzyListenResponse com.alibaba.nacos.api.config.remote.request.FuzzyListenNotifyChangeRequest diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/NacosNamingService.java b/client/src/main/java/com/alibaba/nacos/client/naming/NacosNamingService.java index bf382f02e27..3125502b114 100644 --- a/client/src/main/java/com/alibaba/nacos/client/naming/NacosNamingService.java +++ b/client/src/main/java/com/alibaba/nacos/client/naming/NacosNamingService.java @@ -20,6 +20,7 @@ import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.naming.NamingService; +import com.alibaba.nacos.api.naming.listener.AbstractFuzzyWatchEventListener; import com.alibaba.nacos.api.naming.listener.EventListener; import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.api.naming.pojo.ListView; @@ -31,9 +32,12 @@ import com.alibaba.nacos.api.selector.AbstractSelector; import com.alibaba.nacos.client.env.NacosClientProperties; import com.alibaba.nacos.client.naming.cache.ServiceInfoHolder; +import com.alibaba.nacos.client.naming.cache.FuzzyWatchServiceListHolder; import com.alibaba.nacos.client.naming.core.Balancer; import com.alibaba.nacos.client.naming.event.InstancesChangeEvent; import com.alibaba.nacos.client.naming.event.InstancesChangeNotifier; +import com.alibaba.nacos.client.naming.event.FuzzyWatchNotifyEvent; +import com.alibaba.nacos.client.naming.event.ServicesChangeNotifier; import com.alibaba.nacos.client.naming.event.InstancesDiff; import com.alibaba.nacos.client.naming.remote.NamingClientProxy; import com.alibaba.nacos.client.naming.remote.NamingClientProxyDelegate; @@ -56,6 +60,7 @@ import java.util.Properties; import java.util.UUID; +import static com.alibaba.nacos.api.common.Constants.FUZZY_WATCH_PATTERN_WILDCARD; import static com.alibaba.nacos.client.naming.selector.NamingSelectorFactory.getUniqueClusterString; import static com.alibaba.nacos.client.utils.LogUtils.NAMING_LOGGER; @@ -83,8 +88,12 @@ public class NacosNamingService implements NamingService { private ServiceInfoHolder serviceInfoHolder; + private FuzzyWatchServiceListHolder fuzzyWatchServiceListHolder; + private InstancesChangeNotifier changeNotifier; + private ServicesChangeNotifier servicesChangeNotifier; + private NamingClientProxy clientProxy; private String notifierEventScope; @@ -113,9 +122,14 @@ private void init(Properties properties) throws NacosException { this.changeNotifier = new InstancesChangeNotifier(this.notifierEventScope); NotifyCenter.registerToPublisher(InstancesChangeEvent.class, 16384); NotifyCenter.registerSubscriber(changeNotifier); + this.servicesChangeNotifier = new ServicesChangeNotifier(this.notifierEventScope); + NotifyCenter.registerToPublisher(FuzzyWatchNotifyEvent.class, 16384); + NotifyCenter.registerSubscriber(servicesChangeNotifier); + this.serviceInfoHolder = new ServiceInfoHolder(namespace, this.notifierEventScope, nacosClientProperties); - this.clientProxy = new NamingClientProxyDelegate(this.namespace, serviceInfoHolder, nacosClientProperties, - changeNotifier); + this.fuzzyWatchServiceListHolder = new FuzzyWatchServiceListHolder(this.notifierEventScope, nacosClientProperties); + this.clientProxy = new NamingClientProxyDelegate(this.namespace, serviceInfoHolder, fuzzyWatchServiceListHolder, + nacosClientProperties, changeNotifier); } @Deprecated @@ -525,6 +539,58 @@ private void doUnsubscribe(String serviceName, String groupName, NamingSelector } } + @Override + public void fuzzyWatch(String fixedGroupName, AbstractFuzzyWatchEventListener listener) throws NacosException { + doFuzzyWatch(FUZZY_WATCH_PATTERN_WILDCARD, fixedGroupName, listener); + } + + @Override + public void fuzzyWatch(String serviceNamePattern, String fixedGroupName, + AbstractFuzzyWatchEventListener listener) throws NacosException { + // only support prefix match right now + if (!serviceNamePattern.endsWith(FUZZY_WATCH_PATTERN_WILDCARD)) { + if (serviceNamePattern.startsWith(FUZZY_WATCH_PATTERN_WILDCARD)) { + throw new UnsupportedOperationException("Suffix matching for service names is not supported yet." + + " It will be supported in future updates if needed."); + } else { + throw new UnsupportedOperationException("Illegal service name pattern, please read the documentation and pass a valid pattern."); + } + } + doFuzzyWatch(serviceNamePattern, fixedGroupName, listener); + } + + private void doFuzzyWatch(String serviceNamePattern, String groupNamePattern, + AbstractFuzzyWatchEventListener listener) throws NacosException { + if (null == listener) { + return; + } + String uuid = UUID.randomUUID().toString(); + listener.setUuid(uuid); + servicesChangeNotifier.registerFuzzyWatchListener(serviceNamePattern, groupNamePattern, listener); + clientProxy.fuzzyWatch(serviceNamePattern, groupNamePattern, uuid); + } + + @Override + public void cancelFuzzyWatch(String fixedGroupName, AbstractFuzzyWatchEventListener listener) throws NacosException { + doCancelFuzzyWatch(FUZZY_WATCH_PATTERN_WILDCARD, fixedGroupName, listener); + } + + @Override + public void cancelFuzzyWatch(String serviceNamePattern, String fixedGroupName, AbstractFuzzyWatchEventListener listener) throws NacosException { + doCancelFuzzyWatch(serviceNamePattern, fixedGroupName, listener); + } + + private void doCancelFuzzyWatch(String serviceNamePattern, String groupNamePattern, + AbstractFuzzyWatchEventListener listener) throws NacosException { + if (null == listener) { + return; + } + servicesChangeNotifier.deregisterFuzzyWatchListener(serviceNamePattern, groupNamePattern, listener); + if (!servicesChangeNotifier.isWatched(serviceNamePattern, groupNamePattern)) { + clientProxy.cancelFuzzyWatch(serviceNamePattern, groupNamePattern); + } + } + @Override public ListView getServicesOfServer(int pageNo, int pageSize) throws NacosException { return getServicesOfServer(pageNo, pageSize, Constants.DEFAULT_GROUP); diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/cache/FuzzyWatchServiceListHolder.java b/client/src/main/java/com/alibaba/nacos/client/naming/cache/FuzzyWatchServiceListHolder.java new file mode 100644 index 00000000000..dfd0e2bd240 --- /dev/null +++ b/client/src/main/java/com/alibaba/nacos/client/naming/cache/FuzzyWatchServiceListHolder.java @@ -0,0 +1,142 @@ +/* + * 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.cache; + +import com.alibaba.nacos.api.common.Constants; +import com.alibaba.nacos.api.naming.pojo.Service; +import com.alibaba.nacos.api.naming.remote.request.AbstractFuzzyWatchNotifyRequest; +import com.alibaba.nacos.api.naming.remote.request.FuzzyWatchNotifyChangeRequest; +import com.alibaba.nacos.api.naming.remote.request.FuzzyWatchNotifyInitRequest; +import com.alibaba.nacos.api.naming.utils.NamingUtils; +import com.alibaba.nacos.client.env.NacosClientProperties; +import com.alibaba.nacos.client.naming.event.FuzzyWatchNotifyEvent; +import com.alibaba.nacos.client.naming.utils.CollectionUtils; +import com.alibaba.nacos.common.notify.NotifyCenter; +import com.alibaba.nacos.common.utils.ConcurrentHashSet; + +import java.util.Collection; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Naming client fuzzy watch service list holder. + * + * @author tanyongquan + */ +public class FuzzyWatchServiceListHolder { + + private String notifierEventScope; + + /** + * The contents of {@code patternMatchMap} are Map{pattern -> Set[matched services]}. + */ + private Map> patternMatchMap = new ConcurrentHashMap<>(); + + public FuzzyWatchServiceListHolder(String notifierEventScope, NacosClientProperties properties) { + this.notifierEventScope = notifierEventScope; + } + + /** + * Publish service change event notify from nacos server about fuzzy watch. + * + * @param request watch notify request from Nacos server + */ + public void processFuzzyWatchNotify(AbstractFuzzyWatchNotifyRequest request) { + if (request instanceof FuzzyWatchNotifyInitRequest) { + FuzzyWatchNotifyInitRequest watchNotifyInitRequest = (FuzzyWatchNotifyInitRequest) request; + Set matchedServiceSet = patternMatchMap.computeIfAbsent(watchNotifyInitRequest.getPattern(), + keyInner -> new ConcurrentHashSet<>()); + Collection servicesName = watchNotifyInitRequest.getServicesName(); + for (String groupedName : servicesName) { + Service service = new Service(NamingUtils.getServiceName(groupedName), NamingUtils.getGroupName(groupedName)); + // may have a 'change event' sent to client before 'init event' + if (matchedServiceSet.add(service)) { + NotifyCenter.publishEvent(FuzzyWatchNotifyEvent.buildNotifyPatternAllListenersEvent(notifierEventScope, + service, watchNotifyInitRequest.getPattern(), Constants.ServiceChangedType.ADD_SERVICE)); + } + } + } else if (request instanceof FuzzyWatchNotifyChangeRequest) { + FuzzyWatchNotifyChangeRequest notifyChangeRequest = (FuzzyWatchNotifyChangeRequest) request; + Collection matchedPattern = NamingUtils.getServiceMatchedPatterns(notifyChangeRequest.getServiceName(), + notifyChangeRequest.getGroupName(), patternMatchMap.keySet()); + Service service = new Service(notifyChangeRequest.getServiceName(), notifyChangeRequest.getGroupName()); + String serviceChangeType = request.getServiceChangedType(); + + switch (serviceChangeType) { + case Constants.ServiceChangedType.ADD_SERVICE: + case Constants.ServiceChangedType.INSTANCE_CHANGED: + for (String pattern : matchedPattern) { + Set matchedServiceSet = patternMatchMap.get(pattern); + if (matchedServiceSet != null && matchedServiceSet.add(service)) { + NotifyCenter.publishEvent( + FuzzyWatchNotifyEvent.buildNotifyPatternAllListenersEvent(notifierEventScope, + service, pattern, Constants.ServiceChangedType.ADD_SERVICE)); + } + } + break; + case Constants.ServiceChangedType.DELETE_SERVICE: + for (String pattern : matchedPattern) { + Set matchedServiceSet = patternMatchMap.get(pattern); + if (matchedServiceSet != null && matchedServiceSet.remove(service)) { + NotifyCenter.publishEvent( + FuzzyWatchNotifyEvent.buildNotifyPatternAllListenersEvent(notifierEventScope, + service, pattern, Constants.ServiceChangedType.DELETE_SERVICE)); + } + } + break; + default: + break; + } + } + } + + /** + * For a duplicate fuzzy watch of a certain pattern, initiate an initialization event to the corresponding Listener. + * + * @param serviceNamePattern service name pattern. + * @param groupNamePattern group name pattern. + * @param uuid The UUID that identifies the Listener. + */ + public void duplicateFuzzyWatchInit(String serviceNamePattern, String groupNamePattern, String uuid) { + String pattern = NamingUtils.getGroupedName(serviceNamePattern, groupNamePattern); + Collection cacheServices = patternMatchMap.get(pattern); + if (cacheServices == null) { + return; + } + for (Service service : cacheServices) { + NotifyCenter.publishEvent( + FuzzyWatchNotifyEvent.buildNotifyPatternSpecificListenerEvent(notifierEventScope, service, + pattern, uuid, Constants.ServiceChangedType.ADD_SERVICE)); + } + } + + public boolean containsPatternMatchCache(String serviceNamePattern, String groupNamePattern) { + String pattern = NamingUtils.getGroupedName(serviceNamePattern, groupNamePattern); + return CollectionUtils.isEmpty(patternMatchMap.get(pattern)); + } + + public void removePatternMatchCache(String serviceNamePattern, String groupNamePattern) { + String pattern = NamingUtils.getGroupedName(serviceNamePattern, groupNamePattern); + patternMatchMap.remove(pattern); + } + + public void addPatternMatchCache(String serviceNamePattern, String groupNamePattern) { + String pattern = NamingUtils.getGroupedName(serviceNamePattern, groupNamePattern); + patternMatchMap.computeIfAbsent(pattern, keyInner -> new ConcurrentHashSet<>()); + } +} diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/event/FuzzyWatchNotifyEvent.java b/client/src/main/java/com/alibaba/nacos/client/naming/event/FuzzyWatchNotifyEvent.java new file mode 100644 index 00000000000..4f39dc30645 --- /dev/null +++ b/client/src/main/java/com/alibaba/nacos/client/naming/event/FuzzyWatchNotifyEvent.java @@ -0,0 +1,85 @@ +/* + * 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.api.naming.pojo.Service; +import com.alibaba.nacos.common.notify.Event; + +/** + * Watch notify event, including service change/watch initial. + * + * @author tanyongquan + */ +public class FuzzyWatchNotifyEvent extends Event { + + private final String eventScope; + + private final Service changedService; + + private String pattern; + + private String uuid; + + private final String serviceChangedType; + + private FuzzyWatchNotifyEvent(String eventScope, Service changedService, String pattern, String uuid, String serviceChangedType) { + this(eventScope, changedService, pattern, serviceChangedType); + this.uuid = uuid; + } + + private FuzzyWatchNotifyEvent(String eventScope, Service changedService, String pattern, String serviceChangedType) { + this.eventScope = eventScope; + this.changedService = changedService; + this.serviceChangedType = serviceChangedType; + this.pattern = pattern; + } + + public static FuzzyWatchNotifyEvent buildNotifyPatternSpecificListenerEvent(String eventScope, Service changedService, + String pattern, String uuid, String serviceChangedType) { + return new FuzzyWatchNotifyEvent(eventScope, changedService, pattern, uuid, serviceChangedType); + } + + public static FuzzyWatchNotifyEvent buildNotifyPatternAllListenersEvent(String eventScope, Service changedService, + String pattern, String serviceChangedType) { + return new FuzzyWatchNotifyEvent(eventScope, changedService, pattern, serviceChangedType); + } + + public Service getChangedService() { + return changedService; + } + + public String getPattern() { + return pattern; + } + + public void setPattern(String pattern) { + this.pattern = pattern; + } + + public String getUuid() { + return uuid; + } + + public String getServiceChangedType() { + return serviceChangedType; + } + + @Override + public String scope() { + return this.eventScope; + } +} diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/event/ServicesChangeNotifier.java b/client/src/main/java/com/alibaba/nacos/client/naming/event/ServicesChangeNotifier.java new file mode 100644 index 00000000000..26bc5741387 --- /dev/null +++ b/client/src/main/java/com/alibaba/nacos/client/naming/event/ServicesChangeNotifier.java @@ -0,0 +1,147 @@ +/* + * 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.api.naming.listener.AbstractFuzzyWatchEventListener; +import com.alibaba.nacos.api.naming.utils.NamingUtils; +import com.alibaba.nacos.common.JustForTest; +import com.alibaba.nacos.common.notify.Event; +import com.alibaba.nacos.common.notify.listener.Subscriber; +import com.alibaba.nacos.common.utils.CollectionUtils; +import com.alibaba.nacos.common.utils.ConcurrentHashSet; +import com.alibaba.nacos.common.utils.StringUtils; + +import java.util.Collection; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; + +/** + * A watcher to notify service change event Listener callback. + * + * @author tanyongquan + */ +public class ServicesChangeNotifier extends Subscriber { + + private final String eventScope; + + @JustForTest + public ServicesChangeNotifier() { + this.eventScope = UUID.randomUUID().toString(); + } + + public ServicesChangeNotifier(String eventScope) { + this.eventScope = eventScope; + } + + /** + * The content of map is {pattern -> Set[Listener]}. + */ + private final Map> fuzzyWatchListenerMap = new ConcurrentHashMap<>(); + + /** register fuzzy watch listener. + * This listener responds to changes of the services (not the instance's change). + * + * @param serviceNamePattern service name pattern + * @param groupNamePattern group name pattern + * @param listener custom listener + */ + public void registerFuzzyWatchListener(String serviceNamePattern, String groupNamePattern, AbstractFuzzyWatchEventListener listener) { + String key = NamingUtils.getGroupedName(serviceNamePattern, groupNamePattern); + Set eventListeners = fuzzyWatchListenerMap.computeIfAbsent(key, keyInner -> new ConcurrentHashSet<>()); + eventListeners.add(listener); + } + + /** remove fuzzy watch listener. + * + * @param serviceNamePattern service name pattern + * @param groupNamePattern group name pattern + */ + public void deregisterFuzzyWatchListener(String serviceNamePattern, String groupNamePattern, AbstractFuzzyWatchEventListener listener) { + String key = NamingUtils.getGroupedName(serviceNamePattern, groupNamePattern); + ConcurrentHashSet eventListeners = fuzzyWatchListenerMap.get(key); + if (eventListeners == null) { + return; + } + eventListeners.remove(listener); + if (CollectionUtils.isEmpty(eventListeners)) { + fuzzyWatchListenerMap.remove(key); + } + } + + /** + * check pattern is watched. + * + * @param serviceNamePattern service name pattern + * @param groupNamePattern group name pattern + * @return is pattern watched + */ + public boolean isWatched(String serviceNamePattern, String groupNamePattern) { + String key = NamingUtils.getGroupedName(serviceNamePattern, groupNamePattern); + ConcurrentHashSet eventListeners = fuzzyWatchListenerMap.get(key); + return CollectionUtils.isNotEmpty(eventListeners); + } + + /** + * receive fuzzy watch notify (fuzzy watch init or service change) from nacos server, notify all listener watch this pattern. + * If the event contains a UUID, then the event is used to notify the specified Listener when there are + * multiple watches for a particular pattern. + * + * @param event watch notify event + */ + @Override + public void onEvent(FuzzyWatchNotifyEvent event) { + String uuid = event.getUuid(); + Collection listeners = fuzzyWatchListenerMap.get(event.getPattern()); + final com.alibaba.nacos.api.naming.listener.FuzzyWatchNotifyEvent fuzzyWatchNotifyEvent = transferToWatchNotifyEvent(event); + for (AbstractFuzzyWatchEventListener each : listeners) { + // notify all listener watch this pattern + if (StringUtils.isEmpty(uuid)) { + if (each.getExecutor() != null) { + each.getExecutor().execute(() -> each.onEvent(fuzzyWatchNotifyEvent)); + } else { + each.onEvent(fuzzyWatchNotifyEvent); + } + } else if (uuid.equals(each.getUuid())) { + // notify specific listener by uuid, use in duplicate watch a same pattern + if (each.getExecutor() != null) { + each.getExecutor().execute(() -> each.onEvent(fuzzyWatchNotifyEvent)); + } else { + each.onEvent(fuzzyWatchNotifyEvent); + } + return; + } + } + } + + private com.alibaba.nacos.api.naming.listener.FuzzyWatchNotifyEvent transferToWatchNotifyEvent( + FuzzyWatchNotifyEvent fuzzyWatchNotifyEvent) { + return new com.alibaba.nacos.api.naming.listener.FuzzyWatchNotifyEvent(fuzzyWatchNotifyEvent.getChangedService(), + fuzzyWatchNotifyEvent.getServiceChangedType()); + } + + @Override + public Class subscribeType() { + return FuzzyWatchNotifyEvent.class; + } + + @Override + public boolean scopeMatches(FuzzyWatchNotifyEvent event) { + return this.eventScope.equals(event.scope()); + } +} diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/remote/NamingClientProxy.java b/client/src/main/java/com/alibaba/nacos/client/naming/remote/NamingClientProxy.java index 6fc40c35c91..924f45f937e 100644 --- a/client/src/main/java/com/alibaba/nacos/client/naming/remote/NamingClientProxy.java +++ b/client/src/main/java/com/alibaba/nacos/client/naming/remote/NamingClientProxy.java @@ -181,6 +181,33 @@ ListView getServiceList(int pageNo, int pageSize, String groupName, Abst */ boolean isSubscribed(String serviceName, String groupName, String clusters) throws NacosException; + /** + * Fuzzy watch services change by pattern. + * + * @param serviceNamePattern service name pattern + * @param groupNamePattern group name pattern + * @param uuid UUID used to identify the Listener. Used for local initialization when repeating fuzzy watch + * @throws NacosException nacos exception + */ + void fuzzyWatch(String serviceNamePattern, String groupNamePattern, String uuid) throws NacosException; + + /** Judge whether pattern has been watched. + * + * @param serviceNamePattern service name pattern + * @param groupNamePattern group name pattern + * @return {@code true} if subscribed, otherwise {@code false} + * @throws NacosException nacos exception + */ + boolean isFuzzyWatched(String serviceNamePattern, String groupNamePattern) throws NacosException; + + /** Cancel fuzzy watch pattern. + * + * @param serviceNamePattern service name pattern + * @param groupNamePattern group name pattern + * @throws NacosException nacos exception + */ + void cancelFuzzyWatch(String serviceNamePattern, String groupNamePattern) throws NacosException; + /** * Check Server healthy. * diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/remote/NamingClientProxyDelegate.java b/client/src/main/java/com/alibaba/nacos/client/naming/remote/NamingClientProxyDelegate.java index efd7bdabf46..408babbca72 100644 --- a/client/src/main/java/com/alibaba/nacos/client/naming/remote/NamingClientProxyDelegate.java +++ b/client/src/main/java/com/alibaba/nacos/client/naming/remote/NamingClientProxyDelegate.java @@ -26,6 +26,8 @@ import com.alibaba.nacos.api.selector.AbstractSelector; import com.alibaba.nacos.client.env.NacosClientProperties; import com.alibaba.nacos.client.naming.cache.ServiceInfoHolder; +import com.alibaba.nacos.client.naming.cache.FuzzyWatchServiceListHolder; +import com.alibaba.nacos.client.naming.core.ServerListManager; import com.alibaba.nacos.client.naming.core.NamingServerListManager; import com.alibaba.nacos.client.naming.core.ServiceInfoUpdateService; import com.alibaba.nacos.client.naming.event.InstancesChangeNotifier; @@ -58,6 +60,8 @@ public class NamingClientProxyDelegate implements NamingClientProxy { private final ServiceInfoUpdateService serviceInfoUpdateService; private final ServiceInfoHolder serviceInfoHolder; + + private final FuzzyWatchServiceListHolder fuzzyWatchServiceListHolder; private final NamingHttpClientProxy httpClientProxy; @@ -67,19 +71,20 @@ public class NamingClientProxyDelegate implements NamingClientProxy { private ScheduledExecutorService executorService; - public NamingClientProxyDelegate(String namespace, ServiceInfoHolder serviceInfoHolder, + public NamingClientProxyDelegate(String namespace, ServiceInfoHolder serviceInfoHolder, FuzzyWatchServiceListHolder fuzzyWatchServiceListHolder, NacosClientProperties properties, InstancesChangeNotifier changeNotifier) throws NacosException { this.serviceInfoUpdateService = new ServiceInfoUpdateService(properties, serviceInfoHolder, this, changeNotifier); this.serverListManager = new NamingServerListManager(properties, namespace); this.serverListManager.start(); this.serviceInfoHolder = serviceInfoHolder; + this.fuzzyWatchServiceListHolder = fuzzyWatchServiceListHolder; this.securityProxy = new SecurityProxy(this.serverListManager, NamingHttpClientManager.getInstance().getNacosRestTemplate()); initSecurityProxy(properties); this.httpClientProxy = new NamingHttpClientProxy(namespace, securityProxy, serverListManager, properties); this.grpcClientProxy = new NamingGrpcClientProxy(namespace, securityProxy, serverListManager, properties, - serviceInfoHolder); + serviceInfoHolder, fuzzyWatchServiceListHolder); } private void initSecurityProxy(NacosClientProperties properties) { @@ -187,6 +192,31 @@ public boolean isSubscribed(String serviceName, String groupName, String cluster return grpcClientProxy.isSubscribed(serviceName, groupName, clusters); } + @Override + public void fuzzyWatch(String serviceNamePattern, String groupNamePattern, String uuid) throws NacosException { + NAMING_LOGGER.info("[FUZZY-WATCH] serviceNamePattern:{}, groupNamePattern:{}", serviceNamePattern, groupNamePattern); + if (!fuzzyWatchServiceListHolder.containsPatternMatchCache(serviceNamePattern, groupNamePattern) + || !isFuzzyWatched(serviceNamePattern, groupNamePattern)) { + fuzzyWatchServiceListHolder.addPatternMatchCache(serviceNamePattern, groupNamePattern); + grpcClientProxy.fuzzyWatch(serviceNamePattern, groupNamePattern, ""); + } else { + fuzzyWatchServiceListHolder.duplicateFuzzyWatchInit(serviceNamePattern, groupNamePattern, uuid); + } + } + + @Override + public boolean isFuzzyWatched(String serviceNamePattern, String groupNamePattern) { + return grpcClientProxy.isFuzzyWatched(serviceNamePattern, groupNamePattern); + } + + @Override + public void cancelFuzzyWatch(String serviceNamePattern, String groupNamePattern) throws NacosException { + NAMING_LOGGER + .debug("[CANCEL-FUZZY-WATCH] serviceNamePattern:{}, groupNamePattern:{} ", serviceNamePattern, groupNamePattern); + fuzzyWatchServiceListHolder.removePatternMatchCache(serviceNamePattern, groupNamePattern); + grpcClientProxy.cancelFuzzyWatch(serviceNamePattern, groupNamePattern); + } + @Override public boolean serverHealthy() { return grpcClientProxy.serverHealthy() || httpClientProxy.serverHealthy(); diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/NamingGrpcClientProxy.java b/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/NamingGrpcClientProxy.java index 508b76f0ab3..3a301406898 100644 --- a/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/NamingGrpcClientProxy.java +++ b/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/NamingGrpcClientProxy.java @@ -33,10 +33,12 @@ import com.alibaba.nacos.api.naming.remote.request.ServiceListRequest; import com.alibaba.nacos.api.naming.remote.request.ServiceQueryRequest; import com.alibaba.nacos.api.naming.remote.request.SubscribeServiceRequest; +import com.alibaba.nacos.api.naming.remote.request.FuzzyWatchRequest; import com.alibaba.nacos.api.naming.remote.response.BatchInstanceResponse; import com.alibaba.nacos.api.naming.remote.response.QueryServiceResponse; import com.alibaba.nacos.api.naming.remote.response.ServiceListResponse; import com.alibaba.nacos.api.naming.remote.response.SubscribeServiceResponse; +import com.alibaba.nacos.api.naming.remote.response.FuzzyWatchResponse; import com.alibaba.nacos.api.naming.utils.NamingUtils; import com.alibaba.nacos.api.remote.RemoteConstants; import com.alibaba.nacos.api.remote.response.Response; @@ -46,6 +48,8 @@ import com.alibaba.nacos.client.env.NacosClientProperties; import com.alibaba.nacos.client.monitor.MetricsMonitor; import com.alibaba.nacos.client.naming.cache.ServiceInfoHolder; +import com.alibaba.nacos.client.naming.cache.FuzzyWatchServiceListHolder; +import com.alibaba.nacos.client.naming.event.ServerListChangedEvent; import com.alibaba.nacos.client.address.ServerListChangeEvent; import com.alibaba.nacos.client.naming.remote.AbstractNamingClientProxy; import com.alibaba.nacos.client.naming.remote.gprc.redo.NamingGrpcRedoService; @@ -94,7 +98,8 @@ public class NamingGrpcClientProxy extends AbstractNamingClientProxy { private final NamingGrpcRedoService redoService; public NamingGrpcClientProxy(String namespaceId, SecurityProxy securityProxy, ServerListFactory serverListFactory, - NacosClientProperties properties, ServiceInfoHolder serviceInfoHolder) throws NacosException { + NacosClientProperties properties, ServiceInfoHolder serviceInfoHolder, + FuzzyWatchServiceListHolder fuzzyWatchServiceListHolder) throws NacosException { super(securityProxy); this.namespaceId = namespaceId; this.uuid = UUID.randomUUID().toString(); @@ -107,13 +112,15 @@ public NamingGrpcClientProxy(String namespaceId, SecurityProxy securityProxy, Se RpcClientTlsConfigFactory.getInstance().createSdkConfig(properties.asProperties())); this.redoService = new NamingGrpcRedoService(this, properties); NAMING_LOGGER.info("Create naming rpc client for uuid->{}", uuid); - start(serverListFactory, serviceInfoHolder); + start(serverListFactory, serviceInfoHolder, fuzzyWatchServiceListHolder); } - private void start(ServerListFactory serverListFactory, ServiceInfoHolder serviceInfoHolder) throws NacosException { + private void start(ServerListFactory serverListFactory, ServiceInfoHolder serviceInfoHolder, + FuzzyWatchServiceListHolder fuzzyWatchServiceListHolder) throws NacosException { rpcClient.serverListFactory(serverListFactory); rpcClient.registerConnectionListener(redoService); - rpcClient.registerServerRequestHandler(new NamingPushRequestHandler(serviceInfoHolder)); + rpcClient.registerServerRequestHandler(new NamingPushRequestHandler(serviceInfoHolder, + fuzzyWatchServiceListHolder)); rpcClient.start(); NotifyCenter.registerSubscriber(this); } @@ -423,6 +430,58 @@ public void doUnsubscribe(String serviceName, String groupName, String clusters) redoService.removeSubscriberForRedo(serviceName, groupName, clusters); } + @Override + public void fuzzyWatch(String serviceNamePattern, String groupNamePattern, String watcherUuid) throws NacosException { + if (NAMING_LOGGER.isDebugEnabled()) { + NAMING_LOGGER.debug("[GRPC-FUZZY-WATCH] servicePattern:{}, groupPattern:{}", serviceNamePattern, groupNamePattern); + } + redoService.cacheFuzzyWatcherForRedo(serviceNamePattern, groupNamePattern); + doFuzzyWatch(serviceNamePattern, groupNamePattern); + } + + /** + * Execute fuzzy watch operation. + * + * @param serviceNamePattern service name pattern + * @param groupNamePattern group name pattern + * @throws NacosException nacos exception + */ + public void doFuzzyWatch(String serviceNamePattern, String groupNamePattern) throws NacosException { + FuzzyWatchRequest request = new FuzzyWatchRequest(namespaceId, serviceNamePattern, groupNamePattern, + NamingRemoteConstants.FUZZY_WATCH_SERVICE); + requestToServer(request, FuzzyWatchResponse.class); + redoService.fuzzyWatcherRegistered(serviceNamePattern, groupNamePattern); + } + + @Override + public boolean isFuzzyWatched(String serviceNamePattern, String groupNamePattern) { + return redoService.isFuzzyWatcherRegistered(serviceNamePattern, groupNamePattern); + } + + @Override + public void cancelFuzzyWatch(String serviceNamePattern, String groupNamePattern) throws NacosException { + if (NAMING_LOGGER.isDebugEnabled()) { + NAMING_LOGGER + .debug("[GRPC-CANCEL-FUZZY-WATCH] serviceNamePattern:{}, groupNamePattern:{}", serviceNamePattern, groupNamePattern); + } + redoService.fuzzyWatcherDeregister(serviceNamePattern, groupNamePattern); + doCancelFuzzyWatch(serviceNamePattern, groupNamePattern); + } + + /** + * Execute cancel fuzzy watch operation. + * + * @param serviceNamePattern service name pattern + * @param groupNamePattern group name pattern + * @throws NacosException nacos exception + */ + public void doCancelFuzzyWatch(String serviceNamePattern, String groupNamePattern) throws NacosException { + FuzzyWatchRequest request = new FuzzyWatchRequest(namespaceId, serviceNamePattern, groupNamePattern, + NamingRemoteConstants.CANCEL_FUZZY_WATCH_SERVICE); + requestToServer(request, FuzzyWatchResponse.class); + redoService.removeFuzzyWatcherForRedo(serviceNamePattern, groupNamePattern); + } + @Override public boolean serverHealthy() { return rpcClient.isRunning(); @@ -508,4 +567,8 @@ private void shutDownAndRemove(String uuid) { public boolean isEnable() { return rpcClient.isRunning(); } + + public String getNamespaceId() { + return namespaceId; + } } diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/NamingPushRequestHandler.java b/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/NamingPushRequestHandler.java index 83c828c802d..4448158890b 100644 --- a/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/NamingPushRequestHandler.java +++ b/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/NamingPushRequestHandler.java @@ -16,11 +16,14 @@ package com.alibaba.nacos.client.naming.remote.gprc; +import com.alibaba.nacos.api.naming.remote.request.AbstractFuzzyWatchNotifyRequest; import com.alibaba.nacos.api.naming.remote.request.NotifySubscriberRequest; import com.alibaba.nacos.api.naming.remote.response.NotifySubscriberResponse; +import com.alibaba.nacos.api.naming.remote.response.NotifyFuzzyWatcherResponse; import com.alibaba.nacos.api.remote.request.Request; import com.alibaba.nacos.api.remote.response.Response; import com.alibaba.nacos.client.naming.cache.ServiceInfoHolder; +import com.alibaba.nacos.client.naming.cache.FuzzyWatchServiceListHolder; import com.alibaba.nacos.common.remote.client.Connection; import com.alibaba.nacos.common.remote.client.ServerRequestHandler; @@ -33,8 +36,11 @@ public class NamingPushRequestHandler implements ServerRequestHandler { private final ServiceInfoHolder serviceInfoHolder; - public NamingPushRequestHandler(ServiceInfoHolder serviceInfoHolder) { + private final FuzzyWatchServiceListHolder fuzzyWatchServiceListHolder; + + public NamingPushRequestHandler(ServiceInfoHolder serviceInfoHolder, FuzzyWatchServiceListHolder fuzzyWatchServiceListHolder) { this.serviceInfoHolder = serviceInfoHolder; + this.fuzzyWatchServiceListHolder = fuzzyWatchServiceListHolder; } @Override @@ -43,6 +49,10 @@ public Response requestReply(Request request, Connection connection) { NotifySubscriberRequest notifyRequest = (NotifySubscriberRequest) request; serviceInfoHolder.processServiceInfo(notifyRequest.getServiceInfo()); return new NotifySubscriberResponse(); + } else if (request instanceof AbstractFuzzyWatchNotifyRequest) { + AbstractFuzzyWatchNotifyRequest notifyRequest = (AbstractFuzzyWatchNotifyRequest) request; + fuzzyWatchServiceListHolder.processFuzzyWatchNotify(notifyRequest); + return new NotifyFuzzyWatcherResponse(); } return null; } diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/redo/NamingGrpcRedoService.java b/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/redo/NamingGrpcRedoService.java index aad883ddabe..9627684aed5 100644 --- a/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/redo/NamingGrpcRedoService.java +++ b/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/redo/NamingGrpcRedoService.java @@ -26,6 +26,7 @@ import com.alibaba.nacos.client.naming.remote.gprc.redo.data.BatchInstanceRedoData; import com.alibaba.nacos.client.naming.remote.gprc.redo.data.InstanceRedoData; import com.alibaba.nacos.client.naming.remote.gprc.redo.data.SubscriberRedoData; +import com.alibaba.nacos.client.naming.remote.gprc.redo.data.FuzzyWatcherRedoData; import com.alibaba.nacos.client.utils.LogUtils; import com.alibaba.nacos.common.executor.NameThreadFactory; import com.alibaba.nacos.common.remote.client.Connection; @@ -59,6 +60,8 @@ public class NamingGrpcRedoService implements ConnectionEventListener { private final ConcurrentMap subscribes = new ConcurrentHashMap<>(); + private final ConcurrentMap fuzzyWatcher = new ConcurrentHashMap<>(); + private final ScheduledExecutorService redoExecutor; private volatile boolean connected = false; @@ -100,6 +103,9 @@ public void onDisConnect(Connection connection) { synchronized (subscribes) { subscribes.values().forEach(subscriberRedoData -> subscriberRedoData.setRegistered(false)); } + synchronized (fuzzyWatcher) { + fuzzyWatcher.values().forEach(fuzzyWatcherRedoData -> fuzzyWatcherRedoData.setRegistered(false)); + } LogUtils.NAMING_LOGGER.warn("mark to redo completed"); } @@ -315,6 +321,101 @@ public Set findSubscriberRedoData() { return result; } + /** + * Cache fuzzy watcher for redo. + * + * @param serviceNamePattern service name pattern + * @param groupNamePattern group name pattern + */ + public void cacheFuzzyWatcherForRedo(String serviceNamePattern, String groupNamePattern) { + String key = NamingUtils.getGroupedName(serviceNamePattern, groupNamePattern); + FuzzyWatcherRedoData redoData = FuzzyWatcherRedoData.build(serviceNamePattern, groupNamePattern); + synchronized (fuzzyWatcher) { + fuzzyWatcher.put(key, redoData); + } + } + + /** + * Fuzzy watcher register successfully, mark registered status as {@code true}. + * + * @param serviceNamePattern service name pattern + * @param groupNamePattern group name pattern + */ + public void fuzzyWatcherRegistered(String serviceNamePattern, String groupNamePattern) { + String key = NamingUtils.getGroupedName(serviceNamePattern, groupNamePattern); + synchronized (fuzzyWatcher) { + FuzzyWatcherRedoData redoData = fuzzyWatcher.get(key); + if (null != redoData) { + redoData.setRegistered(true); + } + } + } + + /** + * Fuzzy watcher deregister, mark unregistering status as {@code true}. + * + * @param serviceNamePattern service name pattern + * @param groupNamePattern group name pattern + */ + public void fuzzyWatcherDeregister(String serviceNamePattern, String groupNamePattern) { + String key = NamingUtils.getGroupedName(serviceNamePattern, groupNamePattern); + synchronized (fuzzyWatcher) { + FuzzyWatcherRedoData redoData = fuzzyWatcher.get(key); + if (null != redoData) { + redoData.setUnregistering(true); + redoData.setExpectedRegistered(false); + } + } + } + + /** + * Remove fuzzy watcher for redo. + * + * @param serviceNamePattern service name pattern + * @param groupNamePattern group name pattern + */ + public void removeFuzzyWatcherForRedo(String serviceNamePattern, String groupNamePattern) { + String key = NamingUtils.getGroupedName(serviceNamePattern, groupNamePattern); + synchronized (fuzzyWatcher) { + FuzzyWatcherRedoData redoData = fuzzyWatcher.get(key); + if (null != redoData && !redoData.isExpectedRegistered()) { + fuzzyWatcher.remove(key); + } + } + } + + /** + * Judge fuzzy watcher has registered to server. + * + * @param serviceNamePattern service name pattern + * @param groupNamePattern group name pattern + * @return {@code true} if watched, otherwise {@code false} + */ + public boolean isFuzzyWatcherRegistered(String serviceNamePattern, String groupNamePattern) { + String key = NamingUtils.getGroupedName(serviceNamePattern, groupNamePattern); + synchronized (fuzzyWatcher) { + FuzzyWatcherRedoData redoData = fuzzyWatcher.get(key); + return null != redoData && redoData.isRegistered(); + } + } + + /** + * Find all fuzzy watcher redo data which need to redo. + * + * @return set of {@code WatcherRedoData} need to redo. + */ + public Set findFuzzyWatcherRedoData() { + Set result = new HashSet<>(); + synchronized (fuzzyWatcher) { + for (FuzzyWatcherRedoData each : fuzzyWatcher.values()) { + if (each.isNeedRedo()) { + result.add(each); + } + } + } + return result; + } + /** * get Cache service. * diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/redo/RedoScheduledTask.java b/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/redo/RedoScheduledTask.java index 99d890b3e98..64908b1a0eb 100644 --- a/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/redo/RedoScheduledTask.java +++ b/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/redo/RedoScheduledTask.java @@ -22,6 +22,7 @@ import com.alibaba.nacos.client.naming.remote.gprc.redo.data.InstanceRedoData; import com.alibaba.nacos.client.naming.remote.gprc.redo.data.RedoData; import com.alibaba.nacos.client.naming.remote.gprc.redo.data.SubscriberRedoData; +import com.alibaba.nacos.client.naming.remote.gprc.redo.data.FuzzyWatcherRedoData; import com.alibaba.nacos.client.utils.LogUtils; import com.alibaba.nacos.common.task.AbstractExecuteTask; @@ -50,6 +51,7 @@ public void run() { try { redoForInstances(); redoForSubscribes(); + redoForFuzzyWatchers(); } catch (Exception e) { LogUtils.NAMING_LOGGER.warn("Redo task run with unexpected exception: ", e); } @@ -139,6 +141,43 @@ private void redoForSubscribe(SubscriberRedoData redoData) throws NacosException } } + private void redoForFuzzyWatchers() { + for (FuzzyWatcherRedoData each : redoService.findFuzzyWatcherRedoData()) { + try { + redoForFuzzyWatcher(each); + } catch (NacosException e) { + LogUtils.NAMING_LOGGER.error("Redo fuzzy watcher operation {} for pattern {}@@{} in namespace {} failed. ", + each.getRedoType(), each.getGroupName(), each.getServiceName(), clientProxy.getNamespaceId(), e); + } + } + } + + private void redoForFuzzyWatcher(FuzzyWatcherRedoData redoData) throws NacosException { + RedoData.RedoType redoType = redoData.getRedoType(); + String serviceNamePattern = redoData.getServiceName(); + String groupNamePattern = redoData.getGroupName(); + LogUtils.NAMING_LOGGER.info("Redo fuzzy watcher operation {} for pattern {}@@{} in namespace {}", redoType, + groupNamePattern, serviceNamePattern, clientProxy.getNamespaceId()); + switch (redoType) { + case REGISTER: + if (isClientDisabled()) { + return; + } + clientProxy.doFuzzyWatch(serviceNamePattern, groupNamePattern); + break; + case UNREGISTER: + if (isClientDisabled()) { + return; + } + clientProxy.doCancelFuzzyWatch(serviceNamePattern, groupNamePattern); + break; + case REMOVE: + redoService.removeFuzzyWatcherForRedo(serviceNamePattern, groupNamePattern); + break; + default: + } + } + private boolean isClientDisabled() { return !clientProxy.isEnable(); } diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/redo/data/FuzzyWatcherRedoData.java b/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/redo/data/FuzzyWatcherRedoData.java new file mode 100644 index 00000000000..f0e820910dd --- /dev/null +++ b/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/redo/data/FuzzyWatcherRedoData.java @@ -0,0 +1,33 @@ +/* + * 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.remote.gprc.redo.data; + +/** + * Redo data for fuzzy watcher. + * + * @author tanyongquan + */ +public class FuzzyWatcherRedoData extends RedoData { + + private FuzzyWatcherRedoData(String serviceNamePattern, String groupNamePattern) { + super(serviceNamePattern, groupNamePattern); + } + + public static FuzzyWatcherRedoData build(String serviceNamePattern, String groupNamePattern) { + return new FuzzyWatcherRedoData(serviceNamePattern, groupNamePattern); + } +} diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/remote/http/NamingHttpClientProxy.java b/client/src/main/java/com/alibaba/nacos/client/naming/remote/http/NamingHttpClientProxy.java index a6568fc4010..d0411ebb832 100644 --- a/client/src/main/java/com/alibaba/nacos/client/naming/remote/http/NamingHttpClientProxy.java +++ b/client/src/main/java/com/alibaba/nacos/client/naming/remote/http/NamingHttpClientProxy.java @@ -328,6 +328,21 @@ public boolean isSubscribed(String serviceName, String groupName, String cluster return true; } + @Override + public void fuzzyWatch(String serviceNamePattern, String groupNamePattern, String uuid) throws NacosException { + throw new UnsupportedOperationException("Do not support fuzzy watch services by UDP, please use gRPC replaced."); + } + + @Override + public void cancelFuzzyWatch(String serviceNamePattern, String groupNamePattern) throws NacosException { + throw new UnsupportedOperationException("Do not support fuzzy watch service by UDP, please use gRPC replaced."); + } + + @Override + public boolean isFuzzyWatched(String serviceNamePattern, String groupNamePattern) { + throw new UnsupportedOperationException("Do not support fuzzy watch service by UDP, please use gRPC replaced."); + } + public String reqApi(String api, Map params, String method) throws NacosException { return reqApi(api, params, Collections.EMPTY_MAP, method); } diff --git a/client/src/main/resources/META-INF/native-image/com.alibaba.nacos/nacos-client/reflect-config.json b/client/src/main/resources/META-INF/native-image/com.alibaba.nacos/nacos-client/reflect-config.json index 3734fbb2843..da2ba912161 100644 --- a/client/src/main/resources/META-INF/native-image/com.alibaba.nacos/nacos-client/reflect-config.json +++ b/client/src/main/resources/META-INF/native-image/com.alibaba.nacos/nacos-client/reflect-config.json @@ -535,14 +535,23 @@ {"name":"getServiceName","parameterTypes":[] } ] }, +{ + "name":"com.alibaba.nacos.api.naming.remote.request.AbstractFuzzyWatchNotifyRequest", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[ {"name":"getInstances","parameterTypes":[] }, + {"name":"getType","parameterTypes":[] }] +}, { "name":"com.alibaba.nacos.api.naming.remote.request.BatchInstanceRequest", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[ - {"name":"getInstances","parameterTypes":[] }, - {"name":"getType","parameterTypes":[] } + {"name":"","parameterTypes":[] }, + {"name":"getModule","parameterTypes":[] }, + {"name":"getServiceChangedType","parameterTypes":[] } ] }, { @@ -603,6 +612,28 @@ {"name":"isHealthyOnly","parameterTypes":[] } ] }, +{ + "name":"com.alibaba.nacos.api.naming.remote.request.FuzzyWatchNotifyChangeRequest", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[ + {"name":"","parameterTypes":[] }, + {"name":"getServiceName","parameterTypes":[] }, + {"name":"getGroupName","parameterTypes":[] } + ] +}, +{ + "name":"com.alibaba.nacos.api.naming.remote.request.FuzzyWatchNotifyInitRequest", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[ + {"name":"","parameterTypes":[] }, + {"name":"getServicesName","parameterTypes":[] }, + {"name":"getPattern","parameterTypes":[] } + ] +}, { "name":"com.alibaba.nacos.api.naming.remote.request.SubscribeServiceRequest", "allDeclaredFields":true, diff --git a/client/src/test/java/com/alibaba/nacos/client/naming/NacosNamingServiceTest.java b/client/src/test/java/com/alibaba/nacos/client/naming/NacosNamingServiceTest.java index b1c98ee7195..2232aa9f04f 100644 --- a/client/src/test/java/com/alibaba/nacos/client/naming/NacosNamingServiceTest.java +++ b/client/src/test/java/com/alibaba/nacos/client/naming/NacosNamingServiceTest.java @@ -1089,7 +1089,6 @@ public void testUnSubscribeWithServiceAndCustomSelector() throws NacosException verify(proxy, times(1)).unsubscribe(serviceName, Constants.DEFAULT_GROUP, Constants.NULL); } - @Test public void testUnSubscribeWithFullNameAndCustomSelector() throws NacosException { //given String serviceName = "service1"; diff --git a/client/src/test/java/com/alibaba/nacos/client/naming/remote/AbstractNamingClientProxyTest.java b/client/src/test/java/com/alibaba/nacos/client/naming/remote/AbstractNamingClientProxyTest.java index 7f570c2780b..43e01a350bf 100644 --- a/client/src/test/java/com/alibaba/nacos/client/naming/remote/AbstractNamingClientProxyTest.java +++ b/client/src/test/java/com/alibaba/nacos/client/naming/remote/AbstractNamingClientProxyTest.java @@ -174,6 +174,21 @@ public boolean isSubscribed(String serviceName, String groupName, String cluster return false; } + @Override + public void fuzzyWatch(String serviceNamePattern, String groupNamePattern, String uuid) throws NacosException { + + } + + @Override + public void cancelFuzzyWatch(String serviceNamePattern, String groupNamePattern) throws NacosException { + + } + + @Override + public boolean isFuzzyWatched(String serviceNamePattern, String groupNamePattern) { + return false; + } + @Override public boolean serverHealthy() { return false; diff --git a/client/src/test/java/com/alibaba/nacos/client/naming/remote/NamingClientProxyDelegateTest.java b/client/src/test/java/com/alibaba/nacos/client/naming/remote/NamingClientProxyDelegateTest.java index bdc3ac6cec1..7f201b14eb9 100644 --- a/client/src/test/java/com/alibaba/nacos/client/naming/remote/NamingClientProxyDelegateTest.java +++ b/client/src/test/java/com/alibaba/nacos/client/naming/remote/NamingClientProxyDelegateTest.java @@ -28,6 +28,7 @@ import com.alibaba.nacos.api.selector.NoneSelector; import com.alibaba.nacos.client.env.NacosClientProperties; import com.alibaba.nacos.client.naming.cache.ServiceInfoHolder; +import com.alibaba.nacos.client.naming.cache.FuzzyWatchServiceListHolder; import com.alibaba.nacos.client.naming.event.InstancesChangeNotifier; import com.alibaba.nacos.client.naming.remote.gprc.NamingGrpcClientProxy; import com.alibaba.nacos.client.naming.remote.http.NamingHttpClientProxy; @@ -65,6 +66,9 @@ class NamingClientProxyDelegateTest { @Mock NamingGrpcClientProxy mockGrpcClient; + @Mock + FuzzyWatchServiceListHolder fuzzyWatchServiceListHolder; + NamingClientProxyDelegate delegate; InstancesChangeNotifier notifier; @@ -77,7 +81,7 @@ void setUp() throws NacosException, NoSuchFieldException, IllegalAccessException props.setProperty("serverAddr", "localhost"); nacosClientProperties = NacosClientProperties.PROTOTYPE.derive(props); notifier = new InstancesChangeNotifier(); - delegate = new NamingClientProxyDelegate(TEST_NAMESPACE, holder, nacosClientProperties, notifier); + delegate = new NamingClientProxyDelegate(TEST_NAMESPACE, holder, nacosClientProperties,fuzzyWatchServiceListHolder, notifier); Field grpcClientProxyField = NamingClientProxyDelegate.class.getDeclaredField("grpcClientProxy"); grpcClientProxyField.setAccessible(true); grpcClientProxyField.set(delegate, mockGrpcClient); @@ -102,7 +106,7 @@ void testRegisterEphemeralServiceByGrpc() throws NacosException { verify(mockGrpcClient, times(1)).registerService(serviceName, groupName, instance); } - @Test + void testBatchRegisterServiceByGrpc() throws NacosException { String serviceName = "service1"; String groupName = "group1"; @@ -169,7 +173,7 @@ void testRegisterPersistentServiceByHttp() throws NacosException, NoSuchFieldExc verify(mockHttpClient, times(1)).registerService(serviceName, groupName, instance); } - @Test + void testDeregisterEphemeralServiceGrpc() throws NacosException { String serviceName = "service1"; String groupName = "group1"; @@ -221,8 +225,7 @@ void testDeregisterPersistentServiceHttp() throws NacosException, NoSuchFieldExc delegate.deregisterService(serviceName, groupName, instance); verify(mockHttpClient, times(1)).deregisterService(serviceName, groupName, instance); } - - @Test + void testUpdateInstance() { String serviceName = "service1"; String groupName = "group1"; @@ -231,8 +234,7 @@ void testUpdateInstance() { delegate.updateInstance(serviceName, groupName, instance); }); } - - @Test + void testQueryInstancesOfService() throws NacosException { String serviceName = "service1"; String groupName = "group1"; @@ -241,13 +243,13 @@ void testQueryInstancesOfService() throws NacosException { verify(mockGrpcClient, times(1)).queryInstancesOfService(serviceName, groupName, clusters, false); } - @Test + void testQueryService() throws NacosException { Service service = delegate.queryService("a", "b"); assertNull(service); } - @Test + void testCreateService() { Service service = new Service(); Assertions.assertDoesNotThrow(() -> { @@ -255,7 +257,7 @@ void testCreateService() { }); } - @Test + void testDeleteService() throws NacosException { assertFalse(delegate.deleteService("service", "group1")); } @@ -268,7 +270,7 @@ void testUpdateService() { }); } - @Test + void testGetServiceList() throws NacosException { AbstractSelector selector = new ExpressionSelector(); int pageNo = 1; diff --git a/client/src/test/java/com/alibaba/nacos/client/naming/remote/gprc/NamingGrpcClientProxyTest.java b/client/src/test/java/com/alibaba/nacos/client/naming/remote/gprc/NamingGrpcClientProxyTest.java index bdb9472cfbc..910dbdab4c0 100644 --- a/client/src/test/java/com/alibaba/nacos/client/naming/remote/gprc/NamingGrpcClientProxyTest.java +++ b/client/src/test/java/com/alibaba/nacos/client/naming/remote/gprc/NamingGrpcClientProxyTest.java @@ -47,6 +47,8 @@ import com.alibaba.nacos.api.selector.NoneSelector; import com.alibaba.nacos.client.env.NacosClientProperties; import com.alibaba.nacos.client.naming.cache.ServiceInfoHolder; +import com.alibaba.nacos.client.naming.cache.FuzzyWatchServiceListHolder; +import com.alibaba.nacos.client.naming.event.ServerListChangedEvent; import com.alibaba.nacos.client.address.ServerListChangeEvent; import com.alibaba.nacos.client.naming.remote.gprc.redo.NamingGrpcRedoService; import com.alibaba.nacos.client.security.SecurityProxy; @@ -120,6 +122,9 @@ class NamingGrpcClientProxyTest { @Mock private ServiceInfoHolder holder; + @Mock + private FuzzyWatchServiceListHolder fuzzyWatchServiceListHolder; + @Mock private RpcClient rpcClient; @@ -145,7 +150,8 @@ void setUp() throws NacosException, NoSuchFieldException, IllegalAccessException prop = new Properties(); final NacosClientProperties nacosClientProperties = NacosClientProperties.PROTOTYPE.derive(prop); - client = new NamingGrpcClientProxy(NAMESPACE_ID, proxy, factory, nacosClientProperties, holder); + client = new NamingGrpcClientProxy(NAMESPACE_ID, proxy, factory, nacosClientProperties, holder, + fuzzyWatchServiceListHolder); Field uuidField = NamingGrpcClientProxy.class.getDeclaredField("uuid"); uuidField.setAccessible(true); @@ -664,7 +670,7 @@ public void close() { rpcClient.set(client, rpc); rpc.serverListFactory(factory); - rpc.registerServerRequestHandler(new NamingPushRequestHandler(holder)); + rpc.registerServerRequestHandler(new NamingPushRequestHandler(holder, fuzzyWatchServiceListHolder)); Field listenerField = NamingGrpcClientProxy.class.getDeclaredField("redoService"); listenerField.setAccessible(true); NamingGrpcRedoService listener = (NamingGrpcRedoService) listenerField.get(client); @@ -699,7 +705,8 @@ public void close() { @Test void testConfigAppNameLabels() throws Exception { final NacosClientProperties nacosClientProperties = NacosClientProperties.PROTOTYPE.derive(prop); - client = new NamingGrpcClientProxy(NAMESPACE_ID, proxy, factory, nacosClientProperties, holder); + client = new NamingGrpcClientProxy(NAMESPACE_ID, proxy, factory, nacosClientProperties, holder, + fuzzyWatchServiceListHolder); Field rpcClientField = NamingGrpcClientProxy.class.getDeclaredField("rpcClient"); rpcClientField.setAccessible(true); RpcClient rpcClient = (RpcClient) rpcClientField.get(client); diff --git a/client/src/test/java/com/alibaba/nacos/client/naming/remote/gprc/NamingPushRequestHandlerTest.java b/client/src/test/java/com/alibaba/nacos/client/naming/remote/gprc/NamingPushRequestHandlerTest.java index 341c26e1dcd..786a4d7b5e9 100644 --- a/client/src/test/java/com/alibaba/nacos/client/naming/remote/gprc/NamingPushRequestHandlerTest.java +++ b/client/src/test/java/com/alibaba/nacos/client/naming/remote/gprc/NamingPushRequestHandlerTest.java @@ -25,6 +25,9 @@ import com.alibaba.nacos.api.remote.request.Request; import com.alibaba.nacos.api.remote.response.Response; import com.alibaba.nacos.client.naming.cache.ServiceInfoHolder; +import com.alibaba.nacos.client.naming.cache.FuzzyWatchServiceListHolder; +import org.junit.Assert; +import org.junit.Test; import com.alibaba.nacos.client.naming.remote.TestConnection; import com.alibaba.nacos.common.remote.client.RpcClient; import org.junit.jupiter.api.Test; @@ -41,7 +44,8 @@ class NamingPushRequestHandlerTest { void testRequestReply() { //given ServiceInfoHolder holder = mock(ServiceInfoHolder.class); - NamingPushRequestHandler handler = new NamingPushRequestHandler(holder); + FuzzyWatchServiceListHolder fuzzyWatchServiceListHolder = mock(FuzzyWatchServiceListHolder.class); + NamingPushRequestHandler handler = new NamingPushRequestHandler(holder, fuzzyWatchServiceListHolder); ServiceInfo info = new ServiceInfo("name", "cluster1"); Request req = NotifySubscriberRequest.buildNotifySubscriberRequest(info); //when diff --git a/example/src/main/java/com/alibaba/nacos/example/FuzzyWatchExample.java b/example/src/main/java/com/alibaba/nacos/example/FuzzyWatchExample.java new file mode 100644 index 00000000000..20f0829eb94 --- /dev/null +++ b/example/src/main/java/com/alibaba/nacos/example/FuzzyWatchExample.java @@ -0,0 +1,115 @@ +/* + * 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.example; + +import com.alibaba.nacos.api.exception.NacosException; +import com.alibaba.nacos.api.naming.NamingFactory; +import com.alibaba.nacos.api.naming.NamingService; +import com.alibaba.nacos.api.naming.listener.AbstractFuzzyWatchEventListener; +import com.alibaba.nacos.api.naming.listener.FuzzyWatchNotifyEvent; + +import java.util.Properties; +import java.util.concurrent.Executor; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +import static com.alibaba.nacos.api.common.Constants.DEFAULT_GROUP; + +/** + * Nacos naming fuzzy watch example. + *

Add the JVM parameter to run the NamingExample:

+ * {@code -DserverAddr=${nacos.server.ip}:${nacos.server.port} -Dnamespace=${namespaceId}} + * + * @author tanyongquan + */ +public class FuzzyWatchExample { + + public static void main(String[] args) throws NacosException, InterruptedException { + + Properties properties = new Properties(); + properties.setProperty("serverAddr", System.getProperty("serverAddr", "localhost")); + properties.setProperty("namespace", System.getProperty("namespace", "public")); + + NamingService naming = NamingFactory.createNamingService(properties); + + int num = 5; + for (int i = 1; i <= num; i++) { + String s = "nacos.test." + i; + naming.registerInstance(s, "11.11.11.11", 8888); + } + + System.out.println(num + " instance have been registered"); + + Executor executor = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue(), + runnable -> { + Thread thread = new Thread(runnable); + thread.setName("test-thread"); + return thread; + }); + + naming.fuzzyWatch(DEFAULT_GROUP, new AbstractFuzzyWatchEventListener() { + + //EventListener onEvent is sync to handle, If process too low in onEvent, maybe block other onEvent callback. + //So you can override getExecutor() to async handle event. + @Override + public Executor getExecutor() { + return executor; + } + + @Override + public void onEvent(FuzzyWatchNotifyEvent event) { + System.out.println("[Fuzzy-Watch-GROUP]changed service name: " + event.getService().getGroupedServiceName()); + System.out.println("[Fuzzy-Watch-GROUP]change type: " + event.getChangeType()); + } + }); + + naming.fuzzyWatch("nacos.test.*", DEFAULT_GROUP, new AbstractFuzzyWatchEventListener() { + + @Override + public Executor getExecutor() { + return executor; + } + + @Override + public void onEvent(FuzzyWatchNotifyEvent event) { + System.out.println("[Prefix-Fuzzy-Watch]changed service name: " + event.getService().getGroupedServiceName()); + System.out.println("[Prefix-Fuzzy-Watch]change type: " + event.getChangeType()); + } + }); + + naming.registerInstance("nacos.test.-1", "11.11.11.11", 8888); + + Thread.sleep(1000); + + naming.registerInstance("nacos.OTHER-PREFIX", "11.11.11.11", 8888); + + Thread.sleep(1000); + + naming.registerInstance("nacos.OTHER-GROUP", "OTHER-GROUP", "11.11.11.11", 8888); + + Thread.sleep(1000); + + for (int i = 1; i <= num; i++) { + String s = "nacos.test." + i; + naming.deregisterInstance(s, "11.11.11.11", 8888); + } + + Thread.sleep(1000); + + } +} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/client/AbstractClient.java b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/client/AbstractClient.java index 5662a5ae257..4c967a4b1c7 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/client/AbstractClient.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/client/AbstractClient.java @@ -17,6 +17,7 @@ package com.alibaba.nacos.naming.core.v2.client; import com.alibaba.nacos.common.notify.NotifyCenter; +import com.alibaba.nacos.common.utils.ConcurrentHashSet; import com.alibaba.nacos.naming.core.v2.event.client.ClientEvent; import com.alibaba.nacos.naming.core.v2.pojo.BatchInstanceData; import com.alibaba.nacos.naming.core.v2.pojo.BatchInstancePublishInfo; @@ -47,6 +48,8 @@ public abstract class AbstractClient implements Client { protected final ConcurrentHashMap subscribers = new ConcurrentHashMap<>(16, 0.75f, 1); + protected final ConcurrentHashSet watchedPattern = new ConcurrentHashSet<>(); + protected volatile long lastUpdatedTime; protected final AtomicLong revision; @@ -134,6 +137,34 @@ public Collection getAllSubscribeService() { return subscribers.keySet(); } + @Override + public boolean addWatchedPattern(String watchPattern) { + if (watchedPattern.add(watchPattern)) { + // TODO:Watch MetricsMonitor + return true; + } + return true; + } + + @Override + public boolean removeWatchedPattern(String watchPattern) { + if (watchedPattern.remove(watchPattern)) { + // TODO:Watch MetricsMonitor + return true; + } + return true; + } + + @Override + public boolean isWatchedPattern(String watchPattern) { + return watchedPattern.contains(watchPattern); + } + + @Override + public Collection getAllFuzzyWatchPattern() { + return watchedPattern; + } + @Override public ClientSyncData generateSyncData() { List namespaces = new LinkedList<>(); diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/client/Client.java b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/client/Client.java index 6cbe0112dc8..94bfb33f7a9 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/client/Client.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/client/Client.java @@ -122,6 +122,37 @@ public interface Client { */ Collection getAllSubscribeService(); + /** + * Add a watched pattern for this client. + * + * @param pattern watch pattern. + * @return true if add successfully, otherwise false + */ + boolean addWatchedPattern(String pattern); + + /** + * Remove a watched pattern for this client. + * + * @param pattern watch pattern. + * @return true if remove successfully, otherwise false + */ + boolean removeWatchedPattern(String pattern); + + /** + * Judge whether watch this pattern of this client. + * + * @param watchPattern watch patten + * @return true if client watch the given pattern, otherwise false + */ + boolean isWatchedPattern(String watchPattern); + + /** + * Get all watched pattern of current client. + * + * @return watch patterns + */ + Collection getAllFuzzyWatchPattern(); + /** * Generate sync data. * 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 93ecd25cdea..4274cfef9ea 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 @@ -94,6 +94,45 @@ public ClientUnsubscribeServiceEvent(Service service, String clientId) { } } + /** + * Client fuzzy watch service event. + */ + public static class ClientFuzzyWatchEvent extends ClientOperationEvent { + + private static final long serialVersionUID = -4518919987813223119L; + + private final String pattern; + + public ClientFuzzyWatchEvent(String pattern, String clientId) { + super(clientId, null); + this.pattern = pattern; + } + + public String getPattern() { + return pattern; + } + + } + + /** + * 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 { private static final long serialVersionUID = -281486927726245701L; diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/event/service/ServiceEvent.java b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/event/service/ServiceEvent.java index 6d559529880..76ee2a7b2b9 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/event/service/ServiceEvent.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/event/service/ServiceEvent.java @@ -19,6 +19,8 @@ import com.alibaba.nacos.common.notify.Event; import com.alibaba.nacos.naming.core.v2.pojo.Service; +import java.util.Collection; + /** * Service event. * @@ -28,7 +30,10 @@ public class ServiceEvent extends Event { private static final long serialVersionUID = -9173247502346692418L; - private final Service service; + private Service service; + + public ServiceEvent() { + } public ServiceEvent(Service service) { this.service = service; @@ -45,17 +50,25 @@ public static class ServiceChangedEvent extends ServiceEvent { private static final long serialVersionUID = 2123694271992630822L; - public ServiceChangedEvent(Service service) { - this(service, false); + private final String changedType; + + public ServiceChangedEvent(Service service, String changedType) { + this(service, changedType, false); } - public ServiceChangedEvent(Service service, boolean incrementRevision) { + public ServiceChangedEvent(Service service, String changedType, boolean incrementRevision) { super(service); + this.changedType = changedType; service.renewUpdateTime(); if (incrementRevision) { service.incrementRevision(); } } + + public String getChangedType() { + return changedType; + } + } /** @@ -77,4 +90,36 @@ public String getClientId() { } } + /** + * A client initiates a fuzzy watch request. + */ + public static class ServiceFuzzyWatchInitEvent extends ServiceEvent { + + private static final long serialVersionUID = -2645441445867337345L; + + private final String clientId; + + private final String pattern; + + private final Collection matchedService; + + public ServiceFuzzyWatchInitEvent(String clientId, String pattern, Collection matchedService) { + super(); + this.clientId = clientId; + this.pattern = pattern; + this.matchedService = matchedService; + } + + public String getClientId() { + return clientId; + } + + public String getPattern() { + return pattern; + } + + public Collection getMatchedService() { + return matchedService; + } + } } diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/index/ClientServiceIndexesManager.java b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/index/ClientServiceIndexesManager.java index 48ea5611ae6..c5506e2dc4f 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/index/ClientServiceIndexesManager.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/index/ClientServiceIndexesManager.java @@ -16,21 +16,27 @@ package com.alibaba.nacos.naming.core.v2.index; +import com.alibaba.nacos.api.common.Constants; +import com.alibaba.nacos.api.naming.utils.NamingUtils; import com.alibaba.nacos.common.notify.Event; import com.alibaba.nacos.common.notify.NotifyCenter; import com.alibaba.nacos.common.notify.listener.SmartSubscriber; import com.alibaba.nacos.common.trace.DeregisterInstanceReason; import com.alibaba.nacos.common.trace.event.naming.DeregisterInstanceTraceEvent; +import com.alibaba.nacos.common.utils.CollectionUtils; import com.alibaba.nacos.common.utils.ConcurrentHashSet; +import com.alibaba.nacos.naming.core.v2.ServiceManager; import com.alibaba.nacos.naming.core.v2.client.Client; import com.alibaba.nacos.naming.core.v2.event.client.ClientOperationEvent; import com.alibaba.nacos.naming.core.v2.event.publisher.NamingEventPublisherFactory; import com.alibaba.nacos.naming.core.v2.event.service.ServiceEvent; import com.alibaba.nacos.naming.core.v2.pojo.InstancePublishInfo; import com.alibaba.nacos.naming.core.v2.pojo.Service; +import com.alibaba.nacos.naming.misc.Loggers; import org.springframework.stereotype.Component; import java.util.Collection; +import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Set; @@ -49,6 +55,16 @@ public class ClientServiceIndexesManager extends SmartSubscriber { private final ConcurrentMap> subscriberIndexes = new ConcurrentHashMap<>(); + /** + * The content of map is {fuzzy watch pattern -> Set[watcher clientID]}. + */ + private final ConcurrentMap> fuzzyWatcherIndexes = new ConcurrentHashMap<>(); + + /** + * The content of map is {service -> Set[matched fuzzy watch patterns]}. + */ + private final ConcurrentMap> fuzzyWatchPatternMatchIndexes = new ConcurrentHashMap<>(); + public ClientServiceIndexesManager() { NotifyCenter.registerSubscriber(this, NamingEventPublisherFactory.getInstance()); } @@ -65,6 +81,15 @@ public Collection getSubscribedService() { return subscriberIndexes.keySet(); } + public Collection getServiceMatchedPatterns(Service service) { + return fuzzyWatchPatternMatchIndexes.containsKey(service) + ? fuzzyWatchPatternMatchIndexes.get(service) : new ConcurrentHashSet<>(); + } + + public Collection getAllClientFuzzyWatchedPattern(String pattern) { + return fuzzyWatcherIndexes.containsKey(pattern) ? fuzzyWatcherIndexes.get(pattern) : new ConcurrentHashSet<>(); + } + /** * Clear the service index without instances. * @@ -73,6 +98,7 @@ public Collection getSubscribedService() { public void removePublisherIndexesByEmptyService(Service service) { if (publisherIndexes.containsKey(service) && publisherIndexes.get(service).isEmpty()) { publisherIndexes.remove(service); + fuzzyWatchPatternMatchIndexes.remove(service); } } @@ -83,6 +109,8 @@ public List> subscribeTypes() { result.add(ClientOperationEvent.ClientDeregisterServiceEvent.class); result.add(ClientOperationEvent.ClientSubscribeServiceEvent.class); result.add(ClientOperationEvent.ClientUnsubscribeServiceEvent.class); + result.add(ClientOperationEvent.ClientFuzzyWatchEvent.class); + result.add(ClientOperationEvent.ClientCancelFuzzyWatchEvent.class); result.add(ClientOperationEvent.ClientReleaseEvent.class); return result; } @@ -101,6 +129,9 @@ private void handleClientDisconnect(ClientOperationEvent.ClientReleaseEvent even for (Service each : client.getAllSubscribeService()) { removeSubscriberIndexes(each, client.getClientId()); } + for (String eachPattern : client.getAllFuzzyWatchPattern()) { + removeFuzzyWatcherIndexes(eachPattern, client.getClientId()); + } DeregisterInstanceReason reason = event.isNative() ? DeregisterInstanceReason.NATIVE_DISCONNECTED : DeregisterInstanceReason.SYNCED_DISCONNECTED; long currentTimeMillis = System.currentTimeMillis(); @@ -124,18 +155,32 @@ private void handleClientOperation(ClientOperationEvent event) { addSubscriberIndexes(service, clientId); } else if (event instanceof ClientOperationEvent.ClientUnsubscribeServiceEvent) { removeSubscriberIndexes(service, clientId); + } else if (event instanceof ClientOperationEvent.ClientFuzzyWatchEvent) { + String completedPattern = ((ClientOperationEvent.ClientFuzzyWatchEvent) event).getPattern(); + addFuzzyWatcherIndexes(completedPattern, clientId); + } else if (event instanceof ClientOperationEvent.ClientCancelFuzzyWatchEvent) { + String completedPattern = ((ClientOperationEvent.ClientCancelFuzzyWatchEvent) event).getPattern(); + removeFuzzyWatcherIndexes(completedPattern, clientId); } } private void addPublisherIndexes(Service service, String clientId) { + String serviceChangedType = Constants.ServiceChangedType.INSTANCE_CHANGED; + if (!publisherIndexes.containsKey(service)) { + // The only time the index needs to be updated is when the service is first created + updateWatchMatchIndex(service); + serviceChangedType = Constants.ServiceChangedType.ADD_SERVICE; + } + NotifyCenter.publishEvent(new ServiceEvent.ServiceChangedEvent(service, serviceChangedType, true)); publisherIndexes.computeIfAbsent(service, key -> new ConcurrentHashSet<>()).add(clientId); - NotifyCenter.publishEvent(new ServiceEvent.ServiceChangedEvent(service, true)); } private void removePublisherIndexes(Service service, String clientId) { publisherIndexes.computeIfPresent(service, (s, ids) -> { ids.remove(clientId); - NotifyCenter.publishEvent(new ServiceEvent.ServiceChangedEvent(service, true)); + String serviceChangedType = ids.isEmpty() ? Constants.ServiceChangedType.DELETE_SERVICE : + Constants.ServiceChangedType.INSTANCE_CHANGED; + NotifyCenter.publishEvent(new ServiceEvent.ServiceChangedEvent(service, serviceChangedType, true)); return ids.isEmpty() ? null : ids; }); } @@ -158,4 +203,86 @@ private void removeSubscriberIndexes(Service service, String clientId) { subscriberIndexes.remove(service); } } + + private void addFuzzyWatcherIndexes(String completedPattern, String clientId) { + fuzzyWatcherIndexes.computeIfAbsent(completedPattern, key -> new ConcurrentHashSet<>()); + fuzzyWatcherIndexes.get(completedPattern).add(clientId); + Collection matchedService = updateWatchMatchIndex(completedPattern); + NotifyCenter.publishEvent(new ServiceEvent.ServiceFuzzyWatchInitEvent(clientId, completedPattern, matchedService)); + } + + private void removeFuzzyWatcherIndexes(String completedPattern, String clientId) { + if (!fuzzyWatcherIndexes.containsKey(completedPattern)) { + return; + } + fuzzyWatcherIndexes.get(completedPattern).remove(clientId); + if (fuzzyWatcherIndexes.get(completedPattern).isEmpty()) { + fuzzyWatcherIndexes.remove(completedPattern); + } + } + + /** + * This method will build/update the fuzzy watch match index of all patterns. + * + * @param service The service of the Nacos. + */ + public void updateWatchMatchIndex(Service service) { + long matchBeginTime = System.currentTimeMillis(); + Set filteredPattern = NamingUtils.filterPatternWithNamespace(service.getNamespace(), fuzzyWatcherIndexes.keySet()); + Set matchedPattern = NamingUtils.getServiceMatchedPatterns(service.getName(), service.getGroup(), + filteredPattern); + if (CollectionUtils.isNotEmpty(matchedPattern)) { + fuzzyWatchPatternMatchIndexes.computeIfAbsent(service, key -> new ConcurrentHashSet<>()); + for (String each : matchedPattern) { + fuzzyWatchPatternMatchIndexes.get(service).add(NamingUtils.getPatternWithNamespace(service.getNamespace(), each)); + } + Loggers.PERFORMANCE_LOG.info("WATCH: new service {} match {} pattern, {}ms", service.getGroupedServiceName(), + matchedPattern.size(), System.currentTimeMillis() - matchBeginTime); + } + } + + /** + * This method will build/update the fuzzy watch match index for given patterns. + * + * @param completedPattern the completed pattern of watch (with namespace id). + * @return Updated set of services in Nacos server that can match this pattern. + */ + public Collection updateWatchMatchIndex(String completedPattern) { + long matchBeginTime = System.currentTimeMillis(); + String namespaceId = NamingUtils.getNamespaceFromPattern(completedPattern); + Collection serviceSet = ServiceManager.getInstance().getSingletons(namespaceId); + String pattern = NamingUtils.getPatternRemovedNamespace(completedPattern); + + Set matchedService = new HashSet<>(); + for (Service service : serviceSet) { + String serviceName = service.getName(); + String groupName = service.getGroup(); + String serviceNamePattern = NamingUtils.getServiceName(pattern); + String groupNamePattern = NamingUtils.getGroupName(pattern); + if (NamingUtils.isMatchPattern(serviceName, groupName, serviceNamePattern, groupNamePattern)) { + fuzzyWatchPatternMatchIndexes.computeIfAbsent(service, key -> new ConcurrentHashSet<>()); + fuzzyWatchPatternMatchIndexes.get(service).add(completedPattern); + matchedService.add(service); + } + } + Loggers.PERFORMANCE_LOG.info("WATCH: pattern {} match {} services, {}ms", completedPattern, + matchedService.size(), System.currentTimeMillis() - matchBeginTime); + return matchedService; + } + + /** + * This method will remove the match index of fuzzy watch pattern. + * + * @param service The service of the Nacos. + * @param matchedPattern the pattern to remove + */ + public void removeWatchPatternMatchIndex(Service service, String matchedPattern) { + if (!fuzzyWatchPatternMatchIndexes.containsKey(service)) { + return; + } + fuzzyWatchPatternMatchIndexes.get(service).remove(matchedPattern); + if (fuzzyWatchPatternMatchIndexes.get(service).isEmpty()) { + fuzzyWatchPatternMatchIndexes.remove(service); + } + } } diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/metadata/InstanceMetadataProcessor.java b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/metadata/InstanceMetadataProcessor.java index 92cb1ee4f51..d7e6514f287 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/metadata/InstanceMetadataProcessor.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/metadata/InstanceMetadataProcessor.java @@ -108,7 +108,8 @@ private void updateInstanceMetadata(MetadataOperation op) { Service service = Service.newService(op.getNamespace(), op.getGroup(), op.getServiceName()); service = ServiceManager.getInstance().getSingleton(service); namingMetadataManager.updateInstanceMetadata(service, op.getTag(), op.getMetadata()); - NotifyCenter.publishEvent(new ServiceEvent.ServiceChangedEvent(service, true)); + NotifyCenter.publishEvent(new ServiceEvent.ServiceChangedEvent(service, + com.alibaba.nacos.api.common.Constants.ServiceChangedType.INSTANCE_CHANGED, true)); } private void deleteInstanceMetadata(MetadataOperation op) { diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/service/ClientOperationService.java b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/service/ClientOperationService.java index 3addf41e62f..b2696dbc68a 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/service/ClientOperationService.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/service/ClientOperationService.java @@ -85,6 +85,30 @@ default void unsubscribeService(Service service, Subscriber subscriber, String c } + /** + * Fuzzy watch a pattern. + * + * @param namespaceId namespace id of this pattern + * @param serviceNamePattern watch service name pattern rule + * @param groupNamePattern watch service name pattern rule + * @param clientId id of client + */ + default void fuzzyWatch(String namespaceId, String serviceNamePattern, String groupNamePattern, String clientId) { + + } + + /** + * Cancel fuzzy watch a pattern. + * + * @param namespaceId namespace id of this pattern + * @param serviceNamePattern watch service name pattern + * @param groupNamePattern watch service name pattern + * @param clientId id of client + */ + default void cancelFuzzyWatch(String namespaceId, String serviceNamePattern, String groupNamePattern, String clientId) { + + } + /** * get publish info. * diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/service/ClientOperationServiceProxy.java b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/service/ClientOperationServiceProxy.java index 2efa7c6dd9f..7284af587e1 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/service/ClientOperationServiceProxy.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/service/ClientOperationServiceProxy.java @@ -85,6 +85,16 @@ public void unsubscribeService(Service service, Subscriber subscriber, String cl ephemeralClientOperationService.unsubscribeService(service, subscriber, clientId); } + @Override + public void fuzzyWatch(String namespaceId, String serviceNamePattern, String groupNamePattern, String clientId) { + ephemeralClientOperationService.fuzzyWatch(namespaceId, serviceNamePattern, groupNamePattern, clientId); + } + + @Override + public void cancelFuzzyWatch(String namespaceId, String serviceNamePattern, String groupNamePattern, String clientId) { + ephemeralClientOperationService.cancelFuzzyWatch(namespaceId, serviceNamePattern, groupNamePattern, clientId); + } + private ClientOperationService chooseClientOperationService(final Instance instance) { return instance.isEphemeral() ? ephemeralClientOperationService : persistentClientOperationService; } diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/service/impl/EphemeralClientOperationServiceImpl.java b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/service/impl/EphemeralClientOperationServiceImpl.java index 5bcb460fc9d..1ef746435a0 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/service/impl/EphemeralClientOperationServiceImpl.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/service/impl/EphemeralClientOperationServiceImpl.java @@ -136,6 +136,35 @@ public void unsubscribeService(Service service, Subscriber subscriber, String cl client.setLastUpdatedTime(); NotifyCenter.publishEvent(new ClientOperationEvent.ClientUnsubscribeServiceEvent(singleton, clientId)); } + + @Override + public void fuzzyWatch(String namespaceId, String serviceNamePattern, String groupNamePattern, String clientId) { + String patternWithoutNamespace = NamingUtils.getGroupedName(serviceNamePattern, groupNamePattern); + // need store namespace id in server side + String completedPattern = NamingUtils.getPatternWithNamespace(namespaceId, patternWithoutNamespace); + Client client = clientManager.getClient(clientId); + if (!clientIsLegal(client, clientId)) { + return; + } + client.addWatchedPattern(completedPattern); + client.setLastUpdatedTime(); + NotifyCenter.publishEvent(new ClientOperationEvent.ClientFuzzyWatchEvent(completedPattern, clientId)); + } + + @Override + public void cancelFuzzyWatch(String namespaceId, String serviceNamePattern, String groupNamePattern, String clientId) { + String patternWithoutNamespace = NamingUtils.getGroupedName(serviceNamePattern, groupNamePattern); + String completedPattern = NamingUtils.getPatternWithNamespace(namespaceId, patternWithoutNamespace); + Client client = clientManager.getClient(clientId); + if (!clientIsLegal(client, clientId)) { + return; + } + client.removeWatchedPattern(completedPattern); + client.setLastUpdatedTime(); + NotifyCenter.publishEvent(new ClientOperationEvent.ClientCancelFuzzyWatchEvent(completedPattern, clientId)); + } + + private boolean clientIsLegal(Client client, String clientId) { private void checkClientIsLegal(Client client, String clientId) { if (client == null) { diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/service/impl/PersistentClientOperationServiceImpl.java b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/service/impl/PersistentClientOperationServiceImpl.java index 7cb0d32cc9e..d52d35d46d0 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/service/impl/PersistentClientOperationServiceImpl.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/service/impl/PersistentClientOperationServiceImpl.java @@ -182,6 +182,16 @@ public void unsubscribeService(Service service, Subscriber subscriber, String cl throw new UnsupportedOperationException("No persistent subscribers"); } + @Override + public void fuzzyWatch(String namespaceId, String serviceNamePattern, String groupNamePattern, String clientId) { + throw new UnsupportedOperationException("No persistent fuzzy watcher"); + } + + @Override + public void cancelFuzzyWatch(String namespaceId, String serviceNamePattern, String groupNamePattern, String clientId) { + throw new UnsupportedOperationException("No persistent fuzzy watcher"); + } + @Override public Response onRequest(ReadRequest request) { throw new UnsupportedOperationException("Temporary does not support"); diff --git a/naming/src/main/java/com/alibaba/nacos/naming/healthcheck/heartbeat/ClientBeatProcessorV2.java b/naming/src/main/java/com/alibaba/nacos/naming/healthcheck/heartbeat/ClientBeatProcessorV2.java index dee58b8c67f..d1125aac39a 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/healthcheck/heartbeat/ClientBeatProcessorV2.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/healthcheck/heartbeat/ClientBeatProcessorV2.java @@ -16,6 +16,7 @@ package com.alibaba.nacos.naming.healthcheck.heartbeat; +import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.naming.utils.NamingUtils; import com.alibaba.nacos.common.notify.NotifyCenter; import com.alibaba.nacos.common.trace.event.naming.HealthStateChangeTraceEvent; @@ -67,7 +68,7 @@ public void run() { instance.setHealthy(true); Loggers.EVT_LOG.info("service: {} {POS} {IP-ENABLED} valid: {}:{}@{}, region: {}, msg: client beat ok", rsInfo.getServiceName(), ip, port, rsInfo.getCluster(), UtilsAndCommons.LOCALHOST_SITE); - NotifyCenter.publishEvent(new ServiceEvent.ServiceChangedEvent(service)); + NotifyCenter.publishEvent(new ServiceEvent.ServiceChangedEvent(service, Constants.ServiceChangedType.HEART_BEAT)); NotifyCenter.publishEvent(new ClientEvent.ClientChangedEvent(client)); NotifyCenter.publishEvent(new HealthStateChangeTraceEvent(System.currentTimeMillis(), service.getNamespace(), service.getGroup(), service.getName(), instance.getIp(), diff --git a/naming/src/main/java/com/alibaba/nacos/naming/healthcheck/heartbeat/UnhealthyInstanceChecker.java b/naming/src/main/java/com/alibaba/nacos/naming/healthcheck/heartbeat/UnhealthyInstanceChecker.java index 1614a5cf7aa..b3d65847d56 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/healthcheck/heartbeat/UnhealthyInstanceChecker.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/healthcheck/heartbeat/UnhealthyInstanceChecker.java @@ -76,7 +76,7 @@ private void changeHealthyStatus(Client client, Service service, HealthCheckInst .info("{POS} {IP-DISABLED} valid: {}:{}@{}@{}, region: {}, msg: client last beat: {}", instance.getIp(), instance.getPort(), instance.getCluster(), service.getName(), UtilsAndCommons.LOCALHOST_SITE, instance.getLastHeartBeatTime()); - NotifyCenter.publishEvent(new ServiceEvent.ServiceChangedEvent(service)); + NotifyCenter.publishEvent(new ServiceEvent.ServiceChangedEvent(service, Constants.ServiceChangedType.HEART_BEAT)); NotifyCenter.publishEvent(new ClientEvent.ClientChangedEvent(client)); NotifyCenter.publishEvent(new HealthStateChangeTraceEvent(System.currentTimeMillis(), service.getNamespace(), service.getGroup(), service.getName(), instance.getIp(), instance.getPort(), diff --git a/naming/src/main/java/com/alibaba/nacos/naming/push/v2/NamingSubscriberServiceV2Impl.java b/naming/src/main/java/com/alibaba/nacos/naming/push/v2/NamingSubscriberServiceV2Impl.java index 9dd25664f54..95d5fe481f9 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/push/v2/NamingSubscriberServiceV2Impl.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/push/v2/NamingSubscriberServiceV2Impl.java @@ -16,7 +16,9 @@ package com.alibaba.nacos.naming.push.v2; +import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.naming.utils.NamingUtils; +import com.alibaba.nacos.api.utils.StringUtils; import com.alibaba.nacos.common.notify.Event; import com.alibaba.nacos.common.notify.NotifyCenter; import com.alibaba.nacos.common.notify.listener.SmartSubscriber; @@ -33,6 +35,9 @@ import com.alibaba.nacos.naming.pojo.Subscriber; import com.alibaba.nacos.naming.push.NamingSubscriberService; import com.alibaba.nacos.naming.push.v2.executor.PushExecutorDelegate; +import com.alibaba.nacos.naming.push.v2.task.FuzzyWatchInitDelayTask; +import com.alibaba.nacos.naming.push.v2.task.FuzzyWatchNotifyChangeDelayTask; +import com.alibaba.nacos.naming.push.v2.task.FuzzyWatchPushDelayTaskEngine; import com.alibaba.nacos.naming.push.v2.task.PushDelayTask; import com.alibaba.nacos.naming.push.v2.task.PushDelayTaskExecuteEngine; @@ -40,6 +45,7 @@ import java.util.HashSet; import java.util.LinkedList; import java.util.List; +import java.util.stream.Collectors; import java.util.stream.Stream; /** @@ -58,6 +64,8 @@ public class NamingSubscriberServiceV2Impl extends SmartSubscriber implements Na private final PushDelayTaskExecuteEngine delayTaskEngine; + private final FuzzyWatchPushDelayTaskEngine fuzzyWatchPushDelayTaskEngine; + public NamingSubscriberServiceV2Impl(ClientManagerDelegate clientManager, ClientServiceIndexesManager indexesManager, ServiceStorage serviceStorage, NamingMetadataManager metadataManager, PushExecutorDelegate pushExecutor, SwitchDomain switchDomain) { @@ -65,6 +73,8 @@ public NamingSubscriberServiceV2Impl(ClientManagerDelegate clientManager, this.indexesManager = indexesManager; this.delayTaskEngine = new PushDelayTaskExecuteEngine(clientManager, indexesManager, serviceStorage, metadataManager, pushExecutor, switchDomain); + this.fuzzyWatchPushDelayTaskEngine = new FuzzyWatchPushDelayTaskEngine(clientManager, indexesManager, + serviceStorage, metadataManager, pushExecutor, switchDomain); NotifyCenter.registerSubscriber(this, NamingEventPublisherFactory.getInstance()); } @@ -108,6 +118,7 @@ public List> subscribeTypes() { List> result = new LinkedList<>(); result.add(ServiceEvent.ServiceChangedEvent.class); result.add(ServiceEvent.ServiceSubscribedEvent.class); + result.add(ServiceEvent.ServiceFuzzyWatchInitEvent.class); return result; } @@ -118,6 +129,9 @@ public void onEvent(Event event) { ServiceEvent.ServiceChangedEvent serviceChangedEvent = (ServiceEvent.ServiceChangedEvent) event; Service service = serviceChangedEvent.getService(); delayTaskEngine.addTask(service, new PushDelayTask(service, PushConfig.getInstance().getPushTaskDelay())); + // watch notify push task specify by service + fuzzyWatchPushDelayTaskEngine.addTask(service, new FuzzyWatchNotifyChangeDelayTask(service, + serviceChangedEvent.getChangedType(), PushConfig.getInstance().getPushTaskDelay())); MetricsMonitor.incrementServiceChangeCount(service); } else if (event instanceof ServiceEvent.ServiceSubscribedEvent) { // If service is subscribed by one client, only push this client. @@ -125,7 +139,31 @@ public void onEvent(Event event) { Service service = subscribedEvent.getService(); delayTaskEngine.addTask(service, new PushDelayTask(service, PushConfig.getInstance().getPushTaskDelay(), subscribedEvent.getClientId())); + } else if (event instanceof ServiceEvent.ServiceFuzzyWatchInitEvent) { + ServiceEvent.ServiceFuzzyWatchInitEvent serviceFuzzyWatchInitEvent = (ServiceEvent.ServiceFuzzyWatchInitEvent) event; + + String completedPattern = serviceFuzzyWatchInitEvent.getPattern(); + String clientId = serviceFuzzyWatchInitEvent.getClientId(); + String taskKey = getTaskKey(clientId, completedPattern); + Collection matchedService = serviceFuzzyWatchInitEvent.getMatchedService().stream() + .map(Service::getGroupedServiceName) + .collect(Collectors.toSet()); + int originSize = matchedService.size(); + // watch init push task is specify by client id with pattern + // The key is just used to differentiate between different initialization tasks and merge them if needed. + fuzzyWatchPushDelayTaskEngine.addTask(taskKey, new FuzzyWatchInitDelayTask(taskKey, clientId, completedPattern, matchedService, + originSize, PushConfig.getInstance().getPushTaskDelay(), false)); + } + } + + private String getTaskKey(String clientId, String pattern) { + if (StringUtils.isBlank(clientId)) { + throw new IllegalArgumentException("Param 'clientId' is illegal, clientId is blank"); + } + if (StringUtils.isBlank(pattern)) { + throw new IllegalArgumentException("Param 'pattern' is illegal, pattern is blank"); } + return clientId + Constants.SERVICE_INFO_SPLITER + pattern; } private Stream getServiceStream() { diff --git a/naming/src/main/java/com/alibaba/nacos/naming/push/v2/executor/PushExecutor.java b/naming/src/main/java/com/alibaba/nacos/naming/push/v2/executor/PushExecutor.java index 21bcfab0d4b..553ac555a5f 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/push/v2/executor/PushExecutor.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/push/v2/executor/PushExecutor.java @@ -16,6 +16,8 @@ package com.alibaba.nacos.naming.push.v2.executor; +import com.alibaba.nacos.api.naming.remote.request.AbstractFuzzyWatchNotifyRequest; +import com.alibaba.nacos.api.remote.PushCallBack; import com.alibaba.nacos.naming.pojo.Subscriber; import com.alibaba.nacos.naming.push.v2.PushDataWrapper; import com.alibaba.nacos.naming.push.v2.task.NamingPushCallback; @@ -45,4 +47,23 @@ public interface PushExecutor { * @param callBack callback */ void doPushWithCallback(String clientId, Subscriber subscriber, PushDataWrapper data, NamingPushCallback callBack); + + + /** + * Do push to notify fuzzy watcher. + * + * @param clientId client id + * @param fuzzyWatchNotifyRequest request for fuzzy watch notification + */ + void doWatcherNotifyPush(String clientId, AbstractFuzzyWatchNotifyRequest fuzzyWatchNotifyRequest); + + /** + * Do push to notify fuzzy watcher with call back. + * + * @param clientId client id + * @param fuzzyWatchNotifyRequest request for fuzzy watch notification + * @param callBack callback + */ + void doFuzzyWatchNotifyPushWithCallBack(String clientId, AbstractFuzzyWatchNotifyRequest fuzzyWatchNotifyRequest, PushCallBack callBack); + } diff --git a/naming/src/main/java/com/alibaba/nacos/naming/push/v2/executor/PushExecutorDelegate.java b/naming/src/main/java/com/alibaba/nacos/naming/push/v2/executor/PushExecutorDelegate.java index f1fa7288e07..4acad457d27 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/push/v2/executor/PushExecutorDelegate.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/push/v2/executor/PushExecutorDelegate.java @@ -16,6 +16,8 @@ package com.alibaba.nacos.naming.push.v2.executor; +import com.alibaba.nacos.api.naming.remote.request.AbstractFuzzyWatchNotifyRequest; +import com.alibaba.nacos.api.remote.PushCallBack; import com.alibaba.nacos.naming.core.v2.client.impl.IpPortBasedClient; import com.alibaba.nacos.naming.pojo.Subscriber; import com.alibaba.nacos.naming.push.v2.PushDataWrapper; @@ -53,6 +55,19 @@ public void doPushWithCallback(String clientId, Subscriber subscriber, PushDataW getPushExecuteService(clientId, subscriber).doPushWithCallback(clientId, subscriber, data, callBack); } + @Override + public void doWatcherNotifyPush(String clientId, AbstractFuzzyWatchNotifyRequest watchNotifyRequest) { + // only support fuzzy watch by rpc + rpcPushExecuteService.doWatcherNotifyPush(clientId, watchNotifyRequest); + } + + @Override + public void doFuzzyWatchNotifyPushWithCallBack(String clientId, AbstractFuzzyWatchNotifyRequest watchNotifyRequest, + PushCallBack callBack) { + // only support fuzzy watch by rpc + rpcPushExecuteService.doFuzzyWatchNotifyPushWithCallBack(clientId, watchNotifyRequest, callBack); + } + private PushExecutor getPushExecuteService(String clientId, Subscriber subscriber) { Optional result = SpiImplPushExecutorHolder.getInstance() .findPushExecutorSpiImpl(clientId, subscriber); diff --git a/naming/src/main/java/com/alibaba/nacos/naming/push/v2/executor/PushExecutorRpcImpl.java b/naming/src/main/java/com/alibaba/nacos/naming/push/v2/executor/PushExecutorRpcImpl.java index 143d4715bdf..d540ca54223 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/push/v2/executor/PushExecutorRpcImpl.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/push/v2/executor/PushExecutorRpcImpl.java @@ -17,7 +17,9 @@ package com.alibaba.nacos.naming.push.v2.executor; import com.alibaba.nacos.api.naming.pojo.ServiceInfo; +import com.alibaba.nacos.api.naming.remote.request.AbstractFuzzyWatchNotifyRequest; import com.alibaba.nacos.api.naming.remote.request.NotifySubscriberRequest; +import com.alibaba.nacos.api.remote.PushCallBack; import com.alibaba.nacos.core.remote.RpcPushService; import com.alibaba.nacos.naming.misc.GlobalExecutor; import com.alibaba.nacos.naming.pojo.Subscriber; @@ -60,4 +62,15 @@ private ServiceInfo getServiceInfo(PushDataWrapper data, Subscriber subscriber) .selectInstancesWithHealthyProtection(data.getOriginalData(), data.getServiceMetadata(), false, true, subscriber); } + + @Override + public void doWatcherNotifyPush(String clientId, AbstractFuzzyWatchNotifyRequest watchNotifyRequest) { + pushService.pushWithoutAck(clientId, watchNotifyRequest); + } + + @Override + public void doFuzzyWatchNotifyPushWithCallBack(String clientId, AbstractFuzzyWatchNotifyRequest watchNotifyRequest, PushCallBack callBack) { + pushService.pushWithCallback(clientId, watchNotifyRequest, callBack, GlobalExecutor.getCallbackExecutor()); + } + } diff --git a/naming/src/main/java/com/alibaba/nacos/naming/push/v2/executor/PushExecutorUdpImpl.java b/naming/src/main/java/com/alibaba/nacos/naming/push/v2/executor/PushExecutorUdpImpl.java index c60478166d9..3324a3345d2 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/push/v2/executor/PushExecutorUdpImpl.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/push/v2/executor/PushExecutorUdpImpl.java @@ -17,7 +17,9 @@ package com.alibaba.nacos.naming.push.v2.executor; import com.alibaba.nacos.api.naming.pojo.ServiceInfo; +import com.alibaba.nacos.api.naming.remote.request.AbstractFuzzyWatchNotifyRequest; import com.alibaba.nacos.api.naming.utils.NamingUtils; +import com.alibaba.nacos.api.remote.PushCallBack; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.naming.pojo.Subscriber; import com.alibaba.nacos.naming.push.UdpPushService; @@ -93,4 +95,14 @@ private ServiceInfo handleClusterData(ServiceInfo data, Subscriber subscriber) { return StringUtils.isBlank(subscriber.getCluster()) ? data : ServiceUtil.selectInstances(data, subscriber.getCluster()); } + + @Override + public void doWatcherNotifyPush(String clientId, AbstractFuzzyWatchNotifyRequest watchNotifyRequest) { + + } + + @Override + public void doFuzzyWatchNotifyPushWithCallBack(String clientId, AbstractFuzzyWatchNotifyRequest watchNotifyRequest, PushCallBack callBack) { + + } } diff --git a/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchInitDelayTask.java b/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchInitDelayTask.java new file mode 100644 index 00000000000..3e5f9c66c28 --- /dev/null +++ b/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchInitDelayTask.java @@ -0,0 +1,91 @@ +/* + * 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.common.task.AbstractDelayTask; +import com.alibaba.nacos.naming.misc.Loggers; + +import java.util.Collection; + +/** + * Nacos naming fuzzy watch initial push delay task. + * + * @author tanyongquan + */ +public class FuzzyWatchInitDelayTask extends AbstractDelayTask { + + private final String taskKey; + + private final String clientId; + + private final String pattern; + + private final Collection matchedService; + + private final int originSize; + + private final boolean isFinishInit; + + public FuzzyWatchInitDelayTask(String taskKey, String clientId, String pattern, Collection matchedService, + int originSize, long delay, boolean isFinishInit) { + this.taskKey = taskKey; + this.clientId = clientId; + this.pattern = pattern; + this.matchedService = matchedService; + this.originSize = originSize; + this.isFinishInit = isFinishInit; + setTaskInterval(delay); + setLastProcessTime(System.currentTimeMillis()); + } + + @Override + public void merge(AbstractDelayTask task) { + if (!(task instanceof FuzzyWatchInitDelayTask)) { + return; + } + FuzzyWatchInitDelayTask oldTask = (FuzzyWatchInitDelayTask) task; + if (!isFinishInit) { + matchedService.addAll(oldTask.getMatchedService()); + } + setLastProcessTime(Math.min(getLastProcessTime(), task.getLastProcessTime())); + Loggers.PUSH.info("[FUZZY-WATCH-INIT-PUSH] Task merge for pattern {}", pattern); + } + + public String getTaskKey() { + return taskKey; + } + + public String getPattern() { + return pattern; + } + + public Collection getMatchedService() { + return matchedService; + } + + public boolean isFinishInit() { + return isFinishInit; + } + + public int getOriginSize() { + return originSize; + } + + public String getClientId() { + return clientId; + } +} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchInitExecuteTask.java b/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchInitExecuteTask.java new file mode 100644 index 00000000000..b4cb47469ea --- /dev/null +++ b/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchInitExecuteTask.java @@ -0,0 +1,209 @@ +/* + * 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.naming.remote.request.FuzzyWatchNotifyInitRequest; +import com.alibaba.nacos.api.naming.utils.NamingUtils; +import com.alibaba.nacos.api.remote.PushCallBack; +import com.alibaba.nacos.common.task.AbstractExecuteTask; +import com.alibaba.nacos.naming.core.v2.client.Client; +import com.alibaba.nacos.naming.core.v2.client.manager.ClientManager; +import com.alibaba.nacos.naming.misc.Loggers; +import com.alibaba.nacos.naming.push.v2.NoRequiredRetryException; +import com.alibaba.nacos.naming.push.v2.PushConfig; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; + +/** + * Nacos naming fuzzy watch initial push execute task. + * + * @author tanyongquan + */ +public class FuzzyWatchInitExecuteTask extends AbstractExecuteTask { + + private final String taskKey; + + private final String clientId; + + private final String pattern; + + private final FuzzyWatchPushDelayTaskEngine delayTaskEngine; + + private final FuzzyWatchInitDelayTask 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; + + private final int latch; + + /** + * TODO set batch size from config. + */ + private final int batchSize = 10; + + private int sendCount; + + private boolean isFinishInitTask; + + private boolean haveFailPush; + + public FuzzyWatchInitExecuteTask(String taskKey, String clientId, String pattern, int originSize, + FuzzyWatchPushDelayTaskEngine delayTaskEngine, FuzzyWatchInitDelayTask delayTask, boolean isFinishInitTask) { + this.taskKey = taskKey; + this.clientId = clientId; + this.pattern = pattern; + this.delayTaskEngine = delayTaskEngine; + this.delayTask = delayTask; + this.originSize = originSize; + this.latch = delayTask.getMatchedService().size(); + this.sendCount = 0; + this.isFinishInitTask = isFinishInitTask; + this.haveFailPush = false; + } + + @Override + public void run() { + ClientManager clientManager = delayTaskEngine.getClientManager(); + Collection> dividedServices = divideServiceByBatch(delayTask.getMatchedService()); + Client client = clientManager.getClient(clientId); + String patternWithoutNameSpace = NamingUtils.getPatternRemovedNamespace(pattern); + String nameSpaceId = NamingUtils.getNamespaceFromPattern(pattern); + if (null == client) { + return; + } + if (!client.isWatchedPattern(pattern)) { + return; + } + if (isFinishInitTask || delayTask.getMatchedService().isEmpty()) { + // do not match any exist service, just finish init + delayTaskEngine.getPushExecutor().doFuzzyWatchNotifyPushWithCallBack(clientId, + FuzzyWatchNotifyInitRequest.buildInitFinishRequest(nameSpaceId, patternWithoutNameSpace), + new FuzzyWatchInitPushCallback(clientId, null, originSize, true, haveFailPush)); + } else { + for (Collection batchData : dividedServices) { + delayTaskEngine.getPushExecutor().doFuzzyWatchNotifyPushWithCallBack(clientId, FuzzyWatchNotifyInitRequest.buildInitRequest( + nameSpaceId, patternWithoutNameSpace, batchData), + new FuzzyWatchInitPushCallback(clientId, batchData, originSize, false, haveFailPush)); + } + } + + } + + private Collection> divideServiceByBatch(Collection matchedService) { + Collection> result = new ArrayList<>(); + if (matchedService.isEmpty()) { + return result; + } + Set currentBatch = new HashSet<>(); + for (String groupedServiceName : matchedService) { + currentBatch.add(groupedServiceName); + if (currentBatch.size() >= this.batchSize) { + result.add(currentBatch); + currentBatch = new HashSet<>(); + } + } + if (!currentBatch.isEmpty()) { + result.add(currentBatch); + } + return result; + } + + private class FuzzyWatchInitPushCallback implements PushCallBack { + + private final String clientId; + + private final Collection groupedServiceName; + + private final int originSize; + + /** + * Record the push task execute start time. + */ + private final long executeStartTime; + + private boolean isFinishInitTask; + + private boolean haveFailPush; + + private FuzzyWatchInitPushCallback(String clientId, Collection groupedServiceName, int originSize, + boolean isFinishInitTask, boolean haveFailPush) { + this.clientId = clientId; + this.groupedServiceName = groupedServiceName; + this.originSize = originSize; + this.executeStartTime = System.currentTimeMillis(); + this.isFinishInitTask = isFinishInitTask; + this.haveFailPush = haveFailPush; + } + + @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-INIT-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 {}, push size {} : {}", + pushCostTimeForNetWork, pushCostTimeForAll, clientId, pattern, groupedServiceName.size(), + groupedServiceName); + sendCount += groupedServiceName.size(); + // this task is an init push task(not finish notify), and with no failure in this task when executing push batched services + if (!haveFailPush && sendCount >= latch) { + delayTaskEngine.addTask(taskKey, new FuzzyWatchInitDelayTask(taskKey, clientId, pattern, null, + originSize, PushConfig.getInstance().getPushTaskDelay(), true)); + } + } + } + + @Override + public void onFail(Throwable e) { + long pushCostTime = System.currentTimeMillis() - executeStartTime; + Loggers.PUSH.error("[FUZZY-WATCH-PUSH-FAIL] {}ms, pattern {} match {} service: {}, reason={}, client={}", pushCostTime, pattern, + groupedServiceName.size(), groupedServiceName, e.getMessage(), clientId); + setHaveFailPush(true); + if (!(e instanceof NoRequiredRetryException)) { + Loggers.PUSH.error("Reason detail: ", e); + if (isFinishInitTask) { + delayTaskEngine.addTask(taskKey, new FuzzyWatchInitDelayTask(taskKey, clientId, pattern, null, + originSize, PushConfig.getInstance().getPushTaskRetryDelay(), true)); + } else { + delayTaskEngine.addTask(taskKey, new FuzzyWatchInitDelayTask(taskKey, clientId, pattern, groupedServiceName, + originSize, PushConfig.getInstance().getPushTaskRetryDelay(), false)); + } + + } + } + } + + private void setHaveFailPush(boolean haveFailPush) { + this.haveFailPush = haveFailPush; + } +} \ No newline at end of file diff --git a/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchNotifyChangeDelayTask.java b/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchNotifyChangeDelayTask.java new file mode 100644 index 00000000000..e6e8a1893b7 --- /dev/null +++ b/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchNotifyChangeDelayTask.java @@ -0,0 +1,90 @@ +/* + * 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.common.task.AbstractDelayTask; +import com.alibaba.nacos.naming.core.v2.pojo.Service; +import com.alibaba.nacos.naming.misc.Loggers; + +import java.util.HashSet; +import java.util.Set; + +/** + * Nacos naming fuzzy watch notify service change push delay task. + * + * @author tanyongquan + */ +public class FuzzyWatchNotifyChangeDelayTask extends AbstractDelayTask { + private final Service service; + + private final String serviceChangedType; + + private boolean pushToAll; + + private Set targetClients; + + public FuzzyWatchNotifyChangeDelayTask(Service service, String serviceChangedType, long delay) { + this.service = service; + this.serviceChangedType = serviceChangedType; + pushToAll = true; + targetClients = null; + setTaskInterval(delay); + setLastProcessTime(System.currentTimeMillis()); + } + + public FuzzyWatchNotifyChangeDelayTask(Service service, String serviceChangedType, long delay, String targetClient) { + this.service = service; + this.serviceChangedType = serviceChangedType; + this.pushToAll = false; + this.targetClients = new HashSet<>(1); + this.targetClients.add(targetClient); + setTaskInterval(delay); + setLastProcessTime(System.currentTimeMillis()); + } + + @Override + public void merge(AbstractDelayTask task) { + if (!(task instanceof FuzzyWatchNotifyChangeDelayTask)) { + return; + } + FuzzyWatchNotifyChangeDelayTask oldTask = (FuzzyWatchNotifyChangeDelayTask) task; + if (isPushToAll() || oldTask.isPushToAll()) { + pushToAll = true; + targetClients = null; + } else { + targetClients.addAll(oldTask.getTargetClients()); + } + setLastProcessTime(Math.min(getLastProcessTime(), task.getLastProcessTime())); + Loggers.PUSH.info("[FUZZY-WATCH-PUSH] Task merge for {}", service); + } + + public Service getService() { + return service; + } + + public boolean isPushToAll() { + return pushToAll; + } + + public String getServiceChangedType() { + return serviceChangedType; + } + + public Set getTargetClients() { + return targetClients; + } +} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchNotifyChangeExecuteTask.java b/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchNotifyChangeExecuteTask.java new file mode 100644 index 00000000000..49e1d7a07d2 --- /dev/null +++ b/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchNotifyChangeExecuteTask.java @@ -0,0 +1,145 @@ +/* + * 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.naming.remote.request.FuzzyWatchNotifyChangeRequest; +import com.alibaba.nacos.api.remote.PushCallBack; +import com.alibaba.nacos.common.task.AbstractExecuteTask; +import com.alibaba.nacos.naming.core.v2.client.Client; +import com.alibaba.nacos.naming.core.v2.client.manager.ClientManager; +import com.alibaba.nacos.naming.core.v2.index.ClientServiceIndexesManager; +import com.alibaba.nacos.naming.core.v2.pojo.Service; +import com.alibaba.nacos.naming.misc.Loggers; +import com.alibaba.nacos.naming.push.v2.NoRequiredRetryException; +import com.alibaba.nacos.naming.push.v2.PushConfig; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; + +/** + * Nacos naming fuzzy watch notify service change push execute task. + * + * @author tanyongquan + */ +public class FuzzyWatchNotifyChangeExecuteTask extends AbstractExecuteTask { + + private final Service service; + + private final FuzzyWatchPushDelayTaskEngine delayTaskEngine; + + private final FuzzyWatchNotifyChangeDelayTask delayTask; + + public FuzzyWatchNotifyChangeExecuteTask(Service service, FuzzyWatchPushDelayTaskEngine delayTaskEngine, + FuzzyWatchNotifyChangeDelayTask delayTask) { + this.service = service; + this.delayTaskEngine = delayTaskEngine; + this.delayTask = delayTask; + } + + @Override + public void run() { + try { + ClientManager clientManager = delayTaskEngine.getClientManager(); + String serviceChangedType = delayTask.getServiceChangedType(); + for (String clientId : getWatchTargetClientIds()) { + Client client = clientManager.getClient(clientId); + if (null == client) { + continue; + } + delayTaskEngine.getPushExecutor().doFuzzyWatchNotifyPushWithCallBack(clientId, new FuzzyWatchNotifyChangeRequest( + service.getNamespace(), service.getName(), service.getGroup(), serviceChangedType), + new WatchNotifyPushCallback(clientId, serviceChangedType)); + } + } catch (Exception e) { + Loggers.PUSH.error("Fuzzy watch notify task for service" + service.getGroupedServiceName() + " execute failed ", e); + delayTaskEngine.addTask(service, new FuzzyWatchNotifyChangeDelayTask(service, delayTask.getServiceChangedType(), 1000L)); + } + } + + /** + * get watch notify client id. + * + * @return A set of ClientID need to be notified + */ + private Set getWatchTargetClientIds() { + if (!delayTask.isPushToAll()) { + return delayTask.getTargetClients(); + } + Set watchNotifyClientIds = new HashSet<>(16); + ClientServiceIndexesManager indexesManager = delayTaskEngine.getIndexesManager(); + // get match result from index + Collection matchedPatterns = indexesManager.getServiceMatchedPatterns(service); + + for (String eachPattern : matchedPatterns) { + // for every matched pattern, get client id which watching this pattern + Collection clientIDs = indexesManager.getAllClientFuzzyWatchedPattern(eachPattern); + if (clientIDs == null || clientIDs.isEmpty()) { + // find there is nobody watch this pattern anymore (lazy remove) + indexesManager.removeWatchPatternMatchIndex(service, eachPattern); + continue; + } + watchNotifyClientIds.addAll(clientIDs); + } + return watchNotifyClientIds; + } + + private class WatchNotifyPushCallback implements PushCallBack { + + private final String clientId; + + private final String serviceChangedType; + + /** + * Record the push task execute start time. + */ + private final long executeStartTime; + + private WatchNotifyPushCallback(String clientId, String serviceChangedType) { + this.clientId = clientId; + this.serviceChangedType = serviceChangedType; + this.executeStartTime = System.currentTimeMillis(); + } + + @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(); + + Loggers.PUSH.info("[WATCH-PUSH-SUCC] {}ms, all delay time {}ms for client {}, service {}, changed type {} ", + pushCostTimeForNetWork, pushCostTimeForAll, clientId, service.getGroupedServiceName(), serviceChangedType); + } + + @Override + public void onFail(Throwable e) { + long pushCostTime = System.currentTimeMillis() - executeStartTime; + Loggers.PUSH.error("[WATCH-PUSH-FAIL] {}ms, service {}, changed type {}, reason={}, client={}", pushCostTime, + service.getGroupedServiceName(), serviceChangedType, e.getMessage(), clientId); + if (!(e instanceof NoRequiredRetryException)) { + Loggers.PUSH.error("Reason detail: ", e); + delayTaskEngine.addTask(service, new FuzzyWatchNotifyChangeDelayTask(service, + serviceChangedType, PushConfig.getInstance().getPushTaskDelay(), clientId)); + } + } + } +} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchPushDelayTaskEngine.java b/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchPushDelayTaskEngine.java new file mode 100644 index 00000000000..dba23e8bab5 --- /dev/null +++ b/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchPushDelayTaskEngine.java @@ -0,0 +1,120 @@ +/* + * 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.common.task.NacosTask; +import com.alibaba.nacos.common.task.NacosTaskProcessor; +import com.alibaba.nacos.common.task.engine.NacosDelayTaskExecuteEngine; +import com.alibaba.nacos.naming.core.v2.client.manager.ClientManager; +import com.alibaba.nacos.naming.core.v2.index.ClientServiceIndexesManager; +import com.alibaba.nacos.naming.core.v2.index.ServiceStorage; +import com.alibaba.nacos.naming.core.v2.metadata.NamingMetadataManager; +import com.alibaba.nacos.naming.core.v2.pojo.Service; +import com.alibaba.nacos.naming.misc.Loggers; +import com.alibaba.nacos.naming.misc.NamingExecuteTaskDispatcher; +import com.alibaba.nacos.naming.misc.SwitchDomain; +import com.alibaba.nacos.naming.push.v2.executor.PushExecutor; + +/** + * Nacos naming fuzzy watch notify service change push delay task execute engine. + * + * @author tanyongquan + */ +public class FuzzyWatchPushDelayTaskEngine extends NacosDelayTaskExecuteEngine { + + private final ClientManager clientManager; + + private final ClientServiceIndexesManager indexesManager; + + private final ServiceStorage serviceStorage; + + private final NamingMetadataManager metadataManager; + + private final PushExecutor pushExecutor; + + private final SwitchDomain switchDomain; + + public FuzzyWatchPushDelayTaskEngine(ClientManager clientManager, ClientServiceIndexesManager indexesManager, + ServiceStorage serviceStorage, NamingMetadataManager metadataManager, + PushExecutor pushExecutor, SwitchDomain switchDomain) { + super(FuzzyWatchPushDelayTaskEngine.class.getSimpleName(), Loggers.PUSH); + this.clientManager = clientManager; + this.indexesManager = indexesManager; + this.serviceStorage = serviceStorage; + this.metadataManager = metadataManager; + this.pushExecutor = pushExecutor; + this.switchDomain = switchDomain; + setDefaultTaskProcessor(new WatchPushDelayTaskProcessor(this)); + } + + public ClientManager getClientManager() { + return clientManager; + } + + public ClientServiceIndexesManager getIndexesManager() { + return indexesManager; + } + + public ServiceStorage getServiceStorage() { + return serviceStorage; + } + + public NamingMetadataManager getMetadataManager() { + return metadataManager; + } + + public PushExecutor getPushExecutor() { + return pushExecutor; + } + + @Override + protected void processTasks() { + if (!switchDomain.isPushEnabled()) { + return; + } + super.processTasks(); + } + + private static class WatchPushDelayTaskProcessor implements NacosTaskProcessor { + + private final FuzzyWatchPushDelayTaskEngine executeEngine; + + public WatchPushDelayTaskProcessor(FuzzyWatchPushDelayTaskEngine executeEngine) { + this.executeEngine = executeEngine; + } + + @Override + public boolean process(NacosTask task) { + if (task instanceof FuzzyWatchNotifyChangeDelayTask) { + FuzzyWatchNotifyChangeDelayTask notifyDelayTask = (FuzzyWatchNotifyChangeDelayTask) task; + Service service = notifyDelayTask.getService(); + NamingExecuteTaskDispatcher.getInstance() + .dispatchAndExecuteTask(service, new FuzzyWatchNotifyChangeExecuteTask(service, executeEngine, notifyDelayTask)); + } else if (task instanceof FuzzyWatchInitDelayTask) { + FuzzyWatchInitDelayTask fuzzyWatchInitDelayTask = (FuzzyWatchInitDelayTask) task; + String pattern = fuzzyWatchInitDelayTask.getPattern(); + String clientId = fuzzyWatchInitDelayTask.getClientId(); + String taskKey = fuzzyWatchInitDelayTask.getTaskKey(); + NamingExecuteTaskDispatcher.getInstance() + .dispatchAndExecuteTask(taskKey, new FuzzyWatchInitExecuteTask(taskKey, clientId, pattern, + fuzzyWatchInitDelayTask.getOriginSize(), executeEngine, fuzzyWatchInitDelayTask, + fuzzyWatchInitDelayTask.isFinishInit())); + } + return true; + } + } +} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/remote/rpc/handler/FuzzyWatchRequestHandler.java b/naming/src/main/java/com/alibaba/nacos/naming/remote/rpc/handler/FuzzyWatchRequestHandler.java new file mode 100644 index 00000000000..bd7f07f0147 --- /dev/null +++ b/naming/src/main/java/com/alibaba/nacos/naming/remote/rpc/handler/FuzzyWatchRequestHandler.java @@ -0,0 +1,63 @@ +/* + * 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.remote.rpc.handler; + +import com.alibaba.nacos.api.exception.NacosException; +import com.alibaba.nacos.api.naming.remote.NamingRemoteConstants; +import com.alibaba.nacos.api.naming.remote.request.FuzzyWatchRequest; +import com.alibaba.nacos.api.naming.remote.response.FuzzyWatchResponse; +import com.alibaba.nacos.api.remote.request.RequestMeta; +import com.alibaba.nacos.auth.annotation.Secured; +import com.alibaba.nacos.core.remote.RequestHandler; +import com.alibaba.nacos.naming.core.v2.service.impl.EphemeralClientOperationServiceImpl; +import com.alibaba.nacos.plugin.auth.constant.ActionTypes; +import org.springframework.stereotype.Component; + +/** + * Fuzzy watch service request handler. + * + * @author tanyongquan + */ +@Component("fuzzyWatchRequestHandler") +public class FuzzyWatchRequestHandler extends RequestHandler { + + private final EphemeralClientOperationServiceImpl clientOperationService; + + public FuzzyWatchRequestHandler(EphemeralClientOperationServiceImpl clientOperationService) { + this.clientOperationService = clientOperationService; + } + + @Override + @Secured(action = ActionTypes.READ) + public FuzzyWatchResponse handle(FuzzyWatchRequest request, RequestMeta meta) throws NacosException { + String serviceNamePattern = request.getServiceName(); + String groupNamePattern = request.getGroupName(); + String namespaceId = request.getNamespace(); + + switch (request.getType()) { + case NamingRemoteConstants.FUZZY_WATCH_SERVICE: + clientOperationService.fuzzyWatch(namespaceId, serviceNamePattern, groupNamePattern, meta.getConnectionId()); + return FuzzyWatchResponse.buildSuccessResponse(NamingRemoteConstants.FUZZY_WATCH_SERVICE); + case NamingRemoteConstants.CANCEL_FUZZY_WATCH_SERVICE: + clientOperationService.cancelFuzzyWatch(namespaceId, serviceNamePattern, groupNamePattern, meta.getConnectionId()); + return FuzzyWatchResponse.buildSuccessResponse(NamingRemoteConstants.CANCEL_FUZZY_WATCH_SERVICE); + default: + throw new NacosException(NacosException.INVALID_PARAM, + String.format("Unsupported request type %s", request.getType())); + } + } +} diff --git a/naming/src/test/java/com/alibaba/nacos/naming/core/v2/index/ClientServiceIndexesManagerTest.java b/naming/src/test/java/com/alibaba/nacos/naming/core/v2/index/ClientServiceIndexesManagerTest.java index 0c9bc8676a4..b83a9ea6604 100644 --- a/naming/src/test/java/com/alibaba/nacos/naming/core/v2/index/ClientServiceIndexesManagerTest.java +++ b/naming/src/test/java/com/alibaba/nacos/naming/core/v2/index/ClientServiceIndexesManagerTest.java @@ -119,8 +119,9 @@ void testRemovePublisherIndexesByEmptyService() throws NoSuchFieldException, Ill void testSubscribeTypes() { List> classes = clientServiceIndexesManager.subscribeTypes(); + assertNotNull(classes); - assertEquals(5, classes.size()); + assertEquals(7, classes.size()); } @Test diff --git a/naming/src/test/java/com/alibaba/nacos/naming/push/v2/NamingSubscriberServiceV2ImplTest.java b/naming/src/test/java/com/alibaba/nacos/naming/push/v2/NamingSubscriberServiceV2ImplTest.java index 84db805b5e5..2ee1f4c0386 100644 --- a/naming/src/test/java/com/alibaba/nacos/naming/push/v2/NamingSubscriberServiceV2ImplTest.java +++ b/naming/src/test/java/com/alibaba/nacos/naming/push/v2/NamingSubscriberServiceV2ImplTest.java @@ -16,6 +16,7 @@ package com.alibaba.nacos.naming.push.v2; +import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.naming.core.v2.client.Client; import com.alibaba.nacos.naming.core.v2.client.manager.ClientManagerDelegate; import com.alibaba.nacos.naming.core.v2.event.service.ServiceEvent; @@ -116,8 +117,8 @@ void testGetFuzzySubscribersByService() { } @Test - void onEvent() { - subscriberService.onEvent(new ServiceEvent.ServiceChangedEvent(service)); + public void onEvent() { + subscriberService.onEvent(new ServiceEvent.ServiceChangedEvent(service, Constants.ServiceChangedType.ADD_SERVICE)); verify(delayTaskEngine).addTask(eq(service), any(PushDelayTask.class)); } } diff --git a/naming/src/test/java/com/alibaba/nacos/naming/push/v2/task/FixturePushExecutor.java b/naming/src/test/java/com/alibaba/nacos/naming/push/v2/task/FixturePushExecutor.java index f26fb516a50..2a28f0196ad 100644 --- a/naming/src/test/java/com/alibaba/nacos/naming/push/v2/task/FixturePushExecutor.java +++ b/naming/src/test/java/com/alibaba/nacos/naming/push/v2/task/FixturePushExecutor.java @@ -16,6 +16,8 @@ package com.alibaba.nacos.naming.push.v2.task; +import com.alibaba.nacos.api.naming.remote.request.AbstractFuzzyWatchNotifyRequest; +import com.alibaba.nacos.api.remote.PushCallBack; import com.alibaba.nacos.naming.pojo.Subscriber; import com.alibaba.nacos.naming.push.v2.PushDataWrapper; import com.alibaba.nacos.naming.push.v2.executor.PushExecutor; @@ -39,6 +41,21 @@ public void doPushWithCallback(String clientId, Subscriber subscriber, PushDataW } } + @Override + public void doWatcherNotifyPush(String clientId, AbstractFuzzyWatchNotifyRequest watchNotifyRequest) { + + } + + @Override + public void doFuzzyWatchNotifyPushWithCallBack(String clientId, AbstractFuzzyWatchNotifyRequest watchNotifyRequest, + PushCallBack callBack) { + if (shouldSuccess) { + callBack.onSuccess(); + } else { + callBack.onFail(failedException); + } + } + public void setShouldSuccess(boolean shouldSuccess) { this.shouldSuccess = shouldSuccess; } From 7c14cbcb5886a97f4a98e47a444027411c1e2b22 Mon Sep 17 00:00:00 2001 From: "zunfei.lzf" Date: Mon, 30 Dec 2024 10:10:54 +0800 Subject: [PATCH 03/15] =?UTF-8?q?=E6=A8=A1=E7=B3=8A=E8=AE=A2=E9=98=85?= =?UTF-8?q?=E4=BB=A3=E7=A0=81=E8=B0=83=E6=95=B4=E7=AE=80=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../alibaba/nacos/api/common/Constants.java | 46 ++-- .../nacos/api/config/ConfigService.java | 165 +++++------ ...r.java => AbstractFuzzyWatchListener.java} | 20 +- ....java => ConfigFuzzyWatchChangeEvent.java} | 31 ++- ...a => AbstractFuzzyWatchNotifyRequest.java} | 6 +- .../ConfigBatchFuzzyListenRequest.java | 233 ---------------- .../request/ConfigBatchFuzzyWatchRequest.java | 154 +++++++++++ ...ava => FuzzyWatchNotifyChangeRequest.java} | 6 +- ....java => FuzzyWatchNotifyDiffRequest.java} | 14 +- ...ava => ConfigBatchFuzzyWatchResponse.java} | 2 +- ...va => FuzzyWatchNotifyChangeResponse.java} | 2 +- ...java => FuzzyWatchNotifyDiffResponse.java} | 2 +- .../request/FuzzyWatchNotifyInitRequest.java | 4 +- .../nacos/api/naming/utils/NamingUtils.java | 120 -------- .../com.alibaba.nacos.api.remote.Payload | 12 +- .../client/config/NacosConfigService.java | 64 ++--- .../client/config/impl/ClientWorker.java | 191 ++++++------- .../config/impl/FuzzyListenContext.java | 132 +++------ .../client/naming/NacosNamingService.java | 10 +- .../cache/FuzzyWatchServiceListHolder.java | 4 +- .../remote/NamingClientProxyDelegate.java | 1 - .../remote/gprc/NamingGrpcClientProxy.java | 7 +- .../client/config/NacosConfigServiceTest.java | 17 +- .../client/naming/NacosNamingServiceTest.java | 1 + .../remote/NamingClientProxyDelegateTest.java | 20 +- .../gprc/NamingGrpcClientProxyTest.java | 4 +- .../gprc/NamingPushRequestHandlerTest.java | 7 +- .../common/utils/FuzzyGroupKeyPattern.java | 141 ++++++++++ .../nacos/common/utils/GroupKeyPattern.java | 259 ------------------ .../utils/FuzzyGroupKeyPatternTest.java | 45 +++ .../common/utils/GroupKeyPatternTest.java | 136 --------- config/pom.xml | 4 + .../configuration/ConfigCommonConfig.java | 2 + .../nacos/config/server/model/CacheItem.java | 16 -- .../event/ConfigBatchFuzzyListenEvent.java | 34 +-- ... ConfigBatchFuzzyWatchRequestHandler.java} | 35 +-- .../remote/ConfigChangeListenContext.java | 107 +------- .../remote/ConfigFuzzyWatchContext.java | 98 +++++++ ...java => ConfigFuzzyWatchDiffNotifier.java} | 78 +++--- .../RpcFuzzyListenConfigChangeNotifier.java | 19 +- .../server/service/ConfigCacheService.java | 35 +-- ...atchFuzzyListenRequestParamsExtractor.java | 10 +- .../nacos/example/FuzzyListenExample.java | 10 +- .../naming/core/v2/client/AbstractClient.java | 12 +- .../nacos/naming/core/v2/client/Client.java | 2 +- .../index/ClientFuzzyWatchIndexesManager.java | 169 ++++++++++++ .../v2/index/ClientServiceIndexesManager.java | 118 +------- .../EphemeralClientOperationServiceImpl.java | 9 +- .../v2/NamingSubscriberServiceV2Impl.java | 5 +- .../FuzzyWatchNotifyChangeExecuteTask.java | 3 +- .../task/FuzzyWatchPushDelayTaskEngine.java | 12 +- ...va => NamingFuzzyWatchRequestHandler.java} | 4 +- .../v2/NamingSubscriberServiceV2ImplTest.java | 6 +- pom.xml | 2 +- 54 files changed, 1092 insertions(+), 1554 deletions(-) rename api/src/main/java/com/alibaba/nacos/api/config/listener/{AbstractFuzzyListenListener.java => AbstractFuzzyWatchListener.java} (82%) rename api/src/main/java/com/alibaba/nacos/api/config/listener/{FuzzyListenConfigChangeEvent.java => ConfigFuzzyWatchChangeEvent.java} (76%) rename api/src/main/java/com/alibaba/nacos/api/config/remote/request/{AbstractFuzzyListenNotifyRequest.java => AbstractFuzzyWatchNotifyRequest.java} (86%) delete mode 100644 api/src/main/java/com/alibaba/nacos/api/config/remote/request/ConfigBatchFuzzyListenRequest.java create mode 100644 api/src/main/java/com/alibaba/nacos/api/config/remote/request/ConfigBatchFuzzyWatchRequest.java rename api/src/main/java/com/alibaba/nacos/api/config/remote/request/{FuzzyListenNotifyChangeRequest.java => FuzzyWatchNotifyChangeRequest.java} (92%) rename api/src/main/java/com/alibaba/nacos/api/config/remote/request/{FuzzyListenNotifyDiffRequest.java => FuzzyWatchNotifyDiffRequest.java} (88%) rename api/src/main/java/com/alibaba/nacos/api/config/remote/response/{ConfigBatchFuzzyListenResponse.java => ConfigBatchFuzzyWatchResponse.java} (92%) rename api/src/main/java/com/alibaba/nacos/api/config/remote/response/{FuzzyListenNotifyChangeResponse.java => FuzzyWatchNotifyChangeResponse.java} (92%) rename api/src/main/java/com/alibaba/nacos/api/config/remote/response/{FuzzyListenNotifyDiffResponse.java => FuzzyWatchNotifyDiffResponse.java} (92%) create mode 100644 common/src/main/java/com/alibaba/nacos/common/utils/FuzzyGroupKeyPattern.java delete mode 100644 common/src/main/java/com/alibaba/nacos/common/utils/GroupKeyPattern.java create mode 100644 common/src/test/java/com/alibaba/nacos/common/utils/FuzzyGroupKeyPatternTest.java delete mode 100644 common/src/test/java/com/alibaba/nacos/common/utils/GroupKeyPatternTest.java rename config/src/main/java/com/alibaba/nacos/config/server/remote/{ConfigBatchFuzzyListenRequestHandler.java => ConfigBatchFuzzyWatchRequestHandler.java} (70%) create mode 100644 config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigFuzzyWatchContext.java rename config/src/main/java/com/alibaba/nacos/config/server/remote/{RpcFuzzyListenConfigDiffNotifier.java => ConfigFuzzyWatchDiffNotifier.java} (85%) create mode 100644 naming/src/main/java/com/alibaba/nacos/naming/core/v2/index/ClientFuzzyWatchIndexesManager.java rename naming/src/main/java/com/alibaba/nacos/naming/remote/rpc/handler/{FuzzyWatchRequestHandler.java => NamingFuzzyWatchRequestHandler.java} (92%) 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 b8c8e3eeaca..0bce5f55fc5 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 @@ -99,10 +99,6 @@ public class Constants { public static final Integer CLUSTER_GRPC_PORT_DEFAULT_OFFSET = 1001; - public static final String NAMESPACE_ID_SPLITTER = ">>"; - - public static final String DATA_ID_SPLITTER = "@@"; - /** * second. */ @@ -194,12 +190,8 @@ public class Constants { public static final int WRITE_REDIRECT_CODE = 307; - public static final String NAMESPACE_ID_SPLITER = ">>"; - public static final String SERVICE_INFO_SPLITER = "@@"; - public static final String MATCH_PATTERN_SPLITER = "##"; - public static final int SERVICE_INFO_SPLIT_COUNT = 2; public static final String NULL_STRING = "null"; @@ -216,8 +208,6 @@ public class Constants { public static final String ALL_PATTERN = "*"; - public static final String FUZZY_WATCH_PATTERN_WILDCARD = "*"; - public static final String COLON = ":"; public static final String LINE_BREAK = "\n"; @@ -241,16 +231,6 @@ public class Constants { public static final int DEFAULT_REDO_THREAD_COUNT = 1; - public static class ConfigChangeType { - - public static final String ADD_CONFIG = "ADD_CONFIG"; - - public static final String DELETE_CONFIG = "DELETE_CONFIG"; - - public static final String FINISH_LISTEN_INIT = "FINISH_LISTEN_INIT"; - - public static final String LISTEN_INIT = "LISTEN_INIT"; - } public static final String APP_CONN_LABELS_KEY = "nacos.app.conn.labels"; public static final String DOT = "."; @@ -296,8 +276,27 @@ public static class Naming { public static final String CMDB_CONTEXT_TYPE = "CMDB"; } + public static final String FUZZY_WATCH_PATTERN_SPLITTER = ">>"; + + public static final String FUZZY_WATCH_INIT_NOTIFY = "FUZZY_WATCH_INIT_NOTIFY"; + + public static final String FINISH_FUZZY_WATCH_INIT_NOTIFY = "FINISH_FUZZY_WATCH_INIT_NOTIFY"; + /** - * The constants in fuzzy watch event type directory. + * The constants in config fuzzy watch event type directory. + */ + public static class ConfigChangedType { + + public static final String ADD_CONFIG = "ADD_CONFIG"; + + public static final String DELETE_CONFIG = "DELETE_CONFIG"; + + public static final String CONFIG_CHANGED = "CONFIG_CHANGED"; + + } + + /** + * The constants in service fuzzy watch event type directory. */ public static class ServiceChangedType { @@ -309,11 +308,6 @@ public static class ServiceChangedType { public static final String HEART_BEAT = "HEART_BEAT"; - public static final String SERVICE_SUBSCRIBED = "SERVICE_SUBSCRIBED"; - - public static final String WATCH_INITIAL_MATCH = "WATCH_INITIAL_MATCH"; - - public static final String FINISH_WATCH_INIT = "FINISH_WATCH_INIT"; } /** diff --git a/api/src/main/java/com/alibaba/nacos/api/config/ConfigService.java b/api/src/main/java/com/alibaba/nacos/api/config/ConfigService.java index a1017860f61..f930331000f 100644 --- a/api/src/main/java/com/alibaba/nacos/api/config/ConfigService.java +++ b/api/src/main/java/com/alibaba/nacos/api/config/ConfigService.java @@ -17,12 +17,12 @@ package com.alibaba.nacos.api.config; import com.alibaba.nacos.api.config.filter.IConfigFilter; -import com.alibaba.nacos.api.config.listener.AbstractFuzzyListenListener; +import com.alibaba.nacos.api.config.listener.AbstractFuzzyWatchListener; import com.alibaba.nacos.api.config.listener.Listener; import com.alibaba.nacos.api.exception.NacosException; import java.util.Collection; -import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Future; /** * Config Service Interface. @@ -63,8 +63,8 @@ String getConfigAndSignListener(String dataId, String group, long timeoutMs, Lis /** * Add a listener to the configuration, after the server modified the configuration, the client will use the * incoming listener callback. Recommended asynchronous processing, the application can implement the getExecutor - * method in the ManagerListener, provide a thread pool of execution. If not provided, use the main thread callback, - * May block other configurations or be blocked by other configurations. + * method in the ManagerListener, provide a thread pool of execution. If not provided, use the main thread callback, May + * block other configurations or be blocked by other configurations. * * @param dataId dataId * @param group group @@ -73,78 +73,6 @@ String getConfigAndSignListener(String dataId, String group, long timeoutMs, Lis */ void addListener(String dataId, String group, Listener listener) throws NacosException; - /** - * Add a fuzzy listener to the configuration. After the server modifies the configuration matching the specified - * fixed group name, the client will utilize the incoming fuzzy listener callback. Fuzzy listeners allow for - * pattern-based subscription to configurations, where the fixed group name represents the group and dataId patterns - * specified for subscription. - * - * @param fixedGroupName The fixed group name representing the group and dataId patterns to subscribe to. - * @param listener The fuzzy listener to be added. - * @throws NacosException NacosException - */ - void addFuzzyListener(String fixedGroupName, AbstractFuzzyListenListener listener) throws NacosException; - - /** - * Add a fuzzy listener to the configuration. After the server modifies the configuration matching the specified - * dataId pattern and fixed group name, the client will utilize the incoming fuzzy listener callback. Fuzzy - * listeners allow for pattern-based subscription to configurations. - * - * @param dataIdPattern The pattern to match dataIds for subscription. - * @param fixedGroupName The fixed group name representing the group and dataId patterns to subscribe to. - * @param listener The fuzzy listener to be added. - * @throws NacosException NacosException - */ - void addFuzzyListener(String dataIdPattern, String fixedGroupName, AbstractFuzzyListenListener listener) - throws NacosException; - - /** - * Add a fuzzy listener to the configuration and retrieve all configs that match the specified fixed group name. - * Fuzzy listeners allow for pattern-based subscription to configs, where the fixed group name represents the group - * and dataId patterns specified for subscription. - * - * @param fixedGroupName The fixed group name representing the group and dataId patterns to subscribe to. - * @param listener The fuzzy listener to be added. - * @return CompletableFuture containing collection of configs that match the specified fixed group name. - * @throws NacosException NacosException - */ - CompletableFuture> addFuzzyListenerAndGetConfigs(String fixedGroupName, - AbstractFuzzyListenListener listener) throws NacosException; - - /** - * Add a fuzzy listener to the configuration and retrieve all configs that match the specified dataId pattern and - * fixed group name. Fuzzy listeners allow for pattern-based subscription to configs. - * - * @param dataIdPattern The pattern to match dataIds for subscription. - * @param fixedGroupName The fixed group name representing the group and dataId patterns to subscribe to. - * @param listener The fuzzy listener to be added. - * @return CompletableFuture containing collection of configs that match the specified dataId pattern and fixed - * group name. - * @throws NacosException NacosException - */ - CompletableFuture> addFuzzyListenerAndGetConfigs(String dataIdPattern, String fixedGroupName, - AbstractFuzzyListenListener listener) throws NacosException; - - /** - * Cancel fuzzy listen and remove the event listener for a specified fixed group name. - * - * @param fixedGroupName The fixed group name for fuzzy watch. - * @param listener The event listener to be removed. - * @throws NacosException If an error occurs during the cancellation process. - */ - void cancelFuzzyListen(String fixedGroupName, AbstractFuzzyListenListener listener) throws NacosException; - - /** - * Cancel fuzzy listen and remove the event listener for a specified service name pattern and fixed group name. - * - * @param dataIdPatter The pattern to match dataId for fuzzy watch. - * @param fixedGroupName The fixed group name for fuzzy watch. - * @param listener The event listener to be removed. - * @throws NacosException If an error occurs during the cancellation process. - */ - void cancelFuzzyListen(String dataIdPatter, String fixedGroupName, AbstractFuzzyListenListener listener) - throws NacosException; - /** * Publish config. * @@ -220,10 +148,10 @@ boolean publishConfigCas(String dataId, String group, String content, String cas * @return whether health */ String getServerStatus(); - + /** - * add config filter. It is recommended to use {@link com.alibaba.nacos.api.config.filter.AbstractConfigFilter} to - * expand the filter. + * add config filter. + * It is recommended to use {@link com.alibaba.nacos.api.config.filter.AbstractConfigFilter} to expand the filter. * * @param configFilter filter * @since 2.3.0 @@ -236,4 +164,83 @@ boolean publishConfigCas(String dataId, String group, String content, String cas * @throws NacosException exception. */ void shutDown() throws NacosException; + + /** + * Add a fuzzy listener to the configuration. After the server modifies the configuration matching the specified + * fixed group name, the client will utilize the incoming fuzzy listener callback. Fuzzy listeners allow for + * pattern-based subscription to configurations, where the fixed group name represents the group and dataId patterns + * specified for subscription. + * + * @param fixedGroupName The fixed group name representing the group and dataId patterns to subscribe to. + * @param listener The fuzzy listener to be added. + * @throws NacosException NacosException + * @since 3.0 + */ + void fuzzyWatch(String fixedGroupName, AbstractFuzzyWatchListener listener) throws NacosException; + + /** + * Add a fuzzy listener to the configuration. After the server modifies the configuration matching the specified + * dataId pattern and fixed group name, the client will utilize the incoming fuzzy listener callback. Fuzzy + * listeners allow for pattern-based subscription to configurations. + * + * @param dataIdPattern The pattern to match dataIds for subscription. + * @param fixedGroupName The fixed group name representing the group and dataId patterns to subscribe to. + * @param listener The fuzzy listener to be added. + * @throws NacosException NacosException + * @since 3.0 + */ + void fuzzyWatch(String dataIdPattern, String fixedGroupName, AbstractFuzzyWatchListener listener) + throws NacosException; + + /** + * Add a fuzzy listener to the configuration and retrieve all configs that match the specified fixed group name. + * Fuzzy listeners allow for pattern-based subscription to configs, where the fixed group name represents the group + * and dataId patterns specified for subscription. + * + * @param fixedGroupName The fixed group name representing the group and dataId patterns to subscribe to. + * @param listener The fuzzy listener to be added. + * @return CompletableFuture containing collection of configs that match the specified fixed group name. + * @throws NacosException NacosException + * @since 3.0 + */ + Future> fuzzyWatchWithGroupKeys(String fixedGroupName, + AbstractFuzzyWatchListener listener) throws NacosException; + + /** + * Add a fuzzy listener to the configuration and retrieve all configs that match the specified dataId pattern and + * fixed group name. Fuzzy listeners allow for pattern-based subscription to configs. + * + * @param dataIdPattern The pattern to match dataIds for subscription. + * @param fixedGroupName The fixed group name representing the group and dataId patterns to subscribe to. + * @param listener The fuzzy listener to be added. + * @return CompletableFuture containing collection of configs that match the specified dataId pattern and fixed + * group name. + * @throws NacosException NacosException + * @since 3.0 + */ + Future> fuzzyWatchWithGroupKeys(String dataIdPattern, String fixedGroupName, + AbstractFuzzyWatchListener listener) throws NacosException; + + /** + * Cancel fuzzy listen and remove the event listener for a specified fixed group name. + * + * @param fixedGroupName The fixed group name for fuzzy watch. + * @param listener The event listener to be removed. + * @throws NacosException If an error occurs during the cancellation process. + * @since 3.0 + */ + void cancelFuzzyListen(String fixedGroupName, AbstractFuzzyWatchListener listener) throws NacosException; + + /** + * Cancel fuzzy listen and remove the event listener for a specified service name pattern and fixed group name. + * + * @param dataIdPatter The pattern to match dataId for fuzzy watch. + * @param fixedGroupName The fixed group name for fuzzy watch. + * @param listener The event listener to be removed. + * @throws NacosException If an error occurs during the cancellation process. + * @since 3.0 + */ + void cancelFuzzyListen(String dataIdPatter, String fixedGroupName, AbstractFuzzyWatchListener listener) + throws NacosException; + } diff --git a/api/src/main/java/com/alibaba/nacos/api/config/listener/AbstractFuzzyListenListener.java b/api/src/main/java/com/alibaba/nacos/api/config/listener/AbstractFuzzyWatchListener.java similarity index 82% rename from api/src/main/java/com/alibaba/nacos/api/config/listener/AbstractFuzzyListenListener.java rename to api/src/main/java/com/alibaba/nacos/api/config/listener/AbstractFuzzyWatchListener.java index bd317884a48..5f8f6c9006d 100644 --- a/api/src/main/java/com/alibaba/nacos/api/config/listener/AbstractFuzzyListenListener.java +++ b/api/src/main/java/com/alibaba/nacos/api/config/listener/AbstractFuzzyWatchListener.java @@ -17,6 +17,7 @@ package com.alibaba.nacos.api.config.listener; import java.util.Objects; +import java.util.UUID; /** * AbstractFuzzyListenListener is an abstract class that provides basic functionality for listening to fuzzy @@ -25,12 +26,12 @@ * @author stone-98 * @date 2024/3/4 */ -public abstract class AbstractFuzzyListenListener extends AbstractListener { +public abstract class AbstractFuzzyWatchListener extends AbstractListener { /** * Unique identifier for the listener. */ - private String uuid; + String uuid= UUID.randomUUID().toString(); /** * Get the UUID (Unique Identifier) of the listener. @@ -41,21 +42,12 @@ public String getUuid() { return uuid; } - /** - * Set the UUID (Unique Identifier) of the listener. - * - * @param uuid The UUID to be set - */ - public void setUuid(String uuid) { - this.uuid = uuid; - } - /** * Callback method invoked when a fuzzy configuration change event occurs. * * @param event The fuzzy configuration change event */ - public abstract void onEvent(FuzzyListenConfigChangeEvent event); + public abstract void onEvent(ConfigFuzzyWatchChangeEvent event); /** * Receive the configuration information. This method is overridden but does nothing in this abstract class. @@ -63,7 +55,7 @@ public void setUuid(String uuid) { * @param configInfo The configuration information */ @Override - public void receiveConfigInfo(String configInfo) { + public final void receiveConfigInfo(String configInfo) { // Do nothing by default } @@ -92,7 +84,7 @@ public boolean equals(Object o) { if (o == null || getClass() != o.getClass()) { return false; } - AbstractFuzzyListenListener that = (AbstractFuzzyListenListener) o; + AbstractFuzzyWatchListener that = (AbstractFuzzyWatchListener) o; return Objects.equals(uuid, that.uuid); } } diff --git a/api/src/main/java/com/alibaba/nacos/api/config/listener/FuzzyListenConfigChangeEvent.java b/api/src/main/java/com/alibaba/nacos/api/config/listener/ConfigFuzzyWatchChangeEvent.java similarity index 76% rename from api/src/main/java/com/alibaba/nacos/api/config/listener/FuzzyListenConfigChangeEvent.java rename to api/src/main/java/com/alibaba/nacos/api/config/listener/ConfigFuzzyWatchChangeEvent.java index 6e2c3e319ca..bf7acfb795b 100644 --- a/api/src/main/java/com/alibaba/nacos/api/config/listener/FuzzyListenConfigChangeEvent.java +++ b/api/src/main/java/com/alibaba/nacos/api/config/listener/ConfigFuzzyWatchChangeEvent.java @@ -16,6 +16,8 @@ package com.alibaba.nacos.api.config.listener; +import com.alibaba.nacos.api.common.Constants; + /** * Represents a fuzzy listening configuration change event. * @@ -24,7 +26,7 @@ * @author stone-98 * @date 2024/3/12 */ -public class FuzzyListenConfigChangeEvent { +public class ConfigFuzzyWatchChangeEvent { /** * The group of the configuration that has changed. @@ -36,15 +38,18 @@ public class FuzzyListenConfigChangeEvent { */ private String dataId; + + private String tenant; /** * The type of change that has occurred (e.g., "ADD_CONFIG", "DELETE_CONFIG"). + * see {@link Constants.ConfigChangedType} */ - private String type; + private String changedType; /** * Constructs an empty FuzzyListenConfigChangeEvent. */ - public FuzzyListenConfigChangeEvent() { + public ConfigFuzzyWatchChangeEvent() { } /** @@ -52,12 +57,12 @@ public FuzzyListenConfigChangeEvent() { * * @param group The group of the configuration that has changed * @param dataId The data ID of the configuration that has changed - * @param type The type of change that has occurred + * @param changedType The type of change that has occurred */ - public FuzzyListenConfigChangeEvent(String group, String dataId, String type) { + public ConfigFuzzyWatchChangeEvent(String group, String dataId, String changedType) { this.group = group; this.dataId = dataId; - this.type = type; + this.changedType = changedType; } /** @@ -68,8 +73,8 @@ public FuzzyListenConfigChangeEvent(String group, String dataId, String type) { * @param type The type of change that has occurred * @return A new FuzzyListenConfigChangeEvent instance */ - public static FuzzyListenConfigChangeEvent build(String group, String dataId, String type) { - return new FuzzyListenConfigChangeEvent(group, dataId, type); + public static ConfigFuzzyWatchChangeEvent build(String group, String dataId, String type) { + return new ConfigFuzzyWatchChangeEvent(group, dataId, type); } public String getGroup() { @@ -88,12 +93,12 @@ public void setDataId(String dataId) { this.dataId = dataId; } - public String getType() { - return type; + public String getChangedType() { + return changedType; } - public void setType(String type) { - this.type = type; + public void setChangedType(String changedType) { + this.changedType = changedType; } /** @@ -104,6 +109,6 @@ public void setType(String type) { @Override public String toString() { return "FuzzyListenConfigChangeEvent{" + "group='" + group + '\'' + ", dataId='" + dataId + '\'' + ", type='" - + type + '\'' + '}'; + + changedType + '\'' + '}'; } } diff --git a/api/src/main/java/com/alibaba/nacos/api/config/remote/request/AbstractFuzzyListenNotifyRequest.java b/api/src/main/java/com/alibaba/nacos/api/config/remote/request/AbstractFuzzyWatchNotifyRequest.java similarity index 86% rename from api/src/main/java/com/alibaba/nacos/api/config/remote/request/AbstractFuzzyListenNotifyRequest.java rename to api/src/main/java/com/alibaba/nacos/api/config/remote/request/AbstractFuzzyWatchNotifyRequest.java index 1ba2b7bda14..f83c83406da 100644 --- a/api/src/main/java/com/alibaba/nacos/api/config/remote/request/AbstractFuzzyListenNotifyRequest.java +++ b/api/src/main/java/com/alibaba/nacos/api/config/remote/request/AbstractFuzzyWatchNotifyRequest.java @@ -26,14 +26,14 @@ * @author stone-98 * @date 2024/3/14 */ -public abstract class AbstractFuzzyListenNotifyRequest extends ServerRequest { +public abstract class AbstractFuzzyWatchNotifyRequest extends ServerRequest { private String serviceChangedType; - public AbstractFuzzyListenNotifyRequest() { + public AbstractFuzzyWatchNotifyRequest() { } - public AbstractFuzzyListenNotifyRequest(String serviceChangedType) { + public AbstractFuzzyWatchNotifyRequest(String serviceChangedType) { this.serviceChangedType = serviceChangedType; } diff --git a/api/src/main/java/com/alibaba/nacos/api/config/remote/request/ConfigBatchFuzzyListenRequest.java b/api/src/main/java/com/alibaba/nacos/api/config/remote/request/ConfigBatchFuzzyListenRequest.java deleted file mode 100644 index fd5ce81e1cb..00000000000 --- a/api/src/main/java/com/alibaba/nacos/api/config/remote/request/ConfigBatchFuzzyListenRequest.java +++ /dev/null @@ -1,233 +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.api.config.remote.request; - -import com.alibaba.nacos.api.common.Constants; -import com.alibaba.nacos.api.remote.request.Request; -import com.alibaba.nacos.api.utils.StringUtils; - -import java.util.HashSet; -import java.util.Objects; -import java.util.Set; - -/** - * Represents a request for batch fuzzy listening configurations. - * - *

This request is used to request batch fuzzy listening configurations from the server. It contains a set of - * contexts, each representing a fuzzy listening context. - * - * @author stone-98 - * @date 2024/3/4 - */ -public class ConfigBatchFuzzyListenRequest extends Request { - - /** - * Set of fuzzy listening contexts. - */ - private Set contexts = new HashSet<>(); - - /** - * Constructs an empty ConfigBatchFuzzyListenRequest. - */ - public ConfigBatchFuzzyListenRequest() { - } - - /** - * Adds a new context to the request. - * - * @param tenant The namespace or tenant associated with the configurations - * @param group The group associated with the configurations - * @param dataIdPattern The pattern for matching data IDs - * @param dataIds Set of data IDs - * @param listen Flag indicating whether to listen for changes - * @param isInitializing Flag indicating whether the client is initializing - */ - public void addContext(String tenant, String group, String dataIdPattern, Set dataIds, boolean listen, - boolean isInitializing) { - contexts.add( - new Context(StringUtils.isEmpty(tenant) ? Constants.DEFAULT_NAMESPACE_ID : tenant, group, dataIdPattern, - dataIds, listen, isInitializing)); - } - - /** - * Get the set of fuzzy listening contexts. - * - * @return The set of contexts - */ - public Set getContexts() { - return contexts; - } - - /** - * Set the set of fuzzy listening contexts. - * - * @param contexts The set of contexts to be set - */ - public void setContexts(Set contexts) { - this.contexts = contexts; - } - - /** - * Get the module name for this request. - * - * @return The module name - */ - @Override - public String getModule() { - return Constants.Config.CONFIG_MODULE; - } - - /** - * Represents a fuzzy listening context. - */ - public static class Context { - - /** - * The namespace or tenant associated with the configurations. - */ - private String tenant; - - /** - * The group associated with the configurations. - */ - private String group; - - /** - * The pattern for matching data IDs. - */ - private String dataIdPattern; - - /** - * Set of data IDs. - */ - private Set dataIds; - - /** - * Flag indicating whether to listen for changes. - */ - private boolean listen; - - /** - * Flag indicating whether the client is initializing. - */ - private boolean isInitializing; - - /** - * Constructs an empty Context. - */ - public Context() { - } - - /** - * Constructs a Context with the specified parameters. - * - * @param tenant The namespace or tenant associated with the configurations - * @param group The group associated with the configurations - * @param dataIdPattern The pattern for matching data IDs - * @param dataIds Set of data IDs - * @param listen Flag indicating whether to listen for changes - * @param isInitializing Flag indicating whether the client is initializing - */ - public Context(String tenant, String group, String dataIdPattern, Set dataIds, boolean listen, - boolean isInitializing) { - this.tenant = tenant; - this.group = group; - this.dataIdPattern = dataIdPattern; - this.dataIds = dataIds; - this.listen = listen; - this.isInitializing = isInitializing; - } - - public String getTenant() { - return tenant; - } - - public void setTenant(String tenant) { - this.tenant = tenant; - } - - public String getGroup() { - return group; - } - - public void setGroup(String group) { - this.group = group; - } - - public String getDataIdPattern() { - return dataIdPattern; - } - - public void setDataIdPattern(String dataIdPattern) { - this.dataIdPattern = dataIdPattern; - } - - public Set getDataIds() { - return dataIds; - } - - public void setDataIds(Set dataIds) { - this.dataIds = dataIds; - } - - public boolean isListen() { - return listen; - } - - public void setListen(boolean listen) { - this.listen = listen; - } - - public boolean isInitializing() { - return isInitializing; - } - - public void setInitializing(boolean initializing) { - isInitializing = initializing; - } - - /** - * Indicates whether some other object is "equal to" this one. - * - * @param o The reference object with which to compare - * @return True if this object is the same as the obj argument, false otherwise - */ - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - Context that = (Context) o; - return Objects.equals(tenant, that.tenant) && Objects.equals(group, that.group) && Objects.equals( - dataIdPattern, that.dataIdPattern) && Objects.equals(dataIds, that.dataIds) && Objects.equals( - listen, that.listen) && Objects.equals(isInitializing, that.isInitializing); - } - - /** - * Returns a hash code value for the object. - * - * @return A hash code value for this object - */ - @Override - public int hashCode() { - return Objects.hash(tenant, group, dataIdPattern, dataIds, listen, isInitializing); - } - } -} diff --git a/api/src/main/java/com/alibaba/nacos/api/config/remote/request/ConfigBatchFuzzyWatchRequest.java b/api/src/main/java/com/alibaba/nacos/api/config/remote/request/ConfigBatchFuzzyWatchRequest.java new file mode 100644 index 00000000000..8bb5f35f990 --- /dev/null +++ b/api/src/main/java/com/alibaba/nacos/api/config/remote/request/ConfigBatchFuzzyWatchRequest.java @@ -0,0 +1,154 @@ +/* + * 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.remote.request; + +import com.alibaba.nacos.api.common.Constants; +import com.alibaba.nacos.api.remote.request.Request; + +import java.util.HashSet; +import java.util.Set; + +/** + * Represents a request for batch fuzzy listening configurations. + * + *

This request is used to request batch fuzzy listening configurations from the server. It contains a set of + * contexts, each representing a fuzzy listening context. + * + * @author stone-98 + * @date 2024/3/4 + */ +public class ConfigBatchFuzzyWatchRequest extends Request { + + /** + * Set of fuzzy listening contexts. + */ + private Set contexts = new HashSet<>(); + + /** + * Constructs an empty ConfigBatchFuzzyListenRequest. + */ + public ConfigBatchFuzzyWatchRequest() { + } + + /** + * Adds a new context to the request. + * + + * @param listen Flag indicating whether to listen for changes + * @param isInitializing Flag indicating whether the client is initializing + */ + public void addContext(String groupKeyPattern, Set receivedGroupKeys, boolean listen, + boolean isInitializing) { + Context context = new Context(); + context.setGroupKeyPattern(groupKeyPattern); + context.setReceivedGroupKeys(receivedGroupKeys); + context.listen=listen; + context.isInitializing=isInitializing; + contexts.add(context); + } + + /** + * Get the set of fuzzy listening contexts. + * + * @return The set of contexts + */ + public Set getContexts() { + return contexts; + } + + /** + * Set the set of fuzzy listening contexts. + * + * @param contexts The set of contexts to be set + */ + public void setContexts(Set contexts) { + this.contexts = contexts; + } + + /** + * Get the module name for this request. + * + * @return The module name + */ + @Override + public String getModule() { + return Constants.Config.CONFIG_MODULE; + } + + /** + * Represents a fuzzy listening context. + */ + public static class Context { + + /** + * The namespace or tenant associated with the configurations. + */ + private String groupKeyPattern; + + + private Set receivedGroupKeys; + + + /** + * Flag indicating whether to listen for changes. + */ + private boolean listen; + + /** + * Flag indicating whether the client is initializing. + */ + private boolean isInitializing; + + /** + * Constructs an empty Context. + */ + public Context() { + } + + public boolean isListen() { + return listen; + } + + public void setListen(boolean listen) { + this.listen = listen; + } + + public boolean isInitializing() { + return isInitializing; + } + + public void setInitializing(boolean initializing) { + isInitializing = initializing; + } + + public String getGroupKeyPattern() { + return groupKeyPattern; + } + + public void setGroupKeyPattern(String groupKeyPattern) { + this.groupKeyPattern = groupKeyPattern; + } + + public Set getReceivedGroupKeys() { + return receivedGroupKeys; + } + + public void setReceivedGroupKeys(Set receivedGroupKeys) { + this.receivedGroupKeys = receivedGroupKeys; + } + } +} diff --git a/api/src/main/java/com/alibaba/nacos/api/config/remote/request/FuzzyListenNotifyChangeRequest.java b/api/src/main/java/com/alibaba/nacos/api/config/remote/request/FuzzyWatchNotifyChangeRequest.java similarity index 92% rename from api/src/main/java/com/alibaba/nacos/api/config/remote/request/FuzzyListenNotifyChangeRequest.java rename to api/src/main/java/com/alibaba/nacos/api/config/remote/request/FuzzyWatchNotifyChangeRequest.java index b149d70689e..4397cbd5b08 100644 --- a/api/src/main/java/com/alibaba/nacos/api/config/remote/request/FuzzyListenNotifyChangeRequest.java +++ b/api/src/main/java/com/alibaba/nacos/api/config/remote/request/FuzzyWatchNotifyChangeRequest.java @@ -24,7 +24,7 @@ * @author stone-98 * @date 2024/3/13 */ -public class FuzzyListenNotifyChangeRequest extends AbstractFuzzyListenNotifyRequest { +public class FuzzyWatchNotifyChangeRequest extends AbstractFuzzyWatchNotifyRequest { /** * The tenant of the configuration that has changed. @@ -49,7 +49,7 @@ public class FuzzyListenNotifyChangeRequest extends AbstractFuzzyListenNotifyReq /** * Constructs an empty FuzzyListenNotifyChangeRequest. */ - public FuzzyListenNotifyChangeRequest() { + public FuzzyWatchNotifyChangeRequest() { } /** @@ -60,7 +60,7 @@ public FuzzyListenNotifyChangeRequest() { * @param dataId The data ID of the configuration that has changed * @param isExist Indicates whether the configuration exists or not */ - public FuzzyListenNotifyChangeRequest(String tenant, String group, String dataId, boolean isExist) { + public FuzzyWatchNotifyChangeRequest(String tenant, String group, String dataId, boolean isExist) { this.tenant = tenant; this.group = group; this.dataId = dataId; diff --git a/api/src/main/java/com/alibaba/nacos/api/config/remote/request/FuzzyListenNotifyDiffRequest.java b/api/src/main/java/com/alibaba/nacos/api/config/remote/request/FuzzyWatchNotifyDiffRequest.java similarity index 88% rename from api/src/main/java/com/alibaba/nacos/api/config/remote/request/FuzzyListenNotifyDiffRequest.java rename to api/src/main/java/com/alibaba/nacos/api/config/remote/request/FuzzyWatchNotifyDiffRequest.java index 0c925a3dad9..02e8598a35a 100644 --- a/api/src/main/java/com/alibaba/nacos/api/config/remote/request/FuzzyListenNotifyDiffRequest.java +++ b/api/src/main/java/com/alibaba/nacos/api/config/remote/request/FuzzyWatchNotifyDiffRequest.java @@ -31,7 +31,7 @@ * @author stone-98 * @date 2024/3/6 */ -public class FuzzyListenNotifyDiffRequest extends AbstractFuzzyListenNotifyRequest { +public class FuzzyWatchNotifyDiffRequest extends AbstractFuzzyWatchNotifyRequest { /** * The pattern used to match group keys for the configurations. @@ -46,7 +46,7 @@ public class FuzzyListenNotifyDiffRequest extends AbstractFuzzyListenNotifyReque /** * Constructs an empty FuzzyListenNotifyDiffRequest. */ - public FuzzyListenNotifyDiffRequest() { + public FuzzyWatchNotifyDiffRequest() { } /** @@ -56,7 +56,7 @@ public FuzzyListenNotifyDiffRequest() { * @param groupKeyPattern The pattern used to match group keys for the configurations * @param contexts The set of contexts containing information about the configurations */ - public FuzzyListenNotifyDiffRequest(String serviceChangedType, String groupKeyPattern, Set contexts) { + public FuzzyWatchNotifyDiffRequest(String serviceChangedType, String groupKeyPattern, Set contexts) { super(serviceChangedType); this.groupKeyPattern = groupKeyPattern; this.contexts = contexts; @@ -69,8 +69,8 @@ public FuzzyListenNotifyDiffRequest(String serviceChangedType, String groupKeyPa * @param groupKeyPattern The pattern used to match group keys for the configurations * @return An initial FuzzyListenNotifyDiffRequest */ - public static FuzzyListenNotifyDiffRequest buildInitRequest(Set contexts, String groupKeyPattern) { - return new FuzzyListenNotifyDiffRequest(Constants.ConfigChangeType.LISTEN_INIT, groupKeyPattern, contexts); + public static FuzzyWatchNotifyDiffRequest buildInitRequest(Set contexts, String groupKeyPattern) { + return new FuzzyWatchNotifyDiffRequest(Constants.FUZZY_WATCH_INIT_NOTIFY, groupKeyPattern, contexts); } /** @@ -79,8 +79,8 @@ public static FuzzyListenNotifyDiffRequest buildInitRequest(Set context * @param groupKeyPattern The pattern used to match group keys for the configurations * @return A final FuzzyListenNotifyDiffRequest */ - public static FuzzyListenNotifyDiffRequest buildInitFinishRequest(String groupKeyPattern) { - return new FuzzyListenNotifyDiffRequest(Constants.ConfigChangeType.FINISH_LISTEN_INIT, groupKeyPattern, + public static FuzzyWatchNotifyDiffRequest buildInitFinishRequest(String groupKeyPattern) { + return new FuzzyWatchNotifyDiffRequest(Constants.FINISH_FUZZY_WATCH_INIT_NOTIFY, groupKeyPattern, new HashSet<>()); } diff --git a/api/src/main/java/com/alibaba/nacos/api/config/remote/response/ConfigBatchFuzzyListenResponse.java b/api/src/main/java/com/alibaba/nacos/api/config/remote/response/ConfigBatchFuzzyWatchResponse.java similarity index 92% rename from api/src/main/java/com/alibaba/nacos/api/config/remote/response/ConfigBatchFuzzyListenResponse.java rename to api/src/main/java/com/alibaba/nacos/api/config/remote/response/ConfigBatchFuzzyWatchResponse.java index f1eca4df7c5..43106d5291d 100644 --- a/api/src/main/java/com/alibaba/nacos/api/config/remote/response/ConfigBatchFuzzyListenResponse.java +++ b/api/src/main/java/com/alibaba/nacos/api/config/remote/response/ConfigBatchFuzzyWatchResponse.java @@ -24,6 +24,6 @@ * @author stone-98 * @date 2024/3/4 */ -public class ConfigBatchFuzzyListenResponse extends Response { +public class ConfigBatchFuzzyWatchResponse extends Response { } diff --git a/api/src/main/java/com/alibaba/nacos/api/config/remote/response/FuzzyListenNotifyChangeResponse.java b/api/src/main/java/com/alibaba/nacos/api/config/remote/response/FuzzyWatchNotifyChangeResponse.java similarity index 92% rename from api/src/main/java/com/alibaba/nacos/api/config/remote/response/FuzzyListenNotifyChangeResponse.java rename to api/src/main/java/com/alibaba/nacos/api/config/remote/response/FuzzyWatchNotifyChangeResponse.java index 1e58ec3f7dd..6ad06ef0f9b 100644 --- a/api/src/main/java/com/alibaba/nacos/api/config/remote/response/FuzzyListenNotifyChangeResponse.java +++ b/api/src/main/java/com/alibaba/nacos/api/config/remote/response/FuzzyWatchNotifyChangeResponse.java @@ -24,6 +24,6 @@ * @author stone-98 * @date 2024/3/18 */ -public class FuzzyListenNotifyChangeResponse extends Response { +public class FuzzyWatchNotifyChangeResponse extends Response { } diff --git a/api/src/main/java/com/alibaba/nacos/api/config/remote/response/FuzzyListenNotifyDiffResponse.java b/api/src/main/java/com/alibaba/nacos/api/config/remote/response/FuzzyWatchNotifyDiffResponse.java similarity index 92% rename from api/src/main/java/com/alibaba/nacos/api/config/remote/response/FuzzyListenNotifyDiffResponse.java rename to api/src/main/java/com/alibaba/nacos/api/config/remote/response/FuzzyWatchNotifyDiffResponse.java index 672d8dfc800..bf332b53156 100644 --- a/api/src/main/java/com/alibaba/nacos/api/config/remote/response/FuzzyListenNotifyDiffResponse.java +++ b/api/src/main/java/com/alibaba/nacos/api/config/remote/response/FuzzyWatchNotifyDiffResponse.java @@ -24,6 +24,6 @@ * @author stone-98 * @date 2024/3/18 */ -public class FuzzyListenNotifyDiffResponse extends Response { +public class FuzzyWatchNotifyDiffResponse extends Response { } diff --git a/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/FuzzyWatchNotifyInitRequest.java b/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/FuzzyWatchNotifyInitRequest.java index 0983df802d7..7a1aa020d65 100644 --- a/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/FuzzyWatchNotifyInitRequest.java +++ b/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/FuzzyWatchNotifyInitRequest.java @@ -42,11 +42,11 @@ private FuzzyWatchNotifyInitRequest(String namespace, String pattern, String ser } public static FuzzyWatchNotifyInitRequest buildInitRequest(String namespace, String pattern, Collection servicesName) { - return new FuzzyWatchNotifyInitRequest(namespace, pattern, Constants.ServiceChangedType.WATCH_INITIAL_MATCH, servicesName); + return new FuzzyWatchNotifyInitRequest(namespace, pattern, Constants.FUZZY_WATCH_INIT_NOTIFY, servicesName); } public static FuzzyWatchNotifyInitRequest buildInitFinishRequest(String namespace, String pattern) { - return new FuzzyWatchNotifyInitRequest(namespace, pattern, Constants.ServiceChangedType.FINISH_WATCH_INIT, new HashSet<>(1)); + return new FuzzyWatchNotifyInitRequest(namespace, pattern, Constants.FINISH_FUZZY_WATCH_INIT_NOTIFY, new HashSet<>(1)); } public String getPattern() { diff --git a/api/src/main/java/com/alibaba/nacos/api/naming/utils/NamingUtils.java b/api/src/main/java/com/alibaba/nacos/api/naming/utils/NamingUtils.java index 69176918c7d..074c41749e2 100644 --- a/api/src/main/java/com/alibaba/nacos/api/naming/utils/NamingUtils.java +++ b/api/src/main/java/com/alibaba/nacos/api/naming/utils/NamingUtils.java @@ -23,14 +23,12 @@ import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.api.utils.StringUtils; -import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.regex.Pattern; import static com.alibaba.nacos.api.common.Constants.CLUSTER_NAME_PATTERN_STRING; -import static com.alibaba.nacos.api.common.Constants.FUZZY_WATCH_PATTERN_WILDCARD; import static com.alibaba.nacos.api.common.Constants.NUMBER_PATTERN_STRING; /** @@ -191,124 +189,6 @@ public static void batchCheckInstanceIsLegal(List instances) throws Na } } - public static String getPatternWithNamespace(final String namespaceId, final String groupedPattern) { - if (StringUtils.isBlank(namespaceId)) { - throw new IllegalArgumentException("Param 'namespaceId' is illegal, namespaceId is blank"); - } - if (StringUtils.isBlank(groupedPattern)) { - throw new IllegalArgumentException("Param 'groupedPattern' is illegal, groupedPattern is blank"); - } - final String resultGroupedPattern = namespaceId + Constants.NAMESPACE_ID_SPLITER + groupedPattern; - return resultGroupedPattern.intern(); - } - - public static String getNamespaceFromPattern(String completedPattern) { - if (StringUtils.isBlank(completedPattern)) { - return StringUtils.EMPTY; - } - if (!completedPattern.contains(Constants.NAMESPACE_ID_SPLITER)) { - return Constants.DEFAULT_NAMESPACE_ID; - } - return completedPattern.split(Constants.NAMESPACE_ID_SPLITER)[0]; - } - - public static String getPatternRemovedNamespace(String completedPattern) { - if (StringUtils.isBlank(completedPattern)) { - return StringUtils.EMPTY; - } - if (!completedPattern.contains(Constants.NAMESPACE_ID_SPLITER)) { - return completedPattern; - } - return completedPattern.split(Constants.NAMESPACE_ID_SPLITER)[1]; - } - - /** - * Get the pattern watched under given namespace id. - * - * @param namespaceId name space id - * @param completedPatterns a set of all watched pattern(with namespace id) - * @return filtered pattern set - */ - public static Set filterPatternWithNamespace(String namespaceId, Set completedPatterns) { - Set patternsOfGivenNamespace = new HashSet<>(); - for (String each : completedPatterns) { - String nameSpaceOfPattern = getNamespaceFromPattern(each); - if (namespaceId.equals(nameSpaceOfPattern)) { - patternsOfGivenNamespace.add(getPatternRemovedNamespace(each)); - } - } - return patternsOfGivenNamespace; - } - - /** - * Given a service, and a list of watched patterns, return the patterns that the service can match. - * - * @param serviceName service Name - * @param groupName group Name - * @param watchPattern a list of completed watch patterns - * @return the patterns list that the service can match. - */ - public static Set getServiceMatchedPatterns(String serviceName, String groupName, Collection watchPattern) { - if (watchPattern == null || watchPattern.isEmpty()) { - return new HashSet<>(1); - } - Set matchedPatternList = new HashSet<>(); - for (String eachPattern : watchPattern) { - if (isMatchPattern(serviceName, groupName, getServiceName(eachPattern), getGroupName(eachPattern))) { - matchedPatternList.add(eachPattern); - } - } - return matchedPatternList; - } - - /** - * Given a list of service's name, and a pattern to watch, return the services that can match the pattern. - * - * @param servicesList a list of service's name - * @param serviceNamePattern service name Pattern - * @param groupNamePattern group name Pattern - * @return the patterns list that the service can match. - */ - public static Set getPatternMatchedServices(Collection servicesList, String serviceNamePattern, - String groupNamePattern) { - if (servicesList == null || servicesList.isEmpty()) { - return new HashSet<>(1); - } - Set matchList = new HashSet<>(); - for (String eachService : servicesList) { - if (isMatchPattern(getServiceName(eachService), getGroupName(eachService), serviceNamePattern, groupNamePattern)) { - matchList.add(eachService); - } - } - return matchList; - } - - /** - * Given a service name and a pattern to match, determine whether it can match. - * TODO:If want to add a matching method, can implement in here. - * - * @param serviceName service name to judge - * @param groupName group name to judge - * @param serviceNamePattern service name Pattern - * @param groupNamePattern group name Pattern - * @return matching result - */ - public static boolean isMatchPattern(String serviceName, String groupName, String serviceNamePattern, String groupNamePattern) { - // Only support prefix match or all match service name right now - // Only support fixed group name right now - if (serviceNamePattern.equals(FUZZY_WATCH_PATTERN_WILDCARD)) { - return groupName.equals(groupNamePattern); - } else if (serviceNamePattern.endsWith(FUZZY_WATCH_PATTERN_WILDCARD)) { - String serviceMatchName = serviceNamePattern.substring(0, serviceNamePattern.length() - 1); - return prefixMatchWithFixedGroupName(serviceName, serviceMatchName, groupName, groupNamePattern); - } - return false; - } - - private static boolean prefixMatchWithFixedGroupName(String serviceName, String serviceNamePrefix, String groupName, String fixedGroupName) { - return groupName.equals(fixedGroupName) && serviceName.startsWith(serviceNamePrefix); - } - /** * Check string is a number or not. * diff --git a/api/src/main/resources/META-INF/services/com.alibaba.nacos.api.remote.Payload b/api/src/main/resources/META-INF/services/com.alibaba.nacos.api.remote.Payload index 3b4927ec774..773d048c14e 100644 --- a/api/src/main/resources/META-INF/services/com.alibaba.nacos.api.remote.Payload +++ b/api/src/main/resources/META-INF/services/com.alibaba.nacos.api.remote.Payload @@ -63,9 +63,9 @@ com.alibaba.nacos.api.naming.remote.response.QueryServiceResponse com.alibaba.nacos.api.naming.remote.response.ServiceListResponse com.alibaba.nacos.api.naming.remote.response.SubscribeServiceResponse com.alibaba.nacos.api.naming.remote.response.FuzzyWatchResponse -com.alibaba.nacos.api.config.remote.request.ConfigBatchFuzzyListenRequest -com.alibaba.nacos.api.config.remote.response.ConfigBatchFuzzyListenResponse -com.alibaba.nacos.api.config.remote.request.FuzzyListenNotifyChangeRequest -com.alibaba.nacos.api.config.remote.response.FuzzyListenNotifyChangeResponse -com.alibaba.nacos.api.config.remote.request.FuzzyListenNotifyDiffRequest -com.alibaba.nacos.api.config.remote.response.FuzzyListenNotifyDiffResponse +com.alibaba.nacos.api.config.remote.request.ConfigBatchFuzzyWatchRequest +com.alibaba.nacos.api.config.remote.response.ConfigBatchFuzzyWatchResponse +com.alibaba.nacos.api.config.remote.request.FuzzyWatchNotifyChangeRequest +com.alibaba.nacos.api.config.remote.response.FuzzyWatchNotifyChangeResponse +com.alibaba.nacos.api.config.remote.request.FuzzyWatchNotifyDiffRequest +com.alibaba.nacos.api.config.remote.response.FuzzyWatchNotifyDiffResponse diff --git a/client/src/main/java/com/alibaba/nacos/client/config/NacosConfigService.java b/client/src/main/java/com/alibaba/nacos/client/config/NacosConfigService.java index 7e921e45ed7..b811753a9ae 100644 --- a/client/src/main/java/com/alibaba/nacos/client/config/NacosConfigService.java +++ b/client/src/main/java/com/alibaba/nacos/client/config/NacosConfigService.java @@ -21,7 +21,7 @@ import com.alibaba.nacos.api.config.ConfigService; import com.alibaba.nacos.api.config.ConfigType; import com.alibaba.nacos.api.config.filter.IConfigFilter; -import com.alibaba.nacos.api.config.listener.AbstractFuzzyListenListener; +import com.alibaba.nacos.api.config.listener.AbstractFuzzyWatchListener; import com.alibaba.nacos.api.config.listener.Listener; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.client.config.filter.impl.ConfigFilterChainManager; @@ -47,8 +47,9 @@ import java.util.Properties; import java.util.UUID; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Future; -import static com.alibaba.nacos.api.common.Constants.FUZZY_LISTEN_PATTERN_WILDCARD; +import static com.alibaba.nacos.api.common.Constants.ANY_PATTERN; /** * Config Impl. @@ -133,46 +134,36 @@ public void addListener(String dataId, String group, Listener listener) throws N } @Override - public void addFuzzyListener(String fixedGroupName, AbstractFuzzyListenListener listener) throws NacosException { - doFuzzyListen(FUZZY_LISTEN_PATTERN_WILDCARD, fixedGroupName, listener); + public void fuzzyWatch(String fixedGroupName, AbstractFuzzyWatchListener listener) throws NacosException { + doFuzzyListen(ANY_PATTERN, fixedGroupName, listener); } @Override - public void addFuzzyListener(String dataIdPattern, String fixedGroupName, AbstractFuzzyListenListener listener) + public void fuzzyWatch(String dataIdPattern, String fixedGroupName, AbstractFuzzyWatchListener listener) throws NacosException { - // only support prefix match right now - if (!dataIdPattern.endsWith(FUZZY_LISTEN_PATTERN_WILDCARD)) { - if (dataIdPattern.startsWith(FUZZY_LISTEN_PATTERN_WILDCARD)) { - throw new UnsupportedOperationException("Suffix matching for dataId is not supported yet." - + " It will be supported in future updates if needed."); - } else { - throw new UnsupportedOperationException( - "Illegal dataId pattern, please read the documentation and pass a valid pattern."); - } - } doFuzzyListen(dataIdPattern, fixedGroupName, listener); } @Override - public CompletableFuture> addFuzzyListenerAndGetConfigs(String fixedGroupName, - AbstractFuzzyListenListener listener) throws NacosException { - return doAddFuzzyListenerAndGetConfigs(FUZZY_LISTEN_PATTERN_WILDCARD, fixedGroupName, listener); + public Future> fuzzyWatchWithGroupKeys(String fixedGroupName, + AbstractFuzzyWatchListener listener) throws NacosException { + return doAddFuzzyListenerAndGetConfigs(ANY_PATTERN, fixedGroupName, listener); } @Override - public CompletableFuture> addFuzzyListenerAndGetConfigs(String dataIdPattern, - String fixedGroupName, AbstractFuzzyListenListener listener) throws NacosException { + public Future> fuzzyWatchWithGroupKeys(String dataIdPattern, + String fixedGroupName, AbstractFuzzyWatchListener listener) throws NacosException { return doAddFuzzyListenerAndGetConfigs(dataIdPattern, fixedGroupName, listener); } private CompletableFuture> doAddFuzzyListenerAndGetConfigs(String dataIdPattern, - String fixedGroupName, AbstractFuzzyListenListener listener) throws NacosException { + String fixedGroupName, AbstractFuzzyWatchListener listener) throws NacosException { CompletableFuture> future = new CompletableFuture<>(); if (listener == null) { future.completeExceptionally(new IllegalArgumentException("Listener cannot be null")); return future; } - addFuzzyListener(dataIdPattern, fixedGroupName, listener); + fuzzyWatch(dataIdPattern, fixedGroupName, listener); FuzzyListenContext context = worker.getFuzzyListenContext(dataIdPattern, fixedGroupName); if (context == null) { future.complete(Collections.emptyList()); @@ -181,12 +172,11 @@ private CompletableFuture> doAddFuzzyListenerAndGetConfigs(St return context.waitForInitializationComplete(future); } - private void doFuzzyListen(String dataIdPattern, String fixedGroupName, AbstractFuzzyListenListener listener) + private void doFuzzyListen(String dataIdPattern, String fixedGroupName, AbstractFuzzyWatchListener listener) throws NacosException { if (listener == null) { return; } - listener.setUuid(UUID.randomUUID().toString()); if (!worker.containsPatternMatchCache(dataIdPattern, fixedGroupName)) { worker.addTenantFuzzyListenListens(dataIdPattern, fixedGroupName, Collections.singletonList(listener)); } else { @@ -195,18 +185,18 @@ private void doFuzzyListen(String dataIdPattern, String fixedGroupName, Abstract } @Override - public void cancelFuzzyListen(String fixedGroupName, AbstractFuzzyListenListener listener) throws NacosException { - cancelFuzzyListen(FUZZY_LISTEN_PATTERN_WILDCARD, fixedGroupName, listener); + public void cancelFuzzyListen(String fixedGroupName, AbstractFuzzyWatchListener listener) throws NacosException { + cancelFuzzyListen(ANY_PATTERN, fixedGroupName, listener); } @Override - public void cancelFuzzyListen(String dataIdPattern, String fixedGroupName, AbstractFuzzyListenListener listener) + public void cancelFuzzyListen(String dataIdPattern, String fixedGroupName, AbstractFuzzyWatchListener listener) throws NacosException { doCancelFuzzyListen(dataIdPattern, fixedGroupName, listener); } private void doCancelFuzzyListen(String dataIdPattern, String groupNamePattern, - AbstractFuzzyListenListener listener) throws NacosException { + AbstractFuzzyWatchListener listener) throws NacosException { if (null == listener) { return; } @@ -264,8 +254,8 @@ private String getConfigInner(String tenant, String dataId, String group, long t LOGGER.warn("[{}] [get-config] get failover ok, dataId={}, group={}, tenant={}", worker.getAgentName(), dataId, group, tenant); cr.setContent(content); - String encryptedDataKey = LocalEncryptedDataKeyProcessor.getEncryptDataKeyFailover(agent.getName(), dataId, - group, tenant); + String encryptedDataKey = LocalEncryptedDataKeyProcessor + .getEncryptDataKeyFailover(agent.getName(), dataId, group, tenant); cr.setEncryptedDataKey(encryptedDataKey); configFilterChainManager.doFilter(null, cr); content = cr.getContent(); @@ -287,15 +277,15 @@ private String getConfigInner(String tenant, String dataId, String group, long t LOGGER.warn("[{}] [get-config] get from server error, dataId={}, group={}, tenant={}, msg={}", worker.getAgentName(), dataId, group, tenant, ioe.toString()); } - + content = LocalConfigInfoProcessor.getSnapshot(worker.getAgentName(), dataId, group, tenant); if (content != null) { LOGGER.warn("[{}] [get-config] get snapshot ok, dataId={}, group={}, tenant={}", worker.getAgentName(), dataId, group, tenant); } cr.setContent(content); - String encryptedDataKey = LocalEncryptedDataKeyProcessor.getEncryptDataKeySnapshot(agent.getName(), dataId, - group, tenant); + String encryptedDataKey = LocalEncryptedDataKeyProcessor + .getEncryptDataKeySnapshot(agent.getName(), dataId, group, tenant); cr.setEncryptedDataKey(encryptedDataKey); configFilterChainManager.doFilter(null, cr); content = cr.getContent(); @@ -327,8 +317,8 @@ private boolean publishConfigInner(String tenant, String dataId, String group, S content = cr.getContent(); String encryptedDataKey = cr.getEncryptedDataKey(); - return worker.publishConfig(dataId, group, tenant, appName, tag, betaIps, content, encryptedDataKey, casMd5, - type); + return worker + .publishConfig(dataId, group, tenant, appName, tag, betaIps, content, encryptedDataKey, casMd5, type); } @Override @@ -339,12 +329,12 @@ public String getServerStatus() { return DOWN; } } - + @Override public void addConfigFilter(IConfigFilter configFilter) { configFilterChainManager.addFilter(configFilter); } - + @Override public void shutDown() throws NacosException { worker.shutdown(); 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 b2ab88f7774..05bc5e22fbd 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 @@ -19,26 +19,26 @@ import com.alibaba.nacos.api.PropertyKeyConst; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.config.ConfigType; -import com.alibaba.nacos.api.config.listener.AbstractFuzzyListenListener; +import com.alibaba.nacos.api.config.listener.AbstractFuzzyWatchListener; import com.alibaba.nacos.api.config.listener.Listener; import com.alibaba.nacos.api.config.remote.request.ClientConfigMetricRequest; -import com.alibaba.nacos.api.config.remote.request.ConfigBatchFuzzyListenRequest; +import com.alibaba.nacos.api.config.remote.request.ConfigBatchFuzzyWatchRequest; import com.alibaba.nacos.api.config.remote.request.ConfigBatchListenRequest; import com.alibaba.nacos.api.config.remote.request.ConfigChangeNotifyRequest; import com.alibaba.nacos.api.config.remote.request.ConfigPublishRequest; import com.alibaba.nacos.api.config.remote.request.ConfigQueryRequest; import com.alibaba.nacos.api.config.remote.request.ConfigRemoveRequest; -import com.alibaba.nacos.api.config.remote.request.FuzzyListenNotifyChangeRequest; -import com.alibaba.nacos.api.config.remote.request.FuzzyListenNotifyDiffRequest; +import com.alibaba.nacos.api.config.remote.request.FuzzyWatchNotifyChangeRequest; +import com.alibaba.nacos.api.config.remote.request.FuzzyWatchNotifyDiffRequest; import com.alibaba.nacos.api.config.remote.response.ClientConfigMetricResponse; -import com.alibaba.nacos.api.config.remote.response.ConfigBatchFuzzyListenResponse; +import com.alibaba.nacos.api.config.remote.response.ConfigBatchFuzzyWatchResponse; import com.alibaba.nacos.api.config.remote.response.ConfigChangeBatchListenResponse; import com.alibaba.nacos.api.config.remote.response.ConfigChangeNotifyResponse; import com.alibaba.nacos.api.config.remote.response.ConfigPublishResponse; import com.alibaba.nacos.api.config.remote.response.ConfigQueryResponse; import com.alibaba.nacos.api.config.remote.response.ConfigRemoveResponse; -import com.alibaba.nacos.api.config.remote.response.FuzzyListenNotifyChangeResponse; -import com.alibaba.nacos.api.config.remote.response.FuzzyListenNotifyDiffResponse; +import com.alibaba.nacos.api.config.remote.response.FuzzyWatchNotifyChangeResponse; +import com.alibaba.nacos.api.config.remote.response.FuzzyWatchNotifyDiffResponse; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.remote.RemoteConstants; import com.alibaba.nacos.api.remote.request.Request; @@ -72,7 +72,7 @@ import com.alibaba.nacos.common.remote.client.ServerListFactory; import com.alibaba.nacos.common.utils.ConnLabelsUtils; import com.alibaba.nacos.common.utils.ConvertUtils; -import com.alibaba.nacos.common.utils.GroupKeyPattern; +import com.alibaba.nacos.common.utils.FuzzyGroupKeyPattern; import com.alibaba.nacos.common.utils.JacksonUtils; import com.alibaba.nacos.common.utils.MD5Utils; import com.alibaba.nacos.common.utils.StringUtils; @@ -176,7 +176,7 @@ public class ClientWorker implements Closeable { private final List taskIdContextCountList = new ArrayList<>(); @SuppressWarnings("PMD.ThreadPoolCreationRule") - public ClientWorker(final ConfigFilterChainManager configFilterChainManager, ServerListManager serverListManager, + public ClientWorker(final ConfigFilterChainManager configFilterChainManager, ConfigServerListManager serverListManager, final NacosClientProperties properties) throws NacosException { this.configFilterChainManager = configFilterChainManager; @@ -194,16 +194,15 @@ public ClientWorker(final ConfigFilterChainManager configFilterChainManager, Ser * Adds a list of fuzzy listen listeners for the specified data ID pattern and group. * * @param dataIdPattern The pattern of the data ID to listen for. - * @param group The group of the configuration. + * @param groupPattern The group of the configuration. * @param listeners The list of listeners to add. * @throws NacosException If an error occurs while adding the listeners. */ - public void addTenantFuzzyListenListens(String dataIdPattern, String group, - List listeners) throws NacosException { - group = blank2defaultGroup(group); - FuzzyListenContext context = addFuzzyListenContextIfAbsent(dataIdPattern, group); + public void addTenantFuzzyListenListens(String dataIdPattern, String groupPattern, + List listeners) throws NacosException { + FuzzyListenContext context = addFuzzyListenContextIfAbsent(dataIdPattern, groupPattern); synchronized (context) { - for (AbstractFuzzyListenListener listener : listeners) { + for (AbstractFuzzyWatchListener listener : listeners) { context.addListener(listener); } context.setInitializing(true); @@ -213,6 +212,30 @@ public void addTenantFuzzyListenListens(String dataIdPattern, String group, } } + /** + * Initializes a duplicate fuzzy listen for the specified data ID pattern, group, and listener. + * + * @param dataIdPattern The pattern of the data ID to listen for. + * @param group The group of the configuration. + * @param listener The listener to add. + */ + public void duplicateFuzzyListenInit(String dataIdPattern, String group, AbstractFuzzyWatchListener listener) { + String groupKeyPattern = FuzzyGroupKeyPattern.generateFuzzyWatchGroupKeyPattern(dataIdPattern, group,agent.getTenant()); + Map contextMap = fuzzyListenContextMap.get(); + FuzzyListenContext context = contextMap.get(groupKeyPattern); + if (Objects.isNull(context)) { + return; + } + synchronized (context) { + context.addListener(listener); + + for (String dataId : context.getReceivedGroupKeys()) { + NotifyCenter.publishEvent(FuzzyListenNotifyEvent.buildNotifyPatternSpecificListenerEvent(group, dataId, + Constants.ConfigChangedType.ADD_CONFIG, groupKeyPattern, listener.getUuid())); + } + } + } + /** * Add listeners for data. * @@ -343,30 +366,6 @@ public void removeTenantListener(String dataId, String group, Listener listener) } } - /** - * Initializes a duplicate fuzzy listen for the specified data ID pattern, group, and listener. - * - * @param dataIdPattern The pattern of the data ID to listen for. - * @param group The group of the configuration. - * @param listener The listener to add. - */ - public void duplicateFuzzyListenInit(String dataIdPattern, String group, AbstractFuzzyListenListener listener) { - String groupKeyPattern = GroupKeyPattern.generateFuzzyListenGroupKeyPattern(dataIdPattern, group); - Map contextMap = fuzzyListenContextMap.get(); - FuzzyListenContext context = contextMap.get(groupKeyPattern); - if (Objects.isNull(context)) { - return; - } - synchronized (context) { - context.addListener(listener); - - for (String dataId : context.getDataIds()) { - NotifyCenter.publishEvent(FuzzyListenNotifyEvent.buildNotifyPatternSpecificListenerEvent(group, dataId, - Constants.ConfigChangeType.ADD_CONFIG, groupKeyPattern, listener.getUuid())); - } - } - } - /** * Removes a fuzzy listen listener for the specified data ID pattern, group, and listener. * @@ -375,7 +374,7 @@ public void duplicateFuzzyListenInit(String dataIdPattern, String group, Abstrac * @param listener The listener to remove. * @throws NacosException If an error occurs while removing the listener. */ - public void removeFuzzyListenListener(String dataIdPattern, String group, AbstractFuzzyListenListener listener) + public void removeFuzzyListenListener(String dataIdPattern, String group, AbstractFuzzyWatchListener listener) throws NacosException { group = blank2defaultGroup(group); FuzzyListenContext fuzzyListenContext = getFuzzyListenContext(dataIdPattern, group); @@ -394,11 +393,9 @@ public void removeFuzzyListenListener(String dataIdPattern, String group, Abstra /** * Removes the fuzzy listen context for the specified data ID pattern and group. * - * @param dataIdPattern The pattern of the data ID. - * @param group The group of the configuration. + * @param groupKeyPattern The pattern of the data ID. */ - public void removeFuzzyListenContext(String dataIdPattern, String group) { - String groupKeyPattern = GroupKeyPattern.generateFuzzyListenGroupKeyPattern(dataIdPattern, group); + public void removeFuzzyListenContext(String groupKeyPattern) { synchronized (fuzzyListenContextMap) { Map copy = new HashMap<>(fuzzyListenContextMap.get()); FuzzyListenContext removedContext = copy.remove(groupKeyPattern); @@ -408,7 +405,6 @@ public void removeFuzzyListenContext(String dataIdPattern, String group) { fuzzyListenContextMap.set(copy); } LOGGER.info("[{}] [fuzzy-listen-unsubscribe] {}", agent.getName(), groupKeyPattern); - // TODO: Record metric for fuzzy listen unsubscribe. } /** @@ -576,21 +572,21 @@ private void putCache(String key, CacheData cache) { * context already exists, returns the existing context. * * @param dataIdPattern The pattern of the data ID. - * @param group The group of the configuration. + * @param groupPattern The group of the configuration. * @return The fuzzy listen context for the specified data ID pattern and group. */ - public FuzzyListenContext addFuzzyListenContextIfAbsent(String dataIdPattern, String group) { - FuzzyListenContext context = getFuzzyListenContext(dataIdPattern, group); + public FuzzyListenContext addFuzzyListenContextIfAbsent(String dataIdPattern, String groupPattern) { + FuzzyListenContext context = getFuzzyListenContext(dataIdPattern, groupPattern); if (context != null) { return context; } synchronized (fuzzyListenContextMap) { - FuzzyListenContext contextFromMap = getFuzzyListenContext(dataIdPattern, group); + FuzzyListenContext contextFromMap = getFuzzyListenContext(dataIdPattern, groupPattern); if (contextFromMap != null) { context = contextFromMap; context.getIsConsistentWithServer().set(false); } else { - context = new FuzzyListenContext(agent.getName(), dataIdPattern, group); + context = new FuzzyListenContext(agent.getName(),FuzzyGroupKeyPattern.generateFuzzyWatchGroupKeyPattern(dataIdPattern, group,agent.getTenant())); int taskId = calculateContextTaskId(); increaseContextTaskIdCount(taskId); context.setTaskId(taskId); @@ -598,7 +594,7 @@ public FuzzyListenContext addFuzzyListenContextIfAbsent(String dataIdPattern, St } Map copy = new HashMap<>(fuzzyListenContextMap.get()); - String groupKeyPattern = GroupKeyPattern.generateFuzzyListenGroupKeyPattern(dataIdPattern, group); + String groupKeyPattern = FuzzyGroupKeyPattern.generateFuzzyWatchGroupKeyPattern(dataIdPattern, group,agent.getTenant()); copy.put(groupKeyPattern, context); fuzzyListenContextMap.set(copy); @@ -718,7 +714,7 @@ private int calculateId(List countList, long perTaskSize) { */ public FuzzyListenContext getFuzzyListenContext(String dataIdPattern, String group) { return fuzzyListenContextMap.get() - .get(GroupKeyPattern.generateFuzzyListenGroupKeyPattern(dataIdPattern, group)); + .get(FuzzyGroupKeyPattern.generateFuzzyWatchGroupKeyPattern(dataIdPattern, group,agent.getTenant())); } public ConfigResponse getServerConfig(String dataId, String group, String tenant, long readTimeout, boolean notify) @@ -737,29 +733,15 @@ private String blank2defaultGroup(String group) { * Checks if the pattern match cache contains an entry for the specified data ID pattern and group. * * @param dataIdPattern The data ID pattern. - * @param group The group name. + * @param groupPattern The group name. * @return True if the cache contains an entry, false otherwise. */ - public boolean containsPatternMatchCache(String dataIdPattern, String group) { + public boolean containsPatternMatchCache(String dataIdPattern, String groupPattern) { Map contextMap = fuzzyListenContextMap.get(); - String groupKeyPattern = GroupKeyPattern.generateFuzzyListenGroupKeyPattern(dataIdPattern, group); + String groupKeyPattern = FuzzyGroupKeyPattern.generateFuzzyWatchGroupKeyPattern(dataIdPattern, groupPattern,agent.getTenant()); return contextMap.containsKey(groupKeyPattern); } - @SuppressWarnings("PMD.ThreadPoolCreationRule") - public ClientWorker(final ConfigFilterChainManager configFilterChainManager, ConfigServerListManager serverListManager, - final NacosClientProperties properties) throws NacosException { - this.configFilterChainManager = configFilterChainManager; - - init(properties); - - agent = new ConfigRpcTransportClient(properties, serverListManager); - ScheduledExecutorService executorService = Executors.newScheduledThreadPool(initWorkerThreadCount(properties), - new NameThreadFactory("com.alibaba.nacos.client.Worker")); - agent.setExecutor(executorService); - agent.start(); - - } void initAppLabels(Properties properties) { this.appLables = ConnLabelsUtils.addPrefixForEachKey(defaultLabelsCollectorManager.getLabels(properties), @@ -957,33 +939,33 @@ private Map getLabels() { * * @param request The fuzzy listen init notify request to handle. * @param clientName The name of the client sending the request. - * @return A {@link FuzzyListenNotifyDiffResponse} indicating the result of handling the request. + * @return A {@link FuzzyWatchNotifyDiffResponse} indicating the result of handling the request. */ - private FuzzyListenNotifyDiffResponse handleFuzzyListenNotifyDiffRequest(FuzzyListenNotifyDiffRequest request, + private FuzzyWatchNotifyDiffResponse handleFuzzyListenNotifyDiffRequest(FuzzyWatchNotifyDiffRequest request, String clientName) { LOGGER.info("[{}] [fuzzy-listen-config-push] config init.", clientName); String groupKeyPattern = request.getGroupKeyPattern(); FuzzyListenContext context = fuzzyListenContextMap.get().get(groupKeyPattern); - if (Constants.ConfigChangeType.FINISH_LISTEN_INIT.equals(request.getServiceChangedType())) { + if (Constants.FINISH_FUZZY_WATCH_INIT_NOTIFY.equals(request.getServiceChangedType())) { context.markInitializationComplete(); - return new FuzzyListenNotifyDiffResponse(); + return new FuzzyWatchNotifyDiffResponse(); } - for (FuzzyListenNotifyDiffRequest.Context requestContext : request.getContexts()) { - Set existsDataIds = context.getDataIds(); + for (FuzzyWatchNotifyDiffRequest.Context requestContext : request.getContexts()) { + Set matchedGroupKeys = context.getReceivedGroupKeys(); switch (requestContext.getType()) { - case Constants.ConfigChangeType.LISTEN_INIT: - case Constants.ConfigChangeType.ADD_CONFIG: - if (existsDataIds.add(requestContext.getDataId())) { + case Constants.FUZZY_WATCH_INIT_NOTIFY: + case Constants.ConfigChangedType.ADD_CONFIG: + if (matchedGroupKeys.add(GroupKey.getKeyTenant(requestContext.getDataId(),requestContext.getGroup(),requestContext.getTenant()))) { NotifyCenter.publishEvent(FuzzyListenNotifyEvent.buildNotifyPatternAllListenersEvent( requestContext.getGroup(), requestContext.getDataId(), request.getGroupKeyPattern(), - Constants.ConfigChangeType.ADD_CONFIG)); + Constants.ConfigChangedType.ADD_CONFIG)); } break; - case Constants.ConfigChangeType.DELETE_CONFIG: - if (existsDataIds.remove(requestContext.getDataId())) { + case Constants.ConfigChangedType.DELETE_CONFIG: + if (matchedGroupKeys.remove(GroupKey.getKeyTenant(requestContext.getDataId(),requestContext.getGroup(),requestContext.getTenant()))) { NotifyCenter.publishEvent(FuzzyListenNotifyEvent.buildNotifyPatternAllListenersEvent( requestContext.getGroup(), requestContext.getDataId(), request.getGroupKeyPattern(), - Constants.ConfigChangeType.DELETE_CONFIG)); + Constants.ConfigChangedType.DELETE_CONFIG)); } break; default: @@ -991,7 +973,7 @@ private FuzzyListenNotifyDiffResponse handleFuzzyListenNotifyDiffRequest(FuzzyLi break; } } - return new FuzzyListenNotifyDiffResponse(); + return new FuzzyWatchNotifyDiffResponse(); } /** @@ -1003,29 +985,29 @@ private FuzzyListenNotifyDiffResponse handleFuzzyListenNotifyDiffRequest(FuzzyLi * @param request The fuzzy listen notify change request to handle. * @param clientName The name of the client sending the request. */ - private FuzzyListenNotifyChangeResponse handlerFuzzyListenNotifyChangeRequest( - FuzzyListenNotifyChangeRequest request, String clientName) { + private FuzzyWatchNotifyChangeResponse handlerFuzzyListenNotifyChangeRequest( + FuzzyWatchNotifyChangeRequest request, String clientName) { LOGGER.info("[{}] [fuzzy-listen-config-push] config changed.", clientName); Map listenContextMap = fuzzyListenContextMap.get(); - Set matchedPatterns = GroupKeyPattern.getConfigMatchedPatternsWithoutNamespace(request.getDataId(), - request.getGroup(), listenContextMap.keySet()); + Set matchedPatterns = FuzzyGroupKeyPattern.filterMatchedPatterns(listenContextMap.keySet(),request.getDataId(), + request.getGroup(), request.getTenant()); for (String matchedPattern : matchedPatterns) { FuzzyListenContext context = listenContextMap.get(matchedPattern); if (request.isExist()) { - if (context.getDataIds().add(request.getDataId())) { + if (context.getReceivedGroupKeys().add(GroupKey.getKeyTenant(request.getDataId(),request.getGroup(),request.getTenant()))) { NotifyCenter.publishEvent( FuzzyListenNotifyEvent.buildNotifyPatternAllListenersEvent(request.getGroup(), - request.getDataId(), matchedPattern, Constants.ConfigChangeType.ADD_CONFIG)); + request.getDataId(), matchedPattern, Constants.ConfigChangedType.ADD_CONFIG)); } } else { - if (context.getDataIds().remove(request.getDataId())) { + if (context.getReceivedGroupKeys().remove(GroupKey.getKeyTenant(request.getDataId(),request.getGroup(),request.getTenant()))) { NotifyCenter.publishEvent( FuzzyListenNotifyEvent.buildNotifyPatternAllListenersEvent(request.getGroup(), - request.getDataId(), matchedPattern, Constants.ConfigChangeType.DELETE_CONFIG)); + request.getDataId(), matchedPattern, Constants.ConfigChangedType.DELETE_CONFIG)); } } } - return new FuzzyListenNotifyChangeResponse(); + return new FuzzyWatchNotifyChangeResponse(); } ConfigChangeNotifyResponse handleConfigChangeNotifyRequest(ConfigChangeNotifyRequest configChangeNotifyRequest, @@ -1063,12 +1045,12 @@ private void initRpcClientHandler(final RpcClient rpcClientInner) { return handleConfigChangeNotifyRequest((ConfigChangeNotifyRequest) request, rpcClientInner.getName()); } - if (request instanceof FuzzyListenNotifyDiffRequest) { - return handleFuzzyListenNotifyDiffRequest((FuzzyListenNotifyDiffRequest) request, + if (request instanceof FuzzyWatchNotifyDiffRequest) { + return handleFuzzyListenNotifyDiffRequest((FuzzyWatchNotifyDiffRequest) request, rpcClientInner.getName()); } - if (request instanceof FuzzyListenNotifyChangeRequest) { - return handlerFuzzyListenNotifyChangeRequest((FuzzyListenNotifyChangeRequest) request, + if (request instanceof FuzzyWatchNotifyChangeRequest) { + return handlerFuzzyListenNotifyChangeRequest((FuzzyWatchNotifyChangeRequest) request, rpcClientInner.getName()); } return null; @@ -1165,7 +1147,7 @@ public void onEvent(Event event) { if (context == null) { return; } - context.notifyListener(fuzzyListenNotifyEvent.getDataId(), fuzzyListenNotifyEvent.getType(), + context.notifyListener(fuzzyListenNotifyEvent.getDataId(),fuzzyListenNotifyEvent.getGroup(),agent.getTenant(), fuzzyListenNotifyEvent.getType(), fuzzyListenNotifyEvent.getUuid()); } @@ -1360,17 +1342,16 @@ private void doExecuteConfigFuzzyListen(Map> co ExecutorService executorService = ensureSyncExecutor(fuzzyListenerTaskPrefix, taskId); // Submit task for execution Future future = executorService.submit(() -> { - ConfigBatchFuzzyListenRequest configBatchFuzzyListenRequest = buildFuzzyListenConfigRequest( + ConfigBatchFuzzyWatchRequest configBatchFuzzyWatchRequest = buildFuzzyListenConfigRequest( contexts); try { // Execute the fuzzy listen operation - ConfigBatchFuzzyListenResponse listenResponse = (ConfigBatchFuzzyListenResponse) requestProxy( - rpcClient, configBatchFuzzyListenRequest); + ConfigBatchFuzzyWatchResponse listenResponse = (ConfigBatchFuzzyWatchResponse) requestProxy( + rpcClient, configBatchFuzzyWatchRequest); if (listenResponse != null && listenResponse.isSuccess()) { for (FuzzyListenContext context : contexts) { if (context.isDiscard()) { - ClientWorker.this.removeFuzzyListenContext(context.getDataIdPattern(), - context.getGroup()); + ClientWorker.this.removeFuzzyListenContext(context.getGroupKeyPattern()); } else { context.getIsConsistentWithServer().set(true); } @@ -1697,10 +1678,10 @@ private ConfigBatchListenRequest buildConfigRequest(List caches) { * @param contexts The list of fuzzy listen contexts. * @return A {@code ConfigBatchFuzzyListenRequest} object representing the request. */ - private ConfigBatchFuzzyListenRequest buildFuzzyListenConfigRequest(List contexts) { - ConfigBatchFuzzyListenRequest request = new ConfigBatchFuzzyListenRequest(); + private ConfigBatchFuzzyWatchRequest buildFuzzyListenConfigRequest(List contexts) { + ConfigBatchFuzzyWatchRequest request = new ConfigBatchFuzzyWatchRequest(); for (FuzzyListenContext context : contexts) { - request.addContext(getTenant(), context.getGroup(), context.getDataIdPattern(), context.getDataIds(), + request.addContext(context.getGroupKeyPattern(), context.getReceivedGroupKeys(), !context.isDiscard(), context.isInitializing()); } return request; diff --git a/client/src/main/java/com/alibaba/nacos/client/config/impl/FuzzyListenContext.java b/client/src/main/java/com/alibaba/nacos/client/config/impl/FuzzyListenContext.java index 93ddbcfbb7a..0c9a9c59acf 100644 --- a/client/src/main/java/com/alibaba/nacos/client/config/impl/FuzzyListenContext.java +++ b/client/src/main/java/com/alibaba/nacos/client/config/impl/FuzzyListenContext.java @@ -16,8 +16,8 @@ package com.alibaba.nacos.client.config.impl; -import com.alibaba.nacos.api.config.listener.AbstractFuzzyListenListener; -import com.alibaba.nacos.api.config.listener.FuzzyListenConfigChangeEvent; +import com.alibaba.nacos.api.config.listener.AbstractFuzzyWatchListener; +import com.alibaba.nacos.api.config.listener.ConfigFuzzyWatchChangeEvent; import com.alibaba.nacos.client.utils.LogUtils; import com.alibaba.nacos.common.utils.ConcurrentHashSet; import com.alibaba.nacos.common.utils.StringUtils; @@ -60,20 +60,12 @@ public class FuzzyListenContext { */ private int taskId; - /** - * Data ID pattern. - */ - private String dataIdPattern; - - /** - * Group name. - */ - private String group; + private String groupKeyPattern; /** - * Tenant name. + * Set of data IDs associated with the context. */ - private String tenant; + private Set receivedGroupKeys = new ConcurrentHashSet<>(); /** * Flag indicating whether the context is consistent with the server. @@ -99,28 +91,22 @@ public class FuzzyListenContext { * Flag indicating whether the context is discarded. */ private volatile boolean isDiscard = false; - - /** - * Set of data IDs associated with the context. - */ - private Set dataIds = new ConcurrentHashSet<>(); - + /** * Set of listeners associated with the context. */ - private Set listeners = new HashSet<>(); + private Set listeners = new HashSet<>(); /** * Constructor with environment name, data ID pattern, and group. * * @param envName Environment name - * @param dataIdPattern Data ID pattern - * @param group Group name + * @param groupKeyPattern groupKeyPattern */ - public FuzzyListenContext(String envName, String dataIdPattern, String group) { + public FuzzyListenContext(String envName, String groupKeyPattern) { this.envName = envName; - this.dataIdPattern = dataIdPattern; - this.group = group; + this.groupKeyPattern=groupKeyPattern; + } /** @@ -129,12 +115,12 @@ public FuzzyListenContext(String envName, String dataIdPattern, String group) { * @param uuid UUID to filter listeners * @return Set of listeners to notify */ - public Set calculateListenersToNotify(String uuid) { - Set listenersToNotify = new HashSet<>(); + public Set calculateListenersToNotify(String uuid) { + Set listenersToNotify = new HashSet<>(); if (StringUtils.isEmpty(uuid)) { listenersToNotify = listeners; } else { - for (AbstractFuzzyListenListener listener : listeners) { + for (AbstractFuzzyWatchListener listener : listeners) { if (uuid.equals(listener.getUuid())) { listenersToNotify.add(listener); } @@ -150,9 +136,9 @@ public Set calculateListenersToNotify(String uuid) * @param type Type of the event * @param uuid UUID to filter listeners */ - public void notifyListener(final String dataId, final String type, final String uuid) { - Set listenersToNotify = calculateListenersToNotify(uuid); - doNotifyListener(dataId, type, listenersToNotify); + public void notifyListener(final String dataId,final String group,String tenant, final String type, final String uuid) { + Set listenersToNotify = calculateListenersToNotify(uuid); + doNotifyListener(dataId,group,tenant, type, listenersToNotify); } /** @@ -162,14 +148,14 @@ public void notifyListener(final String dataId, final String type, final String * @param type Type of the event * @param listenersToNotify Set of listeners to notify */ - private void doNotifyListener(final String dataId, final String type, - Set listenersToNotify) { - for (AbstractFuzzyListenListener listener : listenersToNotify) { + private void doNotifyListener(final String dataId, final String group,String tenant,final String type, + Set listenersToNotify) { + for (AbstractFuzzyWatchListener listener : listenersToNotify) { AbstractFuzzyNotifyTask job = new AbstractFuzzyNotifyTask() { @Override public void run() { long start = System.currentTimeMillis(); - FuzzyListenConfigChangeEvent event = FuzzyListenConfigChangeEvent.build(group, dataId, type); + ConfigFuzzyWatchChangeEvent event = ConfigFuzzyWatchChangeEvent.build(group, dataId, type); if (listener != null) { listener.onEvent(event); } @@ -213,7 +199,7 @@ public CompletableFuture> waitForInitializationComplete( while (isInitializing) { initializationCompleted.await(); } - future.complete(Collections.unmodifiableCollection(dataIds)); + future.complete(Collections.unmodifiableCollection(receivedGroupKeys)); } catch (InterruptedException e) { future.completeExceptionally(e); } finally { @@ -240,7 +226,7 @@ public void markInitializationComplete() { * * @param listener Listener to be removed */ - public void removeListener(AbstractFuzzyListenListener listener) { + public void removeListener(AbstractFuzzyWatchListener listener) { listeners.remove(listener); } @@ -249,7 +235,7 @@ public void removeListener(AbstractFuzzyListenListener listener) { * * @param listener Listener to be added */ - public void addListener(AbstractFuzzyListenListener listener) { + public void addListener(AbstractFuzzyWatchListener listener) { listeners.add(listener); } @@ -289,60 +275,22 @@ public void setTaskId(int taskId) { this.taskId = taskId; } - /** - * Get the data ID pattern. - * - * @return Data ID pattern - */ - public String getDataIdPattern() { - return dataIdPattern; + public String getGroupKeyPattern() { + return groupKeyPattern; } - /** - * Set the data ID pattern. - * - * @param dataIdPattern Data ID pattern to be set - */ - public void setDataIdPattern(String dataIdPattern) { - this.dataIdPattern = dataIdPattern; + public void setGroupKeyPattern(String groupKeyPattern) { + this.groupKeyPattern = groupKeyPattern; } + /** * Get the group name. * * @return Group name */ - public String getGroup() { - return group; - } - - /** - * Set the group name. - * - * @param group Group name to be set - */ - public void setGroup(String group) { - this.group = group; - } - - /** - * Get the tenant name. - * - * @return Tenant name - */ - public String getTenant() { - return tenant; - } - - /** - * Set the tenant name. - * - * @param tenant Tenant name to be set - */ - public void setTenant(String tenant) { - this.tenant = tenant; - } - + + /** * Get the flag indicating whether the context is consistent with the server. * @@ -393,25 +341,17 @@ public void setInitializing(boolean initializing) { * * @return Set of data IDs */ - public Set getDataIds() { - return Collections.unmodifiableSet(dataIds); - } - - /** - * Set the set of data IDs associated with the context. - * - * @param dataIds Set of data IDs to be set - */ - public void setDataIds(Set dataIds) { - this.dataIds = dataIds; + public Set getReceivedGroupKeys() { + return Collections.unmodifiableSet(receivedGroupKeys); } + /** * Get the set of listeners associated with the context. * * @return Set of listeners */ - public Set getListeners() { + public Set getListeners() { return listeners; } @@ -420,7 +360,7 @@ public Set getListeners() { * * @param listeners Set of listeners to be set */ - public void setListeners(Set listeners) { + public void setListeners(Set listeners) { this.listeners = listeners; } diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/NacosNamingService.java b/client/src/main/java/com/alibaba/nacos/client/naming/NacosNamingService.java index 3125502b114..eb7e8731d81 100644 --- a/client/src/main/java/com/alibaba/nacos/client/naming/NacosNamingService.java +++ b/client/src/main/java/com/alibaba/nacos/client/naming/NacosNamingService.java @@ -60,7 +60,7 @@ import java.util.Properties; import java.util.UUID; -import static com.alibaba.nacos.api.common.Constants.FUZZY_WATCH_PATTERN_WILDCARD; +import static com.alibaba.nacos.api.common.Constants.ANY_PATTERN; import static com.alibaba.nacos.client.naming.selector.NamingSelectorFactory.getUniqueClusterString; import static com.alibaba.nacos.client.utils.LogUtils.NAMING_LOGGER; @@ -541,15 +541,15 @@ private void doUnsubscribe(String serviceName, String groupName, NamingSelector @Override public void fuzzyWatch(String fixedGroupName, AbstractFuzzyWatchEventListener listener) throws NacosException { - doFuzzyWatch(FUZZY_WATCH_PATTERN_WILDCARD, fixedGroupName, listener); + doFuzzyWatch(ANY_PATTERN, fixedGroupName, listener); } @Override public void fuzzyWatch(String serviceNamePattern, String fixedGroupName, AbstractFuzzyWatchEventListener listener) throws NacosException { // only support prefix match right now - if (!serviceNamePattern.endsWith(FUZZY_WATCH_PATTERN_WILDCARD)) { - if (serviceNamePattern.startsWith(FUZZY_WATCH_PATTERN_WILDCARD)) { + if (!serviceNamePattern.endsWith(ANY_PATTERN)) { + if (serviceNamePattern.startsWith(ANY_PATTERN)) { throw new UnsupportedOperationException("Suffix matching for service names is not supported yet." + " It will be supported in future updates if needed."); } else { @@ -572,7 +572,7 @@ private void doFuzzyWatch(String serviceNamePattern, String groupNamePattern, @Override public void cancelFuzzyWatch(String fixedGroupName, AbstractFuzzyWatchEventListener listener) throws NacosException { - doCancelFuzzyWatch(FUZZY_WATCH_PATTERN_WILDCARD, fixedGroupName, listener); + doCancelFuzzyWatch(ANY_PATTERN, fixedGroupName, listener); } @Override diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/cache/FuzzyWatchServiceListHolder.java b/client/src/main/java/com/alibaba/nacos/client/naming/cache/FuzzyWatchServiceListHolder.java index dfd0e2bd240..f0380c8ffba 100644 --- a/client/src/main/java/com/alibaba/nacos/client/naming/cache/FuzzyWatchServiceListHolder.java +++ b/client/src/main/java/com/alibaba/nacos/client/naming/cache/FuzzyWatchServiceListHolder.java @@ -27,6 +27,7 @@ import com.alibaba.nacos.client.naming.utils.CollectionUtils; import com.alibaba.nacos.common.notify.NotifyCenter; import com.alibaba.nacos.common.utils.ConcurrentHashSet; +import com.alibaba.nacos.common.utils.FuzzyGroupKeyPattern; import java.util.Collection; import java.util.Map; @@ -72,8 +73,7 @@ public void processFuzzyWatchNotify(AbstractFuzzyWatchNotifyRequest request) { } } else if (request instanceof FuzzyWatchNotifyChangeRequest) { FuzzyWatchNotifyChangeRequest notifyChangeRequest = (FuzzyWatchNotifyChangeRequest) request; - Collection matchedPattern = NamingUtils.getServiceMatchedPatterns(notifyChangeRequest.getServiceName(), - notifyChangeRequest.getGroupName(), patternMatchMap.keySet()); + Collection matchedPattern = FuzzyGroupKeyPattern.filterMatchedPatterns(patternMatchMap.keySet(),notifyChangeRequest.getNamespace(),notifyChangeRequest.getGroupName(),notifyChangeRequest.getServiceName()); Service service = new Service(notifyChangeRequest.getServiceName(), notifyChangeRequest.getGroupName()); String serviceChangeType = request.getServiceChangedType(); diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/remote/NamingClientProxyDelegate.java b/client/src/main/java/com/alibaba/nacos/client/naming/remote/NamingClientProxyDelegate.java index 408babbca72..35fba8b83dd 100644 --- a/client/src/main/java/com/alibaba/nacos/client/naming/remote/NamingClientProxyDelegate.java +++ b/client/src/main/java/com/alibaba/nacos/client/naming/remote/NamingClientProxyDelegate.java @@ -27,7 +27,6 @@ import com.alibaba.nacos.client.env.NacosClientProperties; import com.alibaba.nacos.client.naming.cache.ServiceInfoHolder; import com.alibaba.nacos.client.naming.cache.FuzzyWatchServiceListHolder; -import com.alibaba.nacos.client.naming.core.ServerListManager; import com.alibaba.nacos.client.naming.core.NamingServerListManager; import com.alibaba.nacos.client.naming.core.ServiceInfoUpdateService; import com.alibaba.nacos.client.naming.event.InstancesChangeNotifier; diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/NamingGrpcClientProxy.java b/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/NamingGrpcClientProxy.java index 3a301406898..6211b885d1e 100644 --- a/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/NamingGrpcClientProxy.java +++ b/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/NamingGrpcClientProxy.java @@ -28,17 +28,17 @@ import com.alibaba.nacos.api.naming.remote.NamingRemoteConstants; import com.alibaba.nacos.api.naming.remote.request.AbstractNamingRequest; import com.alibaba.nacos.api.naming.remote.request.BatchInstanceRequest; +import com.alibaba.nacos.api.naming.remote.request.FuzzyWatchRequest; import com.alibaba.nacos.api.naming.remote.request.InstanceRequest; import com.alibaba.nacos.api.naming.remote.request.PersistentInstanceRequest; import com.alibaba.nacos.api.naming.remote.request.ServiceListRequest; import com.alibaba.nacos.api.naming.remote.request.ServiceQueryRequest; import com.alibaba.nacos.api.naming.remote.request.SubscribeServiceRequest; -import com.alibaba.nacos.api.naming.remote.request.FuzzyWatchRequest; import com.alibaba.nacos.api.naming.remote.response.BatchInstanceResponse; +import com.alibaba.nacos.api.naming.remote.response.FuzzyWatchResponse; import com.alibaba.nacos.api.naming.remote.response.QueryServiceResponse; import com.alibaba.nacos.api.naming.remote.response.ServiceListResponse; import com.alibaba.nacos.api.naming.remote.response.SubscribeServiceResponse; -import com.alibaba.nacos.api.naming.remote.response.FuzzyWatchResponse; import com.alibaba.nacos.api.naming.utils.NamingUtils; import com.alibaba.nacos.api.remote.RemoteConstants; import com.alibaba.nacos.api.remote.response.Response; @@ -47,9 +47,8 @@ import com.alibaba.nacos.api.selector.SelectorType; import com.alibaba.nacos.client.env.NacosClientProperties; import com.alibaba.nacos.client.monitor.MetricsMonitor; -import com.alibaba.nacos.client.naming.cache.ServiceInfoHolder; import com.alibaba.nacos.client.naming.cache.FuzzyWatchServiceListHolder; -import com.alibaba.nacos.client.naming.event.ServerListChangedEvent; +import com.alibaba.nacos.client.naming.cache.ServiceInfoHolder; import com.alibaba.nacos.client.address.ServerListChangeEvent; import com.alibaba.nacos.client.naming.remote.AbstractNamingClientProxy; import com.alibaba.nacos.client.naming.remote.gprc.redo.NamingGrpcRedoService; diff --git a/client/src/test/java/com/alibaba/nacos/client/config/NacosConfigServiceTest.java b/client/src/test/java/com/alibaba/nacos/client/config/NacosConfigServiceTest.java index 4cf62936894..b037c1fd34a 100644 --- a/client/src/test/java/com/alibaba/nacos/client/config/NacosConfigServiceTest.java +++ b/client/src/test/java/com/alibaba/nacos/client/config/NacosConfigServiceTest.java @@ -38,6 +38,7 @@ import org.mockito.quality.Strictness; import java.lang.reflect.Field; +import java.util.Arrays; import java.util.Collections; import java.util.Properties; import java.util.concurrent.Executor; @@ -157,7 +158,6 @@ void testGetConfig403() throws NacosException { .thenThrow(new NacosException(NacosException.NO_RIGHT, "no right")); try { nacosConfigService.getConfig(dataId, group, timeout); - Assert.fail(); assertTrue(false); } catch (NacosException e) { assertEquals(NacosException.NO_RIGHT, e.getErrCode()); @@ -196,12 +196,12 @@ public void receiveConfigInfo(String configInfo) { public void startInternal() throws NacosException { // NOOP } - + @Override public String getName() { return "TestConfigTransportClient"; } - + @Override public void notifyListenConfig() { // NOOP @@ -257,7 +257,7 @@ public boolean removeConfig(String dataId, String group, String tenant, String t Mockito.when(mockWoker.getAgent()).thenReturn(client); final String config = nacosConfigService.getConfigAndSignListener(dataId, group, timeout, listener); - Assert.assertEquals(content, config); + assertEquals(content, config); Mockito.verify(mockWoker, Mockito.times(1)) .addTenantListenersWithContent(dataId, group, content, null, Collections.singletonList(listener)); @@ -275,16 +275,15 @@ void testAddListener() throws NacosException { public Executor getExecutor() { return null; } - + @Override public void receiveConfigInfo(String configInfo) { - + } }; - + nacosConfigService.addListener(dataId, group, listener); - Mockito.verify(mockWoker, Mockito.times(1)) - .addTenantListeners(dataId, group, Collections.singletonList(listener)); + Mockito.verify(mockWoker, Mockito.times(1)).addTenantListeners(dataId, group, Arrays.asList(listener)); } @Test diff --git a/client/src/test/java/com/alibaba/nacos/client/naming/NacosNamingServiceTest.java b/client/src/test/java/com/alibaba/nacos/client/naming/NacosNamingServiceTest.java index 2232aa9f04f..b1c98ee7195 100644 --- a/client/src/test/java/com/alibaba/nacos/client/naming/NacosNamingServiceTest.java +++ b/client/src/test/java/com/alibaba/nacos/client/naming/NacosNamingServiceTest.java @@ -1089,6 +1089,7 @@ public void testUnSubscribeWithServiceAndCustomSelector() throws NacosException verify(proxy, times(1)).unsubscribe(serviceName, Constants.DEFAULT_GROUP, Constants.NULL); } + @Test public void testUnSubscribeWithFullNameAndCustomSelector() throws NacosException { //given String serviceName = "service1"; diff --git a/client/src/test/java/com/alibaba/nacos/client/naming/remote/NamingClientProxyDelegateTest.java b/client/src/test/java/com/alibaba/nacos/client/naming/remote/NamingClientProxyDelegateTest.java index 7f201b14eb9..828637aa3e7 100644 --- a/client/src/test/java/com/alibaba/nacos/client/naming/remote/NamingClientProxyDelegateTest.java +++ b/client/src/test/java/com/alibaba/nacos/client/naming/remote/NamingClientProxyDelegateTest.java @@ -81,7 +81,7 @@ void setUp() throws NacosException, NoSuchFieldException, IllegalAccessException props.setProperty("serverAddr", "localhost"); nacosClientProperties = NacosClientProperties.PROTOTYPE.derive(props); notifier = new InstancesChangeNotifier(); - delegate = new NamingClientProxyDelegate(TEST_NAMESPACE, holder, nacosClientProperties,fuzzyWatchServiceListHolder, notifier); + delegate = new NamingClientProxyDelegate(TEST_NAMESPACE, holder,fuzzyWatchServiceListHolder, nacosClientProperties, notifier); Field grpcClientProxyField = NamingClientProxyDelegate.class.getDeclaredField("grpcClientProxy"); grpcClientProxyField.setAccessible(true); grpcClientProxyField.set(delegate, mockGrpcClient); @@ -106,7 +106,7 @@ void testRegisterEphemeralServiceByGrpc() throws NacosException { verify(mockGrpcClient, times(1)).registerService(serviceName, groupName, instance); } - + @Test void testBatchRegisterServiceByGrpc() throws NacosException { String serviceName = "service1"; String groupName = "group1"; @@ -173,7 +173,7 @@ void testRegisterPersistentServiceByHttp() throws NacosException, NoSuchFieldExc verify(mockHttpClient, times(1)).registerService(serviceName, groupName, instance); } - + @Test void testDeregisterEphemeralServiceGrpc() throws NacosException { String serviceName = "service1"; String groupName = "group1"; @@ -225,7 +225,8 @@ void testDeregisterPersistentServiceHttp() throws NacosException, NoSuchFieldExc delegate.deregisterService(serviceName, groupName, instance); verify(mockHttpClient, times(1)).deregisterService(serviceName, groupName, instance); } - + + @Test void testUpdateInstance() { String serviceName = "service1"; String groupName = "group1"; @@ -234,7 +235,8 @@ void testUpdateInstance() { delegate.updateInstance(serviceName, groupName, instance); }); } - + + @Test void testQueryInstancesOfService() throws NacosException { String serviceName = "service1"; String groupName = "group1"; @@ -243,13 +245,13 @@ void testQueryInstancesOfService() throws NacosException { verify(mockGrpcClient, times(1)).queryInstancesOfService(serviceName, groupName, clusters, false); } - + @Test void testQueryService() throws NacosException { Service service = delegate.queryService("a", "b"); assertNull(service); } - + @Test void testCreateService() { Service service = new Service(); Assertions.assertDoesNotThrow(() -> { @@ -257,7 +259,7 @@ void testCreateService() { }); } - + @Test void testDeleteService() throws NacosException { assertFalse(delegate.deleteService("service", "group1")); } @@ -270,7 +272,7 @@ void testUpdateService() { }); } - + @Test void testGetServiceList() throws NacosException { AbstractSelector selector = new ExpressionSelector(); int pageNo = 1; diff --git a/client/src/test/java/com/alibaba/nacos/client/naming/remote/gprc/NamingGrpcClientProxyTest.java b/client/src/test/java/com/alibaba/nacos/client/naming/remote/gprc/NamingGrpcClientProxyTest.java index 910dbdab4c0..bd563c6c00f 100644 --- a/client/src/test/java/com/alibaba/nacos/client/naming/remote/gprc/NamingGrpcClientProxyTest.java +++ b/client/src/test/java/com/alibaba/nacos/client/naming/remote/gprc/NamingGrpcClientProxyTest.java @@ -48,7 +48,6 @@ import com.alibaba.nacos.client.env.NacosClientProperties; import com.alibaba.nacos.client.naming.cache.ServiceInfoHolder; import com.alibaba.nacos.client.naming.cache.FuzzyWatchServiceListHolder; -import com.alibaba.nacos.client.naming.event.ServerListChangedEvent; import com.alibaba.nacos.client.address.ServerListChangeEvent; import com.alibaba.nacos.client.naming.remote.gprc.redo.NamingGrpcRedoService; import com.alibaba.nacos.client.security.SecurityProxy; @@ -150,8 +149,7 @@ void setUp() throws NacosException, NoSuchFieldException, IllegalAccessException prop = new Properties(); final NacosClientProperties nacosClientProperties = NacosClientProperties.PROTOTYPE.derive(prop); - client = new NamingGrpcClientProxy(NAMESPACE_ID, proxy, factory, nacosClientProperties, holder, - fuzzyWatchServiceListHolder); + client = new NamingGrpcClientProxy(NAMESPACE_ID, proxy, factory, nacosClientProperties, holder,fuzzyWatchServiceListHolder); Field uuidField = NamingGrpcClientProxy.class.getDeclaredField("uuid"); uuidField.setAccessible(true); diff --git a/client/src/test/java/com/alibaba/nacos/client/naming/remote/gprc/NamingPushRequestHandlerTest.java b/client/src/test/java/com/alibaba/nacos/client/naming/remote/gprc/NamingPushRequestHandlerTest.java index 786a4d7b5e9..ccba59738a3 100644 --- a/client/src/test/java/com/alibaba/nacos/client/naming/remote/gprc/NamingPushRequestHandlerTest.java +++ b/client/src/test/java/com/alibaba/nacos/client/naming/remote/gprc/NamingPushRequestHandlerTest.java @@ -26,8 +26,7 @@ import com.alibaba.nacos.api.remote.response.Response; import com.alibaba.nacos.client.naming.cache.ServiceInfoHolder; import com.alibaba.nacos.client.naming.cache.FuzzyWatchServiceListHolder; -import org.junit.Assert; -import org.junit.Test; + import com.alibaba.nacos.client.naming.remote.TestConnection; import com.alibaba.nacos.common.remote.client.RpcClient; import org.junit.jupiter.api.Test; @@ -58,7 +57,9 @@ void testRequestReply() { @Test void testRequestReplyOtherType() { ServiceInfoHolder holder = mock(ServiceInfoHolder.class); - NamingPushRequestHandler handler = new NamingPushRequestHandler(holder); + FuzzyWatchServiceListHolder fuzzyWatchServiceListHolder = mock(FuzzyWatchServiceListHolder.class); + + NamingPushRequestHandler handler = new NamingPushRequestHandler(holder,fuzzyWatchServiceListHolder); assertNull(handler.requestReply(new HealthCheckRequest(), new TestConnection(new RpcClient.ServerInfo()))); } } \ No newline at end of file diff --git a/common/src/main/java/com/alibaba/nacos/common/utils/FuzzyGroupKeyPattern.java b/common/src/main/java/com/alibaba/nacos/common/utils/FuzzyGroupKeyPattern.java new file mode 100644 index 00000000000..a9ba130bfcc --- /dev/null +++ b/common/src/main/java/com/alibaba/nacos/common/utils/FuzzyGroupKeyPattern.java @@ -0,0 +1,141 @@ +/* + * 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.common.utils; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; + +import static com.alibaba.nacos.api.common.Constants.ANY_PATTERN; +import static com.alibaba.nacos.api.common.Constants.DEFAULT_NAMESPACE_ID; +import static com.alibaba.nacos.api.common.Constants.FUZZY_WATCH_PATTERN_SPLITTER; + +/** + * Utility class for matching group keys against a given pattern. + * + *

This class provides methods to match group keys based on a pattern specified. It supports matching based on + * dataId, group, and namespace components of the group key. + * + * @author stone-98 + * @date 2024/3/14 + */ +public class FuzzyGroupKeyPattern { + + /** + * Generates a fuzzy listen group key pattern based on the given dataId pattern, group, and optional tenant. + * pattern result as: fixNamespace>>groupPattern>>dataIdPattern + * + * @param resourcePattern The pattern for matching dataIds or service names. + * @param groupPattern The groupPattern associated with the groups. + * @param fixNamespace (Optional) The tenant associated with the dataIds (can be null or empty). + * @return A unique group key pattern for fuzzy listen. + * @throws IllegalArgumentException If the dataId pattern or group is blank. + */ + public static String generateFuzzyWatchGroupKeyPattern(final String resourcePattern, final String groupPattern, + String fixNamespace) { + if (StringUtils.isBlank(resourcePattern)) { + throw new IllegalArgumentException("Param 'resourcePattern' is illegal, resourcePattern is blank"); + } + if (StringUtils.isBlank(groupPattern)) { + throw new IllegalArgumentException("Param 'groupPattern' is illegal, group is blank"); + } + if (StringUtils.isBlank(fixNamespace)) { + fixNamespace=DEFAULT_NAMESPACE_ID; + } + StringBuilder sb = new StringBuilder(); + sb.append(fixNamespace); + sb.append(FUZZY_WATCH_PATTERN_SPLITTER); + sb.append(groupPattern); + sb.append(FUZZY_WATCH_PATTERN_SPLITTER); + sb.append(resourcePattern); + return sb.toString().intern(); + } + + + /** + * Given a dataId, group, and a collection of completed group key patterns, returns the patterns that match. + * + * @param resourceName The dataId or sservice name to match. + * @param group The group to match. + * @param namespace The group to match. + * @param groupKeyPatterns The collection of completed group key patterns to match against. + * @return A set of patterns that match the dataId and group. + */ + public static Set filterMatchedPatterns(Collection groupKeyPatterns,String resourceName, String group, String namespace + ) { + if (CollectionUtils.isEmpty(groupKeyPatterns)) { + return new HashSet<>(1); + } + Set matchedPatternList = new HashSet<>(); + for (String keyPattern : groupKeyPatterns) { + if (matchPattern(keyPattern,namespace, group,resourceName)) { + matchedPatternList.add(keyPattern); + } + } + return matchedPatternList; + } + + public static boolean matchPattern(String groupKeyPattern,String namespace,String group,String resourceName){ + if(StringUtils.isBlank(namespace)){ + namespace=DEFAULT_NAMESPACE_ID; + } + String[] splitPatterns = groupKeyPattern.split(FUZZY_WATCH_PATTERN_SPLITTER); + return splitPatterns[0].equals(namespace)&&itemMatched(splitPatterns[1],group)&&itemMatched(splitPatterns[2],resourceName); + } + + public static String getNamespaceFromGroupKeyPattern(String groupKeyPattern){ + return groupKeyPattern.split(FUZZY_WATCH_PATTERN_SPLITTER)[0]; + } + /** + * + * @param pattern + * @param resource + * @return + */ + private static boolean itemMatched(String pattern,String resource){ + + //accurate match without * + if (!pattern.contains(ANY_PATTERN)){ + return pattern.equals(resource); + } + + //match for '*' pattern + if (pattern.equals(ANY_PATTERN)){ + return true; + } + + //match for *{string}* + if (pattern.startsWith(ANY_PATTERN)&&pattern.endsWith(ANY_PATTERN)){ + String pureString=pattern.replaceAll(ANY_PATTERN,""); + return resource.contains(pureString); + } + + //match for postfix match *{string} + if (pattern.startsWith(ANY_PATTERN)&&pattern.endsWith(ANY_PATTERN)){ + String pureString=pattern.replaceAll(ANY_PATTERN,""); + return resource.endsWith(pureString); + } + + //match for prefix match {string}* + if (pattern.startsWith(ANY_PATTERN)&&pattern.endsWith(ANY_PATTERN)){ + String pureString=pattern.replaceAll(ANY_PATTERN,""); + return resource.startsWith(pureString); + } + + return false; + } +} diff --git a/common/src/main/java/com/alibaba/nacos/common/utils/GroupKeyPattern.java b/common/src/main/java/com/alibaba/nacos/common/utils/GroupKeyPattern.java deleted file mode 100644 index f00d50e7db2..00000000000 --- a/common/src/main/java/com/alibaba/nacos/common/utils/GroupKeyPattern.java +++ /dev/null @@ -1,259 +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.common.utils; - -import com.alibaba.nacos.api.common.Constants; - -import java.util.Collection; -import java.util.HashSet; -import java.util.Set; - -import static com.alibaba.nacos.api.common.Constants.DATA_ID_SPLITTER; -import static com.alibaba.nacos.api.common.Constants.FUZZY_LISTEN_PATTERN_WILDCARD; -import static com.alibaba.nacos.api.common.Constants.NAMESPACE_ID_SPLITTER; - -/** - * Utility class for matching group keys against a given pattern. - * - *

This class provides methods to match group keys based on a pattern specified. It supports matching based on - * dataId, group, and namespace components of the group key. - * - * @author stone-98 - * @date 2024/3/14 - */ -public class GroupKeyPattern { - - /** - * Generates a fuzzy listen group key pattern based on the given dataId pattern, group, and optional tenant. - * - *

This method generates a unique group key pattern for fuzzy listen based on the specified dataId pattern, - * group, and optional tenant. It concatenates the dataId pattern, group, and tenant (if provided) with a delimiter - * and returns the resulting string. The resulting string is interned to improve memory efficiency. - * - * @param dataIdPattern The pattern for matching dataIds. - * @param group The group associated with the dataIds. - * @param namespace (Optional) The tenant associated with the dataIds (can be null or empty). - * @return A unique group key pattern for fuzzy listen. - * @throws IllegalArgumentException If the dataId pattern or group is blank. - */ - public static String generateFuzzyListenGroupKeyPattern(final String dataIdPattern, final String group, - final String namespace) { - if (StringUtils.isBlank(dataIdPattern)) { - throw new IllegalArgumentException("Param 'dataIdPattern' is illegal, dataIdPattern is blank"); - } - if (StringUtils.isBlank(group)) { - throw new IllegalArgumentException("Param 'group' is illegal, group is blank"); - } - StringBuilder sb = new StringBuilder(); - if (StringUtils.isNotBlank(namespace)) { - sb.append(namespace); - } - sb.append(NAMESPACE_ID_SPLITTER); - sb.append(group); - sb.append(DATA_ID_SPLITTER); - sb.append(dataIdPattern); - return sb.toString().intern(); - } - - /** - * Generates a fuzzy listen group key pattern based on the given dataId pattern and group. - * - *

This method generates a unique group key pattern for fuzzy listen based on the specified dataId pattern and - * group. It concatenates the dataId pattern and group with a delimiter and returns the resulting string. The - * resulting string is interned to improve memory efficiency. - * - * @param dataIdPattern The pattern for matching dataIds. - * @param group The group associated with the dataIds. - * @return A unique group key pattern for fuzzy listen. - * @throws IllegalArgumentException If the dataId pattern or group is blank. - */ - public static String generateFuzzyListenGroupKeyPattern(final String dataIdPattern, final String group) { - if (StringUtils.isBlank(dataIdPattern)) { - throw new IllegalArgumentException("Param 'dataIdPattern' is illegal, dataIdPattern is blank"); - } - if (StringUtils.isBlank(group)) { - throw new IllegalArgumentException("Param 'group' is illegal, group is blank"); - } - final String fuzzyListenGroupKey = group + DATA_ID_SPLITTER + dataIdPattern; - return fuzzyListenGroupKey.intern(); - } - - /** - * Checks whether a group key matches the specified pattern. - * - * @param groupKey The group key to match. - * @param groupKeyPattern The pattern to match against. - * @return {@code true} if the group key matches the pattern, otherwise {@code false}. - */ - public static boolean isMatchPatternWithNamespace(String groupKey, String groupKeyPattern) { - String[] parseKey = GroupKey.parseKey(groupKey); - String dataId = parseKey[0]; - String group = parseKey[1]; - String namespace = parseKey.length > 2 ? parseKey[2] : Constants.DEFAULT_NAMESPACE_ID; - - String namespacePattern = getNamespace(groupKeyPattern); - String groupPattern = getGroup(groupKeyPattern); - String dataIdPattern = getDataIdPattern(groupKeyPattern); - - if (dataIdPattern.equals(FUZZY_LISTEN_PATTERN_WILDCARD)) { - return namespace.equals(namespacePattern) && group.equals(groupPattern); - } - - if (dataIdPattern.endsWith(FUZZY_LISTEN_PATTERN_WILDCARD)) { - String dataIdPrefix = dataIdPattern.substring(0, dataIdPattern.length() - 1); - return namespace.equals(namespacePattern) && groupPattern.equals(group) && dataId.startsWith(dataIdPrefix); - } - - return namespace.equals(namespacePattern) && group.equals(groupPattern) && dataId.equals(dataIdPattern); - } - - /** - * Checks whether a group key matches the specified pattern. - * - * @param groupKey The group key to match. - * @param groupKeyPattern The pattern to match against. - * @return {@code true} if the group key matches the pattern, otherwise {@code false}. - */ - public static boolean isMatchPatternWithoutNamespace(String groupKey, String groupKeyPattern) { - String[] parseKey = GroupKey.parseKey(groupKey); - String dataId = parseKey[0]; - String group = parseKey[1]; - - String groupPattern = getGroup(groupKeyPattern); - String dataIdPattern = getDataIdPattern(groupKeyPattern); - - if (dataIdPattern.equals(FUZZY_LISTEN_PATTERN_WILDCARD)) { - return group.equals(groupPattern); - } - - if (dataIdPattern.endsWith(FUZZY_LISTEN_PATTERN_WILDCARD)) { - String dataIdPrefix = dataIdPattern.substring(0, dataIdPattern.length() - 1); - return groupPattern.equals(group) && dataId.startsWith(dataIdPrefix); - } - - return group.equals(groupPattern) && dataId.equals(dataIdPattern); - } - - /** - * Given a dataId, group, dataId pattern, and group pattern, determines whether it can match. - * - * @param dataId The dataId to match. - * @param group The group to match. - * @param dataIdPattern The dataId pattern to match against. - * @param groupPattern The group pattern to match against. - * @return {@code true} if the dataId and group match the patterns, otherwise {@code false}. - */ - public static boolean isMatchPatternWithoutNamespace(String dataId, String group, String dataIdPattern, - String groupPattern) { - String groupKey = GroupKey.getKey(dataId, group); - String groupKeyPattern = generateFuzzyListenGroupKeyPattern(dataIdPattern, groupPattern); - return isMatchPatternWithoutNamespace(groupKey, groupKeyPattern); - } - - /** - * Given a dataId, group, and a collection of completed group key patterns, returns the patterns that match. - * - * @param dataId The dataId to match. - * @param group The group to match. - * @param groupKeyPatterns The collection of completed group key patterns to match against. - * @return A set of patterns that match the dataId and group. - */ - public static Set getConfigMatchedPatternsWithoutNamespace(String dataId, String group, - Collection groupKeyPatterns) { - if (CollectionUtils.isEmpty(groupKeyPatterns)) { - return new HashSet<>(1); - } - Set matchedPatternList = new HashSet<>(); - for (String keyPattern : groupKeyPatterns) { - if (isMatchPatternWithoutNamespace(dataId, group, getDataIdPattern(keyPattern), getGroup(keyPattern))) { - matchedPatternList.add(keyPattern); - } - } - return matchedPatternList; - } - - /** - * Extracts the namespace from the given group key pattern. - * - * @param groupKeyPattern The group key pattern from which to extract the namespace. - * @return The namespace extracted from the group key pattern. - */ - public static String getNamespace(final String groupKeyPattern) { - if (StringUtils.isBlank(groupKeyPattern)) { - return StringUtils.EMPTY; - } - if (!groupKeyPattern.contains(NAMESPACE_ID_SPLITTER)) { - return StringUtils.EMPTY; - } - return groupKeyPattern.split(NAMESPACE_ID_SPLITTER)[0]; - } - - /** - * Extracts the group from the given group key pattern. - * - * @param groupKeyPattern The group key pattern from which to extract the group. - * @return The group extracted from the group key pattern. - */ - public static String getGroup(final String groupKeyPattern) { - if (StringUtils.isBlank(groupKeyPattern)) { - return StringUtils.EMPTY; - } - String groupWithNamespace; - if (!groupKeyPattern.contains(DATA_ID_SPLITTER)) { - groupWithNamespace = groupKeyPattern; - } else { - groupWithNamespace = groupKeyPattern.split(DATA_ID_SPLITTER)[0]; - } - - if (!groupKeyPattern.contains(NAMESPACE_ID_SPLITTER)) { - return groupWithNamespace; - } - return groupWithNamespace.split(NAMESPACE_ID_SPLITTER)[1]; - } - - /** - * Extracts the dataId pattern from the given group key pattern. - * - * @param groupKeyPattern The group key pattern from which to extract the dataId pattern. - * @return The dataId pattern extracted from the group key pattern. - */ - public static String getDataIdPattern(final String groupKeyPattern) { - if (StringUtils.isBlank(groupKeyPattern)) { - return StringUtils.EMPTY; - } - if (!groupKeyPattern.contains(DATA_ID_SPLITTER)) { - return StringUtils.EMPTY; - } - return groupKeyPattern.split(DATA_ID_SPLITTER)[1]; - } - - /** - * Given a completed pattern, removes the namespace. - * - * @param completedPattern The completed pattern from which to remove the namespace. - * @return The pattern with the namespace removed. - */ - public static String getPatternRemovedNamespace(String completedPattern) { - if (StringUtils.isBlank(completedPattern)) { - return StringUtils.EMPTY; - } - if (!completedPattern.contains(NAMESPACE_ID_SPLITTER)) { - return completedPattern; - } - return completedPattern.split(NAMESPACE_ID_SPLITTER)[1]; - } -} diff --git a/common/src/test/java/com/alibaba/nacos/common/utils/FuzzyGroupKeyPatternTest.java b/common/src/test/java/com/alibaba/nacos/common/utils/FuzzyGroupKeyPatternTest.java new file mode 100644 index 00000000000..7ee1115a810 --- /dev/null +++ b/common/src/test/java/com/alibaba/nacos/common/utils/FuzzyGroupKeyPatternTest.java @@ -0,0 +1,45 @@ +/* + * 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.common.utils; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * GroupKeyPatternUtilsTest. + * + * @author stone-98 + * @date 2024/3/19 + */ +public class FuzzyGroupKeyPatternTest { + + @Test + public void testGetGroupKeyPattern() { + String dataIdPattern = "examplePattern*"; + String group = "exampleGroup"; + String namespace = "exampleNamespace"; + + String groupKeyPattern = FuzzyGroupKeyPattern.generateFuzzyWatchGroupKeyPattern(dataIdPattern, group, namespace); + + assertEquals("exampleNamespace>>exampleGroup@@examplePattern*", groupKeyPattern); + } + + +} + diff --git a/common/src/test/java/com/alibaba/nacos/common/utils/GroupKeyPatternTest.java b/common/src/test/java/com/alibaba/nacos/common/utils/GroupKeyPatternTest.java deleted file mode 100644 index a16e222ecbc..00000000000 --- a/common/src/test/java/com/alibaba/nacos/common/utils/GroupKeyPatternTest.java +++ /dev/null @@ -1,136 +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.common.utils; - -import org.junit.Assert; -import org.junit.Test; - -import java.util.HashSet; -import java.util.Set; - -/** - * GroupKeyPatternUtilsTest. - * - * @author stone-98 - * @date 2024/3/19 - */ -public class GroupKeyPatternTest { - - @Test - public void testGetGroupKeyPatternWithNamespace() { - String dataIdPattern = "examplePattern*"; - String group = "exampleGroup"; - String namespace = "exampleNamespace"; - - String groupKeyPattern = GroupKeyPattern.generateFuzzyListenGroupKeyPattern(dataIdPattern, group, namespace); - - Assert.assertEquals("exampleNamespace>>exampleGroup@@examplePattern*", groupKeyPattern); - } - - @Test - public void testGetGroupKeyPatternWithoutNamespace() { - String dataIdPattern = "examplePattern*"; - String group = "exampleGroup"; - - String groupKeyPattern = GroupKeyPattern.generateFuzzyListenGroupKeyPattern(dataIdPattern, group); - - Assert.assertEquals("exampleGroup@@examplePattern*", groupKeyPattern); - } - - @Test - public void testIsMatchPatternWithNamespace() { - String groupKey = "examplePattern+exampleGroup+exampleNamespace"; - String groupKeyPattern = "exampleNamespace>>exampleGroup@@examplePattern*"; - - boolean result = GroupKeyPattern.isMatchPatternWithNamespace(groupKey, groupKeyPattern); - - Assert.assertTrue(result); - } - - @Test - public void testIsMatchPatternWithoutNamespace() { - String groupKey = "examplePattern+exampleGroup+exampleNamespace"; - String groupKeyPattern = "exampleNamespace>>exampleGroup@@*"; - - boolean result = GroupKeyPattern.isMatchPatternWithoutNamespace(groupKey, groupKeyPattern); - - Assert.assertTrue(result); - } - - @Test - public void testIsMatchPatternWithoutNamespaceWithDataIdPrefix() { - String groupKey = "examplePattern+exampleGroup+exampleNamespace"; - String groupKeyPattern = "exampleNamespace>>exampleGroup@@examplePattern*"; - - boolean result = GroupKeyPattern.isMatchPatternWithoutNamespace(groupKey, groupKeyPattern); - - Assert.assertTrue(result); - } - - @Test - public void testGetConfigMatchedPatternsWithoutNamespace() { - String dataId = "exampleDataId"; - String group = "exampleGroup"; - Set groupKeyPatterns = new HashSet<>(); - groupKeyPatterns.add("exampleGroup@@exampleDataId*"); - groupKeyPatterns.add("exampleGroup@@exampleDataI*"); - - Set matchedPatterns = GroupKeyPattern.getConfigMatchedPatternsWithoutNamespace(dataId, group, - groupKeyPatterns); - - Assert.assertEquals(2, matchedPatterns.size()); - Assert.assertTrue(matchedPatterns.contains("exampleGroup@@exampleDataId*")); - Assert.assertTrue(matchedPatterns.contains("exampleGroup@@exampleDataI*")); - } - - @Test - public void testGetNamespace() { - String groupKeyPattern = "exampleNamespace>>exampleGroup@@examplePattern"; - - String namespace = GroupKeyPattern.getNamespace(groupKeyPattern); - - Assert.assertEquals("exampleNamespace", namespace); - } - - @Test - public void testGetGroup() { - String groupKeyPattern = "exampleNamespace>>exampleGroup@@examplePattern"; - - String group = GroupKeyPattern.getGroup(groupKeyPattern); - - Assert.assertEquals("exampleGroup", group); - } - - @Test - public void testGetDataIdPattern() { - String groupKeyPattern = "exampleNamespace>>exampleGroup@@examplePattern"; - - String dataIdPattern = GroupKeyPattern.getDataIdPattern(groupKeyPattern); - - Assert.assertEquals("examplePattern", dataIdPattern); - } - - @Test - public void testGetPatternRemovedNamespace() { - String groupKeyPattern = "exampleNamespace>>exampleGroup@@examplePattern"; - - String patternRemovedNamespace = GroupKeyPattern.getPatternRemovedNamespace(groupKeyPattern); - - Assert.assertEquals("exampleGroup@@examplePattern", patternRemovedNamespace); - } -} - diff --git a/config/pom.xml b/config/pom.xml index c10e7df3796..521f7d1b515 100644 --- a/config/pom.xml +++ b/config/pom.xml @@ -134,6 +134,10 @@ hamcrest test + + com.alibaba.nacos + nacos-control-plugin + 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 722e5818493..0004c659386 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 @@ -36,6 +36,7 @@ public class ConfigCommonConfig extends AbstractDynamicConfig { private long pushTimeout = 3000L; private int batchSize = 10; + private boolean derbyOpsEnabled = false; private ConfigCommonConfig() { @@ -69,6 +70,7 @@ public int getBatchSize() { public void setBatchSize(int batchSize) { this.batchSize = batchSize; + } public boolean isDerbyOpsEnabled() { return derbyOpsEnabled; } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/model/CacheItem.java b/config/src/main/java/com/alibaba/nacos/config/server/model/CacheItem.java index 836713a3be4..e0423f3daa7 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/model/CacheItem.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/model/CacheItem.java @@ -16,8 +16,6 @@ package com.alibaba.nacos.config.server.model; -import com.alibaba.nacos.common.utils.CollectionUtils; -import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.config.server.utils.SimpleReadWriteLock; import com.alibaba.nacos.core.utils.StringPool; @@ -129,19 +127,5 @@ public void clearConfigGrays() { this.configCacheGray = null; this.sortedConfigCacheGrayList = null; } - - /** - * Checks if the configuration is effective for the specified client IP and tag. - * - * @param tag The tag associated with the configuration. - * @param clientIp The IP address of the client. - * @return true if the configuration is effective for the client, false otherwise. - */ - public boolean effectiveForClient(String tag, String clientIp) { - if (isBeta && CollectionUtils.isNotEmpty(ips4Beta) && !ips4Beta.contains(clientIp)) { - return false; - } - return StringUtils.isBlank(tag) || (getConfigCacheTags() != null && getConfigCacheTags().containsKey(tag)); - } } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/model/event/ConfigBatchFuzzyListenEvent.java b/config/src/main/java/com/alibaba/nacos/config/server/model/event/ConfigBatchFuzzyListenEvent.java index 2e44763ee9e..15e6a1defff 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/model/event/ConfigBatchFuzzyListenEvent.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/model/event/ConfigBatchFuzzyListenEvent.java @@ -35,12 +35,12 @@ public class ConfigBatchFuzzyListenEvent extends Event { /** * ID of the client making the request. */ - private String clientId; + private String connectionId; /** * Pattern for matching group keys. */ - private String keyGroupPattern; + private String groupKeyPattern; /** * Set of existing group keys associated with the client. @@ -55,16 +55,16 @@ public class ConfigBatchFuzzyListenEvent extends Event { /** * Constructs a new ConfigBatchFuzzyListenEvent with the specified parameters. * - * @param clientId ID of the client making the request + * @param connectionId ID of the client making the request * @param clientExistingGroupKeys Set of existing group keys associated with the client - * @param keyGroupPattern Pattern for matching group keys + * @param groupKeyPattern Pattern for matching group keys * @param isInitializing Flag indicating whether the client is initializing */ - public ConfigBatchFuzzyListenEvent(String clientId, Set clientExistingGroupKeys, String keyGroupPattern, + public ConfigBatchFuzzyListenEvent(String connectionId, Set clientExistingGroupKeys, String groupKeyPattern, boolean isInitializing) { - this.clientId = clientId; + this.connectionId = connectionId; this.clientExistingGroupKeys = clientExistingGroupKeys; - this.keyGroupPattern = keyGroupPattern; + this.groupKeyPattern = groupKeyPattern; this.isInitializing = isInitializing; } @@ -73,17 +73,17 @@ public ConfigBatchFuzzyListenEvent(String clientId, Set clientExistingGr * * @return The client ID */ - public String getClientId() { - return clientId; + public String getConnectionId() { + return connectionId; } /** * Set the ID of the client making the request. * - * @param clientId The client ID to be set + * @param connectionId The client ID to be set */ - public void setClientId(String clientId) { - this.clientId = clientId; + public void setConnectionId(String connectionId) { + this.connectionId = connectionId; } /** @@ -91,17 +91,17 @@ public void setClientId(String clientId) { * * @return The key group pattern */ - public String getKeyGroupPattern() { - return keyGroupPattern; + public String getGroupKeyPattern() { + return groupKeyPattern; } /** * Set the pattern for matching group keys. * - * @param keyGroupPattern The key group pattern to be set + * @param groupKeyPattern The key group pattern to be set */ - public void setKeyGroupPattern(String keyGroupPattern) { - this.keyGroupPattern = keyGroupPattern; + public void setGroupKeyPattern(String groupKeyPattern) { + this.groupKeyPattern = groupKeyPattern; } /** diff --git a/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigBatchFuzzyListenRequestHandler.java b/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigBatchFuzzyWatchRequestHandler.java similarity index 70% rename from config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigBatchFuzzyListenRequestHandler.java rename to config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigBatchFuzzyWatchRequestHandler.java index 0a344ba2123..d14fd53100c 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigBatchFuzzyListenRequestHandler.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigBatchFuzzyWatchRequestHandler.java @@ -16,16 +16,13 @@ package com.alibaba.nacos.config.server.remote; -import com.alibaba.nacos.api.config.remote.request.ConfigBatchFuzzyListenRequest; -import com.alibaba.nacos.api.config.remote.response.ConfigBatchFuzzyListenResponse; +import com.alibaba.nacos.api.config.remote.request.ConfigBatchFuzzyWatchRequest; +import com.alibaba.nacos.api.config.remote.response.ConfigBatchFuzzyWatchResponse; import com.alibaba.nacos.api.exception.NacosException; 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.common.utils.CollectionUtils; -import com.alibaba.nacos.common.utils.GroupKeyPattern; import com.alibaba.nacos.config.server.model.event.ConfigBatchFuzzyListenEvent; -import com.alibaba.nacos.config.server.utils.GroupKey; import com.alibaba.nacos.core.control.TpsControl; import com.alibaba.nacos.core.paramcheck.ExtractorManager; import com.alibaba.nacos.core.paramcheck.impl.ConfigBatchFuzzyListenRequestParamsExtractor; @@ -37,7 +34,6 @@ import org.springframework.stereotype.Component; import java.util.Set; -import java.util.stream.Collectors; /** * Handler for processing batch fuzzy listen requests. @@ -51,14 +47,14 @@ * @date 2024/3/4 */ @Component -public class ConfigBatchFuzzyListenRequestHandler - extends RequestHandler { +public class ConfigBatchFuzzyWatchRequestHandler + extends RequestHandler { /** * Context for managing fuzzy listen changes. */ @Autowired - private ConfigChangeListenContext configChangeListenContext; + private ConfigFuzzyWatchContext configFuzzyWatchContext; /** * Handles the batch fuzzy listen request. @@ -76,32 +72,25 @@ public class ConfigBatchFuzzyListenRequestHandler @TpsControl(pointName = "ConfigFuzzyListen") @Secured(action = ActionTypes.READ, signType = SignType.CONFIG) @ExtractorManager.Extractor(rpcExtractor = ConfigBatchFuzzyListenRequestParamsExtractor.class) - public ConfigBatchFuzzyListenResponse handle(ConfigBatchFuzzyListenRequest request, RequestMeta meta) + public ConfigBatchFuzzyWatchResponse handle(ConfigBatchFuzzyWatchRequest request, RequestMeta meta) throws NacosException { String connectionId = StringPool.get(meta.getConnectionId()); - for (ConfigBatchFuzzyListenRequest.Context context : request.getContexts()) { - String groupKeyPattern = GroupKeyPattern.generateFuzzyListenGroupKeyPattern(context.getDataIdPattern(), - context.getGroup(), context.getTenant()); - groupKeyPattern = StringPool.get(groupKeyPattern); + for (ConfigBatchFuzzyWatchRequest.Context context : request.getContexts()) { + String groupKeyPattern = context.getGroupKeyPattern(); if (context.isListen()) { // Add client to the fuzzy listening context - configChangeListenContext.addFuzzyListen(groupKeyPattern, connectionId); + configFuzzyWatchContext.addFuzzyListen(groupKeyPattern, connectionId); // Get existing group keys for the client and publish initialization event - Set clientExistingGroupKeys = null; - if (CollectionUtils.isNotEmpty(context.getDataIds())) { - clientExistingGroupKeys = context.getDataIds().stream() - .map(dataId -> GroupKey.getKeyTenant(dataId, context.getGroup(), context.getTenant())) - .collect(Collectors.toSet()); - } + Set clientExistingGroupKeys = context.getReceivedGroupKeys(); NotifyCenter.publishEvent( new ConfigBatchFuzzyListenEvent(connectionId, clientExistingGroupKeys, groupKeyPattern, context.isInitializing())); } else { // Remove client from the fuzzy listening context - configChangeListenContext.removeFuzzyListen(groupKeyPattern, connectionId); + configFuzzyWatchContext.removeFuzzyListen(groupKeyPattern, connectionId); } } // Return response - return new ConfigBatchFuzzyListenResponse(); + return new ConfigBatchFuzzyWatchResponse(); } } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigChangeListenContext.java b/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigChangeListenContext.java index c71ef288f72..adf7453558a 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigChangeListenContext.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigChangeListenContext.java @@ -17,7 +17,6 @@ package com.alibaba.nacos.config.server.remote; import com.alibaba.nacos.common.utils.CollectionUtils; -import com.alibaba.nacos.common.utils.GroupKeyPattern; import org.springframework.stereotype.Component; import java.util.Collection; @@ -37,100 +36,18 @@ @Component public class ConfigChangeListenContext { - /** - * groupKeyPattern -> connection set. - */ - private final Map> keyPatternContext = new ConcurrentHashMap<>(); - /** * groupKey-> connection set. */ - private final ConcurrentHashMap> groupKeyContext = new ConcurrentHashMap<>(); + private ConcurrentHashMap> groupKeyContext = new ConcurrentHashMap<>(); /** * connectionId-> group key set. */ - private final ConcurrentHashMap> connectionIdContext = new ConcurrentHashMap<>(); + private ConcurrentHashMap> connectionIdContext = new ConcurrentHashMap<>(); /** - * Adds a fuzzy listen connection ID associated with the specified group key pattern. If the key pattern does not - * exist in the context, a new entry will be created. If the key pattern already exists, the connection ID will be - * added to the existing set. - * - * @param groupKeyPattern The group key pattern to associate with the listen connection. - * @param connectId The connection ID to be added. - */ - public synchronized void addFuzzyListen(String groupKeyPattern, String connectId) { - // Add the connection ID to the set associated with the key pattern in keyPatternContext - keyPatternContext.computeIfAbsent(groupKeyPattern, k -> new HashSet<>()).add(connectId); - } - - /** - * Removes a fuzzy listen connection ID associated with the specified group key pattern. If the group key pattern - * exists in the context and the connection ID is found in the associated set, the connection ID will be removed - * from the set. If the set becomes empty after removal, the entry for the group key pattern will be removed from - * the context. - * - * @param groupKeyPattern The group key pattern associated with the listen connection to be removed. - * @param connectionId The connection ID to be removed. - */ - public synchronized void removeFuzzyListen(String groupKeyPattern, String connectionId) { - // Retrieve the set of connection IDs associated with the group key pattern - Set connectIds = keyPatternContext.get(groupKeyPattern); - if (CollectionUtils.isNotEmpty(connectIds)) { - // Remove the connection ID from the set if it exists - connectIds.remove(connectionId); - // Remove the entry for the group key pattern if the set becomes empty after removal - if (connectIds.isEmpty()) { - keyPatternContext.remove(groupKeyPattern); - } - } - } - - /** - * Retrieves the set of fuzzy listen connection IDs associated with the specified group key pattern. - * - * @param groupKeyPattern The group key pattern to retrieve the associated connection IDs. - * @return The set of connection IDs associated with the group key pattern, or null if no connections are found. - */ - public synchronized Set getFuzzyListeners(String groupKeyPattern) { - // Retrieve the set of connection IDs associated with the group key pattern - Set connectionIds = keyPatternContext.get(groupKeyPattern); - // If the set is not empty, create a new set and safely copy the connection IDs into it - if (CollectionUtils.isNotEmpty(connectionIds)) { - Set listenConnections = new HashSet<>(); - safeCopy(connectionIds, listenConnections); - return listenConnections; - } - // Return null if no connections are found for the specified group key pattern - return null; - } - - /** - * Retrieves the set of connection IDs matched with the specified group key. - * - * @param groupKey The group key to match with the key patterns. - * @return The set of connection IDs matched with the group key. - */ - public Set getConnectIdMatchedPatterns(String groupKey) { - // Initialize a set to store the matched connection IDs - Set connectIds = new HashSet<>(); - // Iterate over each key pattern in the context - for (String keyPattern : keyPatternContext.keySet()) { - // Check if the group key matches the current key pattern - if (GroupKeyPattern.isMatchPatternWithNamespace(groupKey, keyPattern)) { - // If matched, add the associated connection IDs to the set - Set connectIdSet = keyPatternContext.get(keyPattern); - if (CollectionUtils.isNotEmpty(connectIdSet)) { - connectIds.addAll(connectIdSet); - } - } - } - return connectIds; - } - - /** - * Add listen. + * add listen. * * @param groupKey groupKey. * @param connectionId connectionId. @@ -210,7 +127,7 @@ public synchronized void clearContextForConnectionId(final String connectionId) return; } for (Map.Entry groupKey : listenKeys.entrySet()) { - + Set connectionIds = groupKeyContext.get(groupKey.getKey()); if (CollectionUtils.isNotEmpty(connectionIds)) { connectionIds.remove(connectionId); @@ -220,23 +137,9 @@ public synchronized void clearContextForConnectionId(final String connectionId) } else { groupKeyContext.remove(groupKey.getKey()); } - + } connectionIdContext.remove(connectionId); - - // Remove any remaining fuzzy listen connections - for (Map.Entry> keyPatternContextEntry : keyPatternContext.entrySet()) { - String keyPattern = keyPatternContextEntry.getKey(); - Set connectionIds = keyPatternContextEntry.getValue(); - if (CollectionUtils.isEmpty(connectionIds)) { - keyPatternContext.remove(keyPattern); - } else { - connectionIds.remove(keyPattern); - if (CollectionUtils.isEmpty(connectionIds)) { - keyPatternContext.remove(keyPattern); - } - } - } } /** diff --git a/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigFuzzyWatchContext.java b/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigFuzzyWatchContext.java new file mode 100644 index 00000000000..6a13408c259 --- /dev/null +++ b/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigFuzzyWatchContext.java @@ -0,0 +1,98 @@ +package com.alibaba.nacos.config.server.remote; + + +import com.alibaba.nacos.common.utils.CollectionUtils; +import com.alibaba.nacos.common.utils.FuzzyGroupKeyPattern; +import com.alibaba.nacos.config.server.utils.GroupKey2; +import org.springframework.stereotype.Component; + +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +@Component +public class ConfigFuzzyWatchContext { + + /** + * groupKeyPattern -> connection set. + */ + private final Map> keyPatternContext = new ConcurrentHashMap<>(); + + /** + * Adds a fuzzy listen connection ID associated with the specified group key pattern. If the key pattern does not + * exist in the context, a new entry will be created. If the key pattern already exists, the connection ID will be + * added to the existing set. + * + * @param groupKeyPattern The group key pattern to associate with the listen connection. + * @param connectId The connection ID to be added. + */ + public synchronized void addFuzzyListen(String groupKeyPattern, String connectId) { + // Add the connection ID to the set associated with the key pattern in keyPatternContext + keyPatternContext.computeIfAbsent(groupKeyPattern, k -> new HashSet<>()).add(connectId); + } + + /** + * Removes a fuzzy listen connection ID associated with the specified group key pattern. If the group key pattern + * exists in the context and the connection ID is found in the associated set, the connection ID will be removed + * from the set. If the set becomes empty after removal, the entry for the group key pattern will be removed from + * the context. + * + * @param groupKeyPattern The group key pattern associated with the listen connection to be removed. + * @param connectionId The connection ID to be removed. + */ + public synchronized void removeFuzzyListen(String groupKeyPattern, String connectionId) { + // Retrieve the set of connection IDs associated with the group key pattern + Set connectIds = keyPatternContext.get(groupKeyPattern); + if (CollectionUtils.isNotEmpty(connectIds)) { + // Remove the connection ID from the set if it exists + connectIds.remove(connectionId); + // Remove the entry for the group key pattern if the set becomes empty after removal + if (connectIds.isEmpty()) { + keyPatternContext.remove(groupKeyPattern); + } + } + } + + + public void removeFuzzyWatchContext(String connectionId){ + for (Map.Entry> keyPatternContextEntry : keyPatternContext.entrySet()) { + String keyPattern = keyPatternContextEntry.getKey(); + Set connectionIds = keyPatternContextEntry.getValue(); + if (CollectionUtils.isEmpty(connectionIds)) { + keyPatternContext.remove(keyPattern); + } else { + connectionIds.remove(connectionId); + if (CollectionUtils.isEmpty(connectionIds)) { + keyPatternContext.remove(keyPattern); + } + } + } + } + + /** + * Retrieves the set of connection IDs matched with the specified group key. + * + * @param groupKey The group key to match with the key patterns. + * @return The set of connection IDs matched with the group key. + */ + public Set getConnectIdMatchedPatterns(String groupKey) { + // Initialize a set to store the matched connection IDs + Set connectIds = new HashSet<>(); + // Iterate over each key pattern in the context + for (String keyPattern : keyPatternContext.keySet()) { + // Check if the group key matches the current key pattern + String[] strings = GroupKey2.parseKey(groupKey); + + if (FuzzyGroupKeyPattern.matchPattern(groupKey,strings[0],strings[1],strings[2])) { + // If matched, add the associated connection IDs to the set + Set connectIdSet = keyPatternContext.get(keyPattern); + if (CollectionUtils.isNotEmpty(connectIdSet)) { + connectIds.addAll(connectIdSet); + } + } + } + return connectIds; + } + +} diff --git a/config/src/main/java/com/alibaba/nacos/config/server/remote/RpcFuzzyListenConfigDiffNotifier.java b/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigFuzzyWatchDiffNotifier.java similarity index 85% rename from config/src/main/java/com/alibaba/nacos/config/server/remote/RpcFuzzyListenConfigDiffNotifier.java rename to config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigFuzzyWatchDiffNotifier.java index 67e7a46de42..8ccddb7c96a 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/remote/RpcFuzzyListenConfigDiffNotifier.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigFuzzyWatchDiffNotifier.java @@ -17,13 +17,12 @@ package com.alibaba.nacos.config.server.remote; import com.alibaba.nacos.api.common.Constants; -import com.alibaba.nacos.api.config.remote.request.FuzzyListenNotifyDiffRequest; +import com.alibaba.nacos.api.config.remote.request.FuzzyWatchNotifyDiffRequest; 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; import com.alibaba.nacos.common.utils.CollectionUtils; -import com.alibaba.nacos.common.utils.GroupKeyPattern; import com.alibaba.nacos.config.server.configuration.ConfigCommonConfig; import com.alibaba.nacos.config.server.model.event.ConfigBatchFuzzyListenEvent; import com.alibaba.nacos.config.server.service.ConfigCacheService; @@ -55,8 +54,8 @@ * @author stone-98 * @date 2024/3/18 */ -@Component(value = "rpcFuzzyListenConfigDiffNotifier") -public class RpcFuzzyListenConfigDiffNotifier extends Subscriber { +@Component(value = "configFuzzyWatchDiffNotifier") +public class ConfigFuzzyWatchDiffNotifier extends Subscriber { private static final String FUZZY_LISTEN_CONFIG_DIFF_PUSH = "FUZZY_LISTEN_CONFIG_DIFF_PUSH_COUNT"; @@ -70,7 +69,7 @@ public class RpcFuzzyListenConfigDiffNotifier extends Subscriber matchGroupKeys = ConfigCacheService.matchClientEffectiveGroupKeys(event.getKeyGroupPattern(), - clientIp, clientTag); + Set matchGroupKeys = ConfigCacheService.matchGroupKeys(event.getGroupKeyPattern()); // Retrieve existing group keys for the client from the event Set clientExistingGroupKeys = event.getClientExistingGroupKeys(); @@ -141,7 +138,7 @@ public void onEvent(ConfigBatchFuzzyListenEvent event) { // Calculate and merge configuration states based on matched and existing group keys List configStates = calculateAndMergeToConfigState(matchGroupKeys, clientExistingGroupKeys); - // If no config states are available, return + // If no diff config states are available, return if (CollectionUtils.isEmpty(configStates)) { return; } @@ -157,29 +154,26 @@ public void onEvent(ConfigBatchFuzzyListenEvent event) { // Iterate over each batch of config states for (List configStateList : divideConfigStatesIntoBatches) { // Map config states to FuzzyListenNotifyDiffRequest.Context objects - Set contexts = configStateList.stream().map(state -> { + Set contexts = configStateList.stream().map(state -> { String[] parseKey = GroupKey.parseKey(state.getGroupKey()); String dataId = parseKey[0]; String group = parseKey[1]; String tenant = parseKey.length > 2 ? parseKey[2] : Constants.DEFAULT_NAMESPACE_ID; - String changeType = event.isInitializing() ? Constants.ConfigChangeType.LISTEN_INIT - : (state.isExist() ? Constants.ConfigChangeType.ADD_CONFIG - : Constants.ConfigChangeType.DELETE_CONFIG); - return FuzzyListenNotifyDiffRequest.Context.build(tenant, group, dataId, changeType); + String changeType = event.isInitializing() ? Constants.FUZZY_WATCH_INIT_NOTIFY + : (state.isExist() ? Constants.ConfigChangedType.ADD_CONFIG + : Constants.ConfigChangedType.DELETE_CONFIG); + return FuzzyWatchNotifyDiffRequest.Context.build(tenant, group, dataId, changeType); }).collect(Collectors.toSet()); - // Remove namespace from the pattern - String patternWithoutNameSpace = GroupKeyPattern.getPatternRemovedNamespace(event.getKeyGroupPattern()); - // Build FuzzyListenNotifyDiffRequest with contexts and pattern - FuzzyListenNotifyDiffRequest request = FuzzyListenNotifyDiffRequest.buildInitRequest(contexts, - patternWithoutNameSpace); + FuzzyWatchNotifyDiffRequest request = FuzzyWatchNotifyDiffRequest.buildInitRequest(contexts, + event.getGroupKeyPattern()); int maxPushRetryTimes = ConfigCommonConfig.getInstance().getMaxPushRetryTimes(); // Create RPC push task and push the request to the client - RpcPushTask rpcPushTask = new RpcPushTask(request, pushBatchFinishCount, originBatchSize, maxPushRetryTimes, - event.getClientId(), clientIp, metaInfo.getAppName()); - push(rpcPushTask, connectionManager); + FuzzyWatchRpcPushTask fuzzyWatchRpcPushTask = new FuzzyWatchRpcPushTask(request, pushBatchFinishCount, originBatchSize, maxPushRetryTimes, + event.getConnectionId(), clientIp, metaInfo.getAppName()); + push(fuzzyWatchRpcPushTask, connectionManager); } } @@ -305,12 +299,12 @@ public void setExist(boolean exist) { /** * Represents a task for pushing FuzzyListenNotifyDiffRequest to clients. */ - class RpcPushTask implements Runnable { + class FuzzyWatchRpcPushTask implements Runnable { /** * The FuzzyListenNotifyDiffRequest to be pushed. */ - FuzzyListenNotifyDiffRequest notifyRequest; + FuzzyWatchNotifyDiffRequest notifyRequest; /** * The maximum number of times to retry pushing the request. @@ -358,7 +352,7 @@ class RpcPushTask implements Runnable { * @param clientIp The IP address of the client * @param appName The name of the client's application */ - public RpcPushTask(FuzzyListenNotifyDiffRequest notifyRequest, AtomicInteger pushBatchFinishCount, + public FuzzyWatchRpcPushTask(FuzzyWatchNotifyDiffRequest notifyRequest, AtomicInteger pushBatchFinishCount, int originBatchSize, int maxRetryTimes, String connectionId, String clientIp, String appName) { this.notifyRequest = notifyRequest; this.pushBatchFinishCount = pushBatchFinishCount; @@ -391,7 +385,7 @@ public void run() { push(this, connectionManager); } else { rpcPushService.pushWithCallback(connectionId, notifyRequest, - new RpcPushCallback(this, tpsControlManager, connectionManager, pushBatchFinishCount, + new FuzzyWatchRpcPushCallback(this, tpsControlManager, connectionManager, pushBatchFinishCount, originBatchSize), ConfigExecutor.getClientConfigNotifierServiceExecutor()); } } @@ -400,12 +394,12 @@ public void run() { /** * Represents a callback for handling the result of an RPC push operation. */ - class RpcPushCallback extends AbstractPushCallBack { + class FuzzyWatchRpcPushCallback extends AbstractPushCallBack { /** * The RpcPushTask associated with the callback. */ - RpcPushTask rpcPushTask; + FuzzyWatchRpcPushTask fuzzyWatchRpcPushTask; /** * The TpsControlManager for checking TPS limits. @@ -430,17 +424,17 @@ class RpcPushCallback extends AbstractPushCallBack { /** * Constructs a new RpcPushCallback with the specified parameters. * - * @param rpcPushTask The RpcPushTask associated with the callback + * @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 pushBatchCount The counter for tracking the number of pushed batches * @param originBatchSize The original size of the batch before splitting */ - public RpcPushCallback(RpcPushTask rpcPushTask, TpsControlManager tpsControlManager, + public FuzzyWatchRpcPushCallback(FuzzyWatchRpcPushTask fuzzyWatchRpcPushTask, TpsControlManager tpsControlManager, ConnectionManager connectionManager, AtomicInteger pushBatchCount, int originBatchSize) { // Set the timeout for the callback super(3000L); - this.rpcPushTask = rpcPushTask; + this.fuzzyWatchRpcPushTask = fuzzyWatchRpcPushTask; this.tpsControlManager = tpsControlManager; this.connectionManager = connectionManager; this.pushBatchCount = pushBatchCount; @@ -460,10 +454,10 @@ public void onSuccess() { if (pushBatchCount.get() < originBatchSize) { pushBatchCount.incrementAndGet(); } else if (pushBatchCount.get() == originBatchSize) { - FuzzyListenNotifyDiffRequest request = FuzzyListenNotifyDiffRequest.buildInitFinishRequest( - rpcPushTask.notifyRequest.getGroupKeyPattern()); - push(new RpcPushTask(request, pushBatchCount, originBatchSize, 50, rpcPushTask.connectionId, - rpcPushTask.clientIp, rpcPushTask.appName), connectionManager); + FuzzyWatchNotifyDiffRequest request = FuzzyWatchNotifyDiffRequest.buildInitFinishRequest( + fuzzyWatchRpcPushTask.notifyRequest.getGroupKeyPattern()); + push(new FuzzyWatchRpcPushTask(request, pushBatchCount, originBatchSize, 50, fuzzyWatchRpcPushTask.connectionId, + fuzzyWatchRpcPushTask.clientIp, fuzzyWatchRpcPushTask.appName), connectionManager); } } @@ -481,8 +475,8 @@ public void onFail(Throwable e) { // Log the failure and retry the task Loggers.REMOTE_PUSH.warn("Push fail, groupKeyPattern={}, clientId={}", - rpcPushTask.notifyRequest.getGroupKeyPattern(), rpcPushTask.connectionId, e); - push(rpcPushTask, connectionManager); + fuzzyWatchRpcPushTask.notifyRequest.getGroupKeyPattern(), fuzzyWatchRpcPushTask.connectionId, e); + push(fuzzyWatchRpcPushTask, connectionManager); } } } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/remote/RpcFuzzyListenConfigChangeNotifier.java b/config/src/main/java/com/alibaba/nacos/config/server/remote/RpcFuzzyListenConfigChangeNotifier.java index 3724a0eaf69..7db486b8b84 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/remote/RpcFuzzyListenConfigChangeNotifier.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/remote/RpcFuzzyListenConfigChangeNotifier.java @@ -16,7 +16,7 @@ package com.alibaba.nacos.config.server.remote; -import com.alibaba.nacos.api.config.remote.request.FuzzyListenNotifyChangeRequest; +import com.alibaba.nacos.api.config.remote.request.FuzzyWatchNotifyChangeRequest; import com.alibaba.nacos.api.remote.AbstractPushCallBack; import com.alibaba.nacos.common.notify.Event; import com.alibaba.nacos.common.notify.NotifyCenter; @@ -62,6 +62,7 @@ public class RpcFuzzyListenConfigChangeNotifier extends Subscriber 2 ? parseKey[2] : ""; - for (String clientId : configChangeListenContext.getConnectIdMatchedPatterns(groupKey)) { + for (String clientId : configFuzzyWatchContext.getConnectIdMatchedPatterns(groupKey)) { Connection connection = connectionManager.getConnection(clientId); if (null == connection) { Loggers.REMOTE_PUSH.warn( @@ -96,10 +98,9 @@ public void onEvent(LocalDataChangeEvent event) { } ConnectionMeta metaInfo = connection.getMetaInfo(); String clientIp = metaInfo.getClientIp(); - String clientTag = metaInfo.getTag(); String appName = metaInfo.getAppName(); - boolean exists = ConfigCacheService.containsAndEffectiveForClient(groupKey, clientIp, clientTag); - FuzzyListenNotifyChangeRequest request = new FuzzyListenNotifyChangeRequest(tenant, group, dataId, exists); + boolean exists = ConfigCacheService.getContentCache(groupKey)!=null; + FuzzyWatchNotifyChangeRequest request = new FuzzyWatchNotifyChangeRequest(tenant, group, dataId, exists); int maxPushRetryTimes = ConfigCommonConfig.getInstance().getMaxPushRetryTimes(); RpcPushTask rpcPushTask = new RpcPushTask(request, maxPushRetryTimes, clientId, clientIp, appName); push(rpcPushTask); @@ -117,7 +118,7 @@ public Class subscribeType() { * @param retryTask The task for retrying to push notification. */ private void push(RpcPushTask retryTask) { - FuzzyListenNotifyChangeRequest notifyRequest = retryTask.notifyRequest; + FuzzyWatchNotifyChangeRequest notifyRequest = retryTask.notifyRequest; if (retryTask.isOverTimes()) { Loggers.REMOTE_PUSH.warn( "push callback retry fail over times. dataId={},group={},tenant={},clientId={}, will unregister client.", @@ -142,7 +143,7 @@ private void push(RpcPushTask retryTask) { */ class RpcPushTask implements Runnable { - FuzzyListenNotifyChangeRequest notifyRequest; + FuzzyWatchNotifyChangeRequest notifyRequest; int maxRetryTimes; @@ -163,7 +164,7 @@ class RpcPushTask implements Runnable { * @param clientIp The IP address of the client. * @param appName The name of the application. */ - public RpcPushTask(FuzzyListenNotifyChangeRequest notifyRequest, int maxRetryTimes, String connectionId, + public RpcPushTask(FuzzyWatchNotifyChangeRequest notifyRequest, int maxRetryTimes, String connectionId, String clientIp, String appName) { this.notifyRequest = notifyRequest; this.maxRetryTimes = maxRetryTimes; diff --git a/config/src/main/java/com/alibaba/nacos/config/server/service/ConfigCacheService.java b/config/src/main/java/com/alibaba/nacos/config/server/service/ConfigCacheService.java index e6e0fe3f060..37f3cc4591b 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/service/ConfigCacheService.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/service/ConfigCacheService.java @@ -17,9 +17,7 @@ package com.alibaba.nacos.config.server.service; import com.alibaba.nacos.common.notify.NotifyCenter; -import com.alibaba.nacos.common.utils.CollectionUtils; -import com.alibaba.nacos.common.utils.GroupKeyPattern; -import com.alibaba.nacos.common.utils.InternetAddressUtil; +import com.alibaba.nacos.common.utils.FuzzyGroupKeyPattern; import com.alibaba.nacos.common.utils.MD5Utils; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.config.server.model.CacheItem; @@ -34,12 +32,9 @@ import com.alibaba.nacos.sys.env.EnvUtil; import java.io.IOException; -import java.util.Collections; -import java.util.List; import java.util.Map; import java.util.Set; import java.util.HashMap; -import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; @@ -80,33 +75,17 @@ public static int groupCount() { * Matches the client effective group keys based on the specified group key pattern, client IP, and tag. * * @param groupKeyPattern The pattern to match group keys. - * @param clientIp The IP address of the client. - * @param tag The tag associated with the configuration. * @return A set of group keys that match the pattern and are effective for the client. */ - public static Set matchClientEffectiveGroupKeys(String groupKeyPattern, String clientIp, String tag) { + public static Set matchGroupKeys(String groupKeyPattern) { + + String[] groupKeyItems = GroupKey2.parseKey(groupKeyPattern); return CACHE.entrySet().stream() - .filter(entry -> GroupKeyPattern.isMatchPatternWithNamespace(entry.getKey(), groupKeyPattern)) - .filter(entry -> entry.getValue().effectiveForClient(tag, clientIp)).map(Map.Entry::getKey) + .filter(entry -> FuzzyGroupKeyPattern.matchPattern(groupKeyPattern,groupKeyItems[0],groupKeyItems[1],groupKeyItems[2])) + .map(Map.Entry::getKey) .collect(Collectors.toSet()); } - /** - * Checks if the specified group key is present in the cache and effective for the client. - * - * @param groupKey The group key to check. - * @param clientIp The IP address of the client. - * @param tag The tag associated with the configuration. - * @return true if the group key is present in the cache and effective for the client, false otherwise. - */ - public static boolean containsAndEffectiveForClient(String groupKey, String clientIp, String tag) { - if (!CACHE.containsKey(groupKey)) { - return false; - } - CacheItem cacheItem = CACHE.get(groupKey); - return cacheItem.effectiveForClient(tag, clientIp); - } - /** * Save config file and update md5 value in cache. * @@ -239,6 +218,8 @@ public static boolean dumpGray(String dataId, String group, String tenant, Strin //check timestamp long localGrayLastModifiedTs = ConfigCacheService.getGrayLastModifiedTs(groupKey, grayName); + + boolean timestampOutdated = lastModifiedTs < localGrayLastModifiedTs; if (timestampOutdated) { DUMP_LOG.warn("[dump-gray-ignore] timestamp is outdated,groupKey={}", groupKey); return true; diff --git a/core/src/main/java/com/alibaba/nacos/core/paramcheck/impl/ConfigBatchFuzzyListenRequestParamsExtractor.java b/core/src/main/java/com/alibaba/nacos/core/paramcheck/impl/ConfigBatchFuzzyListenRequestParamsExtractor.java index 73c3c01b834..64f7eeec996 100644 --- a/core/src/main/java/com/alibaba/nacos/core/paramcheck/impl/ConfigBatchFuzzyListenRequestParamsExtractor.java +++ b/core/src/main/java/com/alibaba/nacos/core/paramcheck/impl/ConfigBatchFuzzyListenRequestParamsExtractor.java @@ -16,7 +16,7 @@ package com.alibaba.nacos.core.paramcheck.impl; -import com.alibaba.nacos.api.config.remote.request.ConfigBatchFuzzyListenRequest; +import com.alibaba.nacos.api.config.remote.request.ConfigBatchFuzzyWatchRequest; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.remote.request.Request; import com.alibaba.nacos.common.paramcheck.ParamInfo; @@ -28,7 +28,7 @@ import java.util.Set; /** - * Extractor for parameters of {@link ConfigBatchFuzzyListenRequest}. This extractor retrieves parameter information + * Extractor for parameters of {@link ConfigBatchFuzzyWatchRequest}. This extractor retrieves parameter information * from the request object and constructs {@link ParamInfo} instances representing the namespace ID, group, and data IDs * contained in the request's contexts. * @@ -46,13 +46,13 @@ public class ConfigBatchFuzzyListenRequestParamsExtractor extends AbstractRpcPar */ @Override public List extractParam(Request request) throws NacosException { - ConfigBatchFuzzyListenRequest req = (ConfigBatchFuzzyListenRequest) request; - Set contexts = req.getContexts(); + ConfigBatchFuzzyWatchRequest req = (ConfigBatchFuzzyWatchRequest) request; + Set contexts = req.getContexts(); List paramInfos = new ArrayList<>(); if (contexts == null) { return paramInfos; } - for (ConfigBatchFuzzyListenRequest.Context context : contexts) { + for (ConfigBatchFuzzyWatchRequest.Context context : contexts) { // Extract namespace ID and group from the context ParamInfo paramInfo1 = new ParamInfo(); paramInfo1.setNamespaceId(context.getTenant()); diff --git a/example/src/main/java/com/alibaba/nacos/example/FuzzyListenExample.java b/example/src/main/java/com/alibaba/nacos/example/FuzzyListenExample.java index 40622c4c4d7..516c9a50971 100644 --- a/example/src/main/java/com/alibaba/nacos/example/FuzzyListenExample.java +++ b/example/src/main/java/com/alibaba/nacos/example/FuzzyListenExample.java @@ -18,8 +18,8 @@ import com.alibaba.nacos.api.config.ConfigFactory; import com.alibaba.nacos.api.config.ConfigService; -import com.alibaba.nacos.api.config.listener.AbstractFuzzyListenListener; -import com.alibaba.nacos.api.config.listener.FuzzyListenConfigChangeEvent; +import com.alibaba.nacos.api.config.listener.AbstractFuzzyWatchListener; +import com.alibaba.nacos.api.config.listener.ConfigFuzzyWatchChangeEvent; import com.alibaba.nacos.api.exception.NacosException; import java.util.Properties; @@ -66,15 +66,15 @@ public static void main(String[] args) throws NacosException, InterruptedExcepti } // Define a fuzzy listener to handle configuration changes - AbstractFuzzyListenListener listener = new AbstractFuzzyListenListener() { + AbstractFuzzyWatchListener listener = new AbstractFuzzyWatchListener() { @Override - public void onEvent(FuzzyListenConfigChangeEvent event) { + public void onEvent(ConfigFuzzyWatchChangeEvent event) { System.out.println("[fuzzy listen config change]" + event.toString()); } }; // Add the fuzzy listener to monitor configurations starting with "test" - configService.addFuzzyListener("test*", "DEFAULT_GROUP", listener); + configService.fuzzyWatch("test*", "DEFAULT_GROUP", listener); System.out.println("[Fuzzy listening started.]"); // Publish more configurations to trigger the listener diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/client/AbstractClient.java b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/client/AbstractClient.java index 4c967a4b1c7..078b1ed0049 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/client/AbstractClient.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/client/AbstractClient.java @@ -48,7 +48,7 @@ public abstract class AbstractClient implements Client { protected final ConcurrentHashMap subscribers = new ConcurrentHashMap<>(16, 0.75f, 1); - protected final ConcurrentHashSet watchedPattern = new ConcurrentHashSet<>(); + protected final ConcurrentHashSet fuzzyWatchedPattern = new ConcurrentHashSet<>(); protected volatile long lastUpdatedTime; @@ -139,7 +139,7 @@ public Collection getAllSubscribeService() { @Override public boolean addWatchedPattern(String watchPattern) { - if (watchedPattern.add(watchPattern)) { + if (fuzzyWatchedPattern.add(watchPattern)) { // TODO:Watch MetricsMonitor return true; } @@ -148,7 +148,7 @@ public boolean addWatchedPattern(String watchPattern) { @Override public boolean removeWatchedPattern(String watchPattern) { - if (watchedPattern.remove(watchPattern)) { + if (fuzzyWatchedPattern.remove(watchPattern)) { // TODO:Watch MetricsMonitor return true; } @@ -157,12 +157,12 @@ public boolean removeWatchedPattern(String watchPattern) { @Override public boolean isWatchedPattern(String watchPattern) { - return watchedPattern.contains(watchPattern); + return fuzzyWatchedPattern.contains(watchPattern); } @Override - public Collection getAllFuzzyWatchPattern() { - return watchedPattern; + public Collection getFuzzyWatchedPattern() { + return fuzzyWatchedPattern; } @Override diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/client/Client.java b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/client/Client.java index 94bfb33f7a9..a5311a6086b 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/client/Client.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/client/Client.java @@ -151,7 +151,7 @@ public interface Client { * * @return watch patterns */ - Collection getAllFuzzyWatchPattern(); + Collection getFuzzyWatchedPattern(); /** * Generate sync data. diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/index/ClientFuzzyWatchIndexesManager.java b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/index/ClientFuzzyWatchIndexesManager.java new file mode 100644 index 00000000000..128bd6440f3 --- /dev/null +++ b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/index/ClientFuzzyWatchIndexesManager.java @@ -0,0 +1,169 @@ +package com.alibaba.nacos.naming.core.v2.index; + +import com.alibaba.nacos.api.naming.utils.NamingUtils; +import com.alibaba.nacos.common.notify.Event; +import com.alibaba.nacos.common.notify.NotifyCenter; +import com.alibaba.nacos.common.notify.listener.SmartSubscriber; +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.naming.core.v2.ServiceManager; +import com.alibaba.nacos.naming.core.v2.client.Client; +import com.alibaba.nacos.naming.core.v2.event.client.ClientOperationEvent; +import com.alibaba.nacos.naming.core.v2.event.service.ServiceEvent; +import com.alibaba.nacos.naming.core.v2.pojo.Service; +import com.alibaba.nacos.naming.misc.Loggers; +import org.springframework.stereotype.Component; + +import java.util.Collection; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +import static com.alibaba.nacos.common.utils.FuzzyGroupKeyPattern.getNamespaceFromGroupKeyPattern; + +@Component +public class ClientFuzzyWatchIndexesManager extends SmartSubscriber { + + /** + * The content of map is {fuzzy watch pattern -> Set[watcher clientID]}. + */ + private final ConcurrentMap> fuzzyWatcherIndexes = new ConcurrentHashMap<>(); + + /** + * The content of map is {service -> Set[matched fuzzy watch patterns]}. + */ + private final ConcurrentMap> fuzzyWatchPatternMatchIndexes = new ConcurrentHashMap<>(); + + + @Override + public List> subscribeTypes() { + List> result = new LinkedList<>(); + result.add(ClientOperationEvent.ClientFuzzyWatchEvent.class); + result.add(ClientOperationEvent.ClientCancelFuzzyWatchEvent.class); + result.add(ClientOperationEvent.ClientReleaseEvent.class); + result.add(ClientOperationEvent.ClientRegisterServiceEvent.class); + return result; + } + + @Override + public void onEvent(Event event) { + if (!(event instanceof ClientOperationEvent)){ + return; + } + ClientOperationEvent clientOperationEvent=(ClientOperationEvent)event; + String clientId = clientOperationEvent.getClientId(); + + if (event instanceof ClientOperationEvent.ClientRegisterServiceEvent) { + + } else if (event instanceof ClientOperationEvent.ClientFuzzyWatchEvent) { + String completedPattern = ((ClientOperationEvent.ClientFuzzyWatchEvent) event).getPattern(); + addFuzzyWatcherIndexes(completedPattern, clientId); + } else if (event instanceof ClientOperationEvent.ClientCancelFuzzyWatchEvent) { + String completedPattern = ((ClientOperationEvent.ClientCancelFuzzyWatchEvent) event).getPattern(); + removeFuzzyWatcherIndexes(completedPattern, clientId); + } else if(event instanceof ClientOperationEvent.ClientReleaseEvent){ + ClientOperationEvent.ClientReleaseEvent clientReleaseEvent=(ClientOperationEvent.ClientReleaseEvent)event; + Client client = clientReleaseEvent.getClient(); + for (String eachPattern : client.getFuzzyWatchedPattern()) { + removeFuzzyWatcherIndexes(eachPattern, client.getClientId()); + } + } + } + + public Collection getServiceMatchedPatterns(Service service) { + return fuzzyWatchPatternMatchIndexes.containsKey(service) + ? fuzzyWatchPatternMatchIndexes.get(service) : new ConcurrentHashSet<>(); + } + + public Collection getAllClientFuzzyWatchedPattern(String pattern) { + return fuzzyWatcherIndexes.containsKey(pattern) ? fuzzyWatcherIndexes.get(pattern) : new ConcurrentHashSet<>(); + } + + + /** + * This method will build/update the fuzzy watch match index of all patterns. + * + * @param service The service of the Nacos. + */ + public void updateWatchMatchIndex(Service service) { + long matchBeginTime = System.currentTimeMillis(); + Set filteredPattern = FuzzyGroupKeyPattern.filterMatchedPatterns(fuzzyWatcherIndexes.keySet(),service.getName(),service.getGroup(),service.getNamespace()); + + if (CollectionUtils.isNotEmpty(filteredPattern)) { + fuzzyWatchPatternMatchIndexes.computeIfAbsent(service, key -> new ConcurrentHashSet<>()); + for (String each : filteredPattern) { + fuzzyWatchPatternMatchIndexes.get(service).add(each); + } + Loggers.PERFORMANCE_LOG.info("WATCH: new service {} match {} pattern, {}ms", service.getGroupedServiceName(), + fuzzyWatchPatternMatchIndexes.size(), System.currentTimeMillis() - matchBeginTime); + } + } + + public void removeWatchMatchIndex(Service service) { + fuzzyWatchPatternMatchIndexes.remove(service); + } + + + private void addFuzzyWatcherIndexes(String completedPattern, String clientId) { + fuzzyWatcherIndexes.computeIfAbsent(completedPattern, key -> new ConcurrentHashSet<>()); + fuzzyWatcherIndexes.get(completedPattern).add(clientId); + Collection matchedService = updateWatchMatchIndex(completedPattern); + NotifyCenter.publishEvent(new ServiceEvent.ServiceFuzzyWatchInitEvent(clientId, completedPattern, matchedService)); + } + + private void removeFuzzyWatcherIndexes(String completedPattern, String clientId) { + if (!fuzzyWatcherIndexes.containsKey(completedPattern)) { + return; + } + fuzzyWatcherIndexes.get(completedPattern).remove(clientId); + if (fuzzyWatcherIndexes.get(completedPattern).isEmpty()) { + fuzzyWatcherIndexes.remove(completedPattern); + } + } + + /** + * This method will remove the match index of fuzzy watch pattern. + * + * @param service The service of the Nacos. + * @param matchedPattern the pattern to remove + */ + public void removeWatchPatternMatchIndex(Service service, String matchedPattern) { + if (!fuzzyWatchPatternMatchIndexes.containsKey(service)) { + return; + } + fuzzyWatchPatternMatchIndexes.get(service).remove(matchedPattern); + if (fuzzyWatchPatternMatchIndexes.get(service).isEmpty()) { + fuzzyWatchPatternMatchIndexes.remove(service); + } + } + + /** + * This method will build/update the fuzzy watch match index for given patterns. + * + * @param completedPattern the completed pattern of watch (with namespace id). + * @return Updated set of services in Nacos server that can match this pattern. + */ + public Collection updateWatchMatchIndex(String completedPattern) { + long matchBeginTime = System.currentTimeMillis(); + Collection serviceSet = ServiceManager.getInstance().getSingletons(getNamespaceFromGroupKeyPattern(completedPattern)); + + Set matchedService = new HashSet<>(); + for (Service service : serviceSet) { + String serviceName = service.getName(); + String groupName = service.getGroup(); + if (FuzzyGroupKeyPattern.matchPattern(completedPattern,service.getNamespace(),groupName,serviceName)) { + fuzzyWatchPatternMatchIndexes.computeIfAbsent(service, key -> new ConcurrentHashSet<>()); + fuzzyWatchPatternMatchIndexes.get(service).add(completedPattern); + matchedService.add(service); + } + } + Loggers.PERFORMANCE_LOG.info("WATCH: pattern {} match {} services, {}ms", completedPattern, + matchedService.size(), System.currentTimeMillis() - matchBeginTime); + return matchedService; + } + +} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/index/ClientServiceIndexesManager.java b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/index/ClientServiceIndexesManager.java index c5506e2dc4f..a12f3b09171 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/index/ClientServiceIndexesManager.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/index/ClientServiceIndexesManager.java @@ -55,15 +55,11 @@ public class ClientServiceIndexesManager extends SmartSubscriber { private final ConcurrentMap> subscriberIndexes = new ConcurrentHashMap<>(); - /** - * The content of map is {fuzzy watch pattern -> Set[watcher clientID]}. - */ - private final ConcurrentMap> fuzzyWatcherIndexes = new ConcurrentHashMap<>(); + private ClientFuzzyWatchIndexesManager clientFuzzyWatchIndexesManager; - /** - * The content of map is {service -> Set[matched fuzzy watch patterns]}. - */ - private final ConcurrentMap> fuzzyWatchPatternMatchIndexes = new ConcurrentHashMap<>(); + public ClientServiceIndexesManager(ClientFuzzyWatchIndexesManager clientFuzzyWatchIndexesManager){ + this.clientFuzzyWatchIndexesManager=clientFuzzyWatchIndexesManager; + } public ClientServiceIndexesManager() { NotifyCenter.registerSubscriber(this, NamingEventPublisherFactory.getInstance()); @@ -80,16 +76,7 @@ public Collection getAllClientsSubscribeService(Service service) { public Collection getSubscribedService() { return subscriberIndexes.keySet(); } - - public Collection getServiceMatchedPatterns(Service service) { - return fuzzyWatchPatternMatchIndexes.containsKey(service) - ? fuzzyWatchPatternMatchIndexes.get(service) : new ConcurrentHashSet<>(); - } - - public Collection getAllClientFuzzyWatchedPattern(String pattern) { - return fuzzyWatcherIndexes.containsKey(pattern) ? fuzzyWatcherIndexes.get(pattern) : new ConcurrentHashSet<>(); - } - + /** * Clear the service index without instances. * @@ -98,7 +85,7 @@ public Collection getAllClientFuzzyWatchedPattern(String pattern) { public void removePublisherIndexesByEmptyService(Service service) { if (publisherIndexes.containsKey(service) && publisherIndexes.get(service).isEmpty()) { publisherIndexes.remove(service); - fuzzyWatchPatternMatchIndexes.remove(service); + clientFuzzyWatchIndexesManager.removeWatchMatchIndex(service); } } @@ -129,9 +116,7 @@ private void handleClientDisconnect(ClientOperationEvent.ClientReleaseEvent even for (Service each : client.getAllSubscribeService()) { removeSubscriberIndexes(each, client.getClientId()); } - for (String eachPattern : client.getAllFuzzyWatchPattern()) { - removeFuzzyWatcherIndexes(eachPattern, client.getClientId()); - } + DeregisterInstanceReason reason = event.isNative() ? DeregisterInstanceReason.NATIVE_DISCONNECTED : DeregisterInstanceReason.SYNCED_DISCONNECTED; long currentTimeMillis = System.currentTimeMillis(); @@ -155,12 +140,6 @@ private void handleClientOperation(ClientOperationEvent event) { addSubscriberIndexes(service, clientId); } else if (event instanceof ClientOperationEvent.ClientUnsubscribeServiceEvent) { removeSubscriberIndexes(service, clientId); - } else if (event instanceof ClientOperationEvent.ClientFuzzyWatchEvent) { - String completedPattern = ((ClientOperationEvent.ClientFuzzyWatchEvent) event).getPattern(); - addFuzzyWatcherIndexes(completedPattern, clientId); - } else if (event instanceof ClientOperationEvent.ClientCancelFuzzyWatchEvent) { - String completedPattern = ((ClientOperationEvent.ClientCancelFuzzyWatchEvent) event).getPattern(); - removeFuzzyWatcherIndexes(completedPattern, clientId); } } @@ -168,7 +147,7 @@ private void addPublisherIndexes(Service service, String clientId) { String serviceChangedType = Constants.ServiceChangedType.INSTANCE_CHANGED; if (!publisherIndexes.containsKey(service)) { // The only time the index needs to be updated is when the service is first created - updateWatchMatchIndex(service); + clientFuzzyWatchIndexesManager.updateWatchMatchIndex(service); serviceChangedType = Constants.ServiceChangedType.ADD_SERVICE; } NotifyCenter.publishEvent(new ServiceEvent.ServiceChangedEvent(service, serviceChangedType, true)); @@ -204,85 +183,4 @@ private void removeSubscriberIndexes(Service service, String clientId) { } } - private void addFuzzyWatcherIndexes(String completedPattern, String clientId) { - fuzzyWatcherIndexes.computeIfAbsent(completedPattern, key -> new ConcurrentHashSet<>()); - fuzzyWatcherIndexes.get(completedPattern).add(clientId); - Collection matchedService = updateWatchMatchIndex(completedPattern); - NotifyCenter.publishEvent(new ServiceEvent.ServiceFuzzyWatchInitEvent(clientId, completedPattern, matchedService)); - } - - private void removeFuzzyWatcherIndexes(String completedPattern, String clientId) { - if (!fuzzyWatcherIndexes.containsKey(completedPattern)) { - return; - } - fuzzyWatcherIndexes.get(completedPattern).remove(clientId); - if (fuzzyWatcherIndexes.get(completedPattern).isEmpty()) { - fuzzyWatcherIndexes.remove(completedPattern); - } - } - - /** - * This method will build/update the fuzzy watch match index of all patterns. - * - * @param service The service of the Nacos. - */ - public void updateWatchMatchIndex(Service service) { - long matchBeginTime = System.currentTimeMillis(); - Set filteredPattern = NamingUtils.filterPatternWithNamespace(service.getNamespace(), fuzzyWatcherIndexes.keySet()); - Set matchedPattern = NamingUtils.getServiceMatchedPatterns(service.getName(), service.getGroup(), - filteredPattern); - if (CollectionUtils.isNotEmpty(matchedPattern)) { - fuzzyWatchPatternMatchIndexes.computeIfAbsent(service, key -> new ConcurrentHashSet<>()); - for (String each : matchedPattern) { - fuzzyWatchPatternMatchIndexes.get(service).add(NamingUtils.getPatternWithNamespace(service.getNamespace(), each)); - } - Loggers.PERFORMANCE_LOG.info("WATCH: new service {} match {} pattern, {}ms", service.getGroupedServiceName(), - matchedPattern.size(), System.currentTimeMillis() - matchBeginTime); - } - } - - /** - * This method will build/update the fuzzy watch match index for given patterns. - * - * @param completedPattern the completed pattern of watch (with namespace id). - * @return Updated set of services in Nacos server that can match this pattern. - */ - public Collection updateWatchMatchIndex(String completedPattern) { - long matchBeginTime = System.currentTimeMillis(); - String namespaceId = NamingUtils.getNamespaceFromPattern(completedPattern); - Collection serviceSet = ServiceManager.getInstance().getSingletons(namespaceId); - String pattern = NamingUtils.getPatternRemovedNamespace(completedPattern); - - Set matchedService = new HashSet<>(); - for (Service service : serviceSet) { - String serviceName = service.getName(); - String groupName = service.getGroup(); - String serviceNamePattern = NamingUtils.getServiceName(pattern); - String groupNamePattern = NamingUtils.getGroupName(pattern); - if (NamingUtils.isMatchPattern(serviceName, groupName, serviceNamePattern, groupNamePattern)) { - fuzzyWatchPatternMatchIndexes.computeIfAbsent(service, key -> new ConcurrentHashSet<>()); - fuzzyWatchPatternMatchIndexes.get(service).add(completedPattern); - matchedService.add(service); - } - } - Loggers.PERFORMANCE_LOG.info("WATCH: pattern {} match {} services, {}ms", completedPattern, - matchedService.size(), System.currentTimeMillis() - matchBeginTime); - return matchedService; - } - - /** - * This method will remove the match index of fuzzy watch pattern. - * - * @param service The service of the Nacos. - * @param matchedPattern the pattern to remove - */ - public void removeWatchPatternMatchIndex(Service service, String matchedPattern) { - if (!fuzzyWatchPatternMatchIndexes.containsKey(service)) { - return; - } - fuzzyWatchPatternMatchIndexes.get(service).remove(matchedPattern); - if (fuzzyWatchPatternMatchIndexes.get(service).isEmpty()) { - fuzzyWatchPatternMatchIndexes.remove(service); - } - } } diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/service/impl/EphemeralClientOperationServiceImpl.java b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/service/impl/EphemeralClientOperationServiceImpl.java index 1ef746435a0..760912660c9 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/service/impl/EphemeralClientOperationServiceImpl.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/service/impl/EphemeralClientOperationServiceImpl.java @@ -164,7 +164,14 @@ public void cancelFuzzyWatch(String namespaceId, String serviceNamePattern, Stri NotifyCenter.publishEvent(new ClientOperationEvent.ClientCancelFuzzyWatchEvent(completedPattern, clientId)); } - private boolean clientIsLegal(Client client, String clientId) { + private boolean clientIsLegal(Client client, String clientId){ + try{ + checkClientIsLegal( client, clientId); + return true; + }catch(NacosRuntimeException thr){ + return false; + } + } private void checkClientIsLegal(Client client, String clientId) { if (client == null) { diff --git a/naming/src/main/java/com/alibaba/nacos/naming/push/v2/NamingSubscriberServiceV2Impl.java b/naming/src/main/java/com/alibaba/nacos/naming/push/v2/NamingSubscriberServiceV2Impl.java index 95d5fe481f9..0416b528c3c 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/push/v2/NamingSubscriberServiceV2Impl.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/push/v2/NamingSubscriberServiceV2Impl.java @@ -26,6 +26,7 @@ import com.alibaba.nacos.naming.core.v2.client.manager.ClientManagerDelegate; import com.alibaba.nacos.naming.core.v2.event.publisher.NamingEventPublisherFactory; import com.alibaba.nacos.naming.core.v2.event.service.ServiceEvent; +import com.alibaba.nacos.naming.core.v2.index.ClientFuzzyWatchIndexesManager; import com.alibaba.nacos.naming.core.v2.index.ClientServiceIndexesManager; import com.alibaba.nacos.naming.core.v2.index.ServiceStorage; import com.alibaba.nacos.naming.core.v2.metadata.NamingMetadataManager; @@ -67,13 +68,13 @@ public class NamingSubscriberServiceV2Impl extends SmartSubscriber implements Na private final FuzzyWatchPushDelayTaskEngine fuzzyWatchPushDelayTaskEngine; public NamingSubscriberServiceV2Impl(ClientManagerDelegate clientManager, - ClientServiceIndexesManager indexesManager, ServiceStorage serviceStorage, + ClientServiceIndexesManager indexesManager, ClientFuzzyWatchIndexesManager clientFuzzyWatchIndexesManager, ServiceStorage serviceStorage, NamingMetadataManager metadataManager, PushExecutorDelegate pushExecutor, SwitchDomain switchDomain) { this.clientManager = clientManager; this.indexesManager = indexesManager; this.delayTaskEngine = new PushDelayTaskExecuteEngine(clientManager, indexesManager, serviceStorage, metadataManager, pushExecutor, switchDomain); - this.fuzzyWatchPushDelayTaskEngine = new FuzzyWatchPushDelayTaskEngine(clientManager, indexesManager, + this.fuzzyWatchPushDelayTaskEngine = new FuzzyWatchPushDelayTaskEngine(clientManager, clientFuzzyWatchIndexesManager, serviceStorage, metadataManager, pushExecutor, switchDomain); NotifyCenter.registerSubscriber(this, NamingEventPublisherFactory.getInstance()); diff --git a/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchNotifyChangeExecuteTask.java b/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchNotifyChangeExecuteTask.java index 49e1d7a07d2..4b9fbf1cc53 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchNotifyChangeExecuteTask.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchNotifyChangeExecuteTask.java @@ -21,6 +21,7 @@ import com.alibaba.nacos.common.task.AbstractExecuteTask; import com.alibaba.nacos.naming.core.v2.client.Client; import com.alibaba.nacos.naming.core.v2.client.manager.ClientManager; +import com.alibaba.nacos.naming.core.v2.index.ClientFuzzyWatchIndexesManager; import com.alibaba.nacos.naming.core.v2.index.ClientServiceIndexesManager; import com.alibaba.nacos.naming.core.v2.pojo.Service; import com.alibaba.nacos.naming.misc.Loggers; @@ -81,7 +82,7 @@ private Set getWatchTargetClientIds() { return delayTask.getTargetClients(); } Set watchNotifyClientIds = new HashSet<>(16); - ClientServiceIndexesManager indexesManager = delayTaskEngine.getIndexesManager(); + ClientFuzzyWatchIndexesManager indexesManager = delayTaskEngine.getClientFuzzyWatchIndexesManager(); // get match result from index Collection matchedPatterns = indexesManager.getServiceMatchedPatterns(service); diff --git a/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchPushDelayTaskEngine.java b/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchPushDelayTaskEngine.java index dba23e8bab5..8bb7aa0ceaa 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchPushDelayTaskEngine.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchPushDelayTaskEngine.java @@ -20,6 +20,7 @@ import com.alibaba.nacos.common.task.NacosTaskProcessor; import com.alibaba.nacos.common.task.engine.NacosDelayTaskExecuteEngine; import com.alibaba.nacos.naming.core.v2.client.manager.ClientManager; +import com.alibaba.nacos.naming.core.v2.index.ClientFuzzyWatchIndexesManager; import com.alibaba.nacos.naming.core.v2.index.ClientServiceIndexesManager; import com.alibaba.nacos.naming.core.v2.index.ServiceStorage; import com.alibaba.nacos.naming.core.v2.metadata.NamingMetadataManager; @@ -38,7 +39,8 @@ public class FuzzyWatchPushDelayTaskEngine extends NacosDelayTaskExecuteEngine { private final ClientManager clientManager; - private final ClientServiceIndexesManager indexesManager; + private final ClientFuzzyWatchIndexesManager clientFuzzyWatchIndexesManager; + private final ServiceStorage serviceStorage; @@ -48,12 +50,12 @@ public class FuzzyWatchPushDelayTaskEngine extends NacosDelayTaskExecuteEngine { private final SwitchDomain switchDomain; - public FuzzyWatchPushDelayTaskEngine(ClientManager clientManager, ClientServiceIndexesManager indexesManager, + public FuzzyWatchPushDelayTaskEngine(ClientManager clientManager, ClientFuzzyWatchIndexesManager clientFuzzyWatchIndexesManager, ServiceStorage serviceStorage, NamingMetadataManager metadataManager, PushExecutor pushExecutor, SwitchDomain switchDomain) { super(FuzzyWatchPushDelayTaskEngine.class.getSimpleName(), Loggers.PUSH); this.clientManager = clientManager; - this.indexesManager = indexesManager; + this.clientFuzzyWatchIndexesManager = clientFuzzyWatchIndexesManager; this.serviceStorage = serviceStorage; this.metadataManager = metadataManager; this.pushExecutor = pushExecutor; @@ -65,8 +67,8 @@ public ClientManager getClientManager() { return clientManager; } - public ClientServiceIndexesManager getIndexesManager() { - return indexesManager; + public ClientFuzzyWatchIndexesManager getClientFuzzyWatchIndexesManager() { + return clientFuzzyWatchIndexesManager; } public ServiceStorage getServiceStorage() { diff --git a/naming/src/main/java/com/alibaba/nacos/naming/remote/rpc/handler/FuzzyWatchRequestHandler.java b/naming/src/main/java/com/alibaba/nacos/naming/remote/rpc/handler/NamingFuzzyWatchRequestHandler.java similarity index 92% rename from naming/src/main/java/com/alibaba/nacos/naming/remote/rpc/handler/FuzzyWatchRequestHandler.java rename to naming/src/main/java/com/alibaba/nacos/naming/remote/rpc/handler/NamingFuzzyWatchRequestHandler.java index bd7f07f0147..4cc4a480973 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/remote/rpc/handler/FuzzyWatchRequestHandler.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/remote/rpc/handler/NamingFuzzyWatchRequestHandler.java @@ -33,11 +33,11 @@ * @author tanyongquan */ @Component("fuzzyWatchRequestHandler") -public class FuzzyWatchRequestHandler extends RequestHandler { +public class NamingFuzzyWatchRequestHandler extends RequestHandler { private final EphemeralClientOperationServiceImpl clientOperationService; - public FuzzyWatchRequestHandler(EphemeralClientOperationServiceImpl clientOperationService) { + public NamingFuzzyWatchRequestHandler(EphemeralClientOperationServiceImpl clientOperationService) { this.clientOperationService = clientOperationService; } diff --git a/naming/src/test/java/com/alibaba/nacos/naming/push/v2/NamingSubscriberServiceV2ImplTest.java b/naming/src/test/java/com/alibaba/nacos/naming/push/v2/NamingSubscriberServiceV2ImplTest.java index 2ee1f4c0386..fe40ea8c705 100644 --- a/naming/src/test/java/com/alibaba/nacos/naming/push/v2/NamingSubscriberServiceV2ImplTest.java +++ b/naming/src/test/java/com/alibaba/nacos/naming/push/v2/NamingSubscriberServiceV2ImplTest.java @@ -20,6 +20,7 @@ import com.alibaba.nacos.naming.core.v2.client.Client; import com.alibaba.nacos.naming.core.v2.client.manager.ClientManagerDelegate; import com.alibaba.nacos.naming.core.v2.event.service.ServiceEvent; +import com.alibaba.nacos.naming.core.v2.index.ClientFuzzyWatchIndexesManager; import com.alibaba.nacos.naming.core.v2.index.ClientServiceIndexesManager; import com.alibaba.nacos.naming.core.v2.pojo.Service; import com.alibaba.nacos.naming.misc.SwitchDomain; @@ -62,6 +63,9 @@ class NamingSubscriberServiceV2ImplTest { @Mock private ClientServiceIndexesManager indexesManager; + @Mock + private ClientFuzzyWatchIndexesManager clientFuzzyWatchIndexesManager; + @Mock private PushDelayTaskExecuteEngine delayTaskEngine; @@ -75,7 +79,7 @@ class NamingSubscriberServiceV2ImplTest { @BeforeEach void setUp() throws Exception { - subscriberService = new NamingSubscriberServiceV2Impl(clientManager, indexesManager, null, null, null, switchDomain); + subscriberService = new NamingSubscriberServiceV2Impl(clientManager, indexesManager, clientFuzzyWatchIndexesManager, null, null,null, switchDomain); ReflectionTestUtils.setField(subscriberService, "delayTaskEngine", delayTaskEngine); when(indexesManager.getAllClientsSubscribeService(service)).thenReturn(Collections.singletonList(testClientId)); when(indexesManager.getAllClientsSubscribeService(service1)).thenReturn(Collections.singletonList(testClientId)); diff --git a/pom.xml b/pom.xml index bd03acffbb1..add48427787 100644 --- a/pom.xml +++ b/pom.xml @@ -88,7 +88,7 @@ - 3.0.0-ALPHA + 3.0.0-ALPHA-SNAPSHOT UTF-8 UTF-8 From 2fd57006811509ee37d92f256de264fc324a6af9 Mon Sep 17 00:00:00 2001 From: "zunfei.lzf" Date: Mon, 30 Dec 2024 17:51:45 +0800 Subject: [PATCH 04/15] =?UTF-8?q?=E6=A8=A1=E7=B3=8A=E8=AE=A2=E9=98=85?= =?UTF-8?q?=E4=BB=A3=E7=A0=81=E8=B0=83=E6=95=B4=E7=AE=80=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../alibaba/nacos/api/common/Constants.java | 2 + .../nacos/api/config/ConfigService.java | 29 +-- .../listener/ConfigFuzzyWatchChangeEvent.java | 22 +- ...hListener.java => ConfigFuzzyWatcher.java} | 24 +- .../AbstractFuzzyWatchNotifyRequest.java | 14 -- ...ava => FuzzyWatchChangeNotifyRequest.java} | 22 +- ...st.java => FuzzyWatchDiffSyncRequest.java} | 76 ++++-- ...va => FuzzyWatchChangeNotifyResponse.java} | 2 +- .../AbstractFuzzyWatchNotifyRequest.java | 24 +- .../FuzzyWatchNotifyChangeRequest.java | 19 +- .../request/FuzzyWatchNotifyInitRequest.java | 16 +- .../remote/request/FuzzyWatchRequest.java | 43 ---- .../request/NamingFuzzyWatchRequest.java | 61 +++++ .../com.alibaba.nacos.api.remote.Payload | 8 +- .../client/config/NacosConfigService.java | 65 ++--- .../client/config/impl/ClientWorker.java | 234 ++++++++++-------- ...text.java => ConfigFuzzyWatchContext.java} | 228 ++++++++--------- ...yEvent.java => FuzzyWatchNotifyEvent.java} | 49 +--- .../cache/FuzzyWatchServiceListHolder.java | 2 +- .../remote/gprc/NamingGrpcClientProxy.java | 7 +- .../common/utils/FuzzyGroupKeyPattern.java | 97 +++++++- .../utils/FuzzyGroupKeyPatternTest.java | 2 +- .../ConfigBatchFuzzyWatchRequestHandler.java | 2 +- .../remote/ConfigFuzzyWatchDiffNotifier.java | 152 +++--------- ...va => FuzzyWatchConfigChangeNotifier.java} | 34 +-- .../nacos/example/FuzzyListenExample.java | 4 +- .../index/ClientFuzzyWatchIndexesManager.java | 5 +- .../EphemeralClientOperationServiceImpl.java | 7 +- .../naming/push/v2/executor/PushExecutor.java | 1 + .../v2/task/FuzzyWatchInitExecuteTask.java | 7 +- .../task/FuzzyWatchNotifyChangeDelayTask.java | 14 +- .../FuzzyWatchNotifyChangeExecuteTask.java | 9 +- .../NamingFuzzyWatchRequestHandler.java | 6 +- 33 files changed, 659 insertions(+), 628 deletions(-) rename api/src/main/java/com/alibaba/nacos/api/config/listener/{AbstractFuzzyWatchListener.java => ConfigFuzzyWatcher.java} (82%) rename api/src/main/java/com/alibaba/nacos/api/config/remote/request/{FuzzyWatchNotifyChangeRequest.java => FuzzyWatchChangeNotifyRequest.java} (82%) rename api/src/main/java/com/alibaba/nacos/api/config/remote/request/{FuzzyWatchNotifyDiffRequest.java => FuzzyWatchDiffSyncRequest.java} (62%) rename api/src/main/java/com/alibaba/nacos/api/config/remote/response/{FuzzyWatchNotifyChangeResponse.java => FuzzyWatchChangeNotifyResponse.java} (92%) delete mode 100644 api/src/main/java/com/alibaba/nacos/api/naming/remote/request/FuzzyWatchRequest.java create mode 100644 api/src/main/java/com/alibaba/nacos/api/naming/remote/request/NamingFuzzyWatchRequest.java rename client/src/main/java/com/alibaba/nacos/client/config/impl/{FuzzyListenContext.java => ConfigFuzzyWatchContext.java} (52%) rename client/src/main/java/com/alibaba/nacos/client/config/impl/{FuzzyListenNotifyEvent.java => FuzzyWatchNotifyEvent.java} (70%) rename config/src/main/java/com/alibaba/nacos/config/server/remote/{RpcFuzzyListenConfigChangeNotifier.java => FuzzyWatchConfigChangeNotifier.java} (87%) 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 0bce5f55fc5..ff32891bb09 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 @@ -282,6 +282,8 @@ public static class Naming { public static final String FINISH_FUZZY_WATCH_INIT_NOTIFY = "FINISH_FUZZY_WATCH_INIT_NOTIFY"; + public static final String FUZZY_WATCH_DIFF_SYNC_NOTIFY = "FUZZY_WATCH_DIFF_SYNC_NOTIFY"; + /** * The constants in config fuzzy watch event type directory. */ diff --git a/api/src/main/java/com/alibaba/nacos/api/config/ConfigService.java b/api/src/main/java/com/alibaba/nacos/api/config/ConfigService.java index f930331000f..870eba56694 100644 --- a/api/src/main/java/com/alibaba/nacos/api/config/ConfigService.java +++ b/api/src/main/java/com/alibaba/nacos/api/config/ConfigService.java @@ -17,11 +17,12 @@ package com.alibaba.nacos.api.config; import com.alibaba.nacos.api.config.filter.IConfigFilter; -import com.alibaba.nacos.api.config.listener.AbstractFuzzyWatchListener; +import com.alibaba.nacos.api.config.listener.ConfigFuzzyWatcher; import com.alibaba.nacos.api.config.listener.Listener; import com.alibaba.nacos.api.exception.NacosException; import java.util.Collection; +import java.util.Set; import java.util.concurrent.Future; /** @@ -176,7 +177,7 @@ boolean publishConfigCas(String dataId, String group, String content, String cas * @throws NacosException NacosException * @since 3.0 */ - void fuzzyWatch(String fixedGroupName, AbstractFuzzyWatchListener listener) throws NacosException; + void fuzzyWatch(String fixedGroupName, ConfigFuzzyWatcher listener) throws NacosException; /** * Add a fuzzy listener to the configuration. After the server modifies the configuration matching the specified @@ -185,11 +186,11 @@ boolean publishConfigCas(String dataId, String group, String content, String cas * * @param dataIdPattern The pattern to match dataIds for subscription. * @param fixedGroupName The fixed group name representing the group and dataId patterns to subscribe to. - * @param listener The fuzzy listener to be added. + * @param watcher The fuzzy listener to be added. * @throws NacosException NacosException * @since 3.0 */ - void fuzzyWatch(String dataIdPattern, String fixedGroupName, AbstractFuzzyWatchListener listener) + void fuzzyWatch(String dataIdPattern, String fixedGroupName, ConfigFuzzyWatcher watcher) throws NacosException; /** @@ -198,13 +199,13 @@ void fuzzyWatch(String dataIdPattern, String fixedGroupName, AbstractFuzzyWatchL * and dataId patterns specified for subscription. * * @param fixedGroupName The fixed group name representing the group and dataId patterns to subscribe to. - * @param listener The fuzzy listener to be added. + * @param watcher The fuzzy watcher to be added. * @return CompletableFuture containing collection of configs that match the specified fixed group name. * @throws NacosException NacosException * @since 3.0 */ - Future> fuzzyWatchWithGroupKeys(String fixedGroupName, - AbstractFuzzyWatchListener listener) throws NacosException; + Future> fuzzyWatchWithGroupKeys(String fixedGroupName, + ConfigFuzzyWatcher watcher) throws NacosException; /** * Add a fuzzy listener to the configuration and retrieve all configs that match the specified dataId pattern and @@ -212,35 +213,35 @@ Future> fuzzyWatchWithGroupKeys(String fixedGroupName, * * @param dataIdPattern The pattern to match dataIds for subscription. * @param fixedGroupName The fixed group name representing the group and dataId patterns to subscribe to. - * @param listener The fuzzy listener to be added. + * @param watcher The fuzzy watcher to be added. * @return CompletableFuture containing collection of configs that match the specified dataId pattern and fixed * group name. * @throws NacosException NacosException * @since 3.0 */ - Future> fuzzyWatchWithGroupKeys(String dataIdPattern, String fixedGroupName, - AbstractFuzzyWatchListener listener) throws NacosException; + Future> fuzzyWatchWithGroupKeys(String dataIdPattern, String fixedGroupName, + ConfigFuzzyWatcher watcher) throws NacosException; /** * Cancel fuzzy listen and remove the event listener for a specified fixed group name. * * @param fixedGroupName The fixed group name for fuzzy watch. - * @param listener The event listener to be removed. + * @param watcher The event watcher to be removed. * @throws NacosException If an error occurs during the cancellation process. * @since 3.0 */ - void cancelFuzzyListen(String fixedGroupName, AbstractFuzzyWatchListener listener) throws NacosException; + void cancelFuzzyWatch(String fixedGroupName, ConfigFuzzyWatcher watcher) throws NacosException; /** * Cancel fuzzy listen and remove the event listener for a specified service name pattern and fixed group name. * * @param dataIdPatter The pattern to match dataId for fuzzy watch. * @param fixedGroupName The fixed group name for fuzzy watch. - * @param listener The event listener to be removed. + * @param watcher The event listener to be removed. * @throws NacosException If an error occurs during the cancellation process. * @since 3.0 */ - void cancelFuzzyListen(String dataIdPatter, String fixedGroupName, AbstractFuzzyWatchListener listener) + void cancelFuzzyWatch(String dataIdPatter, String fixedGroupName, ConfigFuzzyWatcher watcher) throws NacosException; } diff --git a/api/src/main/java/com/alibaba/nacos/api/config/listener/ConfigFuzzyWatchChangeEvent.java b/api/src/main/java/com/alibaba/nacos/api/config/listener/ConfigFuzzyWatchChangeEvent.java index bf7acfb795b..5cac987f436 100644 --- a/api/src/main/java/com/alibaba/nacos/api/config/listener/ConfigFuzzyWatchChangeEvent.java +++ b/api/src/main/java/com/alibaba/nacos/api/config/listener/ConfigFuzzyWatchChangeEvent.java @@ -38,10 +38,13 @@ public class ConfigFuzzyWatchChangeEvent { */ private String dataId; + /** + * The namaspace of the configuration that has changed. + */ + private String namespace; - private String tenant; /** - * The type of change that has occurred (e.g., "ADD_CONFIG", "DELETE_CONFIG"). + * The type of change that has occurred ("FUZZY_WATCH_INIT_NOTIFY", "ADD_CONFIG", "DELETE_CONFIG"). * see {@link Constants.ConfigChangedType} */ private String changedType; @@ -59,9 +62,10 @@ public ConfigFuzzyWatchChangeEvent() { * @param dataId The data ID of the configuration that has changed * @param changedType The type of change that has occurred */ - public ConfigFuzzyWatchChangeEvent(String group, String dataId, String changedType) { + public ConfigFuzzyWatchChangeEvent(String namespace,String group, String dataId, String changedType) { this.group = group; this.dataId = dataId; + this.namespace=namespace; this.changedType = changedType; } @@ -73,8 +77,16 @@ public ConfigFuzzyWatchChangeEvent(String group, String dataId, String changedTy * @param type The type of change that has occurred * @return A new FuzzyListenConfigChangeEvent instance */ - public static ConfigFuzzyWatchChangeEvent build(String group, String dataId, String type) { - return new ConfigFuzzyWatchChangeEvent(group, dataId, type); + public static ConfigFuzzyWatchChangeEvent build(String namespace,String group, String dataId, String type) { + return new ConfigFuzzyWatchChangeEvent(namespace,group, dataId, type); + } + + public String getNamespace() { + return namespace; + } + + public void setNamespace(String namespace) { + this.namespace = namespace; } public String getGroup() { diff --git a/api/src/main/java/com/alibaba/nacos/api/config/listener/AbstractFuzzyWatchListener.java b/api/src/main/java/com/alibaba/nacos/api/config/listener/ConfigFuzzyWatcher.java similarity index 82% rename from api/src/main/java/com/alibaba/nacos/api/config/listener/AbstractFuzzyWatchListener.java rename to api/src/main/java/com/alibaba/nacos/api/config/listener/ConfigFuzzyWatcher.java index 5f8f6c9006d..1b1edc22d98 100644 --- a/api/src/main/java/com/alibaba/nacos/api/config/listener/AbstractFuzzyWatchListener.java +++ b/api/src/main/java/com/alibaba/nacos/api/config/listener/ConfigFuzzyWatcher.java @@ -16,8 +16,11 @@ package com.alibaba.nacos.api.config.listener; +import java.util.HashSet; import java.util.Objects; +import java.util.Set; import java.util.UUID; +import java.util.concurrent.Executor; /** * AbstractFuzzyListenListener is an abstract class that provides basic functionality for listening to fuzzy @@ -26,13 +29,15 @@ * @author stone-98 * @date 2024/3/4 */ -public abstract class AbstractFuzzyWatchListener extends AbstractListener { +public abstract class ConfigFuzzyWatcher { /** * Unique identifier for the listener. */ String uuid= UUID.randomUUID().toString(); + private Set syncGroupKeys = new HashSet<>(); + /** * Get the UUID (Unique Identifier) of the listener. * @@ -49,14 +54,14 @@ public String getUuid() { */ public abstract void onEvent(ConfigFuzzyWatchChangeEvent event); + /** - * Receive the configuration information. This method is overridden but does nothing in this abstract class. + * Get executor for execute this receive. * - * @param configInfo The configuration information + * @return Executor */ - @Override - public final void receiveConfigInfo(String configInfo) { - // Do nothing by default + public Executor getExecutor(){ + return null; } /** @@ -84,7 +89,12 @@ public boolean equals(Object o) { if (o == null || getClass() != o.getClass()) { return false; } - AbstractFuzzyWatchListener that = (AbstractFuzzyWatchListener) o; + ConfigFuzzyWatcher that = (ConfigFuzzyWatcher) o; return Objects.equals(uuid, that.uuid); } + + public Set getSyncGroupKeys() { + return syncGroupKeys; + } + } diff --git a/api/src/main/java/com/alibaba/nacos/api/config/remote/request/AbstractFuzzyWatchNotifyRequest.java b/api/src/main/java/com/alibaba/nacos/api/config/remote/request/AbstractFuzzyWatchNotifyRequest.java index f83c83406da..67a22264e70 100644 --- a/api/src/main/java/com/alibaba/nacos/api/config/remote/request/AbstractFuzzyWatchNotifyRequest.java +++ b/api/src/main/java/com/alibaba/nacos/api/config/remote/request/AbstractFuzzyWatchNotifyRequest.java @@ -28,23 +28,9 @@ */ public abstract class AbstractFuzzyWatchNotifyRequest extends ServerRequest { - private String serviceChangedType; - public AbstractFuzzyWatchNotifyRequest() { } - public AbstractFuzzyWatchNotifyRequest(String serviceChangedType) { - this.serviceChangedType = serviceChangedType; - } - - public String getServiceChangedType() { - return serviceChangedType; - } - - public void setServiceChangedType(String serviceChangedType) { - this.serviceChangedType = serviceChangedType; - } - @Override public String getModule() { return CONFIG_MODULE; diff --git a/api/src/main/java/com/alibaba/nacos/api/config/remote/request/FuzzyWatchNotifyChangeRequest.java b/api/src/main/java/com/alibaba/nacos/api/config/remote/request/FuzzyWatchChangeNotifyRequest.java similarity index 82% rename from api/src/main/java/com/alibaba/nacos/api/config/remote/request/FuzzyWatchNotifyChangeRequest.java rename to api/src/main/java/com/alibaba/nacos/api/config/remote/request/FuzzyWatchChangeNotifyRequest.java index 4397cbd5b08..f826b13a61c 100644 --- a/api/src/main/java/com/alibaba/nacos/api/config/remote/request/FuzzyWatchNotifyChangeRequest.java +++ b/api/src/main/java/com/alibaba/nacos/api/config/remote/request/FuzzyWatchChangeNotifyRequest.java @@ -24,12 +24,12 @@ * @author stone-98 * @date 2024/3/13 */ -public class FuzzyWatchNotifyChangeRequest extends AbstractFuzzyWatchNotifyRequest { +public class FuzzyWatchChangeNotifyRequest extends AbstractFuzzyWatchNotifyRequest { /** * The tenant of the configuration that has changed. */ - private String tenant; + private String namespace; /** * The group of the configuration that has changed. @@ -49,30 +49,30 @@ public class FuzzyWatchNotifyChangeRequest extends AbstractFuzzyWatchNotifyReque /** * Constructs an empty FuzzyListenNotifyChangeRequest. */ - public FuzzyWatchNotifyChangeRequest() { + public FuzzyWatchChangeNotifyRequest() { } /** * Constructs a FuzzyListenNotifyChangeRequest with the specified parameters. * - * @param tenant The tenant of the configuration that has changed + * @param namespace The tenant of the configuration that has changed * @param group The group of the configuration that has changed * @param dataId The data ID of the configuration that has changed * @param isExist Indicates whether the configuration exists or not */ - public FuzzyWatchNotifyChangeRequest(String tenant, String group, String dataId, boolean isExist) { - this.tenant = tenant; + public FuzzyWatchChangeNotifyRequest(String namespace, String group, String dataId, boolean isExist) { + this.namespace = namespace; this.group = group; this.dataId = dataId; this.isExist = isExist; } - public String getTenant() { - return tenant; + public String getNamespace() { + return namespace; } - public void setTenant(String tenant) { - this.tenant = tenant; + public void setNamespace(String namespace) { + this.namespace = namespace; } public String getGroup() { @@ -106,7 +106,7 @@ public void setExist(boolean exist) { */ @Override public String toString() { - return "FuzzyListenNotifyChangeRequest{" + "tenant='" + tenant + '\'' + ", group='" + group + '\'' + return "FuzzyListenNotifyChangeRequest{" + "tenant='" + namespace + '\'' + ", group='" + group + '\'' + ", dataId='" + dataId + '\'' + ", isExist=" + isExist + '}'; } diff --git a/api/src/main/java/com/alibaba/nacos/api/config/remote/request/FuzzyWatchNotifyDiffRequest.java b/api/src/main/java/com/alibaba/nacos/api/config/remote/request/FuzzyWatchDiffSyncRequest.java similarity index 62% rename from api/src/main/java/com/alibaba/nacos/api/config/remote/request/FuzzyWatchNotifyDiffRequest.java rename to api/src/main/java/com/alibaba/nacos/api/config/remote/request/FuzzyWatchDiffSyncRequest.java index 02e8598a35a..52c74843f9b 100644 --- a/api/src/main/java/com/alibaba/nacos/api/config/remote/request/FuzzyWatchNotifyDiffRequest.java +++ b/api/src/main/java/com/alibaba/nacos/api/config/remote/request/FuzzyWatchDiffSyncRequest.java @@ -23,7 +23,7 @@ import java.util.Set; /** - * Represents a request to notify the difference in configurations for fuzzy listening. + * Represents a request to notify the difference between client and server side. * *

This request is used to notify clients about the difference in configurations that match fuzzy listening * patterns. @@ -31,7 +31,7 @@ * @author stone-98 * @date 2024/3/6 */ -public class FuzzyWatchNotifyDiffRequest extends AbstractFuzzyWatchNotifyRequest { +public class FuzzyWatchDiffSyncRequest extends AbstractFuzzyWatchNotifyRequest { /** * The pattern used to match group keys for the configurations. @@ -43,23 +43,36 @@ public class FuzzyWatchNotifyDiffRequest extends AbstractFuzzyWatchNotifyRequest */ private Set contexts; + /** + *see FUZZY_WATCH_INIT_NOTIFY,FINISH_FUZZY_WATCH_INIT_NOTIFY,FUZZY_WATCH_DIFF_SYNC_NOTIFY + */ + private String syncType; + + public String getSyncType() { + return syncType; + } + + public void setSyncType(String syncType) { + this.syncType = syncType; + } + /** * Constructs an empty FuzzyListenNotifyDiffRequest. */ - public FuzzyWatchNotifyDiffRequest() { + public FuzzyWatchDiffSyncRequest() { } /** * Constructs a FuzzyListenNotifyDiffRequest with the specified parameters. * - * @param serviceChangedType The type of service change - * @param groupKeyPattern The pattern used to match group keys for the configurations - * @param contexts The set of contexts containing information about the configurations + * @param groupKeyPattern The pattern used to match group keys for the configurations + * @param contexts The set of contexts containing information about the configurations */ - public FuzzyWatchNotifyDiffRequest(String serviceChangedType, String groupKeyPattern, Set contexts) { - super(serviceChangedType); + public FuzzyWatchDiffSyncRequest(String syncType, String groupKeyPattern, Set contexts) { this.groupKeyPattern = groupKeyPattern; this.contexts = contexts; + this.syncType = syncType; + } /** @@ -69,8 +82,19 @@ public FuzzyWatchNotifyDiffRequest(String serviceChangedType, String groupKeyPat * @param groupKeyPattern The pattern used to match group keys for the configurations * @return An initial FuzzyListenNotifyDiffRequest */ - public static FuzzyWatchNotifyDiffRequest buildInitRequest(Set contexts, String groupKeyPattern) { - return new FuzzyWatchNotifyDiffRequest(Constants.FUZZY_WATCH_INIT_NOTIFY, groupKeyPattern, contexts); + public static FuzzyWatchDiffSyncRequest buildInitRequest(Set contexts, String groupKeyPattern) { + return new FuzzyWatchDiffSyncRequest(Constants.FUZZY_WATCH_INIT_NOTIFY, groupKeyPattern, contexts); + } + + /** + * Builds an initial FuzzyListenNotifyDiffRequest with the specified set of contexts and group key pattern. + * + * @param contexts The set of contexts containing information about the configurations + * @param groupKeyPattern The pattern used to match group keys for the configurations + * @return An initial FuzzyListenNotifyDiffRequest + */ + public static FuzzyWatchDiffSyncRequest buildDiffSyncRequest(Set contexts, String groupKeyPattern) { + return new FuzzyWatchDiffSyncRequest(Constants.FUZZY_WATCH_DIFF_SYNC_NOTIFY, groupKeyPattern, contexts); } /** @@ -79,8 +103,8 @@ public static FuzzyWatchNotifyDiffRequest buildInitRequest(Set contexts * @param groupKeyPattern The pattern used to match group keys for the configurations * @return A final FuzzyListenNotifyDiffRequest */ - public static FuzzyWatchNotifyDiffRequest buildInitFinishRequest(String groupKeyPattern) { - return new FuzzyWatchNotifyDiffRequest(Constants.FINISH_FUZZY_WATCH_INIT_NOTIFY, groupKeyPattern, + public static FuzzyWatchDiffSyncRequest buildInitFinishRequest(String groupKeyPattern) { + return new FuzzyWatchDiffSyncRequest(Constants.FINISH_FUZZY_WATCH_INIT_NOTIFY, groupKeyPattern, new HashSet<>()); } @@ -111,7 +135,11 @@ public static class Context { private String dataId; - private String type; + /** + * see {@link com.alibaba.nacos.api.common.Constants.ConfigChangedType ADD_CONFIG&} ADD_CONFIG: a new config + * should be added for clientside . DELETE_CONFIG: a config should be removed for clientside . + */ + private String changedType; /** * Constructs an empty Context object. @@ -122,18 +150,18 @@ public Context() { /** * Builds a new context object with the provided parameters. * - * @param tenant The tenant associated with the configuration. - * @param group The group associated with the configuration. - * @param dataId The data ID of the configuration. - * @param type The type of the configuration change event. + * @param tenant The tenant associated with the configuration. + * @param group The group associated with the configuration. + * @param dataId The data ID of the configuration. + * @param changedType The type of the configuration change event. * @return A new context object initialized with the provided parameters. */ - public static Context build(String tenant, String group, String dataId, String type) { + public static Context build(String tenant, String group, String dataId, String changedType) { Context context = new Context(); context.setTenant(tenant); context.setGroup(group); context.setDataId(dataId); - context.setType(type); + context.setChangedType(changedType); return context; } @@ -152,7 +180,7 @@ public boolean equals(Object o) { } Context that = (Context) o; return Objects.equals(tenant, that.tenant) && Objects.equals(group, that.group) && Objects.equals(dataId, - that.dataId) && Objects.equals(type, that.type); + that.dataId) && Objects.equals(changedType, that.changedType); } public String getTenant() { @@ -179,12 +207,12 @@ public void setDataId(String dataId) { this.dataId = dataId; } - public String getType() { - return type; + public String getChangedType() { + return changedType; } - public void setType(String type) { - this.type = type; + public void setChangedType(String changedType) { + this.changedType = changedType; } } diff --git a/api/src/main/java/com/alibaba/nacos/api/config/remote/response/FuzzyWatchNotifyChangeResponse.java b/api/src/main/java/com/alibaba/nacos/api/config/remote/response/FuzzyWatchChangeNotifyResponse.java similarity index 92% rename from api/src/main/java/com/alibaba/nacos/api/config/remote/response/FuzzyWatchNotifyChangeResponse.java rename to api/src/main/java/com/alibaba/nacos/api/config/remote/response/FuzzyWatchChangeNotifyResponse.java index 6ad06ef0f9b..e0c76bed799 100644 --- a/api/src/main/java/com/alibaba/nacos/api/config/remote/response/FuzzyWatchNotifyChangeResponse.java +++ b/api/src/main/java/com/alibaba/nacos/api/config/remote/response/FuzzyWatchChangeNotifyResponse.java @@ -24,6 +24,6 @@ * @author stone-98 * @date 2024/3/18 */ -public class FuzzyWatchNotifyChangeResponse extends Response { +public class FuzzyWatchChangeNotifyResponse extends Response { } diff --git a/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/AbstractFuzzyWatchNotifyRequest.java b/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/AbstractFuzzyWatchNotifyRequest.java index b1678a8001d..5ea72b78f3f 100644 --- a/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/AbstractFuzzyWatchNotifyRequest.java +++ b/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/AbstractFuzzyWatchNotifyRequest.java @@ -26,32 +26,22 @@ * @author tanyongquan */ public abstract class AbstractFuzzyWatchNotifyRequest extends ServerRequest { - private String namespace; - private String serviceChangedType; + private String changedType; public AbstractFuzzyWatchNotifyRequest(){ } - public AbstractFuzzyWatchNotifyRequest(String namespace, String serviceChangedType) { - this.namespace = namespace; - this.serviceChangedType = serviceChangedType; + public AbstractFuzzyWatchNotifyRequest( String changedType) { + this.changedType = changedType; } - public String getNamespace() { - return namespace; + public String getChangedType() { + return changedType; } - public void setNamespace(String namespace) { - this.namespace = namespace; - } - - public String getServiceChangedType() { - return serviceChangedType; - } - - public void setServiceChangedType(String serviceChangedType) { - this.serviceChangedType = serviceChangedType; + public void setChangedType(String changedType) { + this.changedType = changedType; } @Override diff --git a/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/FuzzyWatchNotifyChangeRequest.java b/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/FuzzyWatchNotifyChangeRequest.java index bf31ecb58cd..1290de0825a 100644 --- a/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/FuzzyWatchNotifyChangeRequest.java +++ b/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/FuzzyWatchNotifyChangeRequest.java @@ -16,6 +16,10 @@ package com.alibaba.nacos.api.naming.remote.request; +import com.alibaba.nacos.api.remote.request.ServerRequest; + +import static com.alibaba.nacos.api.common.Constants.Naming.NAMING_MODULE; + /** * Nacos fuzzy watch notify service change request, use it when one of the services changes. * @@ -23,6 +27,9 @@ */ public class FuzzyWatchNotifyChangeRequest extends AbstractFuzzyWatchNotifyRequest { + + private String namespace; + String serviceName; String groupName; @@ -32,11 +39,20 @@ public FuzzyWatchNotifyChangeRequest() { public FuzzyWatchNotifyChangeRequest(String namespace, String serviceName, String groupName, String serviceChangedType) { - super(namespace, serviceChangedType); + super(serviceChangedType); + this.namespace=namespace; this.serviceName = serviceName; this.groupName = groupName; } + public String getNamespace() { + return namespace; + } + + public void setNamespace(String namespace) { + this.namespace = namespace; + } + public String getServiceName() { return serviceName; } @@ -52,4 +68,5 @@ public String getGroupName() { public void setGroupName(String groupName) { this.groupName = groupName; } + } diff --git a/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/FuzzyWatchNotifyInitRequest.java b/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/FuzzyWatchNotifyInitRequest.java index 7a1aa020d65..9fe7715880f 100644 --- a/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/FuzzyWatchNotifyInitRequest.java +++ b/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/FuzzyWatchNotifyInitRequest.java @@ -17,10 +17,13 @@ package com.alibaba.nacos.api.naming.remote.request; import com.alibaba.nacos.api.common.Constants; +import com.alibaba.nacos.api.remote.request.ServerRequest; import java.util.Collection; import java.util.HashSet; +import static com.alibaba.nacos.api.common.Constants.Naming.NAMING_MODULE; + /** * Nacos fuzzy watch initial notify request, use it when init a watch request, push service by batch. * @@ -35,18 +38,18 @@ public class FuzzyWatchNotifyInitRequest extends AbstractFuzzyWatchNotifyRequest public FuzzyWatchNotifyInitRequest() { } - private FuzzyWatchNotifyInitRequest(String namespace, String pattern, String serviceChangedType, Collection servicesName) { - super(namespace, serviceChangedType); + private FuzzyWatchNotifyInitRequest(String pattern, String changedType, Collection servicesName) { + super(changedType); this.servicesName = servicesName; this.pattern = pattern; } - public static FuzzyWatchNotifyInitRequest buildInitRequest(String namespace, String pattern, Collection servicesName) { - return new FuzzyWatchNotifyInitRequest(namespace, pattern, Constants.FUZZY_WATCH_INIT_NOTIFY, servicesName); + public static FuzzyWatchNotifyInitRequest buildInitRequest(String pattern, Collection servicesName) { + return new FuzzyWatchNotifyInitRequest(pattern, Constants.FUZZY_WATCH_INIT_NOTIFY, servicesName); } - public static FuzzyWatchNotifyInitRequest buildInitFinishRequest(String namespace, String pattern) { - return new FuzzyWatchNotifyInitRequest(namespace, pattern, Constants.FINISH_FUZZY_WATCH_INIT_NOTIFY, new HashSet<>(1)); + public static FuzzyWatchNotifyInitRequest buildInitFinishRequest(String pattern) { + return new FuzzyWatchNotifyInitRequest(pattern, Constants.FINISH_FUZZY_WATCH_INIT_NOTIFY, new HashSet<>(1)); } public String getPattern() { @@ -64,4 +67,5 @@ public Collection getServicesName() { public void setServicesName(Collection servicesName) { this.servicesName = servicesName; } + } diff --git a/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/FuzzyWatchRequest.java b/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/FuzzyWatchRequest.java deleted file mode 100644 index dd5015abd8b..00000000000 --- a/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/FuzzyWatchRequest.java +++ /dev/null @@ -1,43 +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.api.naming.remote.request; - -/** - * Nacos naming fuzzy watch service request. - * - * @author tanyongquan - */ -public class FuzzyWatchRequest extends AbstractNamingRequest { - - private String type; - - public FuzzyWatchRequest() { - } - - public FuzzyWatchRequest(String namespace, String serviceNamePattern, String groupNamePattern, String type) { - super(namespace, serviceNamePattern, groupNamePattern); - this.type = type; - } - - public void setType(String type) { - this.type = type; - } - - public String getType() { - return this.type; - } -} diff --git a/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/NamingFuzzyWatchRequest.java b/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/NamingFuzzyWatchRequest.java new file mode 100644 index 00000000000..e4b0f1a5924 --- /dev/null +++ b/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/NamingFuzzyWatchRequest.java @@ -0,0 +1,61 @@ +/* + * 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.remote.request; + +import static com.alibaba.nacos.api.common.Constants.Naming.NAMING_MODULE; + +/** + * Nacos naming fuzzy watch service request. + * + * @author tanyongquan + */ +public class NamingFuzzyWatchRequest extends AbstractNamingRequest { + + private String groupKeyPattern; + + private String watchType; + + public NamingFuzzyWatchRequest() { + } + + public NamingFuzzyWatchRequest(String groupKeyPattern, String watchType) { + this.watchType=watchType; + this.groupKeyPattern = groupKeyPattern; + } + + public String getGroupKeyPattern() { + return groupKeyPattern; + } + + public void setGroupKeyPattern(String groupKeyPattern) { + this.groupKeyPattern = groupKeyPattern; + } + + public String getWatchType() { + return watchType; + } + + public void setWatchType(String watchType) { + this.watchType = watchType; + } + + @Override + public String getModule() { + return NAMING_MODULE; + } + +} diff --git a/api/src/main/resources/META-INF/services/com.alibaba.nacos.api.remote.Payload b/api/src/main/resources/META-INF/services/com.alibaba.nacos.api.remote.Payload index 773d048c14e..761df6cfecc 100644 --- a/api/src/main/resources/META-INF/services/com.alibaba.nacos.api.remote.Payload +++ b/api/src/main/resources/META-INF/services/com.alibaba.nacos.api.remote.Payload @@ -54,7 +54,7 @@ com.alibaba.nacos.api.naming.remote.request.FuzzyWatchNotifyInitRequest com.alibaba.nacos.api.naming.remote.request.ServiceListRequest com.alibaba.nacos.api.naming.remote.request.ServiceQueryRequest com.alibaba.nacos.api.naming.remote.request.SubscribeServiceRequest -com.alibaba.nacos.api.naming.remote.request.FuzzyWatchRequest +com.alibaba.nacos.api.naming.remote.request.NamingFuzzyWatchRequest com.alibaba.nacos.api.naming.remote.response.BatchInstanceResponse com.alibaba.nacos.api.naming.remote.response.InstanceResponse com.alibaba.nacos.api.naming.remote.response.NotifySubscriberResponse @@ -65,7 +65,7 @@ com.alibaba.nacos.api.naming.remote.response.SubscribeServiceResponse com.alibaba.nacos.api.naming.remote.response.FuzzyWatchResponse com.alibaba.nacos.api.config.remote.request.ConfigBatchFuzzyWatchRequest com.alibaba.nacos.api.config.remote.response.ConfigBatchFuzzyWatchResponse -com.alibaba.nacos.api.config.remote.request.FuzzyWatchNotifyChangeRequest -com.alibaba.nacos.api.config.remote.response.FuzzyWatchNotifyChangeResponse -com.alibaba.nacos.api.config.remote.request.FuzzyWatchNotifyDiffRequest +com.alibaba.nacos.api.config.remote.request.FuzzyWatchChangeNotifyRequest +com.alibaba.nacos.api.config.remote.response.FuzzyWatchChangeNotifyResponse +com.alibaba.nacos.api.config.remote.request.FuzzyWatchDiffSyncRequest com.alibaba.nacos.api.config.remote.response.FuzzyWatchNotifyDiffResponse diff --git a/client/src/main/java/com/alibaba/nacos/client/config/NacosConfigService.java b/client/src/main/java/com/alibaba/nacos/client/config/NacosConfigService.java index b811753a9ae..eb61b8072dc 100644 --- a/client/src/main/java/com/alibaba/nacos/client/config/NacosConfigService.java +++ b/client/src/main/java/com/alibaba/nacos/client/config/NacosConfigService.java @@ -21,7 +21,7 @@ import com.alibaba.nacos.api.config.ConfigService; import com.alibaba.nacos.api.config.ConfigType; import com.alibaba.nacos.api.config.filter.IConfigFilter; -import com.alibaba.nacos.api.config.listener.AbstractFuzzyWatchListener; +import com.alibaba.nacos.api.config.listener.ConfigFuzzyWatcher; import com.alibaba.nacos.api.config.listener.Listener; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.client.config.filter.impl.ConfigFilterChainManager; @@ -29,7 +29,7 @@ import com.alibaba.nacos.client.config.filter.impl.ConfigResponse; import com.alibaba.nacos.client.config.http.ServerHttpAgent; import com.alibaba.nacos.client.config.impl.ClientWorker; -import com.alibaba.nacos.client.config.impl.FuzzyListenContext; +import com.alibaba.nacos.client.config.impl.ConfigFuzzyWatchContext; import com.alibaba.nacos.client.config.impl.ConfigServerListManager; import com.alibaba.nacos.client.config.impl.LocalConfigInfoProcessor; import com.alibaba.nacos.client.config.impl.LocalEncryptedDataKeyProcessor; @@ -45,7 +45,7 @@ import java.util.Collection; import java.util.Collections; import java.util.Properties; -import java.util.UUID; +import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Future; @@ -134,69 +134,48 @@ public void addListener(String dataId, String group, Listener listener) throws N } @Override - public void fuzzyWatch(String fixedGroupName, AbstractFuzzyWatchListener listener) throws NacosException { - doFuzzyListen(ANY_PATTERN, fixedGroupName, listener); + public void fuzzyWatch(String fixedGroupName, ConfigFuzzyWatcher watcher) throws NacosException { + doAddFuzzyWatch(ANY_PATTERN, fixedGroupName, watcher); } @Override - public void fuzzyWatch(String dataIdPattern, String fixedGroupName, AbstractFuzzyWatchListener listener) + public void fuzzyWatch(String dataIdPattern, String fixedGroupName, ConfigFuzzyWatcher watcher) throws NacosException { - doFuzzyListen(dataIdPattern, fixedGroupName, listener); + doAddFuzzyWatch(dataIdPattern, fixedGroupName, watcher); } @Override - public Future> fuzzyWatchWithGroupKeys(String fixedGroupName, - AbstractFuzzyWatchListener listener) throws NacosException { - return doAddFuzzyListenerAndGetConfigs(ANY_PATTERN, fixedGroupName, listener); + public Future> fuzzyWatchWithGroupKeys(String fixedGroupName, + ConfigFuzzyWatcher watcher) throws NacosException { + return doAddFuzzyWatch(ANY_PATTERN, fixedGroupName, watcher); } @Override - public Future> fuzzyWatchWithGroupKeys(String dataIdPattern, - String fixedGroupName, AbstractFuzzyWatchListener listener) throws NacosException { - return doAddFuzzyListenerAndGetConfigs(dataIdPattern, fixedGroupName, listener); + public Future> fuzzyWatchWithGroupKeys(String dataIdPattern, + String fixedGroupName, ConfigFuzzyWatcher watcher) throws NacosException { + return doAddFuzzyWatch(dataIdPattern, fixedGroupName, watcher); } - private CompletableFuture> doAddFuzzyListenerAndGetConfigs(String dataIdPattern, - String fixedGroupName, AbstractFuzzyWatchListener listener) throws NacosException { - CompletableFuture> future = new CompletableFuture<>(); - if (listener == null) { - future.completeExceptionally(new IllegalArgumentException("Listener cannot be null")); - return future; - } - fuzzyWatch(dataIdPattern, fixedGroupName, listener); - FuzzyListenContext context = worker.getFuzzyListenContext(dataIdPattern, fixedGroupName); - if (context == null) { - future.complete(Collections.emptyList()); - return future; - } - return context.waitForInitializationComplete(future); - } - private void doFuzzyListen(String dataIdPattern, String fixedGroupName, AbstractFuzzyWatchListener listener) - throws NacosException { - if (listener == null) { - return; - } - if (!worker.containsPatternMatchCache(dataIdPattern, fixedGroupName)) { - worker.addTenantFuzzyListenListens(dataIdPattern, fixedGroupName, Collections.singletonList(listener)); - } else { - worker.duplicateFuzzyListenInit(dataIdPattern, fixedGroupName, listener); - } + private CompletableFuture> doAddFuzzyWatch(String dataIdPattern, + String fixedGroupName, ConfigFuzzyWatcher watcher) throws NacosException { + ConfigFuzzyWatchContext configFuzzyWatchContext = worker.registerFuzzyWatcher(dataIdPattern, fixedGroupName,watcher); + return worker.createNewFuture(configFuzzyWatchContext); } @Override - public void cancelFuzzyListen(String fixedGroupName, AbstractFuzzyWatchListener listener) throws NacosException { - cancelFuzzyListen(ANY_PATTERN, fixedGroupName, listener); + public void cancelFuzzyWatch(String fixedGroupName, ConfigFuzzyWatcher watcher) throws NacosException { + cancelFuzzyWatch(ANY_PATTERN, fixedGroupName, watcher); } @Override - public void cancelFuzzyListen(String dataIdPattern, String fixedGroupName, AbstractFuzzyWatchListener listener) + public void cancelFuzzyWatch(String dataIdPattern, String fixedGroupName, ConfigFuzzyWatcher watcher) throws NacosException { - doCancelFuzzyListen(dataIdPattern, fixedGroupName, listener); + doCancelFuzzyListen(dataIdPattern, fixedGroupName, watcher); } private void doCancelFuzzyListen(String dataIdPattern, String groupNamePattern, - AbstractFuzzyWatchListener listener) throws NacosException { + ConfigFuzzyWatcher listener) throws NacosException { if (null == listener) { return; } 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 05bc5e22fbd..f88a3e36e33 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 @@ -19,7 +19,7 @@ import com.alibaba.nacos.api.PropertyKeyConst; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.config.ConfigType; -import com.alibaba.nacos.api.config.listener.AbstractFuzzyWatchListener; +import com.alibaba.nacos.api.config.listener.ConfigFuzzyWatcher; import com.alibaba.nacos.api.config.listener.Listener; import com.alibaba.nacos.api.config.remote.request.ClientConfigMetricRequest; import com.alibaba.nacos.api.config.remote.request.ConfigBatchFuzzyWatchRequest; @@ -28,8 +28,8 @@ import com.alibaba.nacos.api.config.remote.request.ConfigPublishRequest; import com.alibaba.nacos.api.config.remote.request.ConfigQueryRequest; import com.alibaba.nacos.api.config.remote.request.ConfigRemoveRequest; -import com.alibaba.nacos.api.config.remote.request.FuzzyWatchNotifyChangeRequest; -import com.alibaba.nacos.api.config.remote.request.FuzzyWatchNotifyDiffRequest; +import com.alibaba.nacos.api.config.remote.request.FuzzyWatchChangeNotifyRequest; +import com.alibaba.nacos.api.config.remote.request.FuzzyWatchDiffSyncRequest; import com.alibaba.nacos.api.config.remote.response.ClientConfigMetricResponse; import com.alibaba.nacos.api.config.remote.response.ConfigBatchFuzzyWatchResponse; import com.alibaba.nacos.api.config.remote.response.ConfigChangeBatchListenResponse; @@ -37,7 +37,7 @@ import com.alibaba.nacos.api.config.remote.response.ConfigPublishResponse; import com.alibaba.nacos.api.config.remote.response.ConfigQueryResponse; import com.alibaba.nacos.api.config.remote.response.ConfigRemoveResponse; -import com.alibaba.nacos.api.config.remote.response.FuzzyWatchNotifyChangeResponse; +import com.alibaba.nacos.api.config.remote.response.FuzzyWatchChangeNotifyResponse; import com.alibaba.nacos.api.config.remote.response.FuzzyWatchNotifyDiffResponse; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.remote.RemoteConstants; @@ -92,12 +92,13 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.Properties; import java.util.Set; import java.util.UUID; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; @@ -111,6 +112,7 @@ import static com.alibaba.nacos.api.common.Constants.APP_CONN_PREFIX; import static com.alibaba.nacos.api.common.Constants.ENCODE; +import static com.alibaba.nacos.api.common.Constants.FUZZY_WATCH_INIT_NOTIFY; /** * Long polling. @@ -141,7 +143,7 @@ public class ClientWorker implements Closeable { /** * fuzzyListenGroupKey -> fuzzyListenContext. */ - private final AtomicReference> fuzzyListenContextMap = new AtomicReference<>( + private final AtomicReference> fuzzyListenContextMap = new AtomicReference<>( new HashMap<>()); private final DefaultLabelsCollectorManager defaultLabelsCollectorManager = new DefaultLabelsCollectorManager(); @@ -198,44 +200,20 @@ public ClientWorker(final ConfigFilterChainManager configFilterChainManager, Con * @param listeners The list of listeners to add. * @throws NacosException If an error occurs while adding the listeners. */ - public void addTenantFuzzyListenListens(String dataIdPattern, String groupPattern, - List listeners) throws NacosException { - FuzzyListenContext context = addFuzzyListenContextIfAbsent(dataIdPattern, groupPattern); + public void addTenantFuzzyWatcher(String dataIdPattern, String groupPattern, + List listeners) throws NacosException { + ConfigFuzzyWatchContext context = initFuzzyWatchContextIfAbsent(dataIdPattern, groupPattern); synchronized (context) { - for (AbstractFuzzyWatchListener listener : listeners) { - context.addListener(listener); + for (ConfigFuzzyWatcher listener : listeners) { + context.addWatcher(listener); } context.setInitializing(true); context.setDiscard(false); - context.getIsConsistentWithServer().set(false); + context.setConsistentWithServer(false); agent.notifyFuzzyListenConfig(); } } - /** - * Initializes a duplicate fuzzy listen for the specified data ID pattern, group, and listener. - * - * @param dataIdPattern The pattern of the data ID to listen for. - * @param group The group of the configuration. - * @param listener The listener to add. - */ - public void duplicateFuzzyListenInit(String dataIdPattern, String group, AbstractFuzzyWatchListener listener) { - String groupKeyPattern = FuzzyGroupKeyPattern.generateFuzzyWatchGroupKeyPattern(dataIdPattern, group,agent.getTenant()); - Map contextMap = fuzzyListenContextMap.get(); - FuzzyListenContext context = contextMap.get(groupKeyPattern); - if (Objects.isNull(context)) { - return; - } - synchronized (context) { - context.addListener(listener); - - for (String dataId : context.getReceivedGroupKeys()) { - NotifyCenter.publishEvent(FuzzyListenNotifyEvent.buildNotifyPatternSpecificListenerEvent(group, dataId, - Constants.ConfigChangedType.ADD_CONFIG, groupKeyPattern, listener.getUuid())); - } - } - } - /** * Add listeners for data. * @@ -374,16 +352,16 @@ public void removeTenantListener(String dataId, String group, Listener listener) * @param listener The listener to remove. * @throws NacosException If an error occurs while removing the listener. */ - public void removeFuzzyListenListener(String dataIdPattern, String group, AbstractFuzzyWatchListener listener) + public void removeFuzzyListenListener(String dataIdPattern, String group, ConfigFuzzyWatcher listener) throws NacosException { group = blank2defaultGroup(group); - FuzzyListenContext fuzzyListenContext = getFuzzyListenContext(dataIdPattern, group); - if (fuzzyListenContext != null) { - synchronized (fuzzyListenContext) { - fuzzyListenContext.removeListener(listener); - if (fuzzyListenContext.getListeners().isEmpty()) { - fuzzyListenContext.setDiscard(true); - fuzzyListenContext.getIsConsistentWithServer().set(false); + ConfigFuzzyWatchContext configFuzzyWatchContext = getFuzzyListenContext(dataIdPattern, group); + if (configFuzzyWatchContext != null) { + synchronized (configFuzzyWatchContext) { + configFuzzyWatchContext.removeWatcher(listener); + if (configFuzzyWatchContext.getConfigFuzzyWatchers().isEmpty()) { + configFuzzyWatchContext.setDiscard(true); + configFuzzyWatchContext.setConsistentWithServer(false); agent.removeFuzzyListenContext(dataIdPattern, group); } } @@ -397,8 +375,8 @@ public void removeFuzzyListenListener(String dataIdPattern, String group, Abstra */ public void removeFuzzyListenContext(String groupKeyPattern) { synchronized (fuzzyListenContextMap) { - Map copy = new HashMap<>(fuzzyListenContextMap.get()); - FuzzyListenContext removedContext = copy.remove(groupKeyPattern); + Map copy = new HashMap<>(fuzzyListenContextMap.get()); + ConfigFuzzyWatchContext removedContext = copy.remove(groupKeyPattern); if (removedContext != null) { decreaseContextTaskIdCount(removedContext.getTaskId()); } @@ -567,6 +545,40 @@ private void putCache(String key, CacheData cache) { } } + public ConfigFuzzyWatchContext registerFuzzyWatcher(String dataIdPattern, String groupPattern,ConfigFuzzyWatcher configFuzzyWatcher){ + ConfigFuzzyWatchContext configFuzzyWatchContext = initFuzzyWatchContextIfAbsent(dataIdPattern, groupPattern); + configFuzzyWatchContext.addWatcher(configFuzzyWatcher); + agent.notifyFuzzyListenConfig(); + if(configFuzzyWatchContext.getReceivedGroupKeys()!=null){ + for(String groupKey:configFuzzyWatchContext.getReceivedGroupKeys()){ + String[] groupKeyItems = GroupKey.parseKey(groupKey); + FuzzyWatchNotifyEvent fuzzyWatchNotifyEvent = FuzzyWatchNotifyEvent.buildNotifyPatternAllListenersEvent( + groupKeyItems[2], groupKeyItems[1], groupKeyItems[0], + configFuzzyWatchContext.getGroupKeyPattern(), FUZZY_WATCH_INIT_NOTIFY); + fuzzyWatchNotifyEvent.setUuid(configFuzzyWatcher.getUuid()); + NotifyCenter.publishEvent(fuzzyWatchNotifyEvent); + } + + } + + return configFuzzyWatchContext; + } + + public CompletableFuture> createNewFuture(ConfigFuzzyWatchContext configFuzzyWatchContext){ + CompletableFuture> completableFuture=new CompletableFuture>(){ + @Override + public boolean isDone() { + return configFuzzyWatchContext.initializationCompleted.get(); + } + + @Override + public Set get() throws InterruptedException, ExecutionException { + return new HashSet<>(configFuzzyWatchContext.getReceivedGroupKeys()); + } + }; + return completableFuture; + } + /** * Adds a fuzzy listen context if it doesn't already exist for the specified data ID pattern and group. If the * context already exists, returns the existing context. @@ -575,31 +587,30 @@ private void putCache(String key, CacheData cache) { * @param groupPattern The group of the configuration. * @return The fuzzy listen context for the specified data ID pattern and group. */ - public FuzzyListenContext addFuzzyListenContextIfAbsent(String dataIdPattern, String groupPattern) { - FuzzyListenContext context = getFuzzyListenContext(dataIdPattern, groupPattern); + private ConfigFuzzyWatchContext initFuzzyWatchContextIfAbsent(String dataIdPattern, String groupPattern) { + ConfigFuzzyWatchContext context = getFuzzyListenContext(dataIdPattern, groupPattern); if (context != null) { return context; } synchronized (fuzzyListenContextMap) { - FuzzyListenContext contextFromMap = getFuzzyListenContext(dataIdPattern, groupPattern); + ConfigFuzzyWatchContext contextFromMap = getFuzzyListenContext(dataIdPattern, groupPattern); if (contextFromMap != null) { context = contextFromMap; - context.getIsConsistentWithServer().set(false); } else { - context = new FuzzyListenContext(agent.getName(),FuzzyGroupKeyPattern.generateFuzzyWatchGroupKeyPattern(dataIdPattern, group,agent.getTenant())); + context = new ConfigFuzzyWatchContext(agent.getName(),FuzzyGroupKeyPattern.generatePattern(dataIdPattern, groupPattern,agent.getTenant())); int taskId = calculateContextTaskId(); increaseContextTaskIdCount(taskId); context.setTaskId(taskId); + context.setConsistentWithServer(false); + } } - Map copy = new HashMap<>(fuzzyListenContextMap.get()); - String groupKeyPattern = FuzzyGroupKeyPattern.generateFuzzyWatchGroupKeyPattern(dataIdPattern, group,agent.getTenant()); + Map copy = new HashMap<>(fuzzyListenContextMap.get()); + String groupKeyPattern = FuzzyGroupKeyPattern.generatePattern(dataIdPattern, groupPattern,agent.getTenant()); copy.put(groupKeyPattern, context); fuzzyListenContextMap.set(copy); - // TODO: Record metrics - return context; } @@ -712,9 +723,9 @@ private int calculateId(List countList, long perTaskSize) { * @param group The group name. * @return The corresponding FuzzyListenContext, or null if not found. */ - public FuzzyListenContext getFuzzyListenContext(String dataIdPattern, String group) { + public ConfigFuzzyWatchContext getFuzzyListenContext(String dataIdPattern, String group) { return fuzzyListenContextMap.get() - .get(FuzzyGroupKeyPattern.generateFuzzyWatchGroupKeyPattern(dataIdPattern, group,agent.getTenant())); + .get(FuzzyGroupKeyPattern.generatePattern(dataIdPattern, group,agent.getTenant())); } public ConfigResponse getServerConfig(String dataId, String group, String tenant, long readTimeout, boolean notify) @@ -737,8 +748,8 @@ private String blank2defaultGroup(String group) { * @return True if the cache contains an entry, false otherwise. */ public boolean containsPatternMatchCache(String dataIdPattern, String groupPattern) { - Map contextMap = fuzzyListenContextMap.get(); - String groupKeyPattern = FuzzyGroupKeyPattern.generateFuzzyWatchGroupKeyPattern(dataIdPattern, groupPattern,agent.getTenant()); + Map contextMap = fuzzyListenContextMap.get(); + String groupKeyPattern = FuzzyGroupKeyPattern.generatePattern(dataIdPattern, groupPattern,agent.getTenant()); return contextMap.containsKey(groupKeyPattern); } @@ -941,35 +952,39 @@ private Map getLabels() { * @param clientName The name of the client sending the request. * @return A {@link FuzzyWatchNotifyDiffResponse} indicating the result of handling the request. */ - private FuzzyWatchNotifyDiffResponse handleFuzzyListenNotifyDiffRequest(FuzzyWatchNotifyDiffRequest request, + private FuzzyWatchNotifyDiffResponse handleFuzzyWatchNotifyDiffRequest(FuzzyWatchDiffSyncRequest request, String clientName) { - LOGGER.info("[{}] [fuzzy-listen-config-push] config init.", clientName); + LOGGER.info("[{}] [config-fuzzy-watch-diff-sync-push] sync group key pattern ->{}.", clientName,request.getGroupKeyPattern()); String groupKeyPattern = request.getGroupKeyPattern(); - FuzzyListenContext context = fuzzyListenContextMap.get().get(groupKeyPattern); - if (Constants.FINISH_FUZZY_WATCH_INIT_NOTIFY.equals(request.getServiceChangedType())) { + ConfigFuzzyWatchContext context = fuzzyListenContextMap.get().get(groupKeyPattern); + if (Constants.FINISH_FUZZY_WATCH_INIT_NOTIFY.equals(request.getSyncType())) { context.markInitializationComplete(); return new FuzzyWatchNotifyDiffResponse(); } - for (FuzzyWatchNotifyDiffRequest.Context requestContext : request.getContexts()) { + for (FuzzyWatchDiffSyncRequest.Context requestContext : request.getContexts()) { Set matchedGroupKeys = context.getReceivedGroupKeys(); - switch (requestContext.getType()) { - case Constants.FUZZY_WATCH_INIT_NOTIFY: + switch (requestContext.getChangedType()) { + case FUZZY_WATCH_INIT_NOTIFY: case Constants.ConfigChangedType.ADD_CONFIG: if (matchedGroupKeys.add(GroupKey.getKeyTenant(requestContext.getDataId(),requestContext.getGroup(),requestContext.getTenant()))) { - NotifyCenter.publishEvent(FuzzyListenNotifyEvent.buildNotifyPatternAllListenersEvent( + LOGGER.info("[{}] [config-fuzzy-watch-diff-sync-push] sync group key ->{},changed type->{}", clientName,request.getGroupKeyPattern(),requestContext.getChangedType()); + + NotifyCenter.publishEvent(FuzzyWatchNotifyEvent.buildNotifyPatternAllListenersEvent(requestContext.getTenant(), requestContext.getGroup(), requestContext.getDataId(), request.getGroupKeyPattern(), - Constants.ConfigChangedType.ADD_CONFIG)); + requestContext.getChangedType())); } break; case Constants.ConfigChangedType.DELETE_CONFIG: if (matchedGroupKeys.remove(GroupKey.getKeyTenant(requestContext.getDataId(),requestContext.getGroup(),requestContext.getTenant()))) { - NotifyCenter.publishEvent(FuzzyListenNotifyEvent.buildNotifyPatternAllListenersEvent( + LOGGER.info("[{}] [config-fuzzy-watch-diff-sync-push] sync group key ->{},changed type->{}", clientName,request.getGroupKeyPattern(),requestContext.getChangedType()); + + NotifyCenter.publishEvent(FuzzyWatchNotifyEvent.buildNotifyPatternAllListenersEvent(requestContext.getTenant(), requestContext.getGroup(), requestContext.getDataId(), request.getGroupKeyPattern(), - Constants.ConfigChangedType.DELETE_CONFIG)); + requestContext.getChangedType())); } break; default: - LOGGER.error("Invalid config change type: {}", requestContext.getType()); + LOGGER.error("Invalid config change type: {}", requestContext.getChangedType()); break; } } @@ -985,29 +1000,29 @@ private FuzzyWatchNotifyDiffResponse handleFuzzyListenNotifyDiffRequest(FuzzyWat * @param request The fuzzy listen notify change request to handle. * @param clientName The name of the client sending the request. */ - private FuzzyWatchNotifyChangeResponse handlerFuzzyListenNotifyChangeRequest( - FuzzyWatchNotifyChangeRequest request, String clientName) { - LOGGER.info("[{}] [fuzzy-listen-config-push] config changed.", clientName); - Map listenContextMap = fuzzyListenContextMap.get(); + private FuzzyWatchChangeNotifyResponse handlerFuzzyListenNotifyChangeRequest( + FuzzyWatchChangeNotifyRequest request, String clientName) { + LOGGER.info("[{}] [fuzzy-watch-config-push] fuzzy watched config changed,groupKey->{},isDeleted->{} ", clientName,GroupKey.getKeyTenant(request.getDataId(),request.getGroup(),request.getNamespace()),!request.isExist()); + Map listenContextMap = fuzzyListenContextMap.get(); Set matchedPatterns = FuzzyGroupKeyPattern.filterMatchedPatterns(listenContextMap.keySet(),request.getDataId(), - request.getGroup(), request.getTenant()); + request.getGroup(), request.getNamespace()); for (String matchedPattern : matchedPatterns) { - FuzzyListenContext context = listenContextMap.get(matchedPattern); + ConfigFuzzyWatchContext context = listenContextMap.get(matchedPattern); if (request.isExist()) { - if (context.getReceivedGroupKeys().add(GroupKey.getKeyTenant(request.getDataId(),request.getGroup(),request.getTenant()))) { + if (context.getReceivedGroupKeys().add(GroupKey.getKeyTenant(request.getDataId(),request.getGroup(),request.getNamespace()))) { NotifyCenter.publishEvent( - FuzzyListenNotifyEvent.buildNotifyPatternAllListenersEvent(request.getGroup(), + FuzzyWatchNotifyEvent.buildNotifyPatternAllListenersEvent(request.getNamespace(),request.getGroup(), request.getDataId(), matchedPattern, Constants.ConfigChangedType.ADD_CONFIG)); } } else { - if (context.getReceivedGroupKeys().remove(GroupKey.getKeyTenant(request.getDataId(),request.getGroup(),request.getTenant()))) { + if (context.getReceivedGroupKeys().remove(GroupKey.getKeyTenant(request.getDataId(),request.getGroup(),request.getNamespace()))) { NotifyCenter.publishEvent( - FuzzyListenNotifyEvent.buildNotifyPatternAllListenersEvent(request.getGroup(), + FuzzyWatchNotifyEvent.buildNotifyPatternAllListenersEvent(request.getNamespace(),request.getGroup(), request.getDataId(), matchedPattern, Constants.ConfigChangedType.DELETE_CONFIG)); } } } - return new FuzzyWatchNotifyChangeResponse(); + return new FuzzyWatchChangeNotifyResponse(); } ConfigChangeNotifyResponse handleConfigChangeNotifyRequest(ConfigChangeNotifyRequest configChangeNotifyRequest, @@ -1041,16 +1056,19 @@ private void initRpcClientHandler(final RpcClient rpcClientInner) { * Register Config Change /Config ReSync Handler */ rpcClientInner.registerServerRequestHandler((request, connection) -> { + //config change notify if (request instanceof ConfigChangeNotifyRequest) { return handleConfigChangeNotifyRequest((ConfigChangeNotifyRequest) request, rpcClientInner.getName()); } - if (request instanceof FuzzyWatchNotifyDiffRequest) { - return handleFuzzyListenNotifyDiffRequest((FuzzyWatchNotifyDiffRequest) request, + //fuzzy watch diff reconciliation sync + if (request instanceof FuzzyWatchDiffSyncRequest) { + return handleFuzzyWatchNotifyDiffRequest((FuzzyWatchDiffSyncRequest) request, rpcClientInner.getName()); } - if (request instanceof FuzzyWatchNotifyChangeRequest) { - return handlerFuzzyListenNotifyChangeRequest((FuzzyWatchNotifyChangeRequest) request, + //fuzzy watch changed notify for a single config. include config changed or config delete. + if (request instanceof FuzzyWatchChangeNotifyRequest) { + return handlerFuzzyListenNotifyChangeRequest((FuzzyWatchChangeNotifyRequest) request, rpcClientInner.getName()); } return null; @@ -1090,15 +1108,15 @@ public void onDisConnect(Connection connection) { } } - Collection fuzzyListenContexts = fuzzyListenContextMap.get().values(); + Collection configFuzzyWatchContexts = fuzzyListenContextMap.get().values(); - for (FuzzyListenContext context : fuzzyListenContexts) { + for (ConfigFuzzyWatchContext context : configFuzzyWatchContexts) { if (StringUtils.isNotBlank(taskId)) { if (Integer.valueOf(taskId).equals(context.getTaskId())) { - context.getIsConsistentWithServer().set(false); + context.setConsistentWithServer(false); } } else { - context.getIsConsistentWithServer().set(false); + context.setConsistentWithServer(false); } } } @@ -1141,19 +1159,19 @@ public Class subscribeType() { NotifyCenter.registerSubscriber(new Subscriber() { @Override public void onEvent(Event event) { - FuzzyListenNotifyEvent fuzzyListenNotifyEvent = (FuzzyListenNotifyEvent) event; - FuzzyListenContext context = fuzzyListenContextMap.get() - .get(fuzzyListenNotifyEvent.getGroupKeyPattern()); + FuzzyWatchNotifyEvent fuzzyWatchNotifyEvent = (FuzzyWatchNotifyEvent) event; + ConfigFuzzyWatchContext context = fuzzyListenContextMap.get() + .get(fuzzyWatchNotifyEvent.getGroupKeyPattern()); if (context == null) { return; } - context.notifyListener(fuzzyListenNotifyEvent.getDataId(),fuzzyListenNotifyEvent.getGroup(),agent.getTenant(), fuzzyListenNotifyEvent.getType(), - fuzzyListenNotifyEvent.getUuid()); + context.notifyListener(fuzzyWatchNotifyEvent.getDataId(), fuzzyWatchNotifyEvent.getGroup(),agent.getTenant(), fuzzyWatchNotifyEvent.getType(), + fuzzyWatchNotifyEvent.getUuid()); } @Override public Class subscribeType() { - return FuzzyListenNotifyEvent.class; + return FuzzyWatchNotifyEvent.class; } }); } @@ -1283,7 +1301,7 @@ public void executeConfigListen() throws NacosException { */ @Override public void executeConfigFuzzyListen() throws NacosException { - Map> needSyncContextMap = new HashMap<>(16); + Map> needSyncContextMap = new HashMap<>(16); // Obtain the current timestamp long now = System.currentTimeMillis(); @@ -1292,16 +1310,18 @@ public void executeConfigFuzzyListen() throws NacosException { boolean needAllSync = now - fuzzyListenLastAllSyncTime >= FUZZY_LISTEN_ALL_SYNC_INTERNAL; // Iterate through all fuzzy listen contexts - for (FuzzyListenContext context : fuzzyListenContextMap.get().values()) { + for (ConfigFuzzyWatchContext context : fuzzyListenContextMap.get().values()) { // Check if the context is consistent with the server - if (context.getIsConsistentWithServer().get()) { + if (context.isConsistentWithServer()) { // Skip if a full synchronization is not needed if (!needAllSync) { continue; + }else{ + context.syncFuzzyWatchers(); } } - List needSyncContexts = needSyncContextMap.computeIfAbsent( + List needSyncContexts = needSyncContextMap.computeIfAbsent( String.valueOf(context.getTaskId()), k -> new LinkedList<>()); needSyncContexts.add(context); } @@ -1324,7 +1344,7 @@ public void executeConfigFuzzyListen() throws NacosException { * @param contextMap The map of contexts to execute fuzzy listen operations for. * @throws NacosException If an error occurs during the execution of fuzzy listen configuration changes. */ - private void doExecuteConfigFuzzyListen(Map> contextMap) + private void doExecuteConfigFuzzyListen(Map> contextMap) throws NacosException { // Return if the context map is null or empty if (contextMap == null || contextMap.isEmpty()) { @@ -1335,9 +1355,9 @@ private void doExecuteConfigFuzzyListen(Map> co List> listenFutures = new ArrayList<>(); // Iterate through the context map and submit tasks for execution - for (Map.Entry> entry : contextMap.entrySet()) { + for (Map.Entry> entry : contextMap.entrySet()) { String taskId = entry.getKey(); - List contexts = entry.getValue(); + List contexts = entry.getValue(); RpcClient rpcClient = ensureRpcClient(taskId); ExecutorService executorService = ensureSyncExecutor(fuzzyListenerTaskPrefix, taskId); // Submit task for execution @@ -1349,11 +1369,11 @@ private void doExecuteConfigFuzzyListen(Map> co ConfigBatchFuzzyWatchResponse listenResponse = (ConfigBatchFuzzyWatchResponse) requestProxy( rpcClient, configBatchFuzzyWatchRequest); if (listenResponse != null && listenResponse.isSuccess()) { - for (FuzzyListenContext context : contexts) { + for (ConfigFuzzyWatchContext context : contexts) { if (context.isDiscard()) { ClientWorker.this.removeFuzzyListenContext(context.getGroupKeyPattern()); } else { - context.getIsConsistentWithServer().set(true); + context.setConsistentWithServer(true); } } } @@ -1678,9 +1698,9 @@ private ConfigBatchListenRequest buildConfigRequest(List caches) { * @param contexts The list of fuzzy listen contexts. * @return A {@code ConfigBatchFuzzyListenRequest} object representing the request. */ - private ConfigBatchFuzzyWatchRequest buildFuzzyListenConfigRequest(List contexts) { + private ConfigBatchFuzzyWatchRequest buildFuzzyListenConfigRequest(List contexts) { ConfigBatchFuzzyWatchRequest request = new ConfigBatchFuzzyWatchRequest(); - for (FuzzyListenContext context : contexts) { + for (ConfigFuzzyWatchContext context : contexts) { request.addContext(context.getGroupKeyPattern(), context.getReceivedGroupKeys(), !context.isDiscard(), context.isInitializing()); } diff --git a/client/src/main/java/com/alibaba/nacos/client/config/impl/FuzzyListenContext.java b/client/src/main/java/com/alibaba/nacos/client/config/impl/ConfigFuzzyWatchContext.java similarity index 52% rename from client/src/main/java/com/alibaba/nacos/client/config/impl/FuzzyListenContext.java rename to client/src/main/java/com/alibaba/nacos/client/config/impl/ConfigFuzzyWatchContext.java index 0c9a9c59acf..de6804b0854 100644 --- a/client/src/main/java/com/alibaba/nacos/client/config/impl/FuzzyListenContext.java +++ b/client/src/main/java/com/alibaba/nacos/client/config/impl/ConfigFuzzyWatchContext.java @@ -16,25 +16,26 @@ package com.alibaba.nacos.client.config.impl; -import com.alibaba.nacos.api.config.listener.AbstractFuzzyWatchListener; +import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.config.listener.ConfigFuzzyWatchChangeEvent; +import com.alibaba.nacos.api.config.listener.ConfigFuzzyWatcher; import com.alibaba.nacos.client.utils.LogUtils; +import com.alibaba.nacos.common.notify.NotifyCenter; import com.alibaba.nacos.common.utils.ConcurrentHashSet; +import com.alibaba.nacos.common.utils.FuzzyGroupKeyPattern; +import com.alibaba.nacos.common.utils.GroupKey; import com.alibaba.nacos.common.utils.StringUtils; import org.slf4j.Logger; -import java.util.Collection; import java.util.Collections; import java.util.HashSet; +import java.util.List; import java.util.Set; -import java.util.concurrent.CompletableFuture; import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.locks.Condition; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantLock; + /** - * Context for fuzzy listening. + * fuzzy wather 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. @@ -43,12 +44,12 @@ * @author stone-98 * @date 2024/3/4 */ -public class FuzzyListenContext { +public class ConfigFuzzyWatchContext { /** * Logger for FuzzyListenContext. */ - private static final Logger LOGGER = LogUtils.logger(FuzzyListenContext.class); + private static final Logger LOGGER = LogUtils.logger(ConfigFuzzyWatchContext.class); /** * Environment name. @@ -72,15 +73,10 @@ public class FuzzyListenContext { */ private final AtomicBoolean isConsistentWithServer = new AtomicBoolean(); - /** - * Lock object for synchronization of initialization. - */ - private final Lock initializationLock = new ReentrantLock(); - /** * Condition object for waiting initialization completion. */ - private final Condition initializationCompleted = initializationLock.newCondition(); + final AtomicBoolean initializationCompleted = new AtomicBoolean(false); /** * Flag indicating whether the context is initializing. @@ -91,22 +87,21 @@ public class FuzzyListenContext { * Flag indicating whether the context is discarded. */ private volatile boolean isDiscard = false; - + /** * Set of listeners associated with the context. */ - private Set listeners = new HashSet<>(); + private Set configFuzzyWatchers = new HashSet<>(); /** * Constructor with environment name, data ID pattern, and group. * - * @param envName Environment name + * @param envName Environment name * @param groupKeyPattern groupKeyPattern */ - public FuzzyListenContext(String envName, String groupKeyPattern) { + public ConfigFuzzyWatchContext(String envName, String groupKeyPattern) { this.envName = envName; - this.groupKeyPattern=groupKeyPattern; - + this.groupKeyPattern = groupKeyPattern; } /** @@ -115,12 +110,12 @@ public FuzzyListenContext(String envName, String groupKeyPattern) { * @param uuid UUID to filter listeners * @return Set of listeners to notify */ - public Set calculateListenersToNotify(String uuid) { - Set listenersToNotify = new HashSet<>(); + public Set calculateListenersToNotify(String uuid) { + Set listenersToNotify = new HashSet<>(); if (StringUtils.isEmpty(uuid)) { - listenersToNotify = listeners; + listenersToNotify = configFuzzyWatchers; } else { - for (AbstractFuzzyWatchListener listener : listeners) { + for (ConfigFuzzyWatcher listener : configFuzzyWatchers) { if (uuid.equals(listener.getUuid())) { listenersToNotify.add(listener); } @@ -136,9 +131,10 @@ public Set calculateListenersToNotify(String uuid) { * @param type Type of the event * @param uuid UUID to filter listeners */ - public void notifyListener(final String dataId,final String group,String tenant, final String type, final String uuid) { - Set listenersToNotify = calculateListenersToNotify(uuid); - doNotifyListener(dataId,group,tenant, type, listenersToNotify); + public void notifyListener(final String dataId, final String group, String tenant, final String type, + final String uuid) { + Set listenersToNotify = calculateListenersToNotify(uuid); + doNotifyWatchers(dataId, group, tenant, type, listenersToNotify); } /** @@ -148,95 +144,83 @@ public void notifyListener(final String dataId,final String group,String tenant, * @param type Type of the event * @param listenersToNotify Set of listeners to notify */ - private void doNotifyListener(final String dataId, final String group,String tenant,final String type, - Set listenersToNotify) { - for (AbstractFuzzyWatchListener listener : listenersToNotify) { - AbstractFuzzyNotifyTask job = new AbstractFuzzyNotifyTask() { - @Override - public void run() { - long start = System.currentTimeMillis(); - ConfigFuzzyWatchChangeEvent event = ConfigFuzzyWatchChangeEvent.build(group, dataId, type); - if (listener != null) { - listener.onEvent(event); - } - LOGGER.info("[{}] [notify-ok] dataId={}, group={}, tenant={}, listener={}, job run cost={} millis.", - envName, dataId, group, tenant, listener, (System.currentTimeMillis() - start)); - } - }; - - try { - if (null != listener.getExecutor()) { - LOGGER.info( - "[{}] [notify-listener] task submitted to user executor, dataId={}, group={}, tenant={}, listener={}.", - envName, dataId, group, tenant, listener); - job.async = true; - listener.getExecutor().execute(job); - } else { - LOGGER.info( - "[{}] [notify-listener] task execute in nacos thread, dataId={}, group={}, tenant={}, listener={}.", - envName, dataId, group, tenant, listener); - job.run(); - } - } catch (Throwable t) { - LOGGER.error("[{}] [notify-listener-error] dataId={}, group={}, tenant={}, listener={}, throwable={}.", - envName, dataId, group, tenant, listener, t.getCause()); - } + private void doNotifyWatchers(final String dataId, final String group, String tenant, final String type, + Set listenersToNotify) { + for (ConfigFuzzyWatcher watcher : listenersToNotify) { + doNotifyWatcher(dataId,group,tenant,type,watcher); } } + private void doNotifyWatcher(final String dataId, final String group, String tenant, final String type, + ConfigFuzzyWatcher configFuzzyWatcher){ + AbstractFuzzyNotifyTask job = new AbstractFuzzyNotifyTask() { + @Override + public void run() { + long start = System.currentTimeMillis(); + ConfigFuzzyWatchChangeEvent event = ConfigFuzzyWatchChangeEvent.build(tenant,group, dataId, type); + if (configFuzzyWatcher != null) { + configFuzzyWatcher.onEvent(event); + } + LOGGER.info( + "[{}] [notify-watcher-ok] dataId={}, group={}, tenant={}, watcher={}, job run cost={} millis.", + envName, dataId, group, tenant, configFuzzyWatcher, (System.currentTimeMillis() - start)); + if (type.equals(Constants.ConfigChangedType.DELETE_CONFIG)) { + configFuzzyWatcher.getSyncGroupKeys().remove(GroupKey.getKey(dataId, group, tenant)); + } else if (type.equals("FUZZY_WATCH_INIT_NOTIFY") || type.equals( + Constants.ConfigChangedType.ADD_CONFIG)) { + configFuzzyWatcher.getSyncGroupKeys().add(GroupKey.getKey(dataId, group, tenant)); + + } + } + }; - /** - * Wait for initialization to be complete. - * - * @return CompletableFuture> Completes with the collection of data IDs if initialization is - * @return CompletableFuture> Completes with the collection of data IDs if initialization is - * complete, or completes exceptionally if an error occurs - */ - public CompletableFuture> waitForInitializationComplete( - CompletableFuture> future) { - initializationLock.lock(); try { - while (isInitializing) { - initializationCompleted.await(); + if (null != configFuzzyWatcher.getExecutor()) { + LOGGER.info( + "[{}] [notify-watcher] task submitted to user executor, dataId={}, group={}, tenant={}, listener={}.", + envName, dataId, group, tenant, configFuzzyWatcher); + job.async = true; + configFuzzyWatcher.getExecutor().execute(job); + } else { + LOGGER.info( + "[{}] [notify-watcher] task execute in nacos thread, dataId={}, group={}, tenant={}, listener={}.", + envName, dataId, group, tenant, configFuzzyWatcher); + job.run(); } - future.complete(Collections.unmodifiableCollection(receivedGroupKeys)); - } catch (InterruptedException e) { - future.completeExceptionally(e); - } finally { - initializationLock.unlock(); + } catch (Throwable t) { + LOGGER.error("[{}] [notify-watcher-error] dataId={}, group={}, tenant={}, listener={}, throwable={}.", + envName, dataId, group, tenant, configFuzzyWatcher, t.getCause()); } - return future; } - /** * Mark initialization as complete and notify waiting threads. */ public void markInitializationComplete() { - initializationLock.lock(); - try { - isInitializing = false; - initializationCompleted.signalAll(); - } finally { - initializationLock.unlock(); - } + initializationCompleted.set(true); } /** - * Remove a listener from the context. + * Remove a watcher from the context. * - * @param listener Listener to be removed + * @param watcher watcher to be removed */ - public void removeListener(AbstractFuzzyWatchListener listener) { - listeners.remove(listener); + public void removeWatcher(ConfigFuzzyWatcher watcher) { + configFuzzyWatchers.remove(watcher); + + LOGGER.info("[{}] [remove-watcher-ok] groupKeyPattern={}, watcher={},uuid={} ", getEnvName(), + this.groupKeyPattern, watcher, watcher.getUuid()); + } /** - * Add a listener to the context. + * Add a watcher to the context. * - * @param listener Listener to be added + * @param watcher watcher to be added */ - public void addListener(AbstractFuzzyWatchListener listener) { - listeners.add(listener); + public void addWatcher(ConfigFuzzyWatcher watcher) { + configFuzzyWatchers.add(watcher); + LOGGER.info("[{}] [add-watcher-ok] groupKeyPattern={}, watcher={},uuid={} ", getEnvName(), this.groupKeyPattern, + watcher, watcher.getUuid()); } /** @@ -279,25 +263,17 @@ public String getGroupKeyPattern() { return groupKeyPattern; } - public void setGroupKeyPattern(String groupKeyPattern) { - this.groupKeyPattern = groupKeyPattern; - } - - - /** - * Get the group name. - * - * @return Group name - */ - - /** * Get the flag indicating whether the context is consistent with the server. * * @return AtomicBoolean indicating whether the context is consistent with the server */ - public AtomicBoolean getIsConsistentWithServer() { - return isConsistentWithServer; + public boolean isConsistentWithServer() { + return isConsistentWithServer.get(); + } + + public void setConsistentWithServer(boolean isConsistentWithServer) { + this.isConsistentWithServer.set(isConsistentWithServer); } /** @@ -344,26 +320,18 @@ public void setInitializing(boolean initializing) { public Set getReceivedGroupKeys() { return Collections.unmodifiableSet(receivedGroupKeys); } - + /** * Get the set of listeners associated with the context. * * @return Set of listeners */ - public Set getListeners() { - return listeners; - } - - /** - * Set the set of listeners associated with the context. - * - * @param listeners Set of listeners to be set - */ - public void setListeners(Set listeners) { - this.listeners = listeners; + public Set getConfigFuzzyWatchers() { + return configFuzzyWatchers; } + /** * Abstract task for fuzzy notification. */ @@ -383,5 +351,23 @@ public boolean isAsync() { return async; } } + + void syncFuzzyWatchers(){ + for(ConfigFuzzyWatcher configFuzzyWatcher:configFuzzyWatchers){ + Set receivedGroupKeysContext = receivedGroupKeys; + Set syncGroupKeys = configFuzzyWatcher.getSyncGroupKeys(); + List groupKeyStates = FuzzyGroupKeyPattern.diffGroupKeys( + receivedGroupKeysContext, syncGroupKeys); + for(FuzzyGroupKeyPattern.GroupKeyState groupKeyState:groupKeyStates){ + String[] groupKeyItems = GroupKey.parseKey(groupKeyState.getGroupKey()); + FuzzyWatchNotifyEvent fuzzyWatchNotifyEvent = FuzzyWatchNotifyEvent.buildNotifyPatternAllListenersEvent( + groupKeyItems[2], groupKeyItems[1], groupKeyItems[0], + this.groupKeyPattern, groupKeyState.isExist()? Constants.ConfigChangedType.ADD_CONFIG:Constants.ConfigChangedType.DELETE_CONFIG); + fuzzyWatchNotifyEvent.setUuid(configFuzzyWatcher.getUuid()); + NotifyCenter.publishEvent(fuzzyWatchNotifyEvent); + } + + } + } } diff --git a/client/src/main/java/com/alibaba/nacos/client/config/impl/FuzzyListenNotifyEvent.java b/client/src/main/java/com/alibaba/nacos/client/config/impl/FuzzyWatchNotifyEvent.java similarity index 70% rename from client/src/main/java/com/alibaba/nacos/client/config/impl/FuzzyListenNotifyEvent.java rename to client/src/main/java/com/alibaba/nacos/client/config/impl/FuzzyWatchNotifyEvent.java index a710630c131..6b7c84f9231 100644 --- a/client/src/main/java/com/alibaba/nacos/client/config/impl/FuzzyListenNotifyEvent.java +++ b/client/src/main/java/com/alibaba/nacos/client/config/impl/FuzzyWatchNotifyEvent.java @@ -28,10 +28,10 @@ * @author stone-98 * @date 2024/3/4 */ -public class FuzzyListenNotifyEvent extends Event { +public class FuzzyWatchNotifyEvent extends Event { /** - * The unique identifier for the listener. + * The uuid of this watcher for which that this notify event . */ private String uuid; @@ -40,6 +40,11 @@ public class FuzzyListenNotifyEvent extends Event { */ private String groupKeyPattern; + /** + * The namespace of the configuration. + */ + private String namespace; + /** * The group of the configuration. */ @@ -58,25 +63,9 @@ public class FuzzyListenNotifyEvent extends Event { /** * Constructs a new FuzzyListenNotifyEvent. */ - public FuzzyListenNotifyEvent() { - } - - /** - * Constructs a new FuzzyListenNotifyEvent with the specified group, dataId, type, and UUID. - * - * @param group The group of the configuration. - * @param dataId The dataId of the configuration. - * @param type The type of notification. - * @param uuid The UUID (Unique Identifier) of the listener. - */ - public FuzzyListenNotifyEvent(String group, String dataId, String type, String groupKeyPattern, String uuid) { - this.group = group; - this.dataId = dataId; - this.type = type; - this.groupKeyPattern = groupKeyPattern; - this.uuid = uuid; + public FuzzyWatchNotifyEvent() { } - + /** * Constructs a new FuzzyListenNotifyEvent with the specified group, dataId, and type. * @@ -84,26 +73,14 @@ public FuzzyListenNotifyEvent(String group, String dataId, String type, String g * @param dataId The dataId of the configuration. * @param type The type of notification. */ - public FuzzyListenNotifyEvent(String group, String dataId, String type, String groupKeyPattern) { + public FuzzyWatchNotifyEvent(String namespace,String group, String dataId, String type, String groupKeyPattern) { this.group = group; this.dataId = dataId; + this.namespace=namespace; this.type = type; this.groupKeyPattern = groupKeyPattern; } - /** - * Builds a new FuzzyListenNotifyEvent with the specified group, dataId, type, and UUID. - * - * @param group The group of the configuration. - * @param dataId The dataId of the configuration. - * @param type The type of notification. - * @param uuid The UUID (Unique Identifier) of the listener. - * @return A new FuzzyListenNotifyEvent instance. - */ - public static FuzzyListenNotifyEvent buildNotifyPatternSpecificListenerEvent(String group, String dataId, - String type, String groupKeyPattern, String uuid) { - return new FuzzyListenNotifyEvent(group, dataId, type, groupKeyPattern, uuid); - } /** * Builds a new FuzzyListenNotifyEvent with the specified group, dataId, and type. @@ -113,9 +90,9 @@ public static FuzzyListenNotifyEvent buildNotifyPatternSpecificListenerEvent(Str * @param type The type of notification. * @return A new FuzzyListenNotifyEvent instance. */ - public static FuzzyListenNotifyEvent buildNotifyPatternAllListenersEvent(String group, String dataId, + public static FuzzyWatchNotifyEvent buildNotifyPatternAllListenersEvent(String namespace,String group, String dataId, String groupKeyPattern, String type) { - return new FuzzyListenNotifyEvent(group, dataId, type, groupKeyPattern); + return new FuzzyWatchNotifyEvent(namespace,group, dataId, type, groupKeyPattern); } /** diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/cache/FuzzyWatchServiceListHolder.java b/client/src/main/java/com/alibaba/nacos/client/naming/cache/FuzzyWatchServiceListHolder.java index f0380c8ffba..461040eaa34 100644 --- a/client/src/main/java/com/alibaba/nacos/client/naming/cache/FuzzyWatchServiceListHolder.java +++ b/client/src/main/java/com/alibaba/nacos/client/naming/cache/FuzzyWatchServiceListHolder.java @@ -75,7 +75,7 @@ public void processFuzzyWatchNotify(AbstractFuzzyWatchNotifyRequest request) { FuzzyWatchNotifyChangeRequest notifyChangeRequest = (FuzzyWatchNotifyChangeRequest) request; Collection matchedPattern = FuzzyGroupKeyPattern.filterMatchedPatterns(patternMatchMap.keySet(),notifyChangeRequest.getNamespace(),notifyChangeRequest.getGroupName(),notifyChangeRequest.getServiceName()); Service service = new Service(notifyChangeRequest.getServiceName(), notifyChangeRequest.getGroupName()); - String serviceChangeType = request.getServiceChangedType(); + String serviceChangeType = request.getChangedType(); switch (serviceChangeType) { case Constants.ServiceChangedType.ADD_SERVICE: diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/NamingGrpcClientProxy.java b/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/NamingGrpcClientProxy.java index 6211b885d1e..e5d602482c9 100644 --- a/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/NamingGrpcClientProxy.java +++ b/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/NamingGrpcClientProxy.java @@ -28,7 +28,7 @@ import com.alibaba.nacos.api.naming.remote.NamingRemoteConstants; import com.alibaba.nacos.api.naming.remote.request.AbstractNamingRequest; import com.alibaba.nacos.api.naming.remote.request.BatchInstanceRequest; -import com.alibaba.nacos.api.naming.remote.request.FuzzyWatchRequest; +import com.alibaba.nacos.api.naming.remote.request.NamingFuzzyWatchRequest; import com.alibaba.nacos.api.naming.remote.request.InstanceRequest; import com.alibaba.nacos.api.naming.remote.request.PersistentInstanceRequest; import com.alibaba.nacos.api.naming.remote.request.ServiceListRequest; @@ -64,6 +64,7 @@ import com.alibaba.nacos.common.remote.client.RpcClientTlsConfigFactory; import com.alibaba.nacos.common.remote.client.ServerListFactory; import com.alibaba.nacos.common.utils.CollectionUtils; +import com.alibaba.nacos.common.utils.FuzzyGroupKeyPattern; import com.alibaba.nacos.common.utils.JacksonUtils; import java.util.ArrayList; @@ -446,7 +447,7 @@ public void fuzzyWatch(String serviceNamePattern, String groupNamePattern, Strin * @throws NacosException nacos exception */ public void doFuzzyWatch(String serviceNamePattern, String groupNamePattern) throws NacosException { - FuzzyWatchRequest request = new FuzzyWatchRequest(namespaceId, serviceNamePattern, groupNamePattern, + NamingFuzzyWatchRequest request = new NamingFuzzyWatchRequest(FuzzyGroupKeyPattern.generatePattern(serviceNamePattern,groupNamePattern,namespaceId), NamingRemoteConstants.FUZZY_WATCH_SERVICE); requestToServer(request, FuzzyWatchResponse.class); redoService.fuzzyWatcherRegistered(serviceNamePattern, groupNamePattern); @@ -475,7 +476,7 @@ public void cancelFuzzyWatch(String serviceNamePattern, String groupNamePattern) * @throws NacosException nacos exception */ public void doCancelFuzzyWatch(String serviceNamePattern, String groupNamePattern) throws NacosException { - FuzzyWatchRequest request = new FuzzyWatchRequest(namespaceId, serviceNamePattern, groupNamePattern, + NamingFuzzyWatchRequest request = new NamingFuzzyWatchRequest(FuzzyGroupKeyPattern.generatePattern(serviceNamePattern,groupNamePattern,namespaceId), NamingRemoteConstants.CANCEL_FUZZY_WATCH_SERVICE); requestToServer(request, FuzzyWatchResponse.class); redoService.removeFuzzyWatcherForRedo(serviceNamePattern, groupNamePattern); diff --git a/common/src/main/java/com/alibaba/nacos/common/utils/FuzzyGroupKeyPattern.java b/common/src/main/java/com/alibaba/nacos/common/utils/FuzzyGroupKeyPattern.java index a9ba130bfcc..e76b2da1c62 100644 --- a/common/src/main/java/com/alibaba/nacos/common/utils/FuzzyGroupKeyPattern.java +++ b/common/src/main/java/com/alibaba/nacos/common/utils/FuzzyGroupKeyPattern.java @@ -18,7 +18,10 @@ import java.util.Collection; import java.util.HashSet; +import java.util.List; import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; import static com.alibaba.nacos.api.common.Constants.ANY_PATTERN; import static com.alibaba.nacos.api.common.Constants.DEFAULT_NAMESPACE_ID; @@ -45,7 +48,7 @@ public class FuzzyGroupKeyPattern { * @return A unique group key pattern for fuzzy listen. * @throws IllegalArgumentException If the dataId pattern or group is blank. */ - public static String generateFuzzyWatchGroupKeyPattern(final String resourcePattern, final String groupPattern, + public static String generatePattern(final String resourcePattern, final String groupPattern, String fixNamespace) { if (StringUtils.isBlank(resourcePattern)) { throw new IllegalArgumentException("Param 'resourcePattern' is illegal, resourcePattern is blank"); @@ -89,6 +92,8 @@ public static Set filterMatchedPatterns(Collection groupKeyPatte return matchedPatternList; } + + public static boolean matchPattern(String groupKeyPattern,String namespace,String group,String resourceName){ if(StringUtils.isBlank(namespace)){ namespace=DEFAULT_NAMESPACE_ID; @@ -97,7 +102,7 @@ public static boolean matchPattern(String groupKeyPattern,String namespace,Strin return splitPatterns[0].equals(namespace)&&itemMatched(splitPatterns[1],group)&&itemMatched(splitPatterns[2],resourceName); } - public static String getNamespaceFromGroupKeyPattern(String groupKeyPattern){ + public static String getNamespaceFromPattern(String groupKeyPattern){ return groupKeyPattern.split(FUZZY_WATCH_PATTERN_SPLITTER)[0]; } /** @@ -138,4 +143,92 @@ private static boolean itemMatched(String pattern,String resource){ return false; } + + + /** + * Calculates and merges the differences between the matched group keys and the client's existing group keys into a + * list of ConfigState objects. + * + * @param matchGroupKeys The matched group keys set + * @param clientExistingGroupKeys The client's existing group keys set + * @return The merged list of ConfigState objects representing the states to be added or removed + */ + public static List diffGroupKeys(Set basedGroupKeys, + Set followedGroupKeys) { + // Calculate the set of group keys to be added and removed + Set addGroupKeys = new HashSet<>(); + if (CollectionUtils.isNotEmpty(basedGroupKeys)) { + addGroupKeys.addAll(basedGroupKeys); + } + if (CollectionUtils.isNotEmpty(followedGroupKeys)) { + addGroupKeys.removeAll(followedGroupKeys); + } + + Set removeGroupKeys = new HashSet<>(); + if (CollectionUtils.isNotEmpty(followedGroupKeys)) { + removeGroupKeys.addAll(followedGroupKeys); + } + if (CollectionUtils.isNotEmpty(basedGroupKeys)) { + removeGroupKeys.removeAll(basedGroupKeys); + } + + // Convert the group keys to be added and removed into corresponding ConfigState objects and merge them into a list + return Stream.concat(addGroupKeys.stream().map(groupKey -> new GroupKeyState(groupKey, true)), + removeGroupKeys.stream().map(groupKey -> new GroupKeyState(groupKey, false))) + .collect(Collectors.toList()); + } + + + public static class GroupKeyState{ + String groupKey; + + boolean exist; + + /** + * Constructs a new ConfigState instance with the given group key and existence flag. + * + * @param groupKey The group key associated with the configuration. + * @param exist {@code true} if the configuration exists, {@code false} otherwise. + */ + public GroupKeyState(String groupKey, boolean exist) { + this.groupKey = groupKey; + this.exist = exist; + } + + /** + * Retrieves the group key associated with the configuration. + * + * @return The group key. + */ + public String getGroupKey() { + return groupKey; + } + + /** + * Sets the group key associated with the configuration. + * + * @param groupKey The group key to set. + */ + public void setGroupKey(String groupKey) { + this.groupKey = groupKey; + } + + /** + * Checks whether the configuration exists or not. + * + * @return {@code true} if the configuration exists, {@code false} otherwise. + */ + public boolean isExist() { + return exist; + } + + /** + * Sets the existence flag of the configuration. + * + * @param exist {@code true} if the configuration exists, {@code false} otherwise. + */ + public void setExist(boolean exist) { + this.exist = exist; + } + } } diff --git a/common/src/test/java/com/alibaba/nacos/common/utils/FuzzyGroupKeyPatternTest.java b/common/src/test/java/com/alibaba/nacos/common/utils/FuzzyGroupKeyPatternTest.java index 7ee1115a810..15d13250dc6 100644 --- a/common/src/test/java/com/alibaba/nacos/common/utils/FuzzyGroupKeyPatternTest.java +++ b/common/src/test/java/com/alibaba/nacos/common/utils/FuzzyGroupKeyPatternTest.java @@ -35,7 +35,7 @@ public void testGetGroupKeyPattern() { String group = "exampleGroup"; String namespace = "exampleNamespace"; - String groupKeyPattern = FuzzyGroupKeyPattern.generateFuzzyWatchGroupKeyPattern(dataIdPattern, group, namespace); + String groupKeyPattern = FuzzyGroupKeyPattern.generatePattern(dataIdPattern, group, namespace); assertEquals("exampleNamespace>>exampleGroup@@examplePattern*", groupKeyPattern); } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigBatchFuzzyWatchRequestHandler.java b/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigBatchFuzzyWatchRequestHandler.java index d14fd53100c..b2d9ebad6f3 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigBatchFuzzyWatchRequestHandler.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigBatchFuzzyWatchRequestHandler.java @@ -69,7 +69,7 @@ public class ConfigBatchFuzzyWatchRequestHandler * @throws NacosException If an error occurs while processing the request */ @Override - @TpsControl(pointName = "ConfigFuzzyListen") + @TpsControl(pointName = "ConfigFuzzyWatch") @Secured(action = ActionTypes.READ, signType = SignType.CONFIG) @ExtractorManager.Extractor(rpcExtractor = ConfigBatchFuzzyListenRequestParamsExtractor.class) public ConfigBatchFuzzyWatchResponse handle(ConfigBatchFuzzyWatchRequest request, RequestMeta meta) diff --git a/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigFuzzyWatchDiffNotifier.java b/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigFuzzyWatchDiffNotifier.java index 8ccddb7c96a..ffc4444ea28 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigFuzzyWatchDiffNotifier.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigFuzzyWatchDiffNotifier.java @@ -17,12 +17,13 @@ package com.alibaba.nacos.config.server.remote; import com.alibaba.nacos.api.common.Constants; -import com.alibaba.nacos.api.config.remote.request.FuzzyWatchNotifyDiffRequest; +import com.alibaba.nacos.api.config.remote.request.FuzzyWatchDiffSyncRequest; 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; 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.ConfigBatchFuzzyListenEvent; import com.alibaba.nacos.config.server.service.ConfigCacheService; @@ -83,7 +84,7 @@ public ConfigFuzzyWatchDiffNotifier(ConnectionManager connectionManager, RpcPush * @param connectionManager The connection manager for managing client connections */ private static void push(FuzzyWatchRpcPushTask retryTask, ConnectionManager connectionManager) { - FuzzyWatchNotifyDiffRequest notifyRequest = retryTask.notifyRequest; + FuzzyWatchDiffSyncRequest 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 @@ -136,7 +137,7 @@ public void onEvent(ConfigBatchFuzzyListenEvent event) { } // Calculate and merge configuration states based on matched and existing group keys - List configStates = calculateAndMergeToConfigState(matchGroupKeys, clientExistingGroupKeys); + List configStates = FuzzyGroupKeyPattern.diffGroupKeys(matchGroupKeys, clientExistingGroupKeys); // If no diff config states are available, return if (CollectionUtils.isEmpty(configStates)) { @@ -145,71 +146,40 @@ public void onEvent(ConfigBatchFuzzyListenEvent event) { int batchSize = ConfigCommonConfig.getInstance().getBatchSize(); // Divide config states into batches - List> divideConfigStatesIntoBatches = divideConfigStatesIntoBatches(configStates, batchSize); + List> divideConfigStatesIntoBatches = divideConfigStatesIntoBatches(configStates, batchSize); // Calculate the number of batches and initialize push batch finish count int originBatchSize = divideConfigStatesIntoBatches.size(); AtomicInteger pushBatchFinishCount = new AtomicInteger(0); // Iterate over each batch of config states - for (List configStateList : divideConfigStatesIntoBatches) { + for (List configStateList : divideConfigStatesIntoBatches) { // Map config states to FuzzyListenNotifyDiffRequest.Context objects - Set contexts = configStateList.stream().map(state -> { + Set contexts = configStateList.stream().map(state -> { String[] parseKey = GroupKey.parseKey(state.getGroupKey()); String dataId = parseKey[0]; String group = parseKey[1]; String tenant = parseKey.length > 2 ? parseKey[2] : Constants.DEFAULT_NAMESPACE_ID; - String changeType = event.isInitializing() ? Constants.FUZZY_WATCH_INIT_NOTIFY - : (state.isExist() ? Constants.ConfigChangedType.ADD_CONFIG - : Constants.ConfigChangedType.DELETE_CONFIG); - return FuzzyWatchNotifyDiffRequest.Context.build(tenant, group, dataId, changeType); + String changeType = state.isExist() ? Constants.ConfigChangedType.ADD_CONFIG + : Constants.ConfigChangedType.DELETE_CONFIG; + return FuzzyWatchDiffSyncRequest.Context.build(tenant, group, dataId, changeType); }).collect(Collectors.toSet()); // Build FuzzyListenNotifyDiffRequest with contexts and pattern - FuzzyWatchNotifyDiffRequest request = FuzzyWatchNotifyDiffRequest.buildInitRequest(contexts, - event.getGroupKeyPattern()); + FuzzyWatchDiffSyncRequest request = + event.isInitializing() ? FuzzyWatchDiffSyncRequest.buildInitRequest(contexts, + event.getGroupKeyPattern()) + : FuzzyWatchDiffSyncRequest.buildDiffSyncRequest(contexts, event.getGroupKeyPattern()); int maxPushRetryTimes = ConfigCommonConfig.getInstance().getMaxPushRetryTimes(); // Create RPC push task and push the request to the client - FuzzyWatchRpcPushTask fuzzyWatchRpcPushTask = new FuzzyWatchRpcPushTask(request, pushBatchFinishCount, originBatchSize, maxPushRetryTimes, - event.getConnectionId(), clientIp, metaInfo.getAppName()); + FuzzyWatchRpcPushTask fuzzyWatchRpcPushTask = new FuzzyWatchRpcPushTask(request, pushBatchFinishCount, + originBatchSize, maxPushRetryTimes, event.getConnectionId(), clientIp, metaInfo.getAppName()); push(fuzzyWatchRpcPushTask, connectionManager); } } - /** - * Calculates and merges the differences between the matched group keys and the client's existing group keys into a - * list of ConfigState objects. - * - * @param matchGroupKeys The matched group keys set - * @param clientExistingGroupKeys The client's existing group keys set - * @return The merged list of ConfigState objects representing the states to be added or removed - */ - private List calculateAndMergeToConfigState(Set matchGroupKeys, - Set clientExistingGroupKeys) { - // Calculate the set of group keys to be added and removed - Set addGroupKeys = new HashSet<>(); - if (CollectionUtils.isNotEmpty(matchGroupKeys)) { - addGroupKeys.addAll(matchGroupKeys); - } - if (CollectionUtils.isNotEmpty(clientExistingGroupKeys)) { - addGroupKeys.removeAll(clientExistingGroupKeys); - } - - Set removeGroupKeys = new HashSet<>(); - if (CollectionUtils.isNotEmpty(clientExistingGroupKeys)) { - removeGroupKeys.addAll(clientExistingGroupKeys); - } - if (CollectionUtils.isNotEmpty(matchGroupKeys)) { - removeGroupKeys.removeAll(matchGroupKeys); - } - - // Convert the group keys to be added and removed into corresponding ConfigState objects and merge them into a list - return Stream.concat(addGroupKeys.stream().map(groupKey -> new ConfigState(groupKey, true)), - removeGroupKeys.stream().map(groupKey -> new ConfigState(groupKey, false))) - .collect(Collectors.toList()); - } - + @Override public Class subscribeType() { return ConfigBatchFuzzyListenEvent.class; @@ -233,68 +203,6 @@ private List> divideConfigStatesIntoBatches(Collection configStat .values()); } - /** - * ConfigState. - */ - public static class ConfigState { - - /** - * The group key associated with the configuration. - */ - private String groupKey; - - /** - * Indicates whether the configuration exists or not. - */ - private boolean exist; - - /** - * Constructs a new ConfigState instance with the given group key and existence flag. - * - * @param groupKey The group key associated with the configuration. - * @param exist {@code true} if the configuration exists, {@code false} otherwise. - */ - public ConfigState(String groupKey, boolean exist) { - this.groupKey = groupKey; - this.exist = exist; - } - - /** - * Retrieves the group key associated with the configuration. - * - * @return The group key. - */ - public String getGroupKey() { - return groupKey; - } - - /** - * Sets the group key associated with the configuration. - * - * @param groupKey The group key to set. - */ - public void setGroupKey(String groupKey) { - this.groupKey = groupKey; - } - - /** - * Checks whether the configuration exists or not. - * - * @return {@code true} if the configuration exists, {@code false} otherwise. - */ - public boolean isExist() { - return exist; - } - - /** - * Sets the existence flag of the configuration. - * - * @param exist {@code true} if the configuration exists, {@code false} otherwise. - */ - public void setExist(boolean exist) { - this.exist = exist; - } - } /** * Represents a task for pushing FuzzyListenNotifyDiffRequest to clients. @@ -304,7 +212,7 @@ class FuzzyWatchRpcPushTask implements Runnable { /** * The FuzzyListenNotifyDiffRequest to be pushed. */ - FuzzyWatchNotifyDiffRequest notifyRequest; + FuzzyWatchDiffSyncRequest notifyRequest; /** * The maximum number of times to retry pushing the request. @@ -352,7 +260,7 @@ class FuzzyWatchRpcPushTask implements Runnable { * @param clientIp The IP address of the client * @param appName The name of the client's application */ - public FuzzyWatchRpcPushTask(FuzzyWatchNotifyDiffRequest notifyRequest, AtomicInteger pushBatchFinishCount, + public FuzzyWatchRpcPushTask(FuzzyWatchDiffSyncRequest notifyRequest, AtomicInteger pushBatchFinishCount, int originBatchSize, int maxRetryTimes, String connectionId, String clientIp, String appName) { this.notifyRequest = notifyRequest; this.pushBatchFinishCount = pushBatchFinishCount; @@ -424,14 +332,15 @@ class FuzzyWatchRpcPushCallback extends AbstractPushCallBack { /** * 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 pushBatchCount The counter for tracking the number of pushed batches - * @param originBatchSize The original size of the batch before splitting + * @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 pushBatchCount The counter for tracking the number of pushed batches + * @param originBatchSize The original size of the batch before splitting */ - public FuzzyWatchRpcPushCallback(FuzzyWatchRpcPushTask fuzzyWatchRpcPushTask, TpsControlManager tpsControlManager, - ConnectionManager connectionManager, AtomicInteger pushBatchCount, int originBatchSize) { + public FuzzyWatchRpcPushCallback(FuzzyWatchRpcPushTask fuzzyWatchRpcPushTask, + TpsControlManager tpsControlManager, ConnectionManager connectionManager, AtomicInteger pushBatchCount, + int originBatchSize) { // Set the timeout for the callback super(3000L); this.fuzzyWatchRpcPushTask = fuzzyWatchRpcPushTask; @@ -454,10 +363,11 @@ public void onSuccess() { if (pushBatchCount.get() < originBatchSize) { pushBatchCount.incrementAndGet(); } else if (pushBatchCount.get() == originBatchSize) { - FuzzyWatchNotifyDiffRequest request = FuzzyWatchNotifyDiffRequest.buildInitFinishRequest( + FuzzyWatchDiffSyncRequest request = FuzzyWatchDiffSyncRequest.buildInitFinishRequest( fuzzyWatchRpcPushTask.notifyRequest.getGroupKeyPattern()); - push(new FuzzyWatchRpcPushTask(request, pushBatchCount, originBatchSize, 50, fuzzyWatchRpcPushTask.connectionId, - fuzzyWatchRpcPushTask.clientIp, fuzzyWatchRpcPushTask.appName), connectionManager); + push(new FuzzyWatchRpcPushTask(request, pushBatchCount, originBatchSize, 50, + fuzzyWatchRpcPushTask.connectionId, fuzzyWatchRpcPushTask.clientIp, + fuzzyWatchRpcPushTask.appName), connectionManager); } } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/remote/RpcFuzzyListenConfigChangeNotifier.java b/config/src/main/java/com/alibaba/nacos/config/server/remote/FuzzyWatchConfigChangeNotifier.java similarity index 87% rename from config/src/main/java/com/alibaba/nacos/config/server/remote/RpcFuzzyListenConfigChangeNotifier.java rename to config/src/main/java/com/alibaba/nacos/config/server/remote/FuzzyWatchConfigChangeNotifier.java index 7db486b8b84..2af0dddebeb 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/remote/RpcFuzzyListenConfigChangeNotifier.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/remote/FuzzyWatchConfigChangeNotifier.java @@ -16,7 +16,7 @@ package com.alibaba.nacos.config.server.remote; -import com.alibaba.nacos.api.config.remote.request.FuzzyWatchNotifyChangeRequest; +import com.alibaba.nacos.api.config.remote.request.FuzzyWatchChangeNotifyRequest; import com.alibaba.nacos.api.remote.AbstractPushCallBack; import com.alibaba.nacos.common.notify.Event; import com.alibaba.nacos.common.notify.NotifyCenter; @@ -45,14 +45,14 @@ * @author stone-98 * @date 2024/3/18 */ -@Component(value = "rpcFuzzyListenConfigChangeNotifier") -public class RpcFuzzyListenConfigChangeNotifier extends Subscriber { +@Component(value = "fuzzyWatchConfigChangeNotifier") +public class FuzzyWatchConfigChangeNotifier extends Subscriber { - private static final String POINT_FUZZY_LISTEN_CONFIG_PUSH = "POINT_FUZZY_LISTEN_CONFIG_PUSH"; + private static final String POINT_FUZZY_WATCH_CONFIG_PUSH = "POINT_FUZZY_WATCH_CONFIG_PUSH"; - private static final String POINT_FUZZY_LISTEN_CONFIG_PUSH_SUCCESS = "POINT_FUZZY_LISTEN_CONFIG_PUSH_SUCCESS"; + private static final String POINT_FUZZY_WATCH_CONFIG_PUSH_SUCCESS = "POINT_FUZZY_WATCH_CONFIG_PUSH_SUCCESS"; - private static final String POINT_FUZZY_LISTEN_CONFIG_PUSH_FAIL = "POINT_FUZZY_LISTEN_CONFIG_PUSH_FAIL"; + private static final String POINT_FUZZY_WATCH_CONFIG_PUSH_FAIL = "POINT_FUZZY_WATCH_CONFIG_PUSH_FAIL"; private final ConfigChangeListenContext configChangeListenContext; @@ -70,7 +70,7 @@ public class RpcFuzzyListenConfigChangeNotifier extends Subscriber subscribeType() { * @param retryTask The task for retrying to push notification. */ private void push(RpcPushTask retryTask) { - FuzzyWatchNotifyChangeRequest notifyRequest = retryTask.notifyRequest; + FuzzyWatchChangeNotifyRequest notifyRequest = retryTask.notifyRequest; if (retryTask.isOverTimes()) { Loggers.REMOTE_PUSH.warn( "push callback retry fail over times. dataId={},group={},tenant={},clientId={}, will unregister client.", - notifyRequest.getDataId(), notifyRequest.getGroup(), notifyRequest.getTenant(), + notifyRequest.getDataId(), notifyRequest.getGroup(), notifyRequest.getNamespace(), retryTask.connectionId); connectionManager.unregister(retryTask.connectionId); } else if (connectionManager.getConnection(retryTask.connectionId) != null) { @@ -133,7 +133,7 @@ private void push(RpcPushTask retryTask) { // Client is already offline, ignore the task. Loggers.REMOTE_PUSH.warn( "Client is already offline, ignore the task. dataId={},group={},tenant={},clientId={}", - notifyRequest.getDataId(), notifyRequest.getGroup(), notifyRequest.getTenant(), + notifyRequest.getDataId(), notifyRequest.getGroup(), notifyRequest.getNamespace(), retryTask.connectionId); } } @@ -143,7 +143,7 @@ private void push(RpcPushTask retryTask) { */ class RpcPushTask implements Runnable { - FuzzyWatchNotifyChangeRequest notifyRequest; + FuzzyWatchChangeNotifyRequest notifyRequest; int maxRetryTimes; @@ -164,7 +164,7 @@ class RpcPushTask implements Runnable { * @param clientIp The IP address of the client. * @param appName The name of the application. */ - public RpcPushTask(FuzzyWatchNotifyChangeRequest notifyRequest, int maxRetryTimes, String connectionId, + public RpcPushTask(FuzzyWatchChangeNotifyRequest notifyRequest, int maxRetryTimes, String connectionId, String clientIp, String appName) { this.notifyRequest = notifyRequest; this.maxRetryTimes = maxRetryTimes; @@ -186,7 +186,7 @@ public boolean isOverTimes() { public void run() { tryTimes++; TpsCheckRequest tpsCheckRequest = new TpsCheckRequest(); - tpsCheckRequest.setPointName(POINT_FUZZY_LISTEN_CONFIG_PUSH); + tpsCheckRequest.setPointName(POINT_FUZZY_WATCH_CONFIG_PUSH); if (!tpsControlManager.check(tpsCheckRequest).isSuccess()) { push(this); } else { @@ -195,17 +195,17 @@ public void run() { @Override public void onSuccess() { TpsCheckRequest tpsCheckRequest = new TpsCheckRequest(); - tpsCheckRequest.setPointName(POINT_FUZZY_LISTEN_CONFIG_PUSH_SUCCESS); + 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_LISTEN_CONFIG_PUSH_FAIL); + tpsCheckRequest.setPointName(POINT_FUZZY_WATCH_CONFIG_PUSH_FAIL); tpsControlManager.check(tpsCheckRequest); Loggers.REMOTE_PUSH.warn("Push fail, dataId={}, group={}, tenant={}, clientId={}", - notifyRequest.getDataId(), notifyRequest.getGroup(), notifyRequest.getTenant(), + notifyRequest.getDataId(), notifyRequest.getGroup(), notifyRequest.getNamespace(), connectionId, e); push(RpcPushTask.this); } diff --git a/example/src/main/java/com/alibaba/nacos/example/FuzzyListenExample.java b/example/src/main/java/com/alibaba/nacos/example/FuzzyListenExample.java index 516c9a50971..b3978a76b4c 100644 --- a/example/src/main/java/com/alibaba/nacos/example/FuzzyListenExample.java +++ b/example/src/main/java/com/alibaba/nacos/example/FuzzyListenExample.java @@ -18,7 +18,7 @@ import com.alibaba.nacos.api.config.ConfigFactory; import com.alibaba.nacos.api.config.ConfigService; -import com.alibaba.nacos.api.config.listener.AbstractFuzzyWatchListener; +import com.alibaba.nacos.api.config.listener.ConfigFuzzyWatcher; import com.alibaba.nacos.api.config.listener.ConfigFuzzyWatchChangeEvent; import com.alibaba.nacos.api.exception.NacosException; @@ -66,7 +66,7 @@ public static void main(String[] args) throws NacosException, InterruptedExcepti } // Define a fuzzy listener to handle configuration changes - AbstractFuzzyWatchListener listener = new AbstractFuzzyWatchListener() { + ConfigFuzzyWatcher listener = new ConfigFuzzyWatcher() { @Override public void onEvent(ConfigFuzzyWatchChangeEvent event) { System.out.println("[fuzzy listen config change]" + event.toString()); diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/index/ClientFuzzyWatchIndexesManager.java b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/index/ClientFuzzyWatchIndexesManager.java index 128bd6440f3..1adfdce239e 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/index/ClientFuzzyWatchIndexesManager.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/index/ClientFuzzyWatchIndexesManager.java @@ -1,6 +1,5 @@ package com.alibaba.nacos.naming.core.v2.index; -import com.alibaba.nacos.api.naming.utils.NamingUtils; import com.alibaba.nacos.common.notify.Event; import com.alibaba.nacos.common.notify.NotifyCenter; import com.alibaba.nacos.common.notify.listener.SmartSubscriber; @@ -23,7 +22,7 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; -import static com.alibaba.nacos.common.utils.FuzzyGroupKeyPattern.getNamespaceFromGroupKeyPattern; +import static com.alibaba.nacos.common.utils.FuzzyGroupKeyPattern.getNamespaceFromPattern; @Component public class ClientFuzzyWatchIndexesManager extends SmartSubscriber { @@ -149,7 +148,7 @@ public void removeWatchPatternMatchIndex(Service service, String matchedPattern) */ public Collection updateWatchMatchIndex(String completedPattern) { long matchBeginTime = System.currentTimeMillis(); - Collection serviceSet = ServiceManager.getInstance().getSingletons(getNamespaceFromGroupKeyPattern(completedPattern)); + Collection serviceSet = ServiceManager.getInstance().getSingletons(getNamespaceFromPattern(completedPattern)); Set matchedService = new HashSet<>(); for (Service service : serviceSet) { diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/service/impl/EphemeralClientOperationServiceImpl.java b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/service/impl/EphemeralClientOperationServiceImpl.java index 760912660c9..8ac352ff51c 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/service/impl/EphemeralClientOperationServiceImpl.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/service/impl/EphemeralClientOperationServiceImpl.java @@ -21,6 +21,7 @@ import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.api.naming.utils.NamingUtils; import com.alibaba.nacos.common.notify.NotifyCenter; +import com.alibaba.nacos.common.utils.FuzzyGroupKeyPattern; import com.alibaba.nacos.naming.core.v2.ServiceManager; import com.alibaba.nacos.naming.core.v2.client.Client; import com.alibaba.nacos.naming.core.v2.client.manager.ClientManager; @@ -139,9 +140,8 @@ public void unsubscribeService(Service service, Subscriber subscriber, String cl @Override public void fuzzyWatch(String namespaceId, String serviceNamePattern, String groupNamePattern, String clientId) { - String patternWithoutNamespace = NamingUtils.getGroupedName(serviceNamePattern, groupNamePattern); // need store namespace id in server side - String completedPattern = NamingUtils.getPatternWithNamespace(namespaceId, patternWithoutNamespace); + String completedPattern = FuzzyGroupKeyPattern.generatePattern(serviceNamePattern,groupNamePattern, namespaceId); Client client = clientManager.getClient(clientId); if (!clientIsLegal(client, clientId)) { return; @@ -153,8 +153,7 @@ public void fuzzyWatch(String namespaceId, String serviceNamePattern, String gro @Override public void cancelFuzzyWatch(String namespaceId, String serviceNamePattern, String groupNamePattern, String clientId) { - String patternWithoutNamespace = NamingUtils.getGroupedName(serviceNamePattern, groupNamePattern); - String completedPattern = NamingUtils.getPatternWithNamespace(namespaceId, patternWithoutNamespace); + String completedPattern = FuzzyGroupKeyPattern.generatePattern(serviceNamePattern,groupNamePattern, namespaceId); Client client = clientManager.getClient(clientId); if (!clientIsLegal(client, clientId)) { return; diff --git a/naming/src/main/java/com/alibaba/nacos/naming/push/v2/executor/PushExecutor.java b/naming/src/main/java/com/alibaba/nacos/naming/push/v2/executor/PushExecutor.java index 553ac555a5f..e1fc5ecb95d 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/push/v2/executor/PushExecutor.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/push/v2/executor/PushExecutor.java @@ -17,6 +17,7 @@ package com.alibaba.nacos.naming.push.v2.executor; import com.alibaba.nacos.api.naming.remote.request.AbstractFuzzyWatchNotifyRequest; +import com.alibaba.nacos.api.naming.remote.request.FuzzyWatchNotifyInitRequest; import com.alibaba.nacos.api.remote.PushCallBack; import com.alibaba.nacos.naming.pojo.Subscriber; import com.alibaba.nacos.naming.push.v2.PushDataWrapper; diff --git a/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchInitExecuteTask.java b/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchInitExecuteTask.java index b4cb47469ea..94c684ccdc4 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchInitExecuteTask.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchInitExecuteTask.java @@ -20,6 +20,7 @@ import com.alibaba.nacos.api.naming.utils.NamingUtils; import com.alibaba.nacos.api.remote.PushCallBack; import com.alibaba.nacos.common.task.AbstractExecuteTask; +import com.alibaba.nacos.common.utils.FuzzyGroupKeyPattern; import com.alibaba.nacos.naming.core.v2.client.Client; import com.alibaba.nacos.naming.core.v2.client.manager.ClientManager; import com.alibaba.nacos.naming.misc.Loggers; @@ -86,8 +87,6 @@ public void run() { ClientManager clientManager = delayTaskEngine.getClientManager(); Collection> dividedServices = divideServiceByBatch(delayTask.getMatchedService()); Client client = clientManager.getClient(clientId); - String patternWithoutNameSpace = NamingUtils.getPatternRemovedNamespace(pattern); - String nameSpaceId = NamingUtils.getNamespaceFromPattern(pattern); if (null == client) { return; } @@ -97,12 +96,12 @@ public void run() { if (isFinishInitTask || delayTask.getMatchedService().isEmpty()) { // do not match any exist service, just finish init delayTaskEngine.getPushExecutor().doFuzzyWatchNotifyPushWithCallBack(clientId, - FuzzyWatchNotifyInitRequest.buildInitFinishRequest(nameSpaceId, patternWithoutNameSpace), + FuzzyWatchNotifyInitRequest.buildInitFinishRequest(pattern), new FuzzyWatchInitPushCallback(clientId, null, originSize, true, haveFailPush)); } else { for (Collection batchData : dividedServices) { delayTaskEngine.getPushExecutor().doFuzzyWatchNotifyPushWithCallBack(clientId, FuzzyWatchNotifyInitRequest.buildInitRequest( - nameSpaceId, patternWithoutNameSpace, batchData), + pattern, batchData), new FuzzyWatchInitPushCallback(clientId, batchData, originSize, false, haveFailPush)); } } diff --git a/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchNotifyChangeDelayTask.java b/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchNotifyChangeDelayTask.java index e6e8a1893b7..224ef1cb130 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchNotifyChangeDelayTask.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchNotifyChangeDelayTask.java @@ -31,24 +31,24 @@ public class FuzzyWatchNotifyChangeDelayTask extends AbstractDelayTask { private final Service service; - private final String serviceChangedType; + private final String changedType; private boolean pushToAll; private Set targetClients; - public FuzzyWatchNotifyChangeDelayTask(Service service, String serviceChangedType, long delay) { + public FuzzyWatchNotifyChangeDelayTask(Service service, String changedType, long delay) { this.service = service; - this.serviceChangedType = serviceChangedType; + this.changedType = changedType; pushToAll = true; targetClients = null; setTaskInterval(delay); setLastProcessTime(System.currentTimeMillis()); } - public FuzzyWatchNotifyChangeDelayTask(Service service, String serviceChangedType, long delay, String targetClient) { + public FuzzyWatchNotifyChangeDelayTask(Service service, String changedType, long delay, String targetClient) { this.service = service; - this.serviceChangedType = serviceChangedType; + this.changedType = changedType; this.pushToAll = false; this.targetClients = new HashSet<>(1); this.targetClients.add(targetClient); @@ -80,8 +80,8 @@ public boolean isPushToAll() { return pushToAll; } - public String getServiceChangedType() { - return serviceChangedType; + public String getChangedType() { + return changedType; } public Set getTargetClients() { diff --git a/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchNotifyChangeExecuteTask.java b/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchNotifyChangeExecuteTask.java index 4b9fbf1cc53..493bde840c2 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchNotifyChangeExecuteTask.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchNotifyChangeExecuteTask.java @@ -22,7 +22,6 @@ import com.alibaba.nacos.naming.core.v2.client.Client; import com.alibaba.nacos.naming.core.v2.client.manager.ClientManager; import com.alibaba.nacos.naming.core.v2.index.ClientFuzzyWatchIndexesManager; -import com.alibaba.nacos.naming.core.v2.index.ClientServiceIndexesManager; import com.alibaba.nacos.naming.core.v2.pojo.Service; import com.alibaba.nacos.naming.misc.Loggers; import com.alibaba.nacos.naming.push.v2.NoRequiredRetryException; @@ -56,19 +55,19 @@ public FuzzyWatchNotifyChangeExecuteTask(Service service, FuzzyWatchPushDelayTas public void run() { try { ClientManager clientManager = delayTaskEngine.getClientManager(); - String serviceChangedType = delayTask.getServiceChangedType(); + String changedType = delayTask.getChangedType(); for (String clientId : getWatchTargetClientIds()) { Client client = clientManager.getClient(clientId); if (null == client) { continue; } delayTaskEngine.getPushExecutor().doFuzzyWatchNotifyPushWithCallBack(clientId, new FuzzyWatchNotifyChangeRequest( - service.getNamespace(), service.getName(), service.getGroup(), serviceChangedType), - new WatchNotifyPushCallback(clientId, serviceChangedType)); + service.getNamespace(), service.getName(), service.getGroup(), changedType), + new WatchNotifyPushCallback(clientId, changedType)); } } catch (Exception e) { Loggers.PUSH.error("Fuzzy watch notify task for service" + service.getGroupedServiceName() + " execute failed ", e); - delayTaskEngine.addTask(service, new FuzzyWatchNotifyChangeDelayTask(service, delayTask.getServiceChangedType(), 1000L)); + delayTaskEngine.addTask(service, new FuzzyWatchNotifyChangeDelayTask(service, delayTask.getChangedType(), 1000L)); } } 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 4cc4a480973..578c0f91dbd 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 @@ -18,7 +18,7 @@ import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.naming.remote.NamingRemoteConstants; -import com.alibaba.nacos.api.naming.remote.request.FuzzyWatchRequest; +import com.alibaba.nacos.api.naming.remote.request.NamingFuzzyWatchRequest; import com.alibaba.nacos.api.naming.remote.response.FuzzyWatchResponse; import com.alibaba.nacos.api.remote.request.RequestMeta; import com.alibaba.nacos.auth.annotation.Secured; @@ -33,7 +33,7 @@ * @author tanyongquan */ @Component("fuzzyWatchRequestHandler") -public class NamingFuzzyWatchRequestHandler extends RequestHandler { +public class NamingFuzzyWatchRequestHandler extends RequestHandler { private final EphemeralClientOperationServiceImpl clientOperationService; @@ -43,7 +43,7 @@ public NamingFuzzyWatchRequestHandler(EphemeralClientOperationServiceImpl client @Override @Secured(action = ActionTypes.READ) - public FuzzyWatchResponse handle(FuzzyWatchRequest request, RequestMeta meta) throws NacosException { + public FuzzyWatchResponse handle(NamingFuzzyWatchRequest request, RequestMeta meta) throws NacosException { String serviceNamePattern = request.getServiceName(); String groupNamePattern = request.getGroupName(); String namespaceId = request.getNamespace(); From fb2cca424f85ae45b436ee7ea6f0e7643f8aa5f2 Mon Sep 17 00:00:00 2001 From: "zunfei.lzf" Date: Mon, 6 Jan 2025 10:06:08 +0800 Subject: [PATCH 05/15] naming fuzzy watch optimize --- .../alibaba/nacos/api/model/v2/ErrorCode.java | 9 +- .../nacos/api/naming/NamingService.java | 46 ++- ...yEvent.java => FuzzyWatchChangeEvent.java} | 42 +- ...tener.java => FuzzyWatchEventWatcher.java} | 22 +- .../naming/listener/FuzzyWatchListener.java | 13 +- .../FuzzyWatchNotifyChangeRequest.java | 43 +- .../request/FuzzyWatchNotifyInitRequest.java | 17 +- .../request/NamingFuzzyWatchRequest.java | 39 +- ...nse.java => NamingFuzzyWatchResponse.java} | 26 +- .../nacos/api/naming/utils/NamingUtils.java | 20 + .../com.alibaba.nacos.api.remote.Payload | 2 +- .../client/config/NacosConfigService.java | 2 +- .../config/impl/ConfigFuzzyWatchContext.java | 26 +- .../client/naming/NacosNamingService.java | 71 ++-- .../cache/FuzzyWatchServiceListHolder.java | 122 ++---- .../naming/cache/NamingFuzzyWatchContext.java | 388 ++++++++++++++++++ .../naming/event/FuzzyWatchNotifyEvent.java | 58 ++- .../naming/event/ServicesChangeNotifier.java | 147 ------- .../naming/remote/NamingClientProxy.java | 27 -- .../remote/NamingClientProxyDelegate.java | 25 -- .../gprc/NamingFuzzyWatchRequestHandler.java | 92 +++++ .../remote/gprc/NamingGrpcClientProxy.java | 90 ++-- .../remote/gprc/NamingPushRequestHandler.java | 12 +- .../remote/gprc/redo/FuzzyWatchSyncTask.java | 58 +++ .../gprc/redo/NamingGrpcRedoService.java | 106 +---- .../remote/gprc/redo/RedoScheduledTask.java | 39 -- .../remote/http/NamingHttpClientProxy.java | 4 +- .../remote/AbstractNamingClientProxyTest.java | 17 +- .../common/utils/FuzzyGroupKeyPattern.java | 9 +- .../ConfigBatchFuzzyWatchRequestHandler.java | 7 +- .../remote/ConfigConnectionEventListener.java | 7 +- .../remote/ConfigFuzzyWatchContext.java | 98 ----- .../remote/ConfigFuzzyWatchDiffNotifier.java | 12 +- .../FuzzyWatchConfigChangeNotifier.java | 10 +- .../server/service/ConfigCacheService.java | 18 +- .../ConfigFuzzyWatchContextService.java | 174 ++++++++ .../nacos/core/utils/GlobalExecutor.java | 7 + .../alibaba/nacos/core/utils/StringPool.java | 6 +- .../nacos/example/FuzzyWatchExample.java | 17 +- .../nacos/naming/core/v2/client/Client.java | 31 -- .../v2/event/client/ClientOperationEvent.java | 29 +- .../core/v2/event/service/ServiceEvent.java | 11 +- .../index/ClientFuzzyWatchIndexesManager.java | 168 -------- .../v2/index/ClientServiceIndexesManager.java | 17 +- .../index/NamingFuzzyWatchContextService.java | 285 +++++++++++++ .../v2/service/ClientOperationService.java | 24 -- .../EphemeralClientOperationServiceImpl.java | 35 -- .../v2/NamingSubscriberServiceV2Impl.java | 33 +- .../naming/push/v2/executor/PushExecutor.java | 9 - .../v2/executor/PushExecutorDelegate.java | 6 - .../push/v2/executor/PushExecutorRpcImpl.java | 7 +- .../push/v2/executor/PushExecutorUdpImpl.java | 5 - .../FuzzyWatchChangeNotifyExecuteTask.java | 92 +++++ .../v2/task/FuzzyWatchChangeNotifyTask.java | 66 +++ ...a => FuzzyWatchInitNotifyExecuteTask.java} | 46 +-- ...ask.java => FuzzyWatchInitNotifyTask.java} | 15 +- .../task/FuzzyWatchNotifyChangeDelayTask.java | 90 ---- .../FuzzyWatchNotifyChangeExecuteTask.java | 145 ------- .../task/FuzzyWatchPushDelayTaskEngine.java | 73 ++-- .../NamingFuzzyWatchRequestHandler.java | 35 +- .../v2/NamingSubscriberServiceV2ImplTest.java | 7 +- 61 files changed, 1708 insertions(+), 1449 deletions(-) rename api/src/main/java/com/alibaba/nacos/api/naming/listener/{FuzzyWatchNotifyEvent.java => FuzzyWatchChangeEvent.java} (53%) rename api/src/main/java/com/alibaba/nacos/api/naming/listener/{AbstractFuzzyWatchEventListener.java => FuzzyWatchEventWatcher.java} (69%) rename api/src/main/java/com/alibaba/nacos/api/naming/remote/response/{FuzzyWatchResponse.java => NamingFuzzyWatchResponse.java} (63%) create mode 100644 client/src/main/java/com/alibaba/nacos/client/naming/cache/NamingFuzzyWatchContext.java delete mode 100644 client/src/main/java/com/alibaba/nacos/client/naming/event/ServicesChangeNotifier.java create mode 100644 client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/NamingFuzzyWatchRequestHandler.java create mode 100644 client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/redo/FuzzyWatchSyncTask.java delete mode 100644 config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigFuzzyWatchContext.java create mode 100644 config/src/main/java/com/alibaba/nacos/config/server/service/ConfigFuzzyWatchContextService.java delete mode 100644 naming/src/main/java/com/alibaba/nacos/naming/core/v2/index/ClientFuzzyWatchIndexesManager.java create mode 100644 naming/src/main/java/com/alibaba/nacos/naming/core/v2/index/NamingFuzzyWatchContextService.java create mode 100644 naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchChangeNotifyExecuteTask.java create mode 100644 naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchChangeNotifyTask.java rename naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/{FuzzyWatchInitExecuteTask.java => FuzzyWatchInitNotifyExecuteTask.java} (76%) rename naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/{FuzzyWatchInitDelayTask.java => FuzzyWatchInitNotifyTask.java} (83%) delete mode 100644 naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchNotifyChangeDelayTask.java delete mode 100644 naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchNotifyChangeExecuteTask.java 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 4ee3ee2e188..4c1d90509df 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 @@ -221,7 +221,14 @@ public enum ErrorCode { DATA_EMPTY(100005, "导入的文件数据为空"), - NO_SELECTED_CONFIG(100006, "没有选择任何配置"); + NO_SELECTED_CONFIG(100006, "没有选择任何配置"), + + + 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"); + + private final Integer code; diff --git a/api/src/main/java/com/alibaba/nacos/api/naming/NamingService.java b/api/src/main/java/com/alibaba/nacos/api/naming/NamingService.java index 6edc947f578..020db44ff3f 100644 --- a/api/src/main/java/com/alibaba/nacos/api/naming/NamingService.java +++ b/api/src/main/java/com/alibaba/nacos/api/naming/NamingService.java @@ -17,7 +17,7 @@ package com.alibaba.nacos.api.naming; import com.alibaba.nacos.api.exception.NacosException; -import com.alibaba.nacos.api.naming.listener.AbstractFuzzyWatchEventListener; +import com.alibaba.nacos.api.naming.listener.FuzzyWatchEventWatcher; import com.alibaba.nacos.api.naming.listener.EventListener; import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.api.naming.pojo.ListView; @@ -26,6 +26,7 @@ import com.alibaba.nacos.api.selector.AbstractSelector; import java.util.List; +import java.util.concurrent.Future; /** * Naming Service. @@ -561,11 +562,11 @@ void unsubscribe(String serviceName, String groupName, List clusters, Ev * changes occur in the services within the scope. * When given a fixed group name, watch changes in all services under this group. * - * @param fixedGroupName fixed group name for fuzzy watch + * @param groupNamePattern group name pattern for fuzzy watch * @param listener event listener * @throws NacosException nacos exception */ - void fuzzyWatch(String fixedGroupName, AbstractFuzzyWatchEventListener listener) throws NacosException; + void fuzzyWatch(String groupNamePattern, FuzzyWatchEventWatcher listener) throws NacosException; /** * According to matching rules, watch services within a specific scope, and receive notifications when @@ -574,31 +575,56 @@ void unsubscribe(String serviceName, String groupName, List clusters, Ev * this group that match the specified pattern. * * @param serviceNamePattern service name pattern for fuzzy watch - * @param fixedGroupName fixed group name for fuzzy watch + * @param groupNamePattern group name pattern for fuzzy watch * @param listener event listener * @throws NacosException nacos exception */ - void fuzzyWatch(String serviceNamePattern, String fixedGroupName, - AbstractFuzzyWatchEventListener listener) throws NacosException; + void fuzzyWatch(String serviceNamePattern, String groupNamePattern, + FuzzyWatchEventWatcher listener) throws NacosException; + + /** + * According to matching rules, watch services within a specific scope, and receive notifications when + * changes occur in the services within the scope. + * When given a fixed group name, watch changes in all services under this group. + * + * @param groupNamePattern group name pattern for fuzzy watch + * @param listener event listener + * @throws NacosException nacos exception + */ + Future> fuzzyWatchWithServiceKeys(String groupNamePattern, FuzzyWatchEventWatcher listener) throws NacosException; + + /** + * According to matching rules, watch services within a specific scope, and receive notifications when + * changes occur in the services within the scope. + * When provided with a fixed group name and pattern of service name, watch changes in services under + * this group that match the specified pattern. + * + * @param serviceNamePattern service name pattern for fuzzy watch + * @param groupNamePattern group name pattern for fuzzy watch + * @param listener event listener + * @throws NacosException nacos exception + */ + Future> fuzzyWatchWithServiceKeys(String serviceNamePattern, String groupNamePattern, + FuzzyWatchEventWatcher listener) throws NacosException; /** * Cancel fuzzy watch, and remove event listener of a pattern. * - * @param fixedGroupName fixed group name for fuzzy watch + * @param groupNamePattern group name for fuzzy watch * @param listener event listener * @throws NacosException nacos exception */ - void cancelFuzzyWatch(String fixedGroupName, AbstractFuzzyWatchEventListener listener) throws NacosException; + void cancelFuzzyWatch(String groupNamePattern, FuzzyWatchEventWatcher listener) throws NacosException; /** * Cancel fuzzy watch, and remove event listener of a pattern. * * @param serviceNamePattern service name pattern for fuzzy watch - * @param fixedGroupName fixed group name for fuzzy watch + * @param groupNamePattern fixed group name for fuzzy watch * @param listener event listener * @throws NacosException nacos exception */ - void cancelFuzzyWatch(String serviceNamePattern, String fixedGroupName, AbstractFuzzyWatchEventListener listener) throws NacosException; + void cancelFuzzyWatch(String serviceNamePattern, String groupNamePattern, FuzzyWatchEventWatcher listener) throws NacosException; /** * Unsubscribe event listener of service. diff --git a/api/src/main/java/com/alibaba/nacos/api/naming/listener/FuzzyWatchNotifyEvent.java b/api/src/main/java/com/alibaba/nacos/api/naming/listener/FuzzyWatchChangeEvent.java similarity index 53% rename from api/src/main/java/com/alibaba/nacos/api/naming/listener/FuzzyWatchNotifyEvent.java rename to api/src/main/java/com/alibaba/nacos/api/naming/listener/FuzzyWatchChangeEvent.java index 29f07309389..b444be15bde 100644 --- a/api/src/main/java/com/alibaba/nacos/api/naming/listener/FuzzyWatchNotifyEvent.java +++ b/api/src/main/java/com/alibaba/nacos/api/naming/listener/FuzzyWatchChangeEvent.java @@ -16,33 +16,53 @@ package com.alibaba.nacos.api.naming.listener; -import com.alibaba.nacos.api.naming.pojo.Service; - /** * Fuzzy Watch Notify Event. * * @author tanyongquan */ -public class FuzzyWatchNotifyEvent implements Event { +public class FuzzyWatchChangeEvent implements Event { + + private String serviceName; + + private String groupName; - private Service service; + private String namespace; private String changeType; - public FuzzyWatchNotifyEvent() { + public FuzzyWatchChangeEvent() { } - public FuzzyWatchNotifyEvent(Service service, String changeType) { - this.service = service; + public FuzzyWatchChangeEvent(String serviceName,String groupName,String namespace, String changeType) { this.changeType = changeType; + this.serviceName=serviceName; + this.groupName=groupName; + this.namespace=namespace; + } + + public String getServiceName() { + return serviceName; + } + + public void setServiceName(String serviceName) { + this.serviceName = serviceName; + } + + public String getGroupName() { + return groupName; + } + + public void setGroupName(String groupName) { + this.groupName = groupName; } - public Service getService() { - return service; + public String getNamespace() { + return namespace; } - public void setService(Service service) { - this.service = service; + public void setNamespace(String namespace) { + this.namespace = namespace; } public String getChangeType() { diff --git a/api/src/main/java/com/alibaba/nacos/api/naming/listener/AbstractFuzzyWatchEventListener.java b/api/src/main/java/com/alibaba/nacos/api/naming/listener/FuzzyWatchEventWatcher.java similarity index 69% rename from api/src/main/java/com/alibaba/nacos/api/naming/listener/AbstractFuzzyWatchEventListener.java rename to api/src/main/java/com/alibaba/nacos/api/naming/listener/FuzzyWatchEventWatcher.java index e4c23dbe43b..5489dcd7ff8 100644 --- a/api/src/main/java/com/alibaba/nacos/api/naming/listener/AbstractFuzzyWatchEventListener.java +++ b/api/src/main/java/com/alibaba/nacos/api/naming/listener/FuzzyWatchEventWatcher.java @@ -16,6 +16,10 @@ package com.alibaba.nacos.api.naming.listener; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; import java.util.concurrent.Executor; /** @@ -23,19 +27,25 @@ * * @author tanyongquan */ -public abstract class AbstractFuzzyWatchEventListener implements FuzzyWatchListener { +public abstract class FuzzyWatchEventWatcher { + + String uuid= UUID.randomUUID().toString(); + + private Set syncGroupKeys = new HashSet<>(); - String uuid; public Executor getExecutor() { return null; } - public final void setUuid(String uuid) { - this.uuid = uuid; - } - public final String getUuid() { return uuid; } + + public Set getSyncGroupKeys() { + return Collections.unmodifiableSet(syncGroupKeys); + } + + public abstract void onEvent(FuzzyWatchChangeEvent event); + } diff --git a/api/src/main/java/com/alibaba/nacos/api/naming/listener/FuzzyWatchListener.java b/api/src/main/java/com/alibaba/nacos/api/naming/listener/FuzzyWatchListener.java index ffd2090a799..bbefb455fc2 100644 --- a/api/src/main/java/com/alibaba/nacos/api/naming/listener/FuzzyWatchListener.java +++ b/api/src/main/java/com/alibaba/nacos/api/naming/listener/FuzzyWatchListener.java @@ -16,17 +16,26 @@ package com.alibaba.nacos.api.naming.listener; +import java.util.UUID; +import java.util.concurrent.Executor; + /** * Fuzzy Watch Listener. * * @author tanyongquan */ -public interface FuzzyWatchListener { +public abstract class FuzzyWatchListener { + + + String uuid= UUID.randomUUID().toString(); + + public Executor getExecutor() { + return null; + } /** * callback event. * * @param event event */ - void onEvent(FuzzyWatchNotifyEvent event); } diff --git a/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/FuzzyWatchNotifyChangeRequest.java b/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/FuzzyWatchNotifyChangeRequest.java index 1290de0825a..6d8d5dab099 100644 --- a/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/FuzzyWatchNotifyChangeRequest.java +++ b/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/FuzzyWatchNotifyChangeRequest.java @@ -27,46 +27,19 @@ */ public class FuzzyWatchNotifyChangeRequest extends AbstractFuzzyWatchNotifyRequest { + private String serviceKey; + - private String namespace; - - String serviceName; - - String groupName; - - public FuzzyWatchNotifyChangeRequest() { - } - - public FuzzyWatchNotifyChangeRequest(String namespace, String serviceName, - String groupName, String serviceChangedType) { + public FuzzyWatchNotifyChangeRequest(String serviceKey, String serviceChangedType) { super(serviceChangedType); - this.namespace=namespace; - this.serviceName = serviceName; - this.groupName = groupName; - } - - public String getNamespace() { - return namespace; + this.serviceKey=serviceKey; } - public void setNamespace(String namespace) { - this.namespace = namespace; + public String getServiceKey() { + return serviceKey; } - public String getServiceName() { - return serviceName; + public void setServiceKey(String serviceKey) { + this.serviceKey = serviceKey; } - - public void setServiceName(String serviceName) { - this.serviceName = serviceName; - } - - public String getGroupName() { - return groupName; - } - - public void setGroupName(String groupName) { - this.groupName = groupName; - } - } diff --git a/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/FuzzyWatchNotifyInitRequest.java b/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/FuzzyWatchNotifyInitRequest.java index 9fe7715880f..91f66544e4a 100644 --- a/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/FuzzyWatchNotifyInitRequest.java +++ b/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/FuzzyWatchNotifyInitRequest.java @@ -17,13 +17,10 @@ package com.alibaba.nacos.api.naming.remote.request; import com.alibaba.nacos.api.common.Constants; -import com.alibaba.nacos.api.remote.request.ServerRequest; import java.util.Collection; import java.util.HashSet; -import static com.alibaba.nacos.api.common.Constants.Naming.NAMING_MODULE; - /** * Nacos fuzzy watch initial notify request, use it when init a watch request, push service by batch. * @@ -33,14 +30,14 @@ public class FuzzyWatchNotifyInitRequest extends AbstractFuzzyWatchNotifyRequest private String pattern; - private Collection servicesName; + private Collection serviceKeys; public FuzzyWatchNotifyInitRequest() { } - private FuzzyWatchNotifyInitRequest(String pattern, String changedType, Collection servicesName) { + private FuzzyWatchNotifyInitRequest(String pattern, String changedType, Collection serviceKeys) { super(changedType); - this.servicesName = servicesName; + this.serviceKeys = serviceKeys; this.pattern = pattern; } @@ -60,12 +57,12 @@ public void setPattern(String pattern) { this.pattern = pattern; } - public Collection getServicesName() { - return servicesName; + public Collection getServiceKeys() { + return serviceKeys; } - public void setServicesName(Collection servicesName) { - this.servicesName = servicesName; + public void setServiceKeys(Collection serviceKeys) { + this.serviceKeys = serviceKeys; } } diff --git a/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/NamingFuzzyWatchRequest.java b/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/NamingFuzzyWatchRequest.java index e4b0f1a5924..d0022fc070f 100644 --- a/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/NamingFuzzyWatchRequest.java +++ b/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/NamingFuzzyWatchRequest.java @@ -16,6 +16,10 @@ package com.alibaba.nacos.api.naming.remote.request; +import com.alibaba.nacos.api.remote.request.Request; + +import java.util.Set; + import static com.alibaba.nacos.api.common.Constants.Naming.NAMING_MODULE; /** @@ -23,10 +27,19 @@ * * @author tanyongquan */ -public class NamingFuzzyWatchRequest extends AbstractNamingRequest { +public class NamingFuzzyWatchRequest extends Request { + + private boolean isInitializing; + private String namespace; + + /** + * The namespace or tenant associated with the configurations. + */ private String groupKeyPattern; + private Set receivedGroupKeys; + private String watchType; public NamingFuzzyWatchRequest() { @@ -53,6 +66,30 @@ public void setWatchType(String watchType) { this.watchType = watchType; } + public Set getReceivedGroupKeys() { + return receivedGroupKeys; + } + + public String getNamespace() { + return namespace; + } + + public void setNamespace(String namespace) { + this.namespace = namespace; + } + + public boolean isInitializing() { + return isInitializing; + } + + public void setInitializing(boolean initializing) { + isInitializing = initializing; + } + + public void setReceivedGroupKeys(Set receivedGroupKeys) { + this.receivedGroupKeys = receivedGroupKeys; + } + @Override public String getModule() { return NAMING_MODULE; diff --git a/api/src/main/java/com/alibaba/nacos/api/naming/remote/response/FuzzyWatchResponse.java b/api/src/main/java/com/alibaba/nacos/api/naming/remote/response/NamingFuzzyWatchResponse.java similarity index 63% rename from api/src/main/java/com/alibaba/nacos/api/naming/remote/response/FuzzyWatchResponse.java rename to api/src/main/java/com/alibaba/nacos/api/naming/remote/response/NamingFuzzyWatchResponse.java index 6abbad6ba54..446f6bcb504 100644 --- a/api/src/main/java/com/alibaba/nacos/api/naming/remote/response/FuzzyWatchResponse.java +++ b/api/src/main/java/com/alibaba/nacos/api/naming/remote/response/NamingFuzzyWatchResponse.java @@ -24,19 +24,19 @@ * * @author tanyongquan */ -public class FuzzyWatchResponse extends Response { +public class NamingFuzzyWatchResponse extends Response { - private String type; + private String watchType; - public FuzzyWatchResponse(){ + public NamingFuzzyWatchResponse(){ } - public FuzzyWatchResponse(String type) { - this.type = type; + public NamingFuzzyWatchResponse(String watchType) { + this.watchType = watchType; } - public static FuzzyWatchResponse buildSuccessResponse(String type) { - return new FuzzyWatchResponse(type); + public static NamingFuzzyWatchResponse buildSuccessResponse(String watchType) { + return new NamingFuzzyWatchResponse(watchType); } /** @@ -45,17 +45,17 @@ public static FuzzyWatchResponse buildSuccessResponse(String type) { * @param message error message * @return fail response */ - public static FuzzyWatchResponse buildFailResponse(String message) { - FuzzyWatchResponse result = new FuzzyWatchResponse(); + public static NamingFuzzyWatchResponse buildFailResponse(String message) { + NamingFuzzyWatchResponse result = new NamingFuzzyWatchResponse(); result.setErrorInfo(ResponseCode.FAIL.getCode(), message); return result; } - public String getType() { - return type; + public String getWatchType() { + return watchType; } - public void setType(String type) { - this.type = type; + public void setWatchType(String watchType) { + this.watchType = watchType; } } diff --git a/api/src/main/java/com/alibaba/nacos/api/naming/utils/NamingUtils.java b/api/src/main/java/com/alibaba/nacos/api/naming/utils/NamingUtils.java index 074c41749e2..26008ec0e9e 100644 --- a/api/src/main/java/com/alibaba/nacos/api/naming/utils/NamingUtils.java +++ b/api/src/main/java/com/alibaba/nacos/api/naming/utils/NamingUtils.java @@ -29,6 +29,7 @@ import java.util.regex.Pattern; import static com.alibaba.nacos.api.common.Constants.CLUSTER_NAME_PATTERN_STRING; +import static com.alibaba.nacos.api.common.Constants.DEFAULT_NAMESPACE_ID; import static com.alibaba.nacos.api.common.Constants.NUMBER_PATTERN_STRING; /** @@ -67,6 +68,25 @@ public static String getGroupedName(final String serviceName, final String group return resultGroupedName.intern(); } + public static String getServiceKey(String namespace,String group,String serviceName){ + if (StringUtils.isBlank(namespace)){ + namespace=DEFAULT_NAMESPACE_ID; + } + return namespace + Constants.SERVICE_INFO_SPLITER +group+ Constants.SERVICE_INFO_SPLITER +serviceName; + } + + /** + * parse service key items for servicekey. + * item[0] for namespace + * item[1] for group + * item[2] for service name + * @param serviceKey + * @return + */ + public static String[] parseServiceKey(String serviceKey){ + return serviceKey.split(Constants.SERVICE_INFO_SPLITER); + } + public static String getServiceName(final String serviceNameWithGroup) { if (StringUtils.isBlank(serviceNameWithGroup)) { return StringUtils.EMPTY; diff --git a/api/src/main/resources/META-INF/services/com.alibaba.nacos.api.remote.Payload b/api/src/main/resources/META-INF/services/com.alibaba.nacos.api.remote.Payload index 761df6cfecc..74d4cc1da22 100644 --- a/api/src/main/resources/META-INF/services/com.alibaba.nacos.api.remote.Payload +++ b/api/src/main/resources/META-INF/services/com.alibaba.nacos.api.remote.Payload @@ -62,7 +62,7 @@ com.alibaba.nacos.api.naming.remote.response.NotifyFuzzyWatcherResponse com.alibaba.nacos.api.naming.remote.response.QueryServiceResponse com.alibaba.nacos.api.naming.remote.response.ServiceListResponse com.alibaba.nacos.api.naming.remote.response.SubscribeServiceResponse -com.alibaba.nacos.api.naming.remote.response.FuzzyWatchResponse +com.alibaba.nacos.api.naming.remote.response.NamingFuzzyWatchResponse com.alibaba.nacos.api.config.remote.request.ConfigBatchFuzzyWatchRequest com.alibaba.nacos.api.config.remote.response.ConfigBatchFuzzyWatchResponse com.alibaba.nacos.api.config.remote.request.FuzzyWatchChangeNotifyRequest diff --git a/client/src/main/java/com/alibaba/nacos/client/config/NacosConfigService.java b/client/src/main/java/com/alibaba/nacos/client/config/NacosConfigService.java index eb61b8072dc..d7ea0ac1bb8 100644 --- a/client/src/main/java/com/alibaba/nacos/client/config/NacosConfigService.java +++ b/client/src/main/java/com/alibaba/nacos/client/config/NacosConfigService.java @@ -160,7 +160,7 @@ public Future> fuzzyWatchWithGroupKeys(String dataIdPattern, private CompletableFuture> doAddFuzzyWatch(String dataIdPattern, String fixedGroupName, ConfigFuzzyWatcher watcher) throws NacosException { ConfigFuzzyWatchContext configFuzzyWatchContext = worker.registerFuzzyWatcher(dataIdPattern, fixedGroupName,watcher); - return worker.createNewFuture(configFuzzyWatchContext); + return configFuzzyWatchContext.createNewFuture(); } @Override 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 de6804b0854..67ad9d41bb7 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 @@ -31,6 +31,8 @@ import java.util.HashSet; import java.util.List; import java.util.Set; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; import java.util.concurrent.atomic.AtomicBoolean; @@ -360,14 +362,28 @@ void syncFuzzyWatchers(){ receivedGroupKeysContext, syncGroupKeys); for(FuzzyGroupKeyPattern.GroupKeyState groupKeyState:groupKeyStates){ String[] groupKeyItems = GroupKey.parseKey(groupKeyState.getGroupKey()); - FuzzyWatchNotifyEvent fuzzyWatchNotifyEvent = FuzzyWatchNotifyEvent.buildNotifyPatternAllListenersEvent( - groupKeyItems[2], groupKeyItems[1], groupKeyItems[0], - this.groupKeyPattern, groupKeyState.isExist()? Constants.ConfigChangedType.ADD_CONFIG:Constants.ConfigChangedType.DELETE_CONFIG); - fuzzyWatchNotifyEvent.setUuid(configFuzzyWatcher.getUuid()); - NotifyCenter.publishEvent(fuzzyWatchNotifyEvent); + + String changedType=groupKeyState.isExist()? Constants.ConfigChangedType.ADD_CONFIG:Constants.ConfigChangedType.DELETE_CONFIG; + doNotifyWatcher( groupKeyItems[0], groupKeyItems[1], groupKeyItems[2],changedType,configFuzzyWatcher); } } } + + + public CompletableFuture> createNewFuture(){ + CompletableFuture> completableFuture=new CompletableFuture>(){ + @Override + public boolean isDone() { + return ConfigFuzzyWatchContext.this.initializationCompleted.get(); + } + + @Override + public Set get() throws InterruptedException, ExecutionException { + return new HashSet<>(ConfigFuzzyWatchContext.this.getReceivedGroupKeys()); + } + }; + return completableFuture; + } } diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/NacosNamingService.java b/client/src/main/java/com/alibaba/nacos/client/naming/NacosNamingService.java index eb7e8731d81..2dbda8dcbaf 100644 --- a/client/src/main/java/com/alibaba/nacos/client/naming/NacosNamingService.java +++ b/client/src/main/java/com/alibaba/nacos/client/naming/NacosNamingService.java @@ -20,7 +20,7 @@ import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.naming.NamingService; -import com.alibaba.nacos.api.naming.listener.AbstractFuzzyWatchEventListener; +import com.alibaba.nacos.api.naming.listener.FuzzyWatchEventWatcher; import com.alibaba.nacos.api.naming.listener.EventListener; import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.api.naming.pojo.ListView; @@ -31,13 +31,13 @@ import com.alibaba.nacos.api.naming.utils.NamingUtils; import com.alibaba.nacos.api.selector.AbstractSelector; import com.alibaba.nacos.client.env.NacosClientProperties; +import com.alibaba.nacos.client.naming.cache.NamingFuzzyWatchContext; import com.alibaba.nacos.client.naming.cache.ServiceInfoHolder; import com.alibaba.nacos.client.naming.cache.FuzzyWatchServiceListHolder; import com.alibaba.nacos.client.naming.core.Balancer; import com.alibaba.nacos.client.naming.event.InstancesChangeEvent; import com.alibaba.nacos.client.naming.event.InstancesChangeNotifier; import com.alibaba.nacos.client.naming.event.FuzzyWatchNotifyEvent; -import com.alibaba.nacos.client.naming.event.ServicesChangeNotifier; import com.alibaba.nacos.client.naming.event.InstancesDiff; import com.alibaba.nacos.client.naming.remote.NamingClientProxy; import com.alibaba.nacos.client.naming.remote.NamingClientProxyDelegate; @@ -51,6 +51,7 @@ import com.alibaba.nacos.client.utils.PreInitUtils; import com.alibaba.nacos.client.utils.ValidatorUtils; import com.alibaba.nacos.common.notify.NotifyCenter; +import com.alibaba.nacos.common.utils.FuzzyGroupKeyPattern; import com.alibaba.nacos.common.utils.JacksonUtils; import com.alibaba.nacos.common.utils.StringUtils; @@ -59,6 +60,7 @@ import java.util.List; import java.util.Properties; import java.util.UUID; +import java.util.concurrent.Future; import static com.alibaba.nacos.api.common.Constants.ANY_PATTERN; import static com.alibaba.nacos.client.naming.selector.NamingSelectorFactory.getUniqueClusterString; @@ -92,8 +94,6 @@ public class NacosNamingService implements NamingService { private InstancesChangeNotifier changeNotifier; - private ServicesChangeNotifier servicesChangeNotifier; - private NamingClientProxy clientProxy; private String notifierEventScope; @@ -122,14 +122,14 @@ private void init(Properties properties) throws NacosException { this.changeNotifier = new InstancesChangeNotifier(this.notifierEventScope); NotifyCenter.registerToPublisher(InstancesChangeEvent.class, 16384); NotifyCenter.registerSubscriber(changeNotifier); - this.servicesChangeNotifier = new ServicesChangeNotifier(this.notifierEventScope); - NotifyCenter.registerToPublisher(FuzzyWatchNotifyEvent.class, 16384); - NotifyCenter.registerSubscriber(servicesChangeNotifier); - this.serviceInfoHolder = new ServiceInfoHolder(namespace, this.notifierEventScope, nacosClientProperties); + + NotifyCenter.registerToPublisher(FuzzyWatchNotifyEvent.class, 16384); this.fuzzyWatchServiceListHolder = new FuzzyWatchServiceListHolder(this.notifierEventScope, nacosClientProperties); + this.clientProxy = new NamingClientProxyDelegate(this.namespace, serviceInfoHolder, fuzzyWatchServiceListHolder, nacosClientProperties, changeNotifier); + } @Deprecated @@ -540,13 +540,13 @@ private void doUnsubscribe(String serviceName, String groupName, NamingSelector } @Override - public void fuzzyWatch(String fixedGroupName, AbstractFuzzyWatchEventListener listener) throws NacosException { + public void fuzzyWatch(String fixedGroupName, FuzzyWatchEventWatcher listener) throws NacosException { doFuzzyWatch(ANY_PATTERN, fixedGroupName, listener); } @Override - public void fuzzyWatch(String serviceNamePattern, String fixedGroupName, - AbstractFuzzyWatchEventListener listener) throws NacosException { + public void fuzzyWatch(String serviceNamePattern, String groupNamePattern, + FuzzyWatchEventWatcher listener) throws NacosException { // only support prefix match right now if (!serviceNamePattern.endsWith(ANY_PATTERN)) { if (serviceNamePattern.startsWith(ANY_PATTERN)) { @@ -556,39 +556,56 @@ public void fuzzyWatch(String serviceNamePattern, String fixedGroupName, throw new UnsupportedOperationException("Illegal service name pattern, please read the documentation and pass a valid pattern."); } } - doFuzzyWatch(serviceNamePattern, fixedGroupName, listener); + doFuzzyWatch(serviceNamePattern, groupNamePattern, listener); } - private void doFuzzyWatch(String serviceNamePattern, String groupNamePattern, - AbstractFuzzyWatchEventListener listener) throws NacosException { - if (null == listener) { - return; + @Override + public Future> fuzzyWatchWithServiceKeys(String fixedGroupName, FuzzyWatchEventWatcher listener) throws NacosException { + return doFuzzyWatch(ANY_PATTERN, fixedGroupName, listener); + } + + @Override + public Future> fuzzyWatchWithServiceKeys(String serviceNamePattern, String groupNamePattern, + FuzzyWatchEventWatcher listener) throws NacosException { + return doFuzzyWatch(serviceNamePattern, groupNamePattern, listener); + } + + + private Future> doFuzzyWatch(String serviceNamePattern, String groupNamePattern, + FuzzyWatchEventWatcher watcher) throws NacosException { + if (null == watcher) { + return null; } - String uuid = UUID.randomUUID().toString(); - listener.setUuid(uuid); - servicesChangeNotifier.registerFuzzyWatchListener(serviceNamePattern, groupNamePattern, listener); - clientProxy.fuzzyWatch(serviceNamePattern, groupNamePattern, uuid); + + String groupKeyPattern = FuzzyGroupKeyPattern.generatePattern(serviceNamePattern, groupNamePattern, namespace); + NamingFuzzyWatchContext namingFuzzyWatchContext = fuzzyWatchServiceListHolder.initFuzzyWatchContextIfNeed( + groupKeyPattern); + namingFuzzyWatchContext.registerWatcher(watcher); + return namingFuzzyWatchContext.createNewFuture(); } @Override - public void cancelFuzzyWatch(String fixedGroupName, AbstractFuzzyWatchEventListener listener) throws NacosException { + public void cancelFuzzyWatch(String fixedGroupName, FuzzyWatchEventWatcher listener) throws NacosException { doCancelFuzzyWatch(ANY_PATTERN, fixedGroupName, listener); } @Override - public void cancelFuzzyWatch(String serviceNamePattern, String fixedGroupName, AbstractFuzzyWatchEventListener listener) throws NacosException { + public void cancelFuzzyWatch(String serviceNamePattern, String fixedGroupName, FuzzyWatchEventWatcher listener) throws NacosException { doCancelFuzzyWatch(serviceNamePattern, fixedGroupName, listener); } private void doCancelFuzzyWatch(String serviceNamePattern, String groupNamePattern, - AbstractFuzzyWatchEventListener listener) throws NacosException { - if (null == listener) { + FuzzyWatchEventWatcher watcher) throws NacosException { + if (null == watcher) { return; } - servicesChangeNotifier.deregisterFuzzyWatchListener(serviceNamePattern, groupNamePattern, listener); - if (!servicesChangeNotifier.isWatched(serviceNamePattern, groupNamePattern)) { - clientProxy.cancelFuzzyWatch(serviceNamePattern, groupNamePattern); + String groupKeyPattern=FuzzyGroupKeyPattern.generatePattern(serviceNamePattern,groupNamePattern,namespace); + + NamingFuzzyWatchContext namingFuzzyWatchContext = fuzzyWatchServiceListHolder.patternWatched(groupKeyPattern); + if (namingFuzzyWatchContext!=null){ + namingFuzzyWatchContext.removeWatcher(watcher); } + } @Override diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/cache/FuzzyWatchServiceListHolder.java b/client/src/main/java/com/alibaba/nacos/client/naming/cache/FuzzyWatchServiceListHolder.java index 461040eaa34..b32def7c340 100644 --- a/client/src/main/java/com/alibaba/nacos/client/naming/cache/FuzzyWatchServiceListHolder.java +++ b/client/src/main/java/com/alibaba/nacos/client/naming/cache/FuzzyWatchServiceListHolder.java @@ -17,21 +17,19 @@ package com.alibaba.nacos.client.naming.cache; import com.alibaba.nacos.api.common.Constants; -import com.alibaba.nacos.api.naming.pojo.Service; import com.alibaba.nacos.api.naming.remote.request.AbstractFuzzyWatchNotifyRequest; import com.alibaba.nacos.api.naming.remote.request.FuzzyWatchNotifyChangeRequest; import com.alibaba.nacos.api.naming.remote.request.FuzzyWatchNotifyInitRequest; import com.alibaba.nacos.api.naming.utils.NamingUtils; import com.alibaba.nacos.client.env.NacosClientProperties; import com.alibaba.nacos.client.naming.event.FuzzyWatchNotifyEvent; -import com.alibaba.nacos.client.naming.utils.CollectionUtils; +import com.alibaba.nacos.common.notify.Event; import com.alibaba.nacos.common.notify.NotifyCenter; -import com.alibaba.nacos.common.utils.ConcurrentHashSet; +import com.alibaba.nacos.common.notify.listener.Subscriber; import com.alibaba.nacos.common.utils.FuzzyGroupKeyPattern; import java.util.Collection; import java.util.Map; -import java.util.Set; import java.util.concurrent.ConcurrentHashMap; /** @@ -39,104 +37,66 @@ * * @author tanyongquan */ -public class FuzzyWatchServiceListHolder { +public class FuzzyWatchServiceListHolder extends Subscriber { private String notifierEventScope; /** * The contents of {@code patternMatchMap} are Map{pattern -> Set[matched services]}. */ - private Map> patternMatchMap = new ConcurrentHashMap<>(); + private Map fuzzyMatchContextMap = new ConcurrentHashMap<>(); public FuzzyWatchServiceListHolder(String notifierEventScope, NacosClientProperties properties) { this.notifierEventScope = notifierEventScope; } - /** - * Publish service change event notify from nacos server about fuzzy watch. - * - * @param request watch notify request from Nacos server - */ - public void processFuzzyWatchNotify(AbstractFuzzyWatchNotifyRequest request) { - if (request instanceof FuzzyWatchNotifyInitRequest) { - FuzzyWatchNotifyInitRequest watchNotifyInitRequest = (FuzzyWatchNotifyInitRequest) request; - Set matchedServiceSet = patternMatchMap.computeIfAbsent(watchNotifyInitRequest.getPattern(), - keyInner -> new ConcurrentHashSet<>()); - Collection servicesName = watchNotifyInitRequest.getServicesName(); - for (String groupedName : servicesName) { - Service service = new Service(NamingUtils.getServiceName(groupedName), NamingUtils.getGroupName(groupedName)); - // may have a 'change event' sent to client before 'init event' - if (matchedServiceSet.add(service)) { - NotifyCenter.publishEvent(FuzzyWatchNotifyEvent.buildNotifyPatternAllListenersEvent(notifierEventScope, - service, watchNotifyInitRequest.getPattern(), Constants.ServiceChangedType.ADD_SERVICE)); - } - } - } else if (request instanceof FuzzyWatchNotifyChangeRequest) { - FuzzyWatchNotifyChangeRequest notifyChangeRequest = (FuzzyWatchNotifyChangeRequest) request; - Collection matchedPattern = FuzzyGroupKeyPattern.filterMatchedPatterns(patternMatchMap.keySet(),notifyChangeRequest.getNamespace(),notifyChangeRequest.getGroupName(),notifyChangeRequest.getServiceName()); - Service service = new Service(notifyChangeRequest.getServiceName(), notifyChangeRequest.getGroupName()); - String serviceChangeType = request.getChangedType(); - - switch (serviceChangeType) { - case Constants.ServiceChangedType.ADD_SERVICE: - case Constants.ServiceChangedType.INSTANCE_CHANGED: - for (String pattern : matchedPattern) { - Set matchedServiceSet = patternMatchMap.get(pattern); - if (matchedServiceSet != null && matchedServiceSet.add(service)) { - NotifyCenter.publishEvent( - FuzzyWatchNotifyEvent.buildNotifyPatternAllListenersEvent(notifierEventScope, - service, pattern, Constants.ServiceChangedType.ADD_SERVICE)); - } - } - break; - case Constants.ServiceChangedType.DELETE_SERVICE: - for (String pattern : matchedPattern) { - Set matchedServiceSet = patternMatchMap.get(pattern); - if (matchedServiceSet != null && matchedServiceSet.remove(service)) { - NotifyCenter.publishEvent( - FuzzyWatchNotifyEvent.buildNotifyPatternAllListenersEvent(notifierEventScope, - service, pattern, Constants.ServiceChangedType.DELETE_SERVICE)); - } - } - break; - default: - break; - } - } + public NamingFuzzyWatchContext patternWatched(String groupKeyPattern){ + return fuzzyMatchContextMap.get(groupKeyPattern); } - /** - * For a duplicate fuzzy watch of a certain pattern, initiate an initialization event to the corresponding Listener. - * - * @param serviceNamePattern service name pattern. - * @param groupNamePattern group name pattern. - * @param uuid The UUID that identifies the Listener. - */ - public void duplicateFuzzyWatchInit(String serviceNamePattern, String groupNamePattern, String uuid) { - String pattern = NamingUtils.getGroupedName(serviceNamePattern, groupNamePattern); - Collection cacheServices = patternMatchMap.get(pattern); - if (cacheServices == null) { + public NamingFuzzyWatchContext initFuzzyWatchContextIfNeed(String groupKeyPattern){ + if (fuzzyMatchContextMap.containsKey(groupKeyPattern)){ + return fuzzyMatchContextMap.get(groupKeyPattern); + }else { + return fuzzyMatchContextMap.putIfAbsent(groupKeyPattern,new NamingFuzzyWatchContext(notifierEventScope,groupKeyPattern)); + } + } + + public synchronized void removePatternMatchCache(String groupkeyPattern) { + NamingFuzzyWatchContext namingFuzzyWatchContext = fuzzyMatchContextMap.get(groupkeyPattern); + if (namingFuzzyWatchContext==null){ return; } - for (Service service : cacheServices) { - NotifyCenter.publishEvent( - FuzzyWatchNotifyEvent.buildNotifyPatternSpecificListenerEvent(notifierEventScope, service, - pattern, uuid, Constants.ServiceChangedType.ADD_SERVICE)); + if (namingFuzzyWatchContext.isDiscard()&&namingFuzzyWatchContext.getNamingFuzzyWatchers().isEmpty()){ + fuzzyMatchContextMap.remove(groupkeyPattern) } } - public boolean containsPatternMatchCache(String serviceNamePattern, String groupNamePattern) { - String pattern = NamingUtils.getGroupedName(serviceNamePattern, groupNamePattern); - return CollectionUtils.isEmpty(patternMatchMap.get(pattern)); + public Map getFuzzyMatchContextMap() { + return fuzzyMatchContextMap; + } + + @Override + public void onEvent(FuzzyWatchNotifyEvent event) { + if (!event.scope().equals(notifierEventScope)){ + return; + } + String changedType = event.getChangedType(); + String serviceKey = event.getServiceKey(); + String pattern = event.getPattern(); + NamingFuzzyWatchContext namingFuzzyWatchContext = fuzzyMatchContextMap.get(pattern); + if (fuzzyMatchContextMap!=null){ + namingFuzzyWatchContext.notifyFuzzyWatchers(serviceKey,changedType); + } + } - public void removePatternMatchCache(String serviceNamePattern, String groupNamePattern) { - String pattern = NamingUtils.getGroupedName(serviceNamePattern, groupNamePattern); - patternMatchMap.remove(pattern); + @Override + public Class subscribeType() { + return FuzzyWatchNotifyEvent.class; } - public void addPatternMatchCache(String serviceNamePattern, String groupNamePattern) { - String pattern = NamingUtils.getGroupedName(serviceNamePattern, groupNamePattern); - patternMatchMap.computeIfAbsent(pattern, keyInner -> new ConcurrentHashSet<>()); + public String getNotifierEventScope() { + return notifierEventScope; } } 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 new file mode 100644 index 00000000000..ee328a890a4 --- /dev/null +++ b/client/src/main/java/com/alibaba/nacos/client/naming/cache/NamingFuzzyWatchContext.java @@ -0,0 +1,388 @@ +/* + * 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.cache; + +import com.alibaba.nacos.api.common.Constants; +import com.alibaba.nacos.api.naming.listener.FuzzyWatchChangeEvent; +import com.alibaba.nacos.api.naming.listener.FuzzyWatchEventWatcher; +import com.alibaba.nacos.api.naming.pojo.ListView; +import com.alibaba.nacos.api.naming.utils.NamingUtils; +import com.alibaba.nacos.client.utils.LogUtils; +import com.alibaba.nacos.common.utils.ConcurrentHashSet; +import com.alibaba.nacos.common.utils.FuzzyGroupKeyPattern; +import com.alibaba.nacos.common.utils.GroupKey; +import com.alibaba.nacos.common.utils.StringUtils; +import org.slf4j.Logger; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.atomic.AtomicBoolean; + +import static com.alibaba.nacos.api.common.Constants.FUZZY_WATCH_INIT_NOTIFY; + + +/** + * fuzzy wather 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. + *

+ * + * @author stone-98 + * @date 2024/3/4 + */ +public class NamingFuzzyWatchContext { + + /** + * Logger for FuzzyListenContext. + */ + private static final Logger LOGGER = LogUtils.logger(NamingFuzzyWatchContext.class); + + /** + * Environment name. + */ + private String envName; + + /** + * Task ID. + */ + private int taskId; + + private String groupKeyPattern; + + /** + * Set of service keys associated with the context. + */ + private Set receivedServiceKeys = new ConcurrentHashSet<>(); + + /** + * Flag indicating whether the context is consistent with the server. + */ + private final AtomicBoolean isConsistentWithServer = new AtomicBoolean(); + + /** + * Condition object for waiting initialization completion. + */ + final AtomicBoolean initializationCompleted = new AtomicBoolean(false); + + /** + * Flag indicating whether the context is initializing. + */ + private boolean isInitializing = false; + + /** + * Flag indicating whether the context is discarded. + */ + private volatile boolean isDiscard = false; + + /** + * Set of listeners associated with the context. + */ + private Set namingFuzzyWatchers = new HashSet<>(); + + /** + * Constructor with environment name, data ID pattern, and group. + * + * @param envName Environment name + * @param groupKeyPattern groupKeyPattern + */ + public NamingFuzzyWatchContext(String envName, String groupKeyPattern) { + this.envName = envName; + this.groupKeyPattern = groupKeyPattern; + } + + /** + * Calculate the listeners to notify based on the given UUID. + * + * @param uuid UUID to filter listeners + * @return Set of listeners to notify + */ + public Set calculateListenersToNotify(String uuid) { + Set listenersToNotify = new HashSet<>(); + if (StringUtils.isEmpty(uuid)) { + listenersToNotify = namingFuzzyWatchers; + } else { + for (FuzzyWatchEventWatcher listener : namingFuzzyWatchers) { + if (uuid.equals(listener.getUuid())) { + listenersToNotify.add(listener); + } + } + } + return listenersToNotify; + } + + /** + * Notify the listener with the specified data ID, type, and UUID. + * + * @param serviceName Data ID + * @param type Type of the event + * @param uuid UUID to filter listeners + */ + public void notifyListener(final String serviceName, final String groupName, String namespace, final String type, + final String uuid) { + Set listenersToNotify = calculateListenersToNotify(uuid); + doNotifyWatchers(serviceName, groupName, namespace, type, listenersToNotify); + } + + /** + * Perform the notification for the specified data ID, type, and listeners. + * + * @param serviceName Data ID + * @param changedType Type of the event + * @param listenersToNotify Set of listeners to notify + */ + private void doNotifyWatchers(final String serviceName, final String groupName, String namespace, final String changedType, + Set listenersToNotify) { + for (FuzzyWatchEventWatcher watcher : listenersToNotify) { + doNotifyWatcher(serviceName,groupName,namespace,changedType,watcher); + } + } + + private void doNotifyWatcher(final String serviceName, final String groupName, String namespace, final String changedType, + FuzzyWatchEventWatcher namingFuzzyWatcher){ + Runnable job = () -> { + long start = System.currentTimeMillis(); + FuzzyWatchChangeEvent event = new FuzzyWatchChangeEvent(namespace,groupName, serviceName, changedType); + if (namingFuzzyWatcher != null) { + namingFuzzyWatcher.onEvent(event); + } + LOGGER.info( + "[{}] [notify-watcher-ok] serviceName={}, groupName={}, namespace={}, watcher={}, job run cost={} millis.", + envName, serviceName, groupName, namespace, namingFuzzyWatcher, (System.currentTimeMillis() - start)); + if (changedType.equals(Constants.ConfigChangedType.DELETE_CONFIG)) { + namingFuzzyWatcher.getSyncGroupKeys().remove(GroupKey.getKey(serviceName, groupName, namespace)); + } else if (changedType.equals(FUZZY_WATCH_INIT_NOTIFY) || changedType.equals( + Constants.ConfigChangedType.ADD_CONFIG)) { + namingFuzzyWatcher.getSyncGroupKeys().add(GroupKey.getKey(serviceName, groupName, namespace)); + } + }; + + try { + if (null != namingFuzzyWatcher.getExecutor()) { + LOGGER.info( + "[{}] [notify-watcher] task submitted to user executor, serviceName={}, groupName={}, namespace={}, listener={}.", + envName, serviceName, groupName, namespace, namingFuzzyWatcher); + namingFuzzyWatcher.getExecutor().execute(job); + } else { + LOGGER.info( + "[{}] [notify-watcher] task execute in nacos thread, serviceName={}, groupName={}, namespace={}, listener={}.", + envName, serviceName, groupName, namespace, namingFuzzyWatcher); + job.run(); + } + } catch (Throwable t) { + LOGGER.error("[{}] [notify-watcher-error] serviceName={}, groupName={}, namespace={}, listener={}, throwable={}.", + envName, serviceName, groupName, namespace, namingFuzzyWatcher, t.getCause()); + } + } + /** + * Mark initialization as complete and notify waiting threads. + */ + public void markInitializationComplete() { + initializationCompleted.set(true); + } + + /** + * Remove a watcher from the context. + * + * @param watcher watcher to be removed + */ + public void removeWatcher(FuzzyWatchEventWatcher watcher) { + if(namingFuzzyWatchers.remove(watcher)){ + LOGGER.info("[{}] [remove-watcher-ok] groupKeyPattern={}, watcher={},uuid={} ", getEnvName(), + this.groupKeyPattern, watcher, watcher.getUuid()); + + } + } + + + /** + * Add a watcher to the context. + * + * @param watcher watcher to be added + */ + public void registerWatcher(FuzzyWatchEventWatcher watcher) { + if(namingFuzzyWatchers.add(watcher)){ + LOGGER.info("[{}] [add-watcher-ok] groupKeyPattern={}, watcher={},uuid={} ", getEnvName(), this.groupKeyPattern, + watcher, watcher.getUuid()); + } + + } + + /** + * Get the environment name. + * + * @return Environment name + */ + public String getEnvName() { + return envName; + } + + /** + * Set the environment name. + * + * @param envName Environment name to be set + */ + public void setEnvName(String envName) { + this.envName = envName; + } + + /** + * Get the task ID. + * + * @return Task ID + */ + public int getTaskId() { + return taskId; + } + + /** + * Set the task ID. + * + * @param taskId Task ID to be set + */ + public void setTaskId(int taskId) { + this.taskId = taskId; + } + + public String getGroupKeyPattern() { + return groupKeyPattern; + } + + /** + * Get the flag indicating whether the context is consistent with the server. + * + * @return AtomicBoolean indicating whether the context is consistent with the server + */ + public boolean isConsistentWithServer() { + return isConsistentWithServer.get(); + } + + public void setConsistentWithServer(boolean isConsistentWithServer) { + this.isConsistentWithServer.set(isConsistentWithServer); + } + + /** + * Check if the context is discarded. + * + * @return True if the context is discarded, otherwise false + */ + public boolean isDiscard() { + return isDiscard; + } + + /** + * Set the flag indicating whether the context is discarded. + * + * @param discard True to mark the context as discarded, otherwise false + */ + public void setDiscard(boolean discard) { + isDiscard = discard; + } + + /** + * Check if the context is initializing. + * + * @return True if the context is initializing, otherwise false + */ + public boolean isInitializing() { + return isInitializing; + } + + /** + * Set the flag indicating whether the context is initializing. + * + * @param initializing True to mark the context as initializing, otherwise false + */ + public void setInitializing(boolean initializing) { + isInitializing = initializing; + } + + /** + * Get the set of data IDs associated with the context. + * + * @return Set of data IDs + */ + public Set getReceivedServiceKeys() { + return Collections.unmodifiableSet(receivedServiceKeys); + } + + + public boolean addReceivedServiceKey(String serviceKey){ + return receivedServiceKeys.add(serviceKey); + } + + + public boolean removeReceivedServiceKey(String serviceKey){ + return receivedServiceKeys.remove(serviceKey); + } + + /** + * Get the set of listeners associated with the context. + * + * @return Set of listeners + */ + public Set getNamingFuzzyWatchers() { + return namingFuzzyWatchers; + } + + void syncFuzzyWatchers(){ + for(FuzzyWatchEventWatcher namingFuzzyWatcher: namingFuzzyWatchers){ + Set receivedGroupKeysContext = receivedServiceKeys; + Set syncGroupKeys = namingFuzzyWatcher.getSyncGroupKeys(); + List groupKeyStates = FuzzyGroupKeyPattern.diffGroupKeys( + receivedGroupKeysContext, syncGroupKeys); + for(FuzzyGroupKeyPattern.GroupKeyState groupKeyState:groupKeyStates){ + String[] serviceKeyItems = NamingUtils.parseServiceKey(groupKeyState.getGroupKey()); + String changedType=groupKeyState.isExist()? Constants.ConfigChangedType.ADD_CONFIG:Constants.ConfigChangedType.DELETE_CONFIG; + doNotifyWatcher(serviceKeyItems[2], serviceKeyItems[1], serviceKeyItems[0],changedType,namingFuzzyWatcher); + } + + } + } + + void notifyFuzzyWatchers(String serviceKey,String changedType){ + for(FuzzyWatchEventWatcher namingFuzzyWatcher: namingFuzzyWatchers){ + String[] serviceKeyItems = NamingUtils.parseServiceKey(serviceKey); + doNotifyWatcher(serviceKeyItems[2], serviceKeyItems[1], serviceKeyItems[0],changedType,namingFuzzyWatcher); + } + } + + public CompletableFuture> createNewFuture(){ + CompletableFuture> completableFuture=new CompletableFuture>(){ + @Override + public boolean isDone() { + return NamingFuzzyWatchContext.this.initializationCompleted.get(); + } + + @Override + public ListView get() throws InterruptedException, ExecutionException { + + ListView result = new ListView<>(); + result.setData(Arrays.asList(NamingFuzzyWatchContext.this.receivedServiceKeys.toArray(new String[0]))); + result.setCount(result.getData().size()); + return result; + } + }; + return completableFuture; + } +} + diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/event/FuzzyWatchNotifyEvent.java b/client/src/main/java/com/alibaba/nacos/client/naming/event/FuzzyWatchNotifyEvent.java index 4f39dc30645..297cdc993f5 100644 --- a/client/src/main/java/com/alibaba/nacos/client/naming/event/FuzzyWatchNotifyEvent.java +++ b/client/src/main/java/com/alibaba/nacos/client/naming/event/FuzzyWatchNotifyEvent.java @@ -16,7 +16,6 @@ package com.alibaba.nacos.client.naming.event; -import com.alibaba.nacos.api.naming.pojo.Service; import com.alibaba.nacos.common.notify.Event; /** @@ -26,40 +25,25 @@ */ public class FuzzyWatchNotifyEvent extends Event { - private final String eventScope; + private final String scope; - private final Service changedService; + private String serviceKey; private String pattern; - private String uuid; + private final String changedType; - private final String serviceChangedType; - private FuzzyWatchNotifyEvent(String eventScope, Service changedService, String pattern, String uuid, String serviceChangedType) { - this(eventScope, changedService, pattern, serviceChangedType); - this.uuid = uuid; + public FuzzyWatchNotifyEvent(String scope, + String pattern, String serviceKey,String changedType){ + this.scope = scope; + this.pattern=pattern; + this.serviceKey=serviceKey; + this.changedType=changedType; } - - private FuzzyWatchNotifyEvent(String eventScope, Service changedService, String pattern, String serviceChangedType) { - this.eventScope = eventScope; - this.changedService = changedService; - this.serviceChangedType = serviceChangedType; - this.pattern = pattern; - } - - public static FuzzyWatchNotifyEvent buildNotifyPatternSpecificListenerEvent(String eventScope, Service changedService, - String pattern, String uuid, String serviceChangedType) { - return new FuzzyWatchNotifyEvent(eventScope, changedService, pattern, uuid, serviceChangedType); - } - - public static FuzzyWatchNotifyEvent buildNotifyPatternAllListenersEvent(String eventScope, Service changedService, - String pattern, String serviceChangedType) { - return new FuzzyWatchNotifyEvent(eventScope, changedService, pattern, serviceChangedType); - } - - public Service getChangedService() { - return changedService; + public static FuzzyWatchNotifyEvent build(String eventScope, + String pattern, String serviceKey,String changedType) { + return new FuzzyWatchNotifyEvent(eventScope,pattern, serviceKey, changedType); } public String getPattern() { @@ -70,16 +54,20 @@ public void setPattern(String pattern) { this.pattern = pattern; } - public String getUuid() { - return uuid; - } - - public String getServiceChangedType() { - return serviceChangedType; + public String getChangedType() { + return changedType; } @Override public String scope() { - return this.eventScope; + return this.scope; + } + + public String getServiceKey() { + return serviceKey; + } + + public void setServiceKey(String serviceKey) { + this.serviceKey = serviceKey; } } diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/event/ServicesChangeNotifier.java b/client/src/main/java/com/alibaba/nacos/client/naming/event/ServicesChangeNotifier.java deleted file mode 100644 index 26bc5741387..00000000000 --- a/client/src/main/java/com/alibaba/nacos/client/naming/event/ServicesChangeNotifier.java +++ /dev/null @@ -1,147 +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.client.naming.event; - -import com.alibaba.nacos.api.naming.listener.AbstractFuzzyWatchEventListener; -import com.alibaba.nacos.api.naming.utils.NamingUtils; -import com.alibaba.nacos.common.JustForTest; -import com.alibaba.nacos.common.notify.Event; -import com.alibaba.nacos.common.notify.listener.Subscriber; -import com.alibaba.nacos.common.utils.CollectionUtils; -import com.alibaba.nacos.common.utils.ConcurrentHashSet; -import com.alibaba.nacos.common.utils.StringUtils; - -import java.util.Collection; -import java.util.Map; -import java.util.Set; -import java.util.UUID; -import java.util.concurrent.ConcurrentHashMap; - -/** - * A watcher to notify service change event Listener callback. - * - * @author tanyongquan - */ -public class ServicesChangeNotifier extends Subscriber { - - private final String eventScope; - - @JustForTest - public ServicesChangeNotifier() { - this.eventScope = UUID.randomUUID().toString(); - } - - public ServicesChangeNotifier(String eventScope) { - this.eventScope = eventScope; - } - - /** - * The content of map is {pattern -> Set[Listener]}. - */ - private final Map> fuzzyWatchListenerMap = new ConcurrentHashMap<>(); - - /** register fuzzy watch listener. - * This listener responds to changes of the services (not the instance's change). - * - * @param serviceNamePattern service name pattern - * @param groupNamePattern group name pattern - * @param listener custom listener - */ - public void registerFuzzyWatchListener(String serviceNamePattern, String groupNamePattern, AbstractFuzzyWatchEventListener listener) { - String key = NamingUtils.getGroupedName(serviceNamePattern, groupNamePattern); - Set eventListeners = fuzzyWatchListenerMap.computeIfAbsent(key, keyInner -> new ConcurrentHashSet<>()); - eventListeners.add(listener); - } - - /** remove fuzzy watch listener. - * - * @param serviceNamePattern service name pattern - * @param groupNamePattern group name pattern - */ - public void deregisterFuzzyWatchListener(String serviceNamePattern, String groupNamePattern, AbstractFuzzyWatchEventListener listener) { - String key = NamingUtils.getGroupedName(serviceNamePattern, groupNamePattern); - ConcurrentHashSet eventListeners = fuzzyWatchListenerMap.get(key); - if (eventListeners == null) { - return; - } - eventListeners.remove(listener); - if (CollectionUtils.isEmpty(eventListeners)) { - fuzzyWatchListenerMap.remove(key); - } - } - - /** - * check pattern is watched. - * - * @param serviceNamePattern service name pattern - * @param groupNamePattern group name pattern - * @return is pattern watched - */ - public boolean isWatched(String serviceNamePattern, String groupNamePattern) { - String key = NamingUtils.getGroupedName(serviceNamePattern, groupNamePattern); - ConcurrentHashSet eventListeners = fuzzyWatchListenerMap.get(key); - return CollectionUtils.isNotEmpty(eventListeners); - } - - /** - * receive fuzzy watch notify (fuzzy watch init or service change) from nacos server, notify all listener watch this pattern. - * If the event contains a UUID, then the event is used to notify the specified Listener when there are - * multiple watches for a particular pattern. - * - * @param event watch notify event - */ - @Override - public void onEvent(FuzzyWatchNotifyEvent event) { - String uuid = event.getUuid(); - Collection listeners = fuzzyWatchListenerMap.get(event.getPattern()); - final com.alibaba.nacos.api.naming.listener.FuzzyWatchNotifyEvent fuzzyWatchNotifyEvent = transferToWatchNotifyEvent(event); - for (AbstractFuzzyWatchEventListener each : listeners) { - // notify all listener watch this pattern - if (StringUtils.isEmpty(uuid)) { - if (each.getExecutor() != null) { - each.getExecutor().execute(() -> each.onEvent(fuzzyWatchNotifyEvent)); - } else { - each.onEvent(fuzzyWatchNotifyEvent); - } - } else if (uuid.equals(each.getUuid())) { - // notify specific listener by uuid, use in duplicate watch a same pattern - if (each.getExecutor() != null) { - each.getExecutor().execute(() -> each.onEvent(fuzzyWatchNotifyEvent)); - } else { - each.onEvent(fuzzyWatchNotifyEvent); - } - return; - } - } - } - - private com.alibaba.nacos.api.naming.listener.FuzzyWatchNotifyEvent transferToWatchNotifyEvent( - FuzzyWatchNotifyEvent fuzzyWatchNotifyEvent) { - return new com.alibaba.nacos.api.naming.listener.FuzzyWatchNotifyEvent(fuzzyWatchNotifyEvent.getChangedService(), - fuzzyWatchNotifyEvent.getServiceChangedType()); - } - - @Override - public Class subscribeType() { - return FuzzyWatchNotifyEvent.class; - } - - @Override - public boolean scopeMatches(FuzzyWatchNotifyEvent event) { - return this.eventScope.equals(event.scope()); - } -} diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/remote/NamingClientProxy.java b/client/src/main/java/com/alibaba/nacos/client/naming/remote/NamingClientProxy.java index 924f45f937e..6fc40c35c91 100644 --- a/client/src/main/java/com/alibaba/nacos/client/naming/remote/NamingClientProxy.java +++ b/client/src/main/java/com/alibaba/nacos/client/naming/remote/NamingClientProxy.java @@ -181,33 +181,6 @@ ListView getServiceList(int pageNo, int pageSize, String groupName, Abst */ boolean isSubscribed(String serviceName, String groupName, String clusters) throws NacosException; - /** - * Fuzzy watch services change by pattern. - * - * @param serviceNamePattern service name pattern - * @param groupNamePattern group name pattern - * @param uuid UUID used to identify the Listener. Used for local initialization when repeating fuzzy watch - * @throws NacosException nacos exception - */ - void fuzzyWatch(String serviceNamePattern, String groupNamePattern, String uuid) throws NacosException; - - /** Judge whether pattern has been watched. - * - * @param serviceNamePattern service name pattern - * @param groupNamePattern group name pattern - * @return {@code true} if subscribed, otherwise {@code false} - * @throws NacosException nacos exception - */ - boolean isFuzzyWatched(String serviceNamePattern, String groupNamePattern) throws NacosException; - - /** Cancel fuzzy watch pattern. - * - * @param serviceNamePattern service name pattern - * @param groupNamePattern group name pattern - * @throws NacosException nacos exception - */ - void cancelFuzzyWatch(String serviceNamePattern, String groupNamePattern) throws NacosException; - /** * Check Server healthy. * diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/remote/NamingClientProxyDelegate.java b/client/src/main/java/com/alibaba/nacos/client/naming/remote/NamingClientProxyDelegate.java index 35fba8b83dd..47bdd0f7146 100644 --- a/client/src/main/java/com/alibaba/nacos/client/naming/remote/NamingClientProxyDelegate.java +++ b/client/src/main/java/com/alibaba/nacos/client/naming/remote/NamingClientProxyDelegate.java @@ -191,31 +191,6 @@ public boolean isSubscribed(String serviceName, String groupName, String cluster return grpcClientProxy.isSubscribed(serviceName, groupName, clusters); } - @Override - public void fuzzyWatch(String serviceNamePattern, String groupNamePattern, String uuid) throws NacosException { - NAMING_LOGGER.info("[FUZZY-WATCH] serviceNamePattern:{}, groupNamePattern:{}", serviceNamePattern, groupNamePattern); - if (!fuzzyWatchServiceListHolder.containsPatternMatchCache(serviceNamePattern, groupNamePattern) - || !isFuzzyWatched(serviceNamePattern, groupNamePattern)) { - fuzzyWatchServiceListHolder.addPatternMatchCache(serviceNamePattern, groupNamePattern); - grpcClientProxy.fuzzyWatch(serviceNamePattern, groupNamePattern, ""); - } else { - fuzzyWatchServiceListHolder.duplicateFuzzyWatchInit(serviceNamePattern, groupNamePattern, uuid); - } - } - - @Override - public boolean isFuzzyWatched(String serviceNamePattern, String groupNamePattern) { - return grpcClientProxy.isFuzzyWatched(serviceNamePattern, groupNamePattern); - } - - @Override - public void cancelFuzzyWatch(String serviceNamePattern, String groupNamePattern) throws NacosException { - NAMING_LOGGER - .debug("[CANCEL-FUZZY-WATCH] serviceNamePattern:{}, groupNamePattern:{} ", serviceNamePattern, groupNamePattern); - fuzzyWatchServiceListHolder.removePatternMatchCache(serviceNamePattern, groupNamePattern); - grpcClientProxy.cancelFuzzyWatch(serviceNamePattern, groupNamePattern); - } - @Override public boolean serverHealthy() { return grpcClientProxy.serverHealthy() || httpClientProxy.serverHealthy(); diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/NamingFuzzyWatchRequestHandler.java b/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/NamingFuzzyWatchRequestHandler.java new file mode 100644 index 00000000000..bde30f71939 --- /dev/null +++ b/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/NamingFuzzyWatchRequestHandler.java @@ -0,0 +1,92 @@ +package com.alibaba.nacos.client.naming.remote.gprc; + +import com.alibaba.nacos.api.common.Constants; +import com.alibaba.nacos.api.naming.remote.request.AbstractFuzzyWatchNotifyRequest; +import com.alibaba.nacos.api.naming.remote.request.FuzzyWatchNotifyChangeRequest; +import com.alibaba.nacos.api.naming.remote.request.FuzzyWatchNotifyInitRequest; +import com.alibaba.nacos.api.naming.remote.response.NotifyFuzzyWatcherResponse; +import com.alibaba.nacos.api.naming.utils.NamingUtils; +import com.alibaba.nacos.api.remote.request.Request; +import com.alibaba.nacos.api.remote.response.Response; +import com.alibaba.nacos.client.naming.cache.FuzzyWatchServiceListHolder; +import com.alibaba.nacos.client.naming.cache.NamingFuzzyWatchContext; +import com.alibaba.nacos.client.naming.event.FuzzyWatchNotifyEvent; +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 java.util.Collection; + +public class NamingFuzzyWatchRequestHandler implements ServerRequestHandler { + + + FuzzyWatchServiceListHolder fuzzyWatchServiceListHolder; + + public NamingFuzzyWatchRequestHandler(FuzzyWatchServiceListHolder fuzzyWatchServiceListHolder){ + this.fuzzyWatchServiceListHolder=fuzzyWatchServiceListHolder; + } + + @Override + public Response requestReply(Request request, Connection connection) { + if (request instanceof FuzzyWatchNotifyInitRequest) { + FuzzyWatchNotifyInitRequest watchNotifyInitRequest = (FuzzyWatchNotifyInitRequest) request; + NamingFuzzyWatchContext namingFuzzyWatchContext = fuzzyWatchServiceListHolder.getFuzzyMatchContextMap().get(watchNotifyInitRequest.getPattern()); + if (namingFuzzyWatchContext!=null){ + Collection serviceKeys = watchNotifyInitRequest.getServiceKeys(); + if (watchNotifyInitRequest.getChangedType().equals(Constants.FUZZY_WATCH_INIT_NOTIFY)){ + for (String serviceKey : serviceKeys) { + // may have a 'change event' sent to client before 'init event' + if (namingFuzzyWatchContext.addReceivedServiceKey(serviceKey)) { + NotifyCenter.publishEvent(FuzzyWatchNotifyEvent.build(fuzzyWatchServiceListHolder.getNotifierEventScope(), + watchNotifyInitRequest.getPattern(),serviceKey, watchNotifyInitRequest.getChangedType())); + } + } + }else if(watchNotifyInitRequest.getChangedType().equals(Constants.FINISH_FUZZY_WATCH_INIT_NOTIFY)){ + namingFuzzyWatchContext.markInitializationComplete(); + } + } + + return new NotifyFuzzyWatcherResponse(); + + } else if (request instanceof FuzzyWatchNotifyChangeRequest) { + FuzzyWatchNotifyChangeRequest notifyChangeRequest = (FuzzyWatchNotifyChangeRequest) request; + String[] serviceKeyItems = NamingUtils.parseServiceKey(notifyChangeRequest.getServiceKey()); + String namespace=serviceKeyItems[0]; + String groupName=serviceKeyItems[1]; + String serviceName=serviceKeyItems[2]; + + Collection matchedPattern = FuzzyGroupKeyPattern.filterMatchedPatterns(fuzzyWatchServiceListHolder.getFuzzyMatchContextMap().keySet(),namespace,groupName,serviceName); + String serviceChangeType = notifyChangeRequest.getChangedType(); + + switch (serviceChangeType) { + case Constants.ServiceChangedType.ADD_SERVICE: + case Constants.ServiceChangedType.INSTANCE_CHANGED: + for (String pattern : matchedPattern) { + NamingFuzzyWatchContext namingFuzzyWatchContext = fuzzyWatchServiceListHolder.getFuzzyMatchContextMap().get(pattern); + if (namingFuzzyWatchContext != null && namingFuzzyWatchContext.getReceivedServiceKeys().add(((FuzzyWatchNotifyChangeRequest) request).getServiceKey())) { + //publish local service add event + NotifyCenter.publishEvent( + FuzzyWatchNotifyEvent.build(fuzzyWatchServiceListHolder.getNotifierEventScope(), + pattern,notifyChangeRequest.getServiceKey(),Constants.ServiceChangedType.ADD_SERVICE)); + } + } + break; + case Constants.ServiceChangedType.DELETE_SERVICE: + for (String pattern : matchedPattern) { + NamingFuzzyWatchContext namingFuzzyWatchContext = fuzzyWatchServiceListHolder.getFuzzyMatchContextMap().get(pattern); + if (namingFuzzyWatchContext != null && namingFuzzyWatchContext.getReceivedServiceKeys().remove(notifyChangeRequest.getServiceKey())) { + NotifyCenter.publishEvent( + FuzzyWatchNotifyEvent.build(fuzzyWatchServiceListHolder.getNotifierEventScope(), + pattern, notifyChangeRequest.getServiceKey(),Constants.ServiceChangedType.DELETE_SERVICE)); + } + } + break; + default: + break; + } + return new NotifyFuzzyWatcherResponse(); + } + return null; + } +} diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/NamingGrpcClientProxy.java b/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/NamingGrpcClientProxy.java index e5d602482c9..0435f076bc5 100644 --- a/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/NamingGrpcClientProxy.java +++ b/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/NamingGrpcClientProxy.java @@ -35,12 +35,13 @@ import com.alibaba.nacos.api.naming.remote.request.ServiceQueryRequest; import com.alibaba.nacos.api.naming.remote.request.SubscribeServiceRequest; import com.alibaba.nacos.api.naming.remote.response.BatchInstanceResponse; -import com.alibaba.nacos.api.naming.remote.response.FuzzyWatchResponse; +import com.alibaba.nacos.api.naming.remote.response.NamingFuzzyWatchResponse; import com.alibaba.nacos.api.naming.remote.response.QueryServiceResponse; import com.alibaba.nacos.api.naming.remote.response.ServiceListResponse; import com.alibaba.nacos.api.naming.remote.response.SubscribeServiceResponse; import com.alibaba.nacos.api.naming.utils.NamingUtils; import com.alibaba.nacos.api.remote.RemoteConstants; +import com.alibaba.nacos.api.remote.request.Request; import com.alibaba.nacos.api.remote.response.Response; import com.alibaba.nacos.api.remote.response.ResponseCode; import com.alibaba.nacos.api.selector.AbstractSelector; @@ -48,6 +49,7 @@ import com.alibaba.nacos.client.env.NacosClientProperties; import com.alibaba.nacos.client.monitor.MetricsMonitor; import com.alibaba.nacos.client.naming.cache.FuzzyWatchServiceListHolder; +import com.alibaba.nacos.client.naming.cache.NamingFuzzyWatchContext; import com.alibaba.nacos.client.naming.cache.ServiceInfoHolder; import com.alibaba.nacos.client.address.ServerListChangeEvent; import com.alibaba.nacos.client.naming.remote.AbstractNamingClientProxy; @@ -110,7 +112,7 @@ public NamingGrpcClientProxy(String namespaceId, SecurityProxy securityProxy, Se labels.put(Constants.APPNAME, AppNameUtils.getAppName()); this.rpcClient = RpcClientFactory.createClient(uuid, ConnectionType.GRPC, labels, RpcClientTlsConfigFactory.getInstance().createSdkConfig(properties.asProperties())); - this.redoService = new NamingGrpcRedoService(this, properties); + this.redoService = new NamingGrpcRedoService(this,fuzzyWatchServiceListHolder, properties); NAMING_LOGGER.info("Create naming rpc client for uuid->{}", uuid); start(serverListFactory, serviceInfoHolder, fuzzyWatchServiceListHolder); } @@ -119,8 +121,8 @@ private void start(ServerListFactory serverListFactory, ServiceInfoHolder servic FuzzyWatchServiceListHolder fuzzyWatchServiceListHolder) throws NacosException { rpcClient.serverListFactory(serverListFactory); rpcClient.registerConnectionListener(redoService); - rpcClient.registerServerRequestHandler(new NamingPushRequestHandler(serviceInfoHolder, - fuzzyWatchServiceListHolder)); + rpcClient.registerServerRequestHandler(new NamingPushRequestHandler(serviceInfoHolder)); + rpcClient.registerServerRequestHandler(new NamingFuzzyWatchRequestHandler(fuzzyWatchServiceListHolder)); rpcClient.start(); NotifyCenter.registerSubscriber(this); } @@ -431,78 +433,48 @@ public void doUnsubscribe(String serviceName, String groupName, String clusters) } @Override - public void fuzzyWatch(String serviceNamePattern, String groupNamePattern, String watcherUuid) throws NacosException { - if (NAMING_LOGGER.isDebugEnabled()) { - NAMING_LOGGER.debug("[GRPC-FUZZY-WATCH] servicePattern:{}, groupPattern:{}", serviceNamePattern, groupNamePattern); - } - redoService.cacheFuzzyWatcherForRedo(serviceNamePattern, groupNamePattern); - doFuzzyWatch(serviceNamePattern, groupNamePattern); + public boolean serverHealthy() { + return rpcClient.isRunning(); } /** - * Execute fuzzy watch operation. + * Determine whether nacos-server supports the capability. * - * @param serviceNamePattern service name pattern - * @param groupNamePattern group name pattern - * @throws NacosException nacos exception + * @param abilityKey ability key + * @return true if supported, otherwise false */ - public void doFuzzyWatch(String serviceNamePattern, String groupNamePattern) throws NacosException { - NamingFuzzyWatchRequest request = new NamingFuzzyWatchRequest(FuzzyGroupKeyPattern.generatePattern(serviceNamePattern,groupNamePattern,namespaceId), - NamingRemoteConstants.FUZZY_WATCH_SERVICE); - requestToServer(request, FuzzyWatchResponse.class); - redoService.fuzzyWatcherRegistered(serviceNamePattern, groupNamePattern); - } - - @Override - public boolean isFuzzyWatched(String serviceNamePattern, String groupNamePattern) { - return redoService.isFuzzyWatcherRegistered(serviceNamePattern, groupNamePattern); + public boolean isAbilitySupportedByServer(AbilityKey abilityKey) { + return rpcClient.getConnectionAbility(abilityKey) == AbilityStatus.SUPPORTED; } - @Override - public void cancelFuzzyWatch(String serviceNamePattern, String groupNamePattern) throws NacosException { - if (NAMING_LOGGER.isDebugEnabled()) { - NAMING_LOGGER - .debug("[GRPC-CANCEL-FUZZY-WATCH] serviceNamePattern:{}, groupNamePattern:{}", serviceNamePattern, groupNamePattern); - } - redoService.fuzzyWatcherDeregister(serviceNamePattern, groupNamePattern); - doCancelFuzzyWatch(serviceNamePattern, groupNamePattern); - } /** - * Execute cancel fuzzy watch operation. + * Execute unsubscribe operation. * - * @param serviceNamePattern service name pattern - * @param groupNamePattern group name pattern + * @param namingFuzzyWatchRequest namingFuzzyWatchRequest * @throws NacosException nacos exception */ - public void doCancelFuzzyWatch(String serviceNamePattern, String groupNamePattern) throws NacosException { - NamingFuzzyWatchRequest request = new NamingFuzzyWatchRequest(FuzzyGroupKeyPattern.generatePattern(serviceNamePattern,groupNamePattern,namespaceId), - NamingRemoteConstants.CANCEL_FUZZY_WATCH_SERVICE); - requestToServer(request, FuzzyWatchResponse.class); - redoService.removeFuzzyWatcherForRedo(serviceNamePattern, groupNamePattern); + public NamingFuzzyWatchResponse fuzzyWatchRequest(NamingFuzzyWatchRequest namingFuzzyWatchRequest) throws NacosException { + + return requestToServer(namingFuzzyWatchRequest, NamingFuzzyWatchResponse.class); } - @Override - public boolean serverHealthy() { - return rpcClient.isRunning(); - } - /** - * Determine whether nacos-server supports the capability. - * - * @param abilityKey ability key - * @return true if supported, otherwise false - */ - public boolean isAbilitySupportedByServer(AbilityKey abilityKey) { - return rpcClient.getConnectionAbility(abilityKey) == AbilityStatus.SUPPORTED; - } - - private T requestToServer(AbstractNamingRequest request, Class responseClass) + private T requestToServer(Request request, Class responseClass) throws NacosException { Response response = null; try { - request.putAllHeader( - getSecurityHeaders(request.getNamespace(), request.getGroupName(), request.getServiceName())); + if (request instanceof AbstractNamingRequest){ + + request.putAllHeader( + getSecurityHeaders(((AbstractNamingRequest)request).getNamespace(), ((AbstractNamingRequest)request).getGroupName(), ((AbstractNamingRequest)request).getServiceName())); + }else if(request instanceof NamingFuzzyWatchRequest){ + request.putAllHeader( + getSecurityHeaders(((NamingFuzzyWatchRequest)request).getNamespace(),null, null)); + }else{ + throw new NacosException(400,"unknown naming request type"); + } + response = requestTimeout < 0 ? rpcClient.request(request) : rpcClient.request(request, requestTimeout); if (ResponseCode.SUCCESS.getCode() != response.getResultCode()) { // If the 403 login operation is triggered, refresh the accessToken of the client @@ -534,7 +506,7 @@ private T requestToServer(AbstractNamingRequest request, Cl * successful. * @param response The response object containing registration result information, or null if registration failed. */ - private void recordRequestFailedMetrics(AbstractNamingRequest request, Exception exception, Response response) { + private void recordRequestFailedMetrics(Request request, Exception exception, Response response) { if (Objects.isNull(response)) { MetricsMonitor.getNamingRequestFailedMonitor(request.getClass().getSimpleName(), MONITOR_LABEL_NONE, MONITOR_LABEL_NONE, exception.getClass().getSimpleName()).inc(); diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/NamingPushRequestHandler.java b/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/NamingPushRequestHandler.java index 4448158890b..83c828c802d 100644 --- a/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/NamingPushRequestHandler.java +++ b/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/NamingPushRequestHandler.java @@ -16,14 +16,11 @@ package com.alibaba.nacos.client.naming.remote.gprc; -import com.alibaba.nacos.api.naming.remote.request.AbstractFuzzyWatchNotifyRequest; import com.alibaba.nacos.api.naming.remote.request.NotifySubscriberRequest; import com.alibaba.nacos.api.naming.remote.response.NotifySubscriberResponse; -import com.alibaba.nacos.api.naming.remote.response.NotifyFuzzyWatcherResponse; import com.alibaba.nacos.api.remote.request.Request; import com.alibaba.nacos.api.remote.response.Response; import com.alibaba.nacos.client.naming.cache.ServiceInfoHolder; -import com.alibaba.nacos.client.naming.cache.FuzzyWatchServiceListHolder; import com.alibaba.nacos.common.remote.client.Connection; import com.alibaba.nacos.common.remote.client.ServerRequestHandler; @@ -36,11 +33,8 @@ public class NamingPushRequestHandler implements ServerRequestHandler { private final ServiceInfoHolder serviceInfoHolder; - private final FuzzyWatchServiceListHolder fuzzyWatchServiceListHolder; - - public NamingPushRequestHandler(ServiceInfoHolder serviceInfoHolder, FuzzyWatchServiceListHolder fuzzyWatchServiceListHolder) { + public NamingPushRequestHandler(ServiceInfoHolder serviceInfoHolder) { this.serviceInfoHolder = serviceInfoHolder; - this.fuzzyWatchServiceListHolder = fuzzyWatchServiceListHolder; } @Override @@ -49,10 +43,6 @@ public Response requestReply(Request request, Connection connection) { NotifySubscriberRequest notifyRequest = (NotifySubscriberRequest) request; serviceInfoHolder.processServiceInfo(notifyRequest.getServiceInfo()); return new NotifySubscriberResponse(); - } else if (request instanceof AbstractFuzzyWatchNotifyRequest) { - AbstractFuzzyWatchNotifyRequest notifyRequest = (AbstractFuzzyWatchNotifyRequest) request; - fuzzyWatchServiceListHolder.processFuzzyWatchNotify(notifyRequest); - return new NotifyFuzzyWatcherResponse(); } return null; } diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/redo/FuzzyWatchSyncTask.java b/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/redo/FuzzyWatchSyncTask.java new file mode 100644 index 00000000000..393cdcb2371 --- /dev/null +++ b/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/redo/FuzzyWatchSyncTask.java @@ -0,0 +1,58 @@ +package com.alibaba.nacos.client.naming.remote.gprc.redo; + +import com.alibaba.nacos.api.exception.NacosException; +import com.alibaba.nacos.api.naming.remote.NamingRemoteConstants; +import com.alibaba.nacos.api.naming.remote.request.NamingFuzzyWatchRequest; +import com.alibaba.nacos.api.naming.remote.response.NamingFuzzyWatchResponse; +import com.alibaba.nacos.client.naming.cache.FuzzyWatchServiceListHolder; +import com.alibaba.nacos.client.naming.cache.NamingFuzzyWatchContext; +import com.alibaba.nacos.client.naming.remote.gprc.NamingGrpcClientProxy; +import com.alibaba.nacos.common.task.AbstractExecuteTask; + +import java.util.Iterator; +import java.util.Map; + +public class FuzzyWatchSyncTask extends AbstractExecuteTask { + + private FuzzyWatchServiceListHolder fuzzyWatchServiceListHolder; + + private final NamingGrpcClientProxy clientProxy; + + public FuzzyWatchSyncTask(FuzzyWatchServiceListHolder fuzzyWatchServiceListHolder,NamingGrpcClientProxy clientProxy ){ + this.fuzzyWatchServiceListHolder=fuzzyWatchServiceListHolder; + this.clientProxy=clientProxy; + } + + @Override + public void run() { + Iterator> iterator = fuzzyWatchServiceListHolder.getFuzzyMatchContextMap() + .entrySet().iterator(); + while (iterator.hasNext()){ + Map.Entry next = iterator.next(); + while(!next.getValue().isConsistentWithServer()){ + NamingFuzzyWatchContext namingFuzzyWatchContext = next.getValue(); + NamingFuzzyWatchRequest namingFuzzyWatchRequest=new NamingFuzzyWatchRequest(); + namingFuzzyWatchRequest.setInitializing(namingFuzzyWatchContext.isInitializing()); + namingFuzzyWatchRequest.setNamespace(clientProxy.getNamespaceId()); + namingFuzzyWatchRequest.setReceivedGroupKeys(namingFuzzyWatchContext.getReceivedServiceKeys()); + namingFuzzyWatchRequest.setGroupKeyPattern(next.getKey()); + if (namingFuzzyWatchContext.isDiscard()&&namingFuzzyWatchContext.getNamingFuzzyWatchers().isEmpty()){ + namingFuzzyWatchRequest.setWatchType(NamingRemoteConstants.CANCEL_FUZZY_WATCH_SERVICE); + }else{ + namingFuzzyWatchRequest.setWatchType(NamingRemoteConstants.FUZZY_WATCH_SERVICE); + } + try { + NamingFuzzyWatchResponse namingFuzzyWatchResponse = clientProxy.fuzzyWatchRequest( + namingFuzzyWatchRequest); + if (namingFuzzyWatchResponse.isSuccess()&&NamingRemoteConstants.CANCEL_FUZZY_WATCH_SERVICE.equals(namingFuzzyWatchRequest.getWatchType())){ + fuzzyWatchServiceListHolder.removePatternMatchCache(namingFuzzyWatchRequest.getGroupKeyPattern()); + } + } catch (NacosException e) { + throw new RuntimeException(e); + } + + } + } + + } +} diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/redo/NamingGrpcRedoService.java b/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/redo/NamingGrpcRedoService.java index 9627684aed5..71d4cd48928 100644 --- a/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/redo/NamingGrpcRedoService.java +++ b/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/redo/NamingGrpcRedoService.java @@ -22,11 +22,11 @@ import com.alibaba.nacos.api.naming.pojo.ServiceInfo; import com.alibaba.nacos.api.naming.utils.NamingUtils; import com.alibaba.nacos.client.env.NacosClientProperties; +import com.alibaba.nacos.client.naming.cache.FuzzyWatchServiceListHolder; import com.alibaba.nacos.client.naming.remote.gprc.NamingGrpcClientProxy; import com.alibaba.nacos.client.naming.remote.gprc.redo.data.BatchInstanceRedoData; import com.alibaba.nacos.client.naming.remote.gprc.redo.data.InstanceRedoData; import com.alibaba.nacos.client.naming.remote.gprc.redo.data.SubscriberRedoData; -import com.alibaba.nacos.client.naming.remote.gprc.redo.data.FuzzyWatcherRedoData; import com.alibaba.nacos.client.utils.LogUtils; import com.alibaba.nacos.common.executor.NameThreadFactory; import com.alibaba.nacos.common.remote.client.Connection; @@ -60,14 +60,15 @@ public class NamingGrpcRedoService implements ConnectionEventListener { private final ConcurrentMap subscribes = new ConcurrentHashMap<>(); - private final ConcurrentMap fuzzyWatcher = new ConcurrentHashMap<>(); + private final FuzzyWatchServiceListHolder fuzzyWatchServiceListHolder; private final ScheduledExecutorService redoExecutor; private volatile boolean connected = false; - public NamingGrpcRedoService(NamingGrpcClientProxy clientProxy, NacosClientProperties properties) { + public NamingGrpcRedoService(NamingGrpcClientProxy clientProxy,FuzzyWatchServiceListHolder fuzzyWatchServiceListHolder, NacosClientProperties properties) { setProperties(properties); + this.fuzzyWatchServiceListHolder=fuzzyWatchServiceListHolder; this.redoExecutor = new ScheduledThreadPoolExecutor(redoThreadCount, new NameThreadFactory(REDO_THREAD_NAME)); this.redoExecutor.scheduleWithFixedDelay(new RedoScheduledTask(clientProxy, this), redoDelayTime, redoDelayTime, TimeUnit.MILLISECONDS); @@ -103,8 +104,8 @@ public void onDisConnect(Connection connection) { synchronized (subscribes) { subscribes.values().forEach(subscriberRedoData -> subscriberRedoData.setRegistered(false)); } - synchronized (fuzzyWatcher) { - fuzzyWatcher.values().forEach(fuzzyWatcherRedoData -> fuzzyWatcherRedoData.setRegistered(false)); + synchronized (fuzzyWatchServiceListHolder) { + fuzzyWatchServiceListHolder.getFuzzyMatchContextMap().values().forEach(fuzzyWatcherContext -> fuzzyWatcherContext.setConsistentWithServer(false)); } LogUtils.NAMING_LOGGER.warn("mark to redo completed"); } @@ -321,101 +322,6 @@ public Set findSubscriberRedoData() { return result; } - /** - * Cache fuzzy watcher for redo. - * - * @param serviceNamePattern service name pattern - * @param groupNamePattern group name pattern - */ - public void cacheFuzzyWatcherForRedo(String serviceNamePattern, String groupNamePattern) { - String key = NamingUtils.getGroupedName(serviceNamePattern, groupNamePattern); - FuzzyWatcherRedoData redoData = FuzzyWatcherRedoData.build(serviceNamePattern, groupNamePattern); - synchronized (fuzzyWatcher) { - fuzzyWatcher.put(key, redoData); - } - } - - /** - * Fuzzy watcher register successfully, mark registered status as {@code true}. - * - * @param serviceNamePattern service name pattern - * @param groupNamePattern group name pattern - */ - public void fuzzyWatcherRegistered(String serviceNamePattern, String groupNamePattern) { - String key = NamingUtils.getGroupedName(serviceNamePattern, groupNamePattern); - synchronized (fuzzyWatcher) { - FuzzyWatcherRedoData redoData = fuzzyWatcher.get(key); - if (null != redoData) { - redoData.setRegistered(true); - } - } - } - - /** - * Fuzzy watcher deregister, mark unregistering status as {@code true}. - * - * @param serviceNamePattern service name pattern - * @param groupNamePattern group name pattern - */ - public void fuzzyWatcherDeregister(String serviceNamePattern, String groupNamePattern) { - String key = NamingUtils.getGroupedName(serviceNamePattern, groupNamePattern); - synchronized (fuzzyWatcher) { - FuzzyWatcherRedoData redoData = fuzzyWatcher.get(key); - if (null != redoData) { - redoData.setUnregistering(true); - redoData.setExpectedRegistered(false); - } - } - } - - /** - * Remove fuzzy watcher for redo. - * - * @param serviceNamePattern service name pattern - * @param groupNamePattern group name pattern - */ - public void removeFuzzyWatcherForRedo(String serviceNamePattern, String groupNamePattern) { - String key = NamingUtils.getGroupedName(serviceNamePattern, groupNamePattern); - synchronized (fuzzyWatcher) { - FuzzyWatcherRedoData redoData = fuzzyWatcher.get(key); - if (null != redoData && !redoData.isExpectedRegistered()) { - fuzzyWatcher.remove(key); - } - } - } - - /** - * Judge fuzzy watcher has registered to server. - * - * @param serviceNamePattern service name pattern - * @param groupNamePattern group name pattern - * @return {@code true} if watched, otherwise {@code false} - */ - public boolean isFuzzyWatcherRegistered(String serviceNamePattern, String groupNamePattern) { - String key = NamingUtils.getGroupedName(serviceNamePattern, groupNamePattern); - synchronized (fuzzyWatcher) { - FuzzyWatcherRedoData redoData = fuzzyWatcher.get(key); - return null != redoData && redoData.isRegistered(); - } - } - - /** - * Find all fuzzy watcher redo data which need to redo. - * - * @return set of {@code WatcherRedoData} need to redo. - */ - public Set findFuzzyWatcherRedoData() { - Set result = new HashSet<>(); - synchronized (fuzzyWatcher) { - for (FuzzyWatcherRedoData each : fuzzyWatcher.values()) { - if (each.isNeedRedo()) { - result.add(each); - } - } - } - return result; - } - /** * get Cache service. * diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/redo/RedoScheduledTask.java b/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/redo/RedoScheduledTask.java index 64908b1a0eb..99d890b3e98 100644 --- a/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/redo/RedoScheduledTask.java +++ b/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/redo/RedoScheduledTask.java @@ -22,7 +22,6 @@ import com.alibaba.nacos.client.naming.remote.gprc.redo.data.InstanceRedoData; import com.alibaba.nacos.client.naming.remote.gprc.redo.data.RedoData; import com.alibaba.nacos.client.naming.remote.gprc.redo.data.SubscriberRedoData; -import com.alibaba.nacos.client.naming.remote.gprc.redo.data.FuzzyWatcherRedoData; import com.alibaba.nacos.client.utils.LogUtils; import com.alibaba.nacos.common.task.AbstractExecuteTask; @@ -51,7 +50,6 @@ public void run() { try { redoForInstances(); redoForSubscribes(); - redoForFuzzyWatchers(); } catch (Exception e) { LogUtils.NAMING_LOGGER.warn("Redo task run with unexpected exception: ", e); } @@ -141,43 +139,6 @@ private void redoForSubscribe(SubscriberRedoData redoData) throws NacosException } } - private void redoForFuzzyWatchers() { - for (FuzzyWatcherRedoData each : redoService.findFuzzyWatcherRedoData()) { - try { - redoForFuzzyWatcher(each); - } catch (NacosException e) { - LogUtils.NAMING_LOGGER.error("Redo fuzzy watcher operation {} for pattern {}@@{} in namespace {} failed. ", - each.getRedoType(), each.getGroupName(), each.getServiceName(), clientProxy.getNamespaceId(), e); - } - } - } - - private void redoForFuzzyWatcher(FuzzyWatcherRedoData redoData) throws NacosException { - RedoData.RedoType redoType = redoData.getRedoType(); - String serviceNamePattern = redoData.getServiceName(); - String groupNamePattern = redoData.getGroupName(); - LogUtils.NAMING_LOGGER.info("Redo fuzzy watcher operation {} for pattern {}@@{} in namespace {}", redoType, - groupNamePattern, serviceNamePattern, clientProxy.getNamespaceId()); - switch (redoType) { - case REGISTER: - if (isClientDisabled()) { - return; - } - clientProxy.doFuzzyWatch(serviceNamePattern, groupNamePattern); - break; - case UNREGISTER: - if (isClientDisabled()) { - return; - } - clientProxy.doCancelFuzzyWatch(serviceNamePattern, groupNamePattern); - break; - case REMOVE: - redoService.removeFuzzyWatcherForRedo(serviceNamePattern, groupNamePattern); - break; - default: - } - } - private boolean isClientDisabled() { return !clientProxy.isEnable(); } diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/remote/http/NamingHttpClientProxy.java b/client/src/main/java/com/alibaba/nacos/client/naming/remote/http/NamingHttpClientProxy.java index d0411ebb832..34353a024ef 100644 --- a/client/src/main/java/com/alibaba/nacos/client/naming/remote/http/NamingHttpClientProxy.java +++ b/client/src/main/java/com/alibaba/nacos/client/naming/remote/http/NamingHttpClientProxy.java @@ -31,6 +31,7 @@ import com.alibaba.nacos.client.address.ServerListChangeEvent; import com.alibaba.nacos.client.env.NacosClientProperties; import com.alibaba.nacos.client.monitor.MetricsMonitor; +import com.alibaba.nacos.client.naming.cache.NamingFuzzyWatchContext; import com.alibaba.nacos.client.naming.core.NamingServerListManager; import com.alibaba.nacos.client.naming.remote.AbstractNamingClientProxy; import com.alibaba.nacos.client.naming.utils.CollectionUtils; @@ -56,6 +57,7 @@ import java.util.List; import java.util.Map; import java.util.Random; +import java.util.concurrent.Future; import static com.alibaba.nacos.client.utils.LogUtils.NAMING_LOGGER; import static com.alibaba.nacos.common.constant.RequestUrlConstants.HTTPS_PREFIX; @@ -329,7 +331,7 @@ public boolean isSubscribed(String serviceName, String groupName, String cluster } @Override - public void fuzzyWatch(String serviceNamePattern, String groupNamePattern, String uuid) throws NacosException { + public NamingFuzzyWatchContext initFuzzyWatchContext(String serviceNamePattern, String groupNamePattern) throws NacosException { throw new UnsupportedOperationException("Do not support fuzzy watch services by UDP, please use gRPC replaced."); } diff --git a/client/src/test/java/com/alibaba/nacos/client/naming/remote/AbstractNamingClientProxyTest.java b/client/src/test/java/com/alibaba/nacos/client/naming/remote/AbstractNamingClientProxyTest.java index 43e01a350bf..afb0104b8cc 100644 --- a/client/src/test/java/com/alibaba/nacos/client/naming/remote/AbstractNamingClientProxyTest.java +++ b/client/src/test/java/com/alibaba/nacos/client/naming/remote/AbstractNamingClientProxyTest.java @@ -27,6 +27,7 @@ import com.alibaba.nacos.api.selector.AbstractSelector; import com.alibaba.nacos.client.auth.ram.utils.SignUtil; import com.alibaba.nacos.client.address.ServerListChangeEvent; +import com.alibaba.nacos.client.naming.cache.NamingFuzzyWatchContext; import com.alibaba.nacos.client.security.SecurityProxy; import com.alibaba.nacos.client.utils.AppNameUtils; import com.alibaba.nacos.common.notify.Event; @@ -39,6 +40,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.Future; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -174,21 +176,6 @@ public boolean isSubscribed(String serviceName, String groupName, String cluster return false; } - @Override - public void fuzzyWatch(String serviceNamePattern, String groupNamePattern, String uuid) throws NacosException { - - } - - @Override - public void cancelFuzzyWatch(String serviceNamePattern, String groupNamePattern) throws NacosException { - - } - - @Override - public boolean isFuzzyWatched(String serviceNamePattern, String groupNamePattern) { - return false; - } - @Override public boolean serverHealthy() { return false; diff --git a/common/src/main/java/com/alibaba/nacos/common/utils/FuzzyGroupKeyPattern.java b/common/src/main/java/com/alibaba/nacos/common/utils/FuzzyGroupKeyPattern.java index e76b2da1c62..f777540f7dd 100644 --- a/common/src/main/java/com/alibaba/nacos/common/utils/FuzzyGroupKeyPattern.java +++ b/common/src/main/java/com/alibaba/nacos/common/utils/FuzzyGroupKeyPattern.java @@ -85,7 +85,7 @@ public static Set filterMatchedPatterns(Collection groupKeyPatte } Set matchedPatternList = new HashSet<>(); for (String keyPattern : groupKeyPatterns) { - if (matchPattern(keyPattern,namespace, group,resourceName)) { + if (matchPattern(keyPattern,resourceName, group,namespace)) { matchedPatternList.add(keyPattern); } } @@ -94,7 +94,7 @@ public static Set filterMatchedPatterns(Collection groupKeyPatte - public static boolean matchPattern(String groupKeyPattern,String namespace,String group,String resourceName){ + public static boolean matchPattern(String groupKeyPattern,String resourceName,String group,String namespace){ if(StringUtils.isBlank(namespace)){ namespace=DEFAULT_NAMESPACE_ID; } @@ -149,8 +149,8 @@ private static boolean itemMatched(String pattern,String resource){ * Calculates and merges the differences between the matched group keys and the client's existing group keys into a * list of ConfigState objects. * - * @param matchGroupKeys The matched group keys set - * @param clientExistingGroupKeys The client's existing group keys set + * @param basedGroupKeys The matched group keys set + * @param followedGroupKeys The client's existing group keys set * @return The merged list of ConfigState objects representing the states to be added or removed */ public static List diffGroupKeys(Set basedGroupKeys, @@ -180,6 +180,7 @@ public static List diffGroupKeys(Set basedGroupKeys, public static class GroupKeyState{ + String groupKey; boolean exist; diff --git a/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigBatchFuzzyWatchRequestHandler.java b/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigBatchFuzzyWatchRequestHandler.java index b2d9ebad6f3..1aed1fa444a 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigBatchFuzzyWatchRequestHandler.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigBatchFuzzyWatchRequestHandler.java @@ -23,6 +23,7 @@ import com.alibaba.nacos.auth.annotation.Secured; import com.alibaba.nacos.common.notify.NotifyCenter; import com.alibaba.nacos.config.server.model.event.ConfigBatchFuzzyListenEvent; +import com.alibaba.nacos.config.server.service.ConfigFuzzyWatchContextService; import com.alibaba.nacos.core.control.TpsControl; import com.alibaba.nacos.core.paramcheck.ExtractorManager; import com.alibaba.nacos.core.paramcheck.impl.ConfigBatchFuzzyListenRequestParamsExtractor; @@ -54,7 +55,7 @@ public class ConfigBatchFuzzyWatchRequestHandler * Context for managing fuzzy listen changes. */ @Autowired - private ConfigFuzzyWatchContext configFuzzyWatchContext; + private ConfigFuzzyWatchContextService configFuzzyWatchContextService; /** * Handles the batch fuzzy listen request. @@ -79,7 +80,7 @@ public ConfigBatchFuzzyWatchResponse handle(ConfigBatchFuzzyWatchRequest request String groupKeyPattern = context.getGroupKeyPattern(); if (context.isListen()) { // Add client to the fuzzy listening context - configFuzzyWatchContext.addFuzzyListen(groupKeyPattern, connectionId); + configFuzzyWatchContextService.addFuzzyListen(groupKeyPattern, connectionId); // Get existing group keys for the client and publish initialization event Set clientExistingGroupKeys = context.getReceivedGroupKeys(); NotifyCenter.publishEvent( @@ -87,7 +88,7 @@ public ConfigBatchFuzzyWatchResponse handle(ConfigBatchFuzzyWatchRequest request context.isInitializing())); } else { // Remove client from the fuzzy listening context - configFuzzyWatchContext.removeFuzzyListen(groupKeyPattern, connectionId); + configFuzzyWatchContextService.removeFuzzyListen(groupKeyPattern, connectionId); } } // Return response diff --git a/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigConnectionEventListener.java b/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigConnectionEventListener.java index bd9ce128f0b..7c49201198b 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigConnectionEventListener.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigConnectionEventListener.java @@ -16,6 +16,7 @@ package com.alibaba.nacos.config.server.remote; +import com.alibaba.nacos.config.server.service.ConfigFuzzyWatchContextService; import com.alibaba.nacos.core.remote.ClientConnectionEventListener; import com.alibaba.nacos.core.remote.Connection; import com.alibaba.nacos.core.utils.Loggers; @@ -32,8 +33,11 @@ public class ConfigConnectionEventListener extends ClientConnectionEventListener final ConfigChangeListenContext configChangeListenContext; - public ConfigConnectionEventListener(ConfigChangeListenContext configChangeListenContext) { + final ConfigFuzzyWatchContextService configFuzzyWatchContextService; + + public ConfigConnectionEventListener(ConfigChangeListenContext configChangeListenContext,ConfigFuzzyWatchContextService configFuzzyWatchContextService) { this.configChangeListenContext = configChangeListenContext; + this.configFuzzyWatchContextService=configFuzzyWatchContextService; } @Override @@ -46,6 +50,7 @@ public void clientDisConnected(Connection connect) { String connectionId = connect.getMetaInfo().getConnectionId(); Loggers.REMOTE_DIGEST.info("[{}]client disconnected,clear config listen context", connectionId); configChangeListenContext.clearContextForConnectionId(connectionId); + configFuzzyWatchContextService.clearFuzzyWatchContext(connectionId); } } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigFuzzyWatchContext.java b/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigFuzzyWatchContext.java deleted file mode 100644 index 6a13408c259..00000000000 --- a/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigFuzzyWatchContext.java +++ /dev/null @@ -1,98 +0,0 @@ -package com.alibaba.nacos.config.server.remote; - - -import com.alibaba.nacos.common.utils.CollectionUtils; -import com.alibaba.nacos.common.utils.FuzzyGroupKeyPattern; -import com.alibaba.nacos.config.server.utils.GroupKey2; -import org.springframework.stereotype.Component; - -import java.util.HashSet; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; - -@Component -public class ConfigFuzzyWatchContext { - - /** - * groupKeyPattern -> connection set. - */ - private final Map> keyPatternContext = new ConcurrentHashMap<>(); - - /** - * Adds a fuzzy listen connection ID associated with the specified group key pattern. If the key pattern does not - * exist in the context, a new entry will be created. If the key pattern already exists, the connection ID will be - * added to the existing set. - * - * @param groupKeyPattern The group key pattern to associate with the listen connection. - * @param connectId The connection ID to be added. - */ - public synchronized void addFuzzyListen(String groupKeyPattern, String connectId) { - // Add the connection ID to the set associated with the key pattern in keyPatternContext - keyPatternContext.computeIfAbsent(groupKeyPattern, k -> new HashSet<>()).add(connectId); - } - - /** - * Removes a fuzzy listen connection ID associated with the specified group key pattern. If the group key pattern - * exists in the context and the connection ID is found in the associated set, the connection ID will be removed - * from the set. If the set becomes empty after removal, the entry for the group key pattern will be removed from - * the context. - * - * @param groupKeyPattern The group key pattern associated with the listen connection to be removed. - * @param connectionId The connection ID to be removed. - */ - public synchronized void removeFuzzyListen(String groupKeyPattern, String connectionId) { - // Retrieve the set of connection IDs associated with the group key pattern - Set connectIds = keyPatternContext.get(groupKeyPattern); - if (CollectionUtils.isNotEmpty(connectIds)) { - // Remove the connection ID from the set if it exists - connectIds.remove(connectionId); - // Remove the entry for the group key pattern if the set becomes empty after removal - if (connectIds.isEmpty()) { - keyPatternContext.remove(groupKeyPattern); - } - } - } - - - public void removeFuzzyWatchContext(String connectionId){ - for (Map.Entry> keyPatternContextEntry : keyPatternContext.entrySet()) { - String keyPattern = keyPatternContextEntry.getKey(); - Set connectionIds = keyPatternContextEntry.getValue(); - if (CollectionUtils.isEmpty(connectionIds)) { - keyPatternContext.remove(keyPattern); - } else { - connectionIds.remove(connectionId); - if (CollectionUtils.isEmpty(connectionIds)) { - keyPatternContext.remove(keyPattern); - } - } - } - } - - /** - * Retrieves the set of connection IDs matched with the specified group key. - * - * @param groupKey The group key to match with the key patterns. - * @return The set of connection IDs matched with the group key. - */ - public Set getConnectIdMatchedPatterns(String groupKey) { - // Initialize a set to store the matched connection IDs - Set connectIds = new HashSet<>(); - // Iterate over each key pattern in the context - for (String keyPattern : keyPatternContext.keySet()) { - // Check if the group key matches the current key pattern - String[] strings = GroupKey2.parseKey(groupKey); - - if (FuzzyGroupKeyPattern.matchPattern(groupKey,strings[0],strings[1],strings[2])) { - // If matched, add the associated connection IDs to the set - Set connectIdSet = keyPatternContext.get(keyPattern); - if (CollectionUtils.isNotEmpty(connectIdSet)) { - connectIds.addAll(connectIdSet); - } - } - } - return connectIds; - } - -} diff --git a/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigFuzzyWatchDiffNotifier.java b/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigFuzzyWatchDiffNotifier.java index ffc4444ea28..ca93658e5ab 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigFuzzyWatchDiffNotifier.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigFuzzyWatchDiffNotifier.java @@ -26,7 +26,7 @@ import com.alibaba.nacos.common.utils.FuzzyGroupKeyPattern; import com.alibaba.nacos.config.server.configuration.ConfigCommonConfig; import com.alibaba.nacos.config.server.model.event.ConfigBatchFuzzyListenEvent; -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.Connection; @@ -41,13 +41,11 @@ import java.util.ArrayList; import java.util.Collection; -import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; -import java.util.stream.Stream; /** * Handles batch fuzzy listen events and pushes corresponding notifications to clients. @@ -70,10 +68,14 @@ public class ConfigFuzzyWatchDiffNotifier extends Subscriber matchGroupKeys = ConfigCacheService.matchGroupKeys(event.getGroupKeyPattern()); + Set matchGroupKeys = configFuzzyWatchContextService.matchGroupKeys(event.getGroupKeyPattern()); // Retrieve existing group keys for the client from the event Set clientExistingGroupKeys = event.getClientExistingGroupKeys(); diff --git a/config/src/main/java/com/alibaba/nacos/config/server/remote/FuzzyWatchConfigChangeNotifier.java b/config/src/main/java/com/alibaba/nacos/config/server/remote/FuzzyWatchConfigChangeNotifier.java index 2af0dddebeb..1ed9239cf26 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/remote/FuzzyWatchConfigChangeNotifier.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/remote/FuzzyWatchConfigChangeNotifier.java @@ -24,6 +24,7 @@ import com.alibaba.nacos.config.server.configuration.ConfigCommonConfig; 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.Connection; @@ -62,7 +63,7 @@ public class FuzzyWatchConfigChangeNotifier extends Subscriber 2 ? parseKey[2] : ""; - for (String clientId : configFuzzyWatchContext.getConnectIdMatchedPatterns(groupKey)) { + for (String clientId : configFuzzyWatchContextService.getMatchedClients(groupKey)) { Connection connection = connectionManager.getConnection(clientId); if (null == connection) { Loggers.REMOTE_PUSH.warn( diff --git a/config/src/main/java/com/alibaba/nacos/config/server/service/ConfigCacheService.java b/config/src/main/java/com/alibaba/nacos/config/server/service/ConfigCacheService.java index 37f3cc4591b..9dcb9b8c7a7 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/service/ConfigCacheService.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/service/ConfigCacheService.java @@ -65,27 +65,13 @@ public class ConfigCacheService { /** * groupKey -> cacheItem. */ - private static final ConcurrentHashMap CACHE = new ConcurrentHashMap<>(); + static final ConcurrentHashMap CACHE = new ConcurrentHashMap<>(); public static int groupCount() { return CACHE.size(); } - /** - * Matches the client effective group keys based on the specified group key pattern, client IP, and tag. - * - * @param groupKeyPattern The pattern to match group keys. - * @return A set of group keys that match the pattern and are effective for the client. - */ - public static Set matchGroupKeys(String groupKeyPattern) { - - String[] groupKeyItems = GroupKey2.parseKey(groupKeyPattern); - return CACHE.entrySet().stream() - .filter(entry -> FuzzyGroupKeyPattern.matchPattern(groupKeyPattern,groupKeyItems[0],groupKeyItems[1],groupKeyItems[2])) - .map(Map.Entry::getKey) - .collect(Collectors.toSet()); - } - + /** * Save config file and update md5 value in cache. * 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 new file mode 100644 index 00000000000..59ef34ceb63 --- /dev/null +++ b/config/src/main/java/com/alibaba/nacos/config/server/service/ConfigFuzzyWatchContextService.java @@ -0,0 +1,174 @@ +package com.alibaba.nacos.config.server.service; + + +import com.alibaba.nacos.api.exception.runtime.NacosRuntimeException; +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.config.server.utils.GroupKey2; +import com.alibaba.nacos.core.utils.GlobalExecutor; +import org.springframework.stereotype.Component; + +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +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; + +@Component +public class ConfigFuzzyWatchContextService { + + /** + * groupKeyPattern -> watched connection id set. + */ + private final Map> keyPatternWatchClients = new ConcurrentHashMap<>(); + + /** + * groupKeyPattern -> groupKeys set. + */ + private final Map> groupKeyMatched = new ConcurrentHashMap<>(); + + private final int FUZZY_WATCH_MAX_PATTERN_COUNT = 50; + + private final int FUZZY_WATCH_MAX_PATTERN_MATCHED_GROUP_KEY_COUNT = 200; + + + public ConfigFuzzyWatchContextService(){ + + GlobalExecutor.scheduleWithFixDelayByCommon(() -> clearFuzzyWatchContext(), 30000); + } + + private void clearFuzzyWatchContext(){ + Iterator>> iterator = groupKeyMatched.entrySet().iterator(); + while (iterator.hasNext()){ + Map.Entry> next = iterator.next(); + Set watchedClients = keyPatternWatchClients.get(next.getKey()); + if(CollectionUtils.isEmpty(watchedClients)){ + iterator.remove(); + if (watchedClients!=null){ + keyPatternWatchClients.remove(next.getKey()); + } + } + } + } + /** + * get matched exist group keys with the groupKeyPattern. + * return null if not matched + * @param groupKeyPattern + * @return + */ + public Set matchGroupKeys(String groupKeyPattern) { + if (groupKeyMatched.containsKey(groupKeyPattern)) { + return groupKeyMatched.get(groupKeyPattern); + } else { + return null; + } + } + + + /** + * Matches the client effective group keys based on the specified group key pattern, client IP, and tag. + * + * @param groupKeyPattern The pattern to match group keys. + * @return A set of group keys that match the pattern and are effective for the client. + * @throws NacosRuntimeException, + */ + private void initMatchGroupKeys(String groupKeyPattern) { + if (groupKeyMatched.containsKey(groupKeyPattern)) { + return; + } + + if (groupKeyMatched.size() >= FUZZY_WATCH_MAX_PATTERN_COUNT) { + throw new NacosRuntimeException(FUZZY_WATCH_PATTERN_OVER_LIMIT.getCode(),FUZZY_WATCH_PATTERN_OVER_LIMIT.getMsg()); + } + Set matchedGroupKeys = groupKeyMatched.computeIfAbsent(groupKeyPattern, k -> new HashSet<>()); + + 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) { + throw new NacosRuntimeException(FUZZY_WATCH_PATTERN_MATCH_GROUP_KEY_OVER_LIMIT.getCode(), FUZZY_WATCH_PATTERN_MATCH_GROUP_KEY_OVER_LIMIT.getMsg()); + } + matchedGroupKeys.add(groupKey); + } + } + + } + + /** + * Adds a fuzzy listen connection ID associated with the specified group key pattern. If the key pattern does not + * exist in the context, a new entry will be created. If the key pattern already exists, the connection ID will be + * added to the existing set. + * + * @param groupKeyPattern The group key pattern to associate with the listen connection. + * @param connectId The connection ID to be added. + */ + public synchronized void addFuzzyListen(String groupKeyPattern, String connectId) { + + initMatchGroupKeys(groupKeyPattern); + // Add the connection ID to the set associated with the key pattern in keyPatternContext + keyPatternWatchClients.computeIfAbsent(groupKeyPattern, k -> new HashSet<>()).add(connectId); + } + + /** + * Removes a fuzzy listen connection ID associated with the specified group key pattern. If the group key pattern + * exists in the context and the connection ID is found in the associated set, the connection ID will be removed + * from the set. If the set becomes empty after removal, the entry for the group key pattern will be removed from + * the context. + * + * @param groupKeyPattern The group key pattern associated with the listen connection to be removed. + * @param connectionId The connection ID to be removed. + */ + public synchronized void removeFuzzyListen(String groupKeyPattern, String connectionId) { + // Retrieve the set of connection IDs associated with the group key pattern + Set connectIds = keyPatternWatchClients.get(groupKeyPattern); + if (CollectionUtils.isNotEmpty(connectIds)) { + // Remove the connection ID from the set if it exists + connectIds.remove(connectionId); + } + } + + /** + * remove watch context for connection id. + * @param connectionId + */ + public void clearFuzzyWatchContext(String connectionId) { + for (Map.Entry> keyPatternContextEntry : keyPatternWatchClients.entrySet()) { + Set connectionIds = keyPatternContextEntry.getValue(); + if (CollectionUtils.isNotEmpty(connectionIds)) { + connectionIds.remove(connectionId); + } + } + } + + /** + * Retrieves the set of connection IDs matched with the specified group key. + * + * @param groupKey The group key to match with the key patterns. + * @return The set of connection IDs matched with the group key. + */ + 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 + for (String keyPattern : keyPatternWatchClients.keySet()) { + // Check if the group key matches the current key pattern + String[] strings = GroupKey2.parseKey(groupKey); + + if (FuzzyGroupKeyPattern.matchPattern(groupKey, strings[0], strings[1], strings[2])) { + // If matched, add the associated connection IDs to the set + Set connectIdSet = keyPatternWatchClients.get(keyPattern); + if (CollectionUtils.isNotEmpty(connectIdSet)) { + connectIds.addAll(connectIdSet); + } + } + } + return connectIds; + } + +} diff --git a/core/src/main/java/com/alibaba/nacos/core/utils/GlobalExecutor.java b/core/src/main/java/com/alibaba/nacos/core/utils/GlobalExecutor.java index 275b50f1b98..7559b326ccb 100644 --- a/core/src/main/java/com/alibaba/nacos/core/utils/GlobalExecutor.java +++ b/core/src/main/java/com/alibaba/nacos/core/utils/GlobalExecutor.java @@ -72,6 +72,13 @@ public static void scheduleByCommon(Runnable runnable, long delayMs) { COMMON_EXECUTOR.schedule(runnable, delayMs, TimeUnit.MILLISECONDS); } + public static void scheduleWithFixDelayByCommon(Runnable runnable, long delayMs) { + if (COMMON_EXECUTOR.isShutdown()) { + return; + } + COMMON_EXECUTOR.scheduleWithFixedDelay(runnable, delayMs, delayMs, TimeUnit.MILLISECONDS); + } + public static void submitLoadDataTask(Runnable runnable) { DISTRO_EXECUTOR.submit(runnable); } 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 d7f7fca3545..69920b9e0cd 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 @@ -31,7 +31,11 @@ public class StringPool { private static Cache groupKeyCache = CacheBuilder.newBuilder().maximumSize(5000000) - .expireAfterAccess(180, TimeUnit.SECONDS).build(); + .expireAfterAccess(60, TimeUnit.SECONDS).build(); + + static { + GlobalExecutor.scheduleWithFixDelayByCommon(() -> groupKeyCache.cleanUp(), 30000); + } /** * get singleton string value from the pool. diff --git a/example/src/main/java/com/alibaba/nacos/example/FuzzyWatchExample.java b/example/src/main/java/com/alibaba/nacos/example/FuzzyWatchExample.java index 20f0829eb94..4754eecd0da 100644 --- a/example/src/main/java/com/alibaba/nacos/example/FuzzyWatchExample.java +++ b/example/src/main/java/com/alibaba/nacos/example/FuzzyWatchExample.java @@ -19,8 +19,9 @@ import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.naming.NamingFactory; import com.alibaba.nacos.api.naming.NamingService; -import com.alibaba.nacos.api.naming.listener.AbstractFuzzyWatchEventListener; -import com.alibaba.nacos.api.naming.listener.FuzzyWatchNotifyEvent; +import com.alibaba.nacos.api.naming.listener.FuzzyWatchEventWatcher; +import com.alibaba.nacos.api.naming.listener.FuzzyWatchChangeEvent; +import com.alibaba.nacos.api.naming.utils.NamingUtils; import java.util.Properties; import java.util.concurrent.Executor; @@ -62,7 +63,7 @@ public static void main(String[] args) throws NacosException, InterruptedExcepti return thread; }); - naming.fuzzyWatch(DEFAULT_GROUP, new AbstractFuzzyWatchEventListener() { + naming.fuzzyWatch(DEFAULT_GROUP, new FuzzyWatchEventWatcher() { //EventListener onEvent is sync to handle, If process too low in onEvent, maybe block other onEvent callback. //So you can override getExecutor() to async handle event. @@ -72,13 +73,13 @@ public Executor getExecutor() { } @Override - public void onEvent(FuzzyWatchNotifyEvent event) { - System.out.println("[Fuzzy-Watch-GROUP]changed service name: " + event.getService().getGroupedServiceName()); + public void onEvent(FuzzyWatchChangeEvent event) { + System.out.println("[Fuzzy-Watch-GROUP]changed service name: " + NamingUtils.getServiceKey(event.getNamespace(),event.getGroupName(),event.getServiceName())); System.out.println("[Fuzzy-Watch-GROUP]change type: " + event.getChangeType()); } }); - naming.fuzzyWatch("nacos.test.*", DEFAULT_GROUP, new AbstractFuzzyWatchEventListener() { + naming.fuzzyWatch("nacos.test.*", DEFAULT_GROUP, new FuzzyWatchEventWatcher() { @Override public Executor getExecutor() { @@ -86,8 +87,8 @@ public Executor getExecutor() { } @Override - public void onEvent(FuzzyWatchNotifyEvent event) { - System.out.println("[Prefix-Fuzzy-Watch]changed service name: " + event.getService().getGroupedServiceName()); + public void onEvent(FuzzyWatchChangeEvent event) { + System.out.println("[Prefix-Fuzzy-Watch]changed service name: " + NamingUtils.getServiceKey(event.getNamespace(),event.getGroupName(),event.getServiceName())); System.out.println("[Prefix-Fuzzy-Watch]change type: " + event.getChangeType()); } }); diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/client/Client.java b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/client/Client.java index a5311a6086b..6cbe0112dc8 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/client/Client.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/client/Client.java @@ -122,37 +122,6 @@ public interface Client { */ Collection getAllSubscribeService(); - /** - * Add a watched pattern for this client. - * - * @param pattern watch pattern. - * @return true if add successfully, otherwise false - */ - boolean addWatchedPattern(String pattern); - - /** - * Remove a watched pattern for this client. - * - * @param pattern watch pattern. - * @return true if remove successfully, otherwise false - */ - boolean removeWatchedPattern(String pattern); - - /** - * Judge whether watch this pattern of this client. - * - * @param watchPattern watch patten - * @return true if client watch the given pattern, otherwise false - */ - boolean isWatchedPattern(String watchPattern); - - /** - * Get all watched pattern of current client. - * - * @return watch patterns - */ - Collection getFuzzyWatchedPattern(); - /** * Generate sync data. * 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 4274cfef9ea..9954fcc6153 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 @@ -20,6 +20,8 @@ import com.alibaba.nacos.naming.core.v2.client.Client; import com.alibaba.nacos.naming.core.v2.pojo.Service; +import java.util.Set; + /** * Operation client event. * @@ -102,16 +104,37 @@ public static class ClientFuzzyWatchEvent extends ClientOperationEvent { private static final long serialVersionUID = -4518919987813223119L; private final String pattern; - - public ClientFuzzyWatchEvent(String pattern, String clientId) { + + private Set clientReceivedGroupKeys; + + private boolean isInitializing; + + public ClientFuzzyWatchEvent(String pattern, String clientId,Set clientReceivedGroupKeys,boolean isInitializing) { super(clientId, null); this.pattern = pattern; + this.clientReceivedGroupKeys = clientReceivedGroupKeys; + this.isInitializing=isInitializing; } public String getPattern() { return pattern; } - + + public Set getClientReceivedGroupKeys() { + return clientReceivedGroupKeys; + } + + public void setClientReceivedGroupKeys(Set clientReceivedGroupKeys) { + this.clientReceivedGroupKeys = clientReceivedGroupKeys; + } + + public boolean isInitializing() { + return isInitializing; + } + + public void setInitializing(boolean initializing) { + isInitializing = initializing; + } } /** diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/event/service/ServiceEvent.java b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/event/service/ServiceEvent.java index 76ee2a7b2b9..85d32592dee 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/event/service/ServiceEvent.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/event/service/ServiceEvent.java @@ -20,6 +20,7 @@ import com.alibaba.nacos.naming.core.v2.pojo.Service; import java.util.Collection; +import java.util.Set; /** * Service event. @@ -101,13 +102,13 @@ public static class ServiceFuzzyWatchInitEvent extends ServiceEvent { private final String pattern; - private final Collection matchedService; + private final Set matchedServiceKeys; - public ServiceFuzzyWatchInitEvent(String clientId, String pattern, Collection matchedService) { + public ServiceFuzzyWatchInitEvent(String clientId, String pattern, Set matchedServiceKeys) { super(); this.clientId = clientId; this.pattern = pattern; - this.matchedService = matchedService; + this.matchedServiceKeys = matchedServiceKeys; } public String getClientId() { @@ -118,8 +119,8 @@ public String getPattern() { return pattern; } - public Collection getMatchedService() { - return matchedService; + public Set getMatchedServicKeys() { + return matchedServiceKeys; } } } diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/index/ClientFuzzyWatchIndexesManager.java b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/index/ClientFuzzyWatchIndexesManager.java deleted file mode 100644 index 1adfdce239e..00000000000 --- a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/index/ClientFuzzyWatchIndexesManager.java +++ /dev/null @@ -1,168 +0,0 @@ -package com.alibaba.nacos.naming.core.v2.index; - -import com.alibaba.nacos.common.notify.Event; -import com.alibaba.nacos.common.notify.NotifyCenter; -import com.alibaba.nacos.common.notify.listener.SmartSubscriber; -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.naming.core.v2.ServiceManager; -import com.alibaba.nacos.naming.core.v2.client.Client; -import com.alibaba.nacos.naming.core.v2.event.client.ClientOperationEvent; -import com.alibaba.nacos.naming.core.v2.event.service.ServiceEvent; -import com.alibaba.nacos.naming.core.v2.pojo.Service; -import com.alibaba.nacos.naming.misc.Loggers; -import org.springframework.stereotype.Component; - -import java.util.Collection; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; - -import static com.alibaba.nacos.common.utils.FuzzyGroupKeyPattern.getNamespaceFromPattern; - -@Component -public class ClientFuzzyWatchIndexesManager extends SmartSubscriber { - - /** - * The content of map is {fuzzy watch pattern -> Set[watcher clientID]}. - */ - private final ConcurrentMap> fuzzyWatcherIndexes = new ConcurrentHashMap<>(); - - /** - * The content of map is {service -> Set[matched fuzzy watch patterns]}. - */ - private final ConcurrentMap> fuzzyWatchPatternMatchIndexes = new ConcurrentHashMap<>(); - - - @Override - public List> subscribeTypes() { - List> result = new LinkedList<>(); - result.add(ClientOperationEvent.ClientFuzzyWatchEvent.class); - result.add(ClientOperationEvent.ClientCancelFuzzyWatchEvent.class); - result.add(ClientOperationEvent.ClientReleaseEvent.class); - result.add(ClientOperationEvent.ClientRegisterServiceEvent.class); - return result; - } - - @Override - public void onEvent(Event event) { - if (!(event instanceof ClientOperationEvent)){ - return; - } - ClientOperationEvent clientOperationEvent=(ClientOperationEvent)event; - String clientId = clientOperationEvent.getClientId(); - - if (event instanceof ClientOperationEvent.ClientRegisterServiceEvent) { - - } else if (event instanceof ClientOperationEvent.ClientFuzzyWatchEvent) { - String completedPattern = ((ClientOperationEvent.ClientFuzzyWatchEvent) event).getPattern(); - addFuzzyWatcherIndexes(completedPattern, clientId); - } else if (event instanceof ClientOperationEvent.ClientCancelFuzzyWatchEvent) { - String completedPattern = ((ClientOperationEvent.ClientCancelFuzzyWatchEvent) event).getPattern(); - removeFuzzyWatcherIndexes(completedPattern, clientId); - } else if(event instanceof ClientOperationEvent.ClientReleaseEvent){ - ClientOperationEvent.ClientReleaseEvent clientReleaseEvent=(ClientOperationEvent.ClientReleaseEvent)event; - Client client = clientReleaseEvent.getClient(); - for (String eachPattern : client.getFuzzyWatchedPattern()) { - removeFuzzyWatcherIndexes(eachPattern, client.getClientId()); - } - } - } - - public Collection getServiceMatchedPatterns(Service service) { - return fuzzyWatchPatternMatchIndexes.containsKey(service) - ? fuzzyWatchPatternMatchIndexes.get(service) : new ConcurrentHashSet<>(); - } - - public Collection getAllClientFuzzyWatchedPattern(String pattern) { - return fuzzyWatcherIndexes.containsKey(pattern) ? fuzzyWatcherIndexes.get(pattern) : new ConcurrentHashSet<>(); - } - - - /** - * This method will build/update the fuzzy watch match index of all patterns. - * - * @param service The service of the Nacos. - */ - public void updateWatchMatchIndex(Service service) { - long matchBeginTime = System.currentTimeMillis(); - Set filteredPattern = FuzzyGroupKeyPattern.filterMatchedPatterns(fuzzyWatcherIndexes.keySet(),service.getName(),service.getGroup(),service.getNamespace()); - - if (CollectionUtils.isNotEmpty(filteredPattern)) { - fuzzyWatchPatternMatchIndexes.computeIfAbsent(service, key -> new ConcurrentHashSet<>()); - for (String each : filteredPattern) { - fuzzyWatchPatternMatchIndexes.get(service).add(each); - } - Loggers.PERFORMANCE_LOG.info("WATCH: new service {} match {} pattern, {}ms", service.getGroupedServiceName(), - fuzzyWatchPatternMatchIndexes.size(), System.currentTimeMillis() - matchBeginTime); - } - } - - public void removeWatchMatchIndex(Service service) { - fuzzyWatchPatternMatchIndexes.remove(service); - } - - - private void addFuzzyWatcherIndexes(String completedPattern, String clientId) { - fuzzyWatcherIndexes.computeIfAbsent(completedPattern, key -> new ConcurrentHashSet<>()); - fuzzyWatcherIndexes.get(completedPattern).add(clientId); - Collection matchedService = updateWatchMatchIndex(completedPattern); - NotifyCenter.publishEvent(new ServiceEvent.ServiceFuzzyWatchInitEvent(clientId, completedPattern, matchedService)); - } - - private void removeFuzzyWatcherIndexes(String completedPattern, String clientId) { - if (!fuzzyWatcherIndexes.containsKey(completedPattern)) { - return; - } - fuzzyWatcherIndexes.get(completedPattern).remove(clientId); - if (fuzzyWatcherIndexes.get(completedPattern).isEmpty()) { - fuzzyWatcherIndexes.remove(completedPattern); - } - } - - /** - * This method will remove the match index of fuzzy watch pattern. - * - * @param service The service of the Nacos. - * @param matchedPattern the pattern to remove - */ - public void removeWatchPatternMatchIndex(Service service, String matchedPattern) { - if (!fuzzyWatchPatternMatchIndexes.containsKey(service)) { - return; - } - fuzzyWatchPatternMatchIndexes.get(service).remove(matchedPattern); - if (fuzzyWatchPatternMatchIndexes.get(service).isEmpty()) { - fuzzyWatchPatternMatchIndexes.remove(service); - } - } - - /** - * This method will build/update the fuzzy watch match index for given patterns. - * - * @param completedPattern the completed pattern of watch (with namespace id). - * @return Updated set of services in Nacos server that can match this pattern. - */ - public Collection updateWatchMatchIndex(String completedPattern) { - long matchBeginTime = System.currentTimeMillis(); - Collection serviceSet = ServiceManager.getInstance().getSingletons(getNamespaceFromPattern(completedPattern)); - - Set matchedService = new HashSet<>(); - for (Service service : serviceSet) { - String serviceName = service.getName(); - String groupName = service.getGroup(); - if (FuzzyGroupKeyPattern.matchPattern(completedPattern,service.getNamespace(),groupName,serviceName)) { - fuzzyWatchPatternMatchIndexes.computeIfAbsent(service, key -> new ConcurrentHashSet<>()); - fuzzyWatchPatternMatchIndexes.get(service).add(completedPattern); - matchedService.add(service); - } - } - Loggers.PERFORMANCE_LOG.info("WATCH: pattern {} match {} services, {}ms", completedPattern, - matchedService.size(), System.currentTimeMillis() - matchBeginTime); - return matchedService; - } - -} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/index/ClientServiceIndexesManager.java b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/index/ClientServiceIndexesManager.java index a12f3b09171..18f08ed0824 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/index/ClientServiceIndexesManager.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/index/ClientServiceIndexesManager.java @@ -17,26 +17,21 @@ package com.alibaba.nacos.naming.core.v2.index; import com.alibaba.nacos.api.common.Constants; -import com.alibaba.nacos.api.naming.utils.NamingUtils; import com.alibaba.nacos.common.notify.Event; import com.alibaba.nacos.common.notify.NotifyCenter; import com.alibaba.nacos.common.notify.listener.SmartSubscriber; import com.alibaba.nacos.common.trace.DeregisterInstanceReason; import com.alibaba.nacos.common.trace.event.naming.DeregisterInstanceTraceEvent; -import com.alibaba.nacos.common.utils.CollectionUtils; import com.alibaba.nacos.common.utils.ConcurrentHashSet; -import com.alibaba.nacos.naming.core.v2.ServiceManager; import com.alibaba.nacos.naming.core.v2.client.Client; import com.alibaba.nacos.naming.core.v2.event.client.ClientOperationEvent; import com.alibaba.nacos.naming.core.v2.event.publisher.NamingEventPublisherFactory; import com.alibaba.nacos.naming.core.v2.event.service.ServiceEvent; import com.alibaba.nacos.naming.core.v2.pojo.InstancePublishInfo; import com.alibaba.nacos.naming.core.v2.pojo.Service; -import com.alibaba.nacos.naming.misc.Loggers; import org.springframework.stereotype.Component; import java.util.Collection; -import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Set; @@ -55,10 +50,10 @@ public class ClientServiceIndexesManager extends SmartSubscriber { private final ConcurrentMap> subscriberIndexes = new ConcurrentHashMap<>(); - private ClientFuzzyWatchIndexesManager clientFuzzyWatchIndexesManager; + private NamingFuzzyWatchContextService namingFuzzyWatchContextService; - public ClientServiceIndexesManager(ClientFuzzyWatchIndexesManager clientFuzzyWatchIndexesManager){ - this.clientFuzzyWatchIndexesManager=clientFuzzyWatchIndexesManager; + public ClientServiceIndexesManager(NamingFuzzyWatchContextService namingFuzzyWatchContextService){ + this.namingFuzzyWatchContextService = namingFuzzyWatchContextService; } public ClientServiceIndexesManager() { @@ -85,7 +80,6 @@ public Collection getSubscribedService() { public void removePublisherIndexesByEmptyService(Service service) { if (publisherIndexes.containsKey(service) && publisherIndexes.get(service).isEmpty()) { publisherIndexes.remove(service); - clientFuzzyWatchIndexesManager.removeWatchMatchIndex(service); } } @@ -96,8 +90,6 @@ public List> subscribeTypes() { result.add(ClientOperationEvent.ClientDeregisterServiceEvent.class); result.add(ClientOperationEvent.ClientSubscribeServiceEvent.class); result.add(ClientOperationEvent.ClientUnsubscribeServiceEvent.class); - result.add(ClientOperationEvent.ClientFuzzyWatchEvent.class); - result.add(ClientOperationEvent.ClientCancelFuzzyWatchEvent.class); result.add(ClientOperationEvent.ClientReleaseEvent.class); return result; } @@ -116,7 +108,6 @@ private void handleClientDisconnect(ClientOperationEvent.ClientReleaseEvent even for (Service each : client.getAllSubscribeService()) { removeSubscriberIndexes(each, client.getClientId()); } - DeregisterInstanceReason reason = event.isNative() ? DeregisterInstanceReason.NATIVE_DISCONNECTED : DeregisterInstanceReason.SYNCED_DISCONNECTED; long currentTimeMillis = System.currentTimeMillis(); @@ -147,7 +138,7 @@ private void addPublisherIndexes(Service service, String clientId) { String serviceChangedType = Constants.ServiceChangedType.INSTANCE_CHANGED; if (!publisherIndexes.containsKey(service)) { // The only time the index needs to be updated is when the service is first created - clientFuzzyWatchIndexesManager.updateWatchMatchIndex(service); + namingFuzzyWatchContextService.addNewSevice(service); serviceChangedType = Constants.ServiceChangedType.ADD_SERVICE; } NotifyCenter.publishEvent(new ServiceEvent.ServiceChangedEvent(service, serviceChangedType, true)); 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 new file mode 100644 index 00000000000..380f8de8aa3 --- /dev/null +++ b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/index/NamingFuzzyWatchContextService.java @@ -0,0 +1,285 @@ +package com.alibaba.nacos.naming.core.v2.index; + +import com.alibaba.nacos.api.exception.runtime.NacosRuntimeException; +import com.alibaba.nacos.api.naming.utils.NamingUtils; +import com.alibaba.nacos.common.notify.Event; +import com.alibaba.nacos.common.notify.NotifyCenter; +import com.alibaba.nacos.common.notify.listener.SmartSubscriber; +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.core.utils.GlobalExecutor; +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.event.service.ServiceEvent; +import com.alibaba.nacos.naming.core.v2.pojo.Service; +import com.alibaba.nacos.naming.misc.Loggers; +import org.springframework.stereotype.Component; + +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +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; + +@Component +public class NamingFuzzyWatchContextService extends SmartSubscriber { + + /** + * watched client ids of a pattern, {fuzzy watch pattern -> Set[watched clientID]}. + */ + private final ConcurrentMap> keyPatternWatchClients = 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> fuzzyWatchPatternMatchServices = new ConcurrentHashMap<>(); + + private final int FUZZY_WATCH_MAX_PATTERN_COUNT = 50; + + private final int FUZZY_WATCH_MAX_PATTERN_MATCHED_GROUP_KEY_COUNT = 200; + + + public NamingFuzzyWatchContextService() { + GlobalExecutor.scheduleWithFixDelayByCommon(() -> clearFuzzyWatchContext(), 30000); + } + + private void clearFuzzyWatchContext() { + try { + Iterator>> iterator = fuzzyWatchPatternMatchServices.entrySet().iterator(); + while (iterator.hasNext()) { + Map.Entry> next = iterator.next(); + Set watchedClients = keyPatternWatchClients.get(next.getKey()); + if (CollectionUtils.isEmpty(watchedClients)) { + iterator.remove(); + if (watchedClients != null) { + keyPatternWatchClients.remove(next.getKey()); + } + } + } + }catch(Throwable throwable){ + throwable.printStackTrace(); + } + } + + @Override + public List> subscribeTypes() { + List> result = new LinkedList<>(); + result.add(ClientOperationEvent.ClientFuzzyWatchEvent.class); + result.add(ClientOperationEvent.ClientCancelFuzzyWatchEvent.class); + result.add(ClientOperationEvent.ClientReleaseEvent.class); + result.add(ServiceEvent.ServiceChangedEvent.class); + + return result; + } + + @Override + public void onEvent(Event event) { + if (!(event instanceof ClientOperationEvent) && !(event instanceof ServiceEvent.ServiceChangedEvent)) { + return; + } + + if (event instanceof ClientOperationEvent) { + ClientOperationEvent clientOperationEvent = (ClientOperationEvent) event; + String clientId = clientOperationEvent.getClientId(); + + if (event instanceof ClientOperationEvent.ClientFuzzyWatchEvent) { + ClientOperationEvent.ClientFuzzyWatchEvent clientFuzzyWatchEvent = (ClientOperationEvent.ClientFuzzyWatchEvent) event; + String completedPattern = clientFuzzyWatchEvent.getPattern(); + if (clientFuzzyWatchEvent.isInitializing()){ + //fuzzy watch init + addFuzzyWatcherIndexes(completedPattern, clientId); + }else{ + //fuzzy watch sync + syncFuzzyWatcherClientIdContext(completedPattern,clientFuzzyWatchEvent.getClientReceivedGroupKeys(),clientId); + } + + } else if (event instanceof ClientOperationEvent.ClientCancelFuzzyWatchEvent) { + String completedPattern = ((ClientOperationEvent.ClientCancelFuzzyWatchEvent) event).getPattern(); + removeFuzzyWatchContext(completedPattern, clientId); + } else if (event instanceof ClientOperationEvent.ClientReleaseEvent) { + removeFuzzyWatchContext(((ClientOperationEvent.ClientReleaseEvent) event).getClientId()); + } + } + + if (event instanceof ServiceEvent.ServiceChangedEvent) { + ServiceEvent.ServiceChangedEvent serviceChangedEvent = (ServiceEvent.ServiceChangedEvent) event; + String changedType = serviceChangedEvent.getChangedType(); + if (changedType.equals(ADD_SERVICE)) { + Iterator>> iterator = fuzzyWatchPatternMatchServices.entrySet() + .iterator(); + Service changedEventService = serviceChangedEvent.getService(); + + while (iterator.hasNext()) { + Map.Entry> next = iterator.next(); + if (FuzzyGroupKeyPattern.matchPattern(next.getKey(), + changedEventService.getName(), + changedEventService.getGroup(), changedEventService.getNamespace())) { + next.getValue().add(NamingUtils.getServiceKey(changedEventService.getNamespace(),changedEventService.getGroup(),changedEventService.getName())); + } + } + + } else if (changedType.equals(DELETE_SERVICE)) { + Iterator>> iterator = fuzzyWatchPatternMatchServices.entrySet() + .iterator(); + Service changedEventService = serviceChangedEvent.getService(); + + while (iterator.hasNext()) { + Map.Entry> next = iterator.next(); + if (FuzzyGroupKeyPattern.matchPattern(next.getKey(), + changedEventService.getName(), + changedEventService.getGroup(), changedEventService.getNamespace())) { + String serviceKey = NamingUtils.getServiceKey(changedEventService.getNamespace(), + changedEventService.getGroup(), changedEventService.getName()); + next.getValue().remove(serviceKey); + } + } + } + } + } + + + public Set getFuzzyWatchedClients(Service service) { + Set matchedClients = new HashSet<>(); + Iterator>> iterator = keyPatternWatchClients.entrySet().iterator(); + while (iterator.hasNext()) { + if (FuzzyGroupKeyPattern.matchPattern(iterator.next().getKey(), service.getName(), service.getGroup(), + service.getNamespace())) { + matchedClients.addAll(iterator.next().getValue()); + } + } + return matchedClients; + } + + /** + * This method will build/update the fuzzy watch match index of all patterns. + * + * @param service The service of the Nacos. + */ + public void addNewSevice(Service service) { + long matchBeginTime = System.currentTimeMillis(); + Set filteredPattern = FuzzyGroupKeyPattern.filterMatchedPatterns(keyPatternWatchClients.keySet(), + service.getName(), service.getGroup(), service.getNamespace()); + + if (CollectionUtils.isNotEmpty(filteredPattern)) { + for (String each : filteredPattern) { + fuzzyWatchPatternMatchServices.get(each).add(NamingUtils.getServiceKey(service.getNamespace(),service.getGroup(),service.getName())); + } + Loggers.PERFORMANCE_LOG.info("WATCH: new service {} match {} pattern, {}ms", + service.getGroupedServiceName(), fuzzyWatchPatternMatchServices.size(), + System.currentTimeMillis() - matchBeginTime); + } + } + + private void addFuzzyWatcherIndexes(String completedPattern, String clientId) { + + Set matchedServiceKeys = initWatchMatchService(completedPattern); + boolean added =keyPatternWatchClients.computeIfAbsent(completedPattern, key -> new ConcurrentHashSet<>()).add(clientId); + if (added) { + NotifyCenter.publishEvent( + new ServiceEvent.ServiceFuzzyWatchInitEvent(clientId, completedPattern, matchedServiceKeys)); + } + } + + private void syncFuzzyWatcherClientIdContext(String completedPattern,Set clientReceivedServiceKeys, String clientId) { + + Set matchedServiceKeys = getPatternMatchedServiceKeys(completedPattern); + keyPatternWatchClients.computeIfAbsent(completedPattern, key -> new ConcurrentHashSet<>()); + boolean added = keyPatternWatchClients.get(completedPattern).add(clientId); + if (added) { + NotifyCenter.publishEvent( + new ServiceEvent.ServiceFuzzyWatchInitEvent(clientId, completedPattern, matchedServiceKeys)); + } + } + + + + private void removeFuzzyWatchContext(String clientId) { + Iterator>> iterator = keyPatternWatchClients.entrySet().iterator(); + + while (iterator.hasNext()) { + Map.Entry> next = iterator.next(); + next.getValue().remove(clientId); + } + } + + private void removeFuzzyWatchContext(String groupKeyPattern, String clientId) { + + if (keyPatternWatchClients.containsKey(groupKeyPattern)) { + keyPatternWatchClients.get(groupKeyPattern).remove(clientId); + } + } + + /** + * This method will remove the match index of fuzzy watch pattern. + * + * @param service The service of the Nacos. + * @param matchedPattern the pattern to remove + */ + public void removeFuzzyWatchContext(Service service, String matchedPattern) { + if (!fuzzyWatchPatternMatchServices.containsKey(service)) { + return; + } + fuzzyWatchPatternMatchServices.get(service).remove(matchedPattern); + if (fuzzyWatchPatternMatchServices.get(service).isEmpty()) { + fuzzyWatchPatternMatchServices.remove(service); + } + } + + + public Set getPatternMatchedServiceKeys(String completedPattern) { + return fuzzyWatchPatternMatchServices.get(completedPattern); + } + + /** + * This method will build/update the fuzzy watch match index for given patterns. + * + * @param completedPattern the completed pattern of watch (with namespace id). + * @return Updated set of services in Nacos server that can match this pattern. + */ + public Set initWatchMatchService(String completedPattern) { + + if (fuzzyWatchPatternMatchServices.containsKey(completedPattern)) { + return fuzzyWatchPatternMatchServices.get(completedPattern); + } + + if (fuzzyWatchPatternMatchServices.size() >= FUZZY_WATCH_MAX_PATTERN_COUNT) { + throw new NacosRuntimeException(FUZZY_WATCH_PATTERN_OVER_LIMIT.getCode(), + FUZZY_WATCH_PATTERN_OVER_LIMIT.getMsg()); + } + + long matchBeginTime = System.currentTimeMillis(); + Set namespaceServices = ServiceManager.getInstance() + .getSingletons(getNamespaceFromPattern(completedPattern)); + Set matchedServices = fuzzyWatchPatternMatchServices.computeIfAbsent(completedPattern, + k -> new HashSet<>()); + + 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) { + throw new NacosRuntimeException(FUZZY_WATCH_PATTERN_MATCH_GROUP_KEY_OVER_LIMIT.getCode(), + FUZZY_WATCH_PATTERN_MATCH_GROUP_KEY_OVER_LIMIT.getMsg()); + } + matchedServices.add(NamingUtils.getServiceKey(service.getNamespace(),service.getGroup(),service.getName())); + } + } + + Loggers.PERFORMANCE_LOG.info("WATCH: pattern {} match {} services, cost {}ms", completedPattern, + matchedServices.size(), System.currentTimeMillis() - matchBeginTime); + + return matchedServices; + } + +} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/service/ClientOperationService.java b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/service/ClientOperationService.java index b2696dbc68a..3addf41e62f 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/service/ClientOperationService.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/service/ClientOperationService.java @@ -85,30 +85,6 @@ default void unsubscribeService(Service service, Subscriber subscriber, String c } - /** - * Fuzzy watch a pattern. - * - * @param namespaceId namespace id of this pattern - * @param serviceNamePattern watch service name pattern rule - * @param groupNamePattern watch service name pattern rule - * @param clientId id of client - */ - default void fuzzyWatch(String namespaceId, String serviceNamePattern, String groupNamePattern, String clientId) { - - } - - /** - * Cancel fuzzy watch a pattern. - * - * @param namespaceId namespace id of this pattern - * @param serviceNamePattern watch service name pattern - * @param groupNamePattern watch service name pattern - * @param clientId id of client - */ - default void cancelFuzzyWatch(String namespaceId, String serviceNamePattern, String groupNamePattern, String clientId) { - - } - /** * get publish info. * diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/service/impl/EphemeralClientOperationServiceImpl.java b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/service/impl/EphemeralClientOperationServiceImpl.java index 8ac352ff51c..5bcb460fc9d 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/service/impl/EphemeralClientOperationServiceImpl.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/service/impl/EphemeralClientOperationServiceImpl.java @@ -21,7 +21,6 @@ import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.api.naming.utils.NamingUtils; import com.alibaba.nacos.common.notify.NotifyCenter; -import com.alibaba.nacos.common.utils.FuzzyGroupKeyPattern; import com.alibaba.nacos.naming.core.v2.ServiceManager; import com.alibaba.nacos.naming.core.v2.client.Client; import com.alibaba.nacos.naming.core.v2.client.manager.ClientManager; @@ -137,40 +136,6 @@ public void unsubscribeService(Service service, Subscriber subscriber, String cl client.setLastUpdatedTime(); NotifyCenter.publishEvent(new ClientOperationEvent.ClientUnsubscribeServiceEvent(singleton, clientId)); } - - @Override - public void fuzzyWatch(String namespaceId, String serviceNamePattern, String groupNamePattern, String clientId) { - // need store namespace id in server side - String completedPattern = FuzzyGroupKeyPattern.generatePattern(serviceNamePattern,groupNamePattern, namespaceId); - Client client = clientManager.getClient(clientId); - if (!clientIsLegal(client, clientId)) { - return; - } - client.addWatchedPattern(completedPattern); - client.setLastUpdatedTime(); - NotifyCenter.publishEvent(new ClientOperationEvent.ClientFuzzyWatchEvent(completedPattern, clientId)); - } - - @Override - public void cancelFuzzyWatch(String namespaceId, String serviceNamePattern, String groupNamePattern, String clientId) { - String completedPattern = FuzzyGroupKeyPattern.generatePattern(serviceNamePattern,groupNamePattern, namespaceId); - Client client = clientManager.getClient(clientId); - if (!clientIsLegal(client, clientId)) { - return; - } - client.removeWatchedPattern(completedPattern); - client.setLastUpdatedTime(); - NotifyCenter.publishEvent(new ClientOperationEvent.ClientCancelFuzzyWatchEvent(completedPattern, clientId)); - } - - private boolean clientIsLegal(Client client, String clientId){ - try{ - checkClientIsLegal( client, clientId); - return true; - }catch(NacosRuntimeException thr){ - return false; - } - } private void checkClientIsLegal(Client client, String clientId) { if (client == null) { diff --git a/naming/src/main/java/com/alibaba/nacos/naming/push/v2/NamingSubscriberServiceV2Impl.java b/naming/src/main/java/com/alibaba/nacos/naming/push/v2/NamingSubscriberServiceV2Impl.java index 0416b528c3c..66e91deb6cd 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/push/v2/NamingSubscriberServiceV2Impl.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/push/v2/NamingSubscriberServiceV2Impl.java @@ -26,7 +26,7 @@ import com.alibaba.nacos.naming.core.v2.client.manager.ClientManagerDelegate; import com.alibaba.nacos.naming.core.v2.event.publisher.NamingEventPublisherFactory; import com.alibaba.nacos.naming.core.v2.event.service.ServiceEvent; -import com.alibaba.nacos.naming.core.v2.index.ClientFuzzyWatchIndexesManager; +import com.alibaba.nacos.naming.core.v2.index.NamingFuzzyWatchContextService; import com.alibaba.nacos.naming.core.v2.index.ClientServiceIndexesManager; import com.alibaba.nacos.naming.core.v2.index.ServiceStorage; import com.alibaba.nacos.naming.core.v2.metadata.NamingMetadataManager; @@ -36,8 +36,8 @@ import com.alibaba.nacos.naming.pojo.Subscriber; import com.alibaba.nacos.naming.push.NamingSubscriberService; import com.alibaba.nacos.naming.push.v2.executor.PushExecutorDelegate; -import com.alibaba.nacos.naming.push.v2.task.FuzzyWatchInitDelayTask; -import com.alibaba.nacos.naming.push.v2.task.FuzzyWatchNotifyChangeDelayTask; +import com.alibaba.nacos.naming.push.v2.task.FuzzyWatchChangeNotifyTask; +import com.alibaba.nacos.naming.push.v2.task.FuzzyWatchInitNotifyTask; import com.alibaba.nacos.naming.push.v2.task.FuzzyWatchPushDelayTaskEngine; import com.alibaba.nacos.naming.push.v2.task.PushDelayTask; import com.alibaba.nacos.naming.push.v2.task.PushDelayTaskExecuteEngine; @@ -68,13 +68,14 @@ public class NamingSubscriberServiceV2Impl extends SmartSubscriber implements Na private final FuzzyWatchPushDelayTaskEngine fuzzyWatchPushDelayTaskEngine; public NamingSubscriberServiceV2Impl(ClientManagerDelegate clientManager, - ClientServiceIndexesManager indexesManager, ClientFuzzyWatchIndexesManager clientFuzzyWatchIndexesManager, ServiceStorage serviceStorage, + ClientServiceIndexesManager indexesManager, NamingFuzzyWatchContextService namingFuzzyWatchContextService, ServiceStorage serviceStorage, NamingMetadataManager metadataManager, PushExecutorDelegate pushExecutor, SwitchDomain switchDomain) { this.clientManager = clientManager; this.indexesManager = indexesManager; this.delayTaskEngine = new PushDelayTaskExecuteEngine(clientManager, indexesManager, serviceStorage, metadataManager, pushExecutor, switchDomain); - this.fuzzyWatchPushDelayTaskEngine = new FuzzyWatchPushDelayTaskEngine(clientManager, clientFuzzyWatchIndexesManager, + this.fuzzyWatchPushDelayTaskEngine = new FuzzyWatchPushDelayTaskEngine( + namingFuzzyWatchContextService, serviceStorage, metadataManager, pushExecutor, switchDomain); NotifyCenter.registerSubscriber(this, NamingEventPublisherFactory.getInstance()); @@ -130,9 +131,7 @@ public void onEvent(Event event) { ServiceEvent.ServiceChangedEvent serviceChangedEvent = (ServiceEvent.ServiceChangedEvent) event; Service service = serviceChangedEvent.getService(); delayTaskEngine.addTask(service, new PushDelayTask(service, PushConfig.getInstance().getPushTaskDelay())); - // watch notify push task specify by service - fuzzyWatchPushDelayTaskEngine.addTask(service, new FuzzyWatchNotifyChangeDelayTask(service, - serviceChangedEvent.getChangedType(), PushConfig.getInstance().getPushTaskDelay())); + genetateFuzzyWatchChangeNotifyTask(service,serviceChangedEvent.getChangedType()); MetricsMonitor.incrementServiceChangeCount(service); } else if (event instanceof ServiceEvent.ServiceSubscribedEvent) { // If service is subscribed by one client, only push this client. @@ -152,12 +151,24 @@ public void onEvent(Event event) { int originSize = matchedService.size(); // watch init push task is specify by client id with pattern // The key is just used to differentiate between different initialization tasks and merge them if needed. - fuzzyWatchPushDelayTaskEngine.addTask(taskKey, new FuzzyWatchInitDelayTask(taskKey, clientId, completedPattern, matchedService, - originSize, PushConfig.getInstance().getPushTaskDelay(), false)); + FuzzyWatchInitNotifyTask fuzzyWatchInitNotifyTask = new FuzzyWatchInitNotifyTask(clientId, completedPattern, + matchedService, originSize, PushConfig.getInstance().getPushTaskDelay(), false); + fuzzyWatchPushDelayTaskEngine.addTask(FuzzyWatchPushDelayTaskEngine.getTaskKey(fuzzyWatchInitNotifyTask), fuzzyWatchInitNotifyTask); } } - private String getTaskKey(String clientId, String pattern) { + private void genetateFuzzyWatchChangeNotifyTask(Service service,String changedType){ + // watch notify push task specify by service + for (String clientId : fuzzyWatchPushDelayTaskEngine.getNamingFuzzyWatchContextService() + .getFuzzyWatchedClients(service)) { + String serviceKey=NamingUtils.getServiceKey(service.getNamespace(),service.getGroup(),service.getName()); + FuzzyWatchChangeNotifyTask fuzzyWatchChangeNotifyTask = new FuzzyWatchChangeNotifyTask(serviceKey, changedType, + clientId, PushConfig.getInstance().getPushTaskDelay()); + fuzzyWatchPushDelayTaskEngine.addTask(FuzzyWatchPushDelayTaskEngine.getTaskKey(fuzzyWatchChangeNotifyTask),fuzzyWatchChangeNotifyTask ); + } + } + + String getTaskKey(String clientId, String pattern) { if (StringUtils.isBlank(clientId)) { throw new IllegalArgumentException("Param 'clientId' is illegal, clientId is blank"); } diff --git a/naming/src/main/java/com/alibaba/nacos/naming/push/v2/executor/PushExecutor.java b/naming/src/main/java/com/alibaba/nacos/naming/push/v2/executor/PushExecutor.java index e1fc5ecb95d..375fdd2b169 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/push/v2/executor/PushExecutor.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/push/v2/executor/PushExecutor.java @@ -49,15 +49,6 @@ public interface PushExecutor { */ void doPushWithCallback(String clientId, Subscriber subscriber, PushDataWrapper data, NamingPushCallback callBack); - - /** - * Do push to notify fuzzy watcher. - * - * @param clientId client id - * @param fuzzyWatchNotifyRequest request for fuzzy watch notification - */ - void doWatcherNotifyPush(String clientId, AbstractFuzzyWatchNotifyRequest fuzzyWatchNotifyRequest); - /** * Do push to notify fuzzy watcher with call back. * diff --git a/naming/src/main/java/com/alibaba/nacos/naming/push/v2/executor/PushExecutorDelegate.java b/naming/src/main/java/com/alibaba/nacos/naming/push/v2/executor/PushExecutorDelegate.java index 4acad457d27..34006f21092 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/push/v2/executor/PushExecutorDelegate.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/push/v2/executor/PushExecutorDelegate.java @@ -55,12 +55,6 @@ public void doPushWithCallback(String clientId, Subscriber subscriber, PushDataW getPushExecuteService(clientId, subscriber).doPushWithCallback(clientId, subscriber, data, callBack); } - @Override - public void doWatcherNotifyPush(String clientId, AbstractFuzzyWatchNotifyRequest watchNotifyRequest) { - // only support fuzzy watch by rpc - rpcPushExecuteService.doWatcherNotifyPush(clientId, watchNotifyRequest); - } - @Override public void doFuzzyWatchNotifyPushWithCallBack(String clientId, AbstractFuzzyWatchNotifyRequest watchNotifyRequest, PushCallBack callBack) { diff --git a/naming/src/main/java/com/alibaba/nacos/naming/push/v2/executor/PushExecutorRpcImpl.java b/naming/src/main/java/com/alibaba/nacos/naming/push/v2/executor/PushExecutorRpcImpl.java index d540ca54223..a2084b5d380 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/push/v2/executor/PushExecutorRpcImpl.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/push/v2/executor/PushExecutorRpcImpl.java @@ -62,12 +62,7 @@ private ServiceInfo getServiceInfo(PushDataWrapper data, Subscriber subscriber) .selectInstancesWithHealthyProtection(data.getOriginalData(), data.getServiceMetadata(), false, true, subscriber); } - - @Override - public void doWatcherNotifyPush(String clientId, AbstractFuzzyWatchNotifyRequest watchNotifyRequest) { - pushService.pushWithoutAck(clientId, watchNotifyRequest); - } - + @Override public void doFuzzyWatchNotifyPushWithCallBack(String clientId, AbstractFuzzyWatchNotifyRequest watchNotifyRequest, PushCallBack callBack) { pushService.pushWithCallback(clientId, watchNotifyRequest, callBack, GlobalExecutor.getCallbackExecutor()); diff --git a/naming/src/main/java/com/alibaba/nacos/naming/push/v2/executor/PushExecutorUdpImpl.java b/naming/src/main/java/com/alibaba/nacos/naming/push/v2/executor/PushExecutorUdpImpl.java index 3324a3345d2..431ea4f7050 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/push/v2/executor/PushExecutorUdpImpl.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/push/v2/executor/PushExecutorUdpImpl.java @@ -96,11 +96,6 @@ private ServiceInfo handleClusterData(ServiceInfo data, Subscriber subscriber) { : ServiceUtil.selectInstances(data, subscriber.getCluster()); } - @Override - public void doWatcherNotifyPush(String clientId, AbstractFuzzyWatchNotifyRequest watchNotifyRequest) { - - } - @Override public void doFuzzyWatchNotifyPushWithCallBack(String clientId, AbstractFuzzyWatchNotifyRequest watchNotifyRequest, PushCallBack callBack) { 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 new file mode 100644 index 00000000000..a4f4f16df46 --- /dev/null +++ b/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchChangeNotifyExecuteTask.java @@ -0,0 +1,92 @@ +/* + * 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.naming.remote.request.FuzzyWatchNotifyChangeRequest; +import com.alibaba.nacos.api.remote.PushCallBack; +import com.alibaba.nacos.common.task.AbstractExecuteTask; +import com.alibaba.nacos.naming.misc.Loggers; +import com.alibaba.nacos.naming.push.v2.NoRequiredRetryException; +import com.alibaba.nacos.naming.push.v2.PushConfig; + +/** + * Nacos naming fuzzy watch notify service change push delay task. + * + * @author tanyongquan + */ +public class FuzzyWatchChangeNotifyExecuteTask extends AbstractExecuteTask{ + + private final String serviceKey; + + private final String changedType; + + private String clientId; + + private final FuzzyWatchPushDelayTaskEngine delayTaskEngine; + + public FuzzyWatchChangeNotifyExecuteTask(FuzzyWatchPushDelayTaskEngine delayTaskEngine,String serviceKey, String changedType, String targetClient) { + this.serviceKey = serviceKey; + this.changedType = changedType; + this.clientId=targetClient; + this.delayTaskEngine=delayTaskEngine; + } + + @Override + public void run() { + + delayTaskEngine.getPushExecutor().doFuzzyWatchNotifyPushWithCallBack(clientId, + new FuzzyWatchNotifyChangeRequest(serviceKey,changedType), + new FuzzyWatchChangeNotifyCallback(clientId, serviceKey,changedType)); + + } + + private class FuzzyWatchChangeNotifyCallback implements PushCallBack { + + private final String clientId; + + private String service; + + private String serviceChangedType; + + + private FuzzyWatchChangeNotifyCallback(String clientId,String service, String serviceChangedType) { + this.clientId = clientId; + this.service=service; + this.serviceChangedType=serviceChangedType; + + } + + @Override + public long getTimeout() { + return PushConfig.getInstance().getPushTaskTimeout(); + } + + @Override + public void onSuccess() { + + } + + @Override + public void onFail(Throwable e) { + if (!(e instanceof NoRequiredRetryException)) { + Loggers.PUSH.error("fuzzy watch fail , reason detail: ", e); + delayTaskEngine.addTask(System.currentTimeMillis(), new FuzzyWatchChangeNotifyTask( service,serviceChangedType,clientId, + PushConfig.getInstance().getPushTaskRetryDelay())); + } + } + } +} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchChangeNotifyTask.java b/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchChangeNotifyTask.java new file mode 100644 index 00000000000..e9264ad5833 --- /dev/null +++ b/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchChangeNotifyTask.java @@ -0,0 +1,66 @@ +/* + * 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.common.task.AbstractDelayTask; +import com.alibaba.nacos.common.task.AbstractExecuteTask; +import com.alibaba.nacos.naming.core.v2.pojo.Service; + +/** + * Nacos naming fuzzy watch notify service change push delay task. + * + * @author tanyongquan + */ +public class FuzzyWatchChangeNotifyTask extends AbstractDelayTask{ + + private final String serviceKey; + + private final String changedType; + + private final String clientId; + + private final long delay; + + public FuzzyWatchChangeNotifyTask(String serviceKey, String changedType,String clientId,long delay) { + this.serviceKey = serviceKey; + this.changedType = changedType; + this.delay=delay; + this.clientId=clientId; + + } + + public String getChangedType() { + return changedType; + } + + public String getClientId() { + return clientId; + } + + public long getDelay() { + return delay; + } + + public String getServiceKey() { + return serviceKey; + } + + @Override + public void merge(AbstractDelayTask task) { + + } +} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchInitExecuteTask.java b/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchInitNotifyExecuteTask.java similarity index 76% rename from naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchInitExecuteTask.java rename to naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchInitNotifyExecuteTask.java index 94c684ccdc4..d2349ca6994 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchInitExecuteTask.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchInitNotifyExecuteTask.java @@ -17,12 +17,8 @@ package com.alibaba.nacos.naming.push.v2.task; import com.alibaba.nacos.api.naming.remote.request.FuzzyWatchNotifyInitRequest; -import com.alibaba.nacos.api.naming.utils.NamingUtils; import com.alibaba.nacos.api.remote.PushCallBack; import com.alibaba.nacos.common.task.AbstractExecuteTask; -import com.alibaba.nacos.common.utils.FuzzyGroupKeyPattern; -import com.alibaba.nacos.naming.core.v2.client.Client; -import com.alibaba.nacos.naming.core.v2.client.manager.ClientManager; import com.alibaba.nacos.naming.misc.Loggers; import com.alibaba.nacos.naming.push.v2.NoRequiredRetryException; import com.alibaba.nacos.naming.push.v2.PushConfig; @@ -37,17 +33,15 @@ * * @author tanyongquan */ -public class FuzzyWatchInitExecuteTask extends AbstractExecuteTask { - - private final String taskKey; +public class FuzzyWatchInitNotifyExecuteTask extends AbstractExecuteTask { private final String clientId; private final String pattern; - private final FuzzyWatchPushDelayTaskEngine delayTaskEngine; + private final FuzzyWatchPushDelayTaskEngine fuzzyWatchPushDelayTaskEngine; - private final FuzzyWatchInitDelayTask delayTask; + private final FuzzyWatchInitNotifyTask delayTask; /** * Fuzzy watch origin push matched service size, if there is no failure while executing push, {@code originSize == latch}. @@ -68,12 +62,11 @@ public class FuzzyWatchInitExecuteTask extends AbstractExecuteTask { private boolean haveFailPush; - public FuzzyWatchInitExecuteTask(String taskKey, String clientId, String pattern, int originSize, - FuzzyWatchPushDelayTaskEngine delayTaskEngine, FuzzyWatchInitDelayTask delayTask, boolean isFinishInitTask) { - this.taskKey = taskKey; + public FuzzyWatchInitNotifyExecuteTask( String clientId, String pattern, int originSize, + FuzzyWatchPushDelayTaskEngine fuzzyWatchPushDelayTaskEngine, FuzzyWatchInitNotifyTask delayTask, boolean isFinishInitTask) { this.clientId = clientId; this.pattern = pattern; - this.delayTaskEngine = delayTaskEngine; + this.fuzzyWatchPushDelayTaskEngine = fuzzyWatchPushDelayTaskEngine; this.delayTask = delayTask; this.originSize = originSize; this.latch = delayTask.getMatchedService().size(); @@ -84,25 +77,18 @@ public FuzzyWatchInitExecuteTask(String taskKey, String clientId, String pattern @Override public void run() { - ClientManager clientManager = delayTaskEngine.getClientManager(); Collection> dividedServices = divideServiceByBatch(delayTask.getMatchedService()); - Client client = clientManager.getClient(clientId); - if (null == client) { - return; - } - if (!client.isWatchedPattern(pattern)) { - return; - } + if (isFinishInitTask || delayTask.getMatchedService().isEmpty()) { // do not match any exist service, just finish init - delayTaskEngine.getPushExecutor().doFuzzyWatchNotifyPushWithCallBack(clientId, + fuzzyWatchPushDelayTaskEngine.getPushExecutor().doFuzzyWatchNotifyPushWithCallBack(clientId, FuzzyWatchNotifyInitRequest.buildInitFinishRequest(pattern), - new FuzzyWatchInitPushCallback(clientId, null, originSize, true, haveFailPush)); + new FuzzyWatchInitNotifyCallback(clientId, null, originSize, true, haveFailPush)); } else { for (Collection batchData : dividedServices) { - delayTaskEngine.getPushExecutor().doFuzzyWatchNotifyPushWithCallBack(clientId, FuzzyWatchNotifyInitRequest.buildInitRequest( + fuzzyWatchPushDelayTaskEngine.getPushExecutor().doFuzzyWatchNotifyPushWithCallBack(clientId, FuzzyWatchNotifyInitRequest.buildInitRequest( pattern, batchData), - new FuzzyWatchInitPushCallback(clientId, batchData, originSize, false, haveFailPush)); + new FuzzyWatchInitNotifyCallback(clientId, batchData, originSize, false, haveFailPush)); } } @@ -127,7 +113,7 @@ private Collection> divideServiceByBatch(Collection m return result; } - private class FuzzyWatchInitPushCallback implements PushCallBack { + private class FuzzyWatchInitNotifyCallback implements PushCallBack { private final String clientId; @@ -144,7 +130,7 @@ private class FuzzyWatchInitPushCallback implements PushCallBack { private boolean haveFailPush; - private FuzzyWatchInitPushCallback(String clientId, Collection groupedServiceName, int originSize, + private FuzzyWatchInitNotifyCallback(String clientId, Collection groupedServiceName, int originSize, boolean isFinishInitTask, boolean haveFailPush) { this.clientId = clientId; this.groupedServiceName = groupedServiceName; @@ -176,7 +162,7 @@ public void onSuccess() { sendCount += groupedServiceName.size(); // this task is an init push task(not finish notify), and with no failure in this task when executing push batched services if (!haveFailPush && sendCount >= latch) { - delayTaskEngine.addTask(taskKey, new FuzzyWatchInitDelayTask(taskKey, clientId, pattern, null, + fuzzyWatchPushDelayTaskEngine.addTask(System.currentTimeMillis(), new FuzzyWatchInitNotifyTask(clientId, pattern, null, originSize, PushConfig.getInstance().getPushTaskDelay(), true)); } } @@ -191,10 +177,10 @@ public void onFail(Throwable e) { if (!(e instanceof NoRequiredRetryException)) { Loggers.PUSH.error("Reason detail: ", e); if (isFinishInitTask) { - delayTaskEngine.addTask(taskKey, new FuzzyWatchInitDelayTask(taskKey, clientId, pattern, null, + fuzzyWatchPushDelayTaskEngine.addTask(System.currentTimeMillis(),new FuzzyWatchInitNotifyTask(clientId, pattern, null, originSize, PushConfig.getInstance().getPushTaskRetryDelay(), true)); } else { - delayTaskEngine.addTask(taskKey, new FuzzyWatchInitDelayTask(taskKey, clientId, pattern, groupedServiceName, + fuzzyWatchPushDelayTaskEngine.addTask(System.currentTimeMillis(),new FuzzyWatchInitNotifyTask(clientId, pattern, groupedServiceName, originSize, PushConfig.getInstance().getPushTaskRetryDelay(), false)); } diff --git a/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchInitDelayTask.java b/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchInitNotifyTask.java similarity index 83% rename from naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchInitDelayTask.java rename to naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchInitNotifyTask.java index 3e5f9c66c28..96a166ad93c 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchInitDelayTask.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchInitNotifyTask.java @@ -26,9 +26,7 @@ * * @author tanyongquan */ -public class FuzzyWatchInitDelayTask extends AbstractDelayTask { - - private final String taskKey; +public class FuzzyWatchInitNotifyTask extends AbstractDelayTask { private final String clientId; @@ -40,9 +38,8 @@ public class FuzzyWatchInitDelayTask extends AbstractDelayTask { private final boolean isFinishInit; - public FuzzyWatchInitDelayTask(String taskKey, String clientId, String pattern, Collection matchedService, + public FuzzyWatchInitNotifyTask(String clientId, String pattern, Collection matchedService, int originSize, long delay, boolean isFinishInit) { - this.taskKey = taskKey; this.clientId = clientId; this.pattern = pattern; this.matchedService = matchedService; @@ -54,10 +51,10 @@ public FuzzyWatchInitDelayTask(String taskKey, String clientId, String pattern, @Override public void merge(AbstractDelayTask task) { - if (!(task instanceof FuzzyWatchInitDelayTask)) { + if (!(task instanceof FuzzyWatchInitNotifyTask)) { return; } - FuzzyWatchInitDelayTask oldTask = (FuzzyWatchInitDelayTask) task; + FuzzyWatchInitNotifyTask oldTask = (FuzzyWatchInitNotifyTask) task; if (!isFinishInit) { matchedService.addAll(oldTask.getMatchedService()); } @@ -65,10 +62,6 @@ public void merge(AbstractDelayTask task) { Loggers.PUSH.info("[FUZZY-WATCH-INIT-PUSH] Task merge for pattern {}", pattern); } - public String getTaskKey() { - return taskKey; - } - public String getPattern() { return pattern; } diff --git a/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchNotifyChangeDelayTask.java b/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchNotifyChangeDelayTask.java deleted file mode 100644 index 224ef1cb130..00000000000 --- a/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchNotifyChangeDelayTask.java +++ /dev/null @@ -1,90 +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.naming.push.v2.task; - -import com.alibaba.nacos.common.task.AbstractDelayTask; -import com.alibaba.nacos.naming.core.v2.pojo.Service; -import com.alibaba.nacos.naming.misc.Loggers; - -import java.util.HashSet; -import java.util.Set; - -/** - * Nacos naming fuzzy watch notify service change push delay task. - * - * @author tanyongquan - */ -public class FuzzyWatchNotifyChangeDelayTask extends AbstractDelayTask { - private final Service service; - - private final String changedType; - - private boolean pushToAll; - - private Set targetClients; - - public FuzzyWatchNotifyChangeDelayTask(Service service, String changedType, long delay) { - this.service = service; - this.changedType = changedType; - pushToAll = true; - targetClients = null; - setTaskInterval(delay); - setLastProcessTime(System.currentTimeMillis()); - } - - public FuzzyWatchNotifyChangeDelayTask(Service service, String changedType, long delay, String targetClient) { - this.service = service; - this.changedType = changedType; - this.pushToAll = false; - this.targetClients = new HashSet<>(1); - this.targetClients.add(targetClient); - setTaskInterval(delay); - setLastProcessTime(System.currentTimeMillis()); - } - - @Override - public void merge(AbstractDelayTask task) { - if (!(task instanceof FuzzyWatchNotifyChangeDelayTask)) { - return; - } - FuzzyWatchNotifyChangeDelayTask oldTask = (FuzzyWatchNotifyChangeDelayTask) task; - if (isPushToAll() || oldTask.isPushToAll()) { - pushToAll = true; - targetClients = null; - } else { - targetClients.addAll(oldTask.getTargetClients()); - } - setLastProcessTime(Math.min(getLastProcessTime(), task.getLastProcessTime())); - Loggers.PUSH.info("[FUZZY-WATCH-PUSH] Task merge for {}", service); - } - - public Service getService() { - return service; - } - - public boolean isPushToAll() { - return pushToAll; - } - - public String getChangedType() { - return changedType; - } - - public Set getTargetClients() { - return targetClients; - } -} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchNotifyChangeExecuteTask.java b/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchNotifyChangeExecuteTask.java deleted file mode 100644 index 493bde840c2..00000000000 --- a/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchNotifyChangeExecuteTask.java +++ /dev/null @@ -1,145 +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.naming.push.v2.task; - -import com.alibaba.nacos.api.naming.remote.request.FuzzyWatchNotifyChangeRequest; -import com.alibaba.nacos.api.remote.PushCallBack; -import com.alibaba.nacos.common.task.AbstractExecuteTask; -import com.alibaba.nacos.naming.core.v2.client.Client; -import com.alibaba.nacos.naming.core.v2.client.manager.ClientManager; -import com.alibaba.nacos.naming.core.v2.index.ClientFuzzyWatchIndexesManager; -import com.alibaba.nacos.naming.core.v2.pojo.Service; -import com.alibaba.nacos.naming.misc.Loggers; -import com.alibaba.nacos.naming.push.v2.NoRequiredRetryException; -import com.alibaba.nacos.naming.push.v2.PushConfig; - -import java.util.Collection; -import java.util.HashSet; -import java.util.Set; - -/** - * Nacos naming fuzzy watch notify service change push execute task. - * - * @author tanyongquan - */ -public class FuzzyWatchNotifyChangeExecuteTask extends AbstractExecuteTask { - - private final Service service; - - private final FuzzyWatchPushDelayTaskEngine delayTaskEngine; - - private final FuzzyWatchNotifyChangeDelayTask delayTask; - - public FuzzyWatchNotifyChangeExecuteTask(Service service, FuzzyWatchPushDelayTaskEngine delayTaskEngine, - FuzzyWatchNotifyChangeDelayTask delayTask) { - this.service = service; - this.delayTaskEngine = delayTaskEngine; - this.delayTask = delayTask; - } - - @Override - public void run() { - try { - ClientManager clientManager = delayTaskEngine.getClientManager(); - String changedType = delayTask.getChangedType(); - for (String clientId : getWatchTargetClientIds()) { - Client client = clientManager.getClient(clientId); - if (null == client) { - continue; - } - delayTaskEngine.getPushExecutor().doFuzzyWatchNotifyPushWithCallBack(clientId, new FuzzyWatchNotifyChangeRequest( - service.getNamespace(), service.getName(), service.getGroup(), changedType), - new WatchNotifyPushCallback(clientId, changedType)); - } - } catch (Exception e) { - Loggers.PUSH.error("Fuzzy watch notify task for service" + service.getGroupedServiceName() + " execute failed ", e); - delayTaskEngine.addTask(service, new FuzzyWatchNotifyChangeDelayTask(service, delayTask.getChangedType(), 1000L)); - } - } - - /** - * get watch notify client id. - * - * @return A set of ClientID need to be notified - */ - private Set getWatchTargetClientIds() { - if (!delayTask.isPushToAll()) { - return delayTask.getTargetClients(); - } - Set watchNotifyClientIds = new HashSet<>(16); - ClientFuzzyWatchIndexesManager indexesManager = delayTaskEngine.getClientFuzzyWatchIndexesManager(); - // get match result from index - Collection matchedPatterns = indexesManager.getServiceMatchedPatterns(service); - - for (String eachPattern : matchedPatterns) { - // for every matched pattern, get client id which watching this pattern - Collection clientIDs = indexesManager.getAllClientFuzzyWatchedPattern(eachPattern); - if (clientIDs == null || clientIDs.isEmpty()) { - // find there is nobody watch this pattern anymore (lazy remove) - indexesManager.removeWatchPatternMatchIndex(service, eachPattern); - continue; - } - watchNotifyClientIds.addAll(clientIDs); - } - return watchNotifyClientIds; - } - - private class WatchNotifyPushCallback implements PushCallBack { - - private final String clientId; - - private final String serviceChangedType; - - /** - * Record the push task execute start time. - */ - private final long executeStartTime; - - private WatchNotifyPushCallback(String clientId, String serviceChangedType) { - this.clientId = clientId; - this.serviceChangedType = serviceChangedType; - this.executeStartTime = System.currentTimeMillis(); - } - - @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(); - - Loggers.PUSH.info("[WATCH-PUSH-SUCC] {}ms, all delay time {}ms for client {}, service {}, changed type {} ", - pushCostTimeForNetWork, pushCostTimeForAll, clientId, service.getGroupedServiceName(), serviceChangedType); - } - - @Override - public void onFail(Throwable e) { - long pushCostTime = System.currentTimeMillis() - executeStartTime; - Loggers.PUSH.error("[WATCH-PUSH-FAIL] {}ms, service {}, changed type {}, reason={}, client={}", pushCostTime, - service.getGroupedServiceName(), serviceChangedType, e.getMessage(), clientId); - if (!(e instanceof NoRequiredRetryException)) { - Loggers.PUSH.error("Reason detail: ", e); - delayTaskEngine.addTask(service, new FuzzyWatchNotifyChangeDelayTask(service, - serviceChangedType, PushConfig.getInstance().getPushTaskDelay(), clientId)); - } - } - } -} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchPushDelayTaskEngine.java b/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchPushDelayTaskEngine.java index 8bb7aa0ceaa..5a790c35704 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchPushDelayTaskEngine.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchPushDelayTaskEngine.java @@ -16,12 +16,11 @@ package com.alibaba.nacos.naming.push.v2.task; +import com.alibaba.nacos.api.exception.runtime.NacosRuntimeException; import com.alibaba.nacos.common.task.NacosTask; import com.alibaba.nacos.common.task.NacosTaskProcessor; import com.alibaba.nacos.common.task.engine.NacosDelayTaskExecuteEngine; -import com.alibaba.nacos.naming.core.v2.client.manager.ClientManager; -import com.alibaba.nacos.naming.core.v2.index.ClientFuzzyWatchIndexesManager; -import com.alibaba.nacos.naming.core.v2.index.ClientServiceIndexesManager; +import com.alibaba.nacos.naming.core.v2.index.NamingFuzzyWatchContextService; import com.alibaba.nacos.naming.core.v2.index.ServiceStorage; import com.alibaba.nacos.naming.core.v2.metadata.NamingMetadataManager; import com.alibaba.nacos.naming.core.v2.pojo.Service; @@ -37,10 +36,7 @@ */ public class FuzzyWatchPushDelayTaskEngine extends NacosDelayTaskExecuteEngine { - private final ClientManager clientManager; - - private final ClientFuzzyWatchIndexesManager clientFuzzyWatchIndexesManager; - + private final NamingFuzzyWatchContextService namingFuzzyWatchContextService; private final ServiceStorage serviceStorage; @@ -50,12 +46,11 @@ public class FuzzyWatchPushDelayTaskEngine extends NacosDelayTaskExecuteEngine { private final SwitchDomain switchDomain; - public FuzzyWatchPushDelayTaskEngine(ClientManager clientManager, ClientFuzzyWatchIndexesManager clientFuzzyWatchIndexesManager, - ServiceStorage serviceStorage, NamingMetadataManager metadataManager, - PushExecutor pushExecutor, SwitchDomain switchDomain) { + public FuzzyWatchPushDelayTaskEngine(NamingFuzzyWatchContextService namingFuzzyWatchContextService, + ServiceStorage serviceStorage, NamingMetadataManager metadataManager, PushExecutor pushExecutor, + SwitchDomain switchDomain) { super(FuzzyWatchPushDelayTaskEngine.class.getSimpleName(), Loggers.PUSH); - this.clientManager = clientManager; - this.clientFuzzyWatchIndexesManager = clientFuzzyWatchIndexesManager; + this.namingFuzzyWatchContextService = namingFuzzyWatchContextService; this.serviceStorage = serviceStorage; this.metadataManager = metadataManager; this.pushExecutor = pushExecutor; @@ -63,13 +58,6 @@ public FuzzyWatchPushDelayTaskEngine(ClientManager clientManager, ClientFuzzyWat setDefaultTaskProcessor(new WatchPushDelayTaskProcessor(this)); } - public ClientManager getClientManager() { - return clientManager; - } - - public ClientFuzzyWatchIndexesManager getClientFuzzyWatchIndexesManager() { - return clientFuzzyWatchIndexesManager; - } public ServiceStorage getServiceStorage() { return serviceStorage; @@ -91,6 +79,7 @@ protected void processTasks() { super.processTasks(); } + private static class WatchPushDelayTaskProcessor implements NacosTaskProcessor { private final FuzzyWatchPushDelayTaskEngine executeEngine; @@ -101,22 +90,40 @@ public WatchPushDelayTaskProcessor(FuzzyWatchPushDelayTaskEngine executeEngine) @Override public boolean process(NacosTask task) { - if (task instanceof FuzzyWatchNotifyChangeDelayTask) { - FuzzyWatchNotifyChangeDelayTask notifyDelayTask = (FuzzyWatchNotifyChangeDelayTask) task; - Service service = notifyDelayTask.getService(); - NamingExecuteTaskDispatcher.getInstance() - .dispatchAndExecuteTask(service, new FuzzyWatchNotifyChangeExecuteTask(service, executeEngine, notifyDelayTask)); - } else if (task instanceof FuzzyWatchInitDelayTask) { - FuzzyWatchInitDelayTask fuzzyWatchInitDelayTask = (FuzzyWatchInitDelayTask) task; - String pattern = fuzzyWatchInitDelayTask.getPattern(); - String clientId = fuzzyWatchInitDelayTask.getClientId(); - String taskKey = fuzzyWatchInitDelayTask.getTaskKey(); - NamingExecuteTaskDispatcher.getInstance() - .dispatchAndExecuteTask(taskKey, new FuzzyWatchInitExecuteTask(taskKey, clientId, pattern, - fuzzyWatchInitDelayTask.getOriginSize(), executeEngine, fuzzyWatchInitDelayTask, - fuzzyWatchInitDelayTask.isFinishInit())); + + if (task instanceof FuzzyWatchChangeNotifyTask) { + //process fuzzy watch change notify when a service changed + FuzzyWatchChangeNotifyTask fuzzyWatchChangeNotifyTask = (FuzzyWatchChangeNotifyTask) task; + NamingExecuteTaskDispatcher.getInstance().dispatchAndExecuteTask( + getTaskKey(task), + new FuzzyWatchChangeNotifyExecuteTask(executeEngine, fuzzyWatchChangeNotifyTask.getServiceKey(), + fuzzyWatchChangeNotifyTask.getChangedType(), fuzzyWatchChangeNotifyTask.getClientId())); + } else if (task instanceof FuzzyWatchInitNotifyTask) { + //process fuzzy watch init notify when a new client fuzzy watch a pattern + FuzzyWatchInitNotifyTask fuzzyWatchInitNotifyTask = (FuzzyWatchInitNotifyTask) task; + String pattern = fuzzyWatchInitNotifyTask.getPattern(); + String clientId = fuzzyWatchInitNotifyTask.getClientId(); + NamingExecuteTaskDispatcher.getInstance().dispatchAndExecuteTask(getTaskKey(task), + new FuzzyWatchInitNotifyExecuteTask(clientId, pattern, + fuzzyWatchInitNotifyTask.getOriginSize(), executeEngine, fuzzyWatchInitNotifyTask, + fuzzyWatchInitNotifyTask.isFinishInit())); } return true; } + + } + + public NamingFuzzyWatchContextService getNamingFuzzyWatchContextService() { + return namingFuzzyWatchContextService; + } + + public static String getTaskKey(NacosTask task){ + if(task instanceof FuzzyWatchChangeNotifyTask){ + return "fwcnT-"+((FuzzyWatchChangeNotifyTask) task).getClientId()+((FuzzyWatchChangeNotifyTask) task).getServiceKey(); + }else if (task instanceof FuzzyWatchInitNotifyTask){ + return "fwinT-"+((FuzzyWatchInitNotifyTask) task).getClientId()+((FuzzyWatchInitNotifyTask) task).getPattern(); + }else { + throw new NacosRuntimeException(500,"unknown fuzzy task type"); + } } } 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 578c0f91dbd..7f43ce7839d 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 @@ -19,45 +19,44 @@ import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.naming.remote.NamingRemoteConstants; import com.alibaba.nacos.api.naming.remote.request.NamingFuzzyWatchRequest; -import com.alibaba.nacos.api.naming.remote.response.FuzzyWatchResponse; +import com.alibaba.nacos.api.naming.remote.response.NamingFuzzyWatchResponse; 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.core.remote.RequestHandler; -import com.alibaba.nacos.naming.core.v2.service.impl.EphemeralClientOperationServiceImpl; +import com.alibaba.nacos.naming.core.v2.event.client.ClientOperationEvent; import com.alibaba.nacos.plugin.auth.constant.ActionTypes; import org.springframework.stereotype.Component; +import java.util.Set; + /** * Fuzzy watch service request handler. * * @author tanyongquan */ -@Component("fuzzyWatchRequestHandler") -public class NamingFuzzyWatchRequestHandler extends RequestHandler { - - private final EphemeralClientOperationServiceImpl clientOperationService; +@Component("namingFuzzyWatchRequestHandler") +public class NamingFuzzyWatchRequestHandler extends RequestHandler { - public NamingFuzzyWatchRequestHandler(EphemeralClientOperationServiceImpl clientOperationService) { - this.clientOperationService = clientOperationService; + public NamingFuzzyWatchRequestHandler() { } @Override @Secured(action = ActionTypes.READ) - public FuzzyWatchResponse handle(NamingFuzzyWatchRequest request, RequestMeta meta) throws NacosException { - String serviceNamePattern = request.getServiceName(); - String groupNamePattern = request.getGroupName(); - String namespaceId = request.getNamespace(); + public NamingFuzzyWatchResponse handle(NamingFuzzyWatchRequest request, RequestMeta meta) throws NacosException { + + String groupKeyPattern = request.getGroupKeyPattern(); - switch (request.getType()) { + switch (request.getWatchType()) { case NamingRemoteConstants.FUZZY_WATCH_SERVICE: - clientOperationService.fuzzyWatch(namespaceId, serviceNamePattern, groupNamePattern, meta.getConnectionId()); - return FuzzyWatchResponse.buildSuccessResponse(NamingRemoteConstants.FUZZY_WATCH_SERVICE); + NotifyCenter.publishEvent(new ClientOperationEvent.ClientFuzzyWatchEvent(groupKeyPattern, meta.getConnectionId(),request.getReceivedGroupKeys(),request.isInitializing())); + return NamingFuzzyWatchResponse.buildSuccessResponse(NamingRemoteConstants.FUZZY_WATCH_SERVICE); case NamingRemoteConstants.CANCEL_FUZZY_WATCH_SERVICE: - clientOperationService.cancelFuzzyWatch(namespaceId, serviceNamePattern, groupNamePattern, meta.getConnectionId()); - return FuzzyWatchResponse.buildSuccessResponse(NamingRemoteConstants.CANCEL_FUZZY_WATCH_SERVICE); + NotifyCenter.publishEvent(new ClientOperationEvent.ClientCancelFuzzyWatchEvent(groupKeyPattern, meta.getConnectionId())); + return NamingFuzzyWatchResponse.buildSuccessResponse(NamingRemoteConstants.CANCEL_FUZZY_WATCH_SERVICE); default: throw new NacosException(NacosException.INVALID_PARAM, - String.format("Unsupported request type %s", request.getType())); + String.format("Unsupported request type %s", request.getWatchType())); } } } diff --git a/naming/src/test/java/com/alibaba/nacos/naming/push/v2/NamingSubscriberServiceV2ImplTest.java b/naming/src/test/java/com/alibaba/nacos/naming/push/v2/NamingSubscriberServiceV2ImplTest.java index fe40ea8c705..234c7a63be7 100644 --- a/naming/src/test/java/com/alibaba/nacos/naming/push/v2/NamingSubscriberServiceV2ImplTest.java +++ b/naming/src/test/java/com/alibaba/nacos/naming/push/v2/NamingSubscriberServiceV2ImplTest.java @@ -20,7 +20,7 @@ import com.alibaba.nacos.naming.core.v2.client.Client; import com.alibaba.nacos.naming.core.v2.client.manager.ClientManagerDelegate; import com.alibaba.nacos.naming.core.v2.event.service.ServiceEvent; -import com.alibaba.nacos.naming.core.v2.index.ClientFuzzyWatchIndexesManager; +import com.alibaba.nacos.naming.core.v2.index.NamingFuzzyWatchContextService; import com.alibaba.nacos.naming.core.v2.index.ClientServiceIndexesManager; import com.alibaba.nacos.naming.core.v2.pojo.Service; import com.alibaba.nacos.naming.misc.SwitchDomain; @@ -64,7 +64,7 @@ class NamingSubscriberServiceV2ImplTest { private ClientServiceIndexesManager indexesManager; @Mock - private ClientFuzzyWatchIndexesManager clientFuzzyWatchIndexesManager; + private NamingFuzzyWatchContextService namingFuzzyWatchContextService; @Mock private PushDelayTaskExecuteEngine delayTaskEngine; @@ -79,7 +79,8 @@ class NamingSubscriberServiceV2ImplTest { @BeforeEach void setUp() throws Exception { - subscriberService = new NamingSubscriberServiceV2Impl(clientManager, indexesManager, clientFuzzyWatchIndexesManager, null, null,null, switchDomain); + subscriberService = new NamingSubscriberServiceV2Impl(clientManager, indexesManager, + namingFuzzyWatchContextService, null, null,null, switchDomain); ReflectionTestUtils.setField(subscriberService, "delayTaskEngine", delayTaskEngine); when(indexesManager.getAllClientsSubscribeService(service)).thenReturn(Collections.singletonList(testClientId)); when(indexesManager.getAllClientsSubscribeService(service1)).thenReturn(Collections.singletonList(testClientId)); From 63d42c6748d53c8e2048eabb405ae7b5c0902941 Mon Sep 17 00:00:00 2001 From: "zunfei.lzf" Date: Mon, 6 Jan 2025 10:07:38 +0800 Subject: [PATCH 06/15] naming fuzzy watch optimize --- .../nacos/client/naming/cache/FuzzyWatchServiceListHolder.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/cache/FuzzyWatchServiceListHolder.java b/client/src/main/java/com/alibaba/nacos/client/naming/cache/FuzzyWatchServiceListHolder.java index b32def7c340..870c3e6e48e 100644 --- a/client/src/main/java/com/alibaba/nacos/client/naming/cache/FuzzyWatchServiceListHolder.java +++ b/client/src/main/java/com/alibaba/nacos/client/naming/cache/FuzzyWatchServiceListHolder.java @@ -68,7 +68,7 @@ public synchronized void removePatternMatchCache(String groupkeyPattern) { return; } if (namingFuzzyWatchContext.isDiscard()&&namingFuzzyWatchContext.getNamingFuzzyWatchers().isEmpty()){ - fuzzyMatchContextMap.remove(groupkeyPattern) + fuzzyMatchContextMap.remove(groupkeyPattern); } } From 0f94c08248ef0fb9ef62c1f8efb4345f923d172a Mon Sep 17 00:00:00 2001 From: "zunfei.lzf" Date: Tue, 7 Jan 2025 16:32:47 +0800 Subject: [PATCH 07/15] naming fuzzy watch optimize --- .../alibaba/nacos/api/common/Constants.java | 28 +- .../nacos/api/config/ConfigService.java | 9 +- .../listener/ConfigFuzzyWatchChangeEvent.java | 44 +- .../config/listener/ConfigFuzzyWatcher.java | 2 +- .../request/ConfigBatchFuzzyWatchRequest.java | 154 ---- .../ConfigFuzzyWatchChangeNotifyRequest.java | 83 +++ .../request/ConfigFuzzyWatchRequest.java | 104 +++ ....java => ConfigFuzzyWatchSyncRequest.java} | 84 +-- .../FuzzyWatchChangeNotifyRequest.java | 113 --- ...ConfigFuzzyWatchChangeNotifyResponse.java} | 4 +- ...nse.java => ConfigFuzzyWatchResponse.java} | 2 +- ...java => ConfigFuzzyWatchSyncResponse.java} | 2 +- .../listener/FuzzyWatchChangeEvent.java | 5 +- .../listener/FuzzyWatchEventWatcher.java | 6 +- .../naming/listener/FuzzyWatchListener.java | 41 -- .../nacos/api/naming/pojo/Service.java | 29 - .../naming/remote/NamingRemoteConstants.java | 4 - .../AbstractFuzzyWatchNotifyRequest.java | 14 +- .../request/FuzzyWatchNotifyInitRequest.java | 68 -- ... NamingFuzzyWatchChangeNotifyRequest.java} | 20 +- .../request/NamingFuzzyWatchSyncRequest.java | 145 ++++ ...NamingFuzzyWatchChangeNotifyResponse.java} | 2 +- .../response/NamingFuzzyWatchResponse.java | 20 +- .../NamingFuzzyWatchSyncResponse.java | 49 ++ .../com.alibaba.nacos.api.remote.Payload | 22 +- .../client/config/NacosConfigService.java | 21 +- .../ClientFuzzyWatchNotifyRequestHandler.java | 47 ++ .../client/config/impl/ClientWorker.java | 693 ++---------------- .../config/impl/ConfigFuzzyWatchContext.java | 36 +- .../impl/ConfigFuzzyWatchGroupKeyHolder.java | 460 ++++++++++++ .../config/impl/ConfigTransportClient.java | 27 +- .../config/impl/FuzzyWatchNotifyEvent.java | 125 +--- .../client/naming/NacosNamingService.java | 25 +- .../cache/FuzzyWatchServiceListHolder.java | 102 --- .../naming/cache/NamingFuzzyWatchContext.java | 122 ++- .../NamingFuzzyWatchServiceListHolder.java | 319 ++++++++ ....java => NamingFuzzyWatchNotifyEvent.java} | 43 +- .../remote/NamingClientProxyDelegate.java | 12 +- .../NamingFuzzyWatchNotifyRequestHandler.java | 113 +++ .../gprc/NamingFuzzyWatchRequestHandler.java | 92 --- .../remote/gprc/NamingGrpcClientProxy.java | 21 +- .../remote/gprc/redo/FuzzyWatchSyncTask.java | 38 +- .../gprc/redo/NamingGrpcRedoService.java | 13 +- .../remote/http/NamingHttpClientProxy.java | 17 - .../alibaba/nacos/client/utils/ParamUtil.java | 35 +- .../client/config/NacosConfigServiceTest.java | 18 +- .../remote/NamingClientProxyDelegateTest.java | 8 +- .../gprc/NamingGrpcClientProxyTest.java | 11 +- .../gprc/NamingPushRequestHandlerTest.java | 7 +- .../gprc/redo/NamingGrpcRedoServiceTest.java | 7 +- .../common/utils/FuzzyGroupKeyPattern.java | 6 +- .../alibaba/nacos/common/utils/GroupKey.java | 140 ---- .../configuration/ConfigCommonConfig.java | 2 +- ...nEvent.java => ConfigFuzzyWatchEvent.java} | 4 +- .../ConfigBatchFuzzyWatchRequestHandler.java | 30 +- ...java => ConfigFuzzyWatchSyncNotifier.java} | 38 +- .../FuzzyWatchConfigChangeNotifier.java | 54 +- .../server/service/ConfigCacheService.java | 6 +- .../ConfigFuzzyWatchContextService.java | 58 ++ ...atchFuzzyListenRequestParamsExtractor.java | 25 +- .../naming/core/v2/client/AbstractClient.java | 31 - .../v2/event/client/ClientOperationEvent.java | 33 +- .../core/v2/event/service/ServiceEvent.java | 38 +- .../v2/index/ClientServiceIndexesManager.java | 5 +- .../index/NamingFuzzyWatchContextService.java | 236 +++--- .../service/ClientOperationServiceProxy.java | 10 - .../PersistentClientOperationServiceImpl.java | 10 - .../push/NamingFuzzyWatchChangeNotifier.java | 74 ++ .../push/NamingFuzzyWatchSyncNotifier.java | 142 ++++ .../v2/NamingSubscriberServiceV2Impl.java | 52 +- .../naming/push/v2/executor/PushExecutor.java | 1 - .../FuzzyWatchChangeNotifyExecuteTask.java | 34 +- .../v2/task/FuzzyWatchChangeNotifyTask.java | 3 +- .../task/FuzzyWatchInitNotifyExecuteTask.java | 194 ----- .../v2/task/FuzzyWatchInitNotifyTask.java | 84 --- .../task/FuzzyWatchPushDelayTaskEngine.java | 59 +- .../task/FuzzyWatchSyncNotifyExecuteTask.java | 149 ++++ .../v2/task/FuzzyWatchSyncNotifyTask.java | 139 ++++ .../NamingFuzzyWatchRequestHandler.java | 14 +- .../v2/NamingSubscriberServiceV2ImplTest.java | 3 +- .../push/v2/task/FixturePushExecutor.java | 5 - pom.xml | 2 +- 82 files changed, 2535 insertions(+), 2612 deletions(-) delete mode 100644 api/src/main/java/com/alibaba/nacos/api/config/remote/request/ConfigBatchFuzzyWatchRequest.java create mode 100644 api/src/main/java/com/alibaba/nacos/api/config/remote/request/ConfigFuzzyWatchChangeNotifyRequest.java create mode 100644 api/src/main/java/com/alibaba/nacos/api/config/remote/request/ConfigFuzzyWatchRequest.java rename api/src/main/java/com/alibaba/nacos/api/config/remote/request/{FuzzyWatchDiffSyncRequest.java => ConfigFuzzyWatchSyncRequest.java} (64%) delete mode 100644 api/src/main/java/com/alibaba/nacos/api/config/remote/request/FuzzyWatchChangeNotifyRequest.java rename api/src/main/java/com/alibaba/nacos/api/config/remote/response/{FuzzyWatchNotifyDiffResponse.java => ConfigFuzzyWatchChangeNotifyResponse.java} (88%) rename api/src/main/java/com/alibaba/nacos/api/config/remote/response/{ConfigBatchFuzzyWatchResponse.java => ConfigFuzzyWatchResponse.java} (92%) rename api/src/main/java/com/alibaba/nacos/api/config/remote/response/{FuzzyWatchChangeNotifyResponse.java => ConfigFuzzyWatchSyncResponse.java} (92%) delete mode 100644 api/src/main/java/com/alibaba/nacos/api/naming/listener/FuzzyWatchListener.java delete mode 100644 api/src/main/java/com/alibaba/nacos/api/naming/remote/request/FuzzyWatchNotifyInitRequest.java rename api/src/main/java/com/alibaba/nacos/api/naming/remote/request/{FuzzyWatchNotifyChangeRequest.java => NamingFuzzyWatchChangeNotifyRequest.java} (69%) create mode 100644 api/src/main/java/com/alibaba/nacos/api/naming/remote/request/NamingFuzzyWatchSyncRequest.java rename api/src/main/java/com/alibaba/nacos/api/naming/remote/response/{NotifyFuzzyWatcherResponse.java => NamingFuzzyWatchChangeNotifyResponse.java} (92%) create mode 100644 api/src/main/java/com/alibaba/nacos/api/naming/remote/response/NamingFuzzyWatchSyncResponse.java create mode 100644 client/src/main/java/com/alibaba/nacos/client/config/impl/ClientFuzzyWatchNotifyRequestHandler.java create mode 100644 client/src/main/java/com/alibaba/nacos/client/config/impl/ConfigFuzzyWatchGroupKeyHolder.java delete mode 100644 client/src/main/java/com/alibaba/nacos/client/naming/cache/FuzzyWatchServiceListHolder.java create mode 100644 client/src/main/java/com/alibaba/nacos/client/naming/cache/NamingFuzzyWatchServiceListHolder.java rename client/src/main/java/com/alibaba/nacos/client/naming/event/{FuzzyWatchNotifyEvent.java => NamingFuzzyWatchNotifyEvent.java} (54%) create mode 100644 client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/NamingFuzzyWatchNotifyRequestHandler.java delete mode 100644 client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/NamingFuzzyWatchRequestHandler.java delete mode 100644 common/src/main/java/com/alibaba/nacos/common/utils/GroupKey.java rename config/src/main/java/com/alibaba/nacos/config/server/model/event/{ConfigBatchFuzzyListenEvent.java => ConfigFuzzyWatchEvent.java} (95%) rename config/src/main/java/com/alibaba/nacos/config/server/remote/{ConfigFuzzyWatchDiffNotifier.java => ConfigFuzzyWatchSyncNotifier.java} (91%) create mode 100644 naming/src/main/java/com/alibaba/nacos/naming/push/NamingFuzzyWatchChangeNotifier.java create mode 100644 naming/src/main/java/com/alibaba/nacos/naming/push/NamingFuzzyWatchSyncNotifier.java delete mode 100644 naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchInitNotifyExecuteTask.java delete mode 100644 naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchInitNotifyTask.java create mode 100644 naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchSyncNotifyExecuteTask.java create mode 100644 naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchSyncNotifyTask.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 ff32891bb09..39225ac7364 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 @@ -278,14 +278,38 @@ public static class Naming { public static final String FUZZY_WATCH_PATTERN_SPLITTER = ">>"; + /** + * fuzzy watch sync type of watch init notify + */ public static final String FUZZY_WATCH_INIT_NOTIFY = "FUZZY_WATCH_INIT_NOTIFY"; + /** + * fuzzy watch sync type of watch init notify finish + */ public static final String FINISH_FUZZY_WATCH_INIT_NOTIFY = "FINISH_FUZZY_WATCH_INIT_NOTIFY"; + /** + * fuzzy watch sync type of watch diff sync notify + */ public static final String FUZZY_WATCH_DIFF_SYNC_NOTIFY = "FUZZY_WATCH_DIFF_SYNC_NOTIFY"; /** - * The constants in config fuzzy watch event type directory. + * fuzzy watch sync type of watch resource changed + */ + public static final String FUZZY_WATCH_RESOURCE_CHANGED = "FUZZY_WATCH_RESOURCE_CHANGED"; + + /** + * watch type of watch + */ + public static final String WATCH_TYPE_WATCH = "WATCH"; + + /** + * watch type of cancel watch + */ + public static final String WATCH_TYPE_CANCEL_WATCH = "CANCEL_WATCH"; + + /** + * The constants in config fuzzy watch changed type directory. */ public static class ConfigChangedType { @@ -298,7 +322,7 @@ public static class ConfigChangedType { } /** - * The constants in service fuzzy watch event type directory. + * The constants in naming fuzzy watch changed type directory. */ public static class ServiceChangedType { diff --git a/api/src/main/java/com/alibaba/nacos/api/config/ConfigService.java b/api/src/main/java/com/alibaba/nacos/api/config/ConfigService.java index 870eba56694..d7281bbaa68 100644 --- a/api/src/main/java/com/alibaba/nacos/api/config/ConfigService.java +++ b/api/src/main/java/com/alibaba/nacos/api/config/ConfigService.java @@ -21,7 +21,6 @@ import com.alibaba.nacos.api.config.listener.Listener; import com.alibaba.nacos.api.exception.NacosException; -import java.util.Collection; import java.util.Set; import java.util.concurrent.Future; @@ -173,11 +172,11 @@ boolean publishConfigCas(String dataId, String group, String content, String cas * specified for subscription. * * @param fixedGroupName The fixed group name representing the group and dataId patterns to subscribe to. - * @param listener The fuzzy listener to be added. + * @param watcher The fuzzy watcher to be added. * @throws NacosException NacosException * @since 3.0 */ - void fuzzyWatch(String fixedGroupName, ConfigFuzzyWatcher listener) throws NacosException; + void fuzzyWatch(String fixedGroupName, ConfigFuzzyWatcher watcher) throws NacosException; /** * Add a fuzzy listener to the configuration. After the server modifies the configuration matching the specified @@ -235,13 +234,13 @@ Future> fuzzyWatchWithGroupKeys(String dataIdPattern, String fixedGr /** * Cancel fuzzy listen and remove the event listener for a specified service name pattern and fixed group name. * - * @param dataIdPatter The pattern to match dataId for fuzzy watch. + * @param dataIdPattern The pattern to match dataId for fuzzy watch. * @param fixedGroupName The fixed group name for fuzzy watch. * @param watcher The event listener to be removed. * @throws NacosException If an error occurs during the cancellation process. * @since 3.0 */ - void cancelFuzzyWatch(String dataIdPatter, String fixedGroupName, ConfigFuzzyWatcher watcher) + void cancelFuzzyWatch(String dataIdPattern, String fixedGroupName, ConfigFuzzyWatcher watcher) throws NacosException; } diff --git a/api/src/main/java/com/alibaba/nacos/api/config/listener/ConfigFuzzyWatchChangeEvent.java b/api/src/main/java/com/alibaba/nacos/api/config/listener/ConfigFuzzyWatchChangeEvent.java index 5cac987f436..36c9e7be59c 100644 --- a/api/src/main/java/com/alibaba/nacos/api/config/listener/ConfigFuzzyWatchChangeEvent.java +++ b/api/src/main/java/com/alibaba/nacos/api/config/listener/ConfigFuzzyWatchChangeEvent.java @@ -39,21 +39,20 @@ public class ConfigFuzzyWatchChangeEvent { private String dataId; /** - * The namaspace of the configuration that has changed. + * The namespace of the configuration that has changed. */ private String namespace; /** - * The type of change that has occurred ("FUZZY_WATCH_INIT_NOTIFY", "ADD_CONFIG", "DELETE_CONFIG"). + * The change type of local watcher , contains {"ADD_CONFIG", "DELETE_CONFIG"}. * see {@link Constants.ConfigChangedType} */ private String changedType; /** - * Constructs an empty FuzzyListenConfigChangeEvent. + * the sync type that trigger this changed,contains {"FUZZY_WATCH_INIT_NOTIFY","FUZZY_WATCH_RESOURCE_CHANGED", "FUZZY_WATCH_DIFF_SYNC_NOTIFY"} */ - public ConfigFuzzyWatchChangeEvent() { - } + private String syncType; /** * Constructs a FuzzyListenConfigChangeEvent with the specified parameters. @@ -62,11 +61,12 @@ public ConfigFuzzyWatchChangeEvent() { * @param dataId The data ID of the configuration that has changed * @param changedType The type of change that has occurred */ - public ConfigFuzzyWatchChangeEvent(String namespace,String group, String dataId, String changedType) { + private ConfigFuzzyWatchChangeEvent(String namespace,String group, String dataId, String changedType,String syncType) { this.group = group; this.dataId = dataId; this.namespace=namespace; this.changedType = changedType; + this.syncType=syncType; } /** @@ -74,53 +74,37 @@ public ConfigFuzzyWatchChangeEvent(String namespace,String group, String dataId, * * @param group The group of the configuration that has changed * @param dataId The data ID of the configuration that has changed - * @param type The type of change that has occurred + * @param changedType The type of change that has occurred * @return A new FuzzyListenConfigChangeEvent instance */ - public static ConfigFuzzyWatchChangeEvent build(String namespace,String group, String dataId, String type) { - return new ConfigFuzzyWatchChangeEvent(namespace,group, dataId, type); + public static ConfigFuzzyWatchChangeEvent build(String namespace,String group, String dataId, String changedType,String syncType) { + return new ConfigFuzzyWatchChangeEvent(namespace,group, dataId, changedType,syncType); } public String getNamespace() { return namespace; } - public void setNamespace(String namespace) { - this.namespace = namespace; - } - public String getGroup() { return group; } - public void setGroup(String group) { - this.group = group; - } - public String getDataId() { return dataId; } - public void setDataId(String dataId) { - this.dataId = dataId; - } - public String getChangedType() { return changedType; } - public void setChangedType(String changedType) { - this.changedType = changedType; + public String getSyncType() { + return syncType; } - /** - * Returns a string representation of the FuzzyListenConfigChangeEvent. - * - * @return A string representation of the event - */ @Override public String toString() { - return "FuzzyListenConfigChangeEvent{" + "group='" + group + '\'' + ", dataId='" + dataId + '\'' + ", type='" - + changedType + '\'' + '}'; + return "ConfigFuzzyWatchChangeEvent{" + "group='" + group + '\'' + ", dataId='" + dataId + '\'' + + ", namespace='" + namespace + '\'' + ", changedType='" + changedType + '\'' + ", syncType='" + + syncType + '\'' + '}'; } } diff --git a/api/src/main/java/com/alibaba/nacos/api/config/listener/ConfigFuzzyWatcher.java b/api/src/main/java/com/alibaba/nacos/api/config/listener/ConfigFuzzyWatcher.java index 1b1edc22d98..d38826044da 100644 --- a/api/src/main/java/com/alibaba/nacos/api/config/listener/ConfigFuzzyWatcher.java +++ b/api/src/main/java/com/alibaba/nacos/api/config/listener/ConfigFuzzyWatcher.java @@ -93,7 +93,7 @@ public boolean equals(Object o) { return Objects.equals(uuid, that.uuid); } - public Set getSyncGroupKeys() { + public final Set getSyncGroupKeys() { return syncGroupKeys; } diff --git a/api/src/main/java/com/alibaba/nacos/api/config/remote/request/ConfigBatchFuzzyWatchRequest.java b/api/src/main/java/com/alibaba/nacos/api/config/remote/request/ConfigBatchFuzzyWatchRequest.java deleted file mode 100644 index 8bb5f35f990..00000000000 --- a/api/src/main/java/com/alibaba/nacos/api/config/remote/request/ConfigBatchFuzzyWatchRequest.java +++ /dev/null @@ -1,154 +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.api.config.remote.request; - -import com.alibaba.nacos.api.common.Constants; -import com.alibaba.nacos.api.remote.request.Request; - -import java.util.HashSet; -import java.util.Set; - -/** - * Represents a request for batch fuzzy listening configurations. - * - *

This request is used to request batch fuzzy listening configurations from the server. It contains a set of - * contexts, each representing a fuzzy listening context. - * - * @author stone-98 - * @date 2024/3/4 - */ -public class ConfigBatchFuzzyWatchRequest extends Request { - - /** - * Set of fuzzy listening contexts. - */ - private Set contexts = new HashSet<>(); - - /** - * Constructs an empty ConfigBatchFuzzyListenRequest. - */ - public ConfigBatchFuzzyWatchRequest() { - } - - /** - * Adds a new context to the request. - * - - * @param listen Flag indicating whether to listen for changes - * @param isInitializing Flag indicating whether the client is initializing - */ - public void addContext(String groupKeyPattern, Set receivedGroupKeys, boolean listen, - boolean isInitializing) { - Context context = new Context(); - context.setGroupKeyPattern(groupKeyPattern); - context.setReceivedGroupKeys(receivedGroupKeys); - context.listen=listen; - context.isInitializing=isInitializing; - contexts.add(context); - } - - /** - * Get the set of fuzzy listening contexts. - * - * @return The set of contexts - */ - public Set getContexts() { - return contexts; - } - - /** - * Set the set of fuzzy listening contexts. - * - * @param contexts The set of contexts to be set - */ - public void setContexts(Set contexts) { - this.contexts = contexts; - } - - /** - * Get the module name for this request. - * - * @return The module name - */ - @Override - public String getModule() { - return Constants.Config.CONFIG_MODULE; - } - - /** - * Represents a fuzzy listening context. - */ - public static class Context { - - /** - * The namespace or tenant associated with the configurations. - */ - private String groupKeyPattern; - - - private Set receivedGroupKeys; - - - /** - * Flag indicating whether to listen for changes. - */ - private boolean listen; - - /** - * Flag indicating whether the client is initializing. - */ - private boolean isInitializing; - - /** - * Constructs an empty Context. - */ - public Context() { - } - - public boolean isListen() { - return listen; - } - - public void setListen(boolean listen) { - this.listen = listen; - } - - public boolean isInitializing() { - return isInitializing; - } - - public void setInitializing(boolean initializing) { - isInitializing = initializing; - } - - public String getGroupKeyPattern() { - return groupKeyPattern; - } - - public void setGroupKeyPattern(String groupKeyPattern) { - this.groupKeyPattern = groupKeyPattern; - } - - public Set getReceivedGroupKeys() { - return receivedGroupKeys; - } - - public void setReceivedGroupKeys(Set receivedGroupKeys) { - this.receivedGroupKeys = receivedGroupKeys; - } - } -} diff --git a/api/src/main/java/com/alibaba/nacos/api/config/remote/request/ConfigFuzzyWatchChangeNotifyRequest.java b/api/src/main/java/com/alibaba/nacos/api/config/remote/request/ConfigFuzzyWatchChangeNotifyRequest.java new file mode 100644 index 00000000000..b42854819c1 --- /dev/null +++ b/api/src/main/java/com/alibaba/nacos/api/config/remote/request/ConfigFuzzyWatchChangeNotifyRequest.java @@ -0,0 +1,83 @@ +/* + * 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.remote.request; + +/** + * Represents a request to notify changes when a fuzzy watched configuration changed. + * + *

This request is used to notify clients about changes in configurations that match fuzzy listening patterns. + * + * @author stone-98 + * @date 2024/3/13 + */ +public class ConfigFuzzyWatchChangeNotifyRequest extends AbstractFuzzyWatchNotifyRequest { + + /** + * The groupKey of the configuration that has changed. + */ + private String groupKey; + + + /** + * Indicates whether the configuration exists or not. + */ + private String changeType; + + /** + * Constructs an empty FuzzyListenNotifyChangeRequest. + */ + public ConfigFuzzyWatchChangeNotifyRequest() { + } + + /** + * Constructs a FuzzyListenNotifyChangeRequest with the specified parameters. + * + * @param groupKey The group of the configuration that has changed + * @param changeType Indicates whether the configuration exists or not + */ + public ConfigFuzzyWatchChangeNotifyRequest(String groupKey,String changeType) { + this.groupKey = groupKey; + this.changeType = changeType; + } + + public String getGroupKey() { + return groupKey; + } + + public void setGroupKey(String groupKey) { + this.groupKey = groupKey; + } + + public String getChangeType() { + return changeType; + } + + public void setChangeType(String changeType) { + this.changeType = changeType; + } + + /** + * Returns a string representation of the FuzzyListenNotifyChangeRequest. + * + * @return A string representation of the request + */ + @Override + public String toString() { + return "FuzzyListenNotifyChangeRequest{" + '\'' + ", groupKey='" + groupKey + '\'' + ", changeType=" + changeType + '}'; + } + +} diff --git a/api/src/main/java/com/alibaba/nacos/api/config/remote/request/ConfigFuzzyWatchRequest.java b/api/src/main/java/com/alibaba/nacos/api/config/remote/request/ConfigFuzzyWatchRequest.java new file mode 100644 index 00000000000..f1149742f7b --- /dev/null +++ b/api/src/main/java/com/alibaba/nacos/api/config/remote/request/ConfigFuzzyWatchRequest.java @@ -0,0 +1,104 @@ +/* + * 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.remote.request; + +import com.alibaba.nacos.api.common.Constants; +import com.alibaba.nacos.api.remote.request.Request; + +import java.util.Set; + +/** + * Represents a request for batch fuzzy listening configurations. + * + *

This request is used to request batch fuzzy listening configurations from the server. It contains a set of + * contexts, each representing a fuzzy listening context. + * + * @author stone-98 + * @date 2024/3/4 + */ +public class ConfigFuzzyWatchRequest extends Request { + + /** + * The namespace or tenant associated with the configurations. + */ + private String groupKeyPattern; + + + private Set receivedGroupKeys; + + + /** + * Flag indicating whether to listen for changes. + */ + private String watchType; + + /** + * Flag indicating whether the client is initializing. + */ + private boolean isInitializing; + + + /** + * Constructs an empty ConfigBatchFuzzyListenRequest. + */ + public ConfigFuzzyWatchRequest() { + } + + public String getGroupKeyPattern() { + return groupKeyPattern; + } + + public void setGroupKeyPattern(String groupKeyPattern) { + this.groupKeyPattern = groupKeyPattern; + } + + public Set getReceivedGroupKeys() { + return receivedGroupKeys; + } + + public void setReceivedGroupKeys(Set receivedGroupKeys) { + this.receivedGroupKeys = receivedGroupKeys; + } + + public String getWatchType() { + return watchType; + } + + public void setWatchType(String watchType) { + this.watchType = watchType; + } + + public boolean isInitializing() { + return isInitializing; + } + + public void setInitializing(boolean initializing) { + isInitializing = initializing; + } + + /** + * Get the module name for this request. + * + * @return The module name + */ + @Override + public String getModule() { + return Constants.Config.CONFIG_MODULE; + } + + +} diff --git a/api/src/main/java/com/alibaba/nacos/api/config/remote/request/FuzzyWatchDiffSyncRequest.java b/api/src/main/java/com/alibaba/nacos/api/config/remote/request/ConfigFuzzyWatchSyncRequest.java similarity index 64% rename from api/src/main/java/com/alibaba/nacos/api/config/remote/request/FuzzyWatchDiffSyncRequest.java rename to api/src/main/java/com/alibaba/nacos/api/config/remote/request/ConfigFuzzyWatchSyncRequest.java index 52c74843f9b..41800399843 100644 --- a/api/src/main/java/com/alibaba/nacos/api/config/remote/request/FuzzyWatchDiffSyncRequest.java +++ b/api/src/main/java/com/alibaba/nacos/api/config/remote/request/ConfigFuzzyWatchSyncRequest.java @@ -19,7 +19,6 @@ import com.alibaba.nacos.api.common.Constants; import java.util.HashSet; -import java.util.Objects; import java.util.Set; /** @@ -31,7 +30,7 @@ * @author stone-98 * @date 2024/3/6 */ -public class FuzzyWatchDiffSyncRequest extends AbstractFuzzyWatchNotifyRequest { +public class ConfigFuzzyWatchSyncRequest extends AbstractFuzzyWatchNotifyRequest { /** * The pattern used to match group keys for the configurations. @@ -59,7 +58,7 @@ public void setSyncType(String syncType) { /** * Constructs an empty FuzzyListenNotifyDiffRequest. */ - public FuzzyWatchDiffSyncRequest() { + public ConfigFuzzyWatchSyncRequest() { } /** @@ -68,7 +67,7 @@ public FuzzyWatchDiffSyncRequest() { * @param groupKeyPattern The pattern used to match group keys for the configurations * @param contexts The set of contexts containing information about the configurations */ - public FuzzyWatchDiffSyncRequest(String syncType, String groupKeyPattern, Set contexts) { + public ConfigFuzzyWatchSyncRequest(String syncType, String groupKeyPattern, Set contexts) { this.groupKeyPattern = groupKeyPattern; this.contexts = contexts; this.syncType = syncType; @@ -82,8 +81,8 @@ public FuzzyWatchDiffSyncRequest(String syncType, String groupKeyPattern, Set contexts, String groupKeyPattern) { - return new FuzzyWatchDiffSyncRequest(Constants.FUZZY_WATCH_INIT_NOTIFY, groupKeyPattern, contexts); + public static ConfigFuzzyWatchSyncRequest buildInitRequest(Set contexts, String groupKeyPattern) { + return new ConfigFuzzyWatchSyncRequest(Constants.FUZZY_WATCH_INIT_NOTIFY, groupKeyPattern, contexts); } /** @@ -93,8 +92,8 @@ public static FuzzyWatchDiffSyncRequest buildInitRequest(Set contexts, * @param groupKeyPattern The pattern used to match group keys for the configurations * @return An initial FuzzyListenNotifyDiffRequest */ - public static FuzzyWatchDiffSyncRequest buildDiffSyncRequest(Set contexts, String groupKeyPattern) { - return new FuzzyWatchDiffSyncRequest(Constants.FUZZY_WATCH_DIFF_SYNC_NOTIFY, groupKeyPattern, contexts); + public static ConfigFuzzyWatchSyncRequest buildDiffSyncRequest(Set contexts, String groupKeyPattern) { + return new ConfigFuzzyWatchSyncRequest(Constants.FUZZY_WATCH_DIFF_SYNC_NOTIFY, groupKeyPattern, contexts); } /** @@ -103,8 +102,8 @@ public static FuzzyWatchDiffSyncRequest buildDiffSyncRequest(Set contex * @param groupKeyPattern The pattern used to match group keys for the configurations * @return A final FuzzyListenNotifyDiffRequest */ - public static FuzzyWatchDiffSyncRequest buildInitFinishRequest(String groupKeyPattern) { - return new FuzzyWatchDiffSyncRequest(Constants.FINISH_FUZZY_WATCH_INIT_NOTIFY, groupKeyPattern, + public static ConfigFuzzyWatchSyncRequest buildInitFinishRequest(String groupKeyPattern) { + return new ConfigFuzzyWatchSyncRequest(Constants.FINISH_FUZZY_WATCH_INIT_NOTIFY, groupKeyPattern, new HashSet<>()); } @@ -129,12 +128,7 @@ public void setContexts(Set contexts) { */ public static class Context { - private String tenant; - - private String group; - - private String dataId; - + String groupKey; /** * see {@link com.alibaba.nacos.api.common.Constants.ConfigChangedType ADD_CONFIG&} ADD_CONFIG: a new config * should be added for clientside . DELETE_CONFIG: a config should be removed for clientside . @@ -150,63 +144,25 @@ public Context() { /** * Builds a new context object with the provided parameters. * - * @param tenant The tenant associated with the configuration. - * @param group The group associated with the configuration. - * @param dataId The data ID of the configuration. + * @param groupKey The groupKey associated of the configuration. * @param changedType The type of the configuration change event. * @return A new context object initialized with the provided parameters. */ - public static Context build(String tenant, String group, String dataId, String changedType) { + public static Context build(String groupKey, String changedType) { Context context = new Context(); - context.setTenant(tenant); - context.setGroup(group); - context.setDataId(dataId); + context.setGroupKey(groupKey); context.setChangedType(changedType); return context; } - - @Override - public int hashCode() { - return Objects.hash(tenant, group, dataId, tenant); - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - Context that = (Context) o; - return Objects.equals(tenant, that.tenant) && Objects.equals(group, that.group) && Objects.equals(dataId, - that.dataId) && Objects.equals(changedType, that.changedType); - } - - public String getTenant() { - return tenant; - } - - public void setTenant(String tenant) { - this.tenant = tenant; - } - - public String getGroup() { - return group; - } - - public void setGroup(String group) { - this.group = group; - } - - public String getDataId() { - return dataId; + + public String getGroupKey() { + return groupKey; } - - public void setDataId(String dataId) { - this.dataId = dataId; + + public void setGroupKey(String groupKey) { + this.groupKey = groupKey; } - + public String getChangedType() { return changedType; } diff --git a/api/src/main/java/com/alibaba/nacos/api/config/remote/request/FuzzyWatchChangeNotifyRequest.java b/api/src/main/java/com/alibaba/nacos/api/config/remote/request/FuzzyWatchChangeNotifyRequest.java deleted file mode 100644 index f826b13a61c..00000000000 --- a/api/src/main/java/com/alibaba/nacos/api/config/remote/request/FuzzyWatchChangeNotifyRequest.java +++ /dev/null @@ -1,113 +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.api.config.remote.request; - -/** - * Represents a request to notify changes in a fuzzy listening configuration. - * - *

This request is used to notify clients about changes in configurations that match fuzzy listening patterns. - * - * @author stone-98 - * @date 2024/3/13 - */ -public class FuzzyWatchChangeNotifyRequest extends AbstractFuzzyWatchNotifyRequest { - - /** - * The tenant of the configuration that has changed. - */ - private String namespace; - - /** - * The group of the configuration that has changed. - */ - private String group; - - /** - * The data ID of the configuration that has changed. - */ - private String dataId; - - /** - * Indicates whether the configuration exists or not. - */ - private boolean isExist; - - /** - * Constructs an empty FuzzyListenNotifyChangeRequest. - */ - public FuzzyWatchChangeNotifyRequest() { - } - - /** - * Constructs a FuzzyListenNotifyChangeRequest with the specified parameters. - * - * @param namespace The tenant of the configuration that has changed - * @param group The group of the configuration that has changed - * @param dataId The data ID of the configuration that has changed - * @param isExist Indicates whether the configuration exists or not - */ - public FuzzyWatchChangeNotifyRequest(String namespace, String group, String dataId, boolean isExist) { - this.namespace = namespace; - this.group = group; - this.dataId = dataId; - this.isExist = isExist; - } - - public String getNamespace() { - return namespace; - } - - public void setNamespace(String namespace) { - this.namespace = namespace; - } - - public String getGroup() { - return group; - } - - public void setGroup(String group) { - this.group = group; - } - - public String getDataId() { - return dataId; - } - - public void setDataId(String dataId) { - this.dataId = dataId; - } - - public boolean isExist() { - return isExist; - } - - public void setExist(boolean exist) { - isExist = exist; - } - - /** - * Returns a string representation of the FuzzyListenNotifyChangeRequest. - * - * @return A string representation of the request - */ - @Override - public String toString() { - return "FuzzyListenNotifyChangeRequest{" + "tenant='" + namespace + '\'' + ", group='" + group + '\'' - + ", dataId='" + dataId + '\'' + ", isExist=" + isExist + '}'; - } - -} diff --git a/api/src/main/java/com/alibaba/nacos/api/config/remote/response/FuzzyWatchNotifyDiffResponse.java b/api/src/main/java/com/alibaba/nacos/api/config/remote/response/ConfigFuzzyWatchChangeNotifyResponse.java similarity index 88% rename from api/src/main/java/com/alibaba/nacos/api/config/remote/response/FuzzyWatchNotifyDiffResponse.java rename to api/src/main/java/com/alibaba/nacos/api/config/remote/response/ConfigFuzzyWatchChangeNotifyResponse.java index bf332b53156..abb8e8449d7 100644 --- a/api/src/main/java/com/alibaba/nacos/api/config/remote/response/FuzzyWatchNotifyDiffResponse.java +++ b/api/src/main/java/com/alibaba/nacos/api/config/remote/response/ConfigFuzzyWatchChangeNotifyResponse.java @@ -19,11 +19,11 @@ import com.alibaba.nacos.api.remote.response.Response; /** - * FuzzyListenNotifyDiffResponse. + * FuzzyListenNotifyChangeResponse. * * @author stone-98 * @date 2024/3/18 */ -public class FuzzyWatchNotifyDiffResponse extends Response { +public class ConfigFuzzyWatchChangeNotifyResponse extends Response { } diff --git a/api/src/main/java/com/alibaba/nacos/api/config/remote/response/ConfigBatchFuzzyWatchResponse.java b/api/src/main/java/com/alibaba/nacos/api/config/remote/response/ConfigFuzzyWatchResponse.java similarity index 92% rename from api/src/main/java/com/alibaba/nacos/api/config/remote/response/ConfigBatchFuzzyWatchResponse.java rename to api/src/main/java/com/alibaba/nacos/api/config/remote/response/ConfigFuzzyWatchResponse.java index 43106d5291d..bfcf065ed81 100644 --- a/api/src/main/java/com/alibaba/nacos/api/config/remote/response/ConfigBatchFuzzyWatchResponse.java +++ b/api/src/main/java/com/alibaba/nacos/api/config/remote/response/ConfigFuzzyWatchResponse.java @@ -24,6 +24,6 @@ * @author stone-98 * @date 2024/3/4 */ -public class ConfigBatchFuzzyWatchResponse extends Response { +public class ConfigFuzzyWatchResponse extends Response { } diff --git a/api/src/main/java/com/alibaba/nacos/api/config/remote/response/FuzzyWatchChangeNotifyResponse.java b/api/src/main/java/com/alibaba/nacos/api/config/remote/response/ConfigFuzzyWatchSyncResponse.java similarity index 92% rename from api/src/main/java/com/alibaba/nacos/api/config/remote/response/FuzzyWatchChangeNotifyResponse.java rename to api/src/main/java/com/alibaba/nacos/api/config/remote/response/ConfigFuzzyWatchSyncResponse.java index e0c76bed799..9b91e010e2b 100644 --- a/api/src/main/java/com/alibaba/nacos/api/config/remote/response/FuzzyWatchChangeNotifyResponse.java +++ b/api/src/main/java/com/alibaba/nacos/api/config/remote/response/ConfigFuzzyWatchSyncResponse.java @@ -24,6 +24,6 @@ * @author stone-98 * @date 2024/3/18 */ -public class FuzzyWatchChangeNotifyResponse extends Response { +public class ConfigFuzzyWatchSyncResponse extends Response { } diff --git a/api/src/main/java/com/alibaba/nacos/api/naming/listener/FuzzyWatchChangeEvent.java b/api/src/main/java/com/alibaba/nacos/api/naming/listener/FuzzyWatchChangeEvent.java index b444be15bde..e31de48d1a6 100644 --- a/api/src/main/java/com/alibaba/nacos/api/naming/listener/FuzzyWatchChangeEvent.java +++ b/api/src/main/java/com/alibaba/nacos/api/naming/listener/FuzzyWatchChangeEvent.java @@ -31,14 +31,17 @@ public class FuzzyWatchChangeEvent implements Event { private String changeType; + private String syncType; + public FuzzyWatchChangeEvent() { } - public FuzzyWatchChangeEvent(String serviceName,String groupName,String namespace, String changeType) { + public FuzzyWatchChangeEvent(String serviceName,String groupName,String namespace, String changeType,String syncType) { this.changeType = changeType; this.serviceName=serviceName; this.groupName=groupName; this.namespace=namespace; + this.syncType=syncType; } public String getServiceName() { diff --git a/api/src/main/java/com/alibaba/nacos/api/naming/listener/FuzzyWatchEventWatcher.java b/api/src/main/java/com/alibaba/nacos/api/naming/listener/FuzzyWatchEventWatcher.java index 5489dcd7ff8..48ef69b0bad 100644 --- a/api/src/main/java/com/alibaba/nacos/api/naming/listener/FuzzyWatchEventWatcher.java +++ b/api/src/main/java/com/alibaba/nacos/api/naming/listener/FuzzyWatchEventWatcher.java @@ -31,7 +31,7 @@ public abstract class FuzzyWatchEventWatcher { String uuid= UUID.randomUUID().toString(); - private Set syncGroupKeys = new HashSet<>(); + private Set syncServiceKeys = new HashSet<>(); public Executor getExecutor() { @@ -42,8 +42,8 @@ public final String getUuid() { return uuid; } - public Set getSyncGroupKeys() { - return Collections.unmodifiableSet(syncGroupKeys); + public Set getSyncServiceKeys() { + return Collections.unmodifiableSet(syncServiceKeys); } public abstract void onEvent(FuzzyWatchChangeEvent event); diff --git a/api/src/main/java/com/alibaba/nacos/api/naming/listener/FuzzyWatchListener.java b/api/src/main/java/com/alibaba/nacos/api/naming/listener/FuzzyWatchListener.java deleted file mode 100644 index bbefb455fc2..00000000000 --- a/api/src/main/java/com/alibaba/nacos/api/naming/listener/FuzzyWatchListener.java +++ /dev/null @@ -1,41 +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.api.naming.listener; - -import java.util.UUID; -import java.util.concurrent.Executor; - -/** - * Fuzzy Watch Listener. - * - * @author tanyongquan - */ -public abstract class FuzzyWatchListener { - - - String uuid= UUID.randomUUID().toString(); - - public Executor getExecutor() { - return null; - } - - /** - * callback event. - * - * @param event event - */ -} diff --git a/api/src/main/java/com/alibaba/nacos/api/naming/pojo/Service.java b/api/src/main/java/com/alibaba/nacos/api/naming/pojo/Service.java index 3c800dd0bda..08fa6d269ca 100644 --- a/api/src/main/java/com/alibaba/nacos/api/naming/pojo/Service.java +++ b/api/src/main/java/com/alibaba/nacos/api/naming/pojo/Service.java @@ -16,12 +16,9 @@ package com.alibaba.nacos.api.naming.pojo; -import com.alibaba.nacos.api.naming.utils.NamingUtils; - import java.io.Serializable; import java.util.HashMap; import java.util.Map; -import java.util.Objects; /** * Service of Nacos. @@ -66,11 +63,6 @@ public Service(String name) { this.name = name; } - public Service(String name, String groupName) { - this.name = name; - this.groupName = groupName; - } - public String getName() { return name; } @@ -103,10 +95,6 @@ public void setGroupName(String groupName) { this.groupName = groupName; } - public String getGroupedServiceName() { - return NamingUtils.getGroupedName(name, groupName); - } - public Map getMetadata() { return metadata; } @@ -124,21 +112,4 @@ public String toString() { return "Service{" + "name='" + name + '\'' + ", protectThreshold=" + protectThreshold + ", appName='" + appName + '\'' + ", groupName='" + groupName + '\'' + ", metadata=" + metadata + '}'; } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - Service service = (Service) o; - return name.equals(service.name) && groupName.equals(service.groupName); - } - - @Override - public int hashCode() { - return Objects.hash(name, groupName); - } } diff --git a/api/src/main/java/com/alibaba/nacos/api/naming/remote/NamingRemoteConstants.java b/api/src/main/java/com/alibaba/nacos/api/naming/remote/NamingRemoteConstants.java index 36ee9f64034..4eb9cccc30c 100644 --- a/api/src/main/java/com/alibaba/nacos/api/naming/remote/NamingRemoteConstants.java +++ b/api/src/main/java/com/alibaba/nacos/api/naming/remote/NamingRemoteConstants.java @@ -30,10 +30,6 @@ public class NamingRemoteConstants { public static final String DE_REGISTER_INSTANCE = "deregisterInstance"; - public static final String FUZZY_WATCH_SERVICE = "fuzzyWatchService"; - - public static final String CANCEL_FUZZY_WATCH_SERVICE = "cancelFuzzyWatchService"; - public static final String QUERY_SERVICE = "queryService"; public static final String SUBSCRIBE_SERVICE = "subscribeService"; diff --git a/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/AbstractFuzzyWatchNotifyRequest.java b/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/AbstractFuzzyWatchNotifyRequest.java index 5ea72b78f3f..966a0dd992e 100644 --- a/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/AbstractFuzzyWatchNotifyRequest.java +++ b/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/AbstractFuzzyWatchNotifyRequest.java @@ -27,21 +27,21 @@ */ public abstract class AbstractFuzzyWatchNotifyRequest extends ServerRequest { - private String changedType; + private String syncType; public AbstractFuzzyWatchNotifyRequest(){ } - public AbstractFuzzyWatchNotifyRequest( String changedType) { - this.changedType = changedType; + public AbstractFuzzyWatchNotifyRequest( String syncType) { + this.syncType = syncType; } - public String getChangedType() { - return changedType; + public String getSyncType() { + return syncType; } - public void setChangedType(String changedType) { - this.changedType = changedType; + public void setSyncType(String syncType) { + this.syncType = syncType; } @Override diff --git a/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/FuzzyWatchNotifyInitRequest.java b/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/FuzzyWatchNotifyInitRequest.java deleted file mode 100644 index 91f66544e4a..00000000000 --- a/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/FuzzyWatchNotifyInitRequest.java +++ /dev/null @@ -1,68 +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.api.naming.remote.request; - -import com.alibaba.nacos.api.common.Constants; - -import java.util.Collection; -import java.util.HashSet; - -/** - * Nacos fuzzy watch initial notify request, use it when init a watch request, push service by batch. - * - * @author tanyongquan - */ -public class FuzzyWatchNotifyInitRequest extends AbstractFuzzyWatchNotifyRequest { - - private String pattern; - - private Collection serviceKeys; - - public FuzzyWatchNotifyInitRequest() { - } - - private FuzzyWatchNotifyInitRequest(String pattern, String changedType, Collection serviceKeys) { - super(changedType); - this.serviceKeys = serviceKeys; - this.pattern = pattern; - } - - public static FuzzyWatchNotifyInitRequest buildInitRequest(String pattern, Collection servicesName) { - return new FuzzyWatchNotifyInitRequest(pattern, Constants.FUZZY_WATCH_INIT_NOTIFY, servicesName); - } - - public static FuzzyWatchNotifyInitRequest buildInitFinishRequest(String pattern) { - return new FuzzyWatchNotifyInitRequest(pattern, Constants.FINISH_FUZZY_WATCH_INIT_NOTIFY, new HashSet<>(1)); - } - - public String getPattern() { - return pattern; - } - - public void setPattern(String pattern) { - this.pattern = pattern; - } - - public Collection getServiceKeys() { - return serviceKeys; - } - - public void setServiceKeys(Collection serviceKeys) { - this.serviceKeys = serviceKeys; - } - -} diff --git a/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/FuzzyWatchNotifyChangeRequest.java b/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/NamingFuzzyWatchChangeNotifyRequest.java similarity index 69% rename from api/src/main/java/com/alibaba/nacos/api/naming/remote/request/FuzzyWatchNotifyChangeRequest.java rename to api/src/main/java/com/alibaba/nacos/api/naming/remote/request/NamingFuzzyWatchChangeNotifyRequest.java index 6d8d5dab099..e067373782e 100644 --- a/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/FuzzyWatchNotifyChangeRequest.java +++ b/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/NamingFuzzyWatchChangeNotifyRequest.java @@ -16,23 +16,21 @@ package com.alibaba.nacos.api.naming.remote.request; -import com.alibaba.nacos.api.remote.request.ServerRequest; - -import static com.alibaba.nacos.api.common.Constants.Naming.NAMING_MODULE; - /** * Nacos fuzzy watch notify service change request, use it when one of the services changes. * * @author tanyongquan */ -public class FuzzyWatchNotifyChangeRequest extends AbstractFuzzyWatchNotifyRequest { +public class NamingFuzzyWatchChangeNotifyRequest extends AbstractFuzzyWatchNotifyRequest { private String serviceKey; + private String changedType; - public FuzzyWatchNotifyChangeRequest(String serviceKey, String serviceChangedType) { - super(serviceChangedType); + public NamingFuzzyWatchChangeNotifyRequest(String serviceKey,String changedType, String syncType) { + super(syncType); this.serviceKey=serviceKey; + this.changedType=changedType; } public String getServiceKey() { @@ -42,4 +40,12 @@ public String getServiceKey() { public void setServiceKey(String serviceKey) { this.serviceKey = serviceKey; } + + public String getChangedType() { + return changedType; + } + + public void setChangedType(String changedType) { + this.changedType = changedType; + } } 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 new file mode 100644 index 00000000000..ddbc2a354b8 --- /dev/null +++ b/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/NamingFuzzyWatchSyncRequest.java @@ -0,0 +1,145 @@ +/* + * 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.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; + +public class NamingFuzzyWatchSyncRequest extends AbstractFuzzyWatchNotifyRequest { + + /** + * The pattern used to match service keys for the services. + */ + private String groupKeyPattern; + + /** + * The set of contexts containing information about the service. + */ + private Set contexts; + + private int totalBatch; + + private int currentBatch; + + public NamingFuzzyWatchSyncRequest(String pattern, String syncType,Set contexts){ + super(syncType); + this.groupKeyPattern=pattern; + this.contexts=contexts; + } + + public int getTotalBatch() { + return totalBatch; + } + + public void setTotalBatch(int totalBatch) { + this.totalBatch = totalBatch; + } + + public int getCurrentBatch() { + return currentBatch; + } + + 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)); + } + + public static NamingFuzzyWatchSyncRequest buildSyncNotifyRequest(String pattern,String syncType,Set contexts,int totalBatch,int currentBatch) { + NamingFuzzyWatchSyncRequest namingFuzzyWatchSyncRequest =new NamingFuzzyWatchSyncRequest(pattern, syncType, contexts); + namingFuzzyWatchSyncRequest.currentBatch=currentBatch; + namingFuzzyWatchSyncRequest.totalBatch=totalBatch; + return namingFuzzyWatchSyncRequest; + } + + + public String getGroupKeyPattern() { + return groupKeyPattern; + } + + public void setGroupKeyPattern(String groupKeyPattern) { + this.groupKeyPattern = groupKeyPattern; + } + + public Set getContexts() { + return contexts; + } + + public void setContexts(Set contexts) { + this.contexts = contexts; + } + + @Override + public String getModule() { + return NAMING_MODULE; + } + + public static class Context { + + /** + * @see com.alibaba.nacos.api.naming.utils.NamingUtils#getServiceKey(String, String, String) + */ + String serviceKey; + /** + * see {@link com.alibaba.nacos.api.common.Constants.ServiceChangedType&} + * 1.ADD_SERVICE: a new config + * 2.DELETE_SERVICE: a service should be removed for clientside . + */ + private String changedType; + + /** + * Constructs an empty Context object. + */ + public Context() { + } + + /** + * Builds a new context object with the provided parameters. + * + * @param serviceKey The groupKey associated of the configuration. + * @param changedType The type of the configuration change event. + * @return A new context object initialized with the provided parameters. + */ + public static NamingFuzzyWatchSyncRequest.Context build(String serviceKey, String changedType) { + NamingFuzzyWatchSyncRequest.Context context = new NamingFuzzyWatchSyncRequest.Context(); + context.setServiceKey(serviceKey); + context.setChangedType(changedType); + return context; + } + + public String getServiceKey() { + return serviceKey; + } + + public void setServiceKey(String serviceKey) { + this.serviceKey = serviceKey; + } + + public String getChangedType() { + return changedType; + } + + public void setChangedType(String changedType) { + this.changedType = changedType; + } + } +} diff --git a/api/src/main/java/com/alibaba/nacos/api/naming/remote/response/NotifyFuzzyWatcherResponse.java b/api/src/main/java/com/alibaba/nacos/api/naming/remote/response/NamingFuzzyWatchChangeNotifyResponse.java similarity index 92% rename from api/src/main/java/com/alibaba/nacos/api/naming/remote/response/NotifyFuzzyWatcherResponse.java rename to api/src/main/java/com/alibaba/nacos/api/naming/remote/response/NamingFuzzyWatchChangeNotifyResponse.java index ac9d55298db..3fdeefe0971 100644 --- a/api/src/main/java/com/alibaba/nacos/api/naming/remote/response/NotifyFuzzyWatcherResponse.java +++ b/api/src/main/java/com/alibaba/nacos/api/naming/remote/response/NamingFuzzyWatchChangeNotifyResponse.java @@ -23,6 +23,6 @@ * * @author tanyongquan */ -public class NotifyFuzzyWatcherResponse extends Response { +public class NamingFuzzyWatchChangeNotifyResponse extends Response { } diff --git a/api/src/main/java/com/alibaba/nacos/api/naming/remote/response/NamingFuzzyWatchResponse.java b/api/src/main/java/com/alibaba/nacos/api/naming/remote/response/NamingFuzzyWatchResponse.java index 446f6bcb504..1442b3d8bcd 100644 --- a/api/src/main/java/com/alibaba/nacos/api/naming/remote/response/NamingFuzzyWatchResponse.java +++ b/api/src/main/java/com/alibaba/nacos/api/naming/remote/response/NamingFuzzyWatchResponse.java @@ -26,17 +26,12 @@ */ public class NamingFuzzyWatchResponse extends Response { - private String watchType; - public NamingFuzzyWatchResponse(){ } - public NamingFuzzyWatchResponse(String watchType) { - this.watchType = watchType; - } - - public static NamingFuzzyWatchResponse buildSuccessResponse(String watchType) { - return new NamingFuzzyWatchResponse(watchType); + + public static NamingFuzzyWatchResponse buildSuccessResponse() { + return new NamingFuzzyWatchResponse(); } /** @@ -50,12 +45,5 @@ public static NamingFuzzyWatchResponse buildFailResponse(String message) { result.setErrorInfo(ResponseCode.FAIL.getCode(), message); return result; } - - public String getWatchType() { - return watchType; - } - - public void setWatchType(String watchType) { - this.watchType = watchType; - } + } diff --git a/api/src/main/java/com/alibaba/nacos/api/naming/remote/response/NamingFuzzyWatchSyncResponse.java b/api/src/main/java/com/alibaba/nacos/api/naming/remote/response/NamingFuzzyWatchSyncResponse.java new file mode 100644 index 00000000000..dc81384b21d --- /dev/null +++ b/api/src/main/java/com/alibaba/nacos/api/naming/remote/response/NamingFuzzyWatchSyncResponse.java @@ -0,0 +1,49 @@ +/* + * 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.remote.response; + +import com.alibaba.nacos.api.remote.response.Response; +import com.alibaba.nacos.api.remote.response.ResponseCode; + +/** + * Nacos naming fuzzy watch service response. + * + * @author tanyongquan + */ +public class NamingFuzzyWatchSyncResponse extends Response { + + public NamingFuzzyWatchSyncResponse(){ + } + + + public static NamingFuzzyWatchSyncResponse buildSuccessResponse() { + return new NamingFuzzyWatchSyncResponse(); + } + + /** + * Build fail response. + * + * @param message error message + * @return fail response + */ + public static NamingFuzzyWatchSyncResponse buildFailResponse(String message) { + NamingFuzzyWatchSyncResponse result = new NamingFuzzyWatchSyncResponse(); + result.setErrorInfo(ResponseCode.FAIL.getCode(), message); + return result; + } + +} diff --git a/api/src/main/resources/META-INF/services/com.alibaba.nacos.api.remote.Payload b/api/src/main/resources/META-INF/services/com.alibaba.nacos.api.remote.Payload index 74d4cc1da22..e82f9815f25 100644 --- a/api/src/main/resources/META-INF/services/com.alibaba.nacos.api.remote.Payload +++ b/api/src/main/resources/META-INF/services/com.alibaba.nacos.api.remote.Payload @@ -45,27 +45,29 @@ com.alibaba.nacos.api.config.remote.response.ConfigQueryResponse com.alibaba.nacos.api.config.remote.response.ConfigRemoveResponse com.alibaba.nacos.api.config.remote.request.cluster.ConfigChangeClusterSyncRequest com.alibaba.nacos.api.config.remote.response.cluster.ConfigChangeClusterSyncResponse +com.alibaba.nacos.api.config.remote.request.ConfigFuzzyWatchRequest +com.alibaba.nacos.api.config.remote.response.ConfigFuzzyWatchResponse +com.alibaba.nacos.api.config.remote.request.ConfigFuzzyWatchChangeNotifyRequest +com.alibaba.nacos.api.config.remote.response.ConfigFuzzyWatchChangeNotifyResponse +com.alibaba.nacos.api.config.remote.response.ConfigFuzzyWatchSyncResponse +com.alibaba.nacos.api.config.remote.request.ConfigFuzzyWatchSyncRequest + com.alibaba.nacos.api.naming.remote.request.BatchInstanceRequest com.alibaba.nacos.api.naming.remote.request.InstanceRequest com.alibaba.nacos.api.naming.remote.request.PersistentInstanceRequest com.alibaba.nacos.api.naming.remote.request.NotifySubscriberRequest -com.alibaba.nacos.api.naming.remote.request.FuzzyWatchNotifyChangeRequest -com.alibaba.nacos.api.naming.remote.request.FuzzyWatchNotifyInitRequest com.alibaba.nacos.api.naming.remote.request.ServiceListRequest com.alibaba.nacos.api.naming.remote.request.ServiceQueryRequest com.alibaba.nacos.api.naming.remote.request.SubscribeServiceRequest -com.alibaba.nacos.api.naming.remote.request.NamingFuzzyWatchRequest com.alibaba.nacos.api.naming.remote.response.BatchInstanceResponse com.alibaba.nacos.api.naming.remote.response.InstanceResponse com.alibaba.nacos.api.naming.remote.response.NotifySubscriberResponse -com.alibaba.nacos.api.naming.remote.response.NotifyFuzzyWatcherResponse com.alibaba.nacos.api.naming.remote.response.QueryServiceResponse com.alibaba.nacos.api.naming.remote.response.ServiceListResponse com.alibaba.nacos.api.naming.remote.response.SubscribeServiceResponse +com.alibaba.nacos.api.naming.remote.request.NamingFuzzyWatchRequest com.alibaba.nacos.api.naming.remote.response.NamingFuzzyWatchResponse -com.alibaba.nacos.api.config.remote.request.ConfigBatchFuzzyWatchRequest -com.alibaba.nacos.api.config.remote.response.ConfigBatchFuzzyWatchResponse -com.alibaba.nacos.api.config.remote.request.FuzzyWatchChangeNotifyRequest -com.alibaba.nacos.api.config.remote.response.FuzzyWatchChangeNotifyResponse -com.alibaba.nacos.api.config.remote.request.FuzzyWatchDiffSyncRequest -com.alibaba.nacos.api.config.remote.response.FuzzyWatchNotifyDiffResponse +com.alibaba.nacos.api.naming.remote.request.NamingFuzzyWatchChangeNotifyRequest +com.alibaba.nacos.api.naming.remote.response.NamingFuzzyWatchChangeNotifyResponse +com.alibaba.nacos.api.naming.remote.request.NamingFuzzyWatchSyncRequest +com.alibaba.nacos.api.naming.remote.response.NamingFuzzyWatchSyncResponse diff --git a/client/src/main/java/com/alibaba/nacos/client/config/NacosConfigService.java b/client/src/main/java/com/alibaba/nacos/client/config/NacosConfigService.java index d7ea0ac1bb8..6bffb443687 100644 --- a/client/src/main/java/com/alibaba/nacos/client/config/NacosConfigService.java +++ b/client/src/main/java/com/alibaba/nacos/client/config/NacosConfigService.java @@ -42,7 +42,6 @@ import com.alibaba.nacos.common.utils.StringUtils; import org.slf4j.Logger; -import java.util.Collection; import java.util.Collections; import java.util.Properties; import java.util.Set; @@ -65,12 +64,6 @@ public class NacosConfigService implements ConfigService { private static final String DOWN = "DOWN"; - /** - * will be deleted in 2.0 later versions - */ - @Deprecated - ServerHttpAgent agent = null; - /** * long polling. */ @@ -92,8 +85,6 @@ public NacosConfigService(Properties properties) throws NacosException { serverListManager.start(); this.worker = new ClientWorker(this.configFilterChainManager, serverListManager, clientProperties); - // will be deleted in 2.0 later versions - agent = new ServerHttpAgent(serverListManager); } @@ -159,7 +150,7 @@ public Future> fuzzyWatchWithGroupKeys(String dataIdPattern, private CompletableFuture> doAddFuzzyWatch(String dataIdPattern, String fixedGroupName, ConfigFuzzyWatcher watcher) throws NacosException { - ConfigFuzzyWatchContext configFuzzyWatchContext = worker.registerFuzzyWatcher(dataIdPattern, fixedGroupName,watcher); + ConfigFuzzyWatchContext configFuzzyWatchContext = worker.addTenantFuzzyWatcher(dataIdPattern, fixedGroupName,watcher); return configFuzzyWatchContext.createNewFuture(); } @@ -175,11 +166,11 @@ public void cancelFuzzyWatch(String dataIdPattern, String fixedGroupName, Config } private void doCancelFuzzyListen(String dataIdPattern, String groupNamePattern, - ConfigFuzzyWatcher listener) throws NacosException { - if (null == listener) { + ConfigFuzzyWatcher watcher) throws NacosException { + if (null == watcher) { return; } - worker.removeFuzzyListenListener(dataIdPattern, groupNamePattern, listener); + worker.removeFuzzyListenListener(dataIdPattern, groupNamePattern, watcher); } @Override @@ -234,7 +225,7 @@ private String getConfigInner(String tenant, String dataId, String group, long t worker.getAgentName(), dataId, group, tenant); cr.setContent(content); String encryptedDataKey = LocalEncryptedDataKeyProcessor - .getEncryptDataKeyFailover(agent.getName(), dataId, group, tenant); + .getEncryptDataKeyFailover(worker.getAgentName(), dataId, group, tenant); cr.setEncryptedDataKey(encryptedDataKey); configFilterChainManager.doFilter(null, cr); content = cr.getContent(); @@ -264,7 +255,7 @@ private String getConfigInner(String tenant, String dataId, String group, long t } cr.setContent(content); String encryptedDataKey = LocalEncryptedDataKeyProcessor - .getEncryptDataKeySnapshot(agent.getName(), dataId, group, tenant); + .getEncryptDataKeySnapshot(worker.getAgentName(), dataId, group, tenant); cr.setEncryptedDataKey(encryptedDataKey); configFilterChainManager.doFilter(null, cr); content = cr.getContent(); 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 new file mode 100644 index 00000000000..69907826516 --- /dev/null +++ b/client/src/main/java/com/alibaba/nacos/client/config/impl/ClientFuzzyWatchNotifyRequestHandler.java @@ -0,0 +1,47 @@ +/* + * 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.remote.request.ConfigFuzzyWatchChangeNotifyRequest; +import com.alibaba.nacos.api.config.remote.request.ConfigFuzzyWatchSyncRequest; +import com.alibaba.nacos.api.remote.request.Request; +import com.alibaba.nacos.api.remote.response.Response; +import com.alibaba.nacos.common.remote.client.Connection; +import com.alibaba.nacos.common.remote.client.ServerRequestHandler; + +public class ClientFuzzyWatchNotifyRequestHandler implements ServerRequestHandler { + + ConfigFuzzyWatchGroupKeyHolder configFuzzyWatchGroupKeyHolder; + + public ClientFuzzyWatchNotifyRequestHandler(ConfigFuzzyWatchGroupKeyHolder configFuzzyWatchGroupKeyHolder){ + + this.configFuzzyWatchGroupKeyHolder = configFuzzyWatchGroupKeyHolder; + } + @Override + public Response requestReply(Request request, Connection connection) { + //fuzzy watch diff reconciliation sync + if (request instanceof ConfigFuzzyWatchSyncRequest) { + return configFuzzyWatchGroupKeyHolder.handleFuzzyWatchNotifyDiffRequest((ConfigFuzzyWatchSyncRequest) request + ); + } + //fuzzy watch changed notify for a single config. include config changed or config delete. + if (request instanceof ConfigFuzzyWatchChangeNotifyRequest) { + return configFuzzyWatchGroupKeyHolder.handlerFuzzyListenNotifyChangeRequest((ConfigFuzzyWatchChangeNotifyRequest) request); + } + return null; + } +} 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 f88a3e36e33..61686b235e2 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 @@ -22,23 +22,17 @@ import com.alibaba.nacos.api.config.listener.ConfigFuzzyWatcher; import com.alibaba.nacos.api.config.listener.Listener; import com.alibaba.nacos.api.config.remote.request.ClientConfigMetricRequest; -import com.alibaba.nacos.api.config.remote.request.ConfigBatchFuzzyWatchRequest; import com.alibaba.nacos.api.config.remote.request.ConfigBatchListenRequest; import com.alibaba.nacos.api.config.remote.request.ConfigChangeNotifyRequest; import com.alibaba.nacos.api.config.remote.request.ConfigPublishRequest; import com.alibaba.nacos.api.config.remote.request.ConfigQueryRequest; import com.alibaba.nacos.api.config.remote.request.ConfigRemoveRequest; -import com.alibaba.nacos.api.config.remote.request.FuzzyWatchChangeNotifyRequest; -import com.alibaba.nacos.api.config.remote.request.FuzzyWatchDiffSyncRequest; import com.alibaba.nacos.api.config.remote.response.ClientConfigMetricResponse; -import com.alibaba.nacos.api.config.remote.response.ConfigBatchFuzzyWatchResponse; import com.alibaba.nacos.api.config.remote.response.ConfigChangeBatchListenResponse; import com.alibaba.nacos.api.config.remote.response.ConfigChangeNotifyResponse; import com.alibaba.nacos.api.config.remote.response.ConfigPublishResponse; import com.alibaba.nacos.api.config.remote.response.ConfigQueryResponse; import com.alibaba.nacos.api.config.remote.response.ConfigRemoveResponse; -import com.alibaba.nacos.api.config.remote.response.FuzzyWatchChangeNotifyResponse; -import com.alibaba.nacos.api.config.remote.response.FuzzyWatchNotifyDiffResponse; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.remote.RemoteConstants; import com.alibaba.nacos.api.remote.request.Request; @@ -72,7 +66,6 @@ import com.alibaba.nacos.common.remote.client.ServerListFactory; import com.alibaba.nacos.common.utils.ConnLabelsUtils; import com.alibaba.nacos.common.utils.ConvertUtils; -import com.alibaba.nacos.common.utils.FuzzyGroupKeyPattern; import com.alibaba.nacos.common.utils.JacksonUtils; import com.alibaba.nacos.common.utils.MD5Utils; import com.alibaba.nacos.common.utils.StringUtils; @@ -97,8 +90,6 @@ import java.util.UUID; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; @@ -112,7 +103,6 @@ import static com.alibaba.nacos.api.common.Constants.APP_CONN_PREFIX; import static com.alibaba.nacos.api.common.Constants.ENCODE; -import static com.alibaba.nacos.api.common.Constants.FUZZY_WATCH_INIT_NOTIFY; /** * Long polling. @@ -140,13 +130,10 @@ public class ClientWorker implements Closeable { */ private final AtomicReference> cacheMap = new AtomicReference<>(new HashMap<>()); - /** - * fuzzyListenGroupKey -> fuzzyListenContext. - */ - private final AtomicReference> fuzzyListenContextMap = new AtomicReference<>( - new HashMap<>()); private final DefaultLabelsCollectorManager defaultLabelsCollectorManager = new DefaultLabelsCollectorManager(); + private ConfigFuzzyWatchGroupKeyHolder configFuzzyWatchGroupKeyHolder; + private Map appLables = new HashMap<>(); private final ConfigFilterChainManager configFilterChainManager; @@ -172,48 +159,6 @@ public class ClientWorker implements Closeable { */ private final List taskIdCacheCountList = new ArrayList<>(); - /** - * index(taskId)-> total context count for this taskId. - */ - private final List taskIdContextCountList = new ArrayList<>(); - - @SuppressWarnings("PMD.ThreadPoolCreationRule") - public ClientWorker(final ConfigFilterChainManager configFilterChainManager, ConfigServerListManager serverListManager, - final NacosClientProperties properties) throws NacosException { - this.configFilterChainManager = configFilterChainManager; - - init(properties); - - agent = new ConfigRpcTransportClient(properties, serverListManager); - ScheduledExecutorService executorService = Executors.newScheduledThreadPool(initWorkerThreadCount(properties), - new NameThreadFactory("com.alibaba.nacos.client.Worker")); - agent.setExecutor(executorService); - agent.start(); - - } - - /** - * Adds a list of fuzzy listen listeners for the specified data ID pattern and group. - * - * @param dataIdPattern The pattern of the data ID to listen for. - * @param groupPattern The group of the configuration. - * @param listeners The list of listeners to add. - * @throws NacosException If an error occurs while adding the listeners. - */ - public void addTenantFuzzyWatcher(String dataIdPattern, String groupPattern, - List listeners) throws NacosException { - ConfigFuzzyWatchContext context = initFuzzyWatchContextIfAbsent(dataIdPattern, groupPattern); - synchronized (context) { - for (ConfigFuzzyWatcher listener : listeners) { - context.addWatcher(listener); - } - context.setInitializing(true); - context.setDiscard(false); - context.setConsistentWithServer(false); - agent.notifyFuzzyListenConfig(); - } - } - /** * Add listeners for data. * @@ -344,45 +289,44 @@ public void removeTenantListener(String dataId, String group, Listener listener) } } + /** + * Adds a list of fuzzy listen listeners for the specified data ID pattern and group. + * + * @param dataIdPattern The pattern of the data ID to listen for. + * @param groupPattern The group of the configuration. + * @param configFuzzyWatcher The configFuzzyWatcher to add. + * @throws NacosException If an error occurs while adding the listeners. + */ + public ConfigFuzzyWatchContext addTenantFuzzyWatcher(String dataIdPattern, String groupPattern, + ConfigFuzzyWatcher configFuzzyWatcher){ + return configFuzzyWatchGroupKeyHolder.registerFuzzyWatcher(dataIdPattern,groupPattern,configFuzzyWatcher); + } + /** * Removes a fuzzy listen listener for the specified data ID pattern, group, and listener. * * @param dataIdPattern The pattern of the data ID. * @param group The group of the configuration. - * @param listener The listener to remove. + * @param watcher The listener to remove. * @throws NacosException If an error occurs while removing the listener. */ - public void removeFuzzyListenListener(String dataIdPattern, String group, ConfigFuzzyWatcher listener) - throws NacosException { - group = blank2defaultGroup(group); - ConfigFuzzyWatchContext configFuzzyWatchContext = getFuzzyListenContext(dataIdPattern, group); - if (configFuzzyWatchContext != null) { - synchronized (configFuzzyWatchContext) { - configFuzzyWatchContext.removeWatcher(listener); - if (configFuzzyWatchContext.getConfigFuzzyWatchers().isEmpty()) { - configFuzzyWatchContext.setDiscard(true); - configFuzzyWatchContext.setConsistentWithServer(false); - agent.removeFuzzyListenContext(dataIdPattern, group); - } - } - } + public void removeFuzzyListenListener(String dataIdPattern, String group, ConfigFuzzyWatcher watcher) { + configFuzzyWatchGroupKeyHolder.removeFuzzyWatcher(dataIdPattern,group,watcher); } - /** - * Removes the fuzzy listen context for the specified data ID pattern and group. - * - * @param groupKeyPattern The pattern of the data ID. - */ - public void removeFuzzyListenContext(String groupKeyPattern) { - synchronized (fuzzyListenContextMap) { - Map copy = new HashMap<>(fuzzyListenContextMap.get()); - ConfigFuzzyWatchContext removedContext = copy.remove(groupKeyPattern); - if (removedContext != null) { - decreaseContextTaskIdCount(removedContext.getTaskId()); + void removeCache(String dataId, String group, String tenant) { + String groupKey = GroupKey.getKeyTenant(dataId, group, tenant); + synchronized (cacheMap) { + Map copy = new HashMap<>(cacheMap.get()); + CacheData remove = copy.remove(groupKey); + if (remove != null) { + decreaseTaskIdCount(remove.getTaskId()); } - fuzzyListenContextMap.set(copy); + cacheMap.set(copy); } - LOGGER.info("[{}] [fuzzy-listen-unsubscribe] {}", agent.getName(), groupKeyPattern); + LOGGER.info("[{}] [unsubscribe] {}", agent.getName(), groupKey); + + MetricsMonitor.getListenConfigCountMonitor().set(cacheMap.get().size()); } /** @@ -509,28 +453,6 @@ public CacheData addCacheDataIfAbsent(String dataId, String group, String tenant return cache; } - /** - * Removes the cache entry associated with the given data ID, group, and tenant. - * - * @param dataId The data ID. - * @param group The group name. - * @param tenant The tenant. - */ - public void removeCache(String dataId, String group, String tenant) { - String groupKey = GroupKey.getKeyTenant(dataId, group, tenant); - synchronized (cacheMap) { - Map copy = new HashMap<>(cacheMap.get()); - CacheData remove = copy.remove(groupKey); - if (remove != null) { - decreaseTaskIdCount(remove.getTaskId()); - } - cacheMap.set(copy); - } - LOGGER.info("[{}] [unsubscribe] {}", agent.getName(), groupKey); - - MetricsMonitor.getListenConfigCountMonitor().set(cacheMap.get().size()); - } - /** * Put cache. * @@ -545,147 +467,23 @@ private void putCache(String key, CacheData cache) { } } - public ConfigFuzzyWatchContext registerFuzzyWatcher(String dataIdPattern, String groupPattern,ConfigFuzzyWatcher configFuzzyWatcher){ - ConfigFuzzyWatchContext configFuzzyWatchContext = initFuzzyWatchContextIfAbsent(dataIdPattern, groupPattern); - configFuzzyWatchContext.addWatcher(configFuzzyWatcher); - agent.notifyFuzzyListenConfig(); - if(configFuzzyWatchContext.getReceivedGroupKeys()!=null){ - for(String groupKey:configFuzzyWatchContext.getReceivedGroupKeys()){ - String[] groupKeyItems = GroupKey.parseKey(groupKey); - FuzzyWatchNotifyEvent fuzzyWatchNotifyEvent = FuzzyWatchNotifyEvent.buildNotifyPatternAllListenersEvent( - groupKeyItems[2], groupKeyItems[1], groupKeyItems[0], - configFuzzyWatchContext.getGroupKeyPattern(), FUZZY_WATCH_INIT_NOTIFY); - fuzzyWatchNotifyEvent.setUuid(configFuzzyWatcher.getUuid()); - NotifyCenter.publishEvent(fuzzyWatchNotifyEvent); - } - - } - - return configFuzzyWatchContext; - } - - public CompletableFuture> createNewFuture(ConfigFuzzyWatchContext configFuzzyWatchContext){ - CompletableFuture> completableFuture=new CompletableFuture>(){ - @Override - public boolean isDone() { - return configFuzzyWatchContext.initializationCompleted.get(); - } - - @Override - public Set get() throws InterruptedException, ExecutionException { - return new HashSet<>(configFuzzyWatchContext.getReceivedGroupKeys()); - } - }; - return completableFuture; - } - - /** - * Adds a fuzzy listen context if it doesn't already exist for the specified data ID pattern and group. If the - * context already exists, returns the existing context. - * - * @param dataIdPattern The pattern of the data ID. - * @param groupPattern The group of the configuration. - * @return The fuzzy listen context for the specified data ID pattern and group. - */ - private ConfigFuzzyWatchContext initFuzzyWatchContextIfAbsent(String dataIdPattern, String groupPattern) { - ConfigFuzzyWatchContext context = getFuzzyListenContext(dataIdPattern, groupPattern); - if (context != null) { - return context; - } - synchronized (fuzzyListenContextMap) { - ConfigFuzzyWatchContext contextFromMap = getFuzzyListenContext(dataIdPattern, groupPattern); - if (contextFromMap != null) { - context = contextFromMap; - } else { - context = new ConfigFuzzyWatchContext(agent.getName(),FuzzyGroupKeyPattern.generatePattern(dataIdPattern, groupPattern,agent.getTenant())); - int taskId = calculateContextTaskId(); - increaseContextTaskIdCount(taskId); - context.setTaskId(taskId); - context.setConsistentWithServer(false); - - } - } - - Map copy = new HashMap<>(fuzzyListenContextMap.get()); - String groupKeyPattern = FuzzyGroupKeyPattern.generatePattern(dataIdPattern, groupPattern,agent.getTenant()); - copy.put(groupKeyPattern, context); - fuzzyListenContextMap.set(copy); - - return context; - } - - /** - * Increases the count for the specified task ID in the given count list. - * - * @param taskId The ID of the task for which the count needs to be increased. - */ private void increaseTaskIdCount(int taskId) { - increaseCount(taskId, taskIdCacheCountList); + taskIdCacheCountList.get(taskId).incrementAndGet(); } - /** - * Decreases the count for the specified task ID in the given count list. - * - * @param taskId The ID of the task for which the count needs to be decreased. - */ private void decreaseTaskIdCount(int taskId) { - decreaseCount(taskId, taskIdCacheCountList); - } - - /** - * Increases the context task ID count in the corresponding list. - * - * @param taskId The ID of the context task for which the count needs to be increased. - */ - private void increaseContextTaskIdCount(int taskId) { - increaseCount(taskId, taskIdContextCountList); - } - - /** - * Decreases the context task ID count in the corresponding list. - * - * @param taskId The ID of the context task for which the count needs to be decreased. - */ - private void decreaseContextTaskIdCount(int taskId) { - decreaseCount(taskId, taskIdContextCountList); + taskIdCacheCountList.get(taskId).decrementAndGet(); } - /** - * Calculates the task ID based on the configuration size. - * - * @return The calculated task ID. - */ private int calculateTaskId() { - return calculateId(taskIdCacheCountList, (long) ParamUtil.getPerTaskConfigSize()); - } - - /** - * Calculates the context task ID based on the configuration size. - * - * @return The calculated context task ID. - */ - private int calculateContextTaskId() { - return calculateId(taskIdContextCountList, (long) ParamUtil.getPerTaskContextSize()); - } - - /** - * Increases the count for the specified task ID in the given count list. - * - * @param taskId The ID of the task for which the count needs to be increased. - * @param countList The list containing the counts for different task IDs. - */ - private void increaseCount(int taskId, List countList) { - countList.get(taskId).incrementAndGet(); - } - - /** - * Decreases the count for the specified task ID in the given count list. - * - * @param taskId The ID of the task for which the count needs to be decreased. - * @param countList The list containing the counts for different task IDs. - */ - private void decreaseCount(int taskId, List countList) { - countList.get(taskId).decrementAndGet(); + int perTaskSize = (int) ParamUtil.getPerTaskConfigSize(); + for (int index = 0; index < taskIdCacheCountList.size(); index++) { + if (taskIdCacheCountList.get(index).get() < perTaskSize) { + return index; + } + } + taskIdCacheCountList.add(new AtomicInteger(0)); + return taskIdCacheCountList.size() - 1; } public CacheData getCache(String dataId, String group) { @@ -699,35 +497,6 @@ public CacheData getCache(String dataId, String group, String tenant) { return cacheMap.get().get(GroupKey.getKeyTenant(dataId, group, tenant)); } - /** - * Calculates the task ID based on the provided count list and per-task size. - * - * @param countList The list containing the counts for different task IDs. - * @param perTaskSize The size of each task. - * @return The calculated task ID. - */ - private int calculateId(List countList, long perTaskSize) { - for (int index = 0; index < countList.size(); index++) { - if (countList.get(index).get() < perTaskSize) { - return index; - } - } - countList.add(new AtomicInteger(0)); - return countList.size() - 1; - } - - /** - * Retrieves the FuzzyListenContext for the given data ID pattern and group. - * - * @param dataIdPattern The data ID pattern. - * @param group The group name. - * @return The corresponding FuzzyListenContext, or null if not found. - */ - public ConfigFuzzyWatchContext getFuzzyListenContext(String dataIdPattern, String group) { - return fuzzyListenContextMap.get() - .get(FuzzyGroupKeyPattern.generatePattern(dataIdPattern, group,agent.getTenant())); - } - public ConfigResponse getServerConfig(String dataId, String group, String tenant, long readTimeout, boolean notify) throws NacosException { if (StringUtils.isBlank(group)) { @@ -740,19 +509,22 @@ private String blank2defaultGroup(String group) { return StringUtils.isBlank(group) ? Constants.DEFAULT_GROUP : group.trim(); } - /** - * Checks if the pattern match cache contains an entry for the specified data ID pattern and group. - * - * @param dataIdPattern The data ID pattern. - * @param groupPattern The group name. - * @return True if the cache contains an entry, false otherwise. - */ - public boolean containsPatternMatchCache(String dataIdPattern, String groupPattern) { - Map contextMap = fuzzyListenContextMap.get(); - String groupKeyPattern = FuzzyGroupKeyPattern.generatePattern(dataIdPattern, groupPattern,agent.getTenant()); - return contextMap.containsKey(groupKeyPattern); + @SuppressWarnings("PMD.ThreadPoolCreationRule") + public ClientWorker(final ConfigFilterChainManager configFilterChainManager, ConfigServerListManager serverListManager, + final NacosClientProperties properties) throws NacosException { + this.configFilterChainManager = configFilterChainManager; + + init(properties); + + agent = new ConfigRpcTransportClient(properties, serverListManager); + + configFuzzyWatchGroupKeyHolder =new ConfigFuzzyWatchGroupKeyHolder(agent); + ScheduledExecutorService executorService = Executors.newScheduledThreadPool(initWorkerThreadCount(properties), + new NameThreadFactory("com.alibaba.nacos.client.Worker")); + agent.setExecutor(executorService); + agent.start(); + } - void initAppLabels(Properties properties) { this.appLables = ConnLabelsUtils.addPrefixForEachKey(defaultLabelsCollectorManager.getLabels(properties), @@ -845,28 +617,14 @@ public boolean isHealthServer() { public class ConfigRpcTransportClient extends ConfigTransportClient { - /** - * 5 minutes to check all fuzzy listen context. - */ - private static final long FUZZY_LISTEN_ALL_SYNC_INTERNAL = 5 * 60 * 1000L; - - private final String configListenerTaskPrefix = "nacos.client.config.listener.task"; - - private final String fuzzyListenerTaskPrefix = "nacos.client.config.fuzzyListener.task"; + Map multiTaskExecutor = new HashMap<>(); private final BlockingQueue listenExecutebell = new ArrayBlockingQueue<>(1); - private final Map multiTaskExecutor = new HashMap<>(); - private final Object bellItem = new Object(); private long lastAllSyncTime = System.currentTimeMillis(); - /** - * fuzzyListenExecuteBell. - */ - private final BlockingQueue fuzzyListenExecuteBell = new ArrayBlockingQueue<>(1); - Subscriber subscriber = null; /** @@ -874,11 +632,6 @@ public class ConfigRpcTransportClient extends ConfigTransportClient { */ private static final long ALL_SYNC_INTERNAL = 3 * 60 * 1000L; - /** - * fuzzyListenLastAllSyncTime. - */ - private long fuzzyListenLastAllSyncTime = System.currentTimeMillis(); - public ConfigRpcTransportClient(NacosClientProperties properties, ConfigServerListManager serverListManager) { super(properties, serverListManager); } @@ -942,89 +695,6 @@ private Map getLabels() { return labels; } - /** - * Handles a fuzzy listen init notify request. - * - *

This method processes the incoming fuzzy listen init notify request from a client. It updates the fuzzy - * listen context based on the request's information, and publishes events if necessary. - * - * @param request The fuzzy listen init notify request to handle. - * @param clientName The name of the client sending the request. - * @return A {@link FuzzyWatchNotifyDiffResponse} indicating the result of handling the request. - */ - private FuzzyWatchNotifyDiffResponse handleFuzzyWatchNotifyDiffRequest(FuzzyWatchDiffSyncRequest request, - String clientName) { - LOGGER.info("[{}] [config-fuzzy-watch-diff-sync-push] sync group key pattern ->{}.", clientName,request.getGroupKeyPattern()); - String groupKeyPattern = request.getGroupKeyPattern(); - ConfigFuzzyWatchContext context = fuzzyListenContextMap.get().get(groupKeyPattern); - if (Constants.FINISH_FUZZY_WATCH_INIT_NOTIFY.equals(request.getSyncType())) { - context.markInitializationComplete(); - return new FuzzyWatchNotifyDiffResponse(); - } - for (FuzzyWatchDiffSyncRequest.Context requestContext : request.getContexts()) { - Set matchedGroupKeys = context.getReceivedGroupKeys(); - switch (requestContext.getChangedType()) { - case FUZZY_WATCH_INIT_NOTIFY: - case Constants.ConfigChangedType.ADD_CONFIG: - if (matchedGroupKeys.add(GroupKey.getKeyTenant(requestContext.getDataId(),requestContext.getGroup(),requestContext.getTenant()))) { - LOGGER.info("[{}] [config-fuzzy-watch-diff-sync-push] sync group key ->{},changed type->{}", clientName,request.getGroupKeyPattern(),requestContext.getChangedType()); - - NotifyCenter.publishEvent(FuzzyWatchNotifyEvent.buildNotifyPatternAllListenersEvent(requestContext.getTenant(), - requestContext.getGroup(), requestContext.getDataId(), request.getGroupKeyPattern(), - requestContext.getChangedType())); - } - break; - case Constants.ConfigChangedType.DELETE_CONFIG: - if (matchedGroupKeys.remove(GroupKey.getKeyTenant(requestContext.getDataId(),requestContext.getGroup(),requestContext.getTenant()))) { - LOGGER.info("[{}] [config-fuzzy-watch-diff-sync-push] sync group key ->{},changed type->{}", clientName,request.getGroupKeyPattern(),requestContext.getChangedType()); - - NotifyCenter.publishEvent(FuzzyWatchNotifyEvent.buildNotifyPatternAllListenersEvent(requestContext.getTenant(), - requestContext.getGroup(), requestContext.getDataId(), request.getGroupKeyPattern(), - requestContext.getChangedType())); - } - break; - default: - LOGGER.error("Invalid config change type: {}", requestContext.getChangedType()); - break; - } - } - return new FuzzyWatchNotifyDiffResponse(); - } - - /** - * Handles a fuzzy listen notify change request. - * - *

This method processes the incoming fuzzy listen notify change request from a client. It updates the fuzzy - * listen context based on the request's information, and publishes events if necessary. - * - * @param request The fuzzy listen notify change request to handle. - * @param clientName The name of the client sending the request. - */ - private FuzzyWatchChangeNotifyResponse handlerFuzzyListenNotifyChangeRequest( - FuzzyWatchChangeNotifyRequest request, String clientName) { - LOGGER.info("[{}] [fuzzy-watch-config-push] fuzzy watched config changed,groupKey->{},isDeleted->{} ", clientName,GroupKey.getKeyTenant(request.getDataId(),request.getGroup(),request.getNamespace()),!request.isExist()); - Map listenContextMap = fuzzyListenContextMap.get(); - Set matchedPatterns = FuzzyGroupKeyPattern.filterMatchedPatterns(listenContextMap.keySet(),request.getDataId(), - request.getGroup(), request.getNamespace()); - for (String matchedPattern : matchedPatterns) { - ConfigFuzzyWatchContext context = listenContextMap.get(matchedPattern); - if (request.isExist()) { - if (context.getReceivedGroupKeys().add(GroupKey.getKeyTenant(request.getDataId(),request.getGroup(),request.getNamespace()))) { - NotifyCenter.publishEvent( - FuzzyWatchNotifyEvent.buildNotifyPatternAllListenersEvent(request.getNamespace(),request.getGroup(), - request.getDataId(), matchedPattern, Constants.ConfigChangedType.ADD_CONFIG)); - } - } else { - if (context.getReceivedGroupKeys().remove(GroupKey.getKeyTenant(request.getDataId(),request.getGroup(),request.getNamespace()))) { - NotifyCenter.publishEvent( - FuzzyWatchNotifyEvent.buildNotifyPatternAllListenersEvent(request.getNamespace(),request.getGroup(), - request.getDataId(), matchedPattern, Constants.ConfigChangedType.DELETE_CONFIG)); - } - } - } - return new FuzzyWatchChangeNotifyResponse(); - } - ConfigChangeNotifyResponse handleConfigChangeNotifyRequest(ConfigChangeNotifyRequest configChangeNotifyRequest, String clientName) { LOGGER.info("[{}] [server-push] config changed. dataId={}, group={},tenant={}", clientName, @@ -1061,16 +731,6 @@ private void initRpcClientHandler(final RpcClient rpcClientInner) { return handleConfigChangeNotifyRequest((ConfigChangeNotifyRequest) request, rpcClientInner.getName()); } - //fuzzy watch diff reconciliation sync - if (request instanceof FuzzyWatchDiffSyncRequest) { - return handleFuzzyWatchNotifyDiffRequest((FuzzyWatchDiffSyncRequest) request, - rpcClientInner.getName()); - } - //fuzzy watch changed notify for a single config. include config changed or config delete. - if (request instanceof FuzzyWatchChangeNotifyRequest) { - return handlerFuzzyListenNotifyChangeRequest((FuzzyWatchChangeNotifyRequest) request, - rpcClientInner.getName()); - } return null; }); @@ -1081,6 +741,9 @@ private void initRpcClientHandler(final RpcClient rpcClientInner) { return null; }); + rpcClientInner.registerServerRequestHandler(new ClientFuzzyWatchNotifyRequestHandler( + configFuzzyWatchGroupKeyHolder)); + rpcClientInner.registerConnectionListener(new ConnectionEventListener() { @Override @@ -1089,13 +752,13 @@ public void onConnected(Connection connection) { notifyListenConfig(); LOGGER.info("[{}] Connected,notify fuzzy listen context...", rpcClientInner.getName()); - notifyFuzzyListenConfig(); + configFuzzyWatchGroupKeyHolder.notifyFuzzyWatchSync(); } @Override public void onDisConnect(Connection connection) { String taskId = rpcClientInner.getLabels().get("taskId"); - LOGGER.info("[{}] DisConnected,clear listen context...", rpcClientInner.getName()); + LOGGER.info("[{}] DisConnected,reset listen context", rpcClientInner.getName()); Collection values = cacheMap.get().values(); for (CacheData cacheData : values) { @@ -1107,18 +770,9 @@ public void onDisConnect(Connection connection) { cacheData.setConsistentWithServer(false); } } - - Collection configFuzzyWatchContexts = fuzzyListenContextMap.get().values(); - - for (ConfigFuzzyWatchContext context : configFuzzyWatchContexts) { - if (StringUtils.isNotBlank(taskId)) { - if (Integer.valueOf(taskId).equals(context.getTaskId())) { - context.setConsistentWithServer(false); - } - } else { - context.setConsistentWithServer(false); - } - } + + LOGGER.info("[{}] DisConnected,reset fuzzy watch consistence status", rpcClientInner.getName()); + configFuzzyWatchGroupKeyHolder.resetConsistenceStatus(); } }); @@ -1155,25 +809,6 @@ public Class subscribeType() { } }; NotifyCenter.registerSubscriber(subscriber); - - NotifyCenter.registerSubscriber(new Subscriber() { - @Override - public void onEvent(Event event) { - FuzzyWatchNotifyEvent fuzzyWatchNotifyEvent = (FuzzyWatchNotifyEvent) event; - ConfigFuzzyWatchContext context = fuzzyListenContextMap.get() - .get(fuzzyWatchNotifyEvent.getGroupKeyPattern()); - if (context == null) { - return; - } - context.notifyListener(fuzzyWatchNotifyEvent.getDataId(), fuzzyWatchNotifyEvent.getGroup(),agent.getTenant(), fuzzyWatchNotifyEvent.getType(), - fuzzyWatchNotifyEvent.getUuid()); - } - - @Override - public Class subscribeType() { - return FuzzyWatchNotifyEvent.class; - } - }); } @Override @@ -1198,26 +833,6 @@ public void startInternal() { } }, 0L, TimeUnit.MILLISECONDS); - executor.schedule(() -> { - while (!executor.isShutdown() && !executor.isTerminated()) { - try { - fuzzyListenExecuteBell.poll(5L, TimeUnit.SECONDS); - if (executor.isShutdown() || executor.isTerminated()) { - continue; - } - executeConfigFuzzyListen(); - } catch (Throwable e) { - LOGGER.error("[rpc-fuzzy-listen-execute] rpc fuzzy listen exception", e); - try { - Thread.sleep(50L); - } catch (InterruptedException interruptedException) { - //ignore - } - notifyFuzzyListenConfig(); - } - } - }, 0L, TimeUnit.MILLISECONDS); - } @Override @@ -1230,11 +845,6 @@ public void notifyListenConfig() { listenExecutebell.offer(bellItem); } - @Override - public void notifyFuzzyListenConfig() { - fuzzyListenExecuteBell.offer(bellItem); - } - @Override public void executeConfigListen() throws NacosException { @@ -1290,119 +900,6 @@ public void executeConfigListen() throws NacosException { } - /** - * Execute fuzzy listen configuration changes. - * - *

This method iterates through all fuzzy listen contexts and determines whether they need to be added or - * removed based on their consistency with the server and discard status. It then calls the appropriate method - * to execute the fuzzy listen operation. - * - * @throws NacosException If an error occurs during the execution of fuzzy listen configuration changes. - */ - @Override - public void executeConfigFuzzyListen() throws NacosException { - Map> needSyncContextMap = new HashMap<>(16); - - // Obtain the current timestamp - long now = System.currentTimeMillis(); - - // Determine whether a full synchronization is needed - boolean needAllSync = now - fuzzyListenLastAllSyncTime >= FUZZY_LISTEN_ALL_SYNC_INTERNAL; - - // Iterate through all fuzzy listen contexts - for (ConfigFuzzyWatchContext context : fuzzyListenContextMap.get().values()) { - // Check if the context is consistent with the server - if (context.isConsistentWithServer()) { - // Skip if a full synchronization is not needed - if (!needAllSync) { - continue; - }else{ - context.syncFuzzyWatchers(); - } - } - - List needSyncContexts = needSyncContextMap.computeIfAbsent( - String.valueOf(context.getTaskId()), k -> new LinkedList<>()); - needSyncContexts.add(context); - } - - // Execute fuzzy listen operation for addition - doExecuteConfigFuzzyListen(needSyncContextMap); - - // Update last all sync time if a full synchronization was performed - if (needAllSync) { - fuzzyListenLastAllSyncTime = now; - } - } - - /** - * Execute fuzzy listen configuration changes for a specific map of contexts. - * - *

This method submits tasks to execute fuzzy listen operations asynchronously for the provided contexts. It - * waits for all tasks to complete and logs any errors that occur. - * - * @param contextMap The map of contexts to execute fuzzy listen operations for. - * @throws NacosException If an error occurs during the execution of fuzzy listen configuration changes. - */ - private void doExecuteConfigFuzzyListen(Map> contextMap) - throws NacosException { - // Return if the context map is null or empty - if (contextMap == null || contextMap.isEmpty()) { - return; - } - - // List to hold futures for asynchronous tasks - List> listenFutures = new ArrayList<>(); - - // Iterate through the context map and submit tasks for execution - for (Map.Entry> entry : contextMap.entrySet()) { - String taskId = entry.getKey(); - List contexts = entry.getValue(); - RpcClient rpcClient = ensureRpcClient(taskId); - ExecutorService executorService = ensureSyncExecutor(fuzzyListenerTaskPrefix, taskId); - // Submit task for execution - Future future = executorService.submit(() -> { - ConfigBatchFuzzyWatchRequest configBatchFuzzyWatchRequest = buildFuzzyListenConfigRequest( - contexts); - try { - // Execute the fuzzy listen operation - ConfigBatchFuzzyWatchResponse listenResponse = (ConfigBatchFuzzyWatchResponse) requestProxy( - rpcClient, configBatchFuzzyWatchRequest); - if (listenResponse != null && listenResponse.isSuccess()) { - for (ConfigFuzzyWatchContext context : contexts) { - if (context.isDiscard()) { - ClientWorker.this.removeFuzzyListenContext(context.getGroupKeyPattern()); - } else { - context.setConsistentWithServer(true); - } - } - } - } 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 - } - // Retry notification - notifyFuzzyListenConfig(); - } - }); - listenFutures.add(future); - } - - // Wait for all tasks to complete - for (Future future : listenFutures) { - try { - future.get(); - } catch (Throwable throwable) { - // Log async listen error - LOGGER.error("Async fuzzy listen config change error.", throwable); - } - } - } - /** * Checks and handles local configuration for a given CacheData object. This method evaluates the use of * failover files for local configuration storage and updates the CacheData accordingly. @@ -1451,41 +948,16 @@ public void checkLocalConfig(CacheData cacheData) { } } - /** - * Ensure to create a synchronous executor for the given task prefix and task ID. If an executor for the given - * task doesn't exist yet, a new executor will be created. - * - * @param taskPrefix The prefix of the task identifier - * @param taskId The ID of the task - * @return The created or existing executor - */ - private ExecutorService ensureSyncExecutor(String taskPrefix, String taskId) { - // Generate the unique task identifier - String taskIdentifier = generateTaskIdentifier(taskPrefix, taskId); - - // If the task identifier doesn't exist in the existing executors, create a new executor and add it to the multiTaskExecutor map - if (!multiTaskExecutor.containsKey(taskIdentifier)) { - multiTaskExecutor.put(taskIdentifier, + private ExecutorService ensureSyncExecutor(String taskId) { + if (!multiTaskExecutor.containsKey(taskId)) { + multiTaskExecutor.put(taskId, new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(), r -> { - Thread thread = new Thread(r, taskIdentifier); + Thread thread = new Thread(r, "nacos.client.config.listener.task-" + taskId); thread.setDaemon(true); return thread; })); } - - // Return the created or existing executor - return multiTaskExecutor.get(taskIdentifier); - } - - /** - * Generate a task identifier based on the task prefix and task ID. - * - * @param taskPrefix The prefix of the task identifier - * @param taskId The ID of the task - * @return The generated task identifier - */ - private String generateTaskIdentifier(String taskPrefix, String taskId) { - return taskPrefix + "-" + taskId; + return multiTaskExecutor.get(taskId); } private void refreshContentAndCheck(RpcClient rpcClient, String groupKey, boolean notify) { @@ -1525,7 +997,7 @@ private void checkRemoveListenCache(Map> removeListenCac String taskId = entry.getKey(); RpcClient rpcClient = ensureRpcClient(taskId); - ExecutorService executorService = ensureSyncExecutor(configListenerTaskPrefix, taskId); + ExecutorService executorService = ensureSyncExecutor(taskId); Future future = executorService.submit(() -> { List removeListenCaches = entry.getValue(); ConfigBatchListenRequest configChangeListenRequest = buildConfigRequest(removeListenCaches); @@ -1575,7 +1047,7 @@ private boolean checkListenCache(Map> listenCachesMap) t String taskId = entry.getKey(); RpcClient rpcClient = ensureRpcClient(taskId); - ExecutorService executorService = ensureSyncExecutor(configListenerTaskPrefix, taskId); + ExecutorService executorService = ensureSyncExecutor(taskId); Future future = executorService.submit(() -> { List listenCaches = entry.getValue(); //reset notify change flag. @@ -1656,7 +1128,7 @@ private boolean checkListenCache(Map> listenCachesMap) t return hasChangedKeys.get(); } - private RpcClient ensureRpcClient(String taskId) throws NacosException { + RpcClient ensureRpcClient(String taskId) throws NacosException { synchronized (ClientWorker.this) { Map labels = getLabels(); Map newLabels = new HashMap<>(labels); @@ -1692,33 +1164,12 @@ private ConfigBatchListenRequest buildConfigRequest(List caches) { return configChangeListenRequest; } - /** - * Builds a request for fuzzy listen configuration. - * - * @param contexts The list of fuzzy listen contexts. - * @return A {@code ConfigBatchFuzzyListenRequest} object representing the request. - */ - private ConfigBatchFuzzyWatchRequest buildFuzzyListenConfigRequest(List contexts) { - ConfigBatchFuzzyWatchRequest request = new ConfigBatchFuzzyWatchRequest(); - for (ConfigFuzzyWatchContext context : contexts) { - request.addContext(context.getGroupKeyPattern(), context.getReceivedGroupKeys(), - !context.isDiscard(), context.isInitializing()); - } - return request; - } - @Override public void removeCache(String dataId, String group) { // Notify to rpc un listen ,and remove cache if success. notifyListenConfig(); } - @Override - public void removeFuzzyListenContext(String dataIdPattern, String group) throws NacosException { - // Notify to rpc un fuzzy listen, and remove cache if success. - notifyFuzzyListenConfig(); - } - /** * send cancel listen config change request . * @@ -1790,7 +1241,7 @@ ConfigResponse queryConfigInner(RpcClient rpcClient, String dataId, String group } } - private Response requestProxy(RpcClient rpcClientInner, Request request) throws NacosException { + Response requestProxy(RpcClient rpcClientInner, Request request) throws NacosException { return requestProxy(rpcClientInner, request, requestTimeout); } 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 67ad9d41bb7..6305b3f27f3 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 @@ -19,11 +19,10 @@ import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.config.listener.ConfigFuzzyWatchChangeEvent; import com.alibaba.nacos.api.config.listener.ConfigFuzzyWatcher; +import com.alibaba.nacos.client.config.common.GroupKey; import com.alibaba.nacos.client.utils.LogUtils; -import com.alibaba.nacos.common.notify.NotifyCenter; import com.alibaba.nacos.common.utils.ConcurrentHashSet; import com.alibaba.nacos.common.utils.FuzzyGroupKeyPattern; -import com.alibaba.nacos.common.utils.GroupKey; import com.alibaba.nacos.common.utils.StringUtils; import org.slf4j.Logger; @@ -35,6 +34,8 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.atomic.AtomicBoolean; +import static com.alibaba.nacos.api.common.Constants.FUZZY_WATCH_DIFF_SYNC_NOTIFY; + /** * fuzzy wather context for a single group key pattern. @@ -130,45 +131,43 @@ public Set calculateListenersToNotify(String uuid) { * Notify the listener with the specified data ID, type, and UUID. * * @param dataId Data ID - * @param type Type of the event * @param uuid UUID to filter listeners */ - public void notifyListener(final String dataId, final String group, String tenant, final String type, + public void notifyWatcher(final String dataId, final String group, String tenant, final String changedType,final String syncType, final String uuid) { Set listenersToNotify = calculateListenersToNotify(uuid); - doNotifyWatchers(dataId, group, tenant, type, listenersToNotify); + doNotifyWatchers(dataId, group, tenant, changedType,syncType, listenersToNotify); } /** * Perform the notification for the specified data ID, type, and listeners. * * @param dataId Data ID - * @param type Type of the event * @param listenersToNotify Set of listeners to notify */ - private void doNotifyWatchers(final String dataId, final String group, String tenant, final String type, + private void doNotifyWatchers(final String dataId, final String group, String tenant, final String changedType,final String syncType, Set listenersToNotify) { for (ConfigFuzzyWatcher watcher : listenersToNotify) { - doNotifyWatcher(dataId,group,tenant,type,watcher); + doNotifyWatcher(dataId,group,tenant,changedType,syncType,watcher); } } - private void doNotifyWatcher(final String dataId, final String group, String tenant, final String type, + private void doNotifyWatcher(final String dataId, final String group, String tenant, final String changedType,final String syncType, ConfigFuzzyWatcher configFuzzyWatcher){ AbstractFuzzyNotifyTask job = new AbstractFuzzyNotifyTask() { @Override public void run() { long start = System.currentTimeMillis(); - ConfigFuzzyWatchChangeEvent event = ConfigFuzzyWatchChangeEvent.build(tenant,group, dataId, type); + ConfigFuzzyWatchChangeEvent event = ConfigFuzzyWatchChangeEvent.build(tenant,group, dataId, changedType,syncType); if (configFuzzyWatcher != null) { configFuzzyWatcher.onEvent(event); } LOGGER.info( "[{}] [notify-watcher-ok] dataId={}, group={}, tenant={}, watcher={}, job run cost={} millis.", envName, dataId, group, tenant, configFuzzyWatcher, (System.currentTimeMillis() - start)); - if (type.equals(Constants.ConfigChangedType.DELETE_CONFIG)) { + if (changedType.equals(Constants.ConfigChangedType.DELETE_CONFIG)) { configFuzzyWatcher.getSyncGroupKeys().remove(GroupKey.getKey(dataId, group, tenant)); - } else if (type.equals("FUZZY_WATCH_INIT_NOTIFY") || type.equals( + } else if (changedType.equals( Constants.ConfigChangedType.ADD_CONFIG)) { configFuzzyWatcher.getSyncGroupKeys().add(GroupKey.getKey(dataId, group, tenant)); @@ -219,10 +218,13 @@ public void removeWatcher(ConfigFuzzyWatcher watcher) { * * @param watcher watcher to be added */ - public void addWatcher(ConfigFuzzyWatcher watcher) { - configFuzzyWatchers.add(watcher); - LOGGER.info("[{}] [add-watcher-ok] groupKeyPattern={}, watcher={},uuid={} ", getEnvName(), this.groupKeyPattern, - watcher, watcher.getUuid()); + public boolean addWatcher(ConfigFuzzyWatcher watcher) { + boolean added=configFuzzyWatchers.add(watcher); + if(added){ + LOGGER.info("[{}] [add-watcher-ok] groupKeyPattern={}, watcher={},uuid={} ", getEnvName(), this.groupKeyPattern, + watcher, watcher.getUuid()); + } + return added; } /** @@ -364,7 +366,7 @@ void syncFuzzyWatchers(){ String[] groupKeyItems = GroupKey.parseKey(groupKeyState.getGroupKey()); String changedType=groupKeyState.isExist()? Constants.ConfigChangedType.ADD_CONFIG:Constants.ConfigChangedType.DELETE_CONFIG; - doNotifyWatcher( groupKeyItems[0], groupKeyItems[1], groupKeyItems[2],changedType,configFuzzyWatcher); + doNotifyWatcher( groupKeyItems[0], groupKeyItems[1], groupKeyItems[2],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 new file mode 100644 index 00000000000..a454ad5e1a2 --- /dev/null +++ b/client/src/main/java/com/alibaba/nacos/client/config/impl/ConfigFuzzyWatchGroupKeyHolder.java @@ -0,0 +1,460 @@ +/* + * 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.common.Constants; +import com.alibaba.nacos.api.config.listener.ConfigFuzzyWatcher; +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.ConfigFuzzyWatchChangeNotifyResponse; +import com.alibaba.nacos.api.config.remote.response.ConfigFuzzyWatchResponse; +import com.alibaba.nacos.api.config.remote.response.ConfigFuzzyWatchSyncResponse; +import com.alibaba.nacos.api.exception.NacosException; +import com.alibaba.nacos.client.config.common.GroupKey; +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.remote.client.RpcClient; +import com.alibaba.nacos.common.utils.CollectionUtils; +import com.alibaba.nacos.common.utils.FuzzyGroupKeyPattern; +import org.slf4j.Logger; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +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.ExecutorService; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.AtomicReference; + +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.common.Constants.FUZZY_WATCH_INIT_NOTIFY; +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; + +public class ConfigFuzzyWatchGroupKeyHolder { + + private static final Logger LOGGER = LogUtils.logger(ClientWorker.class); + + private final ClientWorker.ConfigRpcTransportClient agent; + + /** + * fuzzyListenExecuteBell. + */ + private final BlockingQueue fuzzyListenExecuteBell = new ArrayBlockingQueue<>(1); + + private final Object bellItem = new Object(); + + private final AtomicLong fuzzyListenLastAllSyncTime=new AtomicLong(System.currentTimeMillis()); + + private final long FUZZY_LISTEN_ALL_SYNC_INTERNAL=3*60*1000; + + private String taskId="0"; + /** + * fuzzyListenGroupKey -> fuzzyListenContext. + */ + private final AtomicReference> fuzzyListenContextMap = new AtomicReference<>( + new HashMap<>()); + + + public ConfigFuzzyWatchGroupKeyHolder(ClientWorker.ConfigRpcTransportClient agent){ + + this.agent=agent; + NotifyCenter.registerSubscriber(new Subscriber() { + @Override + public void onEvent(Event event) { + FuzzyWatchNotifyEvent fuzzyWatchNotifyEvent = (FuzzyWatchNotifyEvent) event; + ConfigFuzzyWatchContext context = fuzzyListenContextMap.get() + .get(fuzzyWatchNotifyEvent.getGroupKeyPattern()); + if (context == null) { + return; + } + + String[] parseKey = GroupKey.parseKey( + fuzzyWatchNotifyEvent.getGroupKey()); + context.notifyWatcher(parseKey[0], parseKey[1],parseKey[2], fuzzyWatchNotifyEvent.getChangedType(),fuzzyWatchNotifyEvent.getSyncType(), + fuzzyWatchNotifyEvent.getWatcherUuid()); + } + + @Override + public Class subscribeType() { + return FuzzyWatchNotifyEvent.class; + } + }); + + agent.executor.schedule(() -> { + while (!agent.executor.isShutdown() && !agent.executor.isTerminated()) { + try { + fuzzyListenExecuteBell.poll(5L, TimeUnit.SECONDS); + if (agent.executor.isShutdown() || agent.executor.isTerminated()) { + continue; + } + executeConfigFuzzyListen(); + } catch (Throwable e) { + LOGGER.error("[rpc-fuzzy-listen-execute] rpc fuzzy listen exception", e); + try { + Thread.sleep(50L); + } catch (InterruptedException interruptedException) { + //ignore + } + notifyFuzzyWatchSync(); + } + } + }, 0L, TimeUnit.MILLISECONDS); + } + + + /** + * Removes the fuzzy listen context for the specified data ID pattern and group. + * + * @param groupKeyPattern The pattern of the data ID. + */ + public void removeFuzzyListenContext(String groupKeyPattern) { + synchronized (fuzzyListenContextMap) { + Map copy = new HashMap<>(fuzzyListenContextMap.get()); + ConfigFuzzyWatchContext removedContext = copy.remove(groupKeyPattern); + + fuzzyListenContextMap.set(copy); + } + LOGGER.info("[{}] [fuzzy-listen-unsubscribe] {}", agent.getName(), groupKeyPattern); + } + + public ConfigFuzzyWatchContext registerFuzzyWatcher(String dataIdPattern, String groupPattern, + ConfigFuzzyWatcher configFuzzyWatcher){ + ConfigFuzzyWatchContext configFuzzyWatchContext = initFuzzyWatchContextIfAbsent(dataIdPattern, groupPattern); + if(configFuzzyWatchContext.addWatcher(configFuzzyWatcher)){ + if(configFuzzyWatchContext.getReceivedGroupKeys()!=null){ + for(String groupKey:configFuzzyWatchContext.getReceivedGroupKeys()){ + FuzzyWatchNotifyEvent fuzzyWatchNotifyEvent = FuzzyWatchNotifyEvent.buildNotifyPatternAllListenersEvent(groupKey, + configFuzzyWatchContext.getGroupKeyPattern(), ADD_CONFIG,FUZZY_WATCH_INIT_NOTIFY,configFuzzyWatcher.getUuid()); + NotifyCenter.publishEvent(fuzzyWatchNotifyEvent); + } + + } + + } + return configFuzzyWatchContext; + } + + /** + * Checks if the pattern match cache contains an entry for the specified data ID pattern and group. + * + * @param dataIdPattern The data ID pattern. + * @param groupPattern The group name. + * @return True if the cache contains an entry, false otherwise. + */ + public boolean containsPatternMatchCache(String dataIdPattern, String groupPattern) { + Map contextMap = fuzzyListenContextMap.get(); + String groupKeyPattern = FuzzyGroupKeyPattern.generatePattern(dataIdPattern, groupPattern,agent.getTenant()); + return contextMap.containsKey(groupKeyPattern); + } + + + /** + * Retrieves the FuzzyListenContext for the given data ID pattern and group. + * + * @param dataIdPattern The data ID pattern. + * @param groupPattern The group name pattern. + * @return The corresponding FuzzyListenContext, or null if not found. + */ + public ConfigFuzzyWatchContext getFuzzyListenContext(String dataIdPattern, String groupPattern) { + return fuzzyListenContextMap.get() + .get(FuzzyGroupKeyPattern.generatePattern(dataIdPattern, groupPattern,agent.getTenant())); + } + + + /** + * Handles a fuzzy listen init notify request. + * + *

This method processes the incoming fuzzy listen init notify request from a client. It updates the fuzzy + * listen context based on the request's information, and publishes events if necessary. + * + * @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) { + LOGGER.info("[{}] [config-fuzzy-watch-diff-sync-push] sync group key pattern ->{}.", agent.getName(),request.getGroupKeyPattern()); + String groupKeyPattern = request.getGroupKeyPattern(); + ConfigFuzzyWatchContext context = fuzzyListenContextMap.get().get(groupKeyPattern); + if (Constants.FINISH_FUZZY_WATCH_INIT_NOTIFY.equals(request.getSyncType())) { + context.markInitializationComplete(); + return new ConfigFuzzyWatchSyncResponse(); + } + for (ConfigFuzzyWatchSyncRequest.Context requestContext : request.getContexts()) { + Set matchedGroupKeys = context.getReceivedGroupKeys(); + switch (requestContext.getChangedType()) { + case FUZZY_WATCH_INIT_NOTIFY: + case ADD_CONFIG: + if (matchedGroupKeys.add(requestContext.getGroupKey())) { + LOGGER.info("[{}] [config-fuzzy-watch-diff-sync-push] sync group key ->{},changed type->{}", agent.getName(),request.getGroupKeyPattern(),requestContext.getChangedType()); + + NotifyCenter.publishEvent(FuzzyWatchNotifyEvent.buildNotifyPatternAllListenersEvent(requestContext.getGroupKey(), request.getGroupKeyPattern(), + requestContext.getChangedType(),request.getSyncType())); + } + break; + case Constants.ConfigChangedType.DELETE_CONFIG: + if (matchedGroupKeys.remove(requestContext)) { + LOGGER.info("[{}] [config-fuzzy-watch-diff-sync-push] sync group key ->{},changed type->{}", agent.getName(),request.getGroupKeyPattern(),requestContext.getChangedType()); + + NotifyCenter.publishEvent(FuzzyWatchNotifyEvent.buildNotifyPatternAllListenersEvent(requestContext.getGroupKey(), request.getGroupKeyPattern(), + requestContext.getChangedType(),request.getSyncType())); + } + break; + default: + LOGGER.error("Invalid config change type: {}", requestContext.getChangedType()); + break; + } + } + return new ConfigFuzzyWatchSyncResponse(); + } + + /** + * Removes a fuzzy listen listener for the specified data ID pattern, group, and listener. + * + * @param dataIdPattern The pattern of the data ID. + * @param groupPattern The group of the configuration. + * @param watcher The listener to remove. + * @throws NacosException If an error occurs while removing the listener. + */ + public void removeFuzzyWatcher(String dataIdPattern, String groupPattern, ConfigFuzzyWatcher watcher){ + ConfigFuzzyWatchContext configFuzzyWatchContext = getFuzzyListenContext(dataIdPattern, groupPattern); + if (configFuzzyWatchContext != null) { + synchronized (configFuzzyWatchContext) { + configFuzzyWatchContext.removeWatcher(watcher); + if (configFuzzyWatchContext.getConfigFuzzyWatchers().isEmpty()) { + configFuzzyWatchContext.setDiscard(true); + configFuzzyWatchContext.setConsistentWithServer(false); + } + } + } + } + + /** + * Handles a fuzzy listen notify change request. + * + *

This method processes the incoming fuzzy listen notify change request from a client. It updates the fuzzy + * listen context based on the request's information, and publishes events if necessary. + * + * @param request The fuzzy listen notify change request to handle. + */ + ConfigFuzzyWatchChangeNotifyResponse handlerFuzzyListenNotifyChangeRequest( + ConfigFuzzyWatchChangeNotifyRequest request) { + LOGGER.info("[{}] [fuzzy-watch-config-push] fuzzy watched config changed,groupKey->{},changedType->{} ", agent.getName(),request.getGroupKey(),request.getChangeType()); + Map listenContextMap = fuzzyListenContextMap.get(); + String[] groupItems = GroupKey.parseKey(request.getGroupKey()); + Set matchedPatterns = FuzzyGroupKeyPattern.filterMatchedPatterns(listenContextMap.keySet(),groupItems[0], + groupItems[1], groupItems[2]); + for (String matchedPattern : matchedPatterns) { + ConfigFuzzyWatchContext context = listenContextMap.get(matchedPattern); + if (ADD_CONFIG.equals(request.getChangeType())||CONFIG_CHANGED.equals(request.getChangeType())) { + if (context.getReceivedGroupKeys().add(request.getGroupKey())) { + NotifyCenter.publishEvent( + FuzzyWatchNotifyEvent.buildNotifyPatternAllListenersEvent(request.getGroupKey(), matchedPattern, ADD_CONFIG,FUZZY_WATCH_RESOURCE_CHANGED)); + } + } else if (DELETE_CONFIG.equals(request.getChangeType())&&context.getReceivedGroupKeys().remove(request.getGroupKey())) { + NotifyCenter.publishEvent( + FuzzyWatchNotifyEvent.buildNotifyPatternAllListenersEvent(request.getGroupKey(), matchedPattern, Constants.ConfigChangedType.DELETE_CONFIG,FUZZY_WATCH_RESOURCE_CHANGED)); + + } + } + return new ConfigFuzzyWatchChangeNotifyResponse(); + } + + + void notifyFuzzyWatchSync(){ + fuzzyListenExecuteBell.offer(bellItem); + + } + + /** + * Execute fuzzy listen configuration changes. + * + *

This method iterates through all fuzzy listen contexts and determines whether they need to be added or + * removed based on their consistency with the server and discard status. It then calls the appropriate method + * to execute the fuzzy listen operation. + * + * @throws NacosException If an error occurs during the execution of fuzzy listen configuration changes. + */ + public void executeConfigFuzzyListen() throws NacosException { + + // Obtain the current timestamp + long now = System.currentTimeMillis(); + + // Determine whether a full synchronization is needed + boolean needAllSync = now - fuzzyListenLastAllSyncTime.get() >= FUZZY_LISTEN_ALL_SYNC_INTERNAL; + + List needSyncContexts=new ArrayList<>(); + // Iterate through all fuzzy listen contexts + for (ConfigFuzzyWatchContext context : fuzzyListenContextMap.get().values()) { + // Check if the context is consistent with the server + if (context.isConsistentWithServer()) { + // Skip if a full synchronization is not needed + if (!needAllSync) { + continue; + }else{ + context.syncFuzzyWatchers(); + } + } + + needSyncContexts.add(context); + } + + // Execute fuzzy listen operation for addition + doExecuteConfigFuzzyListen(needSyncContexts); + + // Update last all sync time if a full synchronization was performed + if (needAllSync) { + fuzzyListenLastAllSyncTime.set(now); + } + } + + void resetConsistenceStatus(){ + Collection configFuzzyWatchContexts = fuzzyListenContextMap.get().values(); + + for (ConfigFuzzyWatchContext context : configFuzzyWatchContexts) { + context.setConsistentWithServer(false); + } + } + + /** + * Execute fuzzy listen configuration changes for a specific map of contexts. + * + *

This method submits tasks to execute fuzzy listen operations asynchronously for the provided contexts. It + * waits for all tasks to complete and logs any errors that occur. + * + * @param contextLists The map of contexts to execute fuzzy listen operations for. + * @throws NacosException If an error occurs during the execution of fuzzy listen configuration changes. + */ + private void doExecuteConfigFuzzyListen(List contextLists) + throws NacosException { + // Return if the context map is null or empty + if (CollectionUtils.isEmpty(contextLists)) { + return; + } + + // List to hold futures for asynchronous tasks + List> listenFutures = new ArrayList<>(); + + RpcClient rpcClient = agent.ensureRpcClient(taskId); + + // Iterate through the context map and submit tasks for execution + for (ConfigFuzzyWatchContext entry : contextLists) { + ExecutorService executorService = agent.executor; + // 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); + } + + } + } 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 + } + // Retry notification + notifyFuzzyWatchSync(); + } + }); + listenFutures.add(future); + } + + // Wait for all tasks to complete + for (Future future : listenFutures) { + try { + future.get(); + } catch (Throwable throwable) { + // Log async listen error + LOGGER.error("Async fuzzy listen config change error.", throwable); + } + } + } + + + /** + * Builds a request for fuzzy listen configuration. + * + * @param context The list of fuzzy listen contexts. + * @return A {@code ConfigBatchFuzzyListenRequest} object representing the request. + */ + private ConfigFuzzyWatchRequest buildFuzzyListenConfigRequest(ConfigFuzzyWatchContext context) { + ConfigFuzzyWatchRequest request = new ConfigFuzzyWatchRequest(); + request.setGroupKeyPattern(context.getGroupKeyPattern()); + request.setInitializing(context.isInitializing()); + request.setWatchType(context.isDiscard()?WATCH_TYPE_WATCH:WATCH_TYPE_CANCEL_WATCH); + request.setReceivedGroupKeys(context.getReceivedGroupKeys()); + return request; + } + + + /** + * Adds a fuzzy listen context if it doesn't already exist for the specified data ID pattern and group. If the + * context already exists, returns the existing context. + * + * @param dataIdPattern The pattern of the data ID. + * @param groupPattern The group of the configuration. + * @return The fuzzy listen context for the specified data ID pattern and group. + */ + private ConfigFuzzyWatchContext initFuzzyWatchContextIfAbsent(String dataIdPattern, String groupPattern) { + ConfigFuzzyWatchContext context = getFuzzyListenContext(dataIdPattern, groupPattern); + if (context != null) { + return context; + } + synchronized (fuzzyListenContextMap) { + ConfigFuzzyWatchContext contextFromMap = getFuzzyListenContext(dataIdPattern, groupPattern); + if (contextFromMap != null) { + context = contextFromMap; + } else { + context = new ConfigFuzzyWatchContext(agent.getName(), + FuzzyGroupKeyPattern.generatePattern(dataIdPattern, groupPattern,agent.getTenant())); + context.setConsistentWithServer(false); + Map copy = new HashMap<>(fuzzyListenContextMap.get()); + String groupKeyPattern = FuzzyGroupKeyPattern.generatePattern(dataIdPattern, groupPattern,agent.getTenant()); + copy.put(groupKeyPattern, context); + fuzzyListenContextMap.set(copy); + notifyFuzzyWatchSync(); + } + } + + return context; + } + +} 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 28d49e59054..1bd5a8e10c5 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.config.filter.impl.ConfigResponse; 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.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.plugin.auth.api.RequestResource; +import com.alibaba.nacos.client.utils.ParamUtil; import java.util.HashMap; import java.util.Map; @@ -177,11 +177,6 @@ public String getTenant() { **/ public abstract void notifyListenConfig(); - /** - * notify fuzzy listen config. - */ - public abstract void notifyFuzzyListenConfig(); - /** * listen change . * @@ -189,13 +184,6 @@ public String getTenant() { */ public abstract void executeConfigListen() throws NacosException; - /** - * Fuzzy listen change. - * - * @throws NacosException nacos exception throws, should retry. - */ - public abstract void executeConfigFuzzyListen() throws NacosException; - /** * remove cache implements. * @@ -204,15 +192,6 @@ public String getTenant() { */ public abstract void removeCache(String dataId, String group); - /** - * Remove fuzzy listen context. - * - * @param dataIdPattern dataIdPattern - * @param group group - * @throws NacosException if an error occurs while removing the fuzzy listen context. - */ - public abstract void removeFuzzyListenContext(String dataIdPattern, String group) throws NacosException; - /** * query config. * diff --git a/client/src/main/java/com/alibaba/nacos/client/config/impl/FuzzyWatchNotifyEvent.java b/client/src/main/java/com/alibaba/nacos/client/config/impl/FuzzyWatchNotifyEvent.java index 6b7c84f9231..220660ed599 100644 --- a/client/src/main/java/com/alibaba/nacos/client/config/impl/FuzzyWatchNotifyEvent.java +++ b/client/src/main/java/com/alibaba/nacos/client/config/impl/FuzzyWatchNotifyEvent.java @@ -33,32 +33,21 @@ public class FuzzyWatchNotifyEvent extends Event { /** * The uuid of this watcher for which that this notify event . */ - private String uuid; + private String watcherUuid; /** * The groupKeyPattern of configuration. */ private String groupKeyPattern; - /** - * The namespace of the configuration. - */ - private String namespace; - - /** - * The group of the configuration. - */ - private String group; - - /** - * The dataId of the configuration. - */ - private String dataId; + private String groupKey; /** * The type of notification (e.g., ADD_CONFIG, DELETE_CONFIG). */ - private String type; + private String changedType; + + private String syncType; /** * Constructs a new FuzzyListenNotifyEvent. @@ -69,15 +58,13 @@ public FuzzyWatchNotifyEvent() { /** * Constructs a new FuzzyListenNotifyEvent with the specified group, dataId, and type. * - * @param group The group of the configuration. - * @param dataId The dataId of the configuration. - * @param type The type of notification. + * @param groupKey The groupKey of the configuration. + * @param changedType The type of notification. */ - public FuzzyWatchNotifyEvent(String namespace,String group, String dataId, String type, String groupKeyPattern) { - this.group = group; - this.dataId = dataId; - this.namespace=namespace; - this.type = type; + private FuzzyWatchNotifyEvent(String groupKey, String changedType, String syncType, String groupKeyPattern) { + this.groupKey = groupKey; + this.syncType=syncType; + this.changedType = changedType; this.groupKeyPattern = groupKeyPattern; } @@ -85,93 +72,57 @@ public FuzzyWatchNotifyEvent(String namespace,String group, String dataId, Strin /** * Builds a new FuzzyListenNotifyEvent with the specified group, dataId, and type. * - * @param group The group of the configuration. - * @param dataId The dataId of the configuration. - * @param type The type of notification. + * @param groupKey The groupKey of the configuration. * @return A new FuzzyListenNotifyEvent instance. */ - public static FuzzyWatchNotifyEvent buildNotifyPatternAllListenersEvent(String namespace,String group, String dataId, - String groupKeyPattern, String type) { - return new FuzzyWatchNotifyEvent(namespace,group, dataId, type, groupKeyPattern); + public static FuzzyWatchNotifyEvent buildNotifyPatternAllListenersEvent(String groupKey, + String groupKeyPattern, String changedType,String syncType) { + return new FuzzyWatchNotifyEvent(groupKey, changedType, syncType,groupKeyPattern); } /** - * Gets the UUID (Unique Identifier) of the listener. + * Builds a new FuzzyListenNotifyEvent with the specified group, dataId, and type. * - * @return The UUID of the listener. + * @param groupKey The groupKey of the configuration. + * @return A new FuzzyListenNotifyEvent instance. */ - public String getUuid() { - return uuid; + public static FuzzyWatchNotifyEvent buildNotifyPatternAllListenersEvent(String groupKey, + String groupKeyPattern, String changedType,String syncType,String uuid) { + FuzzyWatchNotifyEvent fuzzyWatchNotifyEvent = new FuzzyWatchNotifyEvent(groupKey, changedType, syncType, + groupKeyPattern); + fuzzyWatchNotifyEvent.watcherUuid =uuid; + return fuzzyWatchNotifyEvent; } - /** - * Sets the UUID (Unique Identifier) of the listener. + * Gets the UUID (Unique Identifier) of the listener. * - * @param uuid The UUID to set. + * @return The UUID of the listener. */ - public void setUuid(String uuid) { - this.uuid = uuid; + public String getWatcherUuid() { + return watcherUuid; } + public String getGroupKeyPattern() { return groupKeyPattern; } - public void setGroupKeyPattern(String groupKeyPattern) { - this.groupKeyPattern = groupKeyPattern; - } - - /** - * Gets the group of the configuration. - * - * @return The group of the configuration. - */ - public String getGroup() { - return group; - } - - /** - * Sets the group of the configuration. - * - * @param group The group to set. - */ - public void setGroup(String group) { - this.group = group; - } - - /** - * Gets the dataId of the configuration. - * - * @return The dataId of the configuration. - */ - public String getDataId() { - return dataId; + + public String getGroupKey() { + return groupKey; } + - /** - * Sets the dataId of the configuration. - * - * @param dataId The dataId to set. - */ - public void setDataId(String dataId) { - this.dataId = dataId; + public String getSyncType() { + return syncType; } - + /** * Gets the type of notification. * * @return The type of notification. */ - public String getType() { - return type; - } - - /** - * Sets the type of notification. - * - * @param type The type to set. - */ - public void setType(String type) { - this.type = type; + public String getChangedType() { + return changedType; } } diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/NacosNamingService.java b/client/src/main/java/com/alibaba/nacos/client/naming/NacosNamingService.java index 2dbda8dcbaf..3cf73004799 100644 --- a/client/src/main/java/com/alibaba/nacos/client/naming/NacosNamingService.java +++ b/client/src/main/java/com/alibaba/nacos/client/naming/NacosNamingService.java @@ -33,11 +33,11 @@ import com.alibaba.nacos.client.env.NacosClientProperties; import com.alibaba.nacos.client.naming.cache.NamingFuzzyWatchContext; import com.alibaba.nacos.client.naming.cache.ServiceInfoHolder; -import com.alibaba.nacos.client.naming.cache.FuzzyWatchServiceListHolder; +import com.alibaba.nacos.client.naming.cache.NamingFuzzyWatchServiceListHolder; import com.alibaba.nacos.client.naming.core.Balancer; import com.alibaba.nacos.client.naming.event.InstancesChangeEvent; import com.alibaba.nacos.client.naming.event.InstancesChangeNotifier; -import com.alibaba.nacos.client.naming.event.FuzzyWatchNotifyEvent; +import com.alibaba.nacos.client.naming.event.NamingFuzzyWatchNotifyEvent; import com.alibaba.nacos.client.naming.event.InstancesDiff; import com.alibaba.nacos.client.naming.remote.NamingClientProxy; import com.alibaba.nacos.client.naming.remote.NamingClientProxyDelegate; @@ -90,7 +90,7 @@ public class NacosNamingService implements NamingService { private ServiceInfoHolder serviceInfoHolder; - private FuzzyWatchServiceListHolder fuzzyWatchServiceListHolder; + private NamingFuzzyWatchServiceListHolder namingFuzzyWatchServiceListHolder; private InstancesChangeNotifier changeNotifier; @@ -124,11 +124,13 @@ private void init(Properties properties) throws NacosException { NotifyCenter.registerSubscriber(changeNotifier); this.serviceInfoHolder = new ServiceInfoHolder(namespace, this.notifierEventScope, nacosClientProperties); - NotifyCenter.registerToPublisher(FuzzyWatchNotifyEvent.class, 16384); - this.fuzzyWatchServiceListHolder = new FuzzyWatchServiceListHolder(this.notifierEventScope, nacosClientProperties); + NotifyCenter.registerToPublisher(NamingFuzzyWatchNotifyEvent.class, 16384); + this.namingFuzzyWatchServiceListHolder = new NamingFuzzyWatchServiceListHolder(this.notifierEventScope); + + this.clientProxy = new NamingClientProxyDelegate(this.namespace, serviceInfoHolder, + nacosClientProperties, changeNotifier,namingFuzzyWatchServiceListHolder); + - this.clientProxy = new NamingClientProxyDelegate(this.namespace, serviceInfoHolder, fuzzyWatchServiceListHolder, - nacosClientProperties, changeNotifier); } @@ -572,15 +574,13 @@ public Future> fuzzyWatchWithServiceKeys(String serviceNamePat private Future> doFuzzyWatch(String serviceNamePattern, String groupNamePattern, - FuzzyWatchEventWatcher watcher) throws NacosException { + FuzzyWatchEventWatcher watcher) { if (null == watcher) { return null; } String groupKeyPattern = FuzzyGroupKeyPattern.generatePattern(serviceNamePattern, groupNamePattern, namespace); - NamingFuzzyWatchContext namingFuzzyWatchContext = fuzzyWatchServiceListHolder.initFuzzyWatchContextIfNeed( - groupKeyPattern); - namingFuzzyWatchContext.registerWatcher(watcher); + NamingFuzzyWatchContext namingFuzzyWatchContext = namingFuzzyWatchServiceListHolder.registerFuzzyWatcher(groupKeyPattern,watcher); return namingFuzzyWatchContext.createNewFuture(); } @@ -601,7 +601,7 @@ private void doCancelFuzzyWatch(String serviceNamePattern, String groupNamePatte } String groupKeyPattern=FuzzyGroupKeyPattern.generatePattern(serviceNamePattern,groupNamePattern,namespace); - NamingFuzzyWatchContext namingFuzzyWatchContext = fuzzyWatchServiceListHolder.patternWatched(groupKeyPattern); + NamingFuzzyWatchContext namingFuzzyWatchContext = namingFuzzyWatchServiceListHolder.getFuzzyWatchContext(groupKeyPattern); if (namingFuzzyWatchContext!=null){ namingFuzzyWatchContext.removeWatcher(watcher); } @@ -644,6 +644,7 @@ public String getServerStatus() { public void shutDown() throws NacosException { serviceInfoHolder.shutdown(); clientProxy.shutdown(); + namingFuzzyWatchServiceListHolder.shutdown(); NotifyCenter.deregisterSubscriber(changeNotifier); } diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/cache/FuzzyWatchServiceListHolder.java b/client/src/main/java/com/alibaba/nacos/client/naming/cache/FuzzyWatchServiceListHolder.java deleted file mode 100644 index 870c3e6e48e..00000000000 --- a/client/src/main/java/com/alibaba/nacos/client/naming/cache/FuzzyWatchServiceListHolder.java +++ /dev/null @@ -1,102 +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.client.naming.cache; - -import com.alibaba.nacos.api.common.Constants; -import com.alibaba.nacos.api.naming.remote.request.AbstractFuzzyWatchNotifyRequest; -import com.alibaba.nacos.api.naming.remote.request.FuzzyWatchNotifyChangeRequest; -import com.alibaba.nacos.api.naming.remote.request.FuzzyWatchNotifyInitRequest; -import com.alibaba.nacos.api.naming.utils.NamingUtils; -import com.alibaba.nacos.client.env.NacosClientProperties; -import com.alibaba.nacos.client.naming.event.FuzzyWatchNotifyEvent; -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.utils.FuzzyGroupKeyPattern; - -import java.util.Collection; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -/** - * Naming client fuzzy watch service list holder. - * - * @author tanyongquan - */ -public class FuzzyWatchServiceListHolder extends Subscriber { - - private String notifierEventScope; - - /** - * The contents of {@code patternMatchMap} are Map{pattern -> Set[matched services]}. - */ - private Map fuzzyMatchContextMap = new ConcurrentHashMap<>(); - - public FuzzyWatchServiceListHolder(String notifierEventScope, NacosClientProperties properties) { - this.notifierEventScope = notifierEventScope; - } - - public NamingFuzzyWatchContext patternWatched(String groupKeyPattern){ - return fuzzyMatchContextMap.get(groupKeyPattern); - } - - public NamingFuzzyWatchContext initFuzzyWatchContextIfNeed(String groupKeyPattern){ - if (fuzzyMatchContextMap.containsKey(groupKeyPattern)){ - return fuzzyMatchContextMap.get(groupKeyPattern); - }else { - return fuzzyMatchContextMap.putIfAbsent(groupKeyPattern,new NamingFuzzyWatchContext(notifierEventScope,groupKeyPattern)); - } - } - - public synchronized void removePatternMatchCache(String groupkeyPattern) { - NamingFuzzyWatchContext namingFuzzyWatchContext = fuzzyMatchContextMap.get(groupkeyPattern); - if (namingFuzzyWatchContext==null){ - return; - } - if (namingFuzzyWatchContext.isDiscard()&&namingFuzzyWatchContext.getNamingFuzzyWatchers().isEmpty()){ - fuzzyMatchContextMap.remove(groupkeyPattern); - } - } - - public Map getFuzzyMatchContextMap() { - return fuzzyMatchContextMap; - } - - @Override - public void onEvent(FuzzyWatchNotifyEvent event) { - if (!event.scope().equals(notifierEventScope)){ - return; - } - String changedType = event.getChangedType(); - String serviceKey = event.getServiceKey(); - String pattern = event.getPattern(); - NamingFuzzyWatchContext namingFuzzyWatchContext = fuzzyMatchContextMap.get(pattern); - if (fuzzyMatchContextMap!=null){ - namingFuzzyWatchContext.notifyFuzzyWatchers(serviceKey,changedType); - } - - } - - @Override - public Class subscribeType() { - return FuzzyWatchNotifyEvent.class; - } - - public String getNotifierEventScope() { - return notifierEventScope; - } -} 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 ee328a890a4..8fee42f49c3 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 @@ -22,9 +22,9 @@ import com.alibaba.nacos.api.naming.pojo.ListView; import com.alibaba.nacos.api.naming.utils.NamingUtils; 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.GroupKey; import com.alibaba.nacos.common.utils.StringUtils; import org.slf4j.Logger; @@ -34,10 +34,13 @@ import java.util.List; import java.util.Set; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutionException; 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; /** @@ -62,10 +65,6 @@ public class NamingFuzzyWatchContext { */ private String envName; - /** - * Task ID. - */ - private int taskId; private String groupKeyPattern; @@ -130,38 +129,26 @@ public Set calculateListenersToNotify(String uuid) { return listenersToNotify; } - /** - * Notify the listener with the specified data ID, type, and UUID. - * - * @param serviceName Data ID - * @param type Type of the event - * @param uuid UUID to filter listeners - */ - public void notifyListener(final String serviceName, final String groupName, String namespace, final String type, - final String uuid) { - Set listenersToNotify = calculateListenersToNotify(uuid); - doNotifyWatchers(serviceName, groupName, namespace, type, listenersToNotify); - } + private void doNotifyWatcher(final String serviceKey, final String changedType,String syncType, + FuzzyWatchEventWatcher namingFuzzyWatcher){ - /** - * Perform the notification for the specified data ID, type, and listeners. - * - * @param serviceName Data ID - * @param changedType Type of the event - * @param listenersToNotify Set of listeners to notify - */ - private void doNotifyWatchers(final String serviceName, final String groupName, String namespace, final String changedType, - Set listenersToNotify) { - for (FuzzyWatchEventWatcher watcher : listenersToNotify) { - doNotifyWatcher(serviceName,groupName,namespace,changedType,watcher); + if (ADD_SERVICE.equals(changedType)&&namingFuzzyWatcher.getSyncServiceKeys().contains(serviceKey)){ + return; } - } - private void doNotifyWatcher(final String serviceName, final String groupName, String namespace, final String changedType, - FuzzyWatchEventWatcher namingFuzzyWatcher){ + if (DELETE_SERVICE.equals(changedType)&&!namingFuzzyWatcher.getSyncServiceKeys().contains(serviceKey)){ + return; + } + + String[] serviceKeyItems = NamingUtils.parseServiceKey(serviceKey); + String namespace=serviceKeyItems[0]; + String groupName=serviceKeyItems[0]; + String serviceName=serviceKeyItems[0]; + + Runnable job = () -> { long start = System.currentTimeMillis(); - FuzzyWatchChangeEvent event = new FuzzyWatchChangeEvent(namespace,groupName, serviceName, changedType); + FuzzyWatchChangeEvent event = new FuzzyWatchChangeEvent(namespace,groupName, serviceName, changedType,syncType); if (namingFuzzyWatcher != null) { namingFuzzyWatcher.onEvent(event); } @@ -169,10 +156,10 @@ private void doNotifyWatcher(final String serviceName, final String groupName, S "[{}] [notify-watcher-ok] serviceName={}, groupName={}, namespace={}, watcher={}, job run cost={} millis.", envName, serviceName, groupName, namespace, namingFuzzyWatcher, (System.currentTimeMillis() - start)); if (changedType.equals(Constants.ConfigChangedType.DELETE_CONFIG)) { - namingFuzzyWatcher.getSyncGroupKeys().remove(GroupKey.getKey(serviceName, groupName, namespace)); + namingFuzzyWatcher.getSyncServiceKeys().remove(NamingUtils.getServiceKey(namespace, groupName, serviceName)); } else if (changedType.equals(FUZZY_WATCH_INIT_NOTIFY) || changedType.equals( Constants.ConfigChangedType.ADD_CONFIG)) { - namingFuzzyWatcher.getSyncGroupKeys().add(GroupKey.getKey(serviceName, groupName, namespace)); + namingFuzzyWatcher.getSyncServiceKeys().add(NamingUtils.getServiceKey(namespace, groupName, serviceName)); } }; @@ -213,20 +200,6 @@ public void removeWatcher(FuzzyWatchEventWatcher watcher) { } } - - /** - * Add a watcher to the context. - * - * @param watcher watcher to be added - */ - public void registerWatcher(FuzzyWatchEventWatcher watcher) { - if(namingFuzzyWatchers.add(watcher)){ - LOGGER.info("[{}] [add-watcher-ok] groupKeyPattern={}, watcher={},uuid={} ", getEnvName(), this.groupKeyPattern, - watcher, watcher.getUuid()); - } - - } - /** * Get the environment name. * @@ -245,24 +218,6 @@ public void setEnvName(String envName) { this.envName = envName; } - /** - * Get the task ID. - * - * @return Task ID - */ - public int getTaskId() { - return taskId; - } - - /** - * Set the task ID. - * - * @param taskId Task ID to be set - */ - public void setTaskId(int taskId) { - this.taskId = taskId; - } - public String getGroupKeyPattern() { return groupKeyPattern; } @@ -346,24 +301,32 @@ public Set getNamingFuzzyWatchers() { void syncFuzzyWatchers(){ for(FuzzyWatchEventWatcher namingFuzzyWatcher: namingFuzzyWatchers){ - Set receivedGroupKeysContext = receivedServiceKeys; - Set syncGroupKeys = namingFuzzyWatcher.getSyncGroupKeys(); + Set receivedServiceKeysContext = this.getReceivedServiceKeys(); + Set syncGroupKeys = namingFuzzyWatcher.getSyncServiceKeys(); List groupKeyStates = FuzzyGroupKeyPattern.diffGroupKeys( - receivedGroupKeysContext, syncGroupKeys); + receivedServiceKeysContext, syncGroupKeys); for(FuzzyGroupKeyPattern.GroupKeyState groupKeyState:groupKeyStates){ - String[] serviceKeyItems = NamingUtils.parseServiceKey(groupKeyState.getGroupKey()); - String changedType=groupKeyState.isExist()? Constants.ConfigChangedType.ADD_CONFIG:Constants.ConfigChangedType.DELETE_CONFIG; - doNotifyWatcher(serviceKeyItems[2], serviceKeyItems[1], serviceKeyItems[0],changedType,namingFuzzyWatcher); + String changedType=groupKeyState.isExist()? ADD_SERVICE:DELETE_SERVICE; + doNotifyWatcher(groupKeyState.getGroupKey(),changedType,FUZZY_WATCH_DIFF_SYNC_NOTIFY,namingFuzzyWatcher); } - } } - void notifyFuzzyWatchers(String serviceKey,String changedType){ - for(FuzzyWatchEventWatcher namingFuzzyWatcher: namingFuzzyWatchers){ - String[] serviceKeyItems = NamingUtils.parseServiceKey(serviceKey); - doNotifyWatcher(serviceKeyItems[2], serviceKeyItems[1], serviceKeyItems[0],changedType,namingFuzzyWatcher); + void notifyFuzzyWatchers(String serviceKey,String syncType,String changedType,String watcherUuid){ + for(FuzzyWatchEventWatcher namingFuzzyWatcher: filterWatchers(watcherUuid)){ + doNotifyWatcher(serviceKey,changedType,syncType,namingFuzzyWatcher); + } + } + + + private Set filterWatchers(String uuid){ + if (StringUtils.isBlank(uuid)|| CollectionUtils.isEmpty(getNamingFuzzyWatchers())){ + return getNamingFuzzyWatchers(); + }else{ + return getNamingFuzzyWatchers().stream().filter(a->a.getUuid().equals(uuid)).collect( + Collectors.toSet()); } + } public CompletableFuture> createNewFuture(){ @@ -374,8 +337,7 @@ public boolean isDone() { } @Override - public ListView get() throws InterruptedException, ExecutionException { - + public ListView get() { ListView result = new ListView<>(); result.setData(Arrays.asList(NamingFuzzyWatchContext.this.receivedServiceKeys.toArray(new String[0]))); result.setCount(result.getData().size()); 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 new file mode 100644 index 00000000000..d89ae4a6695 --- /dev/null +++ b/client/src/main/java/com/alibaba/nacos/client/naming/cache/NamingFuzzyWatchServiceListHolder.java @@ -0,0 +1,319 @@ +/* + * 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.cache; + + +import com.alibaba.nacos.api.exception.NacosException; +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.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.utils.CollectionUtils; +import org.slf4j.Logger; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; + +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.WATCH_TYPE_CANCEL_WATCH; +import static com.alibaba.nacos.api.common.Constants.WATCH_TYPE_WATCH; + +/** + * Naming client fuzzy watch service list holder. + * + * @author tanyongquan + */ +public class NamingFuzzyWatchServiceListHolder extends Subscriber { + + private static final Logger LOGGER = LogUtils.logger(NamingFuzzyWatchServiceListHolder.class); + + private String notifierEventScope; + + private NamingGrpcClientProxy namingGrpcClientProxy; + + /** + * fuzzyListenExecuteBell. + */ + private final BlockingQueue fuzzyWatchExecuteBell = new ArrayBlockingQueue<>(1); + + private final Object bellItem = new Object(); + + private final AtomicLong fuzzyWatchLastAllSyncTime =new AtomicLong(System.currentTimeMillis()); + + private final long FUZZY_LISTEN_ALL_SYNC_INTERNAL=3*60*1000; + + ScheduledExecutorService executorService; + + /** + * The contents of {@code patternMatchMap} are Map{pattern -> Set[matched services]}. + */ + private Map fuzzyMatchContextMap = new ConcurrentHashMap<>(); + + public NamingFuzzyWatchServiceListHolder(String notifierEventScope) { + this.notifierEventScope = notifierEventScope; + } + + + public void shutdown(){ + if (executorService!=null&&!executorService.isShutdown()){ + executorService.shutdown(); + } + } + public void start(){ + executorService = Executors.newSingleThreadScheduledExecutor( + new NameThreadFactory("com.alibaba.nacos.client.naming.fuzzy.watch.Worker")); + + executorService.submit(() -> { + while (!executorService.isShutdown() && !executorService.isTerminated()) { + try { + fuzzyWatchExecuteBell.poll(5L, TimeUnit.SECONDS); + if (executorService.isShutdown() || executorService.isTerminated()) { + continue; + } + executeNamingFuzzyWatch(); + } catch (Throwable e) { + LOGGER.error("[rpc-fuzzy-watch-execute] rpc fuzzy watch exception", e); + try { + Thread.sleep(50L); + } catch (InterruptedException interruptedException) { + //ignore + } + notifyFuzzyWatchSync(); + } + } + }); + } + + public void registerNamingGrpcClientProxy(NamingGrpcClientProxy namingGrpcClientProxy ){ + this.namingGrpcClientProxy=namingGrpcClientProxy; + } + + public NamingFuzzyWatchContext getFuzzyWatchContext(String groupKeyPattern){ + return fuzzyMatchContextMap.get(groupKeyPattern); + } + + /** + * Add a watcher to the context. + * + * @param watcher watcher to be added + */ + public NamingFuzzyWatchContext registerFuzzyWatcher(String groupKeyPattern,FuzzyWatchEventWatcher watcher) { + NamingFuzzyWatchContext namingFuzzyWatchContext = initFuzzyWatchContextIfNeed(groupKeyPattern); + if(namingFuzzyWatchContext.getNamingFuzzyWatchers().add(watcher)){ + LOGGER.info("[{}] [add-watcher-ok] groupKeyPattern={}, watcher={},uuid={} ", groupKeyPattern, + watcher, watcher.getUuid()); + if(CollectionUtils.isNotEmpty(namingFuzzyWatchContext.getReceivedServiceKeys())){ + for(String serviceKey:namingFuzzyWatchContext.getReceivedServiceKeys()){ + NamingFuzzyWatchNotifyEvent namingFuzzyWatchNotifyEvent = NamingFuzzyWatchNotifyEvent.build(notifierEventScope, + groupKeyPattern, serviceKey,ADD_SERVICE,FUZZY_WATCH_INIT_NOTIFY,watcher.getUuid()); + NotifyCenter.publishEvent(namingFuzzyWatchNotifyEvent); + } + } + } + return namingFuzzyWatchContext; + } + + public NamingFuzzyWatchContext initFuzzyWatchContextIfNeed(String groupKeyPattern){ + if (fuzzyMatchContextMap.containsKey(groupKeyPattern)){ + return fuzzyMatchContextMap.get(groupKeyPattern); + }else { + synchronized (fuzzyMatchContextMap){ + if (fuzzyMatchContextMap.containsKey(groupKeyPattern)){ + return fuzzyMatchContextMap.get(groupKeyPattern); + } + NamingFuzzyWatchContext namingFuzzyWatchContext = fuzzyMatchContextMap.putIfAbsent(groupKeyPattern, + new NamingFuzzyWatchContext(notifierEventScope, groupKeyPattern)); + notifyFuzzyWatchSync(); + return namingFuzzyWatchContext; + + } + } + } + + public synchronized void removePatternMatchCache(String groupKeyPattern) { + NamingFuzzyWatchContext namingFuzzyWatchContext = fuzzyMatchContextMap.get(groupKeyPattern); + if (namingFuzzyWatchContext==null){ + return; + } + if (namingFuzzyWatchContext.isDiscard()&&namingFuzzyWatchContext.getNamingFuzzyWatchers().isEmpty()){ + fuzzyMatchContextMap.remove(groupKeyPattern); + } + } + + void notifyFuzzyWatchSync(){ + fuzzyWatchExecuteBell.offer(bellItem); + } + + /** + * Execute fuzzy listen configuration changes. + * + *

This method iterates through all fuzzy listen contexts and determines whether they need to be added or + * removed based on their consistency with the server and discard status. It then calls the appropriate method + * to execute the fuzzy listen operation. + * + * @throws NacosException If an error occurs during the execution of fuzzy listen configuration changes. + */ + public void executeNamingFuzzyWatch() throws NacosException { + + // Obtain the current timestamp + long now = System.currentTimeMillis(); + + // Determine whether a full synchronization is needed + boolean needAllSync = now - fuzzyWatchLastAllSyncTime.get() >= FUZZY_LISTEN_ALL_SYNC_INTERNAL; + + List needSyncContexts=new ArrayList<>(); + // Iterate through all fuzzy listen contexts + for (NamingFuzzyWatchContext context : fuzzyMatchContextMap.values()) { + // Check if the context is consistent with the server + if (context.isConsistentWithServer()) { + // Skip if a full synchronization is not needed + if (!needAllSync) { + continue; + }else{ + context.syncFuzzyWatchers(); + } + } + + needSyncContexts.add(context); + } + + // Execute fuzzy listen operation for addition + doExecuteNamingFuzzyWatch(needSyncContexts); + + // Update last all sync time if a full synchronization was performed + if (needAllSync) { + fuzzyWatchLastAllSyncTime.set(now); + } + } + + public void resetConsistenceStatus(){ + fuzzyMatchContextMap.values().forEach(fuzzyWatcherContext -> fuzzyWatcherContext.setConsistentWithServer(false)); + } + + /** + * Execute fuzzy listen configuration changes for a specific map of contexts. + * + *

This method submits tasks to execute fuzzy listen operations asynchronously for the provided contexts. It + * waits for all tasks to complete and logs any errors that occur. + * + * @param contextLists The map of contexts to execute fuzzy listen operations for. + * @throws NacosException If an error occurs during the execution of fuzzy listen configuration changes. + */ + private void doExecuteNamingFuzzyWatch(List contextLists) + throws NacosException { + // Return if the context map is null or empty + if (CollectionUtils.isEmpty(contextLists)) { + return; + } + + // Iterate through the context map and submit tasks for execution + for (NamingFuzzyWatchContext entry : contextLists) { + // Submit task for execution + NamingFuzzyWatchRequest configFuzzyWatchRequest = buildFuzzyWatchNamingRequest( + entry); + try { + + + // Execute the fuzzy listen operation + NamingFuzzyWatchResponse listenResponse = namingGrpcClientProxy.fuzzyWatchRequest(configFuzzyWatchRequest); + if (listenResponse != null && listenResponse.isSuccess()) { + + if (configFuzzyWatchRequest.getWatchType().equals(WATCH_TYPE_CANCEL_WATCH)) { + removePatternMatchCache(entry.getGroupKeyPattern()); + } 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 + } + // Retry notification + notifyFuzzyWatchSync(); + } + } + + } + + + private NamingFuzzyWatchRequest buildFuzzyWatchNamingRequest(NamingFuzzyWatchContext namingFuzzyWatchContext){ + NamingFuzzyWatchRequest namingFuzzyWatchRequest=new NamingFuzzyWatchRequest(); + namingFuzzyWatchRequest.setInitializing(namingFuzzyWatchContext.isInitializing()); + namingFuzzyWatchRequest.setNamespace(namingGrpcClientProxy.getNamespaceId()); + namingFuzzyWatchRequest.setReceivedGroupKeys(namingFuzzyWatchContext.getReceivedServiceKeys()); + namingFuzzyWatchRequest.setGroupKeyPattern(namingFuzzyWatchContext.getGroupKeyPattern()); + if (namingFuzzyWatchContext.isDiscard()&&namingFuzzyWatchContext.getNamingFuzzyWatchers().isEmpty()){ + namingFuzzyWatchRequest.setWatchType(WATCH_TYPE_WATCH); + }else{ + namingFuzzyWatchRequest.setWatchType(WATCH_TYPE_CANCEL_WATCH); + } + return namingFuzzyWatchRequest; + } + + public Map getFuzzyMatchContextMap() { + return fuzzyMatchContextMap; + } + + @Override + public void onEvent(NamingFuzzyWatchNotifyEvent event) { + if (!event.scope().equals(notifierEventScope)){ + return; + } + + 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 ; + } + namingFuzzyWatchContext.notifyFuzzyWatchers(serviceKey,changedType,syncType,watchUuid); + + } + + @Override + public Class subscribeType() { + return NamingFuzzyWatchNotifyEvent.class; + } + + public String getNotifierEventScope() { + return notifierEventScope; + } +} diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/event/FuzzyWatchNotifyEvent.java b/client/src/main/java/com/alibaba/nacos/client/naming/event/NamingFuzzyWatchNotifyEvent.java similarity index 54% rename from client/src/main/java/com/alibaba/nacos/client/naming/event/FuzzyWatchNotifyEvent.java rename to client/src/main/java/com/alibaba/nacos/client/naming/event/NamingFuzzyWatchNotifyEvent.java index 297cdc993f5..6f6b7bbe706 100644 --- a/client/src/main/java/com/alibaba/nacos/client/naming/event/FuzzyWatchNotifyEvent.java +++ b/client/src/main/java/com/alibaba/nacos/client/naming/event/NamingFuzzyWatchNotifyEvent.java @@ -23,36 +23,43 @@ * * @author tanyongquan */ -public class FuzzyWatchNotifyEvent extends Event { +public class NamingFuzzyWatchNotifyEvent extends Event { private final String scope; + private String watcherUuid; + private String serviceKey; private String pattern; private final String changedType; + private final String syncType; - public FuzzyWatchNotifyEvent(String scope, - String pattern, String serviceKey,String changedType){ + private NamingFuzzyWatchNotifyEvent(String scope, String pattern, String serviceKey, String changedType, String syncType,String watcherUuid) { this.scope = scope; - this.pattern=pattern; - this.serviceKey=serviceKey; - this.changedType=changedType; + this.pattern = pattern; + this.serviceKey = serviceKey; + this.changedType = changedType; + this.syncType = syncType; + this.watcherUuid=watcherUuid; } - public static FuzzyWatchNotifyEvent build(String eventScope, - String pattern, String serviceKey,String changedType) { - return new FuzzyWatchNotifyEvent(eventScope,pattern, serviceKey, changedType); + + public static NamingFuzzyWatchNotifyEvent build(String eventScope, String pattern, String serviceKey, String changedType, + String syncType) { + return new NamingFuzzyWatchNotifyEvent(eventScope, pattern, serviceKey, changedType, syncType,null); + } + + public static NamingFuzzyWatchNotifyEvent build(String eventScope, String pattern, String serviceKey, String changedType, + String syncType,String watcherUuid) { + return new NamingFuzzyWatchNotifyEvent(eventScope, pattern, serviceKey, changedType, syncType,watcherUuid); } public String getPattern() { return pattern; } - public void setPattern(String pattern) { - this.pattern = pattern; - } public String getChangedType() { return changedType; @@ -63,11 +70,19 @@ public String scope() { return this.scope; } + public String getWatcherUuid() { + return watcherUuid; + } + public String getServiceKey() { return serviceKey; } - public void setServiceKey(String serviceKey) { - this.serviceKey = serviceKey; + public String getScope() { + return scope; + } + + public String getSyncType() { + return syncType; } } diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/remote/NamingClientProxyDelegate.java b/client/src/main/java/com/alibaba/nacos/client/naming/remote/NamingClientProxyDelegate.java index 47bdd0f7146..b741a8f7f7f 100644 --- a/client/src/main/java/com/alibaba/nacos/client/naming/remote/NamingClientProxyDelegate.java +++ b/client/src/main/java/com/alibaba/nacos/client/naming/remote/NamingClientProxyDelegate.java @@ -26,7 +26,7 @@ import com.alibaba.nacos.api.selector.AbstractSelector; import com.alibaba.nacos.client.env.NacosClientProperties; import com.alibaba.nacos.client.naming.cache.ServiceInfoHolder; -import com.alibaba.nacos.client.naming.cache.FuzzyWatchServiceListHolder; +import com.alibaba.nacos.client.naming.cache.NamingFuzzyWatchServiceListHolder; import com.alibaba.nacos.client.naming.core.NamingServerListManager; import com.alibaba.nacos.client.naming.core.ServiceInfoUpdateService; import com.alibaba.nacos.client.naming.event.InstancesChangeNotifier; @@ -59,8 +59,6 @@ public class NamingClientProxyDelegate implements NamingClientProxy { private final ServiceInfoUpdateService serviceInfoUpdateService; private final ServiceInfoHolder serviceInfoHolder; - - private final FuzzyWatchServiceListHolder fuzzyWatchServiceListHolder; private final NamingHttpClientProxy httpClientProxy; @@ -70,20 +68,20 @@ public class NamingClientProxyDelegate implements NamingClientProxy { private ScheduledExecutorService executorService; - public NamingClientProxyDelegate(String namespace, ServiceInfoHolder serviceInfoHolder, FuzzyWatchServiceListHolder fuzzyWatchServiceListHolder, - NacosClientProperties properties, InstancesChangeNotifier changeNotifier) throws NacosException { + + public NamingClientProxyDelegate(String namespace, ServiceInfoHolder serviceInfoHolder, + NacosClientProperties properties, InstancesChangeNotifier changeNotifier,NamingFuzzyWatchServiceListHolder namingFuzzyWatchServiceListHolder) throws NacosException { this.serviceInfoUpdateService = new ServiceInfoUpdateService(properties, serviceInfoHolder, this, changeNotifier); this.serverListManager = new NamingServerListManager(properties, namespace); this.serverListManager.start(); this.serviceInfoHolder = serviceInfoHolder; - this.fuzzyWatchServiceListHolder = fuzzyWatchServiceListHolder; this.securityProxy = new SecurityProxy(this.serverListManager, NamingHttpClientManager.getInstance().getNacosRestTemplate()); initSecurityProxy(properties); this.httpClientProxy = new NamingHttpClientProxy(namespace, securityProxy, serverListManager, properties); this.grpcClientProxy = new NamingGrpcClientProxy(namespace, securityProxy, serverListManager, properties, - serviceInfoHolder, fuzzyWatchServiceListHolder); + serviceInfoHolder, namingFuzzyWatchServiceListHolder); } private void initSecurityProxy(NacosClientProperties properties) { 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 new file mode 100644 index 00000000000..f39a555e805 --- /dev/null +++ b/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/NamingFuzzyWatchNotifyRequestHandler.java @@ -0,0 +1,113 @@ +/* + * 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.remote.gprc; + +import com.alibaba.nacos.api.common.Constants; +import com.alibaba.nacos.api.naming.remote.request.NamingFuzzyWatchChangeNotifyRequest; +import com.alibaba.nacos.api.naming.remote.request.NamingFuzzyWatchSyncRequest; +import com.alibaba.nacos.api.naming.remote.response.NamingFuzzyWatchChangeNotifyResponse; +import com.alibaba.nacos.api.naming.utils.NamingUtils; +import com.alibaba.nacos.api.remote.request.Request; +import com.alibaba.nacos.api.remote.response.Response; +import com.alibaba.nacos.client.naming.cache.NamingFuzzyWatchServiceListHolder; +import com.alibaba.nacos.client.naming.cache.NamingFuzzyWatchContext; +import com.alibaba.nacos.client.naming.event.NamingFuzzyWatchNotifyEvent; +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 java.util.Collection; + +import static com.alibaba.nacos.api.common.Constants.FUZZY_WATCH_RESOURCE_CHANGED; + +public class NamingFuzzyWatchNotifyRequestHandler implements ServerRequestHandler { + + + NamingFuzzyWatchServiceListHolder namingFuzzyWatchServiceListHolder; + + public NamingFuzzyWatchNotifyRequestHandler(NamingFuzzyWatchServiceListHolder namingFuzzyWatchServiceListHolder){ + this.namingFuzzyWatchServiceListHolder = namingFuzzyWatchServiceListHolder; + } + + @Override + public Response requestReply(Request request, Connection connection) { + + if (request instanceof NamingFuzzyWatchSyncRequest) { + NamingFuzzyWatchSyncRequest watchNotifySyncRequest = (NamingFuzzyWatchSyncRequest) request; + NamingFuzzyWatchContext namingFuzzyWatchContext = namingFuzzyWatchServiceListHolder.getFuzzyMatchContextMap().get(watchNotifySyncRequest.getGroupKeyPattern()); + if (namingFuzzyWatchContext!=null){ + Collection serviceKeys = watchNotifySyncRequest.getContexts(); + if (watchNotifySyncRequest.getSyncType().equals(Constants.FUZZY_WATCH_INIT_NOTIFY)||watchNotifySyncRequest.getSyncType().equals(Constants.FUZZY_WATCH_DIFF_SYNC_NOTIFY)){ + for (NamingFuzzyWatchSyncRequest.Context serviceKey : serviceKeys) { + // may have a 'change event' sent to client before 'init event' + if (namingFuzzyWatchContext.addReceivedServiceKey(serviceKey.getServiceKey())) { + NotifyCenter.publishEvent(NamingFuzzyWatchNotifyEvent.build(namingFuzzyWatchServiceListHolder.getNotifierEventScope(), + watchNotifySyncRequest.getGroupKeyPattern(),serviceKey.getServiceKey(),serviceKey.getChangedType(), watchNotifySyncRequest.getSyncType())); + } + } + }else if(watchNotifySyncRequest.getSyncType().equals(Constants.FINISH_FUZZY_WATCH_INIT_NOTIFY)){ + namingFuzzyWatchContext.markInitializationComplete(); + } + } + + return new NamingFuzzyWatchChangeNotifyResponse(); + + } else if (request instanceof NamingFuzzyWatchChangeNotifyRequest) { + NamingFuzzyWatchChangeNotifyRequest notifyChangeRequest = (NamingFuzzyWatchChangeNotifyRequest) request; + String[] serviceKeyItems = NamingUtils.parseServiceKey(notifyChangeRequest.getServiceKey()); + String namespace=serviceKeyItems[0]; + String groupName=serviceKeyItems[1]; + String serviceName=serviceKeyItems[2]; + + Collection matchedPattern = FuzzyGroupKeyPattern.filterMatchedPatterns( + namingFuzzyWatchServiceListHolder.getFuzzyMatchContextMap().keySet(),namespace,groupName,serviceName); + String serviceChangeType = notifyChangeRequest.getChangedType(); + + switch (serviceChangeType) { + case Constants.ServiceChangedType.ADD_SERVICE: + case Constants.ServiceChangedType.INSTANCE_CHANGED: + for (String pattern : matchedPattern) { + NamingFuzzyWatchContext namingFuzzyWatchContext = namingFuzzyWatchServiceListHolder.getFuzzyMatchContextMap().get(pattern); + if (namingFuzzyWatchContext != null && namingFuzzyWatchContext.getReceivedServiceKeys().add(((NamingFuzzyWatchChangeNotifyRequest) request).getServiceKey())) { + //publish local service add event + NotifyCenter.publishEvent( + NamingFuzzyWatchNotifyEvent.build(namingFuzzyWatchServiceListHolder.getNotifierEventScope(), + pattern,notifyChangeRequest.getServiceKey(),Constants.ServiceChangedType.ADD_SERVICE, + FUZZY_WATCH_RESOURCE_CHANGED)); + } + } + break; + case Constants.ServiceChangedType.DELETE_SERVICE: + for (String pattern : matchedPattern) { + NamingFuzzyWatchContext namingFuzzyWatchContext = namingFuzzyWatchServiceListHolder.getFuzzyMatchContextMap().get(pattern); + if (namingFuzzyWatchContext != null && namingFuzzyWatchContext.getReceivedServiceKeys().remove(notifyChangeRequest.getServiceKey())) { + NotifyCenter.publishEvent( + NamingFuzzyWatchNotifyEvent.build(namingFuzzyWatchServiceListHolder.getNotifierEventScope(), + pattern, notifyChangeRequest.getServiceKey(),Constants.ServiceChangedType.DELETE_SERVICE, + FUZZY_WATCH_RESOURCE_CHANGED)); + } + } + break; + default: + break; + } + return new NamingFuzzyWatchChangeNotifyResponse(); + } + return null; + } +} diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/NamingFuzzyWatchRequestHandler.java b/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/NamingFuzzyWatchRequestHandler.java deleted file mode 100644 index bde30f71939..00000000000 --- a/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/NamingFuzzyWatchRequestHandler.java +++ /dev/null @@ -1,92 +0,0 @@ -package com.alibaba.nacos.client.naming.remote.gprc; - -import com.alibaba.nacos.api.common.Constants; -import com.alibaba.nacos.api.naming.remote.request.AbstractFuzzyWatchNotifyRequest; -import com.alibaba.nacos.api.naming.remote.request.FuzzyWatchNotifyChangeRequest; -import com.alibaba.nacos.api.naming.remote.request.FuzzyWatchNotifyInitRequest; -import com.alibaba.nacos.api.naming.remote.response.NotifyFuzzyWatcherResponse; -import com.alibaba.nacos.api.naming.utils.NamingUtils; -import com.alibaba.nacos.api.remote.request.Request; -import com.alibaba.nacos.api.remote.response.Response; -import com.alibaba.nacos.client.naming.cache.FuzzyWatchServiceListHolder; -import com.alibaba.nacos.client.naming.cache.NamingFuzzyWatchContext; -import com.alibaba.nacos.client.naming.event.FuzzyWatchNotifyEvent; -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 java.util.Collection; - -public class NamingFuzzyWatchRequestHandler implements ServerRequestHandler { - - - FuzzyWatchServiceListHolder fuzzyWatchServiceListHolder; - - public NamingFuzzyWatchRequestHandler(FuzzyWatchServiceListHolder fuzzyWatchServiceListHolder){ - this.fuzzyWatchServiceListHolder=fuzzyWatchServiceListHolder; - } - - @Override - public Response requestReply(Request request, Connection connection) { - if (request instanceof FuzzyWatchNotifyInitRequest) { - FuzzyWatchNotifyInitRequest watchNotifyInitRequest = (FuzzyWatchNotifyInitRequest) request; - NamingFuzzyWatchContext namingFuzzyWatchContext = fuzzyWatchServiceListHolder.getFuzzyMatchContextMap().get(watchNotifyInitRequest.getPattern()); - if (namingFuzzyWatchContext!=null){ - Collection serviceKeys = watchNotifyInitRequest.getServiceKeys(); - if (watchNotifyInitRequest.getChangedType().equals(Constants.FUZZY_WATCH_INIT_NOTIFY)){ - for (String serviceKey : serviceKeys) { - // may have a 'change event' sent to client before 'init event' - if (namingFuzzyWatchContext.addReceivedServiceKey(serviceKey)) { - NotifyCenter.publishEvent(FuzzyWatchNotifyEvent.build(fuzzyWatchServiceListHolder.getNotifierEventScope(), - watchNotifyInitRequest.getPattern(),serviceKey, watchNotifyInitRequest.getChangedType())); - } - } - }else if(watchNotifyInitRequest.getChangedType().equals(Constants.FINISH_FUZZY_WATCH_INIT_NOTIFY)){ - namingFuzzyWatchContext.markInitializationComplete(); - } - } - - return new NotifyFuzzyWatcherResponse(); - - } else if (request instanceof FuzzyWatchNotifyChangeRequest) { - FuzzyWatchNotifyChangeRequest notifyChangeRequest = (FuzzyWatchNotifyChangeRequest) request; - String[] serviceKeyItems = NamingUtils.parseServiceKey(notifyChangeRequest.getServiceKey()); - String namespace=serviceKeyItems[0]; - String groupName=serviceKeyItems[1]; - String serviceName=serviceKeyItems[2]; - - Collection matchedPattern = FuzzyGroupKeyPattern.filterMatchedPatterns(fuzzyWatchServiceListHolder.getFuzzyMatchContextMap().keySet(),namespace,groupName,serviceName); - String serviceChangeType = notifyChangeRequest.getChangedType(); - - switch (serviceChangeType) { - case Constants.ServiceChangedType.ADD_SERVICE: - case Constants.ServiceChangedType.INSTANCE_CHANGED: - for (String pattern : matchedPattern) { - NamingFuzzyWatchContext namingFuzzyWatchContext = fuzzyWatchServiceListHolder.getFuzzyMatchContextMap().get(pattern); - if (namingFuzzyWatchContext != null && namingFuzzyWatchContext.getReceivedServiceKeys().add(((FuzzyWatchNotifyChangeRequest) request).getServiceKey())) { - //publish local service add event - NotifyCenter.publishEvent( - FuzzyWatchNotifyEvent.build(fuzzyWatchServiceListHolder.getNotifierEventScope(), - pattern,notifyChangeRequest.getServiceKey(),Constants.ServiceChangedType.ADD_SERVICE)); - } - } - break; - case Constants.ServiceChangedType.DELETE_SERVICE: - for (String pattern : matchedPattern) { - NamingFuzzyWatchContext namingFuzzyWatchContext = fuzzyWatchServiceListHolder.getFuzzyMatchContextMap().get(pattern); - if (namingFuzzyWatchContext != null && namingFuzzyWatchContext.getReceivedServiceKeys().remove(notifyChangeRequest.getServiceKey())) { - NotifyCenter.publishEvent( - FuzzyWatchNotifyEvent.build(fuzzyWatchServiceListHolder.getNotifierEventScope(), - pattern, notifyChangeRequest.getServiceKey(),Constants.ServiceChangedType.DELETE_SERVICE)); - } - } - break; - default: - break; - } - return new NotifyFuzzyWatcherResponse(); - } - return null; - } -} diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/NamingGrpcClientProxy.java b/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/NamingGrpcClientProxy.java index 0435f076bc5..a223f911770 100644 --- a/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/NamingGrpcClientProxy.java +++ b/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/NamingGrpcClientProxy.java @@ -48,8 +48,7 @@ import com.alibaba.nacos.api.selector.SelectorType; import com.alibaba.nacos.client.env.NacosClientProperties; import com.alibaba.nacos.client.monitor.MetricsMonitor; -import com.alibaba.nacos.client.naming.cache.FuzzyWatchServiceListHolder; -import com.alibaba.nacos.client.naming.cache.NamingFuzzyWatchContext; +import com.alibaba.nacos.client.naming.cache.NamingFuzzyWatchServiceListHolder; import com.alibaba.nacos.client.naming.cache.ServiceInfoHolder; import com.alibaba.nacos.client.address.ServerListChangeEvent; import com.alibaba.nacos.client.naming.remote.AbstractNamingClientProxy; @@ -66,7 +65,6 @@ import com.alibaba.nacos.common.remote.client.RpcClientTlsConfigFactory; import com.alibaba.nacos.common.remote.client.ServerListFactory; import com.alibaba.nacos.common.utils.CollectionUtils; -import com.alibaba.nacos.common.utils.FuzzyGroupKeyPattern; import com.alibaba.nacos.common.utils.JacksonUtils; import java.util.ArrayList; @@ -101,7 +99,7 @@ public class NamingGrpcClientProxy extends AbstractNamingClientProxy { public NamingGrpcClientProxy(String namespaceId, SecurityProxy securityProxy, ServerListFactory serverListFactory, NacosClientProperties properties, ServiceInfoHolder serviceInfoHolder, - FuzzyWatchServiceListHolder fuzzyWatchServiceListHolder) throws NacosException { + NamingFuzzyWatchServiceListHolder namingFuzzyWatchServiceListHolder) throws NacosException { super(securityProxy); this.namespaceId = namespaceId; this.uuid = UUID.randomUUID().toString(); @@ -110,20 +108,23 @@ public NamingGrpcClientProxy(String namespaceId, SecurityProxy securityProxy, Se labels.put(RemoteConstants.LABEL_SOURCE, RemoteConstants.LABEL_SOURCE_SDK); labels.put(RemoteConstants.LABEL_MODULE, RemoteConstants.LABEL_MODULE_NAMING); labels.put(Constants.APPNAME, AppNameUtils.getAppName()); + namingFuzzyWatchServiceListHolder.registerNamingGrpcClientProxy(this); this.rpcClient = RpcClientFactory.createClient(uuid, ConnectionType.GRPC, labels, RpcClientTlsConfigFactory.getInstance().createSdkConfig(properties.asProperties())); - this.redoService = new NamingGrpcRedoService(this,fuzzyWatchServiceListHolder, properties); + this.redoService = new NamingGrpcRedoService(this, namingFuzzyWatchServiceListHolder, properties); NAMING_LOGGER.info("Create naming rpc client for uuid->{}", uuid); - start(serverListFactory, serviceInfoHolder, fuzzyWatchServiceListHolder); + start(serverListFactory, serviceInfoHolder, namingFuzzyWatchServiceListHolder); } private void start(ServerListFactory serverListFactory, ServiceInfoHolder serviceInfoHolder, - FuzzyWatchServiceListHolder fuzzyWatchServiceListHolder) throws NacosException { + NamingFuzzyWatchServiceListHolder namingFuzzyWatchServiceListHolder) throws NacosException { rpcClient.serverListFactory(serverListFactory); rpcClient.registerConnectionListener(redoService); rpcClient.registerServerRequestHandler(new NamingPushRequestHandler(serviceInfoHolder)); - rpcClient.registerServerRequestHandler(new NamingFuzzyWatchRequestHandler(fuzzyWatchServiceListHolder)); + rpcClient.registerServerRequestHandler(new NamingFuzzyWatchNotifyRequestHandler( + namingFuzzyWatchServiceListHolder)); rpcClient.start(); + namingFuzzyWatchServiceListHolder.start(); NotifyCenter.registerSubscriber(this); } @@ -447,7 +448,6 @@ public boolean isAbilitySupportedByServer(AbilityKey abilityKey) { return rpcClient.getConnectionAbility(abilityKey) == AbilityStatus.SUPPORTED; } - /** * Execute unsubscribe operation. * @@ -455,17 +455,14 @@ public boolean isAbilitySupportedByServer(AbilityKey abilityKey) { * @throws NacosException nacos exception */ public NamingFuzzyWatchResponse fuzzyWatchRequest(NamingFuzzyWatchRequest namingFuzzyWatchRequest) throws NacosException { - return requestToServer(namingFuzzyWatchRequest, NamingFuzzyWatchResponse.class); } - private T requestToServer(Request request, Class responseClass) throws NacosException { Response response = null; try { if (request instanceof AbstractNamingRequest){ - request.putAllHeader( getSecurityHeaders(((AbstractNamingRequest)request).getNamespace(), ((AbstractNamingRequest)request).getGroupName(), ((AbstractNamingRequest)request).getServiceName())); }else if(request instanceof NamingFuzzyWatchRequest){ diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/redo/FuzzyWatchSyncTask.java b/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/redo/FuzzyWatchSyncTask.java index 393cdcb2371..bf245962838 100644 --- a/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/redo/FuzzyWatchSyncTask.java +++ b/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/redo/FuzzyWatchSyncTask.java @@ -1,10 +1,25 @@ +/* + * 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.remote.gprc.redo; import com.alibaba.nacos.api.exception.NacosException; -import com.alibaba.nacos.api.naming.remote.NamingRemoteConstants; import com.alibaba.nacos.api.naming.remote.request.NamingFuzzyWatchRequest; import com.alibaba.nacos.api.naming.remote.response.NamingFuzzyWatchResponse; -import com.alibaba.nacos.client.naming.cache.FuzzyWatchServiceListHolder; +import com.alibaba.nacos.client.naming.cache.NamingFuzzyWatchServiceListHolder; import com.alibaba.nacos.client.naming.cache.NamingFuzzyWatchContext; import com.alibaba.nacos.client.naming.remote.gprc.NamingGrpcClientProxy; import com.alibaba.nacos.common.task.AbstractExecuteTask; @@ -12,20 +27,23 @@ import java.util.Iterator; import java.util.Map; +import static com.alibaba.nacos.api.common.Constants.WATCH_TYPE_CANCEL_WATCH; +import static com.alibaba.nacos.api.common.Constants.WATCH_TYPE_WATCH; + public class FuzzyWatchSyncTask extends AbstractExecuteTask { - private FuzzyWatchServiceListHolder fuzzyWatchServiceListHolder; + private NamingFuzzyWatchServiceListHolder namingFuzzyWatchServiceListHolder; private final NamingGrpcClientProxy clientProxy; - public FuzzyWatchSyncTask(FuzzyWatchServiceListHolder fuzzyWatchServiceListHolder,NamingGrpcClientProxy clientProxy ){ - this.fuzzyWatchServiceListHolder=fuzzyWatchServiceListHolder; + public FuzzyWatchSyncTask(NamingFuzzyWatchServiceListHolder namingFuzzyWatchServiceListHolder,NamingGrpcClientProxy clientProxy ){ + this.namingFuzzyWatchServiceListHolder = namingFuzzyWatchServiceListHolder; this.clientProxy=clientProxy; } @Override public void run() { - Iterator> iterator = fuzzyWatchServiceListHolder.getFuzzyMatchContextMap() + Iterator> iterator = namingFuzzyWatchServiceListHolder.getFuzzyMatchContextMap() .entrySet().iterator(); while (iterator.hasNext()){ Map.Entry next = iterator.next(); @@ -37,15 +55,15 @@ public void run() { namingFuzzyWatchRequest.setReceivedGroupKeys(namingFuzzyWatchContext.getReceivedServiceKeys()); namingFuzzyWatchRequest.setGroupKeyPattern(next.getKey()); if (namingFuzzyWatchContext.isDiscard()&&namingFuzzyWatchContext.getNamingFuzzyWatchers().isEmpty()){ - namingFuzzyWatchRequest.setWatchType(NamingRemoteConstants.CANCEL_FUZZY_WATCH_SERVICE); + namingFuzzyWatchRequest.setWatchType(WATCH_TYPE_WATCH); }else{ - namingFuzzyWatchRequest.setWatchType(NamingRemoteConstants.FUZZY_WATCH_SERVICE); + namingFuzzyWatchRequest.setWatchType(WATCH_TYPE_CANCEL_WATCH); } try { NamingFuzzyWatchResponse namingFuzzyWatchResponse = clientProxy.fuzzyWatchRequest( namingFuzzyWatchRequest); - if (namingFuzzyWatchResponse.isSuccess()&&NamingRemoteConstants.CANCEL_FUZZY_WATCH_SERVICE.equals(namingFuzzyWatchRequest.getWatchType())){ - fuzzyWatchServiceListHolder.removePatternMatchCache(namingFuzzyWatchRequest.getGroupKeyPattern()); + if (namingFuzzyWatchResponse.isSuccess()&&WATCH_TYPE_CANCEL_WATCH.equals(namingFuzzyWatchRequest.getWatchType())){ + namingFuzzyWatchServiceListHolder.removePatternMatchCache(namingFuzzyWatchRequest.getGroupKeyPattern()); } } catch (NacosException e) { throw new RuntimeException(e); diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/redo/NamingGrpcRedoService.java b/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/redo/NamingGrpcRedoService.java index 71d4cd48928..7527b9d00ce 100644 --- a/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/redo/NamingGrpcRedoService.java +++ b/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/redo/NamingGrpcRedoService.java @@ -22,7 +22,7 @@ import com.alibaba.nacos.api.naming.pojo.ServiceInfo; import com.alibaba.nacos.api.naming.utils.NamingUtils; import com.alibaba.nacos.client.env.NacosClientProperties; -import com.alibaba.nacos.client.naming.cache.FuzzyWatchServiceListHolder; +import com.alibaba.nacos.client.naming.cache.NamingFuzzyWatchServiceListHolder; import com.alibaba.nacos.client.naming.remote.gprc.NamingGrpcClientProxy; import com.alibaba.nacos.client.naming.remote.gprc.redo.data.BatchInstanceRedoData; import com.alibaba.nacos.client.naming.remote.gprc.redo.data.InstanceRedoData; @@ -60,15 +60,16 @@ public class NamingGrpcRedoService implements ConnectionEventListener { private final ConcurrentMap subscribes = new ConcurrentHashMap<>(); - private final FuzzyWatchServiceListHolder fuzzyWatchServiceListHolder; + private final NamingFuzzyWatchServiceListHolder namingFuzzyWatchServiceListHolder; private final ScheduledExecutorService redoExecutor; private volatile boolean connected = false; - public NamingGrpcRedoService(NamingGrpcClientProxy clientProxy,FuzzyWatchServiceListHolder fuzzyWatchServiceListHolder, NacosClientProperties properties) { + public NamingGrpcRedoService(NamingGrpcClientProxy clientProxy, + NamingFuzzyWatchServiceListHolder namingFuzzyWatchServiceListHolder, NacosClientProperties properties) { setProperties(properties); - this.fuzzyWatchServiceListHolder=fuzzyWatchServiceListHolder; + this.namingFuzzyWatchServiceListHolder = namingFuzzyWatchServiceListHolder; this.redoExecutor = new ScheduledThreadPoolExecutor(redoThreadCount, new NameThreadFactory(REDO_THREAD_NAME)); this.redoExecutor.scheduleWithFixedDelay(new RedoScheduledTask(clientProxy, this), redoDelayTime, redoDelayTime, TimeUnit.MILLISECONDS); @@ -104,8 +105,8 @@ public void onDisConnect(Connection connection) { synchronized (subscribes) { subscribes.values().forEach(subscriberRedoData -> subscriberRedoData.setRegistered(false)); } - synchronized (fuzzyWatchServiceListHolder) { - fuzzyWatchServiceListHolder.getFuzzyMatchContextMap().values().forEach(fuzzyWatcherContext -> fuzzyWatcherContext.setConsistentWithServer(false)); + synchronized (namingFuzzyWatchServiceListHolder) { + namingFuzzyWatchServiceListHolder.resetConsistenceStatus(); } LogUtils.NAMING_LOGGER.warn("mark to redo completed"); } diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/remote/http/NamingHttpClientProxy.java b/client/src/main/java/com/alibaba/nacos/client/naming/remote/http/NamingHttpClientProxy.java index 34353a024ef..a6568fc4010 100644 --- a/client/src/main/java/com/alibaba/nacos/client/naming/remote/http/NamingHttpClientProxy.java +++ b/client/src/main/java/com/alibaba/nacos/client/naming/remote/http/NamingHttpClientProxy.java @@ -31,7 +31,6 @@ import com.alibaba.nacos.client.address.ServerListChangeEvent; import com.alibaba.nacos.client.env.NacosClientProperties; import com.alibaba.nacos.client.monitor.MetricsMonitor; -import com.alibaba.nacos.client.naming.cache.NamingFuzzyWatchContext; import com.alibaba.nacos.client.naming.core.NamingServerListManager; import com.alibaba.nacos.client.naming.remote.AbstractNamingClientProxy; import com.alibaba.nacos.client.naming.utils.CollectionUtils; @@ -57,7 +56,6 @@ import java.util.List; import java.util.Map; import java.util.Random; -import java.util.concurrent.Future; import static com.alibaba.nacos.client.utils.LogUtils.NAMING_LOGGER; import static com.alibaba.nacos.common.constant.RequestUrlConstants.HTTPS_PREFIX; @@ -330,21 +328,6 @@ public boolean isSubscribed(String serviceName, String groupName, String cluster return true; } - @Override - public NamingFuzzyWatchContext initFuzzyWatchContext(String serviceNamePattern, String groupNamePattern) throws NacosException { - throw new UnsupportedOperationException("Do not support fuzzy watch services by UDP, please use gRPC replaced."); - } - - @Override - public void cancelFuzzyWatch(String serviceNamePattern, String groupNamePattern) throws NacosException { - throw new UnsupportedOperationException("Do not support fuzzy watch service by UDP, please use gRPC replaced."); - } - - @Override - public boolean isFuzzyWatched(String serviceNamePattern, String groupNamePattern) { - throw new UnsupportedOperationException("Do not support fuzzy watch service by UDP, please use gRPC replaced."); - } - public String reqApi(String api, Map params, String method) throws NacosException { return reqApi(api, params, Collections.EMPTY_MAP, method); } diff --git a/client/src/main/java/com/alibaba/nacos/client/utils/ParamUtil.java b/client/src/main/java/com/alibaba/nacos/client/utils/ParamUtil.java index f6f8be2b4cc..4325d60adcc 100644 --- a/client/src/main/java/com/alibaba/nacos/client/utils/ParamUtil.java +++ b/client/src/main/java/com/alibaba/nacos/client/utils/ParamUtil.java @@ -62,8 +62,6 @@ public class ParamUtil { private static double perTaskConfigSize = 3000; - private static final String PER_TASK_CONTEXT_SIZE_KEY = "PER_TASK_CONTEXT_SIZE_KEY"; - private static final String NACOS_CLIENT_APP_KEY = "nacos.client.appKey"; private static final String BLANK_STR = ""; @@ -86,9 +84,6 @@ public class ParamUtil { private static final String DEFAULT_PER_TASK_CONFIG_SIZE_KEY = "3000"; - private static final String DEFAULT_PER_TASK_CONTEXT_SIZE_KEY = "3000"; - - private static double perTaskContextSize = 3000; private static final int DESENSITISE_PARAMETER_MIN_LENGTH = 2; private static final int DESENSITISE_PARAMETER_KEEP_ONE_CHAR_LENGTH = 8; @@ -115,9 +110,6 @@ public class ParamUtil { perTaskConfigSize = initPerTaskConfigSize(); LOGGER.info("PER_TASK_CONFIG_SIZE: {}", perTaskConfigSize); - - perTaskContextSize = initPerTaskContextSize(); - LOGGER.info("PER_TASK_CONTEXT_SIZE: {}", perTaskContextSize); } private static int initConnectionTimeout() { @@ -154,16 +146,6 @@ private static double initPerTaskConfigSize() { } } - private static double initPerTaskContextSize() { - try { - return Double.parseDouble(NacosClientProperties.PROTOTYPE.getProperty(PER_TASK_CONTEXT_SIZE_KEY, - DEFAULT_PER_TASK_CONTEXT_SIZE_KEY)); - } catch (NumberFormatException e) { - LOGGER.error("[PER_TASK_CONTEXT_SIZE] PER_TASK_CONTEXT_SIZE invalid", e); - throw new IllegalArgumentException("invalid PER_TASK_CONTEXT_SIZE, expected value type double", e); - } - } - public static String getAppKey() { return appKey; } @@ -220,14 +202,6 @@ public static void setPerTaskConfigSize(double perTaskConfigSize) { ParamUtil.perTaskConfigSize = perTaskConfigSize; } - public static double getPerTaskContextSize() { - return perTaskContextSize; - } - - public static void setPerTaskContextSize(double perTaskContextSize) { - ParamUtil.perTaskContextSize = perTaskContextSize; - } - public static String getDefaultServerPort() { return serverPort; } @@ -283,10 +257,10 @@ public static String parsingEndpointRule(String endpointUrl) { if (StringUtils.isNotBlank(endpointUrlSource)) { endpointUrl = endpointUrlSource; } - + return StringUtils.isNotBlank(endpointUrl) ? endpointUrl : ""; } - + endpointUrl = endpointUrl.substring(endpointUrl.indexOf("${") + 2, endpointUrl.lastIndexOf("}")); int defStartOf = endpointUrl.indexOf(":"); String defaultEndpointUrl = null; @@ -294,13 +268,12 @@ public static String parsingEndpointRule(String endpointUrl) { defaultEndpointUrl = endpointUrl.substring(defStartOf + 1); endpointUrl = endpointUrl.substring(0, defStartOf); } - String endpointUrlSource = TemplateUtils.stringBlankAndThenExecute( NacosClientProperties.PROTOTYPE.getProperty(endpointUrl), () -> NacosClientProperties.PROTOTYPE.getProperty( PropertyKeyConst.SystemEnv.ALIBABA_ALIWARE_ENDPOINT_URL)); - + if (StringUtils.isBlank(endpointUrlSource)) { if (StringUtils.isNotBlank(defaultEndpointUrl)) { endpointUrl = defaultEndpointUrl; @@ -308,7 +281,7 @@ public static String parsingEndpointRule(String endpointUrl) { } else { endpointUrl = endpointUrlSource; } - + return StringUtils.isNotBlank(endpointUrl) ? endpointUrl : ""; } diff --git a/client/src/test/java/com/alibaba/nacos/client/config/NacosConfigServiceTest.java b/client/src/test/java/com/alibaba/nacos/client/config/NacosConfigServiceTest.java index b037c1fd34a..7f53968926e 100644 --- a/client/src/test/java/com/alibaba/nacos/client/config/NacosConfigServiceTest.java +++ b/client/src/test/java/com/alibaba/nacos/client/config/NacosConfigServiceTest.java @@ -206,32 +206,18 @@ public String getName() { public void notifyListenConfig() { // NOOP } - - @Override - public void notifyFuzzyListenConfig() { - // NOOP - } - + @Override public void executeConfigListen() { // NOOP } - - @Override - public void executeConfigFuzzyListen() throws NacosException { - // NOOP - } + @Override public void removeCache(String dataId, String group) { // NOOP } - @Override - public void removeFuzzyListenContext(String dataIdPattern, String group) { - // NOOP - } - @Override public ConfigResponse queryConfig(String dataId, String group, String tenant, long readTimeous, boolean notify) throws NacosException { diff --git a/client/src/test/java/com/alibaba/nacos/client/naming/remote/NamingClientProxyDelegateTest.java b/client/src/test/java/com/alibaba/nacos/client/naming/remote/NamingClientProxyDelegateTest.java index 828637aa3e7..5ab5ec969c6 100644 --- a/client/src/test/java/com/alibaba/nacos/client/naming/remote/NamingClientProxyDelegateTest.java +++ b/client/src/test/java/com/alibaba/nacos/client/naming/remote/NamingClientProxyDelegateTest.java @@ -28,7 +28,7 @@ import com.alibaba.nacos.api.selector.NoneSelector; import com.alibaba.nacos.client.env.NacosClientProperties; import com.alibaba.nacos.client.naming.cache.ServiceInfoHolder; -import com.alibaba.nacos.client.naming.cache.FuzzyWatchServiceListHolder; +import com.alibaba.nacos.client.naming.cache.NamingFuzzyWatchServiceListHolder; import com.alibaba.nacos.client.naming.event.InstancesChangeNotifier; import com.alibaba.nacos.client.naming.remote.gprc.NamingGrpcClientProxy; import com.alibaba.nacos.client.naming.remote.http.NamingHttpClientProxy; @@ -66,8 +66,8 @@ class NamingClientProxyDelegateTest { @Mock NamingGrpcClientProxy mockGrpcClient; - @Mock - FuzzyWatchServiceListHolder fuzzyWatchServiceListHolder; + @Mock + NamingFuzzyWatchServiceListHolder namingFuzzyWatchServiceListHolder; NamingClientProxyDelegate delegate; @@ -81,7 +81,7 @@ void setUp() throws NacosException, NoSuchFieldException, IllegalAccessException props.setProperty("serverAddr", "localhost"); nacosClientProperties = NacosClientProperties.PROTOTYPE.derive(props); notifier = new InstancesChangeNotifier(); - delegate = new NamingClientProxyDelegate(TEST_NAMESPACE, holder,fuzzyWatchServiceListHolder, nacosClientProperties, notifier); + delegate = new NamingClientProxyDelegate(TEST_NAMESPACE, holder, nacosClientProperties, notifier,namingFuzzyWatchServiceListHolder); Field grpcClientProxyField = NamingClientProxyDelegate.class.getDeclaredField("grpcClientProxy"); grpcClientProxyField.setAccessible(true); grpcClientProxyField.set(delegate, mockGrpcClient); diff --git a/client/src/test/java/com/alibaba/nacos/client/naming/remote/gprc/NamingGrpcClientProxyTest.java b/client/src/test/java/com/alibaba/nacos/client/naming/remote/gprc/NamingGrpcClientProxyTest.java index bd563c6c00f..0efeba8f6db 100644 --- a/client/src/test/java/com/alibaba/nacos/client/naming/remote/gprc/NamingGrpcClientProxyTest.java +++ b/client/src/test/java/com/alibaba/nacos/client/naming/remote/gprc/NamingGrpcClientProxyTest.java @@ -47,7 +47,7 @@ import com.alibaba.nacos.api.selector.NoneSelector; import com.alibaba.nacos.client.env.NacosClientProperties; import com.alibaba.nacos.client.naming.cache.ServiceInfoHolder; -import com.alibaba.nacos.client.naming.cache.FuzzyWatchServiceListHolder; +import com.alibaba.nacos.client.naming.cache.NamingFuzzyWatchServiceListHolder; import com.alibaba.nacos.client.address.ServerListChangeEvent; import com.alibaba.nacos.client.naming.remote.gprc.redo.NamingGrpcRedoService; import com.alibaba.nacos.client.security.SecurityProxy; @@ -122,7 +122,7 @@ class NamingGrpcClientProxyTest { private ServiceInfoHolder holder; @Mock - private FuzzyWatchServiceListHolder fuzzyWatchServiceListHolder; + private NamingFuzzyWatchServiceListHolder namingFuzzyWatchServiceListHolder; @Mock private RpcClient rpcClient; @@ -149,7 +149,8 @@ void setUp() throws NacosException, NoSuchFieldException, IllegalAccessException prop = new Properties(); final NacosClientProperties nacosClientProperties = NacosClientProperties.PROTOTYPE.derive(prop); - client = new NamingGrpcClientProxy(NAMESPACE_ID, proxy, factory, nacosClientProperties, holder,fuzzyWatchServiceListHolder); + client = new NamingGrpcClientProxy(NAMESPACE_ID, proxy, factory, nacosClientProperties, holder, + namingFuzzyWatchServiceListHolder); Field uuidField = NamingGrpcClientProxy.class.getDeclaredField("uuid"); uuidField.setAccessible(true); @@ -668,7 +669,7 @@ public void close() { rpcClient.set(client, rpc); rpc.serverListFactory(factory); - rpc.registerServerRequestHandler(new NamingPushRequestHandler(holder, fuzzyWatchServiceListHolder)); + rpc.registerServerRequestHandler(new NamingPushRequestHandler(holder)); Field listenerField = NamingGrpcClientProxy.class.getDeclaredField("redoService"); listenerField.setAccessible(true); NamingGrpcRedoService listener = (NamingGrpcRedoService) listenerField.get(client); @@ -704,7 +705,7 @@ public void close() { void testConfigAppNameLabels() throws Exception { final NacosClientProperties nacosClientProperties = NacosClientProperties.PROTOTYPE.derive(prop); client = new NamingGrpcClientProxy(NAMESPACE_ID, proxy, factory, nacosClientProperties, holder, - fuzzyWatchServiceListHolder); + namingFuzzyWatchServiceListHolder); Field rpcClientField = NamingGrpcClientProxy.class.getDeclaredField("rpcClient"); rpcClientField.setAccessible(true); RpcClient rpcClient = (RpcClient) rpcClientField.get(client); diff --git a/client/src/test/java/com/alibaba/nacos/client/naming/remote/gprc/NamingPushRequestHandlerTest.java b/client/src/test/java/com/alibaba/nacos/client/naming/remote/gprc/NamingPushRequestHandlerTest.java index ccba59738a3..40855f67ae6 100644 --- a/client/src/test/java/com/alibaba/nacos/client/naming/remote/gprc/NamingPushRequestHandlerTest.java +++ b/client/src/test/java/com/alibaba/nacos/client/naming/remote/gprc/NamingPushRequestHandlerTest.java @@ -25,7 +25,6 @@ import com.alibaba.nacos.api.remote.request.Request; import com.alibaba.nacos.api.remote.response.Response; import com.alibaba.nacos.client.naming.cache.ServiceInfoHolder; -import com.alibaba.nacos.client.naming.cache.FuzzyWatchServiceListHolder; import com.alibaba.nacos.client.naming.remote.TestConnection; import com.alibaba.nacos.common.remote.client.RpcClient; @@ -43,8 +42,7 @@ class NamingPushRequestHandlerTest { void testRequestReply() { //given ServiceInfoHolder holder = mock(ServiceInfoHolder.class); - FuzzyWatchServiceListHolder fuzzyWatchServiceListHolder = mock(FuzzyWatchServiceListHolder.class); - NamingPushRequestHandler handler = new NamingPushRequestHandler(holder, fuzzyWatchServiceListHolder); + NamingPushRequestHandler handler = new NamingPushRequestHandler(holder); ServiceInfo info = new ServiceInfo("name", "cluster1"); Request req = NotifySubscriberRequest.buildNotifySubscriberRequest(info); //when @@ -57,9 +55,8 @@ void testRequestReply() { @Test void testRequestReplyOtherType() { ServiceInfoHolder holder = mock(ServiceInfoHolder.class); - FuzzyWatchServiceListHolder fuzzyWatchServiceListHolder = mock(FuzzyWatchServiceListHolder.class); - NamingPushRequestHandler handler = new NamingPushRequestHandler(holder,fuzzyWatchServiceListHolder); + NamingPushRequestHandler handler = new NamingPushRequestHandler(holder); assertNull(handler.requestReply(new HealthCheckRequest(), new TestConnection(new RpcClient.ServerInfo()))); } } \ No newline at end of file diff --git a/client/src/test/java/com/alibaba/nacos/client/naming/remote/gprc/redo/NamingGrpcRedoServiceTest.java b/client/src/test/java/com/alibaba/nacos/client/naming/remote/gprc/redo/NamingGrpcRedoServiceTest.java index 61c31fc51ef..2608c89c284 100644 --- a/client/src/test/java/com/alibaba/nacos/client/naming/remote/gprc/redo/NamingGrpcRedoServiceTest.java +++ b/client/src/test/java/com/alibaba/nacos/client/naming/remote/gprc/redo/NamingGrpcRedoServiceTest.java @@ -19,6 +19,7 @@ import com.alibaba.nacos.api.PropertyKeyConst; import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.client.env.NacosClientProperties; +import com.alibaba.nacos.client.naming.cache.NamingFuzzyWatchServiceListHolder; import com.alibaba.nacos.client.naming.remote.TestConnection; import com.alibaba.nacos.client.naming.remote.gprc.NamingGrpcClientProxy; import com.alibaba.nacos.client.naming.remote.gprc.redo.data.BatchInstanceRedoData; @@ -56,13 +57,15 @@ class NamingGrpcRedoServiceTest { @Mock private NamingGrpcClientProxy clientProxy; + @Mock + private NamingFuzzyWatchServiceListHolder namingFuzzyWatchServiceListHolder; private NamingGrpcRedoService redoService; @BeforeEach void setUp() throws Exception { Properties prop = new Properties(); NacosClientProperties nacosClientProperties = NacosClientProperties.PROTOTYPE.derive(prop); - redoService = new NamingGrpcRedoService(clientProxy, nacosClientProperties); + redoService = new NamingGrpcRedoService(clientProxy, namingFuzzyWatchServiceListHolder, nacosClientProperties); ScheduledExecutorService redoExecutor = (ScheduledExecutorService) ReflectUtils.getFieldValue(redoService, "redoExecutor"); redoExecutor.shutdownNow(); @@ -95,7 +98,7 @@ void testCustomProperties() throws Exception { prop.setProperty(PropertyKeyConst.REDO_DELAY_THREAD_COUNT, "2"); NacosClientProperties nacosClientProperties = NacosClientProperties.PROTOTYPE.derive(prop); - NamingGrpcRedoService redoService = new NamingGrpcRedoService(clientProxy, nacosClientProperties); + NamingGrpcRedoService redoService = new NamingGrpcRedoService(clientProxy, namingFuzzyWatchServiceListHolder, nacosClientProperties); Field redoThreadCountField = NamingGrpcRedoService.class.getDeclaredField("redoThreadCount"); redoThreadCountField.setAccessible(true); diff --git a/common/src/main/java/com/alibaba/nacos/common/utils/FuzzyGroupKeyPattern.java b/common/src/main/java/com/alibaba/nacos/common/utils/FuzzyGroupKeyPattern.java index f777540f7dd..e70a0108869 100644 --- a/common/src/main/java/com/alibaba/nacos/common/utils/FuzzyGroupKeyPattern.java +++ b/common/src/main/java/com/alibaba/nacos/common/utils/FuzzyGroupKeyPattern.java @@ -150,8 +150,10 @@ private static boolean itemMatched(String pattern,String resource){ * list of ConfigState objects. * * @param basedGroupKeys The matched group keys set - * @param followedGroupKeys The client's existing group keys set - * @return The merged list of ConfigState objects representing the states to be added or removed + * @param followedGroupKeys The followed existing group keys set + * @return a different list of GroupKeyState objects representing the states which the followed sets should be added or removed + * GroupKeyState#exist true presents follow set should add,GroupKeyState#exist false presents follow set should removed. + * */ public static List diffGroupKeys(Set basedGroupKeys, Set followedGroupKeys) { diff --git a/common/src/main/java/com/alibaba/nacos/common/utils/GroupKey.java b/common/src/main/java/com/alibaba/nacos/common/utils/GroupKey.java deleted file mode 100644 index c821876ed55..00000000000 --- a/common/src/main/java/com/alibaba/nacos/common/utils/GroupKey.java +++ /dev/null @@ -1,140 +0,0 @@ -/* - * 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.common.utils; - -/** - * Synthesize the form of dataId+groupId. Escapes reserved characters in dataId and groupId. - * - * @author Nacos - */ -public class GroupKey { - - private static final char PLUS = '+'; - - private static final char PERCENT = '%'; - - private static final char TWO = '2'; - - private static final char B = 'B'; - - private static final char FIVE = '5'; - - public static String getKey(String dataId, String group) { - return getKey(dataId, group, ""); - } - - public static String getKey(String dataId, String group, String datumStr) { - return doGetKey(dataId, group, datumStr); - } - - public static String getKeyTenant(String dataId, String group, String tenant) { - return doGetKey(dataId, group, tenant); - } - - private static String doGetKey(String dataId, String group, String datumStr) { - if (StringUtils.isBlank(dataId)) { - throw new IllegalArgumentException("invalid dataId"); - } - if (StringUtils.isBlank(group)) { - throw new IllegalArgumentException("invalid group"); - } - StringBuilder sb = new StringBuilder(); - urlEncode(dataId, sb); - sb.append(PLUS); - urlEncode(group, sb); - if (StringUtils.isNotEmpty(datumStr)) { - sb.append(PLUS); - urlEncode(datumStr, sb); - } - - return sb.toString(); - } - - /** - * Parse key. - * - * @param groupKey group key - * @return parsed key - */ - public static String[] parseKey(String groupKey) { - StringBuilder sb = new StringBuilder(); - String dataId = null; - String group = null; - String tenant = null; - - for (int i = 0; i < groupKey.length(); ++i) { - char c = groupKey.charAt(i); - if (PLUS == c) { - if (null == dataId) { - dataId = sb.toString(); - sb.setLength(0); - } else if (null == group) { - group = sb.toString(); - sb.setLength(0); - } else { - throw new IllegalArgumentException("invalid groupkey:" + groupKey); - } - } else if (PERCENT == c) { - char next = groupKey.charAt(++i); - char nextnext = groupKey.charAt(++i); - if (TWO == next && B == nextnext) { - sb.append(PLUS); - } else if (TWO == next && FIVE == nextnext) { - sb.append(PERCENT); - } else { - throw new IllegalArgumentException("invalid groupkey:" + groupKey); - } - } else { - sb.append(c); - } - } - - if (group == null) { - group = sb.toString(); - } else { - tenant = sb.toString(); - } - - if (StringUtils.isBlank(dataId)) { - throw new IllegalArgumentException("invalid dataId"); - } - if (StringUtils.isBlank(group)) { - throw new IllegalArgumentException("invalid group"); - } - if (StringUtils.isBlank(tenant)) { - return new String[] {dataId, group}; - } - return new String[] {dataId, group, tenant}; - } - - /** - * + -> %2B % -> %25. - */ - static void urlEncode(String str, StringBuilder sb) { - for (int idx = 0; idx < str.length(); ++idx) { - char c = str.charAt(idx); - if (PLUS == c) { - sb.append("%2B"); - } else if (PERCENT == c) { - sb.append("%25"); - } else { - sb.append(c); - } - } - } - -} 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 0004c659386..88db2f01a45 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 @@ -83,7 +83,7 @@ public void setDerbyOpsEnabled(boolean derbyOpsEnabled) { protected void getConfigFromEnv() { maxPushRetryTimes = EnvUtil.getProperty("nacos.config.push.maxRetryTime", Integer.class, 50); pushTimeout = EnvUtil.getProperty("nacos.config.push.timeout", Long.class, 3000L); - pushTimeout = EnvUtil.getProperty("nacos.config.push.batchSize", Integer.class, 10); + batchSize = EnvUtil.getProperty("nacos.config.push.batchSize", Integer.class, 10); derbyOpsEnabled = EnvUtil.getProperty("nacos.config.derby.ops.enabled", Boolean.class, false); } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/model/event/ConfigBatchFuzzyListenEvent.java b/config/src/main/java/com/alibaba/nacos/config/server/model/event/ConfigFuzzyWatchEvent.java similarity index 95% rename from config/src/main/java/com/alibaba/nacos/config/server/model/event/ConfigBatchFuzzyListenEvent.java rename to config/src/main/java/com/alibaba/nacos/config/server/model/event/ConfigFuzzyWatchEvent.java index 15e6a1defff..c6a75210c50 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/model/event/ConfigBatchFuzzyListenEvent.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/model/event/ConfigFuzzyWatchEvent.java @@ -28,7 +28,7 @@ * @author stone-98 * @date 2024/3/5 */ -public class ConfigBatchFuzzyListenEvent extends Event { +public class ConfigFuzzyWatchEvent extends Event { private static final long serialVersionUID = 1953965691384930209L; @@ -60,7 +60,7 @@ public class ConfigBatchFuzzyListenEvent extends Event { * @param groupKeyPattern Pattern for matching group keys * @param isInitializing Flag indicating whether the client is initializing */ - public ConfigBatchFuzzyListenEvent(String connectionId, Set clientExistingGroupKeys, String groupKeyPattern, + public ConfigFuzzyWatchEvent(String connectionId, Set clientExistingGroupKeys, String groupKeyPattern, boolean isInitializing) { this.connectionId = connectionId; this.clientExistingGroupKeys = clientExistingGroupKeys; diff --git a/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigBatchFuzzyWatchRequestHandler.java b/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigBatchFuzzyWatchRequestHandler.java index 1aed1fa444a..190b6cc7355 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigBatchFuzzyWatchRequestHandler.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigBatchFuzzyWatchRequestHandler.java @@ -16,13 +16,13 @@ package com.alibaba.nacos.config.server.remote; -import com.alibaba.nacos.api.config.remote.request.ConfigBatchFuzzyWatchRequest; -import com.alibaba.nacos.api.config.remote.response.ConfigBatchFuzzyWatchResponse; +import com.alibaba.nacos.api.config.remote.request.ConfigFuzzyWatchRequest; +import com.alibaba.nacos.api.config.remote.response.ConfigFuzzyWatchResponse; import com.alibaba.nacos.api.exception.NacosException; 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.ConfigBatchFuzzyListenEvent; +import com.alibaba.nacos.config.server.model.event.ConfigFuzzyWatchEvent; import com.alibaba.nacos.config.server.service.ConfigFuzzyWatchContextService; import com.alibaba.nacos.core.control.TpsControl; import com.alibaba.nacos.core.paramcheck.ExtractorManager; @@ -34,6 +34,9 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; +import static com.alibaba.nacos.api.common.Constants.WATCH_TYPE_WATCH; +import static com.alibaba.nacos.api.common.Constants.WATCH_TYPE_CANCEL_WATCH; + import java.util.Set; /** @@ -49,7 +52,7 @@ */ @Component public class ConfigBatchFuzzyWatchRequestHandler - extends RequestHandler { + extends RequestHandler { /** * Context for managing fuzzy listen changes. @@ -73,25 +76,24 @@ public class ConfigBatchFuzzyWatchRequestHandler @TpsControl(pointName = "ConfigFuzzyWatch") @Secured(action = ActionTypes.READ, signType = SignType.CONFIG) @ExtractorManager.Extractor(rpcExtractor = ConfigBatchFuzzyListenRequestParamsExtractor.class) - public ConfigBatchFuzzyWatchResponse handle(ConfigBatchFuzzyWatchRequest request, RequestMeta meta) + public ConfigFuzzyWatchResponse handle(ConfigFuzzyWatchRequest request, RequestMeta meta) throws NacosException { String connectionId = StringPool.get(meta.getConnectionId()); - for (ConfigBatchFuzzyWatchRequest.Context context : request.getContexts()) { - String groupKeyPattern = context.getGroupKeyPattern(); - if (context.isListen()) { + 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 = context.getReceivedGroupKeys(); + Set clientExistingGroupKeys = request.getReceivedGroupKeys(); NotifyCenter.publishEvent( - new ConfigBatchFuzzyListenEvent(connectionId, clientExistingGroupKeys, groupKeyPattern, - context.isInitializing())); - } else { + new ConfigFuzzyWatchEvent(connectionId, clientExistingGroupKeys, groupKeyPattern, + request.isInitializing())); + } else if(WATCH_TYPE_CANCEL_WATCH.equals(request.getWatchType())) { // Remove client from the fuzzy listening context configFuzzyWatchContextService.removeFuzzyListen(groupKeyPattern, connectionId); } - } + // Return response - return new ConfigBatchFuzzyWatchResponse(); + return new ConfigFuzzyWatchResponse(); } } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigFuzzyWatchDiffNotifier.java b/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigFuzzyWatchSyncNotifier.java similarity index 91% rename from config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigFuzzyWatchDiffNotifier.java rename to config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigFuzzyWatchSyncNotifier.java index ca93658e5ab..47732ed9c06 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigFuzzyWatchDiffNotifier.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigFuzzyWatchSyncNotifier.java @@ -17,7 +17,7 @@ package com.alibaba.nacos.config.server.remote; import com.alibaba.nacos.api.common.Constants; -import com.alibaba.nacos.api.config.remote.request.FuzzyWatchDiffSyncRequest; +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; @@ -25,10 +25,9 @@ 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.ConfigBatchFuzzyListenEvent; +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.Connection; import com.alibaba.nacos.core.remote.ConnectionManager; import com.alibaba.nacos.core.remote.ConnectionMeta; @@ -53,8 +52,8 @@ * @author stone-98 * @date 2024/3/18 */ -@Component(value = "configFuzzyWatchDiffNotifier") -public class ConfigFuzzyWatchDiffNotifier extends Subscriber { +@Component(value = "configFuzzyWatchSyncNotifier") +public class ConfigFuzzyWatchSyncNotifier extends Subscriber { private static final String FUZZY_LISTEN_CONFIG_DIFF_PUSH = "FUZZY_LISTEN_CONFIG_DIFF_PUSH_COUNT"; @@ -70,7 +69,7 @@ public class ConfigFuzzyWatchDiffNotifier extends Subscriber configStateList : divideConfigStatesIntoBatches) { // Map config states to FuzzyListenNotifyDiffRequest.Context objects - Set contexts = configStateList.stream().map(state -> { - String[] parseKey = GroupKey.parseKey(state.getGroupKey()); - String dataId = parseKey[0]; - String group = parseKey[1]; - String tenant = parseKey.length > 2 ? parseKey[2] : Constants.DEFAULT_NAMESPACE_ID; + Set contexts = configStateList.stream().map(state -> { + String changeType = state.isExist() ? Constants.ConfigChangedType.ADD_CONFIG : Constants.ConfigChangedType.DELETE_CONFIG; - return FuzzyWatchDiffSyncRequest.Context.build(tenant, group, dataId, changeType); + return ConfigFuzzyWatchSyncRequest.Context.build(state.getGroupKey(), changeType); }).collect(Collectors.toSet()); // Build FuzzyListenNotifyDiffRequest with contexts and pattern - FuzzyWatchDiffSyncRequest request = - event.isInitializing() ? FuzzyWatchDiffSyncRequest.buildInitRequest(contexts, + ConfigFuzzyWatchSyncRequest request = + event.isInitializing() ? ConfigFuzzyWatchSyncRequest.buildInitRequest(contexts, event.getGroupKeyPattern()) - : FuzzyWatchDiffSyncRequest.buildDiffSyncRequest(contexts, event.getGroupKeyPattern()); + : ConfigFuzzyWatchSyncRequest.buildDiffSyncRequest(contexts, event.getGroupKeyPattern()); int maxPushRetryTimes = ConfigCommonConfig.getInstance().getMaxPushRetryTimes(); // Create RPC push task and push the request to the client @@ -184,7 +180,7 @@ public void onEvent(ConfigBatchFuzzyListenEvent event) { @Override public Class subscribeType() { - return ConfigBatchFuzzyListenEvent.class; + return ConfigFuzzyWatchEvent.class; } /** @@ -214,7 +210,7 @@ class FuzzyWatchRpcPushTask implements Runnable { /** * The FuzzyListenNotifyDiffRequest to be pushed. */ - FuzzyWatchDiffSyncRequest notifyRequest; + ConfigFuzzyWatchSyncRequest notifyRequest; /** * The maximum number of times to retry pushing the request. @@ -262,7 +258,7 @@ class FuzzyWatchRpcPushTask implements Runnable { * @param clientIp The IP address of the client * @param appName The name of the client's application */ - public FuzzyWatchRpcPushTask(FuzzyWatchDiffSyncRequest notifyRequest, AtomicInteger pushBatchFinishCount, + public FuzzyWatchRpcPushTask(ConfigFuzzyWatchSyncRequest notifyRequest, AtomicInteger pushBatchFinishCount, int originBatchSize, int maxRetryTimes, String connectionId, String clientIp, String appName) { this.notifyRequest = notifyRequest; this.pushBatchFinishCount = pushBatchFinishCount; @@ -365,7 +361,7 @@ public void onSuccess() { if (pushBatchCount.get() < originBatchSize) { pushBatchCount.incrementAndGet(); } else if (pushBatchCount.get() == originBatchSize) { - FuzzyWatchDiffSyncRequest request = FuzzyWatchDiffSyncRequest.buildInitFinishRequest( + ConfigFuzzyWatchSyncRequest request = ConfigFuzzyWatchSyncRequest.buildInitFinishRequest( fuzzyWatchRpcPushTask.notifyRequest.getGroupKeyPattern()); push(new FuzzyWatchRpcPushTask(request, pushBatchCount, originBatchSize, 50, fuzzyWatchRpcPushTask.connectionId, fuzzyWatchRpcPushTask.clientIp, diff --git a/config/src/main/java/com/alibaba/nacos/config/server/remote/FuzzyWatchConfigChangeNotifier.java b/config/src/main/java/com/alibaba/nacos/config/server/remote/FuzzyWatchConfigChangeNotifier.java index 1ed9239cf26..63326a784d7 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/remote/FuzzyWatchConfigChangeNotifier.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/remote/FuzzyWatchConfigChangeNotifier.java @@ -16,7 +16,7 @@ package com.alibaba.nacos.config.server.remote; -import com.alibaba.nacos.api.config.remote.request.FuzzyWatchChangeNotifyRequest; +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; @@ -26,7 +26,6 @@ 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.Connection; import com.alibaba.nacos.core.remote.ConnectionManager; import com.alibaba.nacos.core.remote.ConnectionMeta; @@ -39,6 +38,11 @@ import java.util.concurrent.TimeUnit; +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; + /** * Notify remote clients about fuzzy listen configuration changes. Use subscriber mode to monitor local data changes, * and push notifications to remote clients accordingly. @@ -55,8 +59,6 @@ public class FuzzyWatchConfigChangeNotifier extends Subscriber 2 ? parseKey[2] : ""; + + String changedType=null; + + + boolean exists = ConfigCacheService.getContentCache(event.groupKey)!=null; + if (exists){ + changedType=configFuzzyWatchContextService.newConfigAdded(event.groupKey)? ADD_CONFIG : CONFIG_CHANGED; + }else{ + configFuzzyWatchContextService.configRemoved(event.groupKey); + changedType=DELETE_CONFIG; + } - for (String clientId : configFuzzyWatchContextService.getMatchedClients(groupKey)) { + for (String clientId : configFuzzyWatchContextService.getMatchedClients(event.groupKey)) { Connection connection = connectionManager.getConnection(clientId); if (null == connection) { Loggers.REMOTE_PUSH.warn( @@ -101,8 +106,8 @@ public void onEvent(LocalDataChangeEvent event) { ConnectionMeta metaInfo = connection.getMetaInfo(); String clientIp = metaInfo.getClientIp(); String appName = metaInfo.getAppName(); - boolean exists = ConfigCacheService.getContentCache(groupKey)!=null; - FuzzyWatchChangeNotifyRequest request = new FuzzyWatchChangeNotifyRequest(tenant, group, dataId, exists); + + ConfigFuzzyWatchChangeNotifyRequest request = new ConfigFuzzyWatchChangeNotifyRequest(event.groupKey, changedType); int maxPushRetryTimes = ConfigCommonConfig.getInstance().getMaxPushRetryTimes(); RpcPushTask rpcPushTask = new RpcPushTask(request, maxPushRetryTimes, clientId, clientIp, appName); push(rpcPushTask); @@ -120,11 +125,11 @@ public Class subscribeType() { * @param retryTask The task for retrying to push notification. */ private void push(RpcPushTask retryTask) { - FuzzyWatchChangeNotifyRequest notifyRequest = retryTask.notifyRequest; + ConfigFuzzyWatchChangeNotifyRequest notifyRequest = retryTask.notifyRequest; if (retryTask.isOverTimes()) { Loggers.REMOTE_PUSH.warn( - "push callback retry fail over times. dataId={},group={},tenant={},clientId={}, will unregister client.", - notifyRequest.getDataId(), notifyRequest.getGroup(), notifyRequest.getNamespace(), + "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) { @@ -134,8 +139,7 @@ private void push(RpcPushTask retryTask) { } else { // Client is already offline, ignore the task. Loggers.REMOTE_PUSH.warn( - "Client is already offline, ignore the task. dataId={},group={},tenant={},clientId={}", - notifyRequest.getDataId(), notifyRequest.getGroup(), notifyRequest.getNamespace(), + "Client is already offline, ignore the task. dataId={},groupKey={},tenant={},clientId={}", notifyRequest.getGroupKey(), retryTask.connectionId); } } @@ -145,7 +149,7 @@ private void push(RpcPushTask retryTask) { */ class RpcPushTask implements Runnable { - FuzzyWatchChangeNotifyRequest notifyRequest; + ConfigFuzzyWatchChangeNotifyRequest notifyRequest; int maxRetryTimes; @@ -166,7 +170,7 @@ class RpcPushTask implements Runnable { * @param clientIp The IP address of the client. * @param appName The name of the application. */ - public RpcPushTask(FuzzyWatchChangeNotifyRequest notifyRequest, int maxRetryTimes, String connectionId, + public RpcPushTask(ConfigFuzzyWatchChangeNotifyRequest notifyRequest, int maxRetryTimes, String connectionId, String clientIp, String appName) { this.notifyRequest = notifyRequest; this.maxRetryTimes = maxRetryTimes; @@ -206,8 +210,8 @@ 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, dataId={}, group={}, tenant={}, clientId={}", - notifyRequest.getDataId(), notifyRequest.getGroup(), notifyRequest.getNamespace(), + Loggers.REMOTE_PUSH.warn("Push fail, groupKey={}, clientId={}", + notifyRequest.getGroupKey(), connectionId, e); push(RpcPushTask.this); } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/service/ConfigCacheService.java b/config/src/main/java/com/alibaba/nacos/config/server/service/ConfigCacheService.java index 9dcb9b8c7a7..89e12debc8f 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/service/ConfigCacheService.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/service/ConfigCacheService.java @@ -17,7 +17,6 @@ package com.alibaba.nacos.config.server.service; import com.alibaba.nacos.common.notify.NotifyCenter; -import com.alibaba.nacos.common.utils.FuzzyGroupKeyPattern; import com.alibaba.nacos.common.utils.MD5Utils; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.config.server.model.CacheItem; @@ -32,11 +31,9 @@ import com.alibaba.nacos.sys.env.EnvUtil; import java.io.IOException; -import java.util.Map; -import java.util.Set; import java.util.HashMap; +import java.util.Map; import java.util.concurrent.ConcurrentHashMap; -import java.util.stream.Collectors; import static com.alibaba.nacos.api.common.Constants.CLIENT_IP; import static com.alibaba.nacos.api.common.Constants.VIPSERVER_TAG; @@ -71,7 +68,6 @@ public static int groupCount() { return CACHE.size(); } - /** * Save config file and update md5 value in cache. * 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 59ef34ceb63..6b03a0e8c02 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 @@ -1,3 +1,19 @@ +/* + * 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.service; @@ -54,6 +70,7 @@ private void clearFuzzyWatchContext(){ } } } + /** * get matched exist group keys with the groupKeyPattern. * return null if not matched @@ -68,6 +85,47 @@ public Set matchGroupKeys(String groupKeyPattern) { } } + public boolean newConfigAdded(String groupKey){ + String[] groupKeyItems = GroupKey.parseKey(groupKey); + String dataId=groupKeyItems[0]; + String group=groupKeyItems[1]; + String namespace=groupKeyItems[2]; + Iterator>> iterator = groupKeyMatched.entrySet().iterator(); + boolean added=false; + while (iterator.hasNext()){ + Map.Entry> entry = iterator.next(); + if(FuzzyGroupKeyPattern.matchPattern(entry.getKey(),dataId,group,namespace)){ + if(entry.getValue().add(groupKey)){ + added=true; + } + + } + } + + return added; + + } + + public boolean configRemoved(String groupKey){ + String[] groupKeyItems = GroupKey.parseKey(groupKey); + String dataId=groupKeyItems[0]; + String group=groupKeyItems[1]; + String namespace=groupKeyItems[2]; + Iterator>> iterator = groupKeyMatched.entrySet().iterator(); + boolean removed=false; + while (iterator.hasNext()){ + Map.Entry> entry = iterator.next(); + if(FuzzyGroupKeyPattern.matchPattern(entry.getKey(),dataId,group,namespace)){ + if(entry.getValue().remove(groupKey)){ + removed=true; + } + + } + } + + return removed; + + } /** * Matches the client effective group keys based on the specified group key pattern, client IP, and tag. diff --git a/core/src/main/java/com/alibaba/nacos/core/paramcheck/impl/ConfigBatchFuzzyListenRequestParamsExtractor.java b/core/src/main/java/com/alibaba/nacos/core/paramcheck/impl/ConfigBatchFuzzyListenRequestParamsExtractor.java index 64f7eeec996..99780ce5b91 100644 --- a/core/src/main/java/com/alibaba/nacos/core/paramcheck/impl/ConfigBatchFuzzyListenRequestParamsExtractor.java +++ b/core/src/main/java/com/alibaba/nacos/core/paramcheck/impl/ConfigBatchFuzzyListenRequestParamsExtractor.java @@ -16,11 +16,12 @@ package com.alibaba.nacos.core.paramcheck.impl; -import com.alibaba.nacos.api.config.remote.request.ConfigBatchFuzzyWatchRequest; +import com.alibaba.nacos.api.config.remote.request.ConfigFuzzyWatchRequest; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.remote.request.Request; import com.alibaba.nacos.common.paramcheck.ParamInfo; import com.alibaba.nacos.common.utils.CollectionUtils; +import com.alibaba.nacos.common.utils.FuzzyGroupKeyPattern; import com.alibaba.nacos.core.paramcheck.AbstractRpcParamExtractor; import java.util.ArrayList; @@ -28,7 +29,7 @@ import java.util.Set; /** - * Extractor for parameters of {@link ConfigBatchFuzzyWatchRequest}. This extractor retrieves parameter information + * Extractor for parameters of {@link ConfigFuzzyWatchRequest}. This extractor retrieves parameter information * from the request object and constructs {@link ParamInfo} instances representing the namespace ID, group, and data IDs * contained in the request's contexts. * @@ -46,28 +47,12 @@ public class ConfigBatchFuzzyListenRequestParamsExtractor extends AbstractRpcPar */ @Override public List extractParam(Request request) throws NacosException { - ConfigBatchFuzzyWatchRequest req = (ConfigBatchFuzzyWatchRequest) request; - Set contexts = req.getContexts(); + ConfigFuzzyWatchRequest req = (ConfigFuzzyWatchRequest) request; List paramInfos = new ArrayList<>(); - if (contexts == null) { - return paramInfos; - } - for (ConfigBatchFuzzyWatchRequest.Context context : contexts) { // Extract namespace ID and group from the context ParamInfo paramInfo1 = new ParamInfo(); - paramInfo1.setNamespaceId(context.getTenant()); - paramInfo1.setGroup(context.getGroup()); + paramInfo1.setNamespaceId(FuzzyGroupKeyPattern.getNamespaceFromPattern(req.getGroupKeyPattern())); paramInfos.add(paramInfo1); - - // Extract data IDs from the context if present - if (CollectionUtils.isNotEmpty(context.getDataIds())) { - for (String dataId : context.getDataIds()) { - ParamInfo paramInfo2 = new ParamInfo(); - paramInfo2.setDataId(dataId); - paramInfos.add(paramInfo2); - } - } - } return paramInfos; } } diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/client/AbstractClient.java b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/client/AbstractClient.java index 078b1ed0049..5662a5ae257 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/client/AbstractClient.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/client/AbstractClient.java @@ -17,7 +17,6 @@ package com.alibaba.nacos.naming.core.v2.client; import com.alibaba.nacos.common.notify.NotifyCenter; -import com.alibaba.nacos.common.utils.ConcurrentHashSet; import com.alibaba.nacos.naming.core.v2.event.client.ClientEvent; import com.alibaba.nacos.naming.core.v2.pojo.BatchInstanceData; import com.alibaba.nacos.naming.core.v2.pojo.BatchInstancePublishInfo; @@ -48,8 +47,6 @@ public abstract class AbstractClient implements Client { protected final ConcurrentHashMap subscribers = new ConcurrentHashMap<>(16, 0.75f, 1); - protected final ConcurrentHashSet fuzzyWatchedPattern = new ConcurrentHashSet<>(); - protected volatile long lastUpdatedTime; protected final AtomicLong revision; @@ -137,34 +134,6 @@ public Collection getAllSubscribeService() { return subscribers.keySet(); } - @Override - public boolean addWatchedPattern(String watchPattern) { - if (fuzzyWatchedPattern.add(watchPattern)) { - // TODO:Watch MetricsMonitor - return true; - } - return true; - } - - @Override - public boolean removeWatchedPattern(String watchPattern) { - if (fuzzyWatchedPattern.remove(watchPattern)) { - // TODO:Watch MetricsMonitor - return true; - } - return true; - } - - @Override - public boolean isWatchedPattern(String watchPattern) { - return fuzzyWatchedPattern.contains(watchPattern); - } - - @Override - public Collection getFuzzyWatchedPattern() { - return fuzzyWatchedPattern; - } - @Override public ClientSyncData generateSyncData() { List namespaces = new LinkedList<>(); 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 9954fcc6153..9c9c41ba82d 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 @@ -102,30 +102,39 @@ public ClientUnsubscribeServiceEvent(Service service, String clientId) { public static class ClientFuzzyWatchEvent extends ClientOperationEvent { private static final long serialVersionUID = -4518919987813223119L; - - private final String pattern; - private Set clientReceivedGroupKeys; + /** + * client watched pattern + */ + private final String groupKeyPattern; + + /** + * client side received group keys. + */ + private Set clientReceivedServiceKeys; + /** + * is fuzzy watch initializing + */ private boolean isInitializing; - public ClientFuzzyWatchEvent(String pattern, String clientId,Set clientReceivedGroupKeys,boolean isInitializing) { + public ClientFuzzyWatchEvent(String groupKeyPattern, String clientId,Set clientReceivedServiceKeys,boolean isInitializing) { super(clientId, null); - this.pattern = pattern; - this.clientReceivedGroupKeys = clientReceivedGroupKeys; + this.groupKeyPattern = groupKeyPattern; + this.clientReceivedServiceKeys = clientReceivedServiceKeys; this.isInitializing=isInitializing; } - public String getPattern() { - return pattern; + public String getGroupKeyPattern() { + return groupKeyPattern; } - public Set getClientReceivedGroupKeys() { - return clientReceivedGroupKeys; + public Set getClientReceivedServiceKeys() { + return clientReceivedServiceKeys; } - public void setClientReceivedGroupKeys(Set clientReceivedGroupKeys) { - this.clientReceivedGroupKeys = clientReceivedGroupKeys; + public void setClientReceivedServiceKeys(Set clientReceivedServiceKeys) { + this.clientReceivedServiceKeys = clientReceivedServiceKeys; } public boolean isInitializing() { diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/event/service/ServiceEvent.java b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/event/service/ServiceEvent.java index 85d32592dee..18b40a4f9da 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/event/service/ServiceEvent.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/event/service/ServiceEvent.java @@ -19,7 +19,6 @@ import com.alibaba.nacos.common.notify.Event; import com.alibaba.nacos.naming.core.v2.pojo.Service; -import java.util.Collection; import java.util.Set; /** @@ -31,10 +30,7 @@ public class ServiceEvent extends Event { private static final long serialVersionUID = -9173247502346692418L; - private Service service; - - public ServiceEvent() { - } + private final Service service; public ServiceEvent(Service service) { this.service = service; @@ -91,36 +87,4 @@ public String getClientId() { } } - /** - * A client initiates a fuzzy watch request. - */ - public static class ServiceFuzzyWatchInitEvent extends ServiceEvent { - - private static final long serialVersionUID = -2645441445867337345L; - - private final String clientId; - - private final String pattern; - - private final Set matchedServiceKeys; - - public ServiceFuzzyWatchInitEvent(String clientId, String pattern, Set matchedServiceKeys) { - super(); - this.clientId = clientId; - this.pattern = pattern; - this.matchedServiceKeys = matchedServiceKeys; - } - - public String getClientId() { - return clientId; - } - - public String getPattern() { - return pattern; - } - - public Set getMatchedServicKeys() { - return matchedServiceKeys; - } - } } diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/index/ClientServiceIndexesManager.java b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/index/ClientServiceIndexesManager.java index 18f08ed0824..2f5fbfc9273 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/index/ClientServiceIndexesManager.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/index/ClientServiceIndexesManager.java @@ -71,7 +71,7 @@ public Collection getAllClientsSubscribeService(Service service) { public Collection getSubscribedService() { return subscriberIndexes.keySet(); } - + /** * Clear the service index without instances. * @@ -138,7 +138,7 @@ private void addPublisherIndexes(Service service, String clientId) { String serviceChangedType = Constants.ServiceChangedType.INSTANCE_CHANGED; if (!publisherIndexes.containsKey(service)) { // The only time the index needs to be updated is when the service is first created - namingFuzzyWatchContextService.addNewSevice(service); + namingFuzzyWatchContextService.addNewService(service); serviceChangedType = Constants.ServiceChangedType.ADD_SERVICE; } NotifyCenter.publishEvent(new ServiceEvent.ServiceChangedEvent(service, serviceChangedType, true)); @@ -173,5 +173,4 @@ private void removeSubscriberIndexes(Service service, String clientId) { subscriberIndexes.remove(service); } } - } 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 380f8de8aa3..5af85ea607b 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 @@ -1,9 +1,24 @@ +/* + * 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.runtime.NacosRuntimeException; import com.alibaba.nacos.api.naming.utils.NamingUtils; import com.alibaba.nacos.common.notify.Event; -import com.alibaba.nacos.common.notify.NotifyCenter; import com.alibaba.nacos.common.notify.listener.SmartSubscriber; import com.alibaba.nacos.common.utils.CollectionUtils; import com.alibaba.nacos.common.utils.ConcurrentHashSet; @@ -31,6 +46,11 @@ import static com.alibaba.nacos.api.model.v2.ErrorCode.FUZZY_WATCH_PATTERN_OVER_LIMIT; import static com.alibaba.nacos.common.utils.FuzzyGroupKeyPattern.getNamespaceFromPattern; +/** + * naming fuzzy watch context service + *

+ * 1. handler client fuzzy watch event and cancel watch event 2. service changed notify fuzzy watched clients. + */ @Component public class NamingFuzzyWatchContextService extends SmartSubscriber { @@ -40,9 +60,9 @@ public class NamingFuzzyWatchContextService extends SmartSubscriber { private final ConcurrentMap> keyPatternWatchClients = 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. + * 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> fuzzyWatchPatternMatchServices = new ConcurrentHashMap<>(); @@ -52,10 +72,15 @@ public class NamingFuzzyWatchContextService extends SmartSubscriber { public NamingFuzzyWatchContextService() { - GlobalExecutor.scheduleWithFixDelayByCommon(() -> clearFuzzyWatchContext(), 30000); + GlobalExecutor.scheduleWithFixDelayByCommon(() -> trimFuzzyWatchContext(), 30000); } - private void clearFuzzyWatchContext() { + /** + * trim empty fuzzy watch context.
+ * 1.remove matched service keys context which no client watched.
+ * 2.remove pattern watched clients context if not client watched. + */ + private void trimFuzzyWatchContext() { try { Iterator>> iterator = fuzzyWatchPatternMatchServices.entrySet().iterator(); while (iterator.hasNext()) { @@ -68,7 +93,7 @@ private void clearFuzzyWatchContext() { } } } - }catch(Throwable throwable){ + } catch (Throwable throwable) { throwable.printStackTrace(); } } @@ -76,76 +101,26 @@ private void clearFuzzyWatchContext() { @Override public List> subscribeTypes() { List> result = new LinkedList<>(); - result.add(ClientOperationEvent.ClientFuzzyWatchEvent.class); - result.add(ClientOperationEvent.ClientCancelFuzzyWatchEvent.class); result.add(ClientOperationEvent.ClientReleaseEvent.class); result.add(ServiceEvent.ServiceChangedEvent.class); - return result; } @Override public void onEvent(Event event) { - if (!(event instanceof ClientOperationEvent) && !(event instanceof ServiceEvent.ServiceChangedEvent)) { - return; - } - if (event instanceof ClientOperationEvent) { - ClientOperationEvent clientOperationEvent = (ClientOperationEvent) event; - String clientId = clientOperationEvent.getClientId(); - - if (event instanceof ClientOperationEvent.ClientFuzzyWatchEvent) { - ClientOperationEvent.ClientFuzzyWatchEvent clientFuzzyWatchEvent = (ClientOperationEvent.ClientFuzzyWatchEvent) event; - String completedPattern = clientFuzzyWatchEvent.getPattern(); - if (clientFuzzyWatchEvent.isInitializing()){ - //fuzzy watch init - addFuzzyWatcherIndexes(completedPattern, clientId); - }else{ - //fuzzy watch sync - syncFuzzyWatcherClientIdContext(completedPattern,clientFuzzyWatchEvent.getClientReceivedGroupKeys(),clientId); - } - - } else if (event instanceof ClientOperationEvent.ClientCancelFuzzyWatchEvent) { - String completedPattern = ((ClientOperationEvent.ClientCancelFuzzyWatchEvent) event).getPattern(); - removeFuzzyWatchContext(completedPattern, clientId); - } else if (event instanceof ClientOperationEvent.ClientReleaseEvent) { - removeFuzzyWatchContext(((ClientOperationEvent.ClientReleaseEvent) event).getClientId()); - } + //handle client disconnected event. + if (event instanceof ClientOperationEvent.ClientReleaseEvent) { + removeFuzzyWatchContext(((ClientOperationEvent.ClientReleaseEvent) event).getClientId()); } - + + //handle service add or deleted event if (event instanceof ServiceEvent.ServiceChangedEvent) { ServiceEvent.ServiceChangedEvent serviceChangedEvent = (ServiceEvent.ServiceChangedEvent) event; String changedType = serviceChangedEvent.getChangedType(); - if (changedType.equals(ADD_SERVICE)) { - Iterator>> iterator = fuzzyWatchPatternMatchServices.entrySet() - .iterator(); - Service changedEventService = serviceChangedEvent.getService(); - - while (iterator.hasNext()) { - Map.Entry> next = iterator.next(); - if (FuzzyGroupKeyPattern.matchPattern(next.getKey(), - changedEventService.getName(), - changedEventService.getGroup(), changedEventService.getNamespace())) { - next.getValue().add(NamingUtils.getServiceKey(changedEventService.getNamespace(),changedEventService.getGroup(),changedEventService.getName())); - } - } - - } else if (changedType.equals(DELETE_SERVICE)) { - Iterator>> iterator = fuzzyWatchPatternMatchServices.entrySet() - .iterator(); - Service changedEventService = serviceChangedEvent.getService(); - - while (iterator.hasNext()) { - Map.Entry> next = iterator.next(); - if (FuzzyGroupKeyPattern.matchPattern(next.getKey(), - changedEventService.getName(), - changedEventService.getGroup(), changedEventService.getNamespace())) { - String serviceKey = NamingUtils.getServiceKey(changedEventService.getNamespace(), - changedEventService.getGroup(), changedEventService.getName()); - next.getValue().remove(serviceKey); - } - } - } + Service service = serviceChangedEvent.getService(); + + syncServiceContext(service,changedType); } } @@ -167,44 +142,50 @@ public Set getFuzzyWatchedClients(Service service) { * * @param service The service of the Nacos. */ - public void addNewSevice(Service service) { - long matchBeginTime = System.currentTimeMillis(); + public void addNewService(Service service) { Set filteredPattern = FuzzyGroupKeyPattern.filterMatchedPatterns(keyPatternWatchClients.keySet(), service.getName(), service.getGroup(), service.getNamespace()); if (CollectionUtils.isNotEmpty(filteredPattern)) { for (String each : filteredPattern) { - fuzzyWatchPatternMatchServices.get(each).add(NamingUtils.getServiceKey(service.getNamespace(),service.getGroup(),service.getName())); + fuzzyWatchPatternMatchServices.get(each) + .add(NamingUtils.getServiceKey(service.getNamespace(), service.getGroup(), service.getName())); } - Loggers.PERFORMANCE_LOG.info("WATCH: new service {} match {} pattern, {}ms", - service.getGroupedServiceName(), fuzzyWatchPatternMatchServices.size(), - System.currentTimeMillis() - matchBeginTime); + Loggers.PERFORMANCE_LOG.info("WATCH: new service {} match {} pattern", + service.getGroupedServiceName(), fuzzyWatchPatternMatchServices.size()); } } - private void addFuzzyWatcherIndexes(String completedPattern, String clientId) { - - Set matchedServiceKeys = initWatchMatchService(completedPattern); - boolean added =keyPatternWatchClients.computeIfAbsent(completedPattern, key -> new ConcurrentHashSet<>()).add(clientId); - if (added) { - NotifyCenter.publishEvent( - new ServiceEvent.ServiceFuzzyWatchInitEvent(clientId, completedPattern, matchedServiceKeys)); + public void syncServiceContext(Service changedEventService, String changedType) { + + Iterator>> iterator = fuzzyWatchPatternMatchServices.entrySet() + .iterator(); + + while (iterator.hasNext()) { + Map.Entry> next = iterator.next(); + if (FuzzyGroupKeyPattern.matchPattern(next.getKey(), + changedEventService.getName(), + changedEventService.getGroup(), changedEventService.getNamespace())) { + String serviceKey = NamingUtils.getServiceKey(changedEventService.getNamespace(), + changedEventService.getGroup(), changedEventService.getName()); + if (changedType.equals(ADD_SERVICE)){ + next.getValue().add(serviceKey); + + }else if (changedType.equals(DELETE_SERVICE)){ + next.getValue().remove(serviceKey); + } + } } } - private void syncFuzzyWatcherClientIdContext(String completedPattern,Set clientReceivedServiceKeys, String clientId) { + public Set syncFuzzyWatcherContext(String groupKeyPattern, String clientId) { - Set matchedServiceKeys = getPatternMatchedServiceKeys(completedPattern); - keyPatternWatchClients.computeIfAbsent(completedPattern, key -> new ConcurrentHashSet<>()); - boolean added = keyPatternWatchClients.get(completedPattern).add(clientId); - if (added) { - NotifyCenter.publishEvent( - new ServiceEvent.ServiceFuzzyWatchInitEvent(clientId, completedPattern, matchedServiceKeys)); - } + keyPatternWatchClients.computeIfAbsent(groupKeyPattern, key -> new ConcurrentHashSet<>()) + .add(clientId); + Set matchedServiceKeys = initWatchMatchService(groupKeyPattern); + return matchedServiceKeys; } - - private void removeFuzzyWatchContext(String clientId) { Iterator>> iterator = keyPatternWatchClients.entrySet().iterator(); @@ -214,72 +195,53 @@ private void removeFuzzyWatchContext(String clientId) { } } - private void removeFuzzyWatchContext(String groupKeyPattern, String clientId) { + public void removeFuzzyWatchContext(String groupKeyPattern, String clientId) { if (keyPatternWatchClients.containsKey(groupKeyPattern)) { keyPatternWatchClients.get(groupKeyPattern).remove(clientId); } } - /** - * This method will remove the match index of fuzzy watch pattern. - * - * @param service The service of the Nacos. - * @param matchedPattern the pattern to remove - */ - public void removeFuzzyWatchContext(Service service, String matchedPattern) { - if (!fuzzyWatchPatternMatchServices.containsKey(service)) { - return; - } - fuzzyWatchPatternMatchServices.get(service).remove(matchedPattern); - if (fuzzyWatchPatternMatchServices.get(service).isEmpty()) { - fuzzyWatchPatternMatchServices.remove(service); - } - } - - - public Set getPatternMatchedServiceKeys(String completedPattern) { - return fuzzyWatchPatternMatchServices.get(completedPattern); - } - /** * This method will build/update the fuzzy watch match index for given patterns. * * @param completedPattern the completed pattern of watch (with namespace id). - * @return Updated set of services in Nacos server that can match this pattern. + * @return a copy set of matched service keys in Nacos server */ public Set initWatchMatchService(String completedPattern) { - if (fuzzyWatchPatternMatchServices.containsKey(completedPattern)) { - return fuzzyWatchPatternMatchServices.get(completedPattern); - } - - if (fuzzyWatchPatternMatchServices.size() >= FUZZY_WATCH_MAX_PATTERN_COUNT) { - throw new NacosRuntimeException(FUZZY_WATCH_PATTERN_OVER_LIMIT.getCode(), - FUZZY_WATCH_PATTERN_OVER_LIMIT.getMsg()); - } - - long matchBeginTime = System.currentTimeMillis(); - Set namespaceServices = ServiceManager.getInstance() - .getSingletons(getNamespaceFromPattern(completedPattern)); - Set matchedServices = fuzzyWatchPatternMatchServices.computeIfAbsent(completedPattern, - k -> new HashSet<>()); - - 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) { - throw new NacosRuntimeException(FUZZY_WATCH_PATTERN_MATCH_GROUP_KEY_OVER_LIMIT.getCode(), - FUZZY_WATCH_PATTERN_MATCH_GROUP_KEY_OVER_LIMIT.getMsg()); + if (!fuzzyWatchPatternMatchServices.containsKey(completedPattern)) { + if (fuzzyWatchPatternMatchServices.size() >= FUZZY_WATCH_MAX_PATTERN_COUNT) { + throw new NacosRuntimeException(FUZZY_WATCH_PATTERN_OVER_LIMIT.getCode(), + FUZZY_WATCH_PATTERN_OVER_LIMIT.getMsg()); + } + + long matchBeginTime = System.currentTimeMillis(); + Set namespaceServices = ServiceManager.getInstance() + .getSingletons(getNamespaceFromPattern(completedPattern)); + Set matchedServices = fuzzyWatchPatternMatchServices.computeIfAbsent(completedPattern, + k -> new HashSet<>()); + + 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) { + throw new NacosRuntimeException(FUZZY_WATCH_PATTERN_MATCH_GROUP_KEY_OVER_LIMIT.getCode(), + FUZZY_WATCH_PATTERN_MATCH_GROUP_KEY_OVER_LIMIT.getMsg()); + } + matchedServices.add( + NamingUtils.getServiceKey(service.getNamespace(), service.getGroup(), service.getName())); } - matchedServices.add(NamingUtils.getServiceKey(service.getNamespace(),service.getGroup(),service.getName())); } + fuzzyWatchPatternMatchServices.putIfAbsent(completedPattern,matchedServices); + Loggers.PERFORMANCE_LOG.info("WATCH: pattern {} match {} services, cost {}ms", completedPattern, + matchedServices.size(), System.currentTimeMillis() - matchBeginTime); + } - Loggers.PERFORMANCE_LOG.info("WATCH: pattern {} match {} services, cost {}ms", completedPattern, - matchedServices.size(), System.currentTimeMillis() - matchBeginTime); - - return matchedServices; + return new HashSet(fuzzyWatchPatternMatchServices.get(completedPattern)); } + + } diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/service/ClientOperationServiceProxy.java b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/service/ClientOperationServiceProxy.java index 7284af587e1..2efa7c6dd9f 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/service/ClientOperationServiceProxy.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/service/ClientOperationServiceProxy.java @@ -85,16 +85,6 @@ public void unsubscribeService(Service service, Subscriber subscriber, String cl ephemeralClientOperationService.unsubscribeService(service, subscriber, clientId); } - @Override - public void fuzzyWatch(String namespaceId, String serviceNamePattern, String groupNamePattern, String clientId) { - ephemeralClientOperationService.fuzzyWatch(namespaceId, serviceNamePattern, groupNamePattern, clientId); - } - - @Override - public void cancelFuzzyWatch(String namespaceId, String serviceNamePattern, String groupNamePattern, String clientId) { - ephemeralClientOperationService.cancelFuzzyWatch(namespaceId, serviceNamePattern, groupNamePattern, clientId); - } - private ClientOperationService chooseClientOperationService(final Instance instance) { return instance.isEphemeral() ? ephemeralClientOperationService : persistentClientOperationService; } diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/service/impl/PersistentClientOperationServiceImpl.java b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/service/impl/PersistentClientOperationServiceImpl.java index d52d35d46d0..7cb0d32cc9e 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/service/impl/PersistentClientOperationServiceImpl.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/service/impl/PersistentClientOperationServiceImpl.java @@ -182,16 +182,6 @@ public void unsubscribeService(Service service, Subscriber subscriber, String cl throw new UnsupportedOperationException("No persistent subscribers"); } - @Override - public void fuzzyWatch(String namespaceId, String serviceNamePattern, String groupNamePattern, String clientId) { - throw new UnsupportedOperationException("No persistent fuzzy watcher"); - } - - @Override - public void cancelFuzzyWatch(String namespaceId, String serviceNamePattern, String groupNamePattern, String clientId) { - throw new UnsupportedOperationException("No persistent fuzzy watcher"); - } - @Override public Response onRequest(ReadRequest request) { throw new UnsupportedOperationException("Temporary does not support"); diff --git a/naming/src/main/java/com/alibaba/nacos/naming/push/NamingFuzzyWatchChangeNotifier.java b/naming/src/main/java/com/alibaba/nacos/naming/push/NamingFuzzyWatchChangeNotifier.java new file mode 100644 index 00000000000..4dd2c3475be --- /dev/null +++ b/naming/src/main/java/com/alibaba/nacos/naming/push/NamingFuzzyWatchChangeNotifier.java @@ -0,0 +1,74 @@ +/* + * 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.push; + +import com.alibaba.nacos.api.naming.utils.NamingUtils; +import com.alibaba.nacos.common.notify.Event; +import com.alibaba.nacos.common.notify.listener.SmartSubscriber; +import com.alibaba.nacos.naming.core.v2.event.service.ServiceEvent; +import com.alibaba.nacos.naming.core.v2.index.NamingFuzzyWatchContextService; +import com.alibaba.nacos.naming.push.v2.PushConfig; +import com.alibaba.nacos.naming.push.v2.task.FuzzyWatchChangeNotifyTask; +import com.alibaba.nacos.naming.push.v2.task.FuzzyWatchPushDelayTaskEngine; +import org.springframework.stereotype.Service; + +import java.util.LinkedList; +import java.util.List; + +/** + * service change notify for fuzzy watch + */ +@Service +public class NamingFuzzyWatchChangeNotifier extends SmartSubscriber { + + private NamingFuzzyWatchContextService namingFuzzyWatchContextService; + + private FuzzyWatchPushDelayTaskEngine fuzzyWatchPushDelayTaskEngine; + + public NamingFuzzyWatchChangeNotifier(NamingFuzzyWatchContextService namingFuzzyWatchContextService,FuzzyWatchPushDelayTaskEngine fuzzyWatchPushDelayTaskEngine){ + this.fuzzyWatchPushDelayTaskEngine=fuzzyWatchPushDelayTaskEngine; + this.namingFuzzyWatchContextService=namingFuzzyWatchContextService; + } + + @Override + public List> subscribeTypes() { + List> result = new LinkedList<>(); + result.add(ServiceEvent.ServiceChangedEvent.class); + return result; + } + @Override + public void onEvent(Event event) { + if (event instanceof ServiceEvent.ServiceChangedEvent) { + ServiceEvent.ServiceChangedEvent serviceChangedEvent=(ServiceEvent.ServiceChangedEvent)event; + namingFuzzyWatchContextService.syncServiceContext(serviceChangedEvent.getService(),serviceChangedEvent.getChangedType()); + generateFuzzyWatchChangeNotifyTask(serviceChangedEvent.getService(),serviceChangedEvent.getChangedType()); + } + } + + private void generateFuzzyWatchChangeNotifyTask(com.alibaba.nacos.naming.core.v2.pojo.Service service, String changedType) { + // watch notify push task specify by service + for (String clientId : namingFuzzyWatchContextService.getFuzzyWatchedClients(service)) { + String serviceKey = NamingUtils.getServiceKey(service.getNamespace(), service.getGroup(), + service.getName()); + FuzzyWatchChangeNotifyTask fuzzyWatchChangeNotifyTask = new FuzzyWatchChangeNotifyTask(serviceKey, + changedType, clientId, PushConfig.getInstance().getPushTaskDelay()); + fuzzyWatchPushDelayTaskEngine.addTask(FuzzyWatchPushDelayTaskEngine.getTaskKey(fuzzyWatchChangeNotifyTask), + fuzzyWatchChangeNotifyTask); + } + } + +} 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 new file mode 100644 index 00000000000..57ab8748fd9 --- /dev/null +++ b/naming/src/main/java/com/alibaba/nacos/naming/push/NamingFuzzyWatchSyncNotifier.java @@ -0,0 +1,142 @@ +/* + * 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.push; + +import com.alibaba.nacos.api.naming.remote.request.NamingFuzzyWatchSyncRequest; +import com.alibaba.nacos.common.notify.Event; +import com.alibaba.nacos.common.notify.listener.SmartSubscriber; +import com.alibaba.nacos.common.utils.CollectionUtils; +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.PushConfig; +import com.alibaba.nacos.naming.push.v2.task.FuzzyWatchSyncNotifyTask; +import com.alibaba.nacos.naming.push.v2.task.FuzzyWatchPushDelayTaskEngine; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +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.api.common.Constants.ServiceChangedType.DELETE_SERVICE; + +/** + * fuzzy watch event for fuzzy watch + */ +@Service +public class NamingFuzzyWatchSyncNotifier extends SmartSubscriber { + + + private NamingFuzzyWatchContextService namingFuzzyWatchContextService; + + private FuzzyWatchPushDelayTaskEngine fuzzyWatchPushDelayTaskEngine; + + private static final int batchSize=10; + + + public NamingFuzzyWatchSyncNotifier(NamingFuzzyWatchContextService namingFuzzyWatchContextService,FuzzyWatchPushDelayTaskEngine fuzzyWatchPushDelayTaskEngine){ + this.namingFuzzyWatchContextService=namingFuzzyWatchContextService; + this.fuzzyWatchPushDelayTaskEngine=fuzzyWatchPushDelayTaskEngine; + } + @Override + public List> subscribeTypes() { + List> result = new LinkedList<>(); + result.add(ClientOperationEvent.ClientFuzzyWatchEvent.class); + result.add(ClientOperationEvent.ClientCancelFuzzyWatchEvent.class); + return result; + } + + @Override + public void onEvent(Event event) { + + if (event instanceof ClientOperationEvent.ClientFuzzyWatchEvent) { + //fuzzy watch event + ClientOperationEvent.ClientFuzzyWatchEvent clientFuzzyWatchEvent = (ClientOperationEvent.ClientFuzzyWatchEvent) event; + String completedPattern = clientFuzzyWatchEvent.getGroupKeyPattern(); + + //sync fuzzy watch context + Set patternMatchedServiceKeys = namingFuzzyWatchContextService.syncFuzzyWatcherContext(completedPattern, clientFuzzyWatchEvent.getClientId()); + + Set clientReceivedGroupKeys = clientFuzzyWatchEvent.getClientReceivedServiceKeys(); + List groupKeyStates = FuzzyGroupKeyPattern.diffGroupKeys( + patternMatchedServiceKeys, clientReceivedGroupKeys); + Set syncContext = convert(groupKeyStates); + String syncType =clientFuzzyWatchEvent.isInitializing()?FUZZY_WATCH_INIT_NOTIFY:FUZZY_WATCH_DIFF_SYNC_NOTIFY; + + if(CollectionUtils.isNotEmpty(groupKeyStates)){ + Set> dividedServices = divideServiceByBatch(syncContext); + FuzzyWatchSyncNotifyTask.BatchTaskCounter batchTaskCounter=new FuzzyWatchSyncNotifyTask.BatchTaskCounter(dividedServices.size()); + int currentBatch=1; + for (Set batchData : dividedServices) { + FuzzyWatchSyncNotifyTask fuzzyWatchSyncNotifyTask=new FuzzyWatchSyncNotifyTask(clientFuzzyWatchEvent.getClientId(),completedPattern,syncType,batchData,PushConfig.getInstance().getPushTaskRetryDelay()); + fuzzyWatchSyncNotifyTask.setBatchTaskCounter(batchTaskCounter); + fuzzyWatchSyncNotifyTask.setTotalBatch(dividedServices.size()); + fuzzyWatchSyncNotifyTask.setCurrentBatch(currentBatch); + fuzzyWatchPushDelayTaskEngine.addTask(FuzzyWatchPushDelayTaskEngine.getTaskKey(fuzzyWatchSyncNotifyTask),fuzzyWatchSyncNotifyTask); + currentBatch++; + } + }else if (FUZZY_WATCH_INIT_NOTIFY.equals(syncType)){ + FuzzyWatchSyncNotifyTask fuzzyWatchSyncNotifyTask=new FuzzyWatchSyncNotifyTask(clientFuzzyWatchEvent.getClientId(),completedPattern,FINISH_FUZZY_WATCH_INIT_NOTIFY,null,PushConfig.getInstance().getPushTaskRetryDelay()); + fuzzyWatchPushDelayTaskEngine.addTask(FuzzyWatchPushDelayTaskEngine.getTaskKey(fuzzyWatchSyncNotifyTask),fuzzyWatchSyncNotifyTask); + + } + + }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 Set> divideServiceByBatch( + Collection matchedService) { + Set> result = new HashSet<>(); + if (matchedService.isEmpty()) { + return result; + } + Set currentBatch = new HashSet<>(); + for (NamingFuzzyWatchSyncRequest.Context groupedServiceName : matchedService) { + currentBatch.add(groupedServiceName); + if (currentBatch.size() >= this.batchSize) { + result.add(currentBatch); + currentBatch = new HashSet<>(); + } + } + if (!currentBatch.isEmpty()) { + result.add(currentBatch); + } + return result; + } + + private Set convert(List diffGroupKeys){ + Set syncContext=new HashSet<>(); + for(FuzzyGroupKeyPattern.GroupKeyState groupKeyState:diffGroupKeys){ + NamingFuzzyWatchSyncRequest.Context context=new NamingFuzzyWatchSyncRequest.Context(); + context.setServiceKey(groupKeyState.getGroupKey()); + context.setChangedType(groupKeyState.isExist()?ADD_SERVICE:DELETE_SERVICE); + syncContext.add(context) ; + } + return syncContext; + } +} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/push/v2/NamingSubscriberServiceV2Impl.java b/naming/src/main/java/com/alibaba/nacos/naming/push/v2/NamingSubscriberServiceV2Impl.java index 66e91deb6cd..9dd25664f54 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/push/v2/NamingSubscriberServiceV2Impl.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/push/v2/NamingSubscriberServiceV2Impl.java @@ -16,9 +16,7 @@ package com.alibaba.nacos.naming.push.v2; -import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.naming.utils.NamingUtils; -import com.alibaba.nacos.api.utils.StringUtils; import com.alibaba.nacos.common.notify.Event; import com.alibaba.nacos.common.notify.NotifyCenter; import com.alibaba.nacos.common.notify.listener.SmartSubscriber; @@ -26,7 +24,6 @@ import com.alibaba.nacos.naming.core.v2.client.manager.ClientManagerDelegate; import com.alibaba.nacos.naming.core.v2.event.publisher.NamingEventPublisherFactory; 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.index.ClientServiceIndexesManager; import com.alibaba.nacos.naming.core.v2.index.ServiceStorage; import com.alibaba.nacos.naming.core.v2.metadata.NamingMetadataManager; @@ -36,9 +33,6 @@ import com.alibaba.nacos.naming.pojo.Subscriber; import com.alibaba.nacos.naming.push.NamingSubscriberService; import com.alibaba.nacos.naming.push.v2.executor.PushExecutorDelegate; -import com.alibaba.nacos.naming.push.v2.task.FuzzyWatchChangeNotifyTask; -import com.alibaba.nacos.naming.push.v2.task.FuzzyWatchInitNotifyTask; -import com.alibaba.nacos.naming.push.v2.task.FuzzyWatchPushDelayTaskEngine; import com.alibaba.nacos.naming.push.v2.task.PushDelayTask; import com.alibaba.nacos.naming.push.v2.task.PushDelayTaskExecuteEngine; @@ -46,7 +40,6 @@ import java.util.HashSet; import java.util.LinkedList; import java.util.List; -import java.util.stream.Collectors; import java.util.stream.Stream; /** @@ -65,18 +58,13 @@ public class NamingSubscriberServiceV2Impl extends SmartSubscriber implements Na private final PushDelayTaskExecuteEngine delayTaskEngine; - private final FuzzyWatchPushDelayTaskEngine fuzzyWatchPushDelayTaskEngine; - public NamingSubscriberServiceV2Impl(ClientManagerDelegate clientManager, - ClientServiceIndexesManager indexesManager, NamingFuzzyWatchContextService namingFuzzyWatchContextService, ServiceStorage serviceStorage, + ClientServiceIndexesManager indexesManager, ServiceStorage serviceStorage, NamingMetadataManager metadataManager, PushExecutorDelegate pushExecutor, SwitchDomain switchDomain) { this.clientManager = clientManager; this.indexesManager = indexesManager; this.delayTaskEngine = new PushDelayTaskExecuteEngine(clientManager, indexesManager, serviceStorage, metadataManager, pushExecutor, switchDomain); - this.fuzzyWatchPushDelayTaskEngine = new FuzzyWatchPushDelayTaskEngine( - namingFuzzyWatchContextService, - serviceStorage, metadataManager, pushExecutor, switchDomain); NotifyCenter.registerSubscriber(this, NamingEventPublisherFactory.getInstance()); } @@ -120,7 +108,6 @@ public List> subscribeTypes() { List> result = new LinkedList<>(); result.add(ServiceEvent.ServiceChangedEvent.class); result.add(ServiceEvent.ServiceSubscribedEvent.class); - result.add(ServiceEvent.ServiceFuzzyWatchInitEvent.class); return result; } @@ -131,7 +118,6 @@ public void onEvent(Event event) { ServiceEvent.ServiceChangedEvent serviceChangedEvent = (ServiceEvent.ServiceChangedEvent) event; Service service = serviceChangedEvent.getService(); delayTaskEngine.addTask(service, new PushDelayTask(service, PushConfig.getInstance().getPushTaskDelay())); - genetateFuzzyWatchChangeNotifyTask(service,serviceChangedEvent.getChangedType()); MetricsMonitor.incrementServiceChangeCount(service); } else if (event instanceof ServiceEvent.ServiceSubscribedEvent) { // If service is subscribed by one client, only push this client. @@ -139,43 +125,7 @@ public void onEvent(Event event) { Service service = subscribedEvent.getService(); delayTaskEngine.addTask(service, new PushDelayTask(service, PushConfig.getInstance().getPushTaskDelay(), subscribedEvent.getClientId())); - } else if (event instanceof ServiceEvent.ServiceFuzzyWatchInitEvent) { - ServiceEvent.ServiceFuzzyWatchInitEvent serviceFuzzyWatchInitEvent = (ServiceEvent.ServiceFuzzyWatchInitEvent) event; - - String completedPattern = serviceFuzzyWatchInitEvent.getPattern(); - String clientId = serviceFuzzyWatchInitEvent.getClientId(); - String taskKey = getTaskKey(clientId, completedPattern); - Collection matchedService = serviceFuzzyWatchInitEvent.getMatchedService().stream() - .map(Service::getGroupedServiceName) - .collect(Collectors.toSet()); - int originSize = matchedService.size(); - // watch init push task is specify by client id with pattern - // The key is just used to differentiate between different initialization tasks and merge them if needed. - FuzzyWatchInitNotifyTask fuzzyWatchInitNotifyTask = new FuzzyWatchInitNotifyTask(clientId, completedPattern, - matchedService, originSize, PushConfig.getInstance().getPushTaskDelay(), false); - fuzzyWatchPushDelayTaskEngine.addTask(FuzzyWatchPushDelayTaskEngine.getTaskKey(fuzzyWatchInitNotifyTask), fuzzyWatchInitNotifyTask); - } - } - - private void genetateFuzzyWatchChangeNotifyTask(Service service,String changedType){ - // watch notify push task specify by service - for (String clientId : fuzzyWatchPushDelayTaskEngine.getNamingFuzzyWatchContextService() - .getFuzzyWatchedClients(service)) { - String serviceKey=NamingUtils.getServiceKey(service.getNamespace(),service.getGroup(),service.getName()); - FuzzyWatchChangeNotifyTask fuzzyWatchChangeNotifyTask = new FuzzyWatchChangeNotifyTask(serviceKey, changedType, - clientId, PushConfig.getInstance().getPushTaskDelay()); - fuzzyWatchPushDelayTaskEngine.addTask(FuzzyWatchPushDelayTaskEngine.getTaskKey(fuzzyWatchChangeNotifyTask),fuzzyWatchChangeNotifyTask ); - } - } - - String getTaskKey(String clientId, String pattern) { - if (StringUtils.isBlank(clientId)) { - throw new IllegalArgumentException("Param 'clientId' is illegal, clientId is blank"); - } - if (StringUtils.isBlank(pattern)) { - throw new IllegalArgumentException("Param 'pattern' is illegal, pattern is blank"); } - return clientId + Constants.SERVICE_INFO_SPLITER + pattern; } private Stream getServiceStream() { diff --git a/naming/src/main/java/com/alibaba/nacos/naming/push/v2/executor/PushExecutor.java b/naming/src/main/java/com/alibaba/nacos/naming/push/v2/executor/PushExecutor.java index 375fdd2b169..f081cc32b59 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/push/v2/executor/PushExecutor.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/push/v2/executor/PushExecutor.java @@ -17,7 +17,6 @@ package com.alibaba.nacos.naming.push.v2.executor; import com.alibaba.nacos.api.naming.remote.request.AbstractFuzzyWatchNotifyRequest; -import com.alibaba.nacos.api.naming.remote.request.FuzzyWatchNotifyInitRequest; import com.alibaba.nacos.api.remote.PushCallBack; import com.alibaba.nacos.naming.pojo.Subscriber; import com.alibaba.nacos.naming.push.v2.PushDataWrapper; 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 a4f4f16df46..a077323f16d 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 @@ -16,19 +16,21 @@ package com.alibaba.nacos.naming.push.v2.task; -import com.alibaba.nacos.api.naming.remote.request.FuzzyWatchNotifyChangeRequest; +import com.alibaba.nacos.api.naming.remote.request.NamingFuzzyWatchChangeNotifyRequest; import com.alibaba.nacos.api.remote.PushCallBack; import com.alibaba.nacos.common.task.AbstractExecuteTask; 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.FUZZY_WATCH_RESOURCE_CHANGED; + /** * Nacos naming fuzzy watch notify service change push delay task. * * @author tanyongquan */ -public class FuzzyWatchChangeNotifyExecuteTask extends AbstractExecuteTask{ +public class FuzzyWatchChangeNotifyExecuteTask extends AbstractExecuteTask { private final String serviceKey; @@ -38,19 +40,20 @@ public class FuzzyWatchChangeNotifyExecuteTask extends AbstractExecuteTask{ private final FuzzyWatchPushDelayTaskEngine delayTaskEngine; - public FuzzyWatchChangeNotifyExecuteTask(FuzzyWatchPushDelayTaskEngine delayTaskEngine,String serviceKey, String changedType, String targetClient) { + public FuzzyWatchChangeNotifyExecuteTask(FuzzyWatchPushDelayTaskEngine delayTaskEngine, String serviceKey, + String changedType, String targetClient) { this.serviceKey = serviceKey; this.changedType = changedType; - this.clientId=targetClient; - this.delayTaskEngine=delayTaskEngine; + this.clientId = targetClient; + this.delayTaskEngine = delayTaskEngine; } @Override public void run() { delayTaskEngine.getPushExecutor().doFuzzyWatchNotifyPushWithCallBack(clientId, - new FuzzyWatchNotifyChangeRequest(serviceKey,changedType), - new FuzzyWatchChangeNotifyCallback(clientId, serviceKey,changedType)); + new NamingFuzzyWatchChangeNotifyRequest(serviceKey, changedType, FUZZY_WATCH_RESOURCE_CHANGED), + new FuzzyWatchChangeNotifyCallback(clientId, serviceKey, changedType)); } @@ -58,16 +61,16 @@ private class FuzzyWatchChangeNotifyCallback implements PushCallBack { private final String clientId; - private String service; + private String service; private String serviceChangedType; - - private FuzzyWatchChangeNotifyCallback(String clientId,String service, String serviceChangedType) { + + private FuzzyWatchChangeNotifyCallback(String clientId, String service, String serviceChangedType) { this.clientId = clientId; - this.service=service; - this.serviceChangedType=serviceChangedType; - + this.service = service; + this.serviceChangedType = serviceChangedType; + } @Override @@ -84,8 +87,9 @@ public void onSuccess() { public void onFail(Throwable e) { if (!(e instanceof NoRequiredRetryException)) { Loggers.PUSH.error("fuzzy watch fail , reason detail: ", e); - delayTaskEngine.addTask(System.currentTimeMillis(), new FuzzyWatchChangeNotifyTask( service,serviceChangedType,clientId, - PushConfig.getInstance().getPushTaskRetryDelay())); + delayTaskEngine.addTask(System.currentTimeMillis(), + new FuzzyWatchChangeNotifyTask(service, serviceChangedType, clientId, + PushConfig.getInstance().getPushTaskRetryDelay())); } } } diff --git a/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchChangeNotifyTask.java b/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchChangeNotifyTask.java index e9264ad5833..317d98ce48c 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchChangeNotifyTask.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchChangeNotifyTask.java @@ -17,8 +17,7 @@ package com.alibaba.nacos.naming.push.v2.task; import com.alibaba.nacos.common.task.AbstractDelayTask; -import com.alibaba.nacos.common.task.AbstractExecuteTask; -import com.alibaba.nacos.naming.core.v2.pojo.Service; + /** * Nacos naming fuzzy watch notify service change push delay task. diff --git a/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchInitNotifyExecuteTask.java b/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchInitNotifyExecuteTask.java deleted file mode 100644 index d2349ca6994..00000000000 --- a/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchInitNotifyExecuteTask.java +++ /dev/null @@ -1,194 +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.naming.push.v2.task; - -import com.alibaba.nacos.api.naming.remote.request.FuzzyWatchNotifyInitRequest; -import com.alibaba.nacos.api.remote.PushCallBack; -import com.alibaba.nacos.common.task.AbstractExecuteTask; -import com.alibaba.nacos.naming.misc.Loggers; -import com.alibaba.nacos.naming.push.v2.NoRequiredRetryException; -import com.alibaba.nacos.naming.push.v2.PushConfig; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashSet; -import java.util.Set; - -/** - * Nacos naming fuzzy watch initial push execute task. - * - * @author tanyongquan - */ -public class FuzzyWatchInitNotifyExecuteTask extends AbstractExecuteTask { - - private final String clientId; - - private final String pattern; - - private final FuzzyWatchPushDelayTaskEngine fuzzyWatchPushDelayTaskEngine; - - private final FuzzyWatchInitNotifyTask 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; - - private final int latch; - - /** - * TODO set batch size from config. - */ - private final int batchSize = 10; - - private int sendCount; - - private boolean isFinishInitTask; - - private boolean haveFailPush; - - public FuzzyWatchInitNotifyExecuteTask( String clientId, String pattern, int originSize, - FuzzyWatchPushDelayTaskEngine fuzzyWatchPushDelayTaskEngine, FuzzyWatchInitNotifyTask delayTask, boolean isFinishInitTask) { - this.clientId = clientId; - this.pattern = pattern; - this.fuzzyWatchPushDelayTaskEngine = fuzzyWatchPushDelayTaskEngine; - this.delayTask = delayTask; - this.originSize = originSize; - this.latch = delayTask.getMatchedService().size(); - this.sendCount = 0; - this.isFinishInitTask = isFinishInitTask; - this.haveFailPush = false; - } - - @Override - public void run() { - Collection> dividedServices = divideServiceByBatch(delayTask.getMatchedService()); - - if (isFinishInitTask || delayTask.getMatchedService().isEmpty()) { - // do not match any exist service, just finish init - fuzzyWatchPushDelayTaskEngine.getPushExecutor().doFuzzyWatchNotifyPushWithCallBack(clientId, - FuzzyWatchNotifyInitRequest.buildInitFinishRequest(pattern), - new FuzzyWatchInitNotifyCallback(clientId, null, originSize, true, haveFailPush)); - } else { - for (Collection batchData : dividedServices) { - fuzzyWatchPushDelayTaskEngine.getPushExecutor().doFuzzyWatchNotifyPushWithCallBack(clientId, FuzzyWatchNotifyInitRequest.buildInitRequest( - pattern, batchData), - new FuzzyWatchInitNotifyCallback(clientId, batchData, originSize, false, haveFailPush)); - } - } - - } - - private Collection> divideServiceByBatch(Collection matchedService) { - Collection> result = new ArrayList<>(); - if (matchedService.isEmpty()) { - return result; - } - Set currentBatch = new HashSet<>(); - for (String groupedServiceName : matchedService) { - currentBatch.add(groupedServiceName); - if (currentBatch.size() >= this.batchSize) { - result.add(currentBatch); - currentBatch = new HashSet<>(); - } - } - if (!currentBatch.isEmpty()) { - result.add(currentBatch); - } - return result; - } - - private class FuzzyWatchInitNotifyCallback implements PushCallBack { - - private final String clientId; - - private final Collection groupedServiceName; - - private final int originSize; - - /** - * Record the push task execute start time. - */ - private final long executeStartTime; - - private boolean isFinishInitTask; - - private boolean haveFailPush; - - private FuzzyWatchInitNotifyCallback(String clientId, Collection groupedServiceName, int originSize, - boolean isFinishInitTask, boolean haveFailPush) { - this.clientId = clientId; - this.groupedServiceName = groupedServiceName; - this.originSize = originSize; - this.executeStartTime = System.currentTimeMillis(); - this.isFinishInitTask = isFinishInitTask; - this.haveFailPush = haveFailPush; - } - - @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-INIT-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 {}, push size {} : {}", - pushCostTimeForNetWork, pushCostTimeForAll, clientId, pattern, groupedServiceName.size(), - groupedServiceName); - sendCount += groupedServiceName.size(); - // this task is an init push task(not finish notify), and with no failure in this task when executing push batched services - if (!haveFailPush && sendCount >= latch) { - fuzzyWatchPushDelayTaskEngine.addTask(System.currentTimeMillis(), new FuzzyWatchInitNotifyTask(clientId, pattern, null, - originSize, PushConfig.getInstance().getPushTaskDelay(), true)); - } - } - } - - @Override - public void onFail(Throwable e) { - long pushCostTime = System.currentTimeMillis() - executeStartTime; - Loggers.PUSH.error("[FUZZY-WATCH-PUSH-FAIL] {}ms, pattern {} match {} service: {}, reason={}, client={}", pushCostTime, pattern, - groupedServiceName.size(), groupedServiceName, e.getMessage(), clientId); - setHaveFailPush(true); - if (!(e instanceof NoRequiredRetryException)) { - Loggers.PUSH.error("Reason detail: ", e); - if (isFinishInitTask) { - fuzzyWatchPushDelayTaskEngine.addTask(System.currentTimeMillis(),new FuzzyWatchInitNotifyTask(clientId, pattern, null, - originSize, PushConfig.getInstance().getPushTaskRetryDelay(), true)); - } else { - fuzzyWatchPushDelayTaskEngine.addTask(System.currentTimeMillis(),new FuzzyWatchInitNotifyTask(clientId, pattern, groupedServiceName, - originSize, PushConfig.getInstance().getPushTaskRetryDelay(), false)); - } - - } - } - } - - private void setHaveFailPush(boolean haveFailPush) { - this.haveFailPush = haveFailPush; - } -} \ No newline at end of file diff --git a/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchInitNotifyTask.java b/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchInitNotifyTask.java deleted file mode 100644 index 96a166ad93c..00000000000 --- a/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchInitNotifyTask.java +++ /dev/null @@ -1,84 +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.naming.push.v2.task; - -import com.alibaba.nacos.common.task.AbstractDelayTask; -import com.alibaba.nacos.naming.misc.Loggers; - -import java.util.Collection; - -/** - * Nacos naming fuzzy watch initial push delay task. - * - * @author tanyongquan - */ -public class FuzzyWatchInitNotifyTask extends AbstractDelayTask { - - private final String clientId; - - private final String pattern; - - private final Collection matchedService; - - private final int originSize; - - private final boolean isFinishInit; - - public FuzzyWatchInitNotifyTask(String clientId, String pattern, Collection matchedService, - int originSize, long delay, boolean isFinishInit) { - this.clientId = clientId; - this.pattern = pattern; - this.matchedService = matchedService; - this.originSize = originSize; - this.isFinishInit = isFinishInit; - setTaskInterval(delay); - setLastProcessTime(System.currentTimeMillis()); - } - - @Override - public void merge(AbstractDelayTask task) { - if (!(task instanceof FuzzyWatchInitNotifyTask)) { - return; - } - FuzzyWatchInitNotifyTask oldTask = (FuzzyWatchInitNotifyTask) task; - if (!isFinishInit) { - matchedService.addAll(oldTask.getMatchedService()); - } - setLastProcessTime(Math.min(getLastProcessTime(), task.getLastProcessTime())); - Loggers.PUSH.info("[FUZZY-WATCH-INIT-PUSH] Task merge for pattern {}", pattern); - } - - public String getPattern() { - return pattern; - } - - public Collection getMatchedService() { - return matchedService; - } - - public boolean isFinishInit() { - return isFinishInit; - } - - public int getOriginSize() { - return originSize; - } - - public String getClientId() { - return clientId; - } -} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchPushDelayTaskEngine.java b/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchPushDelayTaskEngine.java index 5a790c35704..d879625d647 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchPushDelayTaskEngine.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchPushDelayTaskEngine.java @@ -20,53 +20,35 @@ import com.alibaba.nacos.common.task.NacosTask; import com.alibaba.nacos.common.task.NacosTaskProcessor; import com.alibaba.nacos.common.task.engine.NacosDelayTaskExecuteEngine; -import com.alibaba.nacos.naming.core.v2.index.NamingFuzzyWatchContextService; -import com.alibaba.nacos.naming.core.v2.index.ServiceStorage; -import com.alibaba.nacos.naming.core.v2.metadata.NamingMetadataManager; -import com.alibaba.nacos.naming.core.v2.pojo.Service; + import com.alibaba.nacos.naming.misc.Loggers; import com.alibaba.nacos.naming.misc.NamingExecuteTaskDispatcher; import com.alibaba.nacos.naming.misc.SwitchDomain; import com.alibaba.nacos.naming.push.v2.executor.PushExecutor; +import org.springframework.stereotype.Component; /** * Nacos naming fuzzy watch notify service change push delay task execute engine. * * @author tanyongquan */ +@Component public class FuzzyWatchPushDelayTaskEngine extends NacosDelayTaskExecuteEngine { - private final NamingFuzzyWatchContextService namingFuzzyWatchContextService; - - private final ServiceStorage serviceStorage; - - private final NamingMetadataManager metadataManager; private final PushExecutor pushExecutor; private final SwitchDomain switchDomain; - public FuzzyWatchPushDelayTaskEngine(NamingFuzzyWatchContextService namingFuzzyWatchContextService, - ServiceStorage serviceStorage, NamingMetadataManager metadataManager, PushExecutor pushExecutor, + public FuzzyWatchPushDelayTaskEngine( + PushExecutor pushExecutor, SwitchDomain switchDomain) { super(FuzzyWatchPushDelayTaskEngine.class.getSimpleName(), Loggers.PUSH); - this.namingFuzzyWatchContextService = namingFuzzyWatchContextService; - this.serviceStorage = serviceStorage; - this.metadataManager = metadataManager; this.pushExecutor = pushExecutor; this.switchDomain = switchDomain; setDefaultTaskProcessor(new WatchPushDelayTaskProcessor(this)); } - - - public ServiceStorage getServiceStorage() { - return serviceStorage; - } - - public NamingMetadataManager getMetadataManager() { - return metadataManager; - } - + public PushExecutor getPushExecutor() { return pushExecutor; } @@ -82,10 +64,10 @@ protected void processTasks() { private static class WatchPushDelayTaskProcessor implements NacosTaskProcessor { - private final FuzzyWatchPushDelayTaskEngine executeEngine; + private final FuzzyWatchPushDelayTaskEngine fuzztWatchPushExecuteEngine; - public WatchPushDelayTaskProcessor(FuzzyWatchPushDelayTaskEngine executeEngine) { - this.executeEngine = executeEngine; + public WatchPushDelayTaskProcessor(FuzzyWatchPushDelayTaskEngine fuzztWatchPushExecuteEngine) { + this.fuzztWatchPushExecuteEngine = fuzztWatchPushExecuteEngine; } @Override @@ -96,32 +78,27 @@ public boolean process(NacosTask task) { FuzzyWatchChangeNotifyTask fuzzyWatchChangeNotifyTask = (FuzzyWatchChangeNotifyTask) task; NamingExecuteTaskDispatcher.getInstance().dispatchAndExecuteTask( getTaskKey(task), - new FuzzyWatchChangeNotifyExecuteTask(executeEngine, fuzzyWatchChangeNotifyTask.getServiceKey(), + new FuzzyWatchChangeNotifyExecuteTask(fuzztWatchPushExecuteEngine, fuzzyWatchChangeNotifyTask.getServiceKey(), fuzzyWatchChangeNotifyTask.getChangedType(), fuzzyWatchChangeNotifyTask.getClientId())); - } else if (task instanceof FuzzyWatchInitNotifyTask) { + } else if (task instanceof FuzzyWatchSyncNotifyTask) { //process fuzzy watch init notify when a new client fuzzy watch a pattern - FuzzyWatchInitNotifyTask fuzzyWatchInitNotifyTask = (FuzzyWatchInitNotifyTask) task; - String pattern = fuzzyWatchInitNotifyTask.getPattern(); - String clientId = fuzzyWatchInitNotifyTask.getClientId(); + FuzzyWatchSyncNotifyTask fuzzyWatchSyncNotifyTask = (FuzzyWatchSyncNotifyTask) task; + String pattern = fuzzyWatchSyncNotifyTask.getPattern(); + String clientId = fuzzyWatchSyncNotifyTask.getClientId(); NamingExecuteTaskDispatcher.getInstance().dispatchAndExecuteTask(getTaskKey(task), - new FuzzyWatchInitNotifyExecuteTask(clientId, pattern, - fuzzyWatchInitNotifyTask.getOriginSize(), executeEngine, fuzzyWatchInitNotifyTask, - fuzzyWatchInitNotifyTask.isFinishInit())); + new FuzzyWatchSyncNotifyExecuteTask(clientId, pattern, + fuzztWatchPushExecuteEngine, fuzzyWatchSyncNotifyTask)); } return true; } } - public NamingFuzzyWatchContextService getNamingFuzzyWatchContextService() { - return namingFuzzyWatchContextService; - } - public static String getTaskKey(NacosTask task){ if(task instanceof FuzzyWatchChangeNotifyTask){ return "fwcnT-"+((FuzzyWatchChangeNotifyTask) task).getClientId()+((FuzzyWatchChangeNotifyTask) task).getServiceKey(); - }else if (task instanceof FuzzyWatchInitNotifyTask){ - return "fwinT-"+((FuzzyWatchInitNotifyTask) task).getClientId()+((FuzzyWatchInitNotifyTask) task).getPattern(); + }else if (task instanceof FuzzyWatchSyncNotifyTask){ + return "fwsnT-"+((FuzzyWatchSyncNotifyTask) task).getSyncType()+"-"+((FuzzyWatchSyncNotifyTask) task).getClientId()+((FuzzyWatchSyncNotifyTask) task).getPattern()+"-"+((FuzzyWatchSyncNotifyTask) task).getCurrentBatch(); }else { throw new NacosRuntimeException(500,"unknown fuzzy task type"); } 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 new file mode 100644 index 00000000000..5f372fdb271 --- /dev/null +++ b/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchSyncNotifyExecuteTask.java @@ -0,0 +1,149 @@ +/* + * 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.naming.remote.request.NamingFuzzyWatchSyncRequest; +import com.alibaba.nacos.api.remote.PushCallBack; +import com.alibaba.nacos.common.task.AbstractExecuteTask; +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. + * + * @author tanyongquan + */ +public class FuzzyWatchSyncNotifyExecuteTask extends AbstractExecuteTask { + + private final String clientId; + + private final String pattern; + + private final FuzzyWatchPushDelayTaskEngine fuzzyWatchPushDelayTaskEngine; + + 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 + public void run() { + + NamingFuzzyWatchSyncRequest namingFuzzyWatchSyncRequest = NamingFuzzyWatchSyncRequest.buildSyncNotifyRequest( + pattern, delayTask.getSyncType(), delayTask.getSyncServiceKeys(), delayTask.getTotalBatch(), 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 FuzzyWatchSyncNotifyTask.BatchTaskCounter batchTaskCounter ; + + private FuzzyWatchSyncNotifyCallback(NamingFuzzyWatchSyncRequest namingFuzzyWatchSyncRequest, + FuzzyWatchSyncNotifyTask.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); + + } + } + } + + +} \ 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 new file mode 100644 index 00000000000..6566b2ff243 --- /dev/null +++ b/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchSyncNotifyTask.java @@ -0,0 +1,139 @@ +/* + * 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.naming.remote.request.NamingFuzzyWatchSyncRequest; +import com.alibaba.nacos.common.task.AbstractDelayTask; +import com.alibaba.nacos.naming.misc.Loggers; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * Nacos naming fuzzy watch initial push delay task. + * + * @author tanyongquan + */ +public class FuzzyWatchSyncNotifyTask extends AbstractDelayTask { + + private final String clientId; + + private final String pattern; + + private final Set syncServiceKeys; + + private final String syncType; + + private int totalBatch; + + private int currentBatch; + + private BatchTaskCounter batchTaskCounter; + + public FuzzyWatchSyncNotifyTask(String clientId, String pattern, String syncType, Set syncServiceKeys, long delay) { + this.clientId = clientId; + this.pattern = pattern; + this.syncType=syncType; + this.syncServiceKeys = syncServiceKeys; + setTaskInterval(delay); + setLastProcessTime(System.currentTimeMillis()); + } + + public int getTotalBatch() { + return totalBatch; + } + + public void setTotalBatch(int totalBatch) { + this.totalBatch = totalBatch; + } + + public int getCurrentBatch() { + return currentBatch; + } + + public void setCurrentBatch(int currentBatch) { + this.currentBatch = currentBatch; + } + + @Override + public void merge(AbstractDelayTask task) { + if (!(task instanceof FuzzyWatchSyncNotifyTask)) { + return; + } + FuzzyWatchSyncNotifyTask oldTask = (FuzzyWatchSyncNotifyTask) task; + syncServiceKeys.addAll(oldTask.getSyncServiceKeys()); + setLastProcessTime(Math.min(getLastProcessTime(), task.getLastProcessTime())); + Loggers.PUSH.info("[FUZZY-WATCH-INIT-PUSH] Task merge for pattern {}", pattern); + } + + public String getPattern() { + return pattern; + } + + public Set getSyncServiceKeys() { + return syncServiceKeys; + } + + public String getSyncType() { + return syncType; + } + + public String getClientId() { + return clientId; + } + + public BatchTaskCounter getBatchTaskCounter() { + return batchTaskCounter; + } + + public void setBatchTaskCounter(BatchTaskCounter batchTaskCounter) { + this.batchTaskCounter = batchTaskCounter; + } + + public static class BatchTaskCounter{ + + List batchCounter; + + public BatchTaskCounter(int totalBatch){ + initBatchCounter(totalBatch); + } + + public void initBatchCounter(int totalBatch){ + batchCounter=new ArrayList<>(totalBatch); + for(int i=0;i - 3.0.0-ALPHA-SNAPSHOT + 3.0.0-ALPHA-lzf-SNAPSHOT UTF-8 UTF-8 From 065ee675e52a3b6ed3b301077b32eb37d3b95fca Mon Sep 17 00:00:00 2001 From: "zunfei.lzf" Date: Wed, 8 Jan 2025 14:40:19 +0800 Subject: [PATCH 08/15] naming fuzzy watch optimize --- .../config/listener/ConfigFuzzyWatcher.java | 54 -------- .../listener/FuzzyWatchChangeEvent.java | 28 +++-- .../listener/FuzzyWatchEventWatcher.java | 14 +-- .../NamingFuzzyWatchChangeNotifyRequest.java | 4 + .../request/NamingFuzzyWatchSyncRequest.java | 3 + .../client/config/impl/ClientWorker.java | 1 + .../config/impl/ConfigFuzzyWatchContext.java | 99 +++++++++------ .../impl/ConfigFuzzyWatchGroupKeyHolder.java | 25 ++-- .../impl/ConfigFuzzyWatcherWrapper.java | 71 +++++++++++ .../client/naming/NacosNamingService.java | 9 -- .../cache/FuzzyWatchEventWatcherWrapper.java | 61 +++++++++ .../naming/cache/NamingFuzzyWatchContext.java | 117 +++++++----------- .../NamingFuzzyWatchServiceListHolder.java | 32 ++--- .../NamingFuzzyWatchNotifyRequestHandler.java | 7 +- .../remote/gprc/redo/FuzzyWatchSyncTask.java | 76 ------------ .../common/utils/FuzzyGroupKeyPattern.java | 18 +-- .../v2/index/ClientServiceIndexesManager.java | 9 +- .../index/NamingFuzzyWatchContextService.java | 81 ++++++------ .../push/NamingFuzzyWatchChangeNotifier.java | 21 +++- .../push/NamingFuzzyWatchSyncNotifier.java | 3 + .../FuzzyWatchChangeNotifyExecuteTask.java | 21 ++-- .../task/FuzzyWatchPushDelayTaskEngine.java | 5 +- .../v2/task/FuzzyWatchSyncNotifyTask.java | 12 +- .../NamingFuzzyWatchRequestHandler.java | 3 + 24 files changed, 398 insertions(+), 376 deletions(-) create mode 100644 client/src/main/java/com/alibaba/nacos/client/config/impl/ConfigFuzzyWatcherWrapper.java create mode 100644 client/src/main/java/com/alibaba/nacos/client/naming/cache/FuzzyWatchEventWatcherWrapper.java delete mode 100644 client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/redo/FuzzyWatchSyncTask.java diff --git a/api/src/main/java/com/alibaba/nacos/api/config/listener/ConfigFuzzyWatcher.java b/api/src/main/java/com/alibaba/nacos/api/config/listener/ConfigFuzzyWatcher.java index d38826044da..d6ecc9e641a 100644 --- a/api/src/main/java/com/alibaba/nacos/api/config/listener/ConfigFuzzyWatcher.java +++ b/api/src/main/java/com/alibaba/nacos/api/config/listener/ConfigFuzzyWatcher.java @@ -16,10 +16,6 @@ package com.alibaba.nacos.api.config.listener; -import java.util.HashSet; -import java.util.Objects; -import java.util.Set; -import java.util.UUID; import java.util.concurrent.Executor; /** @@ -31,22 +27,6 @@ */ public abstract class ConfigFuzzyWatcher { - /** - * Unique identifier for the listener. - */ - String uuid= UUID.randomUUID().toString(); - - private Set syncGroupKeys = new HashSet<>(); - - /** - * Get the UUID (Unique Identifier) of the listener. - * - * @return The UUID of the listener - */ - public String getUuid() { - return uuid; - } - /** * Callback method invoked when a fuzzy configuration change event occurs. * @@ -54,7 +34,6 @@ public String getUuid() { */ public abstract void onEvent(ConfigFuzzyWatchChangeEvent event); - /** * Get executor for execute this receive. * @@ -64,37 +43,4 @@ public Executor getExecutor(){ return null; } - /** - * Compute the hash code for this listener based on its UUID. - * - * @return The hash code value for this listener - */ - @Override - public int hashCode() { - return Objects.hashCode(uuid); - } - - /** - * Compare this listener to the specified object for equality. Two listeners are considered equal if they have the - * same UUID. - * - * @param o The object to compare to - * @return true if the specified object is equal to this listener, false otherwise - */ - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - ConfigFuzzyWatcher that = (ConfigFuzzyWatcher) o; - return Objects.equals(uuid, that.uuid); - } - - public final Set getSyncGroupKeys() { - return syncGroupKeys; - } - } diff --git a/api/src/main/java/com/alibaba/nacos/api/naming/listener/FuzzyWatchChangeEvent.java b/api/src/main/java/com/alibaba/nacos/api/naming/listener/FuzzyWatchChangeEvent.java index e31de48d1a6..13ecc20d524 100644 --- a/api/src/main/java/com/alibaba/nacos/api/naming/listener/FuzzyWatchChangeEvent.java +++ b/api/src/main/java/com/alibaba/nacos/api/naming/listener/FuzzyWatchChangeEvent.java @@ -48,31 +48,35 @@ public String getServiceName() { return serviceName; } - public void setServiceName(String serviceName) { - this.serviceName = serviceName; - } public String getGroupName() { return groupName; } - public void setGroupName(String groupName) { - this.groupName = groupName; - } public String getNamespace() { return namespace; } - public void setNamespace(String namespace) { - this.namespace = namespace; - } - + /** + * The change type of local watcher , contains {"ADD_SERVICE", "DELETE_SERVICE"}. see Constants.ServiceChangedType + */ public String getChangeType() { return changeType; } - public void setChangeType(String changeType) { - this.changeType = changeType; + /** + * the sync type that trigger this changed,contains {"FUZZY_WATCH_INIT_NOTIFY","FUZZY_WATCH_RESOURCE_CHANGED", "FUZZY_WATCH_DIFF_SYNC_NOTIFY"} + * @return + */ + public String getSyncType() { + return syncType; + } + + @Override + public String toString() { + return "FuzzyWatchChangeEvent{" + "serviceName='" + serviceName + '\'' + ", groupName='" + groupName + '\'' + + ", namespace='" + namespace + '\'' + ", changeType='" + changeType + '\'' + ", syncType='" + syncType + + '\'' + '}'; } } diff --git a/api/src/main/java/com/alibaba/nacos/api/naming/listener/FuzzyWatchEventWatcher.java b/api/src/main/java/com/alibaba/nacos/api/naming/listener/FuzzyWatchEventWatcher.java index 48ef69b0bad..4a1eb20413b 100644 --- a/api/src/main/java/com/alibaba/nacos/api/naming/listener/FuzzyWatchEventWatcher.java +++ b/api/src/main/java/com/alibaba/nacos/api/naming/listener/FuzzyWatchEventWatcher.java @@ -29,23 +29,11 @@ */ public abstract class FuzzyWatchEventWatcher { - String uuid= UUID.randomUUID().toString(); - - private Set syncServiceKeys = new HashSet<>(); - - + public Executor getExecutor() { return null; } - public final String getUuid() { - return uuid; - } - - public Set getSyncServiceKeys() { - return Collections.unmodifiableSet(syncServiceKeys); - } - public abstract void onEvent(FuzzyWatchChangeEvent event); } 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 e067373782e..98eaec76cd9 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 @@ -27,6 +27,10 @@ public class NamingFuzzyWatchChangeNotifyRequest extends AbstractFuzzyWatchNotif private String changedType; + public NamingFuzzyWatchChangeNotifyRequest(){ + + } + public NamingFuzzyWatchChangeNotifyRequest(String serviceKey,String changedType, String syncType) { super(syncType); this.serviceKey=serviceKey; 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 ddbc2a354b8..5e2bd2fee58 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 @@ -38,6 +38,9 @@ public class NamingFuzzyWatchSyncRequest extends AbstractFuzzyWatchNotifyRequest private int currentBatch; + public NamingFuzzyWatchSyncRequest(){ + + } public NamingFuzzyWatchSyncRequest(String pattern, String syncType,Set contexts){ super(syncType); this.groupKeyPattern=pattern; 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 61686b235e2..a18b068fd3c 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 @@ -523,6 +523,7 @@ public ClientWorker(final ConfigFilterChainManager configFilterChainManager, Con new NameThreadFactory("com.alibaba.nacos.client.Worker")); agent.setExecutor(executorService); agent.start(); + configFuzzyWatchGroupKeyHolder.start(); } 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 6305b3f27f3..0b40df1c899 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 @@ -16,7 +16,6 @@ package com.alibaba.nacos.client.config.impl; -import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.config.listener.ConfigFuzzyWatchChangeEvent; import com.alibaba.nacos.api.config.listener.ConfigFuzzyWatcher; import com.alibaba.nacos.client.config.common.GroupKey; @@ -28,13 +27,17 @@ import java.util.Collections; import java.util.HashSet; +import java.util.Iterator; import java.util.List; import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.concurrent.atomic.AtomicBoolean; +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; /** @@ -94,7 +97,7 @@ public class ConfigFuzzyWatchContext { /** * Set of listeners associated with the context. */ - private Set configFuzzyWatchers = new HashSet<>(); + private Set configFuzzyWatcherWrappers = new HashSet<>(); /** * Constructor with environment name, data ID pattern, and group. @@ -113,12 +116,12 @@ public ConfigFuzzyWatchContext(String envName, String groupKeyPattern) { * @param uuid UUID to filter listeners * @return Set of listeners to notify */ - public Set calculateListenersToNotify(String uuid) { - Set listenersToNotify = new HashSet<>(); + public Set calculateListenersToNotify(String uuid) { + Set listenersToNotify = new HashSet<>(); if (StringUtils.isEmpty(uuid)) { - listenersToNotify = configFuzzyWatchers; + listenersToNotify = configFuzzyWatcherWrappers; } else { - for (ConfigFuzzyWatcher listener : configFuzzyWatchers) { + for (ConfigFuzzyWatcherWrapper listener : configFuzzyWatcherWrappers) { if (uuid.equals(listener.getUuid())) { listenersToNotify.add(listener); } @@ -130,45 +133,61 @@ public Set calculateListenersToNotify(String uuid) { /** * Notify the listener with the specified data ID, type, and UUID. * - * @param dataId Data ID + * @param groupKey groupKey * @param uuid UUID to filter listeners */ - public void notifyWatcher(final String dataId, final String group, String tenant, final String changedType,final String syncType, + public void notifyWatcher(final String groupKey, final String changedType,final String syncType, final String uuid) { - Set listenersToNotify = calculateListenersToNotify(uuid); - doNotifyWatchers(dataId, group, tenant, changedType,syncType, listenersToNotify); + Set listenersToNotify = calculateListenersToNotify(uuid); + doNotifyWatchers(groupKey, changedType,syncType, listenersToNotify); } /** * Perform the notification for the specified data ID, type, and listeners. * - * @param dataId Data ID + * @param groupKey groupKey * @param listenersToNotify Set of listeners to notify */ - private void doNotifyWatchers(final String dataId, final String group, String tenant, final String changedType,final String syncType, - Set listenersToNotify) { - for (ConfigFuzzyWatcher watcher : listenersToNotify) { - doNotifyWatcher(dataId,group,tenant,changedType,syncType,watcher); + private void doNotifyWatchers(final String groupKey, final String changedType,final String syncType, + Set listenersToNotify) { + for (ConfigFuzzyWatcherWrapper watcher : listenersToNotify) { + doNotifyWatcher(groupKey,changedType,syncType,watcher); } } - private void doNotifyWatcher(final String dataId, final String group, String tenant, final String changedType,final String syncType, - ConfigFuzzyWatcher configFuzzyWatcher){ + private void doNotifyWatcher(final String groupKey, final String changedType,final String syncType, + ConfigFuzzyWatcherWrapper configFuzzyWatcher){ + + if (ADD_CONFIG.equals(changedType)&&configFuzzyWatcher.getSyncGroupKeys().contains(groupKey)){ + return; + } + + if (DELETE_CONFIG.equals(changedType)&&!configFuzzyWatcher.getSyncGroupKeys().contains(groupKey)){ + return; + } + + String[] parseKey = GroupKey.parseKey(groupKey); + String dataId=parseKey[0]; + String group=parseKey[1]; + + String tenant=parseKey[2]; + + final String resetSyncType=initializationCompleted.get()?syncType:FUZZY_WATCH_INIT_NOTIFY; AbstractFuzzyNotifyTask job = new AbstractFuzzyNotifyTask() { @Override public void run() { long start = System.currentTimeMillis(); - ConfigFuzzyWatchChangeEvent event = ConfigFuzzyWatchChangeEvent.build(tenant,group, dataId, changedType,syncType); + ConfigFuzzyWatchChangeEvent event = ConfigFuzzyWatchChangeEvent.build(tenant,group, dataId, changedType,resetSyncType); if (configFuzzyWatcher != null) { - configFuzzyWatcher.onEvent(event); + configFuzzyWatcher.configFuzzyWatcher.onEvent(event); } LOGGER.info( "[{}] [notify-watcher-ok] dataId={}, group={}, tenant={}, watcher={}, job run cost={} millis.", envName, dataId, group, tenant, configFuzzyWatcher, (System.currentTimeMillis() - start)); - if (changedType.equals(Constants.ConfigChangedType.DELETE_CONFIG)) { + if (changedType.equals(DELETE_CONFIG)) { configFuzzyWatcher.getSyncGroupKeys().remove(GroupKey.getKey(dataId, group, tenant)); } else if (changedType.equals( - Constants.ConfigChangedType.ADD_CONFIG)) { + ADD_CONFIG)) { configFuzzyWatcher.getSyncGroupKeys().add(GroupKey.getKey(dataId, group, tenant)); } @@ -176,12 +195,12 @@ public void run() { }; try { - if (null != configFuzzyWatcher.getExecutor()) { + if (null != configFuzzyWatcher.configFuzzyWatcher.getExecutor()) { LOGGER.info( "[{}] [notify-watcher] task submitted to user executor, dataId={}, group={}, tenant={}, listener={}.", envName, dataId, group, tenant, configFuzzyWatcher); job.async = true; - configFuzzyWatcher.getExecutor().execute(job); + configFuzzyWatcher.configFuzzyWatcher.getExecutor().execute(job); } else { LOGGER.info( "[{}] [notify-watcher] task execute in nacos thread, dataId={}, group={}, tenant={}, listener={}.", @@ -206,23 +225,29 @@ public void markInitializationComplete() { * @param watcher watcher to be removed */ public void removeWatcher(ConfigFuzzyWatcher watcher) { - configFuzzyWatchers.remove(watcher); - - LOGGER.info("[{}] [remove-watcher-ok] groupKeyPattern={}, watcher={},uuid={} ", getEnvName(), - this.groupKeyPattern, watcher, watcher.getUuid()); + + Iterator iterator = configFuzzyWatcherWrappers.iterator(); + while (iterator.hasNext()){ + ConfigFuzzyWatcherWrapper next = iterator.next(); + if (next.configFuzzyWatcher.equals(watcher)){ + iterator.remove(); + LOGGER.info("[{}] [remove-watcher-ok] groupKeyPattern={}, watcher={},uuid={} ", getEnvName(), + this.groupKeyPattern, watcher, next.getUuid()); + } + } } /** * Add a watcher to the context. * - * @param watcher watcher to be added + * @param configFuzzyWatcherWrapper watcher to be added */ - public boolean addWatcher(ConfigFuzzyWatcher watcher) { - boolean added=configFuzzyWatchers.add(watcher); + public boolean addWatcher(ConfigFuzzyWatcherWrapper configFuzzyWatcherWrapper) { + boolean added= configFuzzyWatcherWrappers.add(configFuzzyWatcherWrapper); if(added){ LOGGER.info("[{}] [add-watcher-ok] groupKeyPattern={}, watcher={},uuid={} ", getEnvName(), this.groupKeyPattern, - watcher, watcher.getUuid()); + configFuzzyWatcherWrapper.configFuzzyWatcher, configFuzzyWatcherWrapper.getUuid()); } return added; } @@ -331,8 +356,8 @@ public Set getReceivedGroupKeys() { * * @return Set of listeners */ - public Set getConfigFuzzyWatchers() { - return configFuzzyWatchers; + public Set getConfigFuzzyWatcherWrappers() { + return configFuzzyWatcherWrappers; } @@ -357,16 +382,14 @@ public boolean isAsync() { } void syncFuzzyWatchers(){ - for(ConfigFuzzyWatcher configFuzzyWatcher:configFuzzyWatchers){ + for(ConfigFuzzyWatcherWrapper configFuzzyWatcher: configFuzzyWatcherWrappers){ Set receivedGroupKeysContext = receivedGroupKeys; Set syncGroupKeys = configFuzzyWatcher.getSyncGroupKeys(); List groupKeyStates = FuzzyGroupKeyPattern.diffGroupKeys( receivedGroupKeysContext, syncGroupKeys); for(FuzzyGroupKeyPattern.GroupKeyState groupKeyState:groupKeyStates){ - String[] groupKeyItems = GroupKey.parseKey(groupKeyState.getGroupKey()); - - String changedType=groupKeyState.isExist()? Constants.ConfigChangedType.ADD_CONFIG:Constants.ConfigChangedType.DELETE_CONFIG; - doNotifyWatcher( groupKeyItems[0], groupKeyItems[1], groupKeyItems[2],changedType,FUZZY_WATCH_DIFF_SYNC_NOTIFY,configFuzzyWatcher); + 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 a454ad5e1a2..7bd0b3fc6f0 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 @@ -94,10 +94,8 @@ public void onEvent(Event event) { if (context == null) { return; } - - String[] parseKey = GroupKey.parseKey( - fuzzyWatchNotifyEvent.getGroupKey()); - context.notifyWatcher(parseKey[0], parseKey[1],parseKey[2], fuzzyWatchNotifyEvent.getChangedType(),fuzzyWatchNotifyEvent.getSyncType(), + + context.notifyWatcher(fuzzyWatchNotifyEvent.getGroupKey(), fuzzyWatchNotifyEvent.getChangedType(),fuzzyWatchNotifyEvent.getSyncType(), fuzzyWatchNotifyEvent.getWatcherUuid()); } @@ -107,6 +105,10 @@ public Class subscribeType() { } }); + + } + + public void start(){ agent.executor.schedule(() -> { while (!agent.executor.isShutdown() && !agent.executor.isTerminated()) { try { @@ -128,7 +130,6 @@ public Class subscribeType() { }, 0L, TimeUnit.MILLISECONDS); } - /** * Removes the fuzzy listen context for the specified data ID pattern and group. * @@ -137,21 +138,21 @@ public Class subscribeType() { public void removeFuzzyListenContext(String groupKeyPattern) { synchronized (fuzzyListenContextMap) { Map copy = new HashMap<>(fuzzyListenContextMap.get()); - ConfigFuzzyWatchContext removedContext = copy.remove(groupKeyPattern); - + copy.remove(groupKeyPattern); fuzzyListenContextMap.set(copy); } - LOGGER.info("[{}] [fuzzy-listen-unsubscribe] {}", agent.getName(), groupKeyPattern); + LOGGER.info("[{}] [fuzzy-watch-unsubscribe] {}", agent.getName(), groupKeyPattern); } public ConfigFuzzyWatchContext registerFuzzyWatcher(String dataIdPattern, String groupPattern, ConfigFuzzyWatcher configFuzzyWatcher){ ConfigFuzzyWatchContext configFuzzyWatchContext = initFuzzyWatchContextIfAbsent(dataIdPattern, groupPattern); - if(configFuzzyWatchContext.addWatcher(configFuzzyWatcher)){ + ConfigFuzzyWatcherWrapper configFuzzyWatcherWrapper=new ConfigFuzzyWatcherWrapper(configFuzzyWatcher); + if(configFuzzyWatchContext.addWatcher(configFuzzyWatcherWrapper)){ if(configFuzzyWatchContext.getReceivedGroupKeys()!=null){ for(String groupKey:configFuzzyWatchContext.getReceivedGroupKeys()){ FuzzyWatchNotifyEvent fuzzyWatchNotifyEvent = FuzzyWatchNotifyEvent.buildNotifyPatternAllListenersEvent(groupKey, - configFuzzyWatchContext.getGroupKeyPattern(), ADD_CONFIG,FUZZY_WATCH_INIT_NOTIFY,configFuzzyWatcher.getUuid()); + configFuzzyWatchContext.getGroupKeyPattern(), ADD_CONFIG,FUZZY_WATCH_INIT_NOTIFY,configFuzzyWatcherWrapper.getUuid()); NotifyCenter.publishEvent(fuzzyWatchNotifyEvent); } @@ -246,7 +247,7 @@ public void removeFuzzyWatcher(String dataIdPattern, String groupPattern, Config if (configFuzzyWatchContext != null) { synchronized (configFuzzyWatchContext) { configFuzzyWatchContext.removeWatcher(watcher); - if (configFuzzyWatchContext.getConfigFuzzyWatchers().isEmpty()) { + if (configFuzzyWatchContext.getConfigFuzzyWatcherWrappers().isEmpty()) { configFuzzyWatchContext.setDiscard(true); configFuzzyWatchContext.setConsistentWithServer(false); } @@ -419,7 +420,7 @@ private ConfigFuzzyWatchRequest buildFuzzyListenConfigRequest(ConfigFuzzyWatchCo ConfigFuzzyWatchRequest request = new ConfigFuzzyWatchRequest(); request.setGroupKeyPattern(context.getGroupKeyPattern()); request.setInitializing(context.isInitializing()); - request.setWatchType(context.isDiscard()?WATCH_TYPE_WATCH:WATCH_TYPE_CANCEL_WATCH); + request.setWatchType((context.isDiscard()&&CollectionUtils.isEmpty(context.getConfigFuzzyWatcherWrappers()))?WATCH_TYPE_CANCEL_WATCH:WATCH_TYPE_WATCH); request.setReceivedGroupKeys(context.getReceivedGroupKeys()); return request; } 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 new file mode 100644 index 00000000000..8520e260996 --- /dev/null +++ b/client/src/main/java/com/alibaba/nacos/client/config/impl/ConfigFuzzyWatcherWrapper.java @@ -0,0 +1,71 @@ +/* + * 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.ConfigFuzzyWatcher; + +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; +import java.util.UUID; + +public class ConfigFuzzyWatcherWrapper { + + + ConfigFuzzyWatcher configFuzzyWatcher; + + public ConfigFuzzyWatcherWrapper(ConfigFuzzyWatcher configFuzzyWatcher){ + this.configFuzzyWatcher=configFuzzyWatcher; + } + /** + * Unique identifier for the listener. + */ + String uuid= UUID.randomUUID().toString(); + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + ConfigFuzzyWatcherWrapper that = (ConfigFuzzyWatcherWrapper) o; + return Objects.equals(configFuzzyWatcher, that.configFuzzyWatcher) && Objects.equals(uuid, that.uuid); + } + + @Override + public int hashCode() { + return Objects.hash(configFuzzyWatcher, uuid); + } + + private Set syncGroupKeys = new HashSet<>(); + + Set getSyncGroupKeys() { + return syncGroupKeys; + } + + /** + * Get the UUID (Unique Identifier) of the listener. + * + * @return The UUID of the listener + */ + String getUuid() { + return uuid; + } + +} diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/NacosNamingService.java b/client/src/main/java/com/alibaba/nacos/client/naming/NacosNamingService.java index 3cf73004799..c4b51a5936f 100644 --- a/client/src/main/java/com/alibaba/nacos/client/naming/NacosNamingService.java +++ b/client/src/main/java/com/alibaba/nacos/client/naming/NacosNamingService.java @@ -549,15 +549,6 @@ public void fuzzyWatch(String fixedGroupName, FuzzyWatchEventWatcher listener) t @Override public void fuzzyWatch(String serviceNamePattern, String groupNamePattern, FuzzyWatchEventWatcher listener) throws NacosException { - // only support prefix match right now - if (!serviceNamePattern.endsWith(ANY_PATTERN)) { - if (serviceNamePattern.startsWith(ANY_PATTERN)) { - throw new UnsupportedOperationException("Suffix matching for service names is not supported yet." - + " It will be supported in future updates if needed."); - } else { - throw new UnsupportedOperationException("Illegal service name pattern, please read the documentation and pass a valid pattern."); - } - } doFuzzyWatch(serviceNamePattern, groupNamePattern, listener); } 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 new file mode 100644 index 00000000000..d516647da88 --- /dev/null +++ b/client/src/main/java/com/alibaba/nacos/client/naming/cache/FuzzyWatchEventWatcherWrapper.java @@ -0,0 +1,61 @@ +/* + * 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.cache; + +import com.alibaba.nacos.api.naming.listener.FuzzyWatchEventWatcher; + +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; +import java.util.UUID; + +public class FuzzyWatchEventWatcherWrapper { + + FuzzyWatchEventWatcher fuzzyWatchEventWatcher; + public FuzzyWatchEventWatcherWrapper(FuzzyWatchEventWatcher fuzzyWatchEventWatcher){ + this.fuzzyWatchEventWatcher=fuzzyWatchEventWatcher; + } + + String uuid= UUID.randomUUID().toString(); + + private Set syncServiceKeys = new HashSet<>(); + + final String getUuid() { + return uuid; + } + + Set getSyncServiceKeys() { + return syncServiceKeys; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + FuzzyWatchEventWatcherWrapper that = (FuzzyWatchEventWatcherWrapper) o; + return Objects.equals(fuzzyWatchEventWatcher, that.fuzzyWatchEventWatcher); + } + + @Override + public int hashCode() { + return Objects.hash(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 8fee42f49c3..803dfec55d5 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 @@ -31,6 +31,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.HashSet; +import java.util.Iterator; import java.util.List; import java.util.Set; import java.util.concurrent.CompletableFuture; @@ -83,10 +84,6 @@ public class NamingFuzzyWatchContext { */ final AtomicBoolean initializationCompleted = new AtomicBoolean(false); - /** - * Flag indicating whether the context is initializing. - */ - private boolean isInitializing = false; /** * Flag indicating whether the context is discarded. @@ -96,7 +93,7 @@ public class NamingFuzzyWatchContext { /** * Set of listeners associated with the context. */ - private Set namingFuzzyWatchers = new HashSet<>(); + private final Set fuzzyWatchEventWatcherWrappers = new HashSet<>(); /** * Constructor with environment name, data ID pattern, and group. @@ -109,81 +106,63 @@ public NamingFuzzyWatchContext(String envName, String groupKeyPattern) { this.groupKeyPattern = groupKeyPattern; } - /** - * Calculate the listeners to notify based on the given UUID. - * - * @param uuid UUID to filter listeners - * @return Set of listeners to notify - */ - public Set calculateListenersToNotify(String uuid) { - Set listenersToNotify = new HashSet<>(); - if (StringUtils.isEmpty(uuid)) { - listenersToNotify = namingFuzzyWatchers; - } else { - for (FuzzyWatchEventWatcher listener : namingFuzzyWatchers) { - if (uuid.equals(listener.getUuid())) { - listenersToNotify.add(listener); - } - } - } - return listenersToNotify; - } - - private void doNotifyWatcher(final String serviceKey, final String changedType,String syncType, - FuzzyWatchEventWatcher namingFuzzyWatcher){ + private void doNotifyWatcher(final String serviceKey, final String changedType,final String syncType, + FuzzyWatchEventWatcherWrapper fuzzyWatchEventWatcherWrapper){ - if (ADD_SERVICE.equals(changedType)&&namingFuzzyWatcher.getSyncServiceKeys().contains(serviceKey)){ + if (ADD_SERVICE.equals(changedType)&&fuzzyWatchEventWatcherWrapper.getSyncServiceKeys().contains(serviceKey)){ return; } - if (DELETE_SERVICE.equals(changedType)&&!namingFuzzyWatcher.getSyncServiceKeys().contains(serviceKey)){ + if (DELETE_SERVICE.equals(changedType)&&!fuzzyWatchEventWatcherWrapper.getSyncServiceKeys().contains(serviceKey)){ return; } String[] serviceKeyItems = NamingUtils.parseServiceKey(serviceKey); String namespace=serviceKeyItems[0]; - String groupName=serviceKeyItems[0]; - String serviceName=serviceKeyItems[0]; - - + String groupName=serviceKeyItems[1]; + String serviceName=serviceKeyItems[2]; + + final String resetSyncType=!initializationCompleted.get()?FUZZY_WATCH_INIT_NOTIFY:syncType; + Runnable job = () -> { long start = System.currentTimeMillis(); - FuzzyWatchChangeEvent event = new FuzzyWatchChangeEvent(namespace,groupName, serviceName, changedType,syncType); - if (namingFuzzyWatcher != null) { - namingFuzzyWatcher.onEvent(event); + FuzzyWatchChangeEvent event = new FuzzyWatchChangeEvent(serviceName,groupName, namespace, changedType,resetSyncType); + if (fuzzyWatchEventWatcherWrapper != null) { + fuzzyWatchEventWatcherWrapper.fuzzyWatchEventWatcher.onEvent(event); } LOGGER.info( - "[{}] [notify-watcher-ok] serviceName={}, groupName={}, namespace={}, watcher={}, job run cost={} millis.", - envName, serviceName, groupName, namespace, namingFuzzyWatcher, (System.currentTimeMillis() - start)); - if (changedType.equals(Constants.ConfigChangedType.DELETE_CONFIG)) { - namingFuzzyWatcher.getSyncServiceKeys().remove(NamingUtils.getServiceKey(namespace, groupName, serviceName)); - } else if (changedType.equals(FUZZY_WATCH_INIT_NOTIFY) || changedType.equals( - Constants.ConfigChangedType.ADD_CONFIG)) { - namingFuzzyWatcher.getSyncServiceKeys().add(NamingUtils.getServiceKey(namespace, groupName, serviceName)); + "[{}] [notify-watcher-ok] serviceName={}, groupName={}, namespace={}, watcher={},changedType={}, job run cost={} millis.", + envName, serviceName, groupName, namespace, fuzzyWatchEventWatcherWrapper.fuzzyWatchEventWatcher,changedType, (System.currentTimeMillis() - start)); + if (changedType.equals(DELETE_SERVICE)) { + fuzzyWatchEventWatcherWrapper.getSyncServiceKeys().remove(NamingUtils.getServiceKey(namespace, groupName, serviceName)); + } else if (changedType.equals(ADD_SERVICE)) { + fuzzyWatchEventWatcherWrapper.getSyncServiceKeys().add(NamingUtils.getServiceKey(namespace, groupName, serviceName)); } }; try { - if (null != namingFuzzyWatcher.getExecutor()) { + if (null != fuzzyWatchEventWatcherWrapper.fuzzyWatchEventWatcher.getExecutor()) { LOGGER.info( "[{}] [notify-watcher] task submitted to user executor, serviceName={}, groupName={}, namespace={}, listener={}.", - envName, serviceName, groupName, namespace, namingFuzzyWatcher); - namingFuzzyWatcher.getExecutor().execute(job); + envName, serviceName, groupName, namespace, fuzzyWatchEventWatcherWrapper); + fuzzyWatchEventWatcherWrapper.fuzzyWatchEventWatcher.getExecutor().execute(job); } else { LOGGER.info( "[{}] [notify-watcher] task execute in nacos thread, serviceName={}, groupName={}, namespace={}, listener={}.", - envName, serviceName, groupName, namespace, namingFuzzyWatcher); + envName, serviceName, groupName, namespace, fuzzyWatchEventWatcherWrapper); job.run(); } } catch (Throwable t) { LOGGER.error("[{}] [notify-watcher-error] serviceName={}, groupName={}, namespace={}, listener={}, throwable={}.", - envName, serviceName, groupName, namespace, namingFuzzyWatcher, t.getCause()); + envName, serviceName, groupName, namespace, fuzzyWatchEventWatcherWrapper, t.getCause()); } } /** * Mark initialization as complete and notify waiting threads. */ public void markInitializationComplete() { + LOGGER.info("[{}] [fuzzy-watch] pattern init notify finish pattern={},match service count {}", + envName,groupKeyPattern,receivedServiceKeys.size()); initializationCompleted.set(true); } @@ -193,11 +172,16 @@ public void markInitializationComplete() { * @param watcher watcher to be removed */ public void removeWatcher(FuzzyWatchEventWatcher watcher) { - if(namingFuzzyWatchers.remove(watcher)){ - LOGGER.info("[{}] [remove-watcher-ok] groupKeyPattern={}, watcher={},uuid={} ", getEnvName(), - this.groupKeyPattern, watcher, watcher.getUuid()); - + Iterator iterator = fuzzyWatchEventWatcherWrappers.iterator(); + while (iterator.hasNext()){ + FuzzyWatchEventWatcherWrapper next = iterator.next(); + if (next.fuzzyWatchEventWatcher.equals(watcher)){ + iterator.remove(); + LOGGER.info("[{}] [remove-watcher-ok] groupKeyPattern={}, watcher={},uuid={} ", getEnvName(), + this.groupKeyPattern, watcher, next.getUuid()); + } } + } /** @@ -259,16 +243,7 @@ public void setDiscard(boolean discard) { * @return True if the context is initializing, otherwise false */ public boolean isInitializing() { - return isInitializing; - } - - /** - * Set the flag indicating whether the context is initializing. - * - * @param initializing True to mark the context as initializing, otherwise false - */ - public void setInitializing(boolean initializing) { - isInitializing = initializing; + return !initializationCompleted.get(); } /** @@ -295,12 +270,12 @@ public boolean removeReceivedServiceKey(String serviceKey){ * * @return Set of listeners */ - public Set getNamingFuzzyWatchers() { - return namingFuzzyWatchers; + public Set getFuzzyWatchEventWatcherWrappers() { + return fuzzyWatchEventWatcherWrappers; } void syncFuzzyWatchers(){ - for(FuzzyWatchEventWatcher namingFuzzyWatcher: namingFuzzyWatchers){ + for(FuzzyWatchEventWatcherWrapper namingFuzzyWatcher: fuzzyWatchEventWatcherWrappers){ Set receivedServiceKeysContext = this.getReceivedServiceKeys(); Set syncGroupKeys = namingFuzzyWatcher.getSyncServiceKeys(); List groupKeyStates = FuzzyGroupKeyPattern.diffGroupKeys( @@ -312,18 +287,18 @@ void syncFuzzyWatchers(){ } } - void notifyFuzzyWatchers(String serviceKey,String syncType,String changedType,String watcherUuid){ - for(FuzzyWatchEventWatcher namingFuzzyWatcher: filterWatchers(watcherUuid)){ + void notifyFuzzyWatchers(String serviceKey,String changedType,String syncType,String watcherUuid){ + for(FuzzyWatchEventWatcherWrapper namingFuzzyWatcher: filterWatchers(watcherUuid)){ doNotifyWatcher(serviceKey,changedType,syncType,namingFuzzyWatcher); } } - private Set filterWatchers(String uuid){ - if (StringUtils.isBlank(uuid)|| CollectionUtils.isEmpty(getNamingFuzzyWatchers())){ - return getNamingFuzzyWatchers(); + private Set filterWatchers(String uuid){ + if (StringUtils.isBlank(uuid)|| CollectionUtils.isEmpty(getFuzzyWatchEventWatcherWrappers())){ + return getFuzzyWatchEventWatcherWrappers(); }else{ - return getNamingFuzzyWatchers().stream().filter(a->a.getUuid().equals(uuid)).collect( + return getFuzzyWatchEventWatcherWrappers().stream().filter(a->a.getUuid().equals(uuid)).collect( Collectors.toSet()); } 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 d89ae4a6695..d57349a0849 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 @@ -80,6 +80,7 @@ public class NamingFuzzyWatchServiceListHolder extends Subscriber matchedPattern = FuzzyGroupKeyPattern.filterMatchedPatterns( - namingFuzzyWatchServiceListHolder.getFuzzyMatchContextMap().keySet(),namespace,groupName,serviceName); + namingFuzzyWatchServiceListHolder.getFuzzyMatchContextMap().keySet(),serviceName,groupName,namespace); String serviceChangeType = notifyChangeRequest.getChangedType(); switch (serviceChangeType) { @@ -83,7 +84,7 @@ public Response requestReply(Request request, Connection connection) { case Constants.ServiceChangedType.INSTANCE_CHANGED: for (String pattern : matchedPattern) { NamingFuzzyWatchContext namingFuzzyWatchContext = namingFuzzyWatchServiceListHolder.getFuzzyMatchContextMap().get(pattern); - if (namingFuzzyWatchContext != null && namingFuzzyWatchContext.getReceivedServiceKeys().add(((NamingFuzzyWatchChangeNotifyRequest) request).getServiceKey())) { + if (namingFuzzyWatchContext != null && namingFuzzyWatchContext.addReceivedServiceKey(((NamingFuzzyWatchChangeNotifyRequest) request).getServiceKey())) { //publish local service add event NotifyCenter.publishEvent( NamingFuzzyWatchNotifyEvent.build(namingFuzzyWatchServiceListHolder.getNotifierEventScope(), @@ -95,7 +96,7 @@ public Response requestReply(Request request, Connection connection) { case Constants.ServiceChangedType.DELETE_SERVICE: for (String pattern : matchedPattern) { NamingFuzzyWatchContext namingFuzzyWatchContext = namingFuzzyWatchServiceListHolder.getFuzzyMatchContextMap().get(pattern); - if (namingFuzzyWatchContext != null && namingFuzzyWatchContext.getReceivedServiceKeys().remove(notifyChangeRequest.getServiceKey())) { + if (namingFuzzyWatchContext != null && namingFuzzyWatchContext.removeReceivedServiceKey(notifyChangeRequest.getServiceKey())) { NotifyCenter.publishEvent( NamingFuzzyWatchNotifyEvent.build(namingFuzzyWatchServiceListHolder.getNotifierEventScope(), pattern, notifyChangeRequest.getServiceKey(),Constants.ServiceChangedType.DELETE_SERVICE, diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/redo/FuzzyWatchSyncTask.java b/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/redo/FuzzyWatchSyncTask.java deleted file mode 100644 index bf245962838..00000000000 --- a/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/redo/FuzzyWatchSyncTask.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * 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.remote.gprc.redo; - -import com.alibaba.nacos.api.exception.NacosException; -import com.alibaba.nacos.api.naming.remote.request.NamingFuzzyWatchRequest; -import com.alibaba.nacos.api.naming.remote.response.NamingFuzzyWatchResponse; -import com.alibaba.nacos.client.naming.cache.NamingFuzzyWatchServiceListHolder; -import com.alibaba.nacos.client.naming.cache.NamingFuzzyWatchContext; -import com.alibaba.nacos.client.naming.remote.gprc.NamingGrpcClientProxy; -import com.alibaba.nacos.common.task.AbstractExecuteTask; - -import java.util.Iterator; -import java.util.Map; - -import static com.alibaba.nacos.api.common.Constants.WATCH_TYPE_CANCEL_WATCH; -import static com.alibaba.nacos.api.common.Constants.WATCH_TYPE_WATCH; - -public class FuzzyWatchSyncTask extends AbstractExecuteTask { - - private NamingFuzzyWatchServiceListHolder namingFuzzyWatchServiceListHolder; - - private final NamingGrpcClientProxy clientProxy; - - public FuzzyWatchSyncTask(NamingFuzzyWatchServiceListHolder namingFuzzyWatchServiceListHolder,NamingGrpcClientProxy clientProxy ){ - this.namingFuzzyWatchServiceListHolder = namingFuzzyWatchServiceListHolder; - this.clientProxy=clientProxy; - } - - @Override - public void run() { - Iterator> iterator = namingFuzzyWatchServiceListHolder.getFuzzyMatchContextMap() - .entrySet().iterator(); - while (iterator.hasNext()){ - Map.Entry next = iterator.next(); - while(!next.getValue().isConsistentWithServer()){ - NamingFuzzyWatchContext namingFuzzyWatchContext = next.getValue(); - NamingFuzzyWatchRequest namingFuzzyWatchRequest=new NamingFuzzyWatchRequest(); - namingFuzzyWatchRequest.setInitializing(namingFuzzyWatchContext.isInitializing()); - namingFuzzyWatchRequest.setNamespace(clientProxy.getNamespaceId()); - namingFuzzyWatchRequest.setReceivedGroupKeys(namingFuzzyWatchContext.getReceivedServiceKeys()); - namingFuzzyWatchRequest.setGroupKeyPattern(next.getKey()); - if (namingFuzzyWatchContext.isDiscard()&&namingFuzzyWatchContext.getNamingFuzzyWatchers().isEmpty()){ - namingFuzzyWatchRequest.setWatchType(WATCH_TYPE_WATCH); - }else{ - namingFuzzyWatchRequest.setWatchType(WATCH_TYPE_CANCEL_WATCH); - } - try { - NamingFuzzyWatchResponse namingFuzzyWatchResponse = clientProxy.fuzzyWatchRequest( - namingFuzzyWatchRequest); - if (namingFuzzyWatchResponse.isSuccess()&&WATCH_TYPE_CANCEL_WATCH.equals(namingFuzzyWatchRequest.getWatchType())){ - namingFuzzyWatchServiceListHolder.removePatternMatchCache(namingFuzzyWatchRequest.getGroupKeyPattern()); - } - } catch (NacosException e) { - throw new RuntimeException(e); - } - - } - } - - } -} diff --git a/common/src/main/java/com/alibaba/nacos/common/utils/FuzzyGroupKeyPattern.java b/common/src/main/java/com/alibaba/nacos/common/utils/FuzzyGroupKeyPattern.java index e70a0108869..2de49c6b85e 100644 --- a/common/src/main/java/com/alibaba/nacos/common/utils/FuzzyGroupKeyPattern.java +++ b/common/src/main/java/com/alibaba/nacos/common/utils/FuzzyGroupKeyPattern.java @@ -23,7 +23,7 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import static com.alibaba.nacos.api.common.Constants.ANY_PATTERN; +import static com.alibaba.nacos.api.common.Constants.ALL_PATTERN; import static com.alibaba.nacos.api.common.Constants.DEFAULT_NAMESPACE_ID; import static com.alibaba.nacos.api.common.Constants.FUZZY_WATCH_PATTERN_SPLITTER; @@ -114,30 +114,30 @@ public static String getNamespaceFromPattern(String groupKeyPattern){ private static boolean itemMatched(String pattern,String resource){ //accurate match without * - if (!pattern.contains(ANY_PATTERN)){ + if (!pattern.contains(ALL_PATTERN)){ return pattern.equals(resource); } //match for '*' pattern - if (pattern.equals(ANY_PATTERN)){ + if (pattern.equals(ALL_PATTERN)){ return true; } //match for *{string}* - if (pattern.startsWith(ANY_PATTERN)&&pattern.endsWith(ANY_PATTERN)){ - String pureString=pattern.replaceAll(ANY_PATTERN,""); + if (pattern.startsWith(ALL_PATTERN)&&pattern.endsWith(ALL_PATTERN)){ + String pureString=pattern.replace(ALL_PATTERN,""); return resource.contains(pureString); } //match for postfix match *{string} - if (pattern.startsWith(ANY_PATTERN)&&pattern.endsWith(ANY_PATTERN)){ - String pureString=pattern.replaceAll(ANY_PATTERN,""); + if (pattern.startsWith(ALL_PATTERN)){ + String pureString=pattern.replace(ALL_PATTERN,""); return resource.endsWith(pureString); } //match for prefix match {string}* - if (pattern.startsWith(ANY_PATTERN)&&pattern.endsWith(ANY_PATTERN)){ - String pureString=pattern.replaceAll(ANY_PATTERN,""); + if (pattern.endsWith(ALL_PATTERN)){ + String pureString=pattern.replace(ALL_PATTERN,""); return resource.startsWith(pureString); } diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/index/ClientServiceIndexesManager.java b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/index/ClientServiceIndexesManager.java index 2f5fbfc9273..61b6607638a 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/index/ClientServiceIndexesManager.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/index/ClientServiceIndexesManager.java @@ -50,13 +50,7 @@ public class ClientServiceIndexesManager extends SmartSubscriber { private final ConcurrentMap> subscriberIndexes = new ConcurrentHashMap<>(); - private NamingFuzzyWatchContextService namingFuzzyWatchContextService; - - public ClientServiceIndexesManager(NamingFuzzyWatchContextService namingFuzzyWatchContextService){ - this.namingFuzzyWatchContextService = namingFuzzyWatchContextService; - } - - public ClientServiceIndexesManager() { + public ClientServiceIndexesManager(){ NotifyCenter.registerSubscriber(this, NamingEventPublisherFactory.getInstance()); } @@ -138,7 +132,6 @@ private void addPublisherIndexes(Service service, String clientId) { String serviceChangedType = Constants.ServiceChangedType.INSTANCE_CHANGED; if (!publisherIndexes.containsKey(service)) { // The only time the index needs to be updated is when the service is first created - namingFuzzyWatchContextService.addNewService(service); serviceChangedType = Constants.ServiceChangedType.ADD_SERVICE; } NotifyCenter.publishEvent(new ServiceEvent.ServiceChangedEvent(service, serviceChangedType, true)); 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 5af85ea607b..d1db04e0cbf 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 @@ -102,7 +102,6 @@ private void trimFuzzyWatchContext() { public List> subscribeTypes() { List> result = new LinkedList<>(); result.add(ClientOperationEvent.ClientReleaseEvent.class); - result.add(ServiceEvent.ServiceChangedEvent.class); return result; } @@ -113,15 +112,6 @@ public void onEvent(Event event) { if (event instanceof ClientOperationEvent.ClientReleaseEvent) { removeFuzzyWatchContext(((ClientOperationEvent.ClientReleaseEvent) event).getClientId()); } - - //handle service add or deleted event - if (event instanceof ServiceEvent.ServiceChangedEvent) { - ServiceEvent.ServiceChangedEvent serviceChangedEvent = (ServiceEvent.ServiceChangedEvent) event; - String changedType = serviceChangedEvent.getChangedType(); - Service service = serviceChangedEvent.getService(); - - syncServiceContext(service,changedType); - } } @@ -129,35 +119,27 @@ public Set getFuzzyWatchedClients(Service service) { Set matchedClients = new HashSet<>(); Iterator>> iterator = keyPatternWatchClients.entrySet().iterator(); while (iterator.hasNext()) { - if (FuzzyGroupKeyPattern.matchPattern(iterator.next().getKey(), service.getName(), service.getGroup(), + Map.Entry> entry = iterator.next(); + if (FuzzyGroupKeyPattern.matchPattern(entry.getKey(), service.getName(), service.getGroup(), service.getNamespace())) { - matchedClients.addAll(iterator.next().getValue()); + matchedClients.addAll(entry.getValue()); } } return matchedClients; } - /** - * This method will build/update the fuzzy watch match index of all patterns. - * - * @param service The service of the Nacos. - */ - public void addNewService(Service service) { - Set filteredPattern = FuzzyGroupKeyPattern.filterMatchedPatterns(keyPatternWatchClients.keySet(), - service.getName(), service.getGroup(), service.getNamespace()); + public boolean syncServiceContext(Service changedEventService, String changedType) { - if (CollectionUtils.isNotEmpty(filteredPattern)) { - for (String each : filteredPattern) { - fuzzyWatchPatternMatchServices.get(each) - .add(NamingUtils.getServiceKey(service.getNamespace(), service.getGroup(), service.getName())); - } - Loggers.PERFORMANCE_LOG.info("WATCH: new service {} match {} pattern", - service.getGroupedServiceName(), fuzzyWatchPatternMatchServices.size()); + boolean needNotify=false; + if (!changedType.equals(ADD_SERVICE)&&!changedType.equals(DELETE_SERVICE)){ + return false; } - } - - public void syncServiceContext(Service changedEventService, String changedType) { + String serviceKey = NamingUtils.getServiceKey(changedEventService.getNamespace(), + changedEventService.getGroup(), changedEventService.getName()); + Loggers.SRV_LOG.warn("FUZZY_WATCH: service change matched,service key {},changed type {} ", + serviceKey,changedType); + Iterator>> iterator = fuzzyWatchPatternMatchServices.entrySet() .iterator(); @@ -166,16 +148,30 @@ public void syncServiceContext(Service changedEventService, String changedType) if (FuzzyGroupKeyPattern.matchPattern(next.getKey(), changedEventService.getName(), changedEventService.getGroup(), changedEventService.getNamespace())) { - String serviceKey = NamingUtils.getServiceKey(changedEventService.getNamespace(), - changedEventService.getGroup(), changedEventService.getName()); - if (changedType.equals(ADD_SERVICE)){ - next.getValue().add(serviceKey); - }else if (changedType.equals(DELETE_SERVICE)){ - next.getValue().remove(serviceKey); + 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 , 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; + } + + }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; + } } } } + return needNotify; } public Set syncFuzzyWatcherContext(String groupKeyPattern, String clientId) { @@ -188,7 +184,6 @@ public Set syncFuzzyWatcherContext(String groupKeyPattern, String client private void removeFuzzyWatchContext(String clientId) { Iterator>> iterator = keyPatternWatchClients.entrySet().iterator(); - while (iterator.hasNext()) { Map.Entry> next = iterator.next(); next.getValue().remove(clientId); @@ -212,6 +207,8 @@ public Set initWatchMatchService(String completedPattern) { if (!fuzzyWatchPatternMatchServices.containsKey(completedPattern)) { if (fuzzyWatchPatternMatchServices.size() >= FUZZY_WATCH_MAX_PATTERN_COUNT) { + Loggers.SRV_LOG.warn("FUZZY_WATCH: fuzzy watch pattern count is over limit ,pattern {} init fail,current count is {}", + completedPattern,fuzzyWatchPatternMatchServices.size()); throw new NacosRuntimeException(FUZZY_WATCH_PATTERN_OVER_LIMIT.getCode(), FUZZY_WATCH_PATTERN_OVER_LIMIT.getMsg()); } @@ -226,15 +223,19 @@ public Set initWatchMatchService(String completedPattern) { if (FuzzyGroupKeyPattern.matchPattern(completedPattern, service.getName(), service.getGroup(), service.getNamespace())) { if (matchedServices.size() >= FUZZY_WATCH_MAX_PATTERN_MATCHED_GROUP_KEY_COUNT) { - throw new NacosRuntimeException(FUZZY_WATCH_PATTERN_MATCH_GROUP_KEY_OVER_LIMIT.getCode(), - FUZZY_WATCH_PATTERN_MATCH_GROUP_KEY_OVER_LIMIT.getMsg()); + + 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()); + break; +// throw new NacosRuntimeException(FUZZY_WATCH_PATTERN_MATCH_GROUP_KEY_OVER_LIMIT.getCode(), +// FUZZY_WATCH_PATTERN_MATCH_GROUP_KEY_OVER_LIMIT.getMsg()); } matchedServices.add( NamingUtils.getServiceKey(service.getNamespace(), service.getGroup(), service.getName())); } } fuzzyWatchPatternMatchServices.putIfAbsent(completedPattern,matchedServices); - Loggers.PERFORMANCE_LOG.info("WATCH: pattern {} match {} services, cost {}ms", completedPattern, + Loggers.SRV_LOG.info("FUZZY_WATCH: pattern {} match {} services, cost {}ms", completedPattern, matchedServices.size(), System.currentTimeMillis() - matchBeginTime); } diff --git a/naming/src/main/java/com/alibaba/nacos/naming/push/NamingFuzzyWatchChangeNotifier.java b/naming/src/main/java/com/alibaba/nacos/naming/push/NamingFuzzyWatchChangeNotifier.java index 4dd2c3475be..177d8e44eba 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/push/NamingFuzzyWatchChangeNotifier.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/push/NamingFuzzyWatchChangeNotifier.java @@ -18,9 +18,11 @@ import com.alibaba.nacos.api.naming.utils.NamingUtils; import com.alibaba.nacos.common.notify.Event; +import com.alibaba.nacos.common.notify.NotifyCenter; import com.alibaba.nacos.common.notify.listener.SmartSubscriber; import com.alibaba.nacos.naming.core.v2.event.service.ServiceEvent; import com.alibaba.nacos.naming.core.v2.index.NamingFuzzyWatchContextService; +import com.alibaba.nacos.naming.misc.Loggers; import com.alibaba.nacos.naming.push.v2.PushConfig; import com.alibaba.nacos.naming.push.v2.task.FuzzyWatchChangeNotifyTask; import com.alibaba.nacos.naming.push.v2.task.FuzzyWatchPushDelayTaskEngine; @@ -28,6 +30,7 @@ import java.util.LinkedList; import java.util.List; +import java.util.Set; /** * service change notify for fuzzy watch @@ -42,6 +45,7 @@ public class NamingFuzzyWatchChangeNotifier extends SmartSubscriber { public NamingFuzzyWatchChangeNotifier(NamingFuzzyWatchContextService namingFuzzyWatchContextService,FuzzyWatchPushDelayTaskEngine fuzzyWatchPushDelayTaskEngine){ this.fuzzyWatchPushDelayTaskEngine=fuzzyWatchPushDelayTaskEngine; this.namingFuzzyWatchContextService=namingFuzzyWatchContextService; + NotifyCenter.registerSubscriber(this); } @Override @@ -50,20 +54,27 @@ public List> subscribeTypes() { result.add(ServiceEvent.ServiceChangedEvent.class); return result; } + @Override public void onEvent(Event event) { if (event instanceof ServiceEvent.ServiceChangedEvent) { ServiceEvent.ServiceChangedEvent serviceChangedEvent=(ServiceEvent.ServiceChangedEvent)event; - namingFuzzyWatchContextService.syncServiceContext(serviceChangedEvent.getService(),serviceChangedEvent.getChangedType()); - generateFuzzyWatchChangeNotifyTask(serviceChangedEvent.getService(),serviceChangedEvent.getChangedType()); + if(namingFuzzyWatchContextService.syncServiceContext(serviceChangedEvent.getService(),serviceChangedEvent.getChangedType())){ + generateFuzzyWatchChangeNotifyTask(serviceChangedEvent.getService(),serviceChangedEvent.getChangedType()); + } } } private void generateFuzzyWatchChangeNotifyTask(com.alibaba.nacos.naming.core.v2.pojo.Service service, String changedType) { + + String serviceKey = NamingUtils.getServiceKey(service.getNamespace(), service.getGroup(), + service.getName()); + Set fuzzyWatchedClients = namingFuzzyWatchContextService.getFuzzyWatchedClients(service); + + Loggers.SRV_LOG.info("FUZZY_WATCH:serviceKey {} has {} clients fuzzy watched" , + serviceKey,fuzzyWatchedClients==null?0:fuzzyWatchedClients.size()); // watch notify push task specify by service - for (String clientId : namingFuzzyWatchContextService.getFuzzyWatchedClients(service)) { - String serviceKey = NamingUtils.getServiceKey(service.getNamespace(), service.getGroup(), - service.getName()); + for (String clientId :fuzzyWatchedClients) { FuzzyWatchChangeNotifyTask fuzzyWatchChangeNotifyTask = new FuzzyWatchChangeNotifyTask(serviceKey, changedType, clientId, PushConfig.getInstance().getPushTaskDelay()); fuzzyWatchPushDelayTaskEngine.addTask(FuzzyWatchPushDelayTaskEngine.getTaskKey(fuzzyWatchChangeNotifyTask), 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 57ab8748fd9..a092b535486 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 @@ -18,6 +18,7 @@ import com.alibaba.nacos.api.naming.remote.request.NamingFuzzyWatchSyncRequest; import com.alibaba.nacos.common.notify.Event; +import com.alibaba.nacos.common.notify.NotifyCenter; import com.alibaba.nacos.common.notify.listener.SmartSubscriber; import com.alibaba.nacos.common.utils.CollectionUtils; import com.alibaba.nacos.common.utils.FuzzyGroupKeyPattern; @@ -57,7 +58,9 @@ public class NamingFuzzyWatchSyncNotifier extends SmartSubscriber { public NamingFuzzyWatchSyncNotifier(NamingFuzzyWatchContextService namingFuzzyWatchContextService,FuzzyWatchPushDelayTaskEngine fuzzyWatchPushDelayTaskEngine){ this.namingFuzzyWatchContextService=namingFuzzyWatchContextService; this.fuzzyWatchPushDelayTaskEngine=fuzzyWatchPushDelayTaskEngine; + NotifyCenter.registerSubscriber(this); } + @Override public List> subscribeTypes() { List> result = new LinkedList<>(); 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 a077323f16d..b36a498a975 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 @@ -61,15 +61,15 @@ private class FuzzyWatchChangeNotifyCallback implements PushCallBack { private final String clientId; - private String service; + private String serviceKey; - private String serviceChangedType; + private String changedType; - private FuzzyWatchChangeNotifyCallback(String clientId, String service, String serviceChangedType) { + private FuzzyWatchChangeNotifyCallback(String clientId, String serviceKey, String changedType) { this.clientId = clientId; - this.service = service; - this.serviceChangedType = serviceChangedType; + this.serviceKey = serviceKey; + this.changedType = changedType; } @@ -80,15 +80,20 @@ public long getTimeout() { @Override public void onSuccess() { - + Loggers.PUSH.info("[FUZZY-WATCH] change notify success ,clientId {}, serviceKey {] ,changedType {} ", + clientId, clientId, changedType); + } @Override public void onFail(Throwable e) { + + Loggers.PUSH.warn("[FUZZY-WATCH] change notify fail ,clientId {}, serviceKey {] ,changedType {} ", + clientId, clientId, changedType,e); + if (!(e instanceof NoRequiredRetryException)) { - Loggers.PUSH.error("fuzzy watch fail , reason detail: ", e); delayTaskEngine.addTask(System.currentTimeMillis(), - new FuzzyWatchChangeNotifyTask(service, serviceChangedType, clientId, + new FuzzyWatchChangeNotifyTask(serviceKey, changedType, clientId, PushConfig.getInstance().getPushTaskRetryDelay())); } } diff --git a/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchPushDelayTaskEngine.java b/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchPushDelayTaskEngine.java index d879625d647..9ccdb8fce8f 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchPushDelayTaskEngine.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchPushDelayTaskEngine.java @@ -25,6 +25,7 @@ import com.alibaba.nacos.naming.misc.NamingExecuteTaskDispatcher; import com.alibaba.nacos.naming.misc.SwitchDomain; import com.alibaba.nacos.naming.push.v2.executor.PushExecutor; +import com.alibaba.nacos.naming.push.v2.executor.PushExecutorDelegate; import org.springframework.stereotype.Component; /** @@ -36,12 +37,12 @@ public class FuzzyWatchPushDelayTaskEngine extends NacosDelayTaskExecuteEngine { - private final PushExecutor pushExecutor; + private final PushExecutorDelegate pushExecutor; private final SwitchDomain switchDomain; public FuzzyWatchPushDelayTaskEngine( - PushExecutor pushExecutor, + PushExecutorDelegate pushExecutor, SwitchDomain switchDomain) { super(FuzzyWatchPushDelayTaskEngine.class.getSimpleName(), Loggers.PUSH); this.pushExecutor = pushExecutor; 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 6566b2ff243..76b18c1d178 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 @@ -21,6 +21,7 @@ import com.alibaba.nacos.naming.misc.Loggers; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; @@ -50,7 +51,11 @@ public FuzzyWatchSyncNotifyTask(String clientId, String pattern, String syncType this.clientId = clientId; this.pattern = pattern; this.syncType=syncType; - this.syncServiceKeys = syncServiceKeys; + if (syncServiceKeys!=null) { + this.syncServiceKeys = syncServiceKeys; + }else{ + this.syncServiceKeys=new HashSet<>(); + } setTaskInterval(delay); setLastProcessTime(System.currentTimeMillis()); } @@ -77,7 +82,10 @@ public void merge(AbstractDelayTask task) { return; } FuzzyWatchSyncNotifyTask oldTask = (FuzzyWatchSyncNotifyTask) task; - syncServiceKeys.addAll(oldTask.getSyncServiceKeys()); + + if (oldTask.getSyncServiceKeys()!=null){ + syncServiceKeys.addAll(oldTask.getSyncServiceKeys()); + } setLastProcessTime(Math.min(getLastProcessTime(), task.getLastProcessTime())); Loggers.PUSH.info("[FUZZY-WATCH-INIT-PUSH] Task merge for pattern {}", pattern); } 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 f7aa29f3df1..9658e643298 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 @@ -40,6 +40,9 @@ public class NamingFuzzyWatchRequestHandler extends RequestHandler { public NamingFuzzyWatchRequestHandler() { + NotifyCenter.registerToPublisher(ClientOperationEvent.ClientFuzzyWatchEvent.class,1000); + NotifyCenter.registerToPublisher(ClientOperationEvent.ClientCancelFuzzyWatchEvent.class,1000); + } @Override From 6e7bb2d379ec5761a147c030ecc603c7cd76e133 Mon Sep 17 00:00:00 2001 From: "zunfei.lzf" Date: Thu, 9 Jan 2025 11:44:17 +0800 Subject: [PATCH 09/15] fuzzy watch optimize --- .../request/ConfigFuzzyWatchSyncRequest.java | 44 ++-- .../client/config/NacosConfigService.java | 2 +- .../ClientFuzzyWatchNotifyRequestHandler.java | 2 +- .../client/config/impl/ClientWorker.java | 2 +- .../config/impl/ConfigFuzzyWatchContext.java | 82 +++++--- .../impl/ConfigFuzzyWatchGroupKeyHolder.java | 78 ++++--- ....java => ConfigFuzzyWatchNotifyEvent.java} | 34 +-- .../naming/cache/NamingFuzzyWatchContext.java | 47 ++++- .../nacos/common/task/BatchTaskCounter.java | 56 +++++ .../configuration/ConfigCommonConfig.java | 4 +- .../event/ConfigCancelFuzzyWatchEvent.java | 78 +++++++ ...va => ConfigFuzzyWatchChangeNotifier.java} | 49 ++--- ...va => ConfigFuzzyWatchRequestHandler.java} | 16 +- .../remote/ConfigFuzzyWatchSyncNotifier.java | 194 ++++++++---------- .../ConfigFuzzyWatchContextService.java | 156 +++++++------- .../index/NamingFuzzyWatchContextService.java | 118 +++++------ .../push/NamingFuzzyWatchSyncNotifier.java | 61 +++--- .../FuzzyWatchChangeNotifyExecuteTask.java | 1 - .../task/FuzzyWatchPushDelayTaskEngine.java | 10 +- .../task/FuzzyWatchSyncNotifyExecuteTask.java | 6 +- .../v2/task/FuzzyWatchSyncNotifyTask.java | 35 +--- 21 files changed, 635 insertions(+), 440 deletions(-) rename client/src/main/java/com/alibaba/nacos/client/config/impl/{FuzzyWatchNotifyEvent.java => ConfigFuzzyWatchNotifyEvent.java} (74%) create mode 100644 common/src/main/java/com/alibaba/nacos/common/task/BatchTaskCounter.java create mode 100644 config/src/main/java/com/alibaba/nacos/config/server/model/event/ConfigCancelFuzzyWatchEvent.java rename config/src/main/java/com/alibaba/nacos/config/server/remote/{FuzzyWatchConfigChangeNotifier.java => ConfigFuzzyWatchChangeNotifier.java} (84%) rename config/src/main/java/com/alibaba/nacos/config/server/remote/{ConfigBatchFuzzyWatchRequestHandler.java => ConfigFuzzyWatchRequestHandler.java} (90%) 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 41800399843..ebedf7b4207 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 @@ -47,6 +47,12 @@ public class ConfigFuzzyWatchSyncRequest extends AbstractFuzzyWatchNotifyRequest */ private String syncType; + + private int totalBatch; + + private int currentBatch; + + public String getSyncType() { return syncType; } @@ -55,6 +61,22 @@ public void setSyncType(String syncType) { this.syncType = syncType; } + public int getTotalBatch() { + return totalBatch; + } + + public void setTotalBatch(int totalBatch) { + this.totalBatch = totalBatch; + } + + public int getCurrentBatch() { + return currentBatch; + } + + public void setCurrentBatch(int currentBatch) { + this.currentBatch = currentBatch; + } + /** * Constructs an empty FuzzyListenNotifyDiffRequest. */ @@ -67,10 +89,12 @@ public ConfigFuzzyWatchSyncRequest() { * @param groupKeyPattern The pattern used to match group keys for the configurations * @param contexts The set of contexts containing information about the configurations */ - public ConfigFuzzyWatchSyncRequest(String syncType, String groupKeyPattern, Set contexts) { + private ConfigFuzzyWatchSyncRequest(String syncType, String groupKeyPattern, Set contexts,int totalBatch,int currentBatch) { this.groupKeyPattern = groupKeyPattern; this.contexts = contexts; this.syncType = syncType; + this.currentBatch=currentBatch; + this.totalBatch=totalBatch; } @@ -81,21 +105,12 @@ public ConfigFuzzyWatchSyncRequest(String syncType, String groupKeyPattern, Set< * @param groupKeyPattern The pattern used to match group keys for the configurations * @return An initial FuzzyListenNotifyDiffRequest */ - public static ConfigFuzzyWatchSyncRequest buildInitRequest(Set contexts, String groupKeyPattern) { - return new ConfigFuzzyWatchSyncRequest(Constants.FUZZY_WATCH_INIT_NOTIFY, groupKeyPattern, contexts); + public static ConfigFuzzyWatchSyncRequest buildSyncRequest(String syncType,Set contexts, String groupKeyPattern,int totalBatch,int currentBatch) { + return new ConfigFuzzyWatchSyncRequest(syncType, groupKeyPattern, contexts,totalBatch,currentBatch); } - /** - * Builds an initial FuzzyListenNotifyDiffRequest with the specified set of contexts and group key pattern. - * - * @param contexts The set of contexts containing information about the configurations - * @param groupKeyPattern The pattern used to match group keys for the configurations - * @return An initial FuzzyListenNotifyDiffRequest - */ - public static ConfigFuzzyWatchSyncRequest buildDiffSyncRequest(Set contexts, String groupKeyPattern) { - return new ConfigFuzzyWatchSyncRequest(Constants.FUZZY_WATCH_DIFF_SYNC_NOTIFY, groupKeyPattern, contexts); - } + /** * Builds a final FuzzyListenNotifyDiffRequest with the specified group key pattern. * @@ -103,8 +118,7 @@ public static ConfigFuzzyWatchSyncRequest buildDiffSyncRequest(Set cont * @return A final FuzzyListenNotifyDiffRequest */ public static ConfigFuzzyWatchSyncRequest buildInitFinishRequest(String groupKeyPattern) { - return new ConfigFuzzyWatchSyncRequest(Constants.FINISH_FUZZY_WATCH_INIT_NOTIFY, groupKeyPattern, - new HashSet<>()); + return new ConfigFuzzyWatchSyncRequest(Constants.FINISH_FUZZY_WATCH_INIT_NOTIFY, groupKeyPattern,null,0,0); } public String getGroupKeyPattern() { diff --git a/client/src/main/java/com/alibaba/nacos/client/config/NacosConfigService.java b/client/src/main/java/com/alibaba/nacos/client/config/NacosConfigService.java index 6bffb443687..e0b3ca4d8f6 100644 --- a/client/src/main/java/com/alibaba/nacos/client/config/NacosConfigService.java +++ b/client/src/main/java/com/alibaba/nacos/client/config/NacosConfigService.java @@ -148,7 +148,7 @@ public Future> fuzzyWatchWithGroupKeys(String dataIdPattern, } - private CompletableFuture> doAddFuzzyWatch(String dataIdPattern, + private Future> doAddFuzzyWatch(String dataIdPattern, String fixedGroupName, ConfigFuzzyWatcher watcher) throws NacosException { ConfigFuzzyWatchContext configFuzzyWatchContext = worker.addTenantFuzzyWatcher(dataIdPattern, fixedGroupName,watcher); return configFuzzyWatchContext.createNewFuture(); 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 69907826516..d8e1509c453 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 Response requestReply(Request request, Connection connection) { } //fuzzy watch changed notify for a single config. include config changed or config delete. if (request instanceof ConfigFuzzyWatchChangeNotifyRequest) { - return configFuzzyWatchGroupKeyHolder.handlerFuzzyListenNotifyChangeRequest((ConfigFuzzyWatchChangeNotifyRequest) request); + return configFuzzyWatchGroupKeyHolder.handlerFuzzyWatchChangeNotifyRequest((ConfigFuzzyWatchChangeNotifyRequest) request); } return null; } 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 a18b068fd3c..1ce9fc6bd0f 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 @@ -518,7 +518,7 @@ public ClientWorker(final ConfigFilterChainManager configFilterChainManager, Con agent = new ConfigRpcTransportClient(properties, serverListManager); - configFuzzyWatchGroupKeyHolder =new ConfigFuzzyWatchGroupKeyHolder(agent); + configFuzzyWatchGroupKeyHolder =new ConfigFuzzyWatchGroupKeyHolder(agent,uuid); ScheduledExecutorService executorService = Executors.newScheduledThreadPool(initWorkerThreadCount(properties), new NameThreadFactory("com.alibaba.nacos.client.Worker")); agent.setExecutor(executorService); 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 0b40df1c899..268e7786ca6 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,7 +18,9 @@ import com.alibaba.nacos.api.config.listener.ConfigFuzzyWatchChangeEvent; import com.alibaba.nacos.api.config.listener.ConfigFuzzyWatcher; +import com.alibaba.nacos.api.naming.pojo.ListView; import com.alibaba.nacos.client.config.common.GroupKey; +import com.alibaba.nacos.client.naming.cache.NamingFuzzyWatchContext; import com.alibaba.nacos.client.utils.LogUtils; import com.alibaba.nacos.common.utils.ConcurrentHashSet; import com.alibaba.nacos.common.utils.FuzzyGroupKeyPattern; @@ -32,6 +34,9 @@ import java.util.Set; import java.util.concurrent.CompletableFuture; 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.AtomicBoolean; import static com.alibaba.nacos.api.common.Constants.ConfigChangedType.ADD_CONFIG; @@ -84,11 +89,6 @@ public class ConfigFuzzyWatchContext { */ final AtomicBoolean initializationCompleted = new AtomicBoolean(false); - /** - * Flag indicating whether the context is initializing. - */ - private boolean isInitializing = false; - /** * Flag indicating whether the context is discarded. */ @@ -182,7 +182,7 @@ public void run() { configFuzzyWatcher.configFuzzyWatcher.onEvent(event); } LOGGER.info( - "[{}] [notify-watcher-ok] dataId={}, group={}, tenant={}, watcher={}, job run cost={} millis.", + "[{}] [notify-fuzzy-watcher-ok] dataId={}, group={}, tenant={}, watcher={}, job run cost={} millis.", envName, dataId, group, tenant, configFuzzyWatcher, (System.currentTimeMillis() - start)); if (changedType.equals(DELETE_CONFIG)) { configFuzzyWatcher.getSyncGroupKeys().remove(GroupKey.getKey(dataId, group, tenant)); @@ -197,18 +197,18 @@ public void run() { try { if (null != configFuzzyWatcher.configFuzzyWatcher.getExecutor()) { LOGGER.info( - "[{}] [notify-watcher] task submitted to user executor, dataId={}, group={}, tenant={}, listener={}.", + "[{}] [notify-fuzzy-watcher] task submitted to user executor, dataId={}, group={}, tenant={}, listener={}.", envName, dataId, group, tenant, configFuzzyWatcher); job.async = true; configFuzzyWatcher.configFuzzyWatcher.getExecutor().execute(job); } else { LOGGER.info( - "[{}] [notify-watcher] task execute in nacos thread, dataId={}, group={}, tenant={}, listener={}.", + "[{}] [notify-fuzzy-watcher] task execute in nacos thread, dataId={}, group={}, tenant={}, listener={}.", envName, dataId, group, tenant, configFuzzyWatcher); job.run(); } } catch (Throwable t) { - LOGGER.error("[{}] [notify-watcher-error] dataId={}, group={}, tenant={}, listener={}, throwable={}.", + LOGGER.error("[{}] [notify-fuzzy-watcher-error] dataId={}, group={}, tenant={}, listener={}, throwable={}.", envName, dataId, group, tenant, configFuzzyWatcher, t.getCause()); } } @@ -217,6 +217,9 @@ public void run() { */ public void markInitializationComplete() { initializationCompleted.set(true); + synchronized (this){ + this.notifyAll(); + } } /** @@ -231,7 +234,7 @@ public void removeWatcher(ConfigFuzzyWatcher watcher) { ConfigFuzzyWatcherWrapper next = iterator.next(); if (next.configFuzzyWatcher.equals(watcher)){ iterator.remove(); - LOGGER.info("[{}] [remove-watcher-ok] groupKeyPattern={}, watcher={},uuid={} ", getEnvName(), + LOGGER.info("[{}] [remove-fuzzy-watcher-ok] groupKeyPattern={}, watcher={},uuid={} ", getEnvName(), this.groupKeyPattern, watcher, next.getUuid()); } } @@ -246,7 +249,7 @@ public void removeWatcher(ConfigFuzzyWatcher watcher) { public boolean addWatcher(ConfigFuzzyWatcherWrapper configFuzzyWatcherWrapper) { boolean added= configFuzzyWatcherWrappers.add(configFuzzyWatcherWrapper); if(added){ - LOGGER.info("[{}] [add-watcher-ok] groupKeyPattern={}, watcher={},uuid={} ", getEnvName(), this.groupKeyPattern, + LOGGER.info("[{}] [add-fuzzy-watcher-ok] groupKeyPattern={}, watcher={},uuid={} ", getEnvName(), this.groupKeyPattern, configFuzzyWatcherWrapper.configFuzzyWatcher, configFuzzyWatcherWrapper.getUuid()); } return added; @@ -329,21 +332,12 @@ public void setDiscard(boolean discard) { * @return True if the context is initializing, otherwise false */ public boolean isInitializing() { - return isInitializing; - } - - /** - * Set the flag indicating whether the context is initializing. - * - * @param initializing True to mark the context as initializing, otherwise false - */ - public void setInitializing(boolean initializing) { - isInitializing = initializing; + return !initializationCompleted.get(); } /** * Get the set of data IDs associated with the context. - * + *zw * @return Set of data IDs */ public Set getReceivedGroupKeys() { @@ -351,6 +345,15 @@ public Set getReceivedGroupKeys() { } + public boolean addReceivedGroupKey(String groupKey){ + return receivedGroupKeys.add(groupKey); + } + + + public boolean removeReceivedGroupKey(String groupKey){ + return receivedGroupKeys.remove(groupKey); + } + /** * Get the set of listeners associated with the context. * @@ -396,8 +399,18 @@ void syncFuzzyWatchers(){ } - public CompletableFuture> createNewFuture(){ - CompletableFuture> completableFuture=new CompletableFuture>(){ + public Future> createNewFuture(){ + Future> completableFuture=new Future>(){ + @Override + public boolean cancel(boolean mayInterruptIfRunning) { + throw new UnsupportedOperationException("not support to cancel fuzzy watch"); + } + + @Override + public boolean isCancelled() { + return false; + } + @Override public boolean isDone() { return ConfigFuzzyWatchContext.this.initializationCompleted.get(); @@ -405,9 +418,30 @@ public boolean isDone() { @Override public Set get() throws InterruptedException, ExecutionException { + + if(!ConfigFuzzyWatchContext.this.initializationCompleted.get()){ + synchronized (ConfigFuzzyWatchContext.this) { + ConfigFuzzyWatchContext.this.wait(); + } + } + + return new HashSet<>(ConfigFuzzyWatchContext.this.getReceivedGroupKeys()); + } + + public Set get(long timeout, TimeUnit unit) throws InterruptedException, TimeoutException { + if(!ConfigFuzzyWatchContext.this.initializationCompleted.get()){ + synchronized (ConfigFuzzyWatchContext.this) { + ConfigFuzzyWatchContext.this.wait(unit.toMillis(timeout)); + } + } + + if (!ConfigFuzzyWatchContext.this.initializationCompleted.get()){ + throw new TimeoutException("fuzzy watch result future timeout for "+unit.toMillis(timeout)+" millis"); + } return new HashSet<>(ConfigFuzzyWatchContext.this.getReceivedGroupKeys()); } }; + return completableFuture; } } 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 7bd0b3fc6f0..a922be8e5d3 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 @@ -63,6 +63,7 @@ public class ConfigFuzzyWatchGroupKeyHolder { private final ClientWorker.ConfigRpcTransportClient agent; + private final String clientUuid; /** * fuzzyListenExecuteBell. */ @@ -82,26 +83,33 @@ public class ConfigFuzzyWatchGroupKeyHolder { new HashMap<>()); - public ConfigFuzzyWatchGroupKeyHolder(ClientWorker.ConfigRpcTransportClient agent){ - + public ConfigFuzzyWatchGroupKeyHolder(ClientWorker.ConfigRpcTransportClient agent,String clientUuid){ + this.clientUuid=clientUuid; this.agent=agent; NotifyCenter.registerSubscriber(new Subscriber() { @Override public void onEvent(Event event) { - FuzzyWatchNotifyEvent fuzzyWatchNotifyEvent = (FuzzyWatchNotifyEvent) event; + ConfigFuzzyWatchNotifyEvent configFuzzyWatchNotifyEvent = (ConfigFuzzyWatchNotifyEvent) event; + + //instance check + if (!configFuzzyWatchNotifyEvent.getClientUuid().equals(clientUuid)){ + return; + } + ConfigFuzzyWatchContext context = fuzzyListenContextMap.get() - .get(fuzzyWatchNotifyEvent.getGroupKeyPattern()); + .get(configFuzzyWatchNotifyEvent.getGroupKeyPattern()); if (context == null) { return; } - context.notifyWatcher(fuzzyWatchNotifyEvent.getGroupKey(), fuzzyWatchNotifyEvent.getChangedType(),fuzzyWatchNotifyEvent.getSyncType(), - fuzzyWatchNotifyEvent.getWatcherUuid()); + context.notifyWatcher(configFuzzyWatchNotifyEvent.getGroupKey(), configFuzzyWatchNotifyEvent.getChangedType(), + configFuzzyWatchNotifyEvent.getSyncType(), + configFuzzyWatchNotifyEvent.getWatcherUuid()); } @Override public Class subscribeType() { - return FuzzyWatchNotifyEvent.class; + return ConfigFuzzyWatchNotifyEvent.class; } }); @@ -151,9 +159,9 @@ public ConfigFuzzyWatchContext registerFuzzyWatcher(String dataIdPattern, String if(configFuzzyWatchContext.addWatcher(configFuzzyWatcherWrapper)){ if(configFuzzyWatchContext.getReceivedGroupKeys()!=null){ for(String groupKey:configFuzzyWatchContext.getReceivedGroupKeys()){ - FuzzyWatchNotifyEvent fuzzyWatchNotifyEvent = FuzzyWatchNotifyEvent.buildNotifyPatternAllListenersEvent(groupKey, + ConfigFuzzyWatchNotifyEvent configFuzzyWatchNotifyEvent = ConfigFuzzyWatchNotifyEvent.buildNotifyPatternAllListenersEvent(groupKey, configFuzzyWatchContext.getGroupKeyPattern(), ADD_CONFIG,FUZZY_WATCH_INIT_NOTIFY,configFuzzyWatcherWrapper.getUuid()); - NotifyCenter.publishEvent(fuzzyWatchNotifyEvent); + NotifyCenter.publishEvent(configFuzzyWatchNotifyEvent); } } @@ -199,35 +207,34 @@ public ConfigFuzzyWatchContext getFuzzyListenContext(String dataIdPattern, Strin * @return A {@link ConfigFuzzyWatchSyncResponse} indicating the result of handling the request. */ ConfigFuzzyWatchSyncResponse handleFuzzyWatchNotifyDiffRequest(ConfigFuzzyWatchSyncRequest request) { - LOGGER.info("[{}] [config-fuzzy-watch-diff-sync-push] sync group key pattern ->{}.", agent.getName(),request.getGroupKeyPattern()); String groupKeyPattern = request.getGroupKeyPattern(); 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()); context.markInitializationComplete(); return new ConfigFuzzyWatchSyncResponse(); } + + LOGGER.info("[{}] [fuzzy-watch-diff-sync-push] pattern ->{},syncType={},,syncCount={},totalBatch={},currentBatch={}", agent.getName(),request.getGroupKeyPattern(),request.getSyncType(),request.getContexts().size(),request.getTotalBatch(),request.getCurrentBatch()); + for (ConfigFuzzyWatchSyncRequest.Context requestContext : request.getContexts()) { - Set matchedGroupKeys = context.getReceivedGroupKeys(); switch (requestContext.getChangedType()) { - case FUZZY_WATCH_INIT_NOTIFY: case ADD_CONFIG: - if (matchedGroupKeys.add(requestContext.getGroupKey())) { - LOGGER.info("[{}] [config-fuzzy-watch-diff-sync-push] sync group key ->{},changed type->{}", agent.getName(),request.getGroupKeyPattern(),requestContext.getChangedType()); - - NotifyCenter.publishEvent(FuzzyWatchNotifyEvent.buildNotifyPatternAllListenersEvent(requestContext.getGroupKey(), request.getGroupKeyPattern(), - requestContext.getChangedType(),request.getSyncType())); + if (context.addReceivedGroupKey(requestContext.getGroupKey())) { + 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)); } break; - case Constants.ConfigChangedType.DELETE_CONFIG: - if (matchedGroupKeys.remove(requestContext)) { - LOGGER.info("[{}] [config-fuzzy-watch-diff-sync-push] sync group key ->{},changed type->{}", agent.getName(),request.getGroupKeyPattern(),requestContext.getChangedType()); - - NotifyCenter.publishEvent(FuzzyWatchNotifyEvent.buildNotifyPatternAllListenersEvent(requestContext.getGroupKey(), request.getGroupKeyPattern(), - requestContext.getChangedType(),request.getSyncType())); + case DELETE_CONFIG: + if (context.removeReceivedGroupKey(requestContext.getGroupKey())) { + 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)); } break; default: - LOGGER.error("Invalid config change type: {}", requestContext.getChangedType()); + LOGGER.warn("Invalid config change type: {}", requestContext.getChangedType()); break; } } @@ -263,9 +270,11 @@ public void removeFuzzyWatcher(String dataIdPattern, String groupPattern, Config * * @param request The fuzzy listen notify change request to handle. */ - ConfigFuzzyWatchChangeNotifyResponse handlerFuzzyListenNotifyChangeRequest( + ConfigFuzzyWatchChangeNotifyResponse handlerFuzzyWatchChangeNotifyRequest( ConfigFuzzyWatchChangeNotifyRequest request) { - LOGGER.info("[{}] [fuzzy-watch-config-push] fuzzy watched config changed,groupKey->{},changedType->{} ", agent.getName(),request.getGroupKey(),request.getChangeType()); + + LOGGER.info("[{}] [fuzzy-watch-change-notify-push] changeType={},groupKey={}", agent.getName(),request.getChangeType(),request.getGroupKey()); + Map listenContextMap = fuzzyListenContextMap.get(); String[] groupItems = GroupKey.parseKey(request.getGroupKey()); Set matchedPatterns = FuzzyGroupKeyPattern.filterMatchedPatterns(listenContextMap.keySet(),groupItems[0], @@ -273,13 +282,15 @@ ConfigFuzzyWatchChangeNotifyResponse handlerFuzzyListenNotifyChangeRequest( for (String matchedPattern : matchedPatterns) { ConfigFuzzyWatchContext context = listenContextMap.get(matchedPattern); if (ADD_CONFIG.equals(request.getChangeType())||CONFIG_CHANGED.equals(request.getChangeType())) { - if (context.getReceivedGroupKeys().add(request.getGroupKey())) { + if (context.addReceivedGroupKey(request.getGroupKey())) { + LOGGER.info("[{}] [fuzzy-watch-change-notify-push] match group key added ,pattern={},groupKey={}", agent.getName(),request.getChangeType(),request.getGroupKey()); + NotifyCenter.publishEvent( - FuzzyWatchNotifyEvent.buildNotifyPatternAllListenersEvent(request.getGroupKey(), matchedPattern, ADD_CONFIG,FUZZY_WATCH_RESOURCE_CHANGED)); + ConfigFuzzyWatchNotifyEvent.buildNotifyPatternAllListenersEvent(request.getGroupKey(), matchedPattern, ADD_CONFIG,FUZZY_WATCH_RESOURCE_CHANGED,this.clientUuid)); } - } else if (DELETE_CONFIG.equals(request.getChangeType())&&context.getReceivedGroupKeys().remove(request.getGroupKey())) { + } else if (DELETE_CONFIG.equals(request.getChangeType())&&context.removeReceivedGroupKey(request.getGroupKey())) { NotifyCenter.publishEvent( - FuzzyWatchNotifyEvent.buildNotifyPatternAllListenersEvent(request.getGroupKey(), matchedPattern, Constants.ConfigChangedType.DELETE_CONFIG,FUZZY_WATCH_RESOURCE_CHANGED)); + ConfigFuzzyWatchNotifyEvent.buildNotifyPatternAllListenersEvent(request.getGroupKey(), matchedPattern, Constants.ConfigChangedType.DELETE_CONFIG,FUZZY_WATCH_RESOURCE_CHANGED,this.clientUuid)); } } @@ -444,12 +455,13 @@ private ConfigFuzzyWatchContext initFuzzyWatchContextIfAbsent(String dataIdPatte if (contextFromMap != null) { context = contextFromMap; } else { - context = new ConfigFuzzyWatchContext(agent.getName(), - FuzzyGroupKeyPattern.generatePattern(dataIdPattern, groupPattern,agent.getTenant())); + String groupKeyPattern = FuzzyGroupKeyPattern.generatePattern(dataIdPattern, groupPattern,agent.getTenant()); + + context = new ConfigFuzzyWatchContext(agent.getName(),groupKeyPattern); context.setConsistentWithServer(false); Map copy = new HashMap<>(fuzzyListenContextMap.get()); - String groupKeyPattern = FuzzyGroupKeyPattern.generatePattern(dataIdPattern, groupPattern,agent.getTenant()); copy.put(groupKeyPattern, context); + LOGGER.info("[{}][fuzzy-watch] init fuzzy watch context , groupKeyPattern={} ,notify fuzzy watch sync ", agent.getName(), groupKeyPattern); fuzzyListenContextMap.set(copy); notifyFuzzyWatchSync(); } diff --git a/client/src/main/java/com/alibaba/nacos/client/config/impl/FuzzyWatchNotifyEvent.java b/client/src/main/java/com/alibaba/nacos/client/config/impl/ConfigFuzzyWatchNotifyEvent.java similarity index 74% rename from client/src/main/java/com/alibaba/nacos/client/config/impl/FuzzyWatchNotifyEvent.java rename to client/src/main/java/com/alibaba/nacos/client/config/impl/ConfigFuzzyWatchNotifyEvent.java index 220660ed599..ed83231c96b 100644 --- a/client/src/main/java/com/alibaba/nacos/client/config/impl/FuzzyWatchNotifyEvent.java +++ b/client/src/main/java/com/alibaba/nacos/client/config/impl/ConfigFuzzyWatchNotifyEvent.java @@ -28,7 +28,10 @@ * @author stone-98 * @date 2024/3/4 */ -public class FuzzyWatchNotifyEvent extends Event { +public class ConfigFuzzyWatchNotifyEvent extends Event { + + + private String clientUuid; /** * The uuid of this watcher for which that this notify event . @@ -52,7 +55,7 @@ public class FuzzyWatchNotifyEvent extends Event { /** * Constructs a new FuzzyListenNotifyEvent. */ - public FuzzyWatchNotifyEvent() { + public ConfigFuzzyWatchNotifyEvent() { } /** @@ -61,11 +64,13 @@ public FuzzyWatchNotifyEvent() { * @param groupKey The groupKey of the configuration. * @param changedType The type of notification. */ - private FuzzyWatchNotifyEvent(String groupKey, String changedType, String syncType, String groupKeyPattern) { + private ConfigFuzzyWatchNotifyEvent(String groupKey, String changedType, String syncType, String groupKeyPattern,String clientUuid,String watcherUuid) { this.groupKey = groupKey; this.syncType=syncType; this.changedType = changedType; this.groupKeyPattern = groupKeyPattern; + this.clientUuid=clientUuid; + this.watcherUuid=watcherUuid; } @@ -75,9 +80,9 @@ private FuzzyWatchNotifyEvent(String groupKey, String changedType, String syncTy * @param groupKey The groupKey of the configuration. * @return A new FuzzyListenNotifyEvent instance. */ - public static FuzzyWatchNotifyEvent buildNotifyPatternAllListenersEvent(String groupKey, - String groupKeyPattern, String changedType,String syncType) { - return new FuzzyWatchNotifyEvent(groupKey, changedType, syncType,groupKeyPattern); + public static ConfigFuzzyWatchNotifyEvent buildNotifyPatternAllListenersEvent(String groupKey, + String groupKeyPattern, String changedType,String syncType,String clientUuid) { + return buildNotifyPatternAllListenersEvent(groupKey,groupKeyPattern,changedType,syncType,clientUuid,null); } /** @@ -86,13 +91,13 @@ public static FuzzyWatchNotifyEvent buildNotifyPatternAllListenersEvent(String g * @param groupKey The groupKey of the configuration. * @return A new FuzzyListenNotifyEvent instance. */ - public static FuzzyWatchNotifyEvent buildNotifyPatternAllListenersEvent(String groupKey, - String groupKeyPattern, String changedType,String syncType,String uuid) { - FuzzyWatchNotifyEvent fuzzyWatchNotifyEvent = new FuzzyWatchNotifyEvent(groupKey, changedType, syncType, - groupKeyPattern); - fuzzyWatchNotifyEvent.watcherUuid =uuid; - return fuzzyWatchNotifyEvent; + public static ConfigFuzzyWatchNotifyEvent buildNotifyPatternAllListenersEvent(String groupKey, + String groupKeyPattern, String changedType,String syncType,String clientUuid,String watcherUuid) { + ConfigFuzzyWatchNotifyEvent configFuzzyWatchNotifyEvent = new ConfigFuzzyWatchNotifyEvent(groupKey, changedType, syncType, + groupKeyPattern,clientUuid,watcherUuid); + return configFuzzyWatchNotifyEvent; } + /** * Gets the UUID (Unique Identifier) of the listener. * @@ -102,7 +107,10 @@ public String getWatcherUuid() { return watcherUuid; } - + public String getClientUuid() { + return clientUuid; + } + public String getGroupKeyPattern() { return groupKeyPattern; } 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 803dfec55d5..70332fc615d 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 @@ -35,6 +35,9 @@ import java.util.List; import java.util.Set; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicBoolean; import java.util.stream.Collectors; @@ -164,6 +167,9 @@ public void markInitializationComplete() { LOGGER.info("[{}] [fuzzy-watch] pattern init notify finish pattern={},match service count {}", envName,groupKeyPattern,receivedServiceKeys.size()); initializationCompleted.set(true); + synchronized (this){ + notifyAll(); + } } /** @@ -304,15 +310,50 @@ private Set filterWatchers(String uuid){ } - public CompletableFuture> createNewFuture(){ - CompletableFuture> completableFuture=new CompletableFuture>(){ + public Future> createNewFuture(){ + Future> completableFuture=new Future>(){ + @Override + public boolean cancel(boolean mayInterruptIfRunning) { + throw new UnsupportedOperationException("not support to cancel fuzzy watch"); + } + + @Override + public boolean isCancelled() { + return false; + } + @Override public boolean isDone() { return NamingFuzzyWatchContext.this.initializationCompleted.get(); } @Override - public ListView get() { + public ListView get() throws InterruptedException { + synchronized (NamingFuzzyWatchContext.this) { + while(!NamingFuzzyWatchContext.this.initializationCompleted.get()){ + NamingFuzzyWatchContext.this.wait(); + } + } + + ListView result = new ListView<>(); + result.setData(Arrays.asList(NamingFuzzyWatchContext.this.receivedServiceKeys.toArray(new String[0]))); + result.setCount(result.getData().size()); + return result; + } + + @Override + public ListView get(long timeout, TimeUnit unit) throws InterruptedException, TimeoutException { + + if(!NamingFuzzyWatchContext.this.initializationCompleted.get()){ + synchronized (NamingFuzzyWatchContext.this) { + NamingFuzzyWatchContext.this.wait(unit.toMillis(timeout)); + } + } + + if (!NamingFuzzyWatchContext.this.initializationCompleted.get()){ + throw new TimeoutException("fuzzy watch result future timeout for "+unit.toMillis(timeout)+" millis"); + } + ListView result = new ListView<>(); result.setData(Arrays.asList(NamingFuzzyWatchContext.this.receivedServiceKeys.toArray(new String[0]))); result.setCount(result.getData().size()); diff --git a/common/src/main/java/com/alibaba/nacos/common/task/BatchTaskCounter.java b/common/src/main/java/com/alibaba/nacos/common/task/BatchTaskCounter.java new file mode 100644 index 00000000000..07ee2ccb14a --- /dev/null +++ b/common/src/main/java/com/alibaba/nacos/common/task/BatchTaskCounter.java @@ -0,0 +1,56 @@ +/* + * 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.common.task; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; + +public class BatchTaskCounter { + + List batchCounter; + + public BatchTaskCounter(int totalBatch) { + initBatchCounter(totalBatch); + } + + public void initBatchCounter(int totalBatch) { + batchCounter = new ArrayList<>(totalBatch); + for (int i = 0; i < totalBatch; i++) { + batchCounter.add(i, new AtomicBoolean(false)); + } + } + + public void batchSuccess(int batch) { + if (batch <= batchCounter.size()) { + batchCounter.get(batch - 1).set(true); + } + } + + public boolean batchCompleted() { + for (AtomicBoolean atomicBoolean : batchCounter) { + if (!atomicBoolean.get()) { + return false; + } + } + return true; + } + + public int getTotalBatch(){ + return batchCounter.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 88db2f01a45..86e7c89e83a 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 @@ -35,7 +35,7 @@ public class ConfigCommonConfig extends AbstractDynamicConfig { private long pushTimeout = 3000L; - private int batchSize = 10; + private int batchSize = 20; private boolean derbyOpsEnabled = false; @@ -83,7 +83,7 @@ public void setDerbyOpsEnabled(boolean derbyOpsEnabled) { 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, 10); + batchSize = EnvUtil.getProperty("nacos.config.push.batchSize", Integer.class, 20); derbyOpsEnabled = EnvUtil.getProperty("nacos.config.derby.ops.enabled", Boolean.class, false); } 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 new file mode 100644 index 00000000000..d758ede377b --- /dev/null +++ b/config/src/main/java/com/alibaba/nacos/config/server/model/event/ConfigCancelFuzzyWatchEvent.java @@ -0,0 +1,78 @@ +/* + * 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; + +import java.util.Set; + +/** + * 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/FuzzyWatchConfigChangeNotifier.java b/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigFuzzyWatchChangeNotifier.java similarity index 84% rename from config/src/main/java/com/alibaba/nacos/config/server/remote/FuzzyWatchConfigChangeNotifier.java rename to config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigFuzzyWatchChangeNotifier.java index 63326a784d7..de54927ebdc 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/remote/FuzzyWatchConfigChangeNotifier.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigFuzzyWatchChangeNotifier.java @@ -38,7 +38,6 @@ import java.util.concurrent.TimeUnit; -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; @@ -51,7 +50,7 @@ * @date 2024/3/18 */ @Component(value = "fuzzyWatchConfigChangeNotifier") -public class FuzzyWatchConfigChangeNotifier extends Subscriber { +public class ConfigFuzzyWatchChangeNotifier extends Subscriber { private static final String POINT_FUZZY_WATCH_CONFIG_PUSH = "POINT_FUZZY_WATCH_CONFIG_PUSH"; @@ -72,7 +71,7 @@ public class FuzzyWatchConfigChangeNotifier extends Subscriber { - /** - * Context for managing fuzzy listen changes. - */ - @Autowired + private ConfigFuzzyWatchContextService configFuzzyWatchContextService; + public ConfigFuzzyWatchRequestHandler(ConfigFuzzyWatchContextService configFuzzyWatchContextService){ + this.configFuzzyWatchContextService=configFuzzyWatchContextService; + } + /** * Handles the batch fuzzy listen request. *

@@ -89,8 +91,8 @@ public ConfigFuzzyWatchResponse handle(ConfigFuzzyWatchRequest request, RequestM new ConfigFuzzyWatchEvent(connectionId, clientExistingGroupKeys, groupKeyPattern, request.isInitializing())); } else if(WATCH_TYPE_CANCEL_WATCH.equals(request.getWatchType())) { - // Remove client from the fuzzy listening context - configFuzzyWatchContextService.removeFuzzyListen(groupKeyPattern, connectionId); + NotifyCenter.publishEvent( + new ConfigCancelFuzzyWatchEvent(connectionId, groupKeyPattern)); } // 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 47732ed9c06..83d64430cb6 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 @@ -21,16 +21,17 @@ 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; +import com.alibaba.nacos.common.notify.listener.SmartSubscriber; + +import com.alibaba.nacos.common.task.BatchTaskCounter; 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.ConfigFuzzyWatchEvent; +import com.alibaba.nacos.config.server.model.event.ConfigCancelFuzzyWatchEvent; 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; @@ -40,12 +41,16 @@ import java.util.ArrayList; import java.util.Collection; +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; +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; + /** * Handles batch fuzzy listen events and pushes corresponding notifications to clients. * @@ -53,7 +58,7 @@ * @date 2024/3/18 */ @Component(value = "configFuzzyWatchSyncNotifier") -public class ConfigFuzzyWatchSyncNotifier extends Subscriber { +public class ConfigFuzzyWatchSyncNotifier extends SmartSubscriber { private static final String FUZZY_LISTEN_CONFIG_DIFF_PUSH = "FUZZY_LISTEN_CONFIG_DIFF_PUSH_COUNT"; @@ -110,21 +115,7 @@ private static void push(FuzzyWatchRpcPushTask retryTask, ConnectionManager conn * * @param event The ConfigBatchFuzzyListenEvent to handle */ - @Override - public void onEvent(ConfigFuzzyWatchEvent event) { - // Get the connection for the client - Connection connection = connectionManager.getConnection(event.getConnectionId()); - if (connection == null) { - Loggers.REMOTE_PUSH.warn( - "connectionId not found, Config diff notification not sent. connectionId={},keyGroupPattern={}", - event.getConnectionId(), event.getGroupKeyPattern()); - // If connection is not available, return - return; - } - - // Retrieve meta information for the connection - ConnectionMeta metaInfo = connection.getMetaInfo(); - String clientIp = metaInfo.getClientIp(); + public void handleFuzzyWatchEvent(ConfigFuzzyWatchEvent event) { // Match client effective group keys based on the event pattern, client IP, and tag Set matchGroupKeys = configFuzzyWatchContextService.matchGroupKeys(event.getGroupKeyPattern()); @@ -132,57 +123,74 @@ public void onEvent(ConfigFuzzyWatchEvent event) { // Retrieve existing group keys for the client from the event Set clientExistingGroupKeys = event.getClientExistingGroupKeys(); - // Check if both matched and existing group keys are empty, if so, return - if (CollectionUtils.isEmpty(matchGroupKeys) && CollectionUtils.isEmpty(clientExistingGroupKeys)) { - return; - } - // Calculate and merge configuration states based on matched and existing group keys List configStates = FuzzyGroupKeyPattern.diffGroupKeys(matchGroupKeys, clientExistingGroupKeys); + + if (CollectionUtils.isEmpty(configStates)){ + 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{ + String syncType =event.isInitializing()?FUZZY_WATCH_INIT_NOTIFY:FUZZY_WATCH_DIFF_SYNC_NOTIFY; + + int batchSize = ConfigCommonConfig.getInstance().getBatchSize(); + // Divide config states into batches + List> divideConfigStatesIntoBatches = divideConfigStatesIntoBatches(configStates, batchSize); + + // Calculate the number of batches and initialize push batch finish count + int totalBatch = divideConfigStatesIntoBatches.size(); + BatchTaskCounter batchTaskCounter=new BatchTaskCounter(divideConfigStatesIntoBatches.size()); + int currentBatch=1; + for (List configStateList : divideConfigStatesIntoBatches) { + // Map config states to FuzzyListenNotifyDiffRequest.Context objects + Set contexts = configStateList.stream().map(state -> { + + String changeType = state.isExist() ? Constants.ConfigChangedType.ADD_CONFIG + : Constants.ConfigChangedType.DELETE_CONFIG; + return ConfigFuzzyWatchSyncRequest.Context.build(state.getGroupKey(), changeType); + }).collect(Collectors.toSet()); - // If no diff config states are available, return - if (CollectionUtils.isEmpty(configStates)) { - return; + ConfigFuzzyWatchSyncRequest request =ConfigFuzzyWatchSyncRequest.buildSyncRequest(syncType,contexts,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); + currentBatch++; + } } - int batchSize = ConfigCommonConfig.getInstance().getBatchSize(); - // Divide config states into batches - List> divideConfigStatesIntoBatches = divideConfigStatesIntoBatches(configStates, batchSize); - // Calculate the number of batches and initialize push batch finish count - int originBatchSize = divideConfigStatesIntoBatches.size(); - AtomicInteger pushBatchFinishCount = new AtomicInteger(0); - - // Iterate over each batch of config states - for (List configStateList : divideConfigStatesIntoBatches) { - // Map config states to FuzzyListenNotifyDiffRequest.Context objects - Set contexts = configStateList.stream().map(state -> { - - String changeType = state.isExist() ? Constants.ConfigChangedType.ADD_CONFIG - : Constants.ConfigChangedType.DELETE_CONFIG; - return ConfigFuzzyWatchSyncRequest.Context.build(state.getGroupKey(), changeType); - }).collect(Collectors.toSet()); - - // Build FuzzyListenNotifyDiffRequest with contexts and pattern - ConfigFuzzyWatchSyncRequest request = - event.isInitializing() ? ConfigFuzzyWatchSyncRequest.buildInitRequest(contexts, - event.getGroupKeyPattern()) - : ConfigFuzzyWatchSyncRequest.buildDiffSyncRequest(contexts, event.getGroupKeyPattern()); - - int maxPushRetryTimes = ConfigCommonConfig.getInstance().getMaxPushRetryTimes(); - // Create RPC push task and push the request to the client - FuzzyWatchRpcPushTask fuzzyWatchRpcPushTask = new FuzzyWatchRpcPushTask(request, pushBatchFinishCount, - originBatchSize, maxPushRetryTimes, event.getConnectionId(), clientIp, metaInfo.getAppName()); - push(fuzzyWatchRpcPushTask, connectionManager); - } } - + + @Override + public List> subscribeTypes() { + List> result = new LinkedList<>(); + result.add(ConfigFuzzyWatchEvent.class); + result.add(ConfigCancelFuzzyWatchEvent.class); + return result; + } + @Override - public Class subscribeType() { - return ConfigFuzzyWatchEvent.class; + 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()); + } + } + /** * Divides a collection of items into batches. * @@ -201,7 +209,6 @@ private List> divideConfigStatesIntoBatches(Collection configStat .values()); } - /** * Represents a task for pushing FuzzyListenNotifyDiffRequest to clients. */ @@ -237,36 +244,20 @@ class FuzzyWatchRpcPushTask implements Runnable { */ String appName; - /** - * The counter for tracking the number of finished push batches. - */ - AtomicInteger pushBatchFinishCount; - - /** - * The original size of the batch before splitting. - */ - int originBatchSize; - + BatchTaskCounter batchTaskCounter; /** * Constructs a new RpcPushTask with the specified parameters. * * @param notifyRequest The FuzzyListenNotifyDiffRequest to be pushed - * @param pushBatchFinishCount The counter for tracking the number of finished push batches - * @param originBatchSize The original size of the batch before splitting + * @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 - * @param clientIp The IP address of the client - * @param appName The name of the client's application */ - public FuzzyWatchRpcPushTask(ConfigFuzzyWatchSyncRequest notifyRequest, AtomicInteger pushBatchFinishCount, - int originBatchSize, int maxRetryTimes, String connectionId, String clientIp, String appName) { + public FuzzyWatchRpcPushTask(ConfigFuzzyWatchSyncRequest notifyRequest,BatchTaskCounter batchTaskCounter,int maxRetryTimes, String connectionId) { this.notifyRequest = notifyRequest; - this.pushBatchFinishCount = pushBatchFinishCount; - this.originBatchSize = originBatchSize; + this.batchTaskCounter = batchTaskCounter; this.maxRetryTimes = maxRetryTimes; this.connectionId = connectionId; - this.clientIp = clientIp; - this.appName = appName; } /** @@ -291,8 +282,7 @@ public void run() { push(this, connectionManager); } else { rpcPushService.pushWithCallback(connectionId, notifyRequest, - new FuzzyWatchRpcPushCallback(this, tpsControlManager, connectionManager, pushBatchFinishCount, - originBatchSize), ConfigExecutor.getClientConfigNotifierServiceExecutor()); + new FuzzyWatchRpcPushCallback(this, tpsControlManager, connectionManager, batchTaskCounter), ConfigExecutor.getClientConfigNotifierServiceExecutor()); } } } @@ -317,35 +307,24 @@ class FuzzyWatchRpcPushCallback extends AbstractPushCallBack { */ ConnectionManager connectionManager; - /** - * The counter for tracking the number of pushed batches. - */ - AtomicInteger pushBatchCount; - - /** - * The original size of the batch before splitting. - */ - int originBatchSize; - + + 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 pushBatchCount The counter for tracking the number of pushed batches - * @param originBatchSize The original size of the batch before splitting + * @param batchTaskCounter The batchTaskCounter counter */ public FuzzyWatchRpcPushCallback(FuzzyWatchRpcPushTask fuzzyWatchRpcPushTask, - TpsControlManager tpsControlManager, ConnectionManager connectionManager, AtomicInteger pushBatchCount, - int originBatchSize) { - // Set the timeout for the callback + TpsControlManager tpsControlManager, ConnectionManager connectionManager, BatchTaskCounter batchTaskCounter) { super(3000L); this.fuzzyWatchRpcPushTask = fuzzyWatchRpcPushTask; this.tpsControlManager = tpsControlManager; this.connectionManager = connectionManager; - this.pushBatchCount = pushBatchCount; - this.originBatchSize = originBatchSize; + this.batchTaskCounter=batchTaskCounter; + } /** @@ -358,15 +337,16 @@ public void onSuccess() { tpsCheckRequest.setPointName(FUZZY_LISTEN_CONFIG_DIFF_PUSH_SUCCESS); tpsControlManager.check(tpsCheckRequest); - if (pushBatchCount.get() < originBatchSize) { - pushBatchCount.incrementAndGet(); - } else if (pushBatchCount.get() == originBatchSize) { - ConfigFuzzyWatchSyncRequest request = ConfigFuzzyWatchSyncRequest.buildInitFinishRequest( - fuzzyWatchRpcPushTask.notifyRequest.getGroupKeyPattern()); - push(new FuzzyWatchRpcPushTask(request, pushBatchCount, originBatchSize, 50, - fuzzyWatchRpcPushTask.connectionId, fuzzyWatchRpcPushTask.clientIp, - fuzzyWatchRpcPushTask.appName), connectionManager); + 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); + } } + } /** 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 6b03a0e8c02..a318537a352 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 @@ -22,6 +22,7 @@ import com.alibaba.nacos.common.utils.FuzzyGroupKeyPattern; 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; @@ -31,100 +32,91 @@ import java.util.Set; import java.util.concurrent.ConcurrentHashMap; -import static com.alibaba.nacos.api.model.v2.ErrorCode.FUZZY_WATCH_PATTERN_MATCH_GROUP_KEY_OVER_LIMIT; +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_OVER_LIMIT; @Component public class ConfigFuzzyWatchContextService { /** - * groupKeyPattern -> watched connection id set. + * groupKeyPattern -> watched client id set. */ - private final Map> keyPatternWatchClients = new ConcurrentHashMap<>(); + private final Map> watchedClients = new ConcurrentHashMap<>(); /** - * groupKeyPattern -> groupKeys set. + * groupKeyPattern -> matched groupKeys set. */ - private final Map> groupKeyMatched = new ConcurrentHashMap<>(); + private final Map> matchedGroupKeys = new ConcurrentHashMap<>(); private final int FUZZY_WATCH_MAX_PATTERN_COUNT = 50; - private final int FUZZY_WATCH_MAX_PATTERN_MATCHED_GROUP_KEY_COUNT = 200; + private final int FUZZY_WATCH_MAX_PATTERN_MATCHED_GROUP_KEY_COUNT = 1000; - public ConfigFuzzyWatchContextService(){ - - GlobalExecutor.scheduleWithFixDelayByCommon(() -> clearFuzzyWatchContext(), 30000); + public ConfigFuzzyWatchContextService() { + + GlobalExecutor.scheduleWithFixDelayByCommon(() -> trimFuzzyWatchContext(), 30000); } - private void clearFuzzyWatchContext(){ - Iterator>> iterator = groupKeyMatched.entrySet().iterator(); - while (iterator.hasNext()){ + /** + * trim fuzzy watch context.
+ * 1.remove watchedClients if watched client is empty. + * 2.remove matchedServiceKeys if watchedClients is null. + * pattern matchedServiceKeys will be removed in second period to avoid frequently matchedServiceKeys init. + */ + private void trimFuzzyWatchContext() { + Iterator>> iterator = matchedGroupKeys.entrySet().iterator(); + while (iterator.hasNext()) { Map.Entry> next = iterator.next(); - Set watchedClients = keyPatternWatchClients.get(next.getKey()); - if(CollectionUtils.isEmpty(watchedClients)){ + Set watchedClients = this.watchedClients.get(next.getKey()); + + if (watchedClients == null) { iterator.remove(); - if (watchedClients!=null){ - keyPatternWatchClients.remove(next.getKey()); - } + LogUtil.DEFAULT_LOG.info("[fuzzy-watch] no watchedClients context for pattern {},remove matchedGroupKeys context",next.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()); } } } + /** - * get matched exist group keys with the groupKeyPattern. - * return null if not matched + * get matched exist group keys with the groupKeyPattern. return null if not matched + * * @param groupKeyPattern * @return */ public Set matchGroupKeys(String groupKeyPattern) { - if (groupKeyMatched.containsKey(groupKeyPattern)) { - return groupKeyMatched.get(groupKeyPattern); - } else { - return null; - } + return matchedGroupKeys.get(groupKeyPattern); } - public boolean newConfigAdded(String groupKey){ - String[] groupKeyItems = GroupKey.parseKey(groupKey); - String dataId=groupKeyItems[0]; - String group=groupKeyItems[1]; - String namespace=groupKeyItems[2]; - Iterator>> iterator = groupKeyMatched.entrySet().iterator(); - boolean added=false; - while (iterator.hasNext()){ - Map.Entry> entry = iterator.next(); - if(FuzzyGroupKeyPattern.matchPattern(entry.getKey(),dataId,group,namespace)){ - if(entry.getValue().add(groupKey)){ - added=true; - } - - } - } - - return added; - } - public boolean configRemoved(String groupKey){ + public boolean syncGroupKeyContext(String groupKey, String changedType) { + + boolean needNotify=false; + String[] groupKeyItems = GroupKey.parseKey(groupKey); - String dataId=groupKeyItems[0]; - String group=groupKeyItems[1]; - String namespace=groupKeyItems[2]; - Iterator>> iterator = groupKeyMatched.entrySet().iterator(); - boolean removed=false; - while (iterator.hasNext()){ + String dataId = groupKeyItems[0]; + String group = groupKeyItems[1]; + String namespace = groupKeyItems[2]; + Iterator>> iterator = matchedGroupKeys.entrySet().iterator(); + while (iterator.hasNext()) { Map.Entry> entry = iterator.next(); - if(FuzzyGroupKeyPattern.matchPattern(entry.getKey(),dataId,group,namespace)){ - if(entry.getValue().remove(groupKey)){ - removed=true; + if (FuzzyGroupKeyPattern.matchPattern(entry.getKey(), dataId, group, namespace)) { + if ((changedType.equals(ADD_CONFIG)||changedType.equals(CONFIG_CHANGED))&&entry.getValue().add(groupKey)) { + needNotify = true; + } + + if (changedType.equals(DELETE_CONFIG)&&entry.getValue().remove(groupKey)) { + needNotify = true; } - } } - - return removed; - + return needNotify; } /** @@ -135,26 +127,38 @@ public boolean configRemoved(String groupKey){ * @throws NacosRuntimeException, */ private void initMatchGroupKeys(String groupKeyPattern) { - if (groupKeyMatched.containsKey(groupKeyPattern)) { + if (matchedGroupKeys.containsKey(groupKeyPattern)) { return; } - if (groupKeyMatched.size() >= FUZZY_WATCH_MAX_PATTERN_COUNT) { - throw new NacosRuntimeException(FUZZY_WATCH_PATTERN_OVER_LIMIT.getCode(),FUZZY_WATCH_PATTERN_OVER_LIMIT.getMsg()); + if (matchedGroupKeys.size() >= FUZZY_WATCH_MAX_PATTERN_COUNT) { + 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()); } - Set matchedGroupKeys = groupKeyMatched.computeIfAbsent(groupKeyPattern, k -> new HashSet<>()); + matchedGroupKeys.computeIfAbsent(groupKeyPattern, k -> new HashSet<>()); + Set matchedGroupKeys = this.matchedGroupKeys.get(groupKeyPattern); + long matchBeginTime=System.currentTimeMillis(); 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) { - throw new NacosRuntimeException(FUZZY_WATCH_PATTERN_MATCH_GROUP_KEY_OVER_LIMIT.getCode(), FUZZY_WATCH_PATTERN_MATCH_GROUP_KEY_OVER_LIMIT.getMsg()); + 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()); + + break; +// throw new NacosRuntimeException(FUZZY_WATCH_PATTERN_MATCH_GROUP_KEY_OVER_LIMIT.getCode(), +// FUZZY_WATCH_PATTERN_MATCH_GROUP_KEY_OVER_LIMIT.getMsg()); } matchedGroupKeys.add(groupKey); } } + LogUtil.DEFAULT_LOG.info("[fuzzy-watch] pattern {} match {} group keys, cost {}ms", groupKeyPattern, + matchedGroupKeys.size(), System.currentTimeMillis() - matchBeginTime); } @@ -170,7 +174,8 @@ public synchronized void addFuzzyListen(String groupKeyPattern, String connectId initMatchGroupKeys(groupKeyPattern); // Add the connection ID to the set associated with the key pattern in keyPatternContext - keyPatternWatchClients.computeIfAbsent(groupKeyPattern, k -> new HashSet<>()).add(connectId); + watchedClients.computeIfAbsent(groupKeyPattern, k -> new HashSet<>()); + watchedClients.get(groupKeyPattern).add(connectId); } /** @@ -184,7 +189,7 @@ public synchronized void addFuzzyListen(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 = keyPatternWatchClients.get(groupKeyPattern); + Set connectIds = watchedClients.get(groupKeyPattern); if (CollectionUtils.isNotEmpty(connectIds)) { // Remove the connection ID from the set if it exists connectIds.remove(connectionId); @@ -193,10 +198,11 @@ public synchronized void removeFuzzyListen(String groupKeyPattern, String connec /** * remove watch context for connection id. + * * @param connectionId */ public void clearFuzzyWatchContext(String connectionId) { - for (Map.Entry> keyPatternContextEntry : keyPatternWatchClients.entrySet()) { + for (Map.Entry> keyPatternContextEntry : watchedClients.entrySet()) { Set connectionIds = keyPatternContextEntry.getValue(); if (CollectionUtils.isNotEmpty(connectionIds)) { connectionIds.remove(connectionId); @@ -214,15 +220,17 @@ 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 - for (String keyPattern : keyPatternWatchClients.keySet()) { - // Check if the group key matches the current key pattern - String[] strings = GroupKey2.parseKey(groupKey); - - if (FuzzyGroupKeyPattern.matchPattern(groupKey, strings[0], strings[1], strings[2])) { - // If matched, add the associated connection IDs to the set - Set connectIdSet = keyPatternWatchClients.get(keyPattern); - if (CollectionUtils.isNotEmpty(connectIdSet)) { - connectIds.addAll(connectIdSet); + Iterator>> watchClientIterator = watchedClients.entrySet().iterator(); + + String[] groupItems = GroupKey2.parseKey(groupKey); + + while (watchClientIterator.hasNext()){ + Map.Entry> watchClientEntry = watchClientIterator.next(); + + String keyPattern = watchClientEntry.getKey(); + if (FuzzyGroupKeyPattern.matchPattern(keyPattern, groupItems[0], groupItems[1], groupItems[2])) { + if (CollectionUtils.isNotEmpty(watchClientEntry.getValue())) { + connectIds.addAll(watchClientEntry.getValue()); } } } 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 d1db04e0cbf..7d8d997e308 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 @@ -19,14 +19,13 @@ import com.alibaba.nacos.api.exception.runtime.NacosRuntimeException; import com.alibaba.nacos.api.naming.utils.NamingUtils; import com.alibaba.nacos.common.notify.Event; +import com.alibaba.nacos.common.notify.NotifyCenter; import com.alibaba.nacos.common.notify.listener.SmartSubscriber; -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.core.utils.GlobalExecutor; 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.event.service.ServiceEvent; import com.alibaba.nacos.naming.core.v2.pojo.Service; import com.alibaba.nacos.naming.misc.Loggers; import org.springframework.stereotype.Component; @@ -42,7 +41,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; @@ -57,14 +55,14 @@ public class NamingFuzzyWatchContextService extends SmartSubscriber { /** * watched client ids of a pattern, {fuzzy watch pattern -> Set[watched clientID]}. */ - private final ConcurrentMap> keyPatternWatchClients = new ConcurrentHashMap<>(); + private final ConcurrentMap> watchedClients = 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> fuzzyWatchPatternMatchServices = new ConcurrentHashMap<>(); + private final ConcurrentMap> matchedServiceKeys = new ConcurrentHashMap<>(); private final int FUZZY_WATCH_MAX_PATTERN_COUNT = 50; @@ -73,24 +71,24 @@ public class NamingFuzzyWatchContextService extends SmartSubscriber { public NamingFuzzyWatchContextService() { GlobalExecutor.scheduleWithFixDelayByCommon(() -> trimFuzzyWatchContext(), 30000); + NotifyCenter.registerSubscriber(this); } /** - * trim empty fuzzy watch context.
- * 1.remove matched service keys context which no client watched.
- * 2.remove pattern watched clients context if not client watched. + * trim fuzzy watch context.
1.remove watchedClients if watched client is empty. 2.remove matchedServiceKeys + * if watchedClients is null. pattern matchedServiceKeys will be removed in second period to avoid frequently + * matchedServiceKeys init. */ private void trimFuzzyWatchContext() { try { - Iterator>> iterator = fuzzyWatchPatternMatchServices.entrySet().iterator(); + Iterator>> iterator = matchedServiceKeys.entrySet().iterator(); while (iterator.hasNext()) { Map.Entry> next = iterator.next(); - Set watchedClients = keyPatternWatchClients.get(next.getKey()); - if (CollectionUtils.isEmpty(watchedClients)) { + Set watchedClients = this.watchedClients.get(next.getKey()); + if (watchedClients == null) { iterator.remove(); - if (watchedClients != null) { - keyPatternWatchClients.remove(next.getKey()); - } + } else if (watchedClients.isEmpty()) { + this.watchedClients.remove(next.getKey()); } } } catch (Throwable throwable) { @@ -117,7 +115,7 @@ public void onEvent(Event event) { public Set getFuzzyWatchedClients(Service service) { Set matchedClients = new HashSet<>(); - Iterator>> iterator = keyPatternWatchClients.entrySet().iterator(); + Iterator>> iterator = watchedClients.entrySet().iterator(); while (iterator.hasNext()) { Map.Entry> entry = iterator.next(); if (FuzzyGroupKeyPattern.matchPattern(entry.getKey(), service.getName(), service.getGroup(), @@ -130,44 +128,43 @@ public Set getFuzzyWatchedClients(Service service) { public boolean syncServiceContext(Service changedEventService, String changedType) { - boolean needNotify=false; - if (!changedType.equals(ADD_SERVICE)&&!changedType.equals(DELETE_SERVICE)){ + boolean needNotify = false; + if (!changedType.equals(ADD_SERVICE) && !changedType.equals(DELETE_SERVICE)) { return false; } - + String serviceKey = NamingUtils.getServiceKey(changedEventService.getNamespace(), changedEventService.getGroup(), changedEventService.getName()); - Loggers.SRV_LOG.warn("FUZZY_WATCH: service change matched,service key {},changed type {} ", - serviceKey,changedType); + Loggers.SRV_LOG.warn("FUZZY_WATCH: service change matched,service key {},changed type {} ", serviceKey, + changedType); + + Iterator>> iterator = matchedServiceKeys.entrySet().iterator(); - Iterator>> iterator = fuzzyWatchPatternMatchServices.entrySet() - .iterator(); - while (iterator.hasNext()) { Map.Entry> next = iterator.next(); - if (FuzzyGroupKeyPattern.matchPattern(next.getKey(), - changedEventService.getName(), + if (FuzzyGroupKeyPattern.matchPattern(next.getKey(), changedEventService.getName(), changedEventService.getGroup(), changedEventService.getNamespace())) { Set matchedServiceKeys = next.getValue(); - if (changedType.equals(ADD_SERVICE)&&!matchedServiceKeys.contains(serviceKey)){ + 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 , current service will be ignore for pattern {} ,current count is {}", - next.getKey(),matchedServiceKeys.size()); + 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; + if (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; } - - }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; - } } } } @@ -176,14 +173,13 @@ public boolean syncServiceContext(Service changedEventService, String changedTyp public Set syncFuzzyWatcherContext(String groupKeyPattern, String clientId) { - keyPatternWatchClients.computeIfAbsent(groupKeyPattern, key -> new ConcurrentHashSet<>()) - .add(clientId); + watchedClients.computeIfAbsent(groupKeyPattern, key -> new ConcurrentHashSet<>()).add(clientId); Set matchedServiceKeys = initWatchMatchService(groupKeyPattern); return matchedServiceKeys; } private void removeFuzzyWatchContext(String clientId) { - Iterator>> iterator = keyPatternWatchClients.entrySet().iterator(); + Iterator>> iterator = watchedClients.entrySet().iterator(); while (iterator.hasNext()) { Map.Entry> next = iterator.next(); next.getValue().remove(clientId); @@ -192,8 +188,8 @@ private void removeFuzzyWatchContext(String clientId) { public void removeFuzzyWatchContext(String groupKeyPattern, String clientId) { - if (keyPatternWatchClients.containsKey(groupKeyPattern)) { - keyPatternWatchClients.get(groupKeyPattern).remove(clientId); + if (watchedClients.containsKey(groupKeyPattern)) { + watchedClients.get(groupKeyPattern).remove(clientId); } } @@ -205,44 +201,44 @@ public void removeFuzzyWatchContext(String groupKeyPattern, String clientId) { */ public Set initWatchMatchService(String completedPattern) { - if (!fuzzyWatchPatternMatchServices.containsKey(completedPattern)) { - if (fuzzyWatchPatternMatchServices.size() >= FUZZY_WATCH_MAX_PATTERN_COUNT) { - Loggers.SRV_LOG.warn("FUZZY_WATCH: fuzzy watch pattern count is over limit ,pattern {} init fail,current count is {}", - completedPattern,fuzzyWatchPatternMatchServices.size()); + if (!matchedServiceKeys.containsKey(completedPattern)) { + if (matchedServiceKeys.size() >= FUZZY_WATCH_MAX_PATTERN_COUNT) { + 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(), FUZZY_WATCH_PATTERN_OVER_LIMIT.getMsg()); } - + long matchBeginTime = System.currentTimeMillis(); Set namespaceServices = ServiceManager.getInstance() .getSingletons(getNamespaceFromPattern(completedPattern)); - Set matchedServices = fuzzyWatchPatternMatchServices.computeIfAbsent(completedPattern, - k -> new HashSet<>()); - + Set matchedServices = matchedServiceKeys.computeIfAbsent(completedPattern, k -> new HashSet<>()); + 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) { - - 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()); + + 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()); break; -// throw new NacosRuntimeException(FUZZY_WATCH_PATTERN_MATCH_GROUP_KEY_OVER_LIMIT.getCode(), -// FUZZY_WATCH_PATTERN_MATCH_GROUP_KEY_OVER_LIMIT.getMsg()); + // throw new NacosRuntimeException(FUZZY_WATCH_PATTERN_MATCH_GROUP_KEY_OVER_LIMIT.getCode(), + // FUZZY_WATCH_PATTERN_MATCH_GROUP_KEY_OVER_LIMIT.getMsg()); } matchedServices.add( NamingUtils.getServiceKey(service.getNamespace(), service.getGroup(), service.getName())); } } - fuzzyWatchPatternMatchServices.putIfAbsent(completedPattern,matchedServices); + matchedServiceKeys.putIfAbsent(completedPattern, matchedServices); Loggers.SRV_LOG.info("FUZZY_WATCH: pattern {} match {} services, cost {}ms", completedPattern, matchedServices.size(), System.currentTimeMillis() - matchBeginTime); - + } - return new HashSet(fuzzyWatchPatternMatchServices.get(completedPattern)); + return new HashSet(matchedServiceKeys.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 a092b535486..13115d37976 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 @@ -20,6 +20,7 @@ import com.alibaba.nacos.common.notify.Event; import com.alibaba.nacos.common.notify.NotifyCenter; import com.alibaba.nacos.common.notify.listener.SmartSubscriber; +import com.alibaba.nacos.common.task.BatchTaskCounter; import com.alibaba.nacos.common.utils.CollectionUtils; import com.alibaba.nacos.common.utils.FuzzyGroupKeyPattern; import com.alibaba.nacos.naming.core.v2.event.client.ClientOperationEvent; @@ -75,35 +76,7 @@ public void onEvent(Event event) { if (event instanceof ClientOperationEvent.ClientFuzzyWatchEvent) { //fuzzy watch event ClientOperationEvent.ClientFuzzyWatchEvent clientFuzzyWatchEvent = (ClientOperationEvent.ClientFuzzyWatchEvent) event; - String completedPattern = clientFuzzyWatchEvent.getGroupKeyPattern(); - - //sync fuzzy watch context - Set patternMatchedServiceKeys = namingFuzzyWatchContextService.syncFuzzyWatcherContext(completedPattern, clientFuzzyWatchEvent.getClientId()); - - Set clientReceivedGroupKeys = clientFuzzyWatchEvent.getClientReceivedServiceKeys(); - List groupKeyStates = FuzzyGroupKeyPattern.diffGroupKeys( - patternMatchedServiceKeys, clientReceivedGroupKeys); - Set syncContext = convert(groupKeyStates); - String syncType =clientFuzzyWatchEvent.isInitializing()?FUZZY_WATCH_INIT_NOTIFY:FUZZY_WATCH_DIFF_SYNC_NOTIFY; - - if(CollectionUtils.isNotEmpty(groupKeyStates)){ - Set> dividedServices = divideServiceByBatch(syncContext); - FuzzyWatchSyncNotifyTask.BatchTaskCounter batchTaskCounter=new FuzzyWatchSyncNotifyTask.BatchTaskCounter(dividedServices.size()); - int currentBatch=1; - for (Set batchData : dividedServices) { - FuzzyWatchSyncNotifyTask fuzzyWatchSyncNotifyTask=new FuzzyWatchSyncNotifyTask(clientFuzzyWatchEvent.getClientId(),completedPattern,syncType,batchData,PushConfig.getInstance().getPushTaskRetryDelay()); - fuzzyWatchSyncNotifyTask.setBatchTaskCounter(batchTaskCounter); - fuzzyWatchSyncNotifyTask.setTotalBatch(dividedServices.size()); - fuzzyWatchSyncNotifyTask.setCurrentBatch(currentBatch); - fuzzyWatchPushDelayTaskEngine.addTask(FuzzyWatchPushDelayTaskEngine.getTaskKey(fuzzyWatchSyncNotifyTask),fuzzyWatchSyncNotifyTask); - currentBatch++; - } - }else if (FUZZY_WATCH_INIT_NOTIFY.equals(syncType)){ - FuzzyWatchSyncNotifyTask fuzzyWatchSyncNotifyTask=new FuzzyWatchSyncNotifyTask(clientFuzzyWatchEvent.getClientId(),completedPattern,FINISH_FUZZY_WATCH_INIT_NOTIFY,null,PushConfig.getInstance().getPushTaskRetryDelay()); - fuzzyWatchPushDelayTaskEngine.addTask(FuzzyWatchPushDelayTaskEngine.getTaskKey(fuzzyWatchSyncNotifyTask),fuzzyWatchSyncNotifyTask); - - } - + 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(); @@ -112,6 +85,36 @@ public void onEvent(Event event) { } + + private void handleClientFuzzyWatchEvent(ClientOperationEvent.ClientFuzzyWatchEvent clientFuzzyWatchEvent ){ + String completedPattern = clientFuzzyWatchEvent.getGroupKeyPattern(); + + //sync fuzzy watch context + Set patternMatchedServiceKeys = namingFuzzyWatchContextService.syncFuzzyWatcherContext(completedPattern, clientFuzzyWatchEvent.getClientId()); + Set clientReceivedGroupKeys = clientFuzzyWatchEvent.getClientReceivedServiceKeys(); + List groupKeyStates = FuzzyGroupKeyPattern.diffGroupKeys( + patternMatchedServiceKeys, clientReceivedGroupKeys); + Set syncContext = convert(groupKeyStates); + String syncType =clientFuzzyWatchEvent.isInitializing()?FUZZY_WATCH_INIT_NOTIFY:FUZZY_WATCH_DIFF_SYNC_NOTIFY; + + if(CollectionUtils.isNotEmpty(groupKeyStates)){ + Set> dividedServices = divideServiceByBatch(syncContext); + BatchTaskCounter batchTaskCounter=new BatchTaskCounter(dividedServices.size()); + int currentBatch=1; + for (Set batchData : dividedServices) { + FuzzyWatchSyncNotifyTask fuzzyWatchSyncNotifyTask=new FuzzyWatchSyncNotifyTask(clientFuzzyWatchEvent.getClientId(),completedPattern,syncType,batchData,PushConfig.getInstance().getPushTaskRetryDelay()); + fuzzyWatchSyncNotifyTask.setBatchTaskCounter(batchTaskCounter); + fuzzyWatchSyncNotifyTask.setTotalBatch(dividedServices.size()); + fuzzyWatchSyncNotifyTask.setCurrentBatch(currentBatch); + fuzzyWatchPushDelayTaskEngine.addTask(FuzzyWatchPushDelayTaskEngine.getTaskKey(fuzzyWatchSyncNotifyTask),fuzzyWatchSyncNotifyTask); + currentBatch++; + } + }else if (FUZZY_WATCH_INIT_NOTIFY.equals(syncType)){ + FuzzyWatchSyncNotifyTask fuzzyWatchSyncNotifyTask=new FuzzyWatchSyncNotifyTask(clientFuzzyWatchEvent.getClientId(),completedPattern,FINISH_FUZZY_WATCH_INIT_NOTIFY,null,PushConfig.getInstance().getPushTaskRetryDelay()); + fuzzyWatchPushDelayTaskEngine.addTask(FuzzyWatchPushDelayTaskEngine.getTaskKey(fuzzyWatchSyncNotifyTask),fuzzyWatchSyncNotifyTask); + + } + } private Set> divideServiceByBatch( Collection matchedService) { Set> result = new HashSet<>(); 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 b36a498a975..9db80d57abd 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 @@ -70,7 +70,6 @@ private FuzzyWatchChangeNotifyCallback(String clientId, String serviceKey, Strin this.clientId = clientId; this.serviceKey = serviceKey; this.changedType = changedType; - } @Override diff --git a/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchPushDelayTaskEngine.java b/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchPushDelayTaskEngine.java index 9ccdb8fce8f..e1da09d700f 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchPushDelayTaskEngine.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchPushDelayTaskEngine.java @@ -65,10 +65,10 @@ protected void processTasks() { private static class WatchPushDelayTaskProcessor implements NacosTaskProcessor { - private final FuzzyWatchPushDelayTaskEngine fuzztWatchPushExecuteEngine; + private final FuzzyWatchPushDelayTaskEngine fuzzyWatchPushExecuteEngine; - public WatchPushDelayTaskProcessor(FuzzyWatchPushDelayTaskEngine fuzztWatchPushExecuteEngine) { - this.fuzztWatchPushExecuteEngine = fuzztWatchPushExecuteEngine; + public WatchPushDelayTaskProcessor(FuzzyWatchPushDelayTaskEngine fuzzyWatchPushExecuteEngine) { + this.fuzzyWatchPushExecuteEngine = fuzzyWatchPushExecuteEngine; } @Override @@ -79,7 +79,7 @@ public boolean process(NacosTask task) { FuzzyWatchChangeNotifyTask fuzzyWatchChangeNotifyTask = (FuzzyWatchChangeNotifyTask) task; NamingExecuteTaskDispatcher.getInstance().dispatchAndExecuteTask( getTaskKey(task), - new FuzzyWatchChangeNotifyExecuteTask(fuzztWatchPushExecuteEngine, fuzzyWatchChangeNotifyTask.getServiceKey(), + new FuzzyWatchChangeNotifyExecuteTask(fuzzyWatchPushExecuteEngine, fuzzyWatchChangeNotifyTask.getServiceKey(), fuzzyWatchChangeNotifyTask.getChangedType(), fuzzyWatchChangeNotifyTask.getClientId())); } else if (task instanceof FuzzyWatchSyncNotifyTask) { //process fuzzy watch init notify when a new client fuzzy watch a pattern @@ -88,7 +88,7 @@ public boolean process(NacosTask task) { String clientId = fuzzyWatchSyncNotifyTask.getClientId(); NamingExecuteTaskDispatcher.getInstance().dispatchAndExecuteTask(getTaskKey(task), new FuzzyWatchSyncNotifyExecuteTask(clientId, pattern, - fuzztWatchPushExecuteEngine, fuzzyWatchSyncNotifyTask)); + fuzzyWatchPushExecuteEngine, fuzzyWatchSyncNotifyTask)); } return true; } 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 5f372fdb271..5588f89718a 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 @@ -22,7 +22,7 @@ import com.alibaba.nacos.naming.misc.Loggers; import com.alibaba.nacos.naming.push.v2.NoRequiredRetryException; import com.alibaba.nacos.naming.push.v2.PushConfig; - +import com.alibaba.nacos.common.task.BatchTaskCounter; 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; @@ -79,10 +79,10 @@ private class FuzzyWatchSyncNotifyCallback implements PushCallBack { */ private final long executeStartTime; - private FuzzyWatchSyncNotifyTask.BatchTaskCounter batchTaskCounter ; + private BatchTaskCounter batchTaskCounter ; private FuzzyWatchSyncNotifyCallback(NamingFuzzyWatchSyncRequest namingFuzzyWatchSyncRequest, - FuzzyWatchSyncNotifyTask.BatchTaskCounter batchTaskCounter) { + BatchTaskCounter batchTaskCounter) { this.namingFuzzyWatchSyncRequest=namingFuzzyWatchSyncRequest; this.executeStartTime = System.currentTimeMillis(); this.batchTaskCounter=batchTaskCounter; 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 76b18c1d178..a9b95a6cfff 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 @@ -18,13 +18,12 @@ import com.alibaba.nacos.api.naming.remote.request.NamingFuzzyWatchSyncRequest; import com.alibaba.nacos.common.task.AbstractDelayTask; +import com.alibaba.nacos.common.task.BatchTaskCounter; import com.alibaba.nacos.naming.misc.Loggers; -import java.util.ArrayList; + import java.util.HashSet; -import java.util.List; import java.util.Set; -import java.util.concurrent.atomic.AtomicBoolean; /** * Nacos naming fuzzy watch initial push delay task. @@ -114,34 +113,4 @@ public void setBatchTaskCounter(BatchTaskCounter batchTaskCounter) { this.batchTaskCounter = batchTaskCounter; } - public static class BatchTaskCounter{ - - List batchCounter; - - public BatchTaskCounter(int totalBatch){ - initBatchCounter(totalBatch); - } - - public void initBatchCounter(int totalBatch){ - batchCounter=new ArrayList<>(totalBatch); - for(int i=0;i Date: Thu, 9 Jan 2025 15:25:41 +0800 Subject: [PATCH 10/15] fuzzy watch optimize,check style pmd fix --- .../alibaba/nacos/api/common/Constants.java | 12 +- .../nacos/api/config/ConfigService.java | 14 +- ...va => AbstractFuzzyWatchEventWatcher.java} | 12 +- .../api/config/listener/AbstractListener.java | 1 - .../listener/ConfigFuzzyWatchChangeEvent.java | 29 +-- .../listener/FuzzyWatchEventWatcher.java | 43 ++++ .../ConfigFuzzyWatchChangeNotifyRequest.java | 6 +- .../request/ConfigFuzzyWatchRequest.java | 4 - .../request/ConfigFuzzyWatchSyncRequest.java | 30 ++- .../alibaba/nacos/api/model/v2/ErrorCode.java | 69 +++--- .../nacos/api/naming/NamingService.java | 47 ++-- .../AbstractFuzzyWatchEventWatcher.java | 32 +++ .../listener/FuzzyWatchChangeEvent.java | 19 +- .../listener/FuzzyWatchEventWatcher.java | 21 +- .../AbstractFuzzyWatchNotifyRequest.java | 4 +- .../NamingFuzzyWatchChangeNotifyRequest.java | 12 +- .../request/NamingFuzzyWatchRequest.java | 2 +- .../request/NamingFuzzyWatchSyncRequest.java | 57 +++-- .../response/NamingFuzzyWatchResponse.java | 5 +- .../NamingFuzzyWatchSyncResponse.java | 5 +- .../nacos/api/naming/utils/NamingUtils.java | 28 +-- .../client/config/NacosConfigService.java | 58 +++-- .../ClientFuzzyWatchNotifyRequestHandler.java | 14 +- .../client/config/impl/ClientWorker.java | 38 +-- .../config/impl/ConfigFuzzyWatchContext.java | 133 ++++++----- .../impl/ConfigFuzzyWatchGroupKeyHolder.java | 216 +++++++++--------- .../impl/ConfigFuzzyWatchNotifyEvent.java | 35 ++- .../impl/ConfigFuzzyWatcherWrapper.java | 26 ++- .../client/naming/NacosNamingService.java | 58 ++--- .../cache/FuzzyWatchEventWatcherWrapper.java | 15 +- .../naming/cache/NamingFuzzyWatchContext.java | 115 +++++----- .../NamingFuzzyWatchServiceListHolder.java | 177 +++++++------- .../event/NamingFuzzyWatchNotifyEvent.java | 18 +- .../remote/NamingClientProxyDelegate.java | 9 +- .../NamingFuzzyWatchNotifyRequestHandler.java | 72 +++--- .../remote/gprc/NamingGrpcClientProxy.java | 36 +-- .../client/config/NacosConfigServiceTest.java | 65 ++++-- .../remote/AbstractNamingClientProxyTest.java | 4 +- .../remote/NamingClientProxyDelegateTest.java | 7 +- .../gprc/redo/NamingGrpcRedoServiceTest.java | 1 + .../nacos/common/task/BatchTaskCounter.java | 21 +- .../common/utils/FuzzyGroupKeyPattern.java | 101 ++++---- .../utils/FuzzyGroupKeyPatternTest.java | 2 - .../configuration/ConfigCommonConfig.java | 1 + .../event/ConfigCancelFuzzyWatchEvent.java | 8 +- .../remote/ConfigConnectionEventListener.java | 5 +- .../ConfigFuzzyWatchChangeNotifier.java | 29 ++- .../ConfigFuzzyWatchRequestHandler.java | 46 ++-- .../remote/ConfigFuzzyWatchSyncNotifier.java | 97 ++++---- .../ConfigFuzzyWatchContextService.java | 79 ++++--- ...nfigFuzzyWatchRequestParamsExtractor.java} | 16 +- ...mple.java => ConfigFuzzyWatchExample.java} | 7 +- ...mple.java => NamingFuzzyWatchExample.java} | 12 +- .../v2/event/client/ClientOperationEvent.java | 33 +-- .../core/v2/event/service/ServiceEvent.java | 6 +- .../v2/index/ClientServiceIndexesManager.java | 16 +- .../index/NamingFuzzyWatchContextService.java | 67 ++++-- .../push/NamingFuzzyWatchChangeNotifier.java | 37 +-- .../push/NamingFuzzyWatchSyncNotifier.java | 97 ++++---- .../FuzzyWatchChangeNotifyExecuteTask.java | 11 +- .../v2/task/FuzzyWatchChangeNotifyTask.java | 13 +- .../task/FuzzyWatchPushDelayTaskEngine.java | 40 ++-- .../task/FuzzyWatchSyncNotifyExecuteTask.java | 65 +++--- .../v2/task/FuzzyWatchSyncNotifyTask.java | 14 +- .../NamingFuzzyWatchRequestHandler.java | 16 +- .../ClientServiceIndexesManagerTest.java | 1 - .../v2/NamingSubscriberServiceV2ImplTest.java | 17 +- 67 files changed, 1314 insertions(+), 1092 deletions(-) rename api/src/main/java/com/alibaba/nacos/api/config/listener/{ConfigFuzzyWatcher.java => AbstractFuzzyWatchEventWatcher.java} (76%) create mode 100644 api/src/main/java/com/alibaba/nacos/api/config/listener/FuzzyWatchEventWatcher.java create mode 100644 api/src/main/java/com/alibaba/nacos/api/naming/listener/AbstractFuzzyWatchEventWatcher.java rename core/src/main/java/com/alibaba/nacos/core/paramcheck/impl/{ConfigBatchFuzzyListenRequestParamsExtractor.java => ConfigFuzzyWatchRequestParamsExtractor.java} (75%) rename example/src/main/java/com/alibaba/nacos/example/{FuzzyListenExample.java => ConfigFuzzyWatchExample.java} (93%) rename example/src/main/java/com/alibaba/nacos/example/{FuzzyWatchExample.java => NamingFuzzyWatchExample.java} (89%) 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 39225ac7364..c903a54ba2d 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 @@ -279,32 +279,32 @@ public static class Naming { public static final String FUZZY_WATCH_PATTERN_SPLITTER = ">>"; /** - * fuzzy watch sync type of watch init notify + * fuzzy watch sync type of watch init notify. */ public static final String FUZZY_WATCH_INIT_NOTIFY = "FUZZY_WATCH_INIT_NOTIFY"; /** - * fuzzy watch sync type of watch init notify finish + * fuzzy watch sync type of watch init notify finish. */ public static final String FINISH_FUZZY_WATCH_INIT_NOTIFY = "FINISH_FUZZY_WATCH_INIT_NOTIFY"; /** - * fuzzy watch sync type of watch diff sync notify + * fuzzy watch sync type of watch diff sync notify. */ public static final String FUZZY_WATCH_DIFF_SYNC_NOTIFY = "FUZZY_WATCH_DIFF_SYNC_NOTIFY"; /** - * fuzzy watch sync type of watch resource changed + * fuzzy watch sync type of watch resource changed. */ public static final String FUZZY_WATCH_RESOURCE_CHANGED = "FUZZY_WATCH_RESOURCE_CHANGED"; /** - * watch type of watch + * watch type of watch. */ public static final String WATCH_TYPE_WATCH = "WATCH"; /** - * watch type of cancel watch + * watch type of cancel watch. */ public static final String WATCH_TYPE_CANCEL_WATCH = "CANCEL_WATCH"; diff --git a/api/src/main/java/com/alibaba/nacos/api/config/ConfigService.java b/api/src/main/java/com/alibaba/nacos/api/config/ConfigService.java index d7281bbaa68..2c6699a1392 100644 --- a/api/src/main/java/com/alibaba/nacos/api/config/ConfigService.java +++ b/api/src/main/java/com/alibaba/nacos/api/config/ConfigService.java @@ -17,7 +17,7 @@ package com.alibaba.nacos.api.config; import com.alibaba.nacos.api.config.filter.IConfigFilter; -import com.alibaba.nacos.api.config.listener.ConfigFuzzyWatcher; +import com.alibaba.nacos.api.config.listener.FuzzyWatchEventWatcher; import com.alibaba.nacos.api.config.listener.Listener; import com.alibaba.nacos.api.exception.NacosException; @@ -176,7 +176,7 @@ boolean publishConfigCas(String dataId, String group, String content, String cas * @throws NacosException NacosException * @since 3.0 */ - void fuzzyWatch(String fixedGroupName, ConfigFuzzyWatcher watcher) throws NacosException; + void fuzzyWatch(String fixedGroupName, FuzzyWatchEventWatcher watcher) throws NacosException; /** * Add a fuzzy listener to the configuration. After the server modifies the configuration matching the specified @@ -189,7 +189,7 @@ boolean publishConfigCas(String dataId, String group, String content, String cas * @throws NacosException NacosException * @since 3.0 */ - void fuzzyWatch(String dataIdPattern, String fixedGroupName, ConfigFuzzyWatcher watcher) + void fuzzyWatch(String dataIdPattern, String fixedGroupName, FuzzyWatchEventWatcher watcher) throws NacosException; /** @@ -204,7 +204,7 @@ void fuzzyWatch(String dataIdPattern, String fixedGroupName, ConfigFuzzyWatcher * @since 3.0 */ Future> fuzzyWatchWithGroupKeys(String fixedGroupName, - ConfigFuzzyWatcher watcher) throws NacosException; + FuzzyWatchEventWatcher watcher) throws NacosException; /** * Add a fuzzy listener to the configuration and retrieve all configs that match the specified dataId pattern and @@ -219,7 +219,7 @@ Future> fuzzyWatchWithGroupKeys(String fixedGroupName, * @since 3.0 */ Future> fuzzyWatchWithGroupKeys(String dataIdPattern, String fixedGroupName, - ConfigFuzzyWatcher watcher) throws NacosException; + FuzzyWatchEventWatcher watcher) throws NacosException; /** * Cancel fuzzy listen and remove the event listener for a specified fixed group name. @@ -229,7 +229,7 @@ Future> fuzzyWatchWithGroupKeys(String dataIdPattern, String fixedGr * @throws NacosException If an error occurs during the cancellation process. * @since 3.0 */ - void cancelFuzzyWatch(String fixedGroupName, ConfigFuzzyWatcher watcher) throws NacosException; + void cancelFuzzyWatch(String fixedGroupName, FuzzyWatchEventWatcher watcher) throws NacosException; /** * Cancel fuzzy listen and remove the event listener for a specified service name pattern and fixed group name. @@ -240,7 +240,7 @@ Future> fuzzyWatchWithGroupKeys(String dataIdPattern, String fixedGr * @throws NacosException If an error occurs during the cancellation process. * @since 3.0 */ - void cancelFuzzyWatch(String dataIdPattern, String fixedGroupName, ConfigFuzzyWatcher watcher) + void cancelFuzzyWatch(String dataIdPattern, String fixedGroupName, FuzzyWatchEventWatcher watcher) throws NacosException; } diff --git a/api/src/main/java/com/alibaba/nacos/api/config/listener/ConfigFuzzyWatcher.java b/api/src/main/java/com/alibaba/nacos/api/config/listener/AbstractFuzzyWatchEventWatcher.java similarity index 76% rename from api/src/main/java/com/alibaba/nacos/api/config/listener/ConfigFuzzyWatcher.java rename to api/src/main/java/com/alibaba/nacos/api/config/listener/AbstractFuzzyWatchEventWatcher.java index d6ecc9e641a..b62cca652af 100644 --- a/api/src/main/java/com/alibaba/nacos/api/config/listener/ConfigFuzzyWatcher.java +++ b/api/src/main/java/com/alibaba/nacos/api/config/listener/AbstractFuzzyWatchEventWatcher.java @@ -25,22 +25,14 @@ * @author stone-98 * @date 2024/3/4 */ -public abstract class ConfigFuzzyWatcher { - - /** - * Callback method invoked when a fuzzy configuration change event occurs. - * - * @param event The fuzzy configuration change event - */ - public abstract void onEvent(ConfigFuzzyWatchChangeEvent event); +public abstract class AbstractFuzzyWatchEventWatcher implements FuzzyWatchEventWatcher { /** * Get executor for execute this receive. * * @return Executor */ - public Executor getExecutor(){ + public Executor getExecutor() { return null; } - } diff --git a/api/src/main/java/com/alibaba/nacos/api/config/listener/AbstractListener.java b/api/src/main/java/com/alibaba/nacos/api/config/listener/AbstractListener.java index e14008d01d9..3f4c968f378 100644 --- a/api/src/main/java/com/alibaba/nacos/api/config/listener/AbstractListener.java +++ b/api/src/main/java/com/alibaba/nacos/api/config/listener/AbstractListener.java @@ -23,7 +23,6 @@ * * @author water.lyl */ -@SuppressWarnings("PMD.AbstractClassShouldStartWithAbstractNamingRule") public abstract class AbstractListener implements Listener { /** diff --git a/api/src/main/java/com/alibaba/nacos/api/config/listener/ConfigFuzzyWatchChangeEvent.java b/api/src/main/java/com/alibaba/nacos/api/config/listener/ConfigFuzzyWatchChangeEvent.java index 36c9e7be59c..ac90ec4caaf 100644 --- a/api/src/main/java/com/alibaba/nacos/api/config/listener/ConfigFuzzyWatchChangeEvent.java +++ b/api/src/main/java/com/alibaba/nacos/api/config/listener/ConfigFuzzyWatchChangeEvent.java @@ -45,40 +45,43 @@ public class ConfigFuzzyWatchChangeEvent { /** * The change type of local watcher , contains {"ADD_CONFIG", "DELETE_CONFIG"}. - * see {@link Constants.ConfigChangedType} + * {@link Constants.ConfigChangedType}. */ private String changedType; /** - * the sync type that trigger this changed,contains {"FUZZY_WATCH_INIT_NOTIFY","FUZZY_WATCH_RESOURCE_CHANGED", "FUZZY_WATCH_DIFF_SYNC_NOTIFY"} + * the sync type that trigger this changed,contains {"FUZZY_WATCH_INIT_NOTIFY","FUZZY_WATCH_RESOURCE_CHANGED", + * "FUZZY_WATCH_DIFF_SYNC_NOTIFY"}. */ private String syncType; /** * Constructs a FuzzyListenConfigChangeEvent with the specified parameters. * - * @param group The group of the configuration that has changed - * @param dataId The data ID of the configuration that has changed - * @param changedType The type of change that has occurred + * @param group The group of the configuration that has changed + * @param dataId The data ID of the configuration that has changed + * @param changedType The type of change that has occurred */ - private ConfigFuzzyWatchChangeEvent(String namespace,String group, String dataId, String changedType,String syncType) { + private ConfigFuzzyWatchChangeEvent(String namespace, String group, String dataId, String changedType, + String syncType) { this.group = group; this.dataId = dataId; - this.namespace=namespace; + this.namespace = namespace; this.changedType = changedType; - this.syncType=syncType; + this.syncType = syncType; } /** * Constructs and returns a new FuzzyListenConfigChangeEvent with the specified parameters. * - * @param group The group of the configuration that has changed - * @param dataId The data ID of the configuration that has changed - * @param changedType The type of change that has occurred + * @param group The group of the configuration that has changed + * @param dataId The data ID of the configuration that has changed + * @param changedType The type of change that has occurred * @return A new FuzzyListenConfigChangeEvent instance */ - public static ConfigFuzzyWatchChangeEvent build(String namespace,String group, String dataId, String changedType,String syncType) { - return new ConfigFuzzyWatchChangeEvent(namespace,group, dataId, changedType,syncType); + public static ConfigFuzzyWatchChangeEvent build(String namespace, String group, String dataId, String changedType, + String syncType) { + return new ConfigFuzzyWatchChangeEvent(namespace, group, dataId, changedType, syncType); } public String getNamespace() { diff --git a/api/src/main/java/com/alibaba/nacos/api/config/listener/FuzzyWatchEventWatcher.java b/api/src/main/java/com/alibaba/nacos/api/config/listener/FuzzyWatchEventWatcher.java new file mode 100644 index 00000000000..56c1bbe5ed9 --- /dev/null +++ b/api/src/main/java/com/alibaba/nacos/api/config/listener/FuzzyWatchEventWatcher.java @@ -0,0 +1,43 @@ +/* + * 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; + +import java.util.concurrent.Executor; + +/** + * fuzzy watch config changes. + * + * @author stone-98 + * @date 2024/3/4 + */ +public interface FuzzyWatchEventWatcher { + + /** + * Callback method invoked when a fuzzy configuration change event occurs. + * + * @param event The fuzzy configuration change event + */ + void onEvent(ConfigFuzzyWatchChangeEvent event); + + /** + * Get executor for execute this receive. + * + * @return Executor + */ + Executor getExecutor(); + +} diff --git a/api/src/main/java/com/alibaba/nacos/api/config/remote/request/ConfigFuzzyWatchChangeNotifyRequest.java b/api/src/main/java/com/alibaba/nacos/api/config/remote/request/ConfigFuzzyWatchChangeNotifyRequest.java index b42854819c1..aaa64a705db 100644 --- a/api/src/main/java/com/alibaba/nacos/api/config/remote/request/ConfigFuzzyWatchChangeNotifyRequest.java +++ b/api/src/main/java/com/alibaba/nacos/api/config/remote/request/ConfigFuzzyWatchChangeNotifyRequest.java @@ -31,7 +31,6 @@ public class ConfigFuzzyWatchChangeNotifyRequest extends AbstractFuzzyWatchNotif */ private String groupKey; - /** * Indicates whether the configuration exists or not. */ @@ -49,7 +48,7 @@ public ConfigFuzzyWatchChangeNotifyRequest() { * @param groupKey The group of the configuration that has changed * @param changeType Indicates whether the configuration exists or not */ - public ConfigFuzzyWatchChangeNotifyRequest(String groupKey,String changeType) { + public ConfigFuzzyWatchChangeNotifyRequest(String groupKey, String changeType) { this.groupKey = groupKey; this.changeType = changeType; } @@ -77,7 +76,8 @@ public void setChangeType(String changeType) { */ @Override public String toString() { - return "FuzzyListenNotifyChangeRequest{" + '\'' + ", groupKey='" + groupKey + '\'' + ", changeType=" + changeType + '}'; + return "FuzzyListenNotifyChangeRequest{" + '\'' + ", groupKey='" + groupKey + '\'' + ", changeType=" + + changeType + '}'; } } diff --git a/api/src/main/java/com/alibaba/nacos/api/config/remote/request/ConfigFuzzyWatchRequest.java b/api/src/main/java/com/alibaba/nacos/api/config/remote/request/ConfigFuzzyWatchRequest.java index f1149742f7b..f61e36baac2 100644 --- a/api/src/main/java/com/alibaba/nacos/api/config/remote/request/ConfigFuzzyWatchRequest.java +++ b/api/src/main/java/com/alibaba/nacos/api/config/remote/request/ConfigFuzzyWatchRequest.java @@ -37,10 +37,8 @@ public class ConfigFuzzyWatchRequest extends Request { */ private String groupKeyPattern; - private Set receivedGroupKeys; - /** * Flag indicating whether to listen for changes. */ @@ -51,7 +49,6 @@ public class ConfigFuzzyWatchRequest extends Request { */ private boolean isInitializing; - /** * Constructs an empty ConfigBatchFuzzyListenRequest. */ @@ -100,5 +97,4 @@ public String getModule() { return Constants.Config.CONFIG_MODULE; } - } 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 ebedf7b4207..e6ead123c78 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 @@ -18,7 +18,6 @@ import com.alibaba.nacos.api.common.Constants; -import java.util.HashSet; import java.util.Set; /** @@ -43,16 +42,14 @@ public class ConfigFuzzyWatchSyncRequest extends AbstractFuzzyWatchNotifyRequest private Set contexts; /** - *see FUZZY_WATCH_INIT_NOTIFY,FINISH_FUZZY_WATCH_INIT_NOTIFY,FUZZY_WATCH_DIFF_SYNC_NOTIFY + * see FUZZY_WATCH_INIT_NOTIFY,FINISH_FUZZY_WATCH_INIT_NOTIFY,FUZZY_WATCH_DIFF_SYNC_NOTIFY. */ private String syncType; - private int totalBatch; private int currentBatch; - public String getSyncType() { return syncType; } @@ -89,12 +86,13 @@ public ConfigFuzzyWatchSyncRequest() { * @param groupKeyPattern The pattern used to match group keys for the configurations * @param contexts The set of contexts containing information about the configurations */ - private ConfigFuzzyWatchSyncRequest(String syncType, String groupKeyPattern, Set contexts,int totalBatch,int currentBatch) { + private ConfigFuzzyWatchSyncRequest(String syncType, String groupKeyPattern, Set contexts, int totalBatch, + int currentBatch) { this.groupKeyPattern = groupKeyPattern; this.contexts = contexts; this.syncType = syncType; - this.currentBatch=currentBatch; - this.totalBatch=totalBatch; + this.currentBatch = currentBatch; + this.totalBatch = totalBatch; } @@ -105,12 +103,11 @@ private ConfigFuzzyWatchSyncRequest(String syncType, String groupKeyPattern, Set * @param groupKeyPattern The pattern used to match group keys for the configurations * @return An initial FuzzyListenNotifyDiffRequest */ - public static ConfigFuzzyWatchSyncRequest buildSyncRequest(String syncType,Set contexts, String groupKeyPattern,int totalBatch,int currentBatch) { - return new ConfigFuzzyWatchSyncRequest(syncType, groupKeyPattern, contexts,totalBatch,currentBatch); + public static ConfigFuzzyWatchSyncRequest buildSyncRequest(String syncType, Set contexts, + String groupKeyPattern, int totalBatch, int currentBatch) { + return new ConfigFuzzyWatchSyncRequest(syncType, groupKeyPattern, contexts, totalBatch, currentBatch); } - - /** * Builds a final FuzzyListenNotifyDiffRequest with the specified group key pattern. * @@ -118,7 +115,7 @@ public static ConfigFuzzyWatchSyncRequest buildSyncRequest(String syncType,Set contexts) { public static class Context { String groupKey; + /** * see {@link com.alibaba.nacos.api.common.Constants.ConfigChangedType ADD_CONFIG&} ADD_CONFIG: a new config * should be added for clientside . DELETE_CONFIG: a config should be removed for clientside . @@ -158,7 +156,7 @@ public Context() { /** * Builds a new context object with the provided parameters. * - * @param groupKey The groupKey associated of the configuration. + * @param groupKey The groupKey associated of the configuration. * @param changedType The type of the configuration change event. * @return A new context object initialized with the provided parameters. */ @@ -168,15 +166,15 @@ public static Context build(String groupKey, String changedType) { context.setChangedType(changedType); return context; } - + public String getGroupKey() { return groupKey; } - + public void setGroupKey(String groupKey) { this.groupKey = groupKey; } - + public String getChangedType() { return changedType; } 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 4c1d90509df..c810a75440c 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 @@ -26,67 +26,67 @@ public enum ErrorCode { /** - * success. + * success. */ SUCCESS(0, "success"), /** - * parameter missing. + * parameter missing. */ PARAMETER_MISSING(10000, "parameter missing"), /** - * access denied. + * access denied. */ ACCESS_DENIED(10001, "access denied"), /** - * data access error. + * data access error. */ DATA_ACCESS_ERROR(10002, "data access error"), /** - * 'tenant' parameter error. + * 'tenant' parameter error. */ TENANT_PARAM_ERROR(20001, "'tenant' parameter error"), /** - * parameter validate error. + * parameter validate error. */ PARAMETER_VALIDATE_ERROR(20002, "parameter validate error"), /** - * MediaType Error. + * MediaType Error. */ MEDIA_TYPE_ERROR(20003, "MediaType Error"), /** - * resource not found. + * resource not found. */ RESOURCE_NOT_FOUND(20004, "resource not found"), /** - * resource conflict. + * resource conflict. */ RESOURCE_CONFLICT(20005, "resource conflict"), /** - * config listener is null. + * config listener is null. */ CONFIG_LISTENER_IS_NULL(20006, "config listener is null"), /** - * config listener error. + * config listener error. */ CONFIG_LISTENER_ERROR(20007, "config listener error"), /** - * invalid dataId. + * invalid dataId. */ INVALID_DATA_ID(20008, "invalid dataId"), /** - * parameter mismatch. + * parameter mismatch. */ PARAMETER_MISMATCH(20009, "parameter mismatch"), @@ -111,97 +111,97 @@ public enum ErrorCode { CONFIG_GRAY_NAME_UNRECOGNIZED_ERROR(20013, "config gray name not recognized"), /** - * service name error. + * service name error. */ SERVICE_NAME_ERROR(21000, "service name error"), /** - * weight error. + * weight error. */ WEIGHT_ERROR(21001, "weight error"), /** - * instance metadata error. + * instance metadata error. */ INSTANCE_METADATA_ERROR(21002, "instance metadata error"), /** - * instance not found. + * instance not found. */ INSTANCE_NOT_FOUND(21003, "instance not found"), /** - * instance error. + * instance error. */ INSTANCE_ERROR(21004, "instance error"), /** - * service metadata error. + * service metadata error. */ SERVICE_METADATA_ERROR(21005, "service metadata error"), /** - * selector error. + * selector error. */ SELECTOR_ERROR(21006, "selector error"), /** - * service already exist. + * service already exist. */ SERVICE_ALREADY_EXIST(21007, "service already exist"), /** - * service not exist. + * service not exist. */ SERVICE_NOT_EXIST(21008, "service not exist"), /** - * service delete failure. + * service delete failure. */ SERVICE_DELETE_FAILURE(21009, "service delete failure"), /** - * healthy param miss. + * healthy param miss. */ HEALTHY_PARAM_MISS(21010, "healthy param miss"), /** - * health check still running. + * health check still running. */ HEALTH_CHECK_STILL_RUNNING(21011, "health check still running"), /** - * illegal namespace. + * illegal namespace. */ ILLEGAL_NAMESPACE(22000, "illegal namespace"), /** - * namespace not exist. + * namespace not exist. */ NAMESPACE_NOT_EXIST(22001, "namespace not exist"), /** - * namespace already exist. + * namespace already exist. */ NAMESPACE_ALREADY_EXIST(22002, "namespace already exist"), /** - * illegal state. + * illegal state. */ ILLEGAL_STATE(23000, "illegal state"), /** - * node info error. + * node info error. */ NODE_INFO_ERROR(23001, "node info error"), /** - * node down failure. + * node down failure. */ NODE_DOWN_FAILURE(23002, "node down failure"), /** - * server error. + * server error. */ SERVER_ERROR(30000, "server error"), @@ -223,12 +223,9 @@ public enum ErrorCode { NO_SELECTED_CONFIG(100006, "没有选择任何配置"), - 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_GROUP_KEY_OVER_LIMIT(50311, "fuzzy watch pattern matched group key over limit"); private final Integer code; diff --git a/api/src/main/java/com/alibaba/nacos/api/naming/NamingService.java b/api/src/main/java/com/alibaba/nacos/api/naming/NamingService.java index 020db44ff3f..20034bae97e 100644 --- a/api/src/main/java/com/alibaba/nacos/api/naming/NamingService.java +++ b/api/src/main/java/com/alibaba/nacos/api/naming/NamingService.java @@ -556,6 +556,29 @@ void subscribe(String serviceName, String groupName, NamingSelector selector, Ev */ void unsubscribe(String serviceName, String groupName, List clusters, EventListener listener) throws NacosException; + + /** + * Unsubscribe event listener of service. + * + * @param serviceName name of service + * @param selector selector of instances + * @param listener event listener + * @throws NacosException nacos exception + */ + void unsubscribe(String serviceName, NamingSelector selector, EventListener listener) throws NacosException; + + /** + * Unsubscribe event listener of service. + * + * @param serviceName name of service + * @param groupName group of service + * @param selector selector of instances + * @param listener event listener + * @throws NacosException nacos exception + */ + void unsubscribe(String serviceName, String groupName, NamingSelector selector, EventListener listener) + throws NacosException; + /** * According to matching rules, watch services within a specific scope, and receive notifications when @@ -589,6 +612,7 @@ void fuzzyWatch(String serviceNamePattern, String groupNamePattern, * * @param groupNamePattern group name pattern for fuzzy watch * @param listener event listener + * @return matched service keys. * @throws NacosException nacos exception */ Future> fuzzyWatchWithServiceKeys(String groupNamePattern, FuzzyWatchEventWatcher listener) throws NacosException; @@ -602,6 +626,7 @@ void fuzzyWatch(String serviceNamePattern, String groupNamePattern, * @param serviceNamePattern service name pattern for fuzzy watch * @param groupNamePattern group name pattern for fuzzy watch * @param listener event listener + * @return matched service keys. * @throws NacosException nacos exception */ Future> fuzzyWatchWithServiceKeys(String serviceNamePattern, String groupNamePattern, @@ -625,28 +650,6 @@ Future> fuzzyWatchWithServiceKeys(String serviceNamePattern, St * @throws NacosException nacos exception */ void cancelFuzzyWatch(String serviceNamePattern, String groupNamePattern, FuzzyWatchEventWatcher listener) throws NacosException; - - /** - * Unsubscribe event listener of service. - * - * @param serviceName name of service - * @param selector selector of instances - * @param listener event listener - * @throws NacosException nacos exception - */ - void unsubscribe(String serviceName, NamingSelector selector, EventListener listener) throws NacosException; - - /** - * Unsubscribe event listener of service. - * - * @param serviceName name of service - * @param groupName group of service - * @param selector selector of instances - * @param listener event listener - * @throws NacosException nacos exception - */ - void unsubscribe(String serviceName, String groupName, NamingSelector selector, EventListener listener) - throws NacosException; /** * Get all service names from server. 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 new file mode 100644 index 00000000000..fa10d05f1ea --- /dev/null +++ b/api/src/main/java/com/alibaba/nacos/api/naming/listener/AbstractFuzzyWatchEventWatcher.java @@ -0,0 +1,32 @@ +/* + * 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; + +import java.util.concurrent.Executor; + +/** + * Abstract fuzzy watch event listener, to support handle event by user custom executor. + * + * @author tanyongquan + */ +public abstract class AbstractFuzzyWatchEventWatcher implements FuzzyWatchEventWatcher { + + @Override + public Executor getExecutor() { + return null; + } +} diff --git a/api/src/main/java/com/alibaba/nacos/api/naming/listener/FuzzyWatchChangeEvent.java b/api/src/main/java/com/alibaba/nacos/api/naming/listener/FuzzyWatchChangeEvent.java index 13ecc20d524..c76e20d2ae9 100644 --- a/api/src/main/java/com/alibaba/nacos/api/naming/listener/FuzzyWatchChangeEvent.java +++ b/api/src/main/java/com/alibaba/nacos/api/naming/listener/FuzzyWatchChangeEvent.java @@ -36,37 +36,38 @@ public class FuzzyWatchChangeEvent implements Event { public FuzzyWatchChangeEvent() { } - public FuzzyWatchChangeEvent(String serviceName,String groupName,String namespace, String changeType,String syncType) { + public FuzzyWatchChangeEvent(String serviceName, String groupName, String namespace, String changeType, + String syncType) { this.changeType = changeType; - this.serviceName=serviceName; - this.groupName=groupName; - this.namespace=namespace; - this.syncType=syncType; + this.serviceName = serviceName; + this.groupName = groupName; + this.namespace = namespace; + this.syncType = syncType; } public String getServiceName() { return serviceName; } - public String getGroupName() { return groupName; } - public String getNamespace() { return namespace; } /** - * The change type of local watcher , contains {"ADD_SERVICE", "DELETE_SERVICE"}. see Constants.ServiceChangedType + * The change type of local watcher , contains {"ADD_SERVICE", "DELETE_SERVICE"}. see Constants.ServiceChangedType */ public String getChangeType() { return changeType; } /** - * the sync type that trigger this changed,contains {"FUZZY_WATCH_INIT_NOTIFY","FUZZY_WATCH_RESOURCE_CHANGED", "FUZZY_WATCH_DIFF_SYNC_NOTIFY"} + * the sync type that trigger this changed,contains {"FUZZY_WATCH_INIT_NOTIFY","FUZZY_WATCH_RESOURCE_CHANGED", + * "FUZZY_WATCH_DIFF_SYNC_NOTIFY"}. + * * @return */ public String getSyncType() { diff --git a/api/src/main/java/com/alibaba/nacos/api/naming/listener/FuzzyWatchEventWatcher.java b/api/src/main/java/com/alibaba/nacos/api/naming/listener/FuzzyWatchEventWatcher.java index 4a1eb20413b..edd6c2a3603 100644 --- a/api/src/main/java/com/alibaba/nacos/api/naming/listener/FuzzyWatchEventWatcher.java +++ b/api/src/main/java/com/alibaba/nacos/api/naming/listener/FuzzyWatchEventWatcher.java @@ -16,10 +16,6 @@ package com.alibaba.nacos.api.naming.listener; -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; -import java.util.UUID; import java.util.concurrent.Executor; /** @@ -27,13 +23,18 @@ * * @author tanyongquan */ -public abstract class FuzzyWatchEventWatcher { +public interface FuzzyWatchEventWatcher { - - public Executor getExecutor() { - return null; - } + /** + * executor to notify event, using nacos internal notifier if null . + * @return + */ + Executor getExecutor(); - public abstract void onEvent(FuzzyWatchChangeEvent event); + /** + * handle FuzzyWatchChangeEvent. + * @param event fuzzy watch change event. + */ + void onEvent(FuzzyWatchChangeEvent event); } diff --git a/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/AbstractFuzzyWatchNotifyRequest.java b/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/AbstractFuzzyWatchNotifyRequest.java index 966a0dd992e..c0a776d54bb 100644 --- a/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/AbstractFuzzyWatchNotifyRequest.java +++ b/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/AbstractFuzzyWatchNotifyRequest.java @@ -29,10 +29,10 @@ public abstract class AbstractFuzzyWatchNotifyRequest extends ServerRequest { private String syncType; - public AbstractFuzzyWatchNotifyRequest(){ + public AbstractFuzzyWatchNotifyRequest() { } - public AbstractFuzzyWatchNotifyRequest( String syncType) { + public AbstractFuzzyWatchNotifyRequest(String syncType) { this.syncType = syncType; } 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 98eaec76cd9..3abade19bc2 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 @@ -24,17 +24,17 @@ public class NamingFuzzyWatchChangeNotifyRequest extends AbstractFuzzyWatchNotifyRequest { private String serviceKey; - - private String changedType; - public NamingFuzzyWatchChangeNotifyRequest(){ + private String changedType; + + public NamingFuzzyWatchChangeNotifyRequest() { } - public NamingFuzzyWatchChangeNotifyRequest(String serviceKey,String changedType, String syncType) { + public NamingFuzzyWatchChangeNotifyRequest(String serviceKey, String changedType, String syncType) { super(syncType); - this.serviceKey=serviceKey; - this.changedType=changedType; + this.serviceKey = serviceKey; + this.changedType = changedType; } public String getServiceKey() { diff --git a/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/NamingFuzzyWatchRequest.java b/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/NamingFuzzyWatchRequest.java index d0022fc070f..695348f8b00 100644 --- a/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/NamingFuzzyWatchRequest.java +++ b/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/NamingFuzzyWatchRequest.java @@ -46,7 +46,7 @@ public NamingFuzzyWatchRequest() { } public NamingFuzzyWatchRequest(String groupKeyPattern, String watchType) { - this.watchType=watchType; + this.watchType = watchType; this.groupKeyPattern = groupKeyPattern; } 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 5e2bd2fee58..c7ac6ca52a4 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 @@ -17,11 +17,16 @@ 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 { /** @@ -38,13 +43,14 @@ public class NamingFuzzyWatchSyncRequest extends AbstractFuzzyWatchNotifyRequest private int currentBatch; - public NamingFuzzyWatchSyncRequest(){ + public NamingFuzzyWatchSyncRequest() { } - public NamingFuzzyWatchSyncRequest(String pattern, String syncType,Set contexts){ - super(syncType); - this.groupKeyPattern=pattern; - this.contexts=contexts; + + public NamingFuzzyWatchSyncRequest(String pattern, String syncType, Set contexts) { + super(syncType); + this.groupKeyPattern = pattern; + this.contexts = contexts; } public int getTotalBatch() { @@ -67,14 +73,25 @@ public static NamingFuzzyWatchSyncRequest buildInitNotifyFinishRequest(String pa return new NamingFuzzyWatchSyncRequest(pattern, Constants.FINISH_FUZZY_WATCH_INIT_NOTIFY, new HashSet<>(1)); } - public static NamingFuzzyWatchSyncRequest buildSyncNotifyRequest(String pattern,String syncType,Set contexts,int totalBatch,int currentBatch) { - NamingFuzzyWatchSyncRequest namingFuzzyWatchSyncRequest =new NamingFuzzyWatchSyncRequest(pattern, syncType, contexts); - namingFuzzyWatchSyncRequest.currentBatch=currentBatch; - namingFuzzyWatchSyncRequest.totalBatch=totalBatch; + /** + * byuld SyncNotifyRequest. + * + * @param pattern pattern + * @param syncType syncType + * @param contexts contexts + * @param totalBatch totalBatch + * @param currentBatch currentBatch + * @return + */ + public static NamingFuzzyWatchSyncRequest buildSyncNotifyRequest(String pattern, String syncType, + Set contexts, int totalBatch, int currentBatch) { + NamingFuzzyWatchSyncRequest namingFuzzyWatchSyncRequest = new NamingFuzzyWatchSyncRequest(pattern, syncType, + contexts); + namingFuzzyWatchSyncRequest.currentBatch = currentBatch; + namingFuzzyWatchSyncRequest.totalBatch = totalBatch; return namingFuzzyWatchSyncRequest; } - public String getGroupKeyPattern() { return groupKeyPattern; } @@ -96,16 +113,18 @@ public String getModule() { return NAMING_MODULE; } + /** + * fuzzy watch sync context. + */ public static class Context { - + /** - * @see com.alibaba.nacos.api.naming.utils.NamingUtils#getServiceKey(String, String, String) + * service key. */ String serviceKey; + /** - * see {@link com.alibaba.nacos.api.common.Constants.ServiceChangedType&} - * 1.ADD_SERVICE: a new config - * 2.DELETE_SERVICE: a service should be removed for clientside . + * changed type. */ private String changedType; @@ -118,7 +137,7 @@ public Context() { /** * Builds a new context object with the provided parameters. * - * @param serviceKey The groupKey associated of the configuration. + * @param serviceKey The groupKey associated of the configuration. * @param changedType The type of the configuration change event. * @return A new context object initialized with the provided parameters. */ @@ -128,15 +147,15 @@ public static NamingFuzzyWatchSyncRequest.Context build(String serviceKey, Strin context.setChangedType(changedType); return context; } - + public String getServiceKey() { return serviceKey; } - + public void setServiceKey(String serviceKey) { this.serviceKey = serviceKey; } - + public String getChangedType() { return changedType; } diff --git a/api/src/main/java/com/alibaba/nacos/api/naming/remote/response/NamingFuzzyWatchResponse.java b/api/src/main/java/com/alibaba/nacos/api/naming/remote/response/NamingFuzzyWatchResponse.java index 1442b3d8bcd..a18f81b66ec 100644 --- a/api/src/main/java/com/alibaba/nacos/api/naming/remote/response/NamingFuzzyWatchResponse.java +++ b/api/src/main/java/com/alibaba/nacos/api/naming/remote/response/NamingFuzzyWatchResponse.java @@ -26,10 +26,9 @@ */ public class NamingFuzzyWatchResponse extends Response { - public NamingFuzzyWatchResponse(){ + public NamingFuzzyWatchResponse() { } - public static NamingFuzzyWatchResponse buildSuccessResponse() { return new NamingFuzzyWatchResponse(); } @@ -45,5 +44,5 @@ public static NamingFuzzyWatchResponse buildFailResponse(String message) { result.setErrorInfo(ResponseCode.FAIL.getCode(), message); return result; } - + } diff --git a/api/src/main/java/com/alibaba/nacos/api/naming/remote/response/NamingFuzzyWatchSyncResponse.java b/api/src/main/java/com/alibaba/nacos/api/naming/remote/response/NamingFuzzyWatchSyncResponse.java index dc81384b21d..90180a3b74d 100644 --- a/api/src/main/java/com/alibaba/nacos/api/naming/remote/response/NamingFuzzyWatchSyncResponse.java +++ b/api/src/main/java/com/alibaba/nacos/api/naming/remote/response/NamingFuzzyWatchSyncResponse.java @@ -26,10 +26,9 @@ */ public class NamingFuzzyWatchSyncResponse extends Response { - public NamingFuzzyWatchSyncResponse(){ + public NamingFuzzyWatchSyncResponse() { } - public static NamingFuzzyWatchSyncResponse buildSuccessResponse() { return new NamingFuzzyWatchSyncResponse(); } @@ -45,5 +44,5 @@ public static NamingFuzzyWatchSyncResponse buildFailResponse(String message) { result.setErrorInfo(ResponseCode.FAIL.getCode(), message); return result; } - + } diff --git a/api/src/main/java/com/alibaba/nacos/api/naming/utils/NamingUtils.java b/api/src/main/java/com/alibaba/nacos/api/naming/utils/NamingUtils.java index 26008ec0e9e..a74e94fb6e5 100644 --- a/api/src/main/java/com/alibaba/nacos/api/naming/utils/NamingUtils.java +++ b/api/src/main/java/com/alibaba/nacos/api/naming/utils/NamingUtils.java @@ -68,23 +68,21 @@ public static String getGroupedName(final String serviceName, final String group return resultGroupedName.intern(); } - public static String getServiceKey(String namespace,String group,String serviceName){ - if (StringUtils.isBlank(namespace)){ - namespace=DEFAULT_NAMESPACE_ID; + public static String getServiceKey(String namespace, String group, String serviceName) { + if (StringUtils.isBlank(namespace)) { + namespace = DEFAULT_NAMESPACE_ID; } - return namespace + Constants.SERVICE_INFO_SPLITER +group+ Constants.SERVICE_INFO_SPLITER +serviceName; + return namespace + Constants.SERVICE_INFO_SPLITER + group + Constants.SERVICE_INFO_SPLITER + serviceName; } /** - * parse service key items for servicekey. - * item[0] for namespace - * item[1] for group - * item[2] for service name - * @param serviceKey + * parse service key items for serviceKey. item[0] for namespace item[1] for group item[2] for service name + * + * @param serviceKey serviceKey. * @return */ - public static String[] parseServiceKey(String serviceKey){ - return serviceKey.split(Constants.SERVICE_INFO_SPLITER); + public static String[] parseServiceKey(String serviceKey) { + return serviceKey.split(Constants.SERVICE_INFO_SPLITER); } public static String getServiceName(final String serviceNameWithGroup) { @@ -177,7 +175,8 @@ public static void checkInstanceIsLegal(Instance instance) throws NacosException throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.INSTANCE_ERROR, "Instance 'heart beat interval' must less than 'heart beat timeout' and 'ip delete timeout'."); } - if (!StringUtils.isEmpty(instance.getClusterName()) && !CLUSTER_NAME_PATTERN.matcher(instance.getClusterName()).matches()) { + if (!StringUtils.isEmpty(instance.getClusterName()) && !CLUSTER_NAME_PATTERN.matcher(instance.getClusterName()) + .matches()) { throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.INSTANCE_ERROR, String.format("Instance 'clusterName' should be characters with only 0-9a-zA-Z-. (current: %s)", instance.getClusterName())); @@ -186,18 +185,21 @@ public static void checkInstanceIsLegal(Instance instance) throws NacosException /** * check batch register is Ephemeral. + * * @param instance instance * @throws NacosException NacosException */ public static void checkInstanceIsEphemeral(Instance instance) throws NacosException { if (!instance.isEphemeral()) { throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.INSTANCE_ERROR, - String.format("Batch registration does not allow persistent instance registration , Instance:%s", instance)); + String.format("Batch registration does not allow persistent instance registration , Instance:%s", + instance)); } } /** * Batch verify the validity of instances. + * * @param instances List of instances to be registered * @throws NacosException Nacos */ diff --git a/client/src/main/java/com/alibaba/nacos/client/config/NacosConfigService.java b/client/src/main/java/com/alibaba/nacos/client/config/NacosConfigService.java index e0b3ca4d8f6..23997b1e613 100644 --- a/client/src/main/java/com/alibaba/nacos/client/config/NacosConfigService.java +++ b/client/src/main/java/com/alibaba/nacos/client/config/NacosConfigService.java @@ -21,13 +21,12 @@ import com.alibaba.nacos.api.config.ConfigService; import com.alibaba.nacos.api.config.ConfigType; import com.alibaba.nacos.api.config.filter.IConfigFilter; -import com.alibaba.nacos.api.config.listener.ConfigFuzzyWatcher; +import com.alibaba.nacos.api.config.listener.FuzzyWatchEventWatcher; import com.alibaba.nacos.api.config.listener.Listener; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.client.config.filter.impl.ConfigFilterChainManager; import com.alibaba.nacos.client.config.filter.impl.ConfigRequest; import com.alibaba.nacos.client.config.filter.impl.ConfigResponse; -import com.alibaba.nacos.client.config.http.ServerHttpAgent; import com.alibaba.nacos.client.config.impl.ClientWorker; import com.alibaba.nacos.client.config.impl.ConfigFuzzyWatchContext; import com.alibaba.nacos.client.config.impl.ConfigServerListManager; @@ -45,7 +44,6 @@ import java.util.Collections; import java.util.Properties; import java.util.Set; -import java.util.concurrent.CompletableFuture; import java.util.concurrent.Future; import static com.alibaba.nacos.api.common.Constants.ANY_PATTERN; @@ -125,48 +123,48 @@ public void addListener(String dataId, String group, Listener listener) throws N } @Override - public void fuzzyWatch(String fixedGroupName, ConfigFuzzyWatcher watcher) throws NacosException { + public void fuzzyWatch(String fixedGroupName, FuzzyWatchEventWatcher watcher) throws NacosException { doAddFuzzyWatch(ANY_PATTERN, fixedGroupName, watcher); } @Override - public void fuzzyWatch(String dataIdPattern, String fixedGroupName, ConfigFuzzyWatcher watcher) + public void fuzzyWatch(String dataIdPattern, String fixedGroupName, FuzzyWatchEventWatcher watcher) throws NacosException { doAddFuzzyWatch(dataIdPattern, fixedGroupName, watcher); } @Override - public Future> fuzzyWatchWithGroupKeys(String fixedGroupName, - ConfigFuzzyWatcher watcher) throws NacosException { + public Future> fuzzyWatchWithGroupKeys(String fixedGroupName, FuzzyWatchEventWatcher watcher) + throws NacosException { return doAddFuzzyWatch(ANY_PATTERN, fixedGroupName, watcher); } @Override - public Future> fuzzyWatchWithGroupKeys(String dataIdPattern, - String fixedGroupName, ConfigFuzzyWatcher watcher) throws NacosException { + public Future> fuzzyWatchWithGroupKeys(String dataIdPattern, String fixedGroupName, + FuzzyWatchEventWatcher watcher) throws NacosException { return doAddFuzzyWatch(dataIdPattern, fixedGroupName, watcher); } - - private Future> doAddFuzzyWatch(String dataIdPattern, - String fixedGroupName, ConfigFuzzyWatcher watcher) throws NacosException { - ConfigFuzzyWatchContext configFuzzyWatchContext = worker.addTenantFuzzyWatcher(dataIdPattern, fixedGroupName,watcher); + private Future> doAddFuzzyWatch(String dataIdPattern, String fixedGroupName, + FuzzyWatchEventWatcher watcher) throws NacosException { + ConfigFuzzyWatchContext configFuzzyWatchContext = worker.addTenantFuzzyWatcher(dataIdPattern, fixedGroupName, + watcher); return configFuzzyWatchContext.createNewFuture(); } @Override - public void cancelFuzzyWatch(String fixedGroupName, ConfigFuzzyWatcher watcher) throws NacosException { + public void cancelFuzzyWatch(String fixedGroupName, FuzzyWatchEventWatcher watcher) throws NacosException { cancelFuzzyWatch(ANY_PATTERN, fixedGroupName, watcher); } @Override - public void cancelFuzzyWatch(String dataIdPattern, String fixedGroupName, ConfigFuzzyWatcher watcher) + public void cancelFuzzyWatch(String dataIdPattern, String fixedGroupName, FuzzyWatchEventWatcher watcher) throws NacosException { doCancelFuzzyListen(dataIdPattern, fixedGroupName, watcher); } - private void doCancelFuzzyListen(String dataIdPattern, String groupNamePattern, - ConfigFuzzyWatcher watcher) throws NacosException { + private void doCancelFuzzyListen(String dataIdPattern, String groupNamePattern, FuzzyWatchEventWatcher watcher) + throws NacosException { if (null == watcher) { return; } @@ -221,11 +219,11 @@ private String getConfigInner(String tenant, String dataId, String group, long t // changing config needed in the same time, while nacos server is down. String content = LocalConfigInfoProcessor.getFailover(worker.getAgentName(), dataId, group, tenant); if (content != null) { - LOGGER.warn("[{}] [get-config] get failover ok, dataId={}, group={}, tenant={}", - worker.getAgentName(), dataId, group, tenant); + LOGGER.warn("[{}] [get-config] get failover ok, dataId={}, group={}, tenant={}", worker.getAgentName(), + dataId, group, tenant); cr.setContent(content); - String encryptedDataKey = LocalEncryptedDataKeyProcessor - .getEncryptDataKeyFailover(worker.getAgentName(), dataId, group, tenant); + String encryptedDataKey = LocalEncryptedDataKeyProcessor.getEncryptDataKeyFailover(worker.getAgentName(), + dataId, group, tenant); cr.setEncryptedDataKey(encryptedDataKey); configFilterChainManager.doFilter(null, cr); content = cr.getContent(); @@ -247,15 +245,15 @@ private String getConfigInner(String tenant, String dataId, String group, long t LOGGER.warn("[{}] [get-config] get from server error, dataId={}, group={}, tenant={}, msg={}", worker.getAgentName(), dataId, group, tenant, ioe.toString()); } - + content = LocalConfigInfoProcessor.getSnapshot(worker.getAgentName(), dataId, group, tenant); if (content != null) { - LOGGER.warn("[{}] [get-config] get snapshot ok, dataId={}, group={}, tenant={}", - worker.getAgentName(), dataId, group, tenant); + LOGGER.warn("[{}] [get-config] get snapshot ok, dataId={}, group={}, tenant={}", worker.getAgentName(), + dataId, group, tenant); } cr.setContent(content); - String encryptedDataKey = LocalEncryptedDataKeyProcessor - .getEncryptDataKeySnapshot(worker.getAgentName(), dataId, group, tenant); + String encryptedDataKey = LocalEncryptedDataKeyProcessor.getEncryptDataKeySnapshot(worker.getAgentName(), + dataId, group, tenant); cr.setEncryptedDataKey(encryptedDataKey); configFilterChainManager.doFilter(null, cr); content = cr.getContent(); @@ -287,8 +285,8 @@ private boolean publishConfigInner(String tenant, String dataId, String group, S content = cr.getContent(); String encryptedDataKey = cr.getEncryptedDataKey(); - return worker - .publishConfig(dataId, group, tenant, appName, tag, betaIps, content, encryptedDataKey, casMd5, type); + return worker.publishConfig(dataId, group, tenant, appName, tag, betaIps, content, encryptedDataKey, casMd5, + type); } @Override @@ -299,12 +297,12 @@ public String getServerStatus() { return DOWN; } } - + @Override public void addConfigFilter(IConfigFilter configFilter) { configFilterChainManager.addFilter(configFilter); } - + @Override public void shutDown() throws NacosException { worker.shutdown(); 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 d8e1509c453..f6b9a934afd 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 @@ -23,24 +23,30 @@ import com.alibaba.nacos.common.remote.client.Connection; import com.alibaba.nacos.common.remote.client.ServerRequestHandler; +/** + * fuzzy watch request from server . + * @author shiyiyue + */ public class ClientFuzzyWatchNotifyRequestHandler implements ServerRequestHandler { ConfigFuzzyWatchGroupKeyHolder configFuzzyWatchGroupKeyHolder; - public ClientFuzzyWatchNotifyRequestHandler(ConfigFuzzyWatchGroupKeyHolder configFuzzyWatchGroupKeyHolder){ + public ClientFuzzyWatchNotifyRequestHandler(ConfigFuzzyWatchGroupKeyHolder configFuzzyWatchGroupKeyHolder) { this.configFuzzyWatchGroupKeyHolder = configFuzzyWatchGroupKeyHolder; } + @Override public Response requestReply(Request request, Connection connection) { //fuzzy watch diff reconciliation sync if (request instanceof ConfigFuzzyWatchSyncRequest) { - return configFuzzyWatchGroupKeyHolder.handleFuzzyWatchNotifyDiffRequest((ConfigFuzzyWatchSyncRequest) request - ); + return configFuzzyWatchGroupKeyHolder.handleFuzzyWatchNotifyDiffRequest( + (ConfigFuzzyWatchSyncRequest) request); } //fuzzy watch changed notify for a single config. include config changed or config delete. if (request instanceof ConfigFuzzyWatchChangeNotifyRequest) { - return configFuzzyWatchGroupKeyHolder.handlerFuzzyWatchChangeNotifyRequest((ConfigFuzzyWatchChangeNotifyRequest) request); + return configFuzzyWatchGroupKeyHolder.handlerFuzzyWatchChangeNotifyRequest( + (ConfigFuzzyWatchChangeNotifyRequest) request); } return null; } 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 1ce9fc6bd0f..fe04c17623e 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 @@ -19,7 +19,7 @@ import com.alibaba.nacos.api.PropertyKeyConst; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.config.ConfigType; -import com.alibaba.nacos.api.config.listener.ConfigFuzzyWatcher; +import com.alibaba.nacos.api.config.listener.FuzzyWatchEventWatcher; import com.alibaba.nacos.api.config.listener.Listener; import com.alibaba.nacos.api.config.remote.request.ClientConfigMetricRequest; import com.alibaba.nacos.api.config.remote.request.ConfigBatchListenRequest; @@ -292,14 +292,14 @@ public void removeTenantListener(String dataId, String group, Listener listener) /** * Adds a list of fuzzy listen listeners for the specified data ID pattern and group. * - * @param dataIdPattern The pattern of the data ID to listen for. - * @param groupPattern The group of the configuration. - * @param configFuzzyWatcher The configFuzzyWatcher to add. + * @param dataIdPattern The pattern of the data ID to listen for. + * @param groupPattern The group of the configuration. + * @param fuzzyWatchEventWatcher The configFuzzyWatcher to add. * @throws NacosException If an error occurs while adding the listeners. */ public ConfigFuzzyWatchContext addTenantFuzzyWatcher(String dataIdPattern, String groupPattern, - ConfigFuzzyWatcher configFuzzyWatcher){ - return configFuzzyWatchGroupKeyHolder.registerFuzzyWatcher(dataIdPattern,groupPattern,configFuzzyWatcher); + FuzzyWatchEventWatcher fuzzyWatchEventWatcher) { + return configFuzzyWatchGroupKeyHolder.registerFuzzyWatcher(dataIdPattern, groupPattern, fuzzyWatchEventWatcher); } /** @@ -307,11 +307,11 @@ public ConfigFuzzyWatchContext addTenantFuzzyWatcher(String dataIdPattern, Strin * * @param dataIdPattern The pattern of the data ID. * @param group The group of the configuration. - * @param watcher The listener to remove. + * @param watcher The listener to remove. * @throws NacosException If an error occurs while removing the listener. */ - public void removeFuzzyListenListener(String dataIdPattern, String group, ConfigFuzzyWatcher watcher) { - configFuzzyWatchGroupKeyHolder.removeFuzzyWatcher(dataIdPattern,group,watcher); + public void removeFuzzyListenListener(String dataIdPattern, String group, FuzzyWatchEventWatcher watcher) { + configFuzzyWatchGroupKeyHolder.removeFuzzyWatcher(dataIdPattern, group, watcher); } void removeCache(String dataId, String group, String tenant) { @@ -510,15 +510,15 @@ private String blank2defaultGroup(String group) { } @SuppressWarnings("PMD.ThreadPoolCreationRule") - public ClientWorker(final ConfigFilterChainManager configFilterChainManager, ConfigServerListManager serverListManager, - final NacosClientProperties properties) throws NacosException { + public ClientWorker(final ConfigFilterChainManager configFilterChainManager, + ConfigServerListManager serverListManager, final NacosClientProperties properties) throws NacosException { this.configFilterChainManager = configFilterChainManager; init(properties); agent = new ConfigRpcTransportClient(properties, serverListManager); - - configFuzzyWatchGroupKeyHolder =new ConfigFuzzyWatchGroupKeyHolder(agent,uuid); + + configFuzzyWatchGroupKeyHolder = new ConfigFuzzyWatchGroupKeyHolder(agent, uuid); ScheduledExecutorService executorService = Executors.newScheduledThreadPool(initWorkerThreadCount(properties), new NameThreadFactory("com.alibaba.nacos.client.Worker")); agent.setExecutor(executorService); @@ -742,8 +742,8 @@ private void initRpcClientHandler(final RpcClient rpcClientInner) { return null; }); - rpcClientInner.registerServerRequestHandler(new ClientFuzzyWatchNotifyRequestHandler( - configFuzzyWatchGroupKeyHolder)); + rpcClientInner.registerServerRequestHandler( + new ClientFuzzyWatchNotifyRequestHandler(configFuzzyWatchGroupKeyHolder)); rpcClientInner.registerConnectionListener(new ConnectionEventListener() { @@ -771,7 +771,7 @@ public void onDisConnect(Connection connection) { cacheData.setConsistentWithServer(false); } } - + LOGGER.info("[{}] DisConnected,reset fuzzy watch consistence status", rpcClientInner.getName()); configFuzzyWatchGroupKeyHolder.resetConsistenceStatus(); } @@ -1143,7 +1143,7 @@ RpcClient ensureRpcClient(String taskId) throws NacosException { rpcClient.setTenant(getTenant()); rpcClient.start(); } - + return rpcClient; } @@ -1320,8 +1320,8 @@ public boolean publishConfig(String dataId, String group, String tenant, String this.getName(), dataId, group, tenant, response.getErrorCode(), response.getMessage()); return false; } else { - LOGGER.info("[{}] [publish-single] ok, dataId={}, group={}, tenant={}", getName(), - dataId, group, tenant); + LOGGER.info("[{}] [publish-single] ok, dataId={}, group={}, tenant={}", getName(), dataId, group, + tenant); return true; } } catch (Exception e) { 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 268e7786ca6..e9a14ee1cf4 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 @@ -17,10 +17,8 @@ package com.alibaba.nacos.client.config.impl; import com.alibaba.nacos.api.config.listener.ConfigFuzzyWatchChangeEvent; -import com.alibaba.nacos.api.config.listener.ConfigFuzzyWatcher; -import com.alibaba.nacos.api.naming.pojo.ListView; +import com.alibaba.nacos.api.config.listener.FuzzyWatchEventWatcher; import com.alibaba.nacos.client.config.common.GroupKey; -import com.alibaba.nacos.client.naming.cache.NamingFuzzyWatchContext; import com.alibaba.nacos.client.utils.LogUtils; import com.alibaba.nacos.common.utils.ConcurrentHashSet; import com.alibaba.nacos.common.utils.FuzzyGroupKeyPattern; @@ -32,7 +30,6 @@ import java.util.Iterator; import java.util.List; import java.util.Set; -import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; @@ -44,7 +41,6 @@ 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; - /** * fuzzy wather context for a single group key pattern. * @@ -134,73 +130,73 @@ public Set calculateListenersToNotify(String uuid) { * Notify the listener with the specified data ID, type, and UUID. * * @param groupKey groupKey - * @param uuid UUID to filter listeners + * @param uuid UUID to filter listeners */ - public void notifyWatcher(final String groupKey, final String changedType,final String syncType, + public void notifyWatcher(final String groupKey, final String changedType, final String syncType, final String uuid) { Set listenersToNotify = calculateListenersToNotify(uuid); - doNotifyWatchers(groupKey, changedType,syncType, listenersToNotify); + doNotifyWatchers(groupKey, changedType, syncType, listenersToNotify); } /** * Perform the notification for the specified data ID, type, and listeners. * - * @param groupKey groupKey + * @param groupKey groupKey * @param listenersToNotify Set of listeners to notify */ - private void doNotifyWatchers(final String groupKey, final String changedType,final String syncType, + private void doNotifyWatchers(final String groupKey, final String changedType, final String syncType, Set listenersToNotify) { for (ConfigFuzzyWatcherWrapper watcher : listenersToNotify) { - doNotifyWatcher(groupKey,changedType,syncType,watcher); + doNotifyWatcher(groupKey, changedType, syncType, watcher); } } - private void doNotifyWatcher(final String groupKey, final String changedType,final String syncType, - ConfigFuzzyWatcherWrapper configFuzzyWatcher){ - - if (ADD_CONFIG.equals(changedType)&&configFuzzyWatcher.getSyncGroupKeys().contains(groupKey)){ + private void doNotifyWatcher(final String groupKey, final String changedType, final String syncType, + ConfigFuzzyWatcherWrapper configFuzzyWatcher) { + + if (ADD_CONFIG.equals(changedType) && configFuzzyWatcher.getSyncGroupKeys().contains(groupKey)) { return; } - - if (DELETE_CONFIG.equals(changedType)&&!configFuzzyWatcher.getSyncGroupKeys().contains(groupKey)){ + + if (DELETE_CONFIG.equals(changedType) && !configFuzzyWatcher.getSyncGroupKeys().contains(groupKey)) { return; } - + String[] parseKey = GroupKey.parseKey(groupKey); - String dataId=parseKey[0]; - String group=parseKey[1]; - - String tenant=parseKey[2]; - - final String resetSyncType=initializationCompleted.get()?syncType:FUZZY_WATCH_INIT_NOTIFY; + String dataId = parseKey[0]; + String group = parseKey[1]; + + String tenant = parseKey[2]; + + final String resetSyncType = initializationCompleted.get() ? syncType : FUZZY_WATCH_INIT_NOTIFY; AbstractFuzzyNotifyTask job = new AbstractFuzzyNotifyTask() { @Override public void run() { long start = System.currentTimeMillis(); - ConfigFuzzyWatchChangeEvent event = ConfigFuzzyWatchChangeEvent.build(tenant,group, dataId, changedType,resetSyncType); + ConfigFuzzyWatchChangeEvent event = ConfigFuzzyWatchChangeEvent.build(tenant, group, dataId, + changedType, resetSyncType); if (configFuzzyWatcher != null) { - configFuzzyWatcher.configFuzzyWatcher.onEvent(event); + configFuzzyWatcher.fuzzyWatchEventWatcher.onEvent(event); } LOGGER.info( "[{}] [notify-fuzzy-watcher-ok] dataId={}, group={}, tenant={}, watcher={}, job run cost={} millis.", envName, dataId, group, tenant, configFuzzyWatcher, (System.currentTimeMillis() - start)); if (changedType.equals(DELETE_CONFIG)) { configFuzzyWatcher.getSyncGroupKeys().remove(GroupKey.getKey(dataId, group, tenant)); - } else if (changedType.equals( - ADD_CONFIG)) { + } else if (changedType.equals(ADD_CONFIG)) { configFuzzyWatcher.getSyncGroupKeys().add(GroupKey.getKey(dataId, group, tenant)); - + } } }; - + try { - if (null != configFuzzyWatcher.configFuzzyWatcher.getExecutor()) { + if (null != configFuzzyWatcher.fuzzyWatchEventWatcher.getExecutor()) { LOGGER.info( "[{}] [notify-fuzzy-watcher] task submitted to user executor, dataId={}, group={}, tenant={}, listener={}.", envName, dataId, group, tenant, configFuzzyWatcher); job.async = true; - configFuzzyWatcher.configFuzzyWatcher.getExecutor().execute(job); + configFuzzyWatcher.fuzzyWatchEventWatcher.getExecutor().execute(job); } else { LOGGER.info( "[{}] [notify-fuzzy-watcher] task execute in nacos thread, dataId={}, group={}, tenant={}, listener={}.", @@ -212,12 +208,13 @@ public void run() { envName, dataId, group, tenant, configFuzzyWatcher, t.getCause()); } } + /** * Mark initialization as complete and notify waiting threads. */ public void markInitializationComplete() { initializationCompleted.set(true); - synchronized (this){ + synchronized (this) { this.notifyAll(); } } @@ -227,12 +224,12 @@ public void markInitializationComplete() { * * @param watcher watcher to be removed */ - public void removeWatcher(ConfigFuzzyWatcher watcher) { - + public void removeWatcher(FuzzyWatchEventWatcher watcher) { + Iterator iterator = configFuzzyWatcherWrappers.iterator(); - while (iterator.hasNext()){ + while (iterator.hasNext()) { ConfigFuzzyWatcherWrapper next = iterator.next(); - if (next.configFuzzyWatcher.equals(watcher)){ + if (next.fuzzyWatchEventWatcher.equals(watcher)) { iterator.remove(); LOGGER.info("[{}] [remove-fuzzy-watcher-ok] groupKeyPattern={}, watcher={},uuid={} ", getEnvName(), this.groupKeyPattern, watcher, next.getUuid()); @@ -247,10 +244,11 @@ public void removeWatcher(ConfigFuzzyWatcher watcher) { * @param configFuzzyWatcherWrapper watcher to be added */ public boolean addWatcher(ConfigFuzzyWatcherWrapper configFuzzyWatcherWrapper) { - boolean added= configFuzzyWatcherWrappers.add(configFuzzyWatcherWrapper); - if(added){ - LOGGER.info("[{}] [add-fuzzy-watcher-ok] groupKeyPattern={}, watcher={},uuid={} ", getEnvName(), this.groupKeyPattern, - configFuzzyWatcherWrapper.configFuzzyWatcher, configFuzzyWatcherWrapper.getUuid()); + boolean added = configFuzzyWatcherWrappers.add(configFuzzyWatcherWrapper); + if (added) { + LOGGER.info("[{}] [add-fuzzy-watcher-ok] groupKeyPattern={}, watcher={},uuid={} ", getEnvName(), + this.groupKeyPattern, configFuzzyWatcherWrapper.fuzzyWatchEventWatcher, + configFuzzyWatcherWrapper.getUuid()); } return added; } @@ -336,21 +334,19 @@ public boolean isInitializing() { } /** - * Get the set of data IDs associated with the context. - *zw + * Get the set of data IDs associated with the context. zw + * * @return Set of data IDs */ public Set getReceivedGroupKeys() { return Collections.unmodifiableSet(receivedGroupKeys); } - - public boolean addReceivedGroupKey(String groupKey){ + public boolean addReceivedGroupKey(String groupKey) { return receivedGroupKeys.add(groupKey); } - - public boolean removeReceivedGroupKey(String groupKey){ + public boolean removeReceivedGroupKey(String groupKey) { return receivedGroupKeys.remove(groupKey); } @@ -363,7 +359,6 @@ public Set getConfigFuzzyWatcherWrappers() { return configFuzzyWatcherWrappers; } - /** * Abstract task for fuzzy notification. */ @@ -384,33 +379,37 @@ public boolean isAsync() { } } - void syncFuzzyWatchers(){ - for(ConfigFuzzyWatcherWrapper configFuzzyWatcher: configFuzzyWatcherWrappers){ + void syncFuzzyWatchers() { + for (ConfigFuzzyWatcherWrapper configFuzzyWatcher : configFuzzyWatcherWrappers) { Set receivedGroupKeysContext = receivedGroupKeys; 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); + for (FuzzyGroupKeyPattern.GroupKeyState groupKeyState : groupKeyStates) { + String changedType = groupKeyState.isExist() ? ADD_CONFIG : DELETE_CONFIG; + doNotifyWatcher(groupKeyState.getGroupKey(), changedType, FUZZY_WATCH_DIFF_SYNC_NOTIFY, + configFuzzyWatcher); } } } - - public Future> createNewFuture(){ - Future> completableFuture=new Future>(){ + /** + * creat a new future of this context. + * @return + */ + public Future> createNewFuture() { + Future> completableFuture = new Future>() { @Override public boolean cancel(boolean mayInterruptIfRunning) { throw new UnsupportedOperationException("not support to cancel fuzzy watch"); } - + @Override public boolean isCancelled() { return false; } - + @Override public boolean isDone() { return ConfigFuzzyWatchContext.this.initializationCompleted.get(); @@ -418,30 +417,30 @@ public boolean isDone() { @Override public Set get() throws InterruptedException, ExecutionException { - - if(!ConfigFuzzyWatchContext.this.initializationCompleted.get()){ + + if (!ConfigFuzzyWatchContext.this.initializationCompleted.get()) { synchronized (ConfigFuzzyWatchContext.this) { ConfigFuzzyWatchContext.this.wait(); } } - return new HashSet<>(ConfigFuzzyWatchContext.this.getReceivedGroupKeys()); } - public Set get(long timeout, TimeUnit unit) throws InterruptedException, TimeoutException { - if(!ConfigFuzzyWatchContext.this.initializationCompleted.get()){ + public Set get(long timeout, TimeUnit unit) throws InterruptedException, TimeoutException { + if (!ConfigFuzzyWatchContext.this.initializationCompleted.get()) { synchronized (ConfigFuzzyWatchContext.this) { ConfigFuzzyWatchContext.this.wait(unit.toMillis(timeout)); } } - - if (!ConfigFuzzyWatchContext.this.initializationCompleted.get()){ - throw new TimeoutException("fuzzy watch result future timeout for "+unit.toMillis(timeout)+" millis"); + + if (!ConfigFuzzyWatchContext.this.initializationCompleted.get()) { + throw new TimeoutException( + "fuzzy watch result future timeout for " + unit.toMillis(timeout) + " millis"); } return new HashSet<>(ConfigFuzzyWatchContext.this.getReceivedGroupKeys()); } }; - + return completableFuture; } } 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 a922be8e5d3..f64a8ccbff0 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 @@ -17,7 +17,7 @@ package com.alibaba.nacos.client.config.impl; import com.alibaba.nacos.api.common.Constants; -import com.alibaba.nacos.api.config.listener.ConfigFuzzyWatcher; +import com.alibaba.nacos.api.config.listener.FuzzyWatchEventWatcher; 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; @@ -57,6 +57,10 @@ import static com.alibaba.nacos.api.common.Constants.WATCH_TYPE_CANCEL_WATCH; import static com.alibaba.nacos.api.common.Constants.WATCH_TYPE_WATCH; +/** + * config fuzzy watch context holder. + * @author shiyiyue + */ public class ConfigFuzzyWatchGroupKeyHolder { private static final Logger LOGGER = LogUtils.logger(ClientWorker.class); @@ -64,6 +68,7 @@ public class ConfigFuzzyWatchGroupKeyHolder { private final ClientWorker.ConfigRpcTransportClient agent; private final String clientUuid; + /** * fuzzyListenExecuteBell. */ @@ -71,28 +76,28 @@ public class ConfigFuzzyWatchGroupKeyHolder { private final Object bellItem = new Object(); - private final AtomicLong fuzzyListenLastAllSyncTime=new AtomicLong(System.currentTimeMillis()); + private final AtomicLong fuzzyListenLastAllSyncTime = new AtomicLong(System.currentTimeMillis()); - private final long FUZZY_LISTEN_ALL_SYNC_INTERNAL=3*60*1000; + private static final long FUZZY_LISTEN_ALL_SYNC_INTERNAL = 3 * 60 * 1000; + + private String taskId = "0"; - private String taskId="0"; /** * fuzzyListenGroupKey -> fuzzyListenContext. */ private final AtomicReference> fuzzyListenContextMap = new AtomicReference<>( new HashMap<>()); - - public ConfigFuzzyWatchGroupKeyHolder(ClientWorker.ConfigRpcTransportClient agent,String clientUuid){ - this.clientUuid=clientUuid; - this.agent=agent; + public ConfigFuzzyWatchGroupKeyHolder(ClientWorker.ConfigRpcTransportClient agent, String clientUuid) { + this.clientUuid = clientUuid; + this.agent = agent; NotifyCenter.registerSubscriber(new Subscriber() { @Override public void onEvent(Event event) { ConfigFuzzyWatchNotifyEvent configFuzzyWatchNotifyEvent = (ConfigFuzzyWatchNotifyEvent) event; //instance check - if (!configFuzzyWatchNotifyEvent.getClientUuid().equals(clientUuid)){ + if (!configFuzzyWatchNotifyEvent.getClientUuid().equals(clientUuid)) { return; } @@ -102,21 +107,22 @@ public void onEvent(Event event) { return; } - context.notifyWatcher(configFuzzyWatchNotifyEvent.getGroupKey(), configFuzzyWatchNotifyEvent.getChangedType(), - configFuzzyWatchNotifyEvent.getSyncType(), + context.notifyWatcher(configFuzzyWatchNotifyEvent.getGroupKey(), + configFuzzyWatchNotifyEvent.getChangedType(), configFuzzyWatchNotifyEvent.getSyncType(), configFuzzyWatchNotifyEvent.getWatcherUuid()); } - + @Override public Class subscribeType() { return ConfigFuzzyWatchNotifyEvent.class; } }); - - } - public void start(){ + /** + * start. + */ + public void start() { agent.executor.schedule(() -> { while (!agent.executor.isShutdown() && !agent.executor.isTerminated()) { try { @@ -152,85 +158,87 @@ public void removeFuzzyListenContext(String groupKeyPattern) { LOGGER.info("[{}] [fuzzy-watch-unsubscribe] {}", agent.getName(), groupKeyPattern); } + /** + * register fuzzy watcher. + * + * @param dataIdPattern dataIdPattern. + * @param groupPattern groupPattern. + * @param fuzzyWatchEventWatcher fuzzyWatchEventWatcher. + * @return + */ public ConfigFuzzyWatchContext registerFuzzyWatcher(String dataIdPattern, String groupPattern, - ConfigFuzzyWatcher configFuzzyWatcher){ + FuzzyWatchEventWatcher fuzzyWatchEventWatcher) { ConfigFuzzyWatchContext configFuzzyWatchContext = initFuzzyWatchContextIfAbsent(dataIdPattern, groupPattern); - ConfigFuzzyWatcherWrapper configFuzzyWatcherWrapper=new ConfigFuzzyWatcherWrapper(configFuzzyWatcher); - if(configFuzzyWatchContext.addWatcher(configFuzzyWatcherWrapper)){ - if(configFuzzyWatchContext.getReceivedGroupKeys()!=null){ - for(String groupKey:configFuzzyWatchContext.getReceivedGroupKeys()){ - ConfigFuzzyWatchNotifyEvent configFuzzyWatchNotifyEvent = ConfigFuzzyWatchNotifyEvent.buildNotifyPatternAllListenersEvent(groupKey, - configFuzzyWatchContext.getGroupKeyPattern(), ADD_CONFIG,FUZZY_WATCH_INIT_NOTIFY,configFuzzyWatcherWrapper.getUuid()); + ConfigFuzzyWatcherWrapper configFuzzyWatcherWrapper = new ConfigFuzzyWatcherWrapper(fuzzyWatchEventWatcher); + if (configFuzzyWatchContext.addWatcher(configFuzzyWatcherWrapper)) { + if (configFuzzyWatchContext.getReceivedGroupKeys() != null) { + for (String groupKey : configFuzzyWatchContext.getReceivedGroupKeys()) { + ConfigFuzzyWatchNotifyEvent configFuzzyWatchNotifyEvent = ConfigFuzzyWatchNotifyEvent.buildNotifyPatternAllListenersEvent( + groupKey, configFuzzyWatchContext.getGroupKeyPattern(), ADD_CONFIG, FUZZY_WATCH_INIT_NOTIFY, + configFuzzyWatcherWrapper.getUuid()); NotifyCenter.publishEvent(configFuzzyWatchNotifyEvent); } - } - } return configFuzzyWatchContext; } - - /** - * Checks if the pattern match cache contains an entry for the specified data ID pattern and group. - * - * @param dataIdPattern The data ID pattern. - * @param groupPattern The group name. - * @return True if the cache contains an entry, false otherwise. - */ - public boolean containsPatternMatchCache(String dataIdPattern, String groupPattern) { - Map contextMap = fuzzyListenContextMap.get(); - String groupKeyPattern = FuzzyGroupKeyPattern.generatePattern(dataIdPattern, groupPattern,agent.getTenant()); - return contextMap.containsKey(groupKeyPattern); - } - - + /** * Retrieves the FuzzyListenContext for the given data ID pattern and group. * * @param dataIdPattern The data ID pattern. - * @param groupPattern The group name pattern. + * @param groupPattern The group name pattern. * @return The corresponding FuzzyListenContext, or null if not found. */ public ConfigFuzzyWatchContext getFuzzyListenContext(String dataIdPattern, String groupPattern) { return fuzzyListenContextMap.get() - .get(FuzzyGroupKeyPattern.generatePattern(dataIdPattern, groupPattern,agent.getTenant())); + .get(FuzzyGroupKeyPattern.generatePattern(dataIdPattern, groupPattern, agent.getTenant())); } - /** * Handles a fuzzy listen init notify request. * *

This method processes the incoming fuzzy listen init notify request from a client. It updates the fuzzy * listen context based on the request's information, and publishes events if necessary. * - * @param request The fuzzy listen init notify request to handle. + * @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) { String groupKeyPattern = request.getGroupKeyPattern(); 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()); + LOGGER.info("[{}] [fuzzy-watch] init-notify-finished, pattern ->{}, match group keys count {}", + agent.getName(), request.getGroupKeyPattern(), context.getReceivedGroupKeys().size()); context.markInitializationComplete(); return new ConfigFuzzyWatchSyncResponse(); } - - LOGGER.info("[{}] [fuzzy-watch-diff-sync-push] pattern ->{},syncType={},,syncCount={},totalBatch={},currentBatch={}", agent.getName(),request.getGroupKeyPattern(),request.getSyncType(),request.getContexts().size(),request.getTotalBatch(),request.getCurrentBatch()); - + + LOGGER.info( + "[{}] [fuzzy-watch-diff-sync-push] pattern ->{},syncType={},,syncCount={},totalBatch={},currentBatch={}", + agent.getName(), request.getGroupKeyPattern(), request.getSyncType(), request.getContexts().size(), + request.getTotalBatch(), request.getCurrentBatch()); + for (ConfigFuzzyWatchSyncRequest.Context requestContext : request.getContexts()) { switch (requestContext.getChangedType()) { case ADD_CONFIG: if (context.addReceivedGroupKey(requestContext.getGroupKey())) { - 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)); + 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)); } break; case DELETE_CONFIG: if (context.removeReceivedGroupKey(requestContext.getGroupKey())) { - 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)); + 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)); } break; default: @@ -245,11 +253,11 @@ ConfigFuzzyWatchSyncResponse handleFuzzyWatchNotifyDiffRequest(ConfigFuzzyWatchS * Removes a fuzzy listen listener for the specified data ID pattern, group, and listener. * * @param dataIdPattern The pattern of the data ID. - * @param groupPattern The group of the configuration. - * @param watcher The listener to remove. + * @param groupPattern The group of the configuration. + * @param watcher The listener to remove. * @throws NacosException If an error occurs while removing the listener. */ - public void removeFuzzyWatcher(String dataIdPattern, String groupPattern, ConfigFuzzyWatcher watcher){ + public void removeFuzzyWatcher(String dataIdPattern, String groupPattern, FuzzyWatchEventWatcher watcher) { ConfigFuzzyWatchContext configFuzzyWatchContext = getFuzzyListenContext(dataIdPattern, groupPattern); if (configFuzzyWatchContext != null) { synchronized (configFuzzyWatchContext) { @@ -268,47 +276,52 @@ public void removeFuzzyWatcher(String dataIdPattern, String groupPattern, Config *

This method processes the incoming fuzzy listen notify change request from a client. It updates the fuzzy * listen context based on the request's information, and publishes events if necessary. * - * @param request The fuzzy listen notify change request to handle. + * @param request The fuzzy listen notify change request to handle. */ ConfigFuzzyWatchChangeNotifyResponse handlerFuzzyWatchChangeNotifyRequest( ConfigFuzzyWatchChangeNotifyRequest request) { - - LOGGER.info("[{}] [fuzzy-watch-change-notify-push] changeType={},groupKey={}", agent.getName(),request.getChangeType(),request.getGroupKey()); - + + LOGGER.info("[{}] [fuzzy-watch-change-notify-push] changeType={},groupKey={}", agent.getName(), + request.getChangeType(), request.getGroupKey()); + Map listenContextMap = fuzzyListenContextMap.get(); String[] groupItems = GroupKey.parseKey(request.getGroupKey()); - Set matchedPatterns = FuzzyGroupKeyPattern.filterMatchedPatterns(listenContextMap.keySet(),groupItems[0], - groupItems[1], groupItems[2]); + Set matchedPatterns = FuzzyGroupKeyPattern.filterMatchedPatterns(listenContextMap.keySet(), + groupItems[0], groupItems[1], groupItems[2]); for (String matchedPattern : matchedPatterns) { ConfigFuzzyWatchContext context = listenContextMap.get(matchedPattern); - if (ADD_CONFIG.equals(request.getChangeType())||CONFIG_CHANGED.equals(request.getChangeType())) { + if (ADD_CONFIG.equals(request.getChangeType()) || CONFIG_CHANGED.equals(request.getChangeType())) { if (context.addReceivedGroupKey(request.getGroupKey())) { - LOGGER.info("[{}] [fuzzy-watch-change-notify-push] match group key added ,pattern={},groupKey={}", agent.getName(),request.getChangeType(),request.getGroupKey()); - + LOGGER.info("[{}] [fuzzy-watch-change-notify-push] match group key added ,pattern={},groupKey={}", + agent.getName(), request.getChangeType(), request.getGroupKey()); + NotifyCenter.publishEvent( - ConfigFuzzyWatchNotifyEvent.buildNotifyPatternAllListenersEvent(request.getGroupKey(), matchedPattern, ADD_CONFIG,FUZZY_WATCH_RESOURCE_CHANGED,this.clientUuid)); + ConfigFuzzyWatchNotifyEvent.buildNotifyPatternAllListenersEvent(request.getGroupKey(), + matchedPattern, ADD_CONFIG, FUZZY_WATCH_RESOURCE_CHANGED, this.clientUuid)); } - } else if (DELETE_CONFIG.equals(request.getChangeType())&&context.removeReceivedGroupKey(request.getGroupKey())) { + } 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)); + ConfigFuzzyWatchNotifyEvent.buildNotifyPatternAllListenersEvent(request.getGroupKey(), + matchedPattern, Constants.ConfigChangedType.DELETE_CONFIG, FUZZY_WATCH_RESOURCE_CHANGED, + this.clientUuid)); } } return new ConfigFuzzyWatchChangeNotifyResponse(); } - - void notifyFuzzyWatchSync(){ + void notifyFuzzyWatchSync() { fuzzyListenExecuteBell.offer(bellItem); - + } /** * Execute fuzzy listen configuration changes. * *

This method iterates through all fuzzy listen contexts and determines whether they need to be added or - * removed based on their consistency with the server and discard status. It then calls the appropriate method - * to execute the fuzzy listen operation. + * removed based on their consistency with the server and discard status. It then calls the appropriate method to + * execute the fuzzy listen operation. * * @throws NacosException If an error occurs during the execution of fuzzy listen configuration changes. */ @@ -319,8 +332,8 @@ public void executeConfigFuzzyListen() throws NacosException { // Determine whether a full synchronization is needed boolean needAllSync = now - fuzzyListenLastAllSyncTime.get() >= FUZZY_LISTEN_ALL_SYNC_INTERNAL; - - List needSyncContexts=new ArrayList<>(); + + List needSyncContexts = new ArrayList<>(); // Iterate through all fuzzy listen contexts for (ConfigFuzzyWatchContext context : fuzzyListenContextMap.get().values()) { // Check if the context is consistent with the server @@ -328,7 +341,7 @@ public void executeConfigFuzzyListen() throws NacosException { // Skip if a full synchronization is not needed if (!needAllSync) { continue; - }else{ + } else { context.syncFuzzyWatchers(); } } @@ -345,11 +358,11 @@ public void executeConfigFuzzyListen() throws NacosException { } } - void resetConsistenceStatus(){ + void resetConsistenceStatus() { Collection configFuzzyWatchContexts = fuzzyListenContextMap.get().values(); - + for (ConfigFuzzyWatchContext context : configFuzzyWatchContexts) { - context.setConsistentWithServer(false); + context.setConsistentWithServer(false); } } @@ -362,8 +375,7 @@ void resetConsistenceStatus(){ * @param contextLists The map of contexts to execute fuzzy listen operations for. * @throws NacosException If an error occurs during the execution of fuzzy listen configuration changes. */ - private void doExecuteConfigFuzzyListen(List contextLists) - throws NacosException { + private void doExecuteConfigFuzzyListen(List contextLists) throws NacosException { // Return if the context map is null or empty if (CollectionUtils.isEmpty(contextLists)) { return; @@ -371,27 +383,26 @@ private void doExecuteConfigFuzzyListen(List contextLis // List to hold futures for asynchronous tasks List> listenFutures = new ArrayList<>(); - + RpcClient rpcClient = agent.ensureRpcClient(taskId); - + // Iterate through the context map and submit tasks for execution for (ConfigFuzzyWatchContext entry : contextLists) { ExecutorService executorService = agent.executor; // Submit task for execution Future future = executorService.submit(() -> { - ConfigFuzzyWatchRequest configFuzzyWatchRequest = buildFuzzyListenConfigRequest( - entry); + ConfigFuzzyWatchRequest configFuzzyWatchRequest = buildFuzzyListenConfigRequest(entry); try { // Execute the fuzzy listen operation - ConfigFuzzyWatchResponse listenResponse = (ConfigFuzzyWatchResponse) agent.requestProxy( - rpcClient, configFuzzyWatchRequest); + ConfigFuzzyWatchResponse listenResponse = (ConfigFuzzyWatchResponse) agent.requestProxy(rpcClient, + configFuzzyWatchRequest); if (listenResponse != null && listenResponse.isSuccess()) { - - if (entry.isDiscard()) { - removeFuzzyListenContext(entry.getGroupKeyPattern()); - } else { - entry.setConsistentWithServer(true); - } + + if (entry.isDiscard()) { + removeFuzzyListenContext(entry.getGroupKeyPattern()); + } else { + entry.setConsistentWithServer(true); + } } } catch (NacosException e) { @@ -420,7 +431,6 @@ private void doExecuteConfigFuzzyListen(List contextLis } } - /** * Builds a request for fuzzy listen configuration. * @@ -431,18 +441,18 @@ private ConfigFuzzyWatchRequest buildFuzzyListenConfigRequest(ConfigFuzzyWatchCo ConfigFuzzyWatchRequest request = new ConfigFuzzyWatchRequest(); request.setGroupKeyPattern(context.getGroupKeyPattern()); request.setInitializing(context.isInitializing()); - request.setWatchType((context.isDiscard()&&CollectionUtils.isEmpty(context.getConfigFuzzyWatcherWrappers()))?WATCH_TYPE_CANCEL_WATCH:WATCH_TYPE_WATCH); + request.setWatchType((context.isDiscard() && CollectionUtils.isEmpty(context.getConfigFuzzyWatcherWrappers())) + ? WATCH_TYPE_CANCEL_WATCH : WATCH_TYPE_WATCH); request.setReceivedGroupKeys(context.getReceivedGroupKeys()); return request; } - /** * Adds a fuzzy listen context if it doesn't already exist for the specified data ID pattern and group. If the * context already exists, returns the existing context. * * @param dataIdPattern The pattern of the data ID. - * @param groupPattern The group of the configuration. + * @param groupPattern The group of the configuration. * @return The fuzzy listen context for the specified data ID pattern and group. */ private ConfigFuzzyWatchContext initFuzzyWatchContextIfAbsent(String dataIdPattern, String groupPattern) { @@ -455,18 +465,20 @@ private ConfigFuzzyWatchContext initFuzzyWatchContextIfAbsent(String dataIdPatte if (contextFromMap != null) { context = contextFromMap; } else { - String groupKeyPattern = FuzzyGroupKeyPattern.generatePattern(dataIdPattern, groupPattern,agent.getTenant()); - - context = new ConfigFuzzyWatchContext(agent.getName(),groupKeyPattern); + String groupKeyPattern = FuzzyGroupKeyPattern.generatePattern(dataIdPattern, groupPattern, + agent.getTenant()); + + context = new ConfigFuzzyWatchContext(agent.getName(), groupKeyPattern); context.setConsistentWithServer(false); Map copy = new HashMap<>(fuzzyListenContextMap.get()); copy.put(groupKeyPattern, context); - LOGGER.info("[{}][fuzzy-watch] init fuzzy watch context , groupKeyPattern={} ,notify fuzzy watch sync ", agent.getName(), groupKeyPattern); + LOGGER.info("[{}][fuzzy-watch] init fuzzy watch context , groupKeyPattern={} ,notify fuzzy watch sync ", + agent.getName(), groupKeyPattern); fuzzyListenContextMap.set(copy); notifyFuzzyWatchSync(); } } - + return context; } 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 ed83231c96b..3489e77ed40 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 @@ -30,7 +30,6 @@ */ public class ConfigFuzzyWatchNotifyEvent extends Event { - private String clientUuid; /** @@ -57,44 +56,44 @@ public class ConfigFuzzyWatchNotifyEvent extends Event { */ public ConfigFuzzyWatchNotifyEvent() { } - + /** * Constructs a new FuzzyListenNotifyEvent with the specified group, dataId, and type. * - * @param groupKey The groupKey of the configuration. - * @param changedType The type of notification. + * @param groupKey The groupKey of the configuration. + * @param changedType The type of notification. */ - private ConfigFuzzyWatchNotifyEvent(String groupKey, String changedType, String syncType, String groupKeyPattern,String clientUuid,String watcherUuid) { + private ConfigFuzzyWatchNotifyEvent(String groupKey, String changedType, String syncType, String groupKeyPattern, + String clientUuid, String watcherUuid) { this.groupKey = groupKey; - this.syncType=syncType; + this.syncType = syncType; this.changedType = changedType; this.groupKeyPattern = groupKeyPattern; - this.clientUuid=clientUuid; - this.watcherUuid=watcherUuid; + this.clientUuid = clientUuid; + this.watcherUuid = watcherUuid; } - /** * Builds a new FuzzyListenNotifyEvent with the specified group, dataId, and type. * - * @param groupKey The groupKey of the configuration. + * @param groupKey The groupKey of the configuration. * @return A new FuzzyListenNotifyEvent instance. */ public static ConfigFuzzyWatchNotifyEvent buildNotifyPatternAllListenersEvent(String groupKey, - String groupKeyPattern, String changedType,String syncType,String clientUuid) { - return buildNotifyPatternAllListenersEvent(groupKey,groupKeyPattern,changedType,syncType,clientUuid,null); + String groupKeyPattern, String changedType, String syncType, String clientUuid) { + return buildNotifyPatternAllListenersEvent(groupKey, groupKeyPattern, changedType, syncType, clientUuid, null); } /** * Builds a new FuzzyListenNotifyEvent with the specified group, dataId, and type. * - * @param groupKey The groupKey of the configuration. + * @param groupKey The groupKey of the configuration. * @return A new FuzzyListenNotifyEvent instance. */ public static ConfigFuzzyWatchNotifyEvent buildNotifyPatternAllListenersEvent(String groupKey, - String groupKeyPattern, String changedType,String syncType,String clientUuid,String watcherUuid) { - ConfigFuzzyWatchNotifyEvent configFuzzyWatchNotifyEvent = new ConfigFuzzyWatchNotifyEvent(groupKey, changedType, syncType, - groupKeyPattern,clientUuid,watcherUuid); + String groupKeyPattern, String changedType, String syncType, String clientUuid, String watcherUuid) { + ConfigFuzzyWatchNotifyEvent configFuzzyWatchNotifyEvent = new ConfigFuzzyWatchNotifyEvent(groupKey, changedType, + syncType, groupKeyPattern, clientUuid, watcherUuid); return configFuzzyWatchNotifyEvent; } @@ -115,16 +114,14 @@ public String getGroupKeyPattern() { return groupKeyPattern; } - public String getGroupKey() { return groupKey; } - public String getSyncType() { return syncType; } - + /** * Gets the type of notification. * 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 8520e260996..2d7ade95b71 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 @@ -16,25 +16,31 @@ package com.alibaba.nacos.client.config.impl; -import com.alibaba.nacos.api.config.listener.ConfigFuzzyWatcher; +import com.alibaba.nacos.api.config.listener.FuzzyWatchEventWatcher; import java.util.HashSet; import java.util.Objects; import java.util.Set; import java.util.UUID; +/** + * ConfigFuzzyWatcherWrapper. + * @author shiyiyue + */ public class ConfigFuzzyWatcherWrapper { + FuzzyWatchEventWatcher fuzzyWatchEventWatcher; - ConfigFuzzyWatcher configFuzzyWatcher; - - public ConfigFuzzyWatcherWrapper(ConfigFuzzyWatcher configFuzzyWatcher){ - this.configFuzzyWatcher=configFuzzyWatcher; + public ConfigFuzzyWatcherWrapper(FuzzyWatchEventWatcher fuzzyWatchEventWatcher) { + this.fuzzyWatchEventWatcher = fuzzyWatchEventWatcher; } + /** * Unique identifier for the listener. */ - String uuid= UUID.randomUUID().toString(); + String uuid = UUID.randomUUID().toString(); + + private Set syncGroupKeys = new HashSet<>(); @Override public boolean equals(Object o) { @@ -45,16 +51,14 @@ public boolean equals(Object o) { return false; } ConfigFuzzyWatcherWrapper that = (ConfigFuzzyWatcherWrapper) o; - return Objects.equals(configFuzzyWatcher, that.configFuzzyWatcher) && Objects.equals(uuid, that.uuid); + return Objects.equals(fuzzyWatchEventWatcher, that.fuzzyWatchEventWatcher) && Objects.equals(uuid, that.uuid); } @Override public int hashCode() { - return Objects.hash(configFuzzyWatcher, uuid); + return Objects.hash(fuzzyWatchEventWatcher, uuid); } - - private Set syncGroupKeys = new HashSet<>(); - + Set getSyncGroupKeys() { return syncGroupKeys; } diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/NacosNamingService.java b/client/src/main/java/com/alibaba/nacos/client/naming/NacosNamingService.java index c4b51a5936f..1b0b24d9884 100644 --- a/client/src/main/java/com/alibaba/nacos/client/naming/NacosNamingService.java +++ b/client/src/main/java/com/alibaba/nacos/client/naming/NacosNamingService.java @@ -20,8 +20,8 @@ import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.naming.NamingService; -import com.alibaba.nacos.api.naming.listener.FuzzyWatchEventWatcher; import com.alibaba.nacos.api.naming.listener.EventListener; +import com.alibaba.nacos.api.naming.listener.FuzzyWatchEventWatcher; import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.api.naming.pojo.ListView; import com.alibaba.nacos.api.naming.pojo.ServiceInfo; @@ -32,13 +32,13 @@ import com.alibaba.nacos.api.selector.AbstractSelector; import com.alibaba.nacos.client.env.NacosClientProperties; import com.alibaba.nacos.client.naming.cache.NamingFuzzyWatchContext; -import com.alibaba.nacos.client.naming.cache.ServiceInfoHolder; import com.alibaba.nacos.client.naming.cache.NamingFuzzyWatchServiceListHolder; +import com.alibaba.nacos.client.naming.cache.ServiceInfoHolder; import com.alibaba.nacos.client.naming.core.Balancer; import com.alibaba.nacos.client.naming.event.InstancesChangeEvent; import com.alibaba.nacos.client.naming.event.InstancesChangeNotifier; -import com.alibaba.nacos.client.naming.event.NamingFuzzyWatchNotifyEvent; import com.alibaba.nacos.client.naming.event.InstancesDiff; +import com.alibaba.nacos.client.naming.event.NamingFuzzyWatchNotifyEvent; import com.alibaba.nacos.client.naming.remote.NamingClientProxy; import com.alibaba.nacos.client.naming.remote.NamingClientProxyDelegate; import com.alibaba.nacos.client.naming.selector.NamingSelectorFactory; @@ -123,15 +123,12 @@ private void init(Properties properties) throws NacosException { NotifyCenter.registerToPublisher(InstancesChangeEvent.class, 16384); NotifyCenter.registerSubscriber(changeNotifier); this.serviceInfoHolder = new ServiceInfoHolder(namespace, this.notifierEventScope, nacosClientProperties); - + NotifyCenter.registerToPublisher(NamingFuzzyWatchNotifyEvent.class, 16384); this.namingFuzzyWatchServiceListHolder = new NamingFuzzyWatchServiceListHolder(this.notifierEventScope); - - this.clientProxy = new NamingClientProxyDelegate(this.namespace, serviceInfoHolder, - nacosClientProperties, changeNotifier,namingFuzzyWatchServiceListHolder); - - - + + this.clientProxy = new NamingClientProxyDelegate(this.namespace, serviceInfoHolder, nacosClientProperties, + changeNotifier, namingFuzzyWatchServiceListHolder); } @Deprecated @@ -378,7 +375,8 @@ private ServiceInfo getServiceInfoBySubscribe(String serviceName, String groupNa return serviceInfo; } - private ServiceInfo tryToSubscribe(String serviceName, String groupName, ServiceInfo cachedServiceInfo) throws NacosException { + private ServiceInfo tryToSubscribe(String serviceName, String groupName, ServiceInfo cachedServiceInfo) + throws NacosException { // not found in cache, service never subscribed. if (null == cachedServiceInfo) { return clientProxy.subscribe(serviceName, groupName, StringUtils.EMPTY); @@ -547,31 +545,32 @@ public void fuzzyWatch(String fixedGroupName, FuzzyWatchEventWatcher listener) t } @Override - public void fuzzyWatch(String serviceNamePattern, String groupNamePattern, - FuzzyWatchEventWatcher listener) throws NacosException { + public void fuzzyWatch(String serviceNamePattern, String groupNamePattern, FuzzyWatchEventWatcher listener) + throws NacosException { doFuzzyWatch(serviceNamePattern, groupNamePattern, listener); } @Override - public Future> fuzzyWatchWithServiceKeys(String fixedGroupName, FuzzyWatchEventWatcher listener) throws NacosException { + public Future> fuzzyWatchWithServiceKeys(String fixedGroupName, FuzzyWatchEventWatcher listener) + throws NacosException { return doFuzzyWatch(ANY_PATTERN, fixedGroupName, listener); } @Override - public Future> fuzzyWatchWithServiceKeys(String serviceNamePattern, String groupNamePattern, + public Future> fuzzyWatchWithServiceKeys(String serviceNamePattern, String groupNamePattern, FuzzyWatchEventWatcher listener) throws NacosException { - return doFuzzyWatch(serviceNamePattern, groupNamePattern, listener); + return doFuzzyWatch(serviceNamePattern, groupNamePattern, listener); } - - private Future> doFuzzyWatch(String serviceNamePattern, String groupNamePattern, - FuzzyWatchEventWatcher watcher) { + private Future> doFuzzyWatch(String serviceNamePattern, String groupNamePattern, + FuzzyWatchEventWatcher watcher) { if (null == watcher) { return null; } - + String groupKeyPattern = FuzzyGroupKeyPattern.generatePattern(serviceNamePattern, groupNamePattern, namespace); - NamingFuzzyWatchContext namingFuzzyWatchContext = namingFuzzyWatchServiceListHolder.registerFuzzyWatcher(groupKeyPattern,watcher); + NamingFuzzyWatchContext namingFuzzyWatchContext = namingFuzzyWatchServiceListHolder.registerFuzzyWatcher( + groupKeyPattern, watcher); return namingFuzzyWatchContext.createNewFuture(); } @@ -581,22 +580,23 @@ public void cancelFuzzyWatch(String fixedGroupName, FuzzyWatchEventWatcher liste } @Override - public void cancelFuzzyWatch(String serviceNamePattern, String fixedGroupName, FuzzyWatchEventWatcher listener) throws NacosException { + public void cancelFuzzyWatch(String serviceNamePattern, String fixedGroupName, FuzzyWatchEventWatcher listener) + throws NacosException { doCancelFuzzyWatch(serviceNamePattern, fixedGroupName, listener); } - private void doCancelFuzzyWatch(String serviceNamePattern, String groupNamePattern, - FuzzyWatchEventWatcher watcher) throws NacosException { + private void doCancelFuzzyWatch(String serviceNamePattern, String groupNamePattern, FuzzyWatchEventWatcher watcher) + throws NacosException { if (null == watcher) { return; } - String groupKeyPattern=FuzzyGroupKeyPattern.generatePattern(serviceNamePattern,groupNamePattern,namespace); - - NamingFuzzyWatchContext namingFuzzyWatchContext = namingFuzzyWatchServiceListHolder.getFuzzyWatchContext(groupKeyPattern); - if (namingFuzzyWatchContext!=null){ + String groupKeyPattern = FuzzyGroupKeyPattern.generatePattern(serviceNamePattern, groupNamePattern, namespace); + + NamingFuzzyWatchContext namingFuzzyWatchContext = namingFuzzyWatchServiceListHolder.getFuzzyWatchContext( + groupKeyPattern); + if (namingFuzzyWatchContext != null) { namingFuzzyWatchContext.removeWatcher(watcher); } - } @Override 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 d516647da88..c607c8e9a44 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 @@ -23,22 +23,27 @@ import java.util.Set; import java.util.UUID; +/** + * fuzzy watcher wrapper. + * @author shiyiyue + */ public class FuzzyWatchEventWatcherWrapper { FuzzyWatchEventWatcher fuzzyWatchEventWatcher; - public FuzzyWatchEventWatcherWrapper(FuzzyWatchEventWatcher fuzzyWatchEventWatcher){ - this.fuzzyWatchEventWatcher=fuzzyWatchEventWatcher; + + public FuzzyWatchEventWatcherWrapper(FuzzyWatchEventWatcher fuzzyWatchEventWatcher) { + this.fuzzyWatchEventWatcher = fuzzyWatchEventWatcher; } - String uuid= UUID.randomUUID().toString(); + String uuid = UUID.randomUUID().toString(); private Set syncServiceKeys = new HashSet<>(); - final String getUuid() { + final String getUuid() { return uuid; } - Set getSyncServiceKeys() { + Set getSyncServiceKeys() { return syncServiceKeys; } 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 70332fc615d..e5173614bda 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 @@ -16,7 +16,6 @@ package com.alibaba.nacos.client.naming.cache; -import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.naming.listener.FuzzyWatchChangeEvent; import com.alibaba.nacos.api.naming.listener.FuzzyWatchEventWatcher; import com.alibaba.nacos.api.naming.pojo.ListView; @@ -34,19 +33,16 @@ import java.util.Iterator; import java.util.List; import java.util.Set; -import java.util.concurrent.CompletableFuture; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; 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; - /** * fuzzy wather context for a single group key pattern. * @@ -69,7 +65,6 @@ public class NamingFuzzyWatchContext { */ private String envName; - private String groupKeyPattern; /** @@ -87,7 +82,6 @@ public class NamingFuzzyWatchContext { */ final AtomicBoolean initializationCompleted = new AtomicBoolean(false); - /** * Flag indicating whether the context is discarded. */ @@ -109,40 +103,46 @@ public NamingFuzzyWatchContext(String envName, String groupKeyPattern) { this.groupKeyPattern = groupKeyPattern; } - private void doNotifyWatcher(final String serviceKey, final String changedType,final String syncType, - FuzzyWatchEventWatcherWrapper fuzzyWatchEventWatcherWrapper){ - - if (ADD_SERVICE.equals(changedType)&&fuzzyWatchEventWatcherWrapper.getSyncServiceKeys().contains(serviceKey)){ + private void doNotifyWatcher(final String serviceKey, final String changedType, final String syncType, + FuzzyWatchEventWatcherWrapper fuzzyWatchEventWatcherWrapper) { + + if (ADD_SERVICE.equals(changedType) && fuzzyWatchEventWatcherWrapper.getSyncServiceKeys() + .contains(serviceKey)) { return; } - - if (DELETE_SERVICE.equals(changedType)&&!fuzzyWatchEventWatcherWrapper.getSyncServiceKeys().contains(serviceKey)){ + + if (DELETE_SERVICE.equals(changedType) && !fuzzyWatchEventWatcherWrapper.getSyncServiceKeys() + .contains(serviceKey)) { return; } String[] serviceKeyItems = NamingUtils.parseServiceKey(serviceKey); - String namespace=serviceKeyItems[0]; - String groupName=serviceKeyItems[1]; - String serviceName=serviceKeyItems[2]; + String namespace = serviceKeyItems[0]; + String groupName = serviceKeyItems[1]; + String serviceName = serviceKeyItems[2]; - final String resetSyncType=!initializationCompleted.get()?FUZZY_WATCH_INIT_NOTIFY:syncType; + final String resetSyncType = !initializationCompleted.get() ? FUZZY_WATCH_INIT_NOTIFY : syncType; Runnable job = () -> { long start = System.currentTimeMillis(); - FuzzyWatchChangeEvent event = new FuzzyWatchChangeEvent(serviceName,groupName, namespace, changedType,resetSyncType); + FuzzyWatchChangeEvent event = new FuzzyWatchChangeEvent(serviceName, groupName, namespace, changedType, + resetSyncType); if (fuzzyWatchEventWatcherWrapper != null) { fuzzyWatchEventWatcherWrapper.fuzzyWatchEventWatcher.onEvent(event); } LOGGER.info( "[{}] [notify-watcher-ok] serviceName={}, groupName={}, namespace={}, watcher={},changedType={}, job run cost={} millis.", - envName, serviceName, groupName, namespace, fuzzyWatchEventWatcherWrapper.fuzzyWatchEventWatcher,changedType, (System.currentTimeMillis() - start)); + envName, serviceName, groupName, namespace, fuzzyWatchEventWatcherWrapper.fuzzyWatchEventWatcher, + changedType, (System.currentTimeMillis() - start)); if (changedType.equals(DELETE_SERVICE)) { - fuzzyWatchEventWatcherWrapper.getSyncServiceKeys().remove(NamingUtils.getServiceKey(namespace, groupName, serviceName)); + fuzzyWatchEventWatcherWrapper.getSyncServiceKeys() + .remove(NamingUtils.getServiceKey(namespace, groupName, serviceName)); } else if (changedType.equals(ADD_SERVICE)) { - fuzzyWatchEventWatcherWrapper.getSyncServiceKeys().add(NamingUtils.getServiceKey(namespace, groupName, serviceName)); + fuzzyWatchEventWatcherWrapper.getSyncServiceKeys() + .add(NamingUtils.getServiceKey(namespace, groupName, serviceName)); } }; - + try { if (null != fuzzyWatchEventWatcherWrapper.fuzzyWatchEventWatcher.getExecutor()) { LOGGER.info( @@ -156,18 +156,20 @@ private void doNotifyWatcher(final String serviceKey, final String changedType,f job.run(); } } catch (Throwable t) { - LOGGER.error("[{}] [notify-watcher-error] serviceName={}, groupName={}, namespace={}, listener={}, throwable={}.", + LOGGER.error( + "[{}] [notify-watcher-error] serviceName={}, groupName={}, namespace={}, listener={}, throwable={}.", envName, serviceName, groupName, namespace, fuzzyWatchEventWatcherWrapper, t.getCause()); } } + /** * Mark initialization as complete and notify waiting threads. */ public void markInitializationComplete() { - LOGGER.info("[{}] [fuzzy-watch] pattern init notify finish pattern={},match service count {}", - envName,groupKeyPattern,receivedServiceKeys.size()); + LOGGER.info("[{}] [fuzzy-watch] pattern init notify finish pattern={},match service count {}", envName, + groupKeyPattern, receivedServiceKeys.size()); initializationCompleted.set(true); - synchronized (this){ + synchronized (this) { notifyAll(); } } @@ -179,9 +181,9 @@ public void markInitializationComplete() { */ public void removeWatcher(FuzzyWatchEventWatcher watcher) { Iterator iterator = fuzzyWatchEventWatcherWrappers.iterator(); - while (iterator.hasNext()){ + while (iterator.hasNext()) { FuzzyWatchEventWatcherWrapper next = iterator.next(); - if (next.fuzzyWatchEventWatcher.equals(watcher)){ + if (next.fuzzyWatchEventWatcher.equals(watcher)) { iterator.remove(); LOGGER.info("[{}] [remove-watcher-ok] groupKeyPattern={}, watcher={},uuid={} ", getEnvName(), this.groupKeyPattern, watcher, next.getUuid()); @@ -261,13 +263,11 @@ public Set getReceivedServiceKeys() { return Collections.unmodifiableSet(receivedServiceKeys); } - - public boolean addReceivedServiceKey(String serviceKey){ + public boolean addReceivedServiceKey(String serviceKey) { return receivedServiceKeys.add(serviceKey); } - - public boolean removeReceivedServiceKey(String serviceKey){ + public boolean removeReceivedServiceKey(String serviceKey) { return receivedServiceKeys.remove(serviceKey); } @@ -280,48 +280,51 @@ public Set getFuzzyWatchEventWatcherWrappers() { return fuzzyWatchEventWatcherWrappers; } - void syncFuzzyWatchers(){ - for(FuzzyWatchEventWatcherWrapper namingFuzzyWatcher: fuzzyWatchEventWatcherWrappers){ + void syncFuzzyWatchers() { + for (FuzzyWatchEventWatcherWrapper namingFuzzyWatcher : fuzzyWatchEventWatcherWrappers) { Set receivedServiceKeysContext = 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); + for (FuzzyGroupKeyPattern.GroupKeyState groupKeyState : groupKeyStates) { + String changedType = groupKeyState.isExist() ? ADD_SERVICE : DELETE_SERVICE; + doNotifyWatcher(groupKeyState.getGroupKey(), changedType, FUZZY_WATCH_DIFF_SYNC_NOTIFY, + namingFuzzyWatcher); } } } - void notifyFuzzyWatchers(String serviceKey,String changedType,String syncType,String watcherUuid){ - for(FuzzyWatchEventWatcherWrapper namingFuzzyWatcher: filterWatchers(watcherUuid)){ - doNotifyWatcher(serviceKey,changedType,syncType,namingFuzzyWatcher); + void notifyFuzzyWatchers(String serviceKey, String changedType, String syncType, String watcherUuid) { + for (FuzzyWatchEventWatcherWrapper namingFuzzyWatcher : filterWatchers(watcherUuid)) { + doNotifyWatcher(serviceKey, changedType, syncType, namingFuzzyWatcher); } } - - private Set filterWatchers(String uuid){ - if (StringUtils.isBlank(uuid)|| CollectionUtils.isEmpty(getFuzzyWatchEventWatcherWrappers())){ + private Set filterWatchers(String uuid) { + if (StringUtils.isBlank(uuid) || CollectionUtils.isEmpty(getFuzzyWatchEventWatcherWrappers())) { return getFuzzyWatchEventWatcherWrappers(); - }else{ - return getFuzzyWatchEventWatcherWrappers().stream().filter(a->a.getUuid().equals(uuid)).collect( - Collectors.toSet()); + } else { + return getFuzzyWatchEventWatcherWrappers().stream().filter(a -> a.getUuid().equals(uuid)) + .collect(Collectors.toSet()); } - } - public Future> createNewFuture(){ - Future> completableFuture=new Future>(){ + /** + * create a new future of this context. + * @return + */ + public Future> createNewFuture() { + Future> completableFuture = new Future>() { @Override public boolean cancel(boolean mayInterruptIfRunning) { throw new UnsupportedOperationException("not support to cancel fuzzy watch"); } - + @Override public boolean isCancelled() { return false; } - + @Override public boolean isDone() { return NamingFuzzyWatchContext.this.initializationCompleted.get(); @@ -330,7 +333,7 @@ public boolean isDone() { @Override public ListView get() throws InterruptedException { synchronized (NamingFuzzyWatchContext.this) { - while(!NamingFuzzyWatchContext.this.initializationCompleted.get()){ + while (!NamingFuzzyWatchContext.this.initializationCompleted.get()) { NamingFuzzyWatchContext.this.wait(); } } @@ -340,18 +343,19 @@ public ListView get() throws InterruptedException { result.setCount(result.getData().size()); return result; } - + @Override public ListView get(long timeout, TimeUnit unit) throws InterruptedException, TimeoutException { - if(!NamingFuzzyWatchContext.this.initializationCompleted.get()){ + if (!NamingFuzzyWatchContext.this.initializationCompleted.get()) { synchronized (NamingFuzzyWatchContext.this) { NamingFuzzyWatchContext.this.wait(unit.toMillis(timeout)); } } - if (!NamingFuzzyWatchContext.this.initializationCompleted.get()){ - throw new TimeoutException("fuzzy watch result future timeout for "+unit.toMillis(timeout)+" millis"); + if (!NamingFuzzyWatchContext.this.initializationCompleted.get()) { + throw new TimeoutException( + "fuzzy watch result future timeout for " + unit.toMillis(timeout) + " millis"); } ListView result = new ListView<>(); @@ -363,4 +367,3 @@ public ListView get(long timeout, TimeUnit unit) throws InterruptedExcep return completableFuture; } } - 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 d57349a0849..64fcc984eec 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 @@ -16,7 +16,6 @@ package com.alibaba.nacos.client.naming.cache; - import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.naming.listener.FuzzyWatchEventWatcher; import com.alibaba.nacos.api.naming.remote.request.NamingFuzzyWatchRequest; @@ -67,9 +66,9 @@ public class NamingFuzzyWatchServiceListHolder extends Subscriber { while (!executorService.isShutdown() && !executorService.isTerminated()) { try { @@ -115,11 +120,11 @@ public void start(){ }); } - public void registerNamingGrpcClientProxy(NamingGrpcClientProxy namingGrpcClientProxy ){ - this.namingGrpcClientProxy=namingGrpcClientProxy; + public void registerNamingGrpcClientProxy(NamingGrpcClientProxy namingGrpcClientProxy) { + this.namingGrpcClientProxy = namingGrpcClientProxy; } - public NamingFuzzyWatchContext getFuzzyWatchContext(String groupKeyPattern){ + public NamingFuzzyWatchContext getFuzzyWatchContext(String groupKeyPattern) { return fuzzyMatchContextMap.get(groupKeyPattern); } @@ -128,17 +133,18 @@ public NamingFuzzyWatchContext getFuzzyWatchContext(String groupKeyPattern){ * * @param watcher watcher to be added */ - public NamingFuzzyWatchContext registerFuzzyWatcher(String groupKeyPattern,FuzzyWatchEventWatcher watcher) { + 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()); + + 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); } } @@ -146,15 +152,18 @@ public NamingFuzzyWatchContext registerFuzzyWatcher(String groupKeyPattern,Fuzzy return namingFuzzyWatchContext; } - public NamingFuzzyWatchContext initFuzzyWatchContextIfNeed(String groupKeyPattern){ - if (!fuzzyMatchContextMap.containsKey(groupKeyPattern)){ - synchronized (fuzzyMatchContextMap){ - if (fuzzyMatchContextMap.containsKey(groupKeyPattern)){ + /** + * init fuzzy watch context. + * @param groupKeyPattern groupKeyPattern. + * @return fuzzy context. + */ + public NamingFuzzyWatchContext initFuzzyWatchContextIfNeed(String groupKeyPattern) { + if (!fuzzyMatchContextMap.containsKey(groupKeyPattern)) { + synchronized (fuzzyMatchContextMap) { + if (fuzzyMatchContextMap.containsKey(groupKeyPattern)) { return fuzzyMatchContextMap.get(groupKeyPattern); } - LOGGER.info("[fuzzy-watch] init fuzzy watch context for pattern {}", groupKeyPattern); - fuzzyMatchContextMap.putIfAbsent(groupKeyPattern, new NamingFuzzyWatchContext(notifierEventScope, groupKeyPattern)); notifyFuzzyWatchSync(); @@ -162,18 +171,27 @@ public NamingFuzzyWatchContext initFuzzyWatchContextIfNeed(String groupKeyPatter } return fuzzyMatchContextMap.get(groupKeyPattern); } - + + /** + * remove fuzzy watch context for pattern. + * @param groupKeyPattern group key pattern. + */ public synchronized void removePatternMatchCache(String groupKeyPattern) { NamingFuzzyWatchContext namingFuzzyWatchContext = fuzzyMatchContextMap.get(groupKeyPattern); - if (namingFuzzyWatchContext==null){ + if (namingFuzzyWatchContext == null) { return; } - if (namingFuzzyWatchContext.isDiscard()&&namingFuzzyWatchContext.getFuzzyWatchEventWatcherWrappers().isEmpty()){ + if (namingFuzzyWatchContext.isDiscard() && namingFuzzyWatchContext.getFuzzyWatchEventWatcherWrappers() + .isEmpty()) { + LOGGER.info("[fuzzy-watch] remove fuzzy watch context for pattern {}", groupKeyPattern); fuzzyMatchContextMap.remove(groupKeyPattern); } } - void notifyFuzzyWatchSync(){ + /** + * notify sync fuzzy watch with server. + */ + void notifyFuzzyWatchSync() { fuzzyWatchExecuteBell.offer(bellItem); } @@ -181,8 +199,8 @@ void notifyFuzzyWatchSync(){ * Execute fuzzy listen configuration changes. * *

This method iterates through all fuzzy listen contexts and determines whether they need to be added or - * removed based on their consistency with the server and discard status. It then calls the appropriate method - * to execute the fuzzy listen operation. + * removed based on their consistency with the server and discard status. It then calls the appropriate method to + * execute the fuzzy listen operation. * * @throws NacosException If an error occurs during the execution of fuzzy listen configuration changes. */ @@ -194,7 +212,7 @@ public void executeNamingFuzzyWatch() throws NacosException { // Determine whether a full synchronization is needed boolean needAllSync = now - fuzzyWatchLastAllSyncTime.get() >= FUZZY_LISTEN_ALL_SYNC_INTERNAL; - List needSyncContexts=new ArrayList<>(); + List needSyncContexts = new ArrayList<>(); // Iterate through all fuzzy listen contexts for (NamingFuzzyWatchContext context : fuzzyMatchContextMap.values()) { // Check if the context is consistent with the server @@ -202,7 +220,7 @@ public void executeNamingFuzzyWatch() throws NacosException { // Skip if a full synchronization is not needed if (!needAllSync) { continue; - }else{ + } else { context.syncFuzzyWatchers(); } } @@ -219,8 +237,9 @@ public void executeNamingFuzzyWatch() throws NacosException { } } - public void resetConsistenceStatus(){ - fuzzyMatchContextMap.values().forEach(fuzzyWatcherContext -> fuzzyWatcherContext.setConsistentWithServer(false)); + public void resetConsistenceStatus() { + fuzzyMatchContextMap.values() + .forEach(fuzzyWatcherContext -> fuzzyWatcherContext.setConsistentWithServer(false)); } /** @@ -232,8 +251,7 @@ public void resetConsistenceStatus(){ * @param contextLists The map of contexts to execute fuzzy listen operations for. * @throws NacosException If an error occurs during the execution of fuzzy listen configuration changes. */ - private void doExecuteNamingFuzzyWatch(List contextLists) - throws NacosException { + private void doExecuteNamingFuzzyWatch(List contextLists) throws NacosException { // Return if the context map is null or empty if (CollectionUtils.isEmpty(contextLists)) { return; @@ -242,47 +260,46 @@ private void doExecuteNamingFuzzyWatch(List contextList // Iterate through the context map and submit tasks for execution for (NamingFuzzyWatchContext entry : contextLists) { // Submit task for execution - NamingFuzzyWatchRequest configFuzzyWatchRequest = buildFuzzyWatchNamingRequest( - entry); - try { - + NamingFuzzyWatchRequest configFuzzyWatchRequest = buildFuzzyWatchNamingRequest(entry); + try { + + // Execute the fuzzy listen operation + NamingFuzzyWatchResponse listenResponse = namingGrpcClientProxy.fuzzyWatchRequest( + configFuzzyWatchRequest); + if (listenResponse != null && listenResponse.isSuccess()) { - // Execute the fuzzy listen operation - NamingFuzzyWatchResponse listenResponse = namingGrpcClientProxy.fuzzyWatchRequest(configFuzzyWatchRequest); - if (listenResponse != null && listenResponse.isSuccess()) { - - if (configFuzzyWatchRequest.getWatchType().equals(WATCH_TYPE_CANCEL_WATCH)) { - removePatternMatchCache(entry.getGroupKeyPattern()); - } 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 (configFuzzyWatchRequest.getWatchType().equals(WATCH_TYPE_CANCEL_WATCH)) { + removePatternMatchCache(entry.getGroupKeyPattern()); + } else { + entry.setConsistentWithServer(true); } - // Retry notification - notifyFuzzyWatchSync(); + } + } 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 + } + // Retry notification + notifyFuzzyWatchSync(); + } } } - - private NamingFuzzyWatchRequest buildFuzzyWatchNamingRequest(NamingFuzzyWatchContext namingFuzzyWatchContext){ - NamingFuzzyWatchRequest namingFuzzyWatchRequest=new NamingFuzzyWatchRequest(); + private NamingFuzzyWatchRequest buildFuzzyWatchNamingRequest(NamingFuzzyWatchContext namingFuzzyWatchContext) { + NamingFuzzyWatchRequest namingFuzzyWatchRequest = new NamingFuzzyWatchRequest(); namingFuzzyWatchRequest.setInitializing(namingFuzzyWatchContext.isInitializing()); namingFuzzyWatchRequest.setNamespace(namingGrpcClientProxy.getNamespaceId()); namingFuzzyWatchRequest.setReceivedGroupKeys(namingFuzzyWatchContext.getReceivedServiceKeys()); namingFuzzyWatchRequest.setGroupKeyPattern(namingFuzzyWatchContext.getGroupKeyPattern()); - if (namingFuzzyWatchContext.isDiscard()&&namingFuzzyWatchContext.getFuzzyWatchEventWatcherWrappers().isEmpty()){ + if (namingFuzzyWatchContext.isDiscard() && namingFuzzyWatchContext.getFuzzyWatchEventWatcherWrappers() + .isEmpty()) { namingFuzzyWatchRequest.setWatchType(WATCH_TYPE_CANCEL_WATCH); - }else{ + } else { namingFuzzyWatchRequest.setWatchType(WATCH_TYPE_WATCH); } return namingFuzzyWatchRequest; @@ -294,24 +311,24 @@ public Map getFuzzyMatchContextMap() { @Override public void onEvent(NamingFuzzyWatchNotifyEvent event) { - if (!event.scope().equals(notifierEventScope)){ - return; - } - + if (!event.scope().equals(notifierEventScope)) { + return; + } + String changedType = event.getChangedType(); - String syncType=event.getSyncType(); - + String syncType = event.getSyncType(); + String serviceKey = event.getServiceKey(); String pattern = event.getPattern(); - String watchUuid=event.getWatcherUuid(); + String watchUuid = event.getWatcherUuid(); NamingFuzzyWatchContext namingFuzzyWatchContext = fuzzyMatchContextMap.get(pattern); - if (namingFuzzyWatchContext==null){ - return ; + if (namingFuzzyWatchContext == null) { + return; } - namingFuzzyWatchContext.notifyFuzzyWatchers(serviceKey,changedType,syncType,watchUuid); + namingFuzzyWatchContext.notifyFuzzyWatchers(serviceKey, changedType, syncType, watchUuid); } - + @Override public Class subscribeType() { return NamingFuzzyWatchNotifyEvent.class; diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/event/NamingFuzzyWatchNotifyEvent.java b/client/src/main/java/com/alibaba/nacos/client/naming/event/NamingFuzzyWatchNotifyEvent.java index 6f6b7bbe706..072215f5bce 100644 --- a/client/src/main/java/com/alibaba/nacos/client/naming/event/NamingFuzzyWatchNotifyEvent.java +++ b/client/src/main/java/com/alibaba/nacos/client/naming/event/NamingFuzzyWatchNotifyEvent.java @@ -37,30 +37,30 @@ public class NamingFuzzyWatchNotifyEvent extends Event { private final String syncType; - private NamingFuzzyWatchNotifyEvent(String scope, String pattern, String serviceKey, String changedType, String syncType,String watcherUuid) { + private NamingFuzzyWatchNotifyEvent(String scope, String pattern, String serviceKey, String changedType, + String syncType, String watcherUuid) { this.scope = scope; this.pattern = pattern; this.serviceKey = serviceKey; this.changedType = changedType; this.syncType = syncType; - this.watcherUuid=watcherUuid; + this.watcherUuid = watcherUuid; } - public static NamingFuzzyWatchNotifyEvent build(String eventScope, String pattern, String serviceKey, String changedType, - String syncType) { - return new NamingFuzzyWatchNotifyEvent(eventScope, pattern, serviceKey, changedType, syncType,null); + public static NamingFuzzyWatchNotifyEvent build(String eventScope, String pattern, String serviceKey, + String changedType, String syncType) { + return new NamingFuzzyWatchNotifyEvent(eventScope, pattern, serviceKey, changedType, syncType, null); } - public static NamingFuzzyWatchNotifyEvent build(String eventScope, String pattern, String serviceKey, String changedType, - String syncType,String watcherUuid) { - return new NamingFuzzyWatchNotifyEvent(eventScope, pattern, serviceKey, changedType, syncType,watcherUuid); + public static NamingFuzzyWatchNotifyEvent build(String eventScope, String pattern, String serviceKey, + String changedType, String syncType, String watcherUuid) { + return new NamingFuzzyWatchNotifyEvent(eventScope, pattern, serviceKey, changedType, syncType, watcherUuid); } public String getPattern() { return pattern; } - public String getChangedType() { return changedType; } diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/remote/NamingClientProxyDelegate.java b/client/src/main/java/com/alibaba/nacos/client/naming/remote/NamingClientProxyDelegate.java index b741a8f7f7f..bea1ee38ee9 100644 --- a/client/src/main/java/com/alibaba/nacos/client/naming/remote/NamingClientProxyDelegate.java +++ b/client/src/main/java/com/alibaba/nacos/client/naming/remote/NamingClientProxyDelegate.java @@ -25,8 +25,8 @@ import com.alibaba.nacos.api.naming.utils.NamingUtils; import com.alibaba.nacos.api.selector.AbstractSelector; import com.alibaba.nacos.client.env.NacosClientProperties; -import com.alibaba.nacos.client.naming.cache.ServiceInfoHolder; import com.alibaba.nacos.client.naming.cache.NamingFuzzyWatchServiceListHolder; +import com.alibaba.nacos.client.naming.cache.ServiceInfoHolder; import com.alibaba.nacos.client.naming.core.NamingServerListManager; import com.alibaba.nacos.client.naming.core.ServiceInfoUpdateService; import com.alibaba.nacos.client.naming.event.InstancesChangeNotifier; @@ -68,9 +68,9 @@ public class NamingClientProxyDelegate implements NamingClientProxy { private ScheduledExecutorService executorService; - public NamingClientProxyDelegate(String namespace, ServiceInfoHolder serviceInfoHolder, - NacosClientProperties properties, InstancesChangeNotifier changeNotifier,NamingFuzzyWatchServiceListHolder namingFuzzyWatchServiceListHolder) throws NacosException { + NacosClientProperties properties, InstancesChangeNotifier changeNotifier, + NamingFuzzyWatchServiceListHolder namingFuzzyWatchServiceListHolder) throws NacosException { this.serviceInfoUpdateService = new ServiceInfoUpdateService(properties, serviceInfoHolder, this, changeNotifier); this.serverListManager = new NamingServerListManager(properties, namespace); @@ -195,7 +195,8 @@ public boolean serverHealthy() { } private NamingClientProxy getExecuteClientProxy(Instance instance) { - if (instance.isEphemeral() || grpcClientProxy.isAbilitySupportedByServer(AbilityKey.SERVER_SUPPORT_PERSISTENT_INSTANCE_BY_GRPC)) { + if (instance.isEphemeral() || grpcClientProxy.isAbilitySupportedByServer( + AbilityKey.SERVER_SUPPORT_PERSISTENT_INSTANCE_BY_GRPC)) { return grpcClientProxy; } return httpClientProxy; 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 d4589ede22a..aa40d048a01 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 @@ -23,8 +23,8 @@ import com.alibaba.nacos.api.naming.utils.NamingUtils; import com.alibaba.nacos.api.remote.request.Request; import com.alibaba.nacos.api.remote.response.Response; -import com.alibaba.nacos.client.naming.cache.NamingFuzzyWatchServiceListHolder; 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.common.notify.NotifyCenter; import com.alibaba.nacos.common.remote.client.Connection; @@ -35,72 +35,84 @@ import static com.alibaba.nacos.api.common.Constants.FUZZY_WATCH_RESOURCE_CHANGED; +/** + * handle fuzzy watch request from server. + * @author shiyiyue + */ public class NamingFuzzyWatchNotifyRequestHandler implements ServerRequestHandler { - NamingFuzzyWatchServiceListHolder namingFuzzyWatchServiceListHolder; - public NamingFuzzyWatchNotifyRequestHandler(NamingFuzzyWatchServiceListHolder namingFuzzyWatchServiceListHolder){ + public NamingFuzzyWatchNotifyRequestHandler(NamingFuzzyWatchServiceListHolder namingFuzzyWatchServiceListHolder) { this.namingFuzzyWatchServiceListHolder = namingFuzzyWatchServiceListHolder; - NotifyCenter.registerToPublisher(NamingFuzzyWatchNotifyEvent.class,1000); + NotifyCenter.registerToPublisher(NamingFuzzyWatchNotifyEvent.class, 1000); } - + @Override public Response requestReply(Request request, Connection connection) { if (request instanceof NamingFuzzyWatchSyncRequest) { NamingFuzzyWatchSyncRequest watchNotifySyncRequest = (NamingFuzzyWatchSyncRequest) request; - NamingFuzzyWatchContext namingFuzzyWatchContext = namingFuzzyWatchServiceListHolder.getFuzzyMatchContextMap().get(watchNotifySyncRequest.getGroupKeyPattern()); - if (namingFuzzyWatchContext!=null){ + NamingFuzzyWatchContext namingFuzzyWatchContext = namingFuzzyWatchServiceListHolder.getFuzzyMatchContextMap() + .get(watchNotifySyncRequest.getGroupKeyPattern()); + if (namingFuzzyWatchContext != null) { Collection serviceKeys = watchNotifySyncRequest.getContexts(); - if (watchNotifySyncRequest.getSyncType().equals(Constants.FUZZY_WATCH_INIT_NOTIFY)||watchNotifySyncRequest.getSyncType().equals(Constants.FUZZY_WATCH_DIFF_SYNC_NOTIFY)){ + if (watchNotifySyncRequest.getSyncType().equals(Constants.FUZZY_WATCH_INIT_NOTIFY) + || watchNotifySyncRequest.getSyncType().equals(Constants.FUZZY_WATCH_DIFF_SYNC_NOTIFY)) { for (NamingFuzzyWatchSyncRequest.Context serviceKey : serviceKeys) { // may have a 'change event' sent to client before 'init event' if (namingFuzzyWatchContext.addReceivedServiceKey(serviceKey.getServiceKey())) { - NotifyCenter.publishEvent(NamingFuzzyWatchNotifyEvent.build(namingFuzzyWatchServiceListHolder.getNotifierEventScope(), - watchNotifySyncRequest.getGroupKeyPattern(),serviceKey.getServiceKey(),serviceKey.getChangedType(), watchNotifySyncRequest.getSyncType())); + NotifyCenter.publishEvent(NamingFuzzyWatchNotifyEvent.build( + namingFuzzyWatchServiceListHolder.getNotifierEventScope(), + watchNotifySyncRequest.getGroupKeyPattern(), serviceKey.getServiceKey(), + serviceKey.getChangedType(), watchNotifySyncRequest.getSyncType())); } } - }else if(watchNotifySyncRequest.getSyncType().equals(Constants.FINISH_FUZZY_WATCH_INIT_NOTIFY)){ + } else if (watchNotifySyncRequest.getSyncType().equals(Constants.FINISH_FUZZY_WATCH_INIT_NOTIFY)) { namingFuzzyWatchContext.markInitializationComplete(); } } return new NamingFuzzyWatchChangeNotifyResponse(); - + } else if (request instanceof NamingFuzzyWatchChangeNotifyRequest) { NamingFuzzyWatchChangeNotifyRequest notifyChangeRequest = (NamingFuzzyWatchChangeNotifyRequest) request; String[] serviceKeyItems = NamingUtils.parseServiceKey(notifyChangeRequest.getServiceKey()); - String namespace=serviceKeyItems[0]; - String groupName=serviceKeyItems[1]; - String serviceName=serviceKeyItems[2]; - + String namespace = serviceKeyItems[0]; + String groupName = serviceKeyItems[1]; + String serviceName = serviceKeyItems[2]; + Collection matchedPattern = FuzzyGroupKeyPattern.filterMatchedPatterns( - namingFuzzyWatchServiceListHolder.getFuzzyMatchContextMap().keySet(),serviceName,groupName,namespace); + namingFuzzyWatchServiceListHolder.getFuzzyMatchContextMap().keySet(), serviceName, groupName, + namespace); String serviceChangeType = notifyChangeRequest.getChangedType(); - + switch (serviceChangeType) { case Constants.ServiceChangedType.ADD_SERVICE: case Constants.ServiceChangedType.INSTANCE_CHANGED: for (String pattern : matchedPattern) { - NamingFuzzyWatchContext namingFuzzyWatchContext = namingFuzzyWatchServiceListHolder.getFuzzyMatchContextMap().get(pattern); - if (namingFuzzyWatchContext != null && namingFuzzyWatchContext.addReceivedServiceKey(((NamingFuzzyWatchChangeNotifyRequest) request).getServiceKey())) { + NamingFuzzyWatchContext namingFuzzyWatchContext = namingFuzzyWatchServiceListHolder.getFuzzyMatchContextMap() + .get(pattern); + if (namingFuzzyWatchContext != null && namingFuzzyWatchContext.addReceivedServiceKey( + ((NamingFuzzyWatchChangeNotifyRequest) request).getServiceKey())) { //publish local service add event - NotifyCenter.publishEvent( - NamingFuzzyWatchNotifyEvent.build(namingFuzzyWatchServiceListHolder.getNotifierEventScope(), - pattern,notifyChangeRequest.getServiceKey(),Constants.ServiceChangedType.ADD_SERVICE, - FUZZY_WATCH_RESOURCE_CHANGED)); + NotifyCenter.publishEvent(NamingFuzzyWatchNotifyEvent.build( + namingFuzzyWatchServiceListHolder.getNotifierEventScope(), pattern, + notifyChangeRequest.getServiceKey(), Constants.ServiceChangedType.ADD_SERVICE, + FUZZY_WATCH_RESOURCE_CHANGED)); } } break; case Constants.ServiceChangedType.DELETE_SERVICE: for (String pattern : matchedPattern) { - NamingFuzzyWatchContext namingFuzzyWatchContext = namingFuzzyWatchServiceListHolder.getFuzzyMatchContextMap().get(pattern); - if (namingFuzzyWatchContext != null && namingFuzzyWatchContext.removeReceivedServiceKey(notifyChangeRequest.getServiceKey())) { - NotifyCenter.publishEvent( - NamingFuzzyWatchNotifyEvent.build(namingFuzzyWatchServiceListHolder.getNotifierEventScope(), - pattern, notifyChangeRequest.getServiceKey(),Constants.ServiceChangedType.DELETE_SERVICE, - FUZZY_WATCH_RESOURCE_CHANGED)); + NamingFuzzyWatchContext namingFuzzyWatchContext = namingFuzzyWatchServiceListHolder.getFuzzyMatchContextMap() + .get(pattern); + if (namingFuzzyWatchContext != null && namingFuzzyWatchContext.removeReceivedServiceKey( + notifyChangeRequest.getServiceKey())) { + NotifyCenter.publishEvent(NamingFuzzyWatchNotifyEvent.build( + namingFuzzyWatchServiceListHolder.getNotifierEventScope(), pattern, + notifyChangeRequest.getServiceKey(), Constants.ServiceChangedType.DELETE_SERVICE, + FUZZY_WATCH_RESOURCE_CHANGED)); } } break; diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/NamingGrpcClientProxy.java b/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/NamingGrpcClientProxy.java index a223f911770..29d7e480486 100644 --- a/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/NamingGrpcClientProxy.java +++ b/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/NamingGrpcClientProxy.java @@ -28,8 +28,8 @@ import com.alibaba.nacos.api.naming.remote.NamingRemoteConstants; import com.alibaba.nacos.api.naming.remote.request.AbstractNamingRequest; import com.alibaba.nacos.api.naming.remote.request.BatchInstanceRequest; -import com.alibaba.nacos.api.naming.remote.request.NamingFuzzyWatchRequest; import com.alibaba.nacos.api.naming.remote.request.InstanceRequest; +import com.alibaba.nacos.api.naming.remote.request.NamingFuzzyWatchRequest; import com.alibaba.nacos.api.naming.remote.request.PersistentInstanceRequest; import com.alibaba.nacos.api.naming.remote.request.ServiceListRequest; import com.alibaba.nacos.api.naming.remote.request.ServiceQueryRequest; @@ -46,11 +46,11 @@ import com.alibaba.nacos.api.remote.response.ResponseCode; import com.alibaba.nacos.api.selector.AbstractSelector; import com.alibaba.nacos.api.selector.SelectorType; +import com.alibaba.nacos.client.address.ServerListChangeEvent; import com.alibaba.nacos.client.env.NacosClientProperties; import com.alibaba.nacos.client.monitor.MetricsMonitor; import com.alibaba.nacos.client.naming.cache.NamingFuzzyWatchServiceListHolder; import com.alibaba.nacos.client.naming.cache.ServiceInfoHolder; -import com.alibaba.nacos.client.address.ServerListChangeEvent; import com.alibaba.nacos.client.naming.remote.AbstractNamingClientProxy; import com.alibaba.nacos.client.naming.remote.gprc.redo.NamingGrpcRedoService; import com.alibaba.nacos.client.naming.remote.gprc.redo.data.BatchInstanceRedoData; @@ -121,8 +121,8 @@ private void start(ServerListFactory serverListFactory, ServiceInfoHolder servic rpcClient.serverListFactory(serverListFactory); rpcClient.registerConnectionListener(redoService); rpcClient.registerServerRequestHandler(new NamingPushRequestHandler(serviceInfoHolder)); - rpcClient.registerServerRequestHandler(new NamingFuzzyWatchNotifyRequestHandler( - namingFuzzyWatchServiceListHolder)); + rpcClient.registerServerRequestHandler( + new NamingFuzzyWatchNotifyRequestHandler(namingFuzzyWatchServiceListHolder)); rpcClient.start(); namingFuzzyWatchServiceListHolder.start(); NotifyCenter.registerSubscriber(this); @@ -278,9 +278,8 @@ public void doRegisterServiceForPersistent(String serviceName, String groupName, @Override public void deregisterService(String serviceName, String groupName, Instance instance) throws NacosException { - NAMING_LOGGER - .info("[DEREGISTER-SERVICE] {} deregistering service {} with instance: {}", namespaceId, serviceName, - instance); + NAMING_LOGGER.info("[DEREGISTER-SERVICE] {} deregistering service {} with instance: {}", namespaceId, + serviceName, instance); if (instance.isEphemeral()) { deregisterServiceForEphemeral(serviceName, groupName, instance); } else { @@ -454,22 +453,23 @@ public boolean isAbilitySupportedByServer(AbilityKey abilityKey) { * @param namingFuzzyWatchRequest namingFuzzyWatchRequest * @throws NacosException nacos exception */ - public NamingFuzzyWatchResponse fuzzyWatchRequest(NamingFuzzyWatchRequest namingFuzzyWatchRequest) throws NacosException { - return requestToServer(namingFuzzyWatchRequest, NamingFuzzyWatchResponse.class); + public NamingFuzzyWatchResponse fuzzyWatchRequest(NamingFuzzyWatchRequest namingFuzzyWatchRequest) + throws NacosException { + return requestToServer(namingFuzzyWatchRequest, NamingFuzzyWatchResponse.class); } - private T requestToServer(Request request, Class responseClass) - throws NacosException { + private T requestToServer(Request request, Class responseClass) throws NacosException { Response response = null; try { - if (request instanceof AbstractNamingRequest){ - request.putAllHeader( - getSecurityHeaders(((AbstractNamingRequest)request).getNamespace(), ((AbstractNamingRequest)request).getGroupName(), ((AbstractNamingRequest)request).getServiceName())); - }else if(request instanceof NamingFuzzyWatchRequest){ + if (request instanceof AbstractNamingRequest) { + request.putAllHeader(getSecurityHeaders(((AbstractNamingRequest) request).getNamespace(), + ((AbstractNamingRequest) request).getGroupName(), + ((AbstractNamingRequest) request).getServiceName())); + } else if (request instanceof NamingFuzzyWatchRequest) { request.putAllHeader( - getSecurityHeaders(((NamingFuzzyWatchRequest)request).getNamespace(),null, null)); - }else{ - throw new NacosException(400,"unknown naming request type"); + getSecurityHeaders(((NamingFuzzyWatchRequest) request).getNamespace(), null, null)); + } else { + throw new NacosException(400, "unknown naming request type"); } response = requestTimeout < 0 ? rpcClient.request(request) : rpcClient.request(request, requestTimeout); diff --git a/client/src/test/java/com/alibaba/nacos/client/config/NacosConfigServiceTest.java b/client/src/test/java/com/alibaba/nacos/client/config/NacosConfigServiceTest.java index 7f53968926e..6dcc4e368cf 100644 --- a/client/src/test/java/com/alibaba/nacos/client/config/NacosConfigServiceTest.java +++ b/client/src/test/java/com/alibaba/nacos/client/config/NacosConfigServiceTest.java @@ -99,10 +99,12 @@ void testGetConfigFromFailOver() throws NacosException { final String group = "2"; final String tenant = "public"; - MockedStatic localConfigInfoProcessorMockedStatic = Mockito.mockStatic(LocalConfigInfoProcessor.class); + MockedStatic localConfigInfoProcessorMockedStatic = Mockito.mockStatic( + LocalConfigInfoProcessor.class); try { String contentFailOver = "failOverContent" + System.currentTimeMillis(); - localConfigInfoProcessorMockedStatic.when(() -> LocalConfigInfoProcessor.getFailover(any(), eq(dataId), eq(group), eq(tenant))) + localConfigInfoProcessorMockedStatic.when( + () -> LocalConfigInfoProcessor.getFailover(any(), eq(dataId), eq(group), eq(tenant))) .thenReturn(contentFailOver); final int timeout = 3000; @@ -119,18 +121,22 @@ void testGetConfigFromLocalCache() throws NacosException { final String group = "2"; final String tenant = "public"; - MockedStatic localConfigInfoProcessorMockedStatic = Mockito.mockStatic(LocalConfigInfoProcessor.class); + MockedStatic localConfigInfoProcessorMockedStatic = Mockito.mockStatic( + LocalConfigInfoProcessor.class); try { String contentFailOver = "localCacheContent" + System.currentTimeMillis(); //fail over null - localConfigInfoProcessorMockedStatic.when(() -> LocalConfigInfoProcessor.getFailover(any(), eq(dataId), eq(group), eq(tenant))) + localConfigInfoProcessorMockedStatic.when( + () -> LocalConfigInfoProcessor.getFailover(any(), eq(dataId), eq(group), eq(tenant))) .thenReturn(null); //snapshot content - localConfigInfoProcessorMockedStatic.when(() -> LocalConfigInfoProcessor.getSnapshot(any(), eq(dataId), eq(group), eq(tenant))) + localConfigInfoProcessorMockedStatic.when( + () -> LocalConfigInfoProcessor.getSnapshot(any(), eq(dataId), eq(group), eq(tenant))) .thenReturn(contentFailOver); //form server error. final int timeout = 3000; - Mockito.when(mockWoker.getServerConfig(dataId, group, tenant, timeout, false)).thenThrow(new NacosException()); + Mockito.when(mockWoker.getServerConfig(dataId, group, tenant, timeout, false)) + .thenThrow(new NacosException()); final String config = nacosConfigService.getConfig(dataId, group, timeout); assertEquals(contentFailOver, config); @@ -146,10 +152,12 @@ void testGetConfig403() throws NacosException { final String group = "2"; final String tenant = "public"; - MockedStatic localConfigInfoProcessorMockedStatic = Mockito.mockStatic(LocalConfigInfoProcessor.class); + MockedStatic localConfigInfoProcessorMockedStatic = Mockito.mockStatic( + LocalConfigInfoProcessor.class); try { //fail over null - localConfigInfoProcessorMockedStatic.when(() -> LocalConfigInfoProcessor.getFailover(any(), eq(dataId), eq(group), eq(tenant))) + localConfigInfoProcessorMockedStatic.when( + () -> LocalConfigInfoProcessor.getFailover(any(), eq(dataId), eq(group), eq(tenant))) .thenReturn(null); //form server error. @@ -212,15 +220,14 @@ public void executeConfigListen() { // NOOP } - @Override public void removeCache(String dataId, String group) { // NOOP } - + @Override - public ConfigResponse queryConfig(String dataId, String group, String tenant, long readTimeous, boolean notify) - throws NacosException { + public ConfigResponse queryConfig(String dataId, String group, String tenant, long readTimeous, + boolean notify) throws NacosException { ConfigResponse configResponse = new ConfigResponse(); configResponse.setContent(content); configResponse.setDataId(dataId); @@ -230,8 +237,9 @@ public ConfigResponse queryConfig(String dataId, String group, String tenant, lo } @Override - public boolean publishConfig(String dataId, String group, String tenant, String appName, String tag, String betaIps, - String content, String encryptedDataKey, String casMd5, String type) throws NacosException { + public boolean publishConfig(String dataId, String group, String tenant, String appName, String tag, + String betaIps, String content, String encryptedDataKey, String casMd5, String type) + throws NacosException { return false; } @@ -244,12 +252,13 @@ public boolean removeConfig(String dataId, String group, String tenant, String t final String config = nacosConfigService.getConfigAndSignListener(dataId, group, timeout, listener); assertEquals(content, config); - + Mockito.verify(mockWoker, Mockito.times(1)) .addTenantListenersWithContent(dataId, group, content, null, Collections.singletonList(listener)); assertEquals(content, config); - Mockito.verify(mockWoker, Mockito.times(1)).addTenantListenersWithContent(dataId, group, content, null, Arrays.asList(listener)); + Mockito.verify(mockWoker, Mockito.times(1)) + .addTenantListenersWithContent(dataId, group, content, null, Arrays.asList(listener)); } @Test @@ -279,12 +288,14 @@ void testPublishConfig() throws NacosException { String content = "123"; String namespace = "public"; String type = ConfigType.getDefaultType().getType(); - Mockito.when(mockWoker.publishConfig(dataId, group, namespace, null, null, null, content, "", null, type)).thenReturn(true); + Mockito.when(mockWoker.publishConfig(dataId, group, namespace, null, null, null, content, "", null, type)) + .thenReturn(true); final boolean b = nacosConfigService.publishConfig(dataId, group, content); assertTrue(b); - Mockito.verify(mockWoker, Mockito.times(1)).publishConfig(dataId, group, namespace, null, null, null, content, "", null, type); + Mockito.verify(mockWoker, Mockito.times(1)) + .publishConfig(dataId, group, namespace, null, null, null, content, "", null, type); } @Test @@ -295,12 +306,14 @@ void testPublishConfig2() throws NacosException { String namespace = "public"; String type = ConfigType.PROPERTIES.getType(); - Mockito.when(mockWoker.publishConfig(dataId, group, namespace, null, null, null, content, "", null, type)).thenReturn(true); + Mockito.when(mockWoker.publishConfig(dataId, group, namespace, null, null, null, content, "", null, type)) + .thenReturn(true); final boolean b = nacosConfigService.publishConfig(dataId, group, content, type); assertTrue(b); - Mockito.verify(mockWoker, Mockito.times(1)).publishConfig(dataId, group, namespace, null, null, null, content, "", null, type); + Mockito.verify(mockWoker, Mockito.times(1)) + .publishConfig(dataId, group, namespace, null, null, null, content, "", null, type); } @Test @@ -312,12 +325,14 @@ void testPublishConfigCas() throws NacosException { String casMd5 = "96147704e3cb8be8597d55d75d244a02"; String type = ConfigType.getDefaultType().getType(); - Mockito.when(mockWoker.publishConfig(dataId, group, namespace, null, null, null, content, "", casMd5, type)).thenReturn(true); + Mockito.when(mockWoker.publishConfig(dataId, group, namespace, null, null, null, content, "", casMd5, type)) + .thenReturn(true); final boolean b = nacosConfigService.publishConfigCas(dataId, group, content, casMd5); assertTrue(b); - Mockito.verify(mockWoker, Mockito.times(1)).publishConfig(dataId, group, namespace, null, null, null, content, "", casMd5, type); + Mockito.verify(mockWoker, Mockito.times(1)) + .publishConfig(dataId, group, namespace, null, null, null, content, "", casMd5, type); } @Test @@ -329,12 +344,14 @@ void testPublishConfigCas2() throws NacosException { String casMd5 = "96147704e3cb8be8597d55d75d244a02"; String type = ConfigType.PROPERTIES.getType(); - Mockito.when(mockWoker.publishConfig(dataId, group, namespace, null, null, null, content, "", casMd5, type)).thenReturn(true); + Mockito.when(mockWoker.publishConfig(dataId, group, namespace, null, null, null, content, "", casMd5, type)) + .thenReturn(true); final boolean b = nacosConfigService.publishConfigCas(dataId, group, content, casMd5, type); assertTrue(b); - Mockito.verify(mockWoker, Mockito.times(1)).publishConfig(dataId, group, namespace, null, null, null, content, "", casMd5, type); + Mockito.verify(mockWoker, Mockito.times(1)) + .publishConfig(dataId, group, namespace, null, null, null, content, "", casMd5, type); } @Test diff --git a/client/src/test/java/com/alibaba/nacos/client/naming/remote/AbstractNamingClientProxyTest.java b/client/src/test/java/com/alibaba/nacos/client/naming/remote/AbstractNamingClientProxyTest.java index afb0104b8cc..cba87c980c2 100644 --- a/client/src/test/java/com/alibaba/nacos/client/naming/remote/AbstractNamingClientProxyTest.java +++ b/client/src/test/java/com/alibaba/nacos/client/naming/remote/AbstractNamingClientProxyTest.java @@ -25,9 +25,8 @@ import com.alibaba.nacos.api.naming.pojo.Service; import com.alibaba.nacos.api.naming.pojo.ServiceInfo; import com.alibaba.nacos.api.selector.AbstractSelector; -import com.alibaba.nacos.client.auth.ram.utils.SignUtil; import com.alibaba.nacos.client.address.ServerListChangeEvent; -import com.alibaba.nacos.client.naming.cache.NamingFuzzyWatchContext; +import com.alibaba.nacos.client.auth.ram.utils.SignUtil; import com.alibaba.nacos.client.security.SecurityProxy; import com.alibaba.nacos.client.utils.AppNameUtils; import com.alibaba.nacos.common.notify.Event; @@ -40,7 +39,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.concurrent.Future; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; diff --git a/client/src/test/java/com/alibaba/nacos/client/naming/remote/NamingClientProxyDelegateTest.java b/client/src/test/java/com/alibaba/nacos/client/naming/remote/NamingClientProxyDelegateTest.java index 5ab5ec969c6..7e1031cba34 100644 --- a/client/src/test/java/com/alibaba/nacos/client/naming/remote/NamingClientProxyDelegateTest.java +++ b/client/src/test/java/com/alibaba/nacos/client/naming/remote/NamingClientProxyDelegateTest.java @@ -27,8 +27,8 @@ import com.alibaba.nacos.api.selector.ExpressionSelector; import com.alibaba.nacos.api.selector.NoneSelector; import com.alibaba.nacos.client.env.NacosClientProperties; -import com.alibaba.nacos.client.naming.cache.ServiceInfoHolder; import com.alibaba.nacos.client.naming.cache.NamingFuzzyWatchServiceListHolder; +import com.alibaba.nacos.client.naming.cache.ServiceInfoHolder; import com.alibaba.nacos.client.naming.event.InstancesChangeNotifier; import com.alibaba.nacos.client.naming.remote.gprc.NamingGrpcClientProxy; import com.alibaba.nacos.client.naming.remote.http.NamingHttpClientProxy; @@ -68,7 +68,7 @@ class NamingClientProxyDelegateTest { @Mock NamingFuzzyWatchServiceListHolder namingFuzzyWatchServiceListHolder; - + NamingClientProxyDelegate delegate; InstancesChangeNotifier notifier; @@ -81,7 +81,8 @@ void setUp() throws NacosException, NoSuchFieldException, IllegalAccessException props.setProperty("serverAddr", "localhost"); nacosClientProperties = NacosClientProperties.PROTOTYPE.derive(props); notifier = new InstancesChangeNotifier(); - delegate = new NamingClientProxyDelegate(TEST_NAMESPACE, holder, nacosClientProperties, notifier,namingFuzzyWatchServiceListHolder); + delegate = new NamingClientProxyDelegate(TEST_NAMESPACE, holder, nacosClientProperties, notifier, + namingFuzzyWatchServiceListHolder); Field grpcClientProxyField = NamingClientProxyDelegate.class.getDeclaredField("grpcClientProxy"); grpcClientProxyField.setAccessible(true); grpcClientProxyField.set(delegate, mockGrpcClient); diff --git a/client/src/test/java/com/alibaba/nacos/client/naming/remote/gprc/redo/NamingGrpcRedoServiceTest.java b/client/src/test/java/com/alibaba/nacos/client/naming/remote/gprc/redo/NamingGrpcRedoServiceTest.java index 2608c89c284..60fe8c8eab8 100644 --- a/client/src/test/java/com/alibaba/nacos/client/naming/remote/gprc/redo/NamingGrpcRedoServiceTest.java +++ b/client/src/test/java/com/alibaba/nacos/client/naming/remote/gprc/redo/NamingGrpcRedoServiceTest.java @@ -59,6 +59,7 @@ class NamingGrpcRedoServiceTest { @Mock private NamingFuzzyWatchServiceListHolder namingFuzzyWatchServiceListHolder; + private NamingGrpcRedoService redoService; @BeforeEach diff --git a/common/src/main/java/com/alibaba/nacos/common/task/BatchTaskCounter.java b/common/src/main/java/com/alibaba/nacos/common/task/BatchTaskCounter.java index 07ee2ccb14a..1d049d14cb0 100644 --- a/common/src/main/java/com/alibaba/nacos/common/task/BatchTaskCounter.java +++ b/common/src/main/java/com/alibaba/nacos/common/task/BatchTaskCounter.java @@ -20,6 +20,11 @@ import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; +/** + * batch task counter. + * + * @author shiyiyue + */ public class BatchTaskCounter { List batchCounter; @@ -28,19 +33,31 @@ public BatchTaskCounter(int totalBatch) { initBatchCounter(totalBatch); } - public void initBatchCounter(int totalBatch) { + /** + * init counter. + * @param totalBatch totalBatch. + */ + private void initBatchCounter(int totalBatch) { batchCounter = new ArrayList<>(totalBatch); for (int i = 0; i < totalBatch; i++) { batchCounter.add(i, new AtomicBoolean(false)); } } + /** + * set bath succeed. + * @param batch succeed batch. + */ public void batchSuccess(int batch) { if (batch <= batchCounter.size()) { batchCounter.get(batch - 1).set(true); } } + /** + * check all completed. + * @return + */ public boolean batchCompleted() { for (AtomicBoolean atomicBoolean : batchCounter) { if (!atomicBoolean.get()) { @@ -50,7 +67,7 @@ public boolean batchCompleted() { return true; } - public int getTotalBatch(){ + public int getTotalBatch() { return batchCounter.size(); } } diff --git a/common/src/main/java/com/alibaba/nacos/common/utils/FuzzyGroupKeyPattern.java b/common/src/main/java/com/alibaba/nacos/common/utils/FuzzyGroupKeyPattern.java index 2de49c6b85e..fae7accbd90 100644 --- a/common/src/main/java/com/alibaba/nacos/common/utils/FuzzyGroupKeyPattern.java +++ b/common/src/main/java/com/alibaba/nacos/common/utils/FuzzyGroupKeyPattern.java @@ -39,17 +39,16 @@ public class FuzzyGroupKeyPattern { /** - * Generates a fuzzy listen group key pattern based on the given dataId pattern, group, and optional tenant. - * pattern result as: fixNamespace>>groupPattern>>dataIdPattern + * Generates a fuzzy listen group key pattern based on the given dataId pattern, group, and optional tenant. pattern + * result as: fixNamespace>>groupPattern>>dataIdPattern * * @param resourcePattern The pattern for matching dataIds or service names. - * @param groupPattern The groupPattern associated with the groups. - * @param fixNamespace (Optional) The tenant associated with the dataIds (can be null or empty). + * @param groupPattern The groupPattern associated with the groups. + * @param fixNamespace (Optional) The tenant associated with the dataIds (can be null or empty). * @return A unique group key pattern for fuzzy listen. * @throws IllegalArgumentException If the dataId pattern or group is blank. */ - public static String generatePattern(final String resourcePattern, final String groupPattern, - String fixNamespace) { + public static String generatePattern(final String resourcePattern, final String groupPattern, String fixNamespace) { if (StringUtils.isBlank(resourcePattern)) { throw new IllegalArgumentException("Param 'resourcePattern' is illegal, resourcePattern is blank"); } @@ -57,7 +56,7 @@ public static String generatePattern(final String resourcePattern, final String throw new IllegalArgumentException("Param 'groupPattern' is illegal, group is blank"); } if (StringUtils.isBlank(fixNamespace)) { - fixNamespace=DEFAULT_NAMESPACE_ID; + fixNamespace = DEFAULT_NAMESPACE_ID; } StringBuilder sb = new StringBuilder(); sb.append(fixNamespace); @@ -68,95 +67,100 @@ public static String generatePattern(final String resourcePattern, final String return sb.toString().intern(); } - /** * Given a dataId, group, and a collection of completed group key patterns, returns the patterns that match. * - * @param resourceName The dataId or sservice name to match. + * @param resourceName The dataId or service name to match. * @param group The group to match. * @param namespace The group to match. * @param groupKeyPatterns The collection of completed group key patterns to match against. * @return A set of patterns that match the dataId and group. */ - public static Set filterMatchedPatterns(Collection groupKeyPatterns,String resourceName, String group, String namespace - ) { + public static Set filterMatchedPatterns(Collection groupKeyPatterns, String resourceName, + String group, String namespace) { if (CollectionUtils.isEmpty(groupKeyPatterns)) { return new HashSet<>(1); } Set matchedPatternList = new HashSet<>(); for (String keyPattern : groupKeyPatterns) { - if (matchPattern(keyPattern,resourceName, group,namespace)) { + if (matchPattern(keyPattern, resourceName, group, namespace)) { matchedPatternList.add(keyPattern); } } return matchedPatternList; } - - - public static boolean matchPattern(String groupKeyPattern,String resourceName,String group,String namespace){ - if(StringUtils.isBlank(namespace)){ - namespace=DEFAULT_NAMESPACE_ID; + /** + * check if the resource match the groupKeyPattern. + * @param resourceName The dataId or service name to match. + * @param group The group to match. + * @param namespace The group to match. + * @param groupKeyPattern The pattern to match. + * @return matched or not. + */ + public static boolean matchPattern(String groupKeyPattern, String resourceName, String group, String namespace) { + if (StringUtils.isBlank(namespace)) { + namespace = DEFAULT_NAMESPACE_ID; } String[] splitPatterns = groupKeyPattern.split(FUZZY_WATCH_PATTERN_SPLITTER); - return splitPatterns[0].equals(namespace)&&itemMatched(splitPatterns[1],group)&&itemMatched(splitPatterns[2],resourceName); + return splitPatterns[0].equals(namespace) && itemMatched(splitPatterns[1], group) && itemMatched( + splitPatterns[2], resourceName); } - public static String getNamespaceFromPattern(String groupKeyPattern){ + public static String getNamespaceFromPattern(String groupKeyPattern) { return groupKeyPattern.split(FUZZY_WATCH_PATTERN_SPLITTER)[0]; } + /** - * - * @param pattern - * @param resource + * check pattern matched the resource. + * @param pattern pattern contain *. + * @param resource resource to check. * @return */ - private static boolean itemMatched(String pattern,String resource){ + private static boolean itemMatched(String pattern, String resource) { //accurate match without * - if (!pattern.contains(ALL_PATTERN)){ + if (!pattern.contains(ALL_PATTERN)) { return pattern.equals(resource); } - + //match for '*' pattern - if (pattern.equals(ALL_PATTERN)){ + if (pattern.equals(ALL_PATTERN)) { return true; } //match for *{string}* - if (pattern.startsWith(ALL_PATTERN)&&pattern.endsWith(ALL_PATTERN)){ - String pureString=pattern.replace(ALL_PATTERN,""); + if (pattern.startsWith(ALL_PATTERN) && pattern.endsWith(ALL_PATTERN)) { + String pureString = pattern.replace(ALL_PATTERN, ""); return resource.contains(pureString); } - + //match for postfix match *{string} - if (pattern.startsWith(ALL_PATTERN)){ - String pureString=pattern.replace(ALL_PATTERN,""); + if (pattern.startsWith(ALL_PATTERN)) { + String pureString = pattern.replace(ALL_PATTERN, ""); return resource.endsWith(pureString); } - + //match for prefix match {string}* - if (pattern.endsWith(ALL_PATTERN)){ - String pureString=pattern.replace(ALL_PATTERN,""); + if (pattern.endsWith(ALL_PATTERN)) { + String pureString = pattern.replace(ALL_PATTERN, ""); return resource.startsWith(pureString); } - + return false; } - /** * Calculates and merges the differences between the matched group keys and the client's existing group keys into a * list of ConfigState objects. * - * @param basedGroupKeys The matched group keys set + * @param basedGroupKeys The matched group keys set * @param followedGroupKeys The followed existing group keys set - * @return a different list of GroupKeyState objects representing the states which the followed sets should be added or removed - * GroupKeyState#exist true presents follow set should add,GroupKeyState#exist false presents follow set should removed. - * + * @return a different list of GroupKeyState objects representing the states which the followed sets should be added + * or removed GroupKeyState#exist true presents follow set should add,GroupKeyState#exist false presents follow set + * should removed. */ - public static List diffGroupKeys(Set basedGroupKeys, - Set followedGroupKeys) { + public static List diffGroupKeys(Set basedGroupKeys, Set followedGroupKeys) { // Calculate the set of group keys to be added and removed Set addGroupKeys = new HashSet<>(); if (CollectionUtils.isNotEmpty(basedGroupKeys)) { @@ -180,13 +184,12 @@ public static List diffGroupKeys(Set basedGroupKeys, .collect(Collectors.toList()); } - - public static class GroupKeyState{ + public static class GroupKeyState { String groupKey; boolean exist; - + /** * Constructs a new ConfigState instance with the given group key and existence flag. * @@ -197,7 +200,7 @@ public GroupKeyState(String groupKey, boolean exist) { this.groupKey = groupKey; this.exist = exist; } - + /** * Retrieves the group key associated with the configuration. * @@ -206,7 +209,7 @@ public GroupKeyState(String groupKey, boolean exist) { public String getGroupKey() { return groupKey; } - + /** * Sets the group key associated with the configuration. * @@ -215,7 +218,7 @@ public String getGroupKey() { public void setGroupKey(String groupKey) { this.groupKey = groupKey; } - + /** * Checks whether the configuration exists or not. * @@ -224,7 +227,7 @@ public void setGroupKey(String groupKey) { public boolean isExist() { return exist; } - + /** * Sets the existence flag of the configuration. * diff --git a/common/src/test/java/com/alibaba/nacos/common/utils/FuzzyGroupKeyPatternTest.java b/common/src/test/java/com/alibaba/nacos/common/utils/FuzzyGroupKeyPatternTest.java index 15d13250dc6..374d429eac0 100644 --- a/common/src/test/java/com/alibaba/nacos/common/utils/FuzzyGroupKeyPatternTest.java +++ b/common/src/test/java/com/alibaba/nacos/common/utils/FuzzyGroupKeyPatternTest.java @@ -19,7 +19,6 @@ import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; /** * GroupKeyPatternUtilsTest. @@ -40,6 +39,5 @@ public void testGetGroupKeyPattern() { assertEquals("exampleNamespace>>exampleGroup@@examplePattern*", groupKeyPattern); } - } 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 86e7c89e83a..b6af4a6f805 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 @@ -71,6 +71,7 @@ public int getBatchSize() { public void setBatchSize(int batchSize) { this.batchSize = batchSize; } + public boolean isDerbyOpsEnabled() { return derbyOpsEnabled; } 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 index d758ede377b..77fbace8cc4 100644 --- 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 @@ -18,8 +18,6 @@ import com.alibaba.nacos.common.notify.Event; -import java.util.Set; - /** * 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 @@ -45,10 +43,10 @@ public class ConfigCancelFuzzyWatchEvent extends Event { /** * Constructs a new ConfigBatchFuzzyListenEvent with the specified parameters. * - * @param connectionId ID of the client making the request - * @param groupKeyPattern Pattern for matching group keys + * @param connectionId ID of the client making the request + * @param groupKeyPattern Pattern for matching group keys */ - public ConfigCancelFuzzyWatchEvent(String connectionId, String groupKeyPattern) { + public ConfigCancelFuzzyWatchEvent(String connectionId, String groupKeyPattern) { this.connectionId = connectionId; this.groupKeyPattern = groupKeyPattern; } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigConnectionEventListener.java b/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigConnectionEventListener.java index 7c49201198b..a007b1c463b 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigConnectionEventListener.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigConnectionEventListener.java @@ -35,9 +35,10 @@ public class ConfigConnectionEventListener extends ClientConnectionEventListener final ConfigFuzzyWatchContextService configFuzzyWatchContextService; - public ConfigConnectionEventListener(ConfigChangeListenContext configChangeListenContext,ConfigFuzzyWatchContextService configFuzzyWatchContextService) { + public ConfigConnectionEventListener(ConfigChangeListenContext configChangeListenContext, + ConfigFuzzyWatchContextService configFuzzyWatchContextService) { this.configChangeListenContext = configChangeListenContext; - this.configFuzzyWatchContextService=configFuzzyWatchContextService; + this.configFuzzyWatchContextService = configFuzzyWatchContextService; } @Override 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 de54927ebdc..7478e7616c5 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 @@ -39,7 +39,6 @@ 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; /** @@ -65,11 +64,12 @@ public class ConfigFuzzyWatchChangeNotifier extends Subscriber @@ -52,14 +51,12 @@ * @date 2024/3/4 */ @Component -public class ConfigFuzzyWatchRequestHandler - extends RequestHandler { - +public class ConfigFuzzyWatchRequestHandler extends RequestHandler { private ConfigFuzzyWatchContextService configFuzzyWatchContextService; - public ConfigFuzzyWatchRequestHandler(ConfigFuzzyWatchContextService configFuzzyWatchContextService){ - this.configFuzzyWatchContextService=configFuzzyWatchContextService; + public ConfigFuzzyWatchRequestHandler(ConfigFuzzyWatchContextService configFuzzyWatchContextService) { + this.configFuzzyWatchContextService = configFuzzyWatchContextService; } /** @@ -77,23 +74,20 @@ public ConfigFuzzyWatchRequestHandler(ConfigFuzzyWatchContextService configFuzzy @Override @TpsControl(pointName = "ConfigFuzzyWatch") @Secured(action = ActionTypes.READ, signType = SignType.CONFIG) - @ExtractorManager.Extractor(rpcExtractor = ConfigBatchFuzzyListenRequestParamsExtractor.class) - public ConfigFuzzyWatchResponse handle(ConfigFuzzyWatchRequest request, RequestMeta meta) - throws NacosException { + @ExtractorManager.Extractor(rpcExtractor = ConfigFuzzyWatchRequestParamsExtractor.class) + public ConfigFuzzyWatchResponse handle(ConfigFuzzyWatchRequest request, RequestMeta meta) throws NacosException { String connectionId = StringPool.get(meta.getConnectionId()); - 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())); - } else if(WATCH_TYPE_CANCEL_WATCH.equals(request.getWatchType())) { - NotifyCenter.publishEvent( - new ConfigCancelFuzzyWatchEvent(connectionId, groupKeyPattern)); - } + 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())); + } else if (WATCH_TYPE_CANCEL_WATCH.equals(request.getWatchType())) { + NotifyCenter.publishEvent(new ConfigCancelFuzzyWatchEvent(connectionId, groupKeyPattern)); + } // Return response return new 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 83d64430cb6..4710cddb3a2 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 @@ -22,13 +22,12 @@ import com.alibaba.nacos.common.notify.Event; import com.alibaba.nacos.common.notify.NotifyCenter; import com.alibaba.nacos.common.notify.listener.SmartSubscriber; - import com.alibaba.nacos.common.task.BatchTaskCounter; 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.ConfigFuzzyWatchEvent; 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; import com.alibaba.nacos.core.remote.ConnectionManager; @@ -124,50 +123,54 @@ public void handleFuzzyWatchEvent(ConfigFuzzyWatchEvent event) { Set clientExistingGroupKeys = event.getClientExistingGroupKeys(); // Calculate and merge configuration states based on matched and existing group keys - List configStates = FuzzyGroupKeyPattern.diffGroupKeys(matchGroupKeys, clientExistingGroupKeys); - - if (CollectionUtils.isEmpty(configStates)){ - if (event.isInitializing()){ - ConfigFuzzyWatchSyncRequest request =ConfigFuzzyWatchSyncRequest.buildInitFinishRequest(event.getGroupKeyPattern()); + List configStates = FuzzyGroupKeyPattern.diffGroupKeys(matchGroupKeys, + clientExistingGroupKeys); + + if (CollectionUtils.isEmpty(configStates)) { + 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()); + FuzzyWatchRpcPushTask fuzzyWatchRpcPushTask = new FuzzyWatchRpcPushTask(request, null, + maxPushRetryTimes, event.getConnectionId()); push(fuzzyWatchRpcPushTask, connectionManager); } - - }else{ - String syncType =event.isInitializing()?FUZZY_WATCH_INIT_NOTIFY:FUZZY_WATCH_DIFF_SYNC_NOTIFY; - + + } else { + String syncType = event.isInitializing() ? FUZZY_WATCH_INIT_NOTIFY : FUZZY_WATCH_DIFF_SYNC_NOTIFY; + int batchSize = ConfigCommonConfig.getInstance().getBatchSize(); // Divide config states into batches - List> divideConfigStatesIntoBatches = divideConfigStatesIntoBatches(configStates, batchSize); - + List> divideConfigStatesIntoBatches = divideConfigStatesIntoBatches( + configStates, batchSize); + // Calculate the number of batches and initialize push batch finish count int totalBatch = divideConfigStatesIntoBatches.size(); - BatchTaskCounter batchTaskCounter=new BatchTaskCounter(divideConfigStatesIntoBatches.size()); - int currentBatch=1; + BatchTaskCounter batchTaskCounter = new BatchTaskCounter(divideConfigStatesIntoBatches.size()); + int currentBatch = 1; for (List configStateList : divideConfigStatesIntoBatches) { // Map config states to FuzzyListenNotifyDiffRequest.Context objects Set contexts = configStateList.stream().map(state -> { - + String changeType = state.isExist() ? Constants.ConfigChangedType.ADD_CONFIG : Constants.ConfigChangedType.DELETE_CONFIG; return ConfigFuzzyWatchSyncRequest.Context.build(state.getGroupKey(), changeType); }).collect(Collectors.toSet()); - - ConfigFuzzyWatchSyncRequest request =ConfigFuzzyWatchSyncRequest.buildSyncRequest(syncType,contexts,event.getGroupKeyPattern(),totalBatch,currentBatch); + + ConfigFuzzyWatchSyncRequest request = ConfigFuzzyWatchSyncRequest.buildSyncRequest(syncType, contexts, + 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()); + FuzzyWatchRpcPushTask fuzzyWatchRpcPushTask = new FuzzyWatchRpcPushTask(request, batchTaskCounter, + maxPushRetryTimes, event.getConnectionId()); push(fuzzyWatchRpcPushTask, connectionManager); currentBatch++; } } - } - @Override public List> subscribeTypes() { List> result = new LinkedList<>(); @@ -178,19 +181,18 @@ public List> subscribeTypes() { @Override public void onEvent(Event event) { - if (event instanceof ConfigFuzzyWatchEvent){ + if (event instanceof ConfigFuzzyWatchEvent) { handleFuzzyWatchEvent((ConfigFuzzyWatchEvent) event); } - - if (event instanceof ConfigCancelFuzzyWatchEvent){ + + if (event instanceof ConfigCancelFuzzyWatchEvent) { // Remove client from the fuzzy listening context configFuzzyWatchContextService.removeFuzzyListen(((ConfigCancelFuzzyWatchEvent) event).getGroupKeyPattern(), ((ConfigCancelFuzzyWatchEvent) event).getConnectionId()); } - + } - /** * Divides a collection of items into batches. * @@ -234,26 +236,18 @@ class FuzzyWatchRpcPushTask implements Runnable { */ String connectionId; - /** - * The IP address of the client. - */ - String clientIp; - - /** - * The name of the client's application. - */ - String appName; - 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 + * @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) { + public FuzzyWatchRpcPushTask(ConfigFuzzyWatchSyncRequest notifyRequest, BatchTaskCounter batchTaskCounter, + int maxRetryTimes, String connectionId) { this.notifyRequest = notifyRequest; this.batchTaskCounter = batchTaskCounter; this.maxRetryTimes = maxRetryTimes; @@ -282,7 +276,8 @@ public void run() { push(this, connectionManager); } else { rpcPushService.pushWithCallback(connectionId, notifyRequest, - new FuzzyWatchRpcPushCallback(this, tpsControlManager, connectionManager, batchTaskCounter), ConfigExecutor.getClientConfigNotifierServiceExecutor()); + new FuzzyWatchRpcPushCallback(this, tpsControlManager, connectionManager, batchTaskCounter), + ConfigExecutor.getClientConfigNotifierServiceExecutor()); } } } @@ -307,8 +302,8 @@ class FuzzyWatchRpcPushCallback extends AbstractPushCallBack { */ ConnectionManager connectionManager; - BatchTaskCounter batchTaskCounter; + /** * Constructs a new RpcPushCallback with the specified parameters. * @@ -318,12 +313,13 @@ class FuzzyWatchRpcPushCallback extends AbstractPushCallBack { * @param batchTaskCounter The batchTaskCounter counter */ public FuzzyWatchRpcPushCallback(FuzzyWatchRpcPushTask fuzzyWatchRpcPushTask, - TpsControlManager tpsControlManager, ConnectionManager connectionManager, BatchTaskCounter batchTaskCounter) { + TpsControlManager tpsControlManager, ConnectionManager connectionManager, + BatchTaskCounter batchTaskCounter) { super(3000L); this.fuzzyWatchRpcPushTask = fuzzyWatchRpcPushTask; this.tpsControlManager = tpsControlManager; this.connectionManager = connectionManager; - this.batchTaskCounter=batchTaskCounter; + this.batchTaskCounter = batchTaskCounter; } @@ -337,13 +333,14 @@ public void onSuccess() { tpsCheckRequest.setPointName(FUZZY_LISTEN_CONFIG_DIFF_PUSH_SUCCESS); tpsControlManager.check(tpsCheckRequest); - if (batchTaskCounter!=null){ + if (batchTaskCounter != null) { batchTaskCounter.batchSuccess(fuzzyWatchRpcPushTask.notifyRequest.getCurrentBatch()); - if (batchTaskCounter.batchCompleted()&&fuzzyWatchRpcPushTask.notifyRequest.getSyncType().equals(FUZZY_WATCH_INIT_NOTIFY)) { + 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); + push(new FuzzyWatchRpcPushTask(request, null, 50, fuzzyWatchRpcPushTask.connectionId), + connectionManager); } } 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 a318537a352..673622d8f61 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,7 +16,6 @@ package com.alibaba.nacos.config.server.service; - import com.alibaba.nacos.api.exception.runtime.NacosRuntimeException; import com.alibaba.nacos.common.utils.CollectionUtils; import com.alibaba.nacos.common.utils.FuzzyGroupKeyPattern; @@ -35,8 +34,14 @@ 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; +/** + * fuzzy watch context for config. + * + * @author shiyiyue + */ @Component public class ConfigFuzzyWatchContextService { @@ -50,10 +55,9 @@ public class ConfigFuzzyWatchContextService { */ private final Map> matchedGroupKeys = new ConcurrentHashMap<>(); - private final int FUZZY_WATCH_MAX_PATTERN_COUNT = 50; - - private final int FUZZY_WATCH_MAX_PATTERN_MATCHED_GROUP_KEY_COUNT = 1000; + 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() { @@ -61,10 +65,9 @@ public ConfigFuzzyWatchContextService() { } /** - * trim fuzzy watch context.
- * 1.remove watchedClients if watched client is empty. - * 2.remove matchedServiceKeys if watchedClients is null. - * pattern matchedServiceKeys will be removed in second period to avoid frequently matchedServiceKeys init. + * trim fuzzy watch context.
1.remove watchedClients if watched client is empty. 2.remove matchedServiceKeys + * if watchedClients is null. pattern matchedServiceKeys will be removed in second period to avoid frequently + * matchedServiceKeys init. */ private void trimFuzzyWatchContext() { Iterator>> iterator = matchedGroupKeys.entrySet().iterator(); @@ -74,44 +77,52 @@ private void trimFuzzyWatchContext() { if (watchedClients == null) { iterator.remove(); - LogUtil.DEFAULT_LOG.info("[fuzzy-watch] no watchedClients context for pattern {},remove matchedGroupKeys context",next.getKey()); + LogUtil.DEFAULT_LOG.info( + "[fuzzy-watch] no watchedClients context for pattern {},remove matchedGroupKeys context", + next.getKey()); } else if (watchedClients.isEmpty()) { - LogUtil.DEFAULT_LOG.info("[fuzzy-watch] no client watched pattern {},remove watchedClients context",next.getKey()); + LogUtil.DEFAULT_LOG.info("[fuzzy-watch] no client watched pattern {},remove watchedClients context", + next.getKey()); this.watchedClients.remove(next.getKey()); } } } - /** - * get matched exist group keys with the groupKeyPattern. return null if not matched + * get matched exist group keys with the groupKeyPattern. return null if not matched. * - * @param groupKeyPattern + * @param groupKeyPattern groupKeyPattern. * @return */ public Set matchGroupKeys(String groupKeyPattern) { return matchedGroupKeys.get(groupKeyPattern); } - - + /** + * sync group key change to fuzzy context. + * + * @param groupKey groupKey. + * @param changedType changedType. + * @return need notify ot not. + */ public boolean syncGroupKeyContext(String groupKey, String changedType) { - - boolean needNotify=false; - + + boolean needNotify = false; + String[] groupKeyItems = GroupKey.parseKey(groupKey); 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); while (iterator.hasNext()) { Map.Entry> entry = iterator.next(); if (FuzzyGroupKeyPattern.matchPattern(entry.getKey(), dataId, group, namespace)) { - if ((changedType.equals(ADD_CONFIG)||changedType.equals(CONFIG_CHANGED))&&entry.getValue().add(groupKey)) { + if (tryAdd && entry.getValue().add(groupKey)) { needNotify = true; } - - if (changedType.equals(DELETE_CONFIG)&&entry.getValue().remove(groupKey)) { + if (tryRemove && entry.getValue().remove(groupKey)) { needNotify = true; } } @@ -124,7 +135,6 @@ 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. - * @throws NacosRuntimeException, */ private void initMatchGroupKeys(String groupKeyPattern) { if (matchedGroupKeys.containsKey(groupKeyPattern)) { @@ -132,7 +142,8 @@ private void initMatchGroupKeys(String groupKeyPattern) { } if (matchedGroupKeys.size() >= FUZZY_WATCH_MAX_PATTERN_COUNT) { - LogUtil.DEFAULT_LOG.warn("[fuzzy-watch] pattern count is over limit ,pattern {} init fail,current count is {}", + 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()); @@ -140,19 +151,19 @@ private void initMatchGroupKeys(String groupKeyPattern) { matchedGroupKeys.computeIfAbsent(groupKeyPattern, k -> new HashSet<>()); Set matchedGroupKeys = this.matchedGroupKeys.get(groupKeyPattern); - long matchBeginTime=System.currentTimeMillis(); + long matchBeginTime = System.currentTimeMillis(); 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) { - 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()); - - break; -// throw new NacosRuntimeException(FUZZY_WATCH_PATTERN_MATCH_GROUP_KEY_OVER_LIMIT.getCode(), -// FUZZY_WATCH_PATTERN_MATCH_GROUP_KEY_OVER_LIMIT.getMsg()); + 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()); } matchedGroupKeys.add(groupKey); } @@ -199,7 +210,7 @@ public synchronized void removeFuzzyListen(String groupKeyPattern, String connec /** * remove watch context for connection id. * - * @param connectionId + * @param connectionId connection id. */ public void clearFuzzyWatchContext(String connectionId) { for (Map.Entry> keyPatternContextEntry : watchedClients.entrySet()) { @@ -221,12 +232,12 @@ public Set getMatchedClients(String groupKey) { Set connectIds = new HashSet<>(); // Iterate over each key pattern in the context Iterator>> watchClientIterator = watchedClients.entrySet().iterator(); - + String[] groupItems = GroupKey2.parseKey(groupKey); - while (watchClientIterator.hasNext()){ + while (watchClientIterator.hasNext()) { Map.Entry> watchClientEntry = watchClientIterator.next(); - + String keyPattern = watchClientEntry.getKey(); if (FuzzyGroupKeyPattern.matchPattern(keyPattern, groupItems[0], groupItems[1], groupItems[2])) { if (CollectionUtils.isNotEmpty(watchClientEntry.getValue())) { diff --git a/core/src/main/java/com/alibaba/nacos/core/paramcheck/impl/ConfigBatchFuzzyListenRequestParamsExtractor.java b/core/src/main/java/com/alibaba/nacos/core/paramcheck/impl/ConfigFuzzyWatchRequestParamsExtractor.java similarity index 75% rename from core/src/main/java/com/alibaba/nacos/core/paramcheck/impl/ConfigBatchFuzzyListenRequestParamsExtractor.java rename to core/src/main/java/com/alibaba/nacos/core/paramcheck/impl/ConfigFuzzyWatchRequestParamsExtractor.java index 99780ce5b91..d396986896e 100644 --- a/core/src/main/java/com/alibaba/nacos/core/paramcheck/impl/ConfigBatchFuzzyListenRequestParamsExtractor.java +++ b/core/src/main/java/com/alibaba/nacos/core/paramcheck/impl/ConfigFuzzyWatchRequestParamsExtractor.java @@ -20,23 +20,21 @@ import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.remote.request.Request; import com.alibaba.nacos.common.paramcheck.ParamInfo; -import com.alibaba.nacos.common.utils.CollectionUtils; import com.alibaba.nacos.common.utils.FuzzyGroupKeyPattern; import com.alibaba.nacos.core.paramcheck.AbstractRpcParamExtractor; import java.util.ArrayList; import java.util.List; -import java.util.Set; /** - * Extractor for parameters of {@link ConfigFuzzyWatchRequest}. This extractor retrieves parameter information - * from the request object and constructs {@link ParamInfo} instances representing the namespace ID, group, and data IDs + * Extractor for parameters of {@link ConfigFuzzyWatchRequest}. This extractor retrieves parameter information from the + * request object and constructs {@link ParamInfo} instances representing the namespace ID, group, and data IDs * contained in the request's contexts. * * @author stone-98 * @date 2024/3/5 */ -public class ConfigBatchFuzzyListenRequestParamsExtractor extends AbstractRpcParamExtractor { +public class ConfigFuzzyWatchRequestParamsExtractor extends AbstractRpcParamExtractor { /** * Extracts parameter information from the given request. @@ -49,10 +47,10 @@ public class ConfigBatchFuzzyListenRequestParamsExtractor extends AbstractRpcPar public List extractParam(Request request) throws NacosException { ConfigFuzzyWatchRequest req = (ConfigFuzzyWatchRequest) request; List paramInfos = new ArrayList<>(); - // Extract namespace ID and group from the context - ParamInfo paramInfo1 = new ParamInfo(); - paramInfo1.setNamespaceId(FuzzyGroupKeyPattern.getNamespaceFromPattern(req.getGroupKeyPattern())); - paramInfos.add(paramInfo1); + // Extract namespace ID and group from the context + ParamInfo paramInfo1 = new ParamInfo(); + paramInfo1.setNamespaceId(FuzzyGroupKeyPattern.getNamespaceFromPattern(req.getGroupKeyPattern())); + paramInfos.add(paramInfo1); return paramInfos; } } diff --git a/example/src/main/java/com/alibaba/nacos/example/FuzzyListenExample.java b/example/src/main/java/com/alibaba/nacos/example/ConfigFuzzyWatchExample.java similarity index 93% rename from example/src/main/java/com/alibaba/nacos/example/FuzzyListenExample.java rename to example/src/main/java/com/alibaba/nacos/example/ConfigFuzzyWatchExample.java index b3978a76b4c..a1cf4bb3d24 100644 --- a/example/src/main/java/com/alibaba/nacos/example/FuzzyListenExample.java +++ b/example/src/main/java/com/alibaba/nacos/example/ConfigFuzzyWatchExample.java @@ -18,7 +18,8 @@ import com.alibaba.nacos.api.config.ConfigFactory; import com.alibaba.nacos.api.config.ConfigService; -import com.alibaba.nacos.api.config.listener.ConfigFuzzyWatcher; +import com.alibaba.nacos.api.config.listener.AbstractFuzzyWatchEventWatcher; +import com.alibaba.nacos.api.config.listener.FuzzyWatchEventWatcher; import com.alibaba.nacos.api.config.listener.ConfigFuzzyWatchChangeEvent; import com.alibaba.nacos.api.exception.NacosException; @@ -47,7 +48,7 @@ * @author stone-98 * @date 2024/3/14 */ -public class FuzzyListenExample { +public class ConfigFuzzyWatchExample { public static void main(String[] args) throws NacosException, InterruptedException { // Set up properties for Nacos Config Service @@ -66,7 +67,7 @@ public static void main(String[] args) throws NacosException, InterruptedExcepti } // Define a fuzzy listener to handle configuration changes - ConfigFuzzyWatcher listener = new ConfigFuzzyWatcher() { + FuzzyWatchEventWatcher listener = new AbstractFuzzyWatchEventWatcher() { @Override public void onEvent(ConfigFuzzyWatchChangeEvent event) { System.out.println("[fuzzy listen config change]" + event.toString()); diff --git a/example/src/main/java/com/alibaba/nacos/example/FuzzyWatchExample.java b/example/src/main/java/com/alibaba/nacos/example/NamingFuzzyWatchExample.java similarity index 89% rename from example/src/main/java/com/alibaba/nacos/example/FuzzyWatchExample.java rename to example/src/main/java/com/alibaba/nacos/example/NamingFuzzyWatchExample.java index 4754eecd0da..c7efac16a9b 100644 --- a/example/src/main/java/com/alibaba/nacos/example/FuzzyWatchExample.java +++ b/example/src/main/java/com/alibaba/nacos/example/NamingFuzzyWatchExample.java @@ -19,8 +19,8 @@ import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.naming.NamingFactory; import com.alibaba.nacos.api.naming.NamingService; -import com.alibaba.nacos.api.naming.listener.FuzzyWatchEventWatcher; import com.alibaba.nacos.api.naming.listener.FuzzyWatchChangeEvent; +import com.alibaba.nacos.api.naming.listener.FuzzyWatchEventWatcher; import com.alibaba.nacos.api.naming.utils.NamingUtils; import java.util.Properties; @@ -38,7 +38,7 @@ * * @author tanyongquan */ -public class FuzzyWatchExample { +public class NamingFuzzyWatchExample { public static void main(String[] args) throws NacosException, InterruptedException { @@ -74,7 +74,9 @@ public Executor getExecutor() { @Override public void onEvent(FuzzyWatchChangeEvent event) { - System.out.println("[Fuzzy-Watch-GROUP]changed service name: " + NamingUtils.getServiceKey(event.getNamespace(),event.getGroupName(),event.getServiceName())); + System.out.println( + "[Fuzzy-Watch-GROUP]changed service name: " + NamingUtils.getServiceKey(event.getNamespace(), + event.getGroupName(), event.getServiceName())); System.out.println("[Fuzzy-Watch-GROUP]change type: " + event.getChangeType()); } }); @@ -88,7 +90,9 @@ public Executor getExecutor() { @Override public void onEvent(FuzzyWatchChangeEvent event) { - System.out.println("[Prefix-Fuzzy-Watch]changed service name: " + NamingUtils.getServiceKey(event.getNamespace(),event.getGroupName(),event.getServiceName())); + System.out.println( + "[Prefix-Fuzzy-Watch]changed service name: " + NamingUtils.getServiceKey(event.getNamespace(), + event.getGroupName(), event.getServiceName())); System.out.println("[Prefix-Fuzzy-Watch]change type: " + event.getChangeType()); } }); 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 9c9c41ba82d..b08d2d0c496 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 @@ -102,45 +102,46 @@ public ClientUnsubscribeServiceEvent(Service service, String clientId) { public static class ClientFuzzyWatchEvent extends ClientOperationEvent { private static final long serialVersionUID = -4518919987813223119L; - + /** - * client watched pattern + * client watched pattern. */ private final String groupKeyPattern; - + /** * client side received group keys. */ private Set clientReceivedServiceKeys; - + /** - * is fuzzy watch initializing + * is fuzzy watch initializing. */ private boolean isInitializing; - - public ClientFuzzyWatchEvent(String groupKeyPattern, String clientId,Set clientReceivedServiceKeys,boolean isInitializing) { + + public ClientFuzzyWatchEvent(String groupKeyPattern, String clientId, Set clientReceivedServiceKeys, + boolean isInitializing) { super(clientId, null); this.groupKeyPattern = groupKeyPattern; this.clientReceivedServiceKeys = clientReceivedServiceKeys; - this.isInitializing=isInitializing; + this.isInitializing = isInitializing; } public String getGroupKeyPattern() { return groupKeyPattern; } - + 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; } @@ -166,9 +167,9 @@ public String getPattern() { } public static class ClientReleaseEvent extends ClientOperationEvent { - + private static final long serialVersionUID = -281486927726245701L; - + private final Client client; private final boolean isNative; @@ -178,11 +179,11 @@ public ClientReleaseEvent(Client client, boolean isNative) { this.client = client; this.isNative = isNative; } - + public Client getClient() { return client; } - + public boolean isNative() { return isNative; } diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/event/service/ServiceEvent.java b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/event/service/ServiceEvent.java index 18b40a4f9da..9530f95b377 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/event/service/ServiceEvent.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/event/service/ServiceEvent.java @@ -19,8 +19,6 @@ import com.alibaba.nacos.common.notify.Event; import com.alibaba.nacos.naming.core.v2.pojo.Service; -import java.util.Set; - /** * Service event. * @@ -74,14 +72,14 @@ public String getChangedType() { public static class ServiceSubscribedEvent extends ServiceEvent { private static final long serialVersionUID = -2645441445867337345L; - + private final String clientId; public ServiceSubscribedEvent(Service service, String clientId) { super(service); this.clientId = clientId; } - + public String getClientId() { return clientId; } diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/index/ClientServiceIndexesManager.java b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/index/ClientServiceIndexesManager.java index 61b6607638a..2f3bae809c1 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/index/ClientServiceIndexesManager.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/index/ClientServiceIndexesManager.java @@ -50,7 +50,7 @@ public class ClientServiceIndexesManager extends SmartSubscriber { private final ConcurrentMap> subscriberIndexes = new ConcurrentHashMap<>(); - public ClientServiceIndexesManager(){ + public ClientServiceIndexesManager() { NotifyCenter.registerSubscriber(this, NamingEventPublisherFactory.getInstance()); } @@ -102,15 +102,15 @@ private void handleClientDisconnect(ClientOperationEvent.ClientReleaseEvent even for (Service each : client.getAllSubscribeService()) { removeSubscriberIndexes(each, client.getClientId()); } - DeregisterInstanceReason reason = event.isNative() - ? DeregisterInstanceReason.NATIVE_DISCONNECTED : DeregisterInstanceReason.SYNCED_DISCONNECTED; + DeregisterInstanceReason reason = event.isNative() ? DeregisterInstanceReason.NATIVE_DISCONNECTED + : DeregisterInstanceReason.SYNCED_DISCONNECTED; long currentTimeMillis = System.currentTimeMillis(); for (Service each : client.getAllPublishedService()) { removePublisherIndexes(each, client.getClientId()); InstancePublishInfo instance = client.getInstancePublishInfo(each); - NotifyCenter.publishEvent(new DeregisterInstanceTraceEvent(currentTimeMillis, - "", false, reason, each.getNamespace(), each.getGroup(), each.getName(), - instance.getIp(), instance.getPort())); + NotifyCenter.publishEvent( + new DeregisterInstanceTraceEvent(currentTimeMillis, "", false, reason, each.getNamespace(), + each.getGroup(), each.getName(), instance.getIp(), instance.getPort())); } } @@ -141,8 +141,8 @@ private void addPublisherIndexes(Service service, String clientId) { private void removePublisherIndexes(Service service, String clientId) { publisherIndexes.computeIfPresent(service, (s, ids) -> { ids.remove(clientId); - String serviceChangedType = ids.isEmpty() ? Constants.ServiceChangedType.DELETE_SERVICE : - Constants.ServiceChangedType.INSTANCE_CHANGED; + String serviceChangedType = ids.isEmpty() ? Constants.ServiceChangedType.DELETE_SERVICE + : Constants.ServiceChangedType.INSTANCE_CHANGED; NotifyCenter.publishEvent(new ServiceEvent.ServiceChangedEvent(service, serviceChangedType, true)); return ids.isEmpty() ? null : ids; }); 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 7d8d997e308..6f0b0cd5c55 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 @@ -41,13 +41,14 @@ 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; /** - * naming fuzzy watch context service - *

- * 1. handler client fuzzy watch event and cancel watch event 2. service changed notify fuzzy watched clients. + * naming fuzzy watch context service. + * + * @author shiyiyue */ @Component public class NamingFuzzyWatchContextService extends SmartSubscriber { @@ -64,10 +65,9 @@ public class NamingFuzzyWatchContextService extends SmartSubscriber { */ private final ConcurrentMap> matchedServiceKeys = new ConcurrentHashMap<>(); - private final int FUZZY_WATCH_MAX_PATTERN_COUNT = 50; - - private final int FUZZY_WATCH_MAX_PATTERN_MATCHED_GROUP_KEY_COUNT = 200; + private static final int FUZZY_WATCH_MAX_PATTERN_COUNT = 50; + private static final int FUZZY_WATCH_MAX_PATTERN_MATCHED_GROUP_KEY_COUNT = 200; public NamingFuzzyWatchContextService() { GlobalExecutor.scheduleWithFixDelayByCommon(() -> trimFuzzyWatchContext(), 30000); @@ -112,7 +112,12 @@ public void onEvent(Event event) { } } - + /** + * get client that fuzzy watch this service. + * + * @param service service to check fuzzy watcher. + * @return client ids. + */ public Set getFuzzyWatchedClients(Service service) { Set matchedClients = new HashSet<>(); Iterator>> iterator = watchedClients.entrySet().iterator(); @@ -126,15 +131,22 @@ public Set getFuzzyWatchedClients(Service service) { return matchedClients; } - public boolean syncServiceContext(Service changedEventService, String changedType) { + /** + * sync changed service to fuzzy watch context. + * + * @param changedService changed service. + * @param changedType change type. + * @return + */ + public boolean syncServiceContext(Service changedService, String changedType) { boolean needNotify = false; if (!changedType.equals(ADD_SERVICE) && !changedType.equals(DELETE_SERVICE)) { return false; } - String serviceKey = NamingUtils.getServiceKey(changedEventService.getNamespace(), - changedEventService.getGroup(), changedEventService.getName()); + String serviceKey = NamingUtils.getServiceKey(changedService.getNamespace(), changedService.getGroup(), + changedService.getName()); Loggers.SRV_LOG.warn("FUZZY_WATCH: service change matched,service key {},changed type {} ", serviceKey, changedType); @@ -142,15 +154,15 @@ public boolean syncServiceContext(Service changedEventService, String changedTyp while (iterator.hasNext()) { Map.Entry> next = iterator.next(); - if (FuzzyGroupKeyPattern.matchPattern(next.getKey(), changedEventService.getName(), - changedEventService.getGroup(), changedEventService.getNamespace())) { + 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() >= FUZZY_WATCH_MAX_PATTERN_MATCHED_GROUP_KEY_COUNT) { - 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()); + 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)) { @@ -171,8 +183,14 @@ public boolean syncServiceContext(Service changedEventService, String changedTyp return needNotify; } + /** + * sync fuzzy watch context. + * + * @param groupKeyPattern group key pattern. + * @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; @@ -186,8 +204,13 @@ private void removeFuzzyWatchContext(String clientId) { } } + /** + * remove fuzzy watch context for a pattern and client id. + * + * @param groupKeyPattern group key pattern. + * @param clientId client id. + */ public void removeFuzzyWatchContext(String groupKeyPattern, String clientId) { - if (watchedClients.containsKey(groupKeyPattern)) { watchedClients.get(groupKeyPattern).remove(clientId); } @@ -220,12 +243,11 @@ public Set initWatchMatchService(String completedPattern) { service.getNamespace())) { if (matchedServices.size() >= FUZZY_WATCH_MAX_PATTERN_MATCHED_GROUP_KEY_COUNT) { - Loggers.SRV_LOG.warn( - "FUZZY_WATCH: pattern matched service count is over limit , other services will stop notify for pattern {} ,current count is {}", + 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()); - break; - // throw new NacosRuntimeException(FUZZY_WATCH_PATTERN_MATCH_GROUP_KEY_OVER_LIMIT.getCode(), - // FUZZY_WATCH_PATTERN_MATCH_GROUP_KEY_OVER_LIMIT.getMsg()); + throw new NacosRuntimeException(FUZZY_WATCH_PATTERN_MATCH_GROUP_KEY_OVER_LIMIT.getCode(), + FUZZY_WATCH_PATTERN_MATCH_GROUP_KEY_OVER_LIMIT.getMsg()); } matchedServices.add( NamingUtils.getServiceKey(service.getNamespace(), service.getGroup(), service.getName())); @@ -240,5 +262,4 @@ public Set initWatchMatchService(String completedPattern) { return new HashSet(matchedServiceKeys.get(completedPattern)); } - } diff --git a/naming/src/main/java/com/alibaba/nacos/naming/push/NamingFuzzyWatchChangeNotifier.java b/naming/src/main/java/com/alibaba/nacos/naming/push/NamingFuzzyWatchChangeNotifier.java index 177d8e44eba..28a693f4435 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/push/NamingFuzzyWatchChangeNotifier.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/push/NamingFuzzyWatchChangeNotifier.java @@ -33,18 +33,21 @@ import java.util.Set; /** - * service change notify for fuzzy watch + * service change notify for fuzzy watch. + * + * @author shiyiyue */ @Service public class NamingFuzzyWatchChangeNotifier extends SmartSubscriber { private NamingFuzzyWatchContextService namingFuzzyWatchContextService; - private FuzzyWatchPushDelayTaskEngine fuzzyWatchPushDelayTaskEngine; + private FuzzyWatchPushDelayTaskEngine fuzzyWatchPushDelayTaskEngine; - public NamingFuzzyWatchChangeNotifier(NamingFuzzyWatchContextService namingFuzzyWatchContextService,FuzzyWatchPushDelayTaskEngine fuzzyWatchPushDelayTaskEngine){ - this.fuzzyWatchPushDelayTaskEngine=fuzzyWatchPushDelayTaskEngine; - this.namingFuzzyWatchContextService=namingFuzzyWatchContextService; + public NamingFuzzyWatchChangeNotifier(NamingFuzzyWatchContextService namingFuzzyWatchContextService, + FuzzyWatchPushDelayTaskEngine fuzzyWatchPushDelayTaskEngine) { + this.fuzzyWatchPushDelayTaskEngine = fuzzyWatchPushDelayTaskEngine; + this.namingFuzzyWatchContextService = namingFuzzyWatchContextService; NotifyCenter.registerSubscriber(this); } @@ -58,23 +61,25 @@ public List> subscribeTypes() { @Override public void onEvent(Event event) { if (event instanceof ServiceEvent.ServiceChangedEvent) { - ServiceEvent.ServiceChangedEvent serviceChangedEvent=(ServiceEvent.ServiceChangedEvent)event; - if(namingFuzzyWatchContextService.syncServiceContext(serviceChangedEvent.getService(),serviceChangedEvent.getChangedType())){ - generateFuzzyWatchChangeNotifyTask(serviceChangedEvent.getService(),serviceChangedEvent.getChangedType()); + ServiceEvent.ServiceChangedEvent serviceChangedEvent = (ServiceEvent.ServiceChangedEvent) event; + if (namingFuzzyWatchContextService.syncServiceContext(serviceChangedEvent.getService(), + serviceChangedEvent.getChangedType())) { + generateFuzzyWatchChangeNotifyTask(serviceChangedEvent.getService(), + serviceChangedEvent.getChangedType()); } } } - private void generateFuzzyWatchChangeNotifyTask(com.alibaba.nacos.naming.core.v2.pojo.Service service, String changedType) { - - String serviceKey = NamingUtils.getServiceKey(service.getNamespace(), service.getGroup(), - service.getName()); + private void generateFuzzyWatchChangeNotifyTask(com.alibaba.nacos.naming.core.v2.pojo.Service service, + String changedType) { + + String serviceKey = NamingUtils.getServiceKey(service.getNamespace(), service.getGroup(), service.getName()); Set fuzzyWatchedClients = namingFuzzyWatchContextService.getFuzzyWatchedClients(service); - - Loggers.SRV_LOG.info("FUZZY_WATCH:serviceKey {} has {} clients fuzzy watched" , - serviceKey,fuzzyWatchedClients==null?0:fuzzyWatchedClients.size()); + + Loggers.SRV_LOG.info("FUZZY_WATCH:serviceKey {} has {} clients fuzzy watched", serviceKey, + fuzzyWatchedClients == null ? 0 : fuzzyWatchedClients.size()); // watch notify push task specify by service - for (String clientId :fuzzyWatchedClients) { + for (String clientId : fuzzyWatchedClients) { FuzzyWatchChangeNotifyTask fuzzyWatchChangeNotifyTask = new FuzzyWatchChangeNotifyTask(serviceKey, changedType, clientId, PushConfig.getInstance().getPushTaskDelay()); fuzzyWatchPushDelayTaskEngine.addTask(FuzzyWatchPushDelayTaskEngine.getTaskKey(fuzzyWatchChangeNotifyTask), 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 13115d37976..c9e8f897257 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 @@ -26,8 +26,8 @@ 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.PushConfig; -import com.alibaba.nacos.naming.push.v2.task.FuzzyWatchSyncNotifyTask; import com.alibaba.nacos.naming.push.v2.task.FuzzyWatchPushDelayTaskEngine; +import com.alibaba.nacos.naming.push.v2.task.FuzzyWatchSyncNotifyTask; import org.springframework.stereotype.Service; import java.util.Collection; @@ -43,78 +43,87 @@ import static com.alibaba.nacos.api.common.Constants.ServiceChangedType.DELETE_SERVICE; /** - * fuzzy watch event for fuzzy watch + * fuzzy watch event for fuzzy watch. + * @author shiyiyue */ @Service public class NamingFuzzyWatchSyncNotifier extends SmartSubscriber { - private NamingFuzzyWatchContextService namingFuzzyWatchContextService; - private FuzzyWatchPushDelayTaskEngine fuzzyWatchPushDelayTaskEngine; + private FuzzyWatchPushDelayTaskEngine fuzzyWatchPushDelayTaskEngine; - private static final int batchSize=10; + private static final int BATCH_SIZE = 10; - - public NamingFuzzyWatchSyncNotifier(NamingFuzzyWatchContextService namingFuzzyWatchContextService,FuzzyWatchPushDelayTaskEngine fuzzyWatchPushDelayTaskEngine){ - this.namingFuzzyWatchContextService=namingFuzzyWatchContextService; - this.fuzzyWatchPushDelayTaskEngine=fuzzyWatchPushDelayTaskEngine; + public NamingFuzzyWatchSyncNotifier(NamingFuzzyWatchContextService namingFuzzyWatchContextService, + FuzzyWatchPushDelayTaskEngine fuzzyWatchPushDelayTaskEngine) { + this.namingFuzzyWatchContextService = namingFuzzyWatchContextService; + this.fuzzyWatchPushDelayTaskEngine = fuzzyWatchPushDelayTaskEngine; NotifyCenter.registerSubscriber(this); } @Override public List> subscribeTypes() { List> result = new LinkedList<>(); - result.add(ClientOperationEvent.ClientFuzzyWatchEvent.class); - result.add(ClientOperationEvent.ClientCancelFuzzyWatchEvent.class); - return result; + result.add(ClientOperationEvent.ClientFuzzyWatchEvent.class); + result.add(ClientOperationEvent.ClientCancelFuzzyWatchEvent.class); + return result; } @Override public void onEvent(Event event) { - if (event instanceof ClientOperationEvent.ClientFuzzyWatchEvent) { - //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()); - } - + if (event instanceof ClientOperationEvent.ClientFuzzyWatchEvent) { + //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 ){ + private void handleClientFuzzyWatchEvent(ClientOperationEvent.ClientFuzzyWatchEvent clientFuzzyWatchEvent) { String completedPattern = clientFuzzyWatchEvent.getGroupKeyPattern(); - + //sync fuzzy watch context - Set patternMatchedServiceKeys = namingFuzzyWatchContextService.syncFuzzyWatcherContext(completedPattern, clientFuzzyWatchEvent.getClientId()); + Set patternMatchedServiceKeys = namingFuzzyWatchContextService.syncFuzzyWatcherContext(completedPattern, + clientFuzzyWatchEvent.getClientId()); Set clientReceivedGroupKeys = clientFuzzyWatchEvent.getClientReceivedServiceKeys(); List groupKeyStates = FuzzyGroupKeyPattern.diffGroupKeys( patternMatchedServiceKeys, clientReceivedGroupKeys); Set syncContext = convert(groupKeyStates); - String syncType =clientFuzzyWatchEvent.isInitializing()?FUZZY_WATCH_INIT_NOTIFY:FUZZY_WATCH_DIFF_SYNC_NOTIFY; - - if(CollectionUtils.isNotEmpty(groupKeyStates)){ + String syncType = + clientFuzzyWatchEvent.isInitializing() ? FUZZY_WATCH_INIT_NOTIFY : FUZZY_WATCH_DIFF_SYNC_NOTIFY; + + if (CollectionUtils.isNotEmpty(groupKeyStates)) { Set> dividedServices = divideServiceByBatch(syncContext); - BatchTaskCounter batchTaskCounter=new BatchTaskCounter(dividedServices.size()); - int currentBatch=1; + BatchTaskCounter batchTaskCounter = new BatchTaskCounter(dividedServices.size()); + int currentBatch = 1; for (Set batchData : dividedServices) { - FuzzyWatchSyncNotifyTask fuzzyWatchSyncNotifyTask=new FuzzyWatchSyncNotifyTask(clientFuzzyWatchEvent.getClientId(),completedPattern,syncType,batchData,PushConfig.getInstance().getPushTaskRetryDelay()); + FuzzyWatchSyncNotifyTask fuzzyWatchSyncNotifyTask = new FuzzyWatchSyncNotifyTask( + clientFuzzyWatchEvent.getClientId(), completedPattern, syncType, batchData, + PushConfig.getInstance().getPushTaskRetryDelay()); fuzzyWatchSyncNotifyTask.setBatchTaskCounter(batchTaskCounter); fuzzyWatchSyncNotifyTask.setTotalBatch(dividedServices.size()); fuzzyWatchSyncNotifyTask.setCurrentBatch(currentBatch); - fuzzyWatchPushDelayTaskEngine.addTask(FuzzyWatchPushDelayTaskEngine.getTaskKey(fuzzyWatchSyncNotifyTask),fuzzyWatchSyncNotifyTask); + fuzzyWatchPushDelayTaskEngine.addTask( + FuzzyWatchPushDelayTaskEngine.getTaskKey(fuzzyWatchSyncNotifyTask), fuzzyWatchSyncNotifyTask); currentBatch++; } - }else if (FUZZY_WATCH_INIT_NOTIFY.equals(syncType)){ - FuzzyWatchSyncNotifyTask fuzzyWatchSyncNotifyTask=new FuzzyWatchSyncNotifyTask(clientFuzzyWatchEvent.getClientId(),completedPattern,FINISH_FUZZY_WATCH_INIT_NOTIFY,null,PushConfig.getInstance().getPushTaskRetryDelay()); - fuzzyWatchPushDelayTaskEngine.addTask(FuzzyWatchPushDelayTaskEngine.getTaskKey(fuzzyWatchSyncNotifyTask),fuzzyWatchSyncNotifyTask); - + } else if (FUZZY_WATCH_INIT_NOTIFY.equals(syncType)) { + FuzzyWatchSyncNotifyTask fuzzyWatchSyncNotifyTask = new FuzzyWatchSyncNotifyTask( + clientFuzzyWatchEvent.getClientId(), completedPattern, FINISH_FUZZY_WATCH_INIT_NOTIFY, null, + PushConfig.getInstance().getPushTaskRetryDelay()); + fuzzyWatchPushDelayTaskEngine.addTask(FuzzyWatchPushDelayTaskEngine.getTaskKey(fuzzyWatchSyncNotifyTask), + fuzzyWatchSyncNotifyTask); + } } + private Set> divideServiceByBatch( Collection matchedService) { Set> result = new HashSet<>(); @@ -124,7 +133,7 @@ private Set> divideServiceByBatch( Set currentBatch = new HashSet<>(); for (NamingFuzzyWatchSyncRequest.Context groupedServiceName : matchedService) { currentBatch.add(groupedServiceName); - if (currentBatch.size() >= this.batchSize) { + if (currentBatch.size() >= this.BATCH_SIZE) { result.add(currentBatch); currentBatch = new HashSet<>(); } @@ -135,13 +144,13 @@ private Set> divideServiceByBatch( return result; } - private Set convert(List diffGroupKeys){ - Set syncContext=new HashSet<>(); - for(FuzzyGroupKeyPattern.GroupKeyState groupKeyState:diffGroupKeys){ - NamingFuzzyWatchSyncRequest.Context context=new NamingFuzzyWatchSyncRequest.Context(); + private Set convert(List diffGroupKeys) { + Set syncContext = new HashSet<>(); + for (FuzzyGroupKeyPattern.GroupKeyState groupKeyState : diffGroupKeys) { + NamingFuzzyWatchSyncRequest.Context context = new NamingFuzzyWatchSyncRequest.Context(); context.setServiceKey(groupKeyState.getGroupKey()); - context.setChangedType(groupKeyState.isExist()?ADD_SERVICE:DELETE_SERVICE); - syncContext.add(context) ; + context.setChangedType(groupKeyState.isExist() ? ADD_SERVICE : DELETE_SERVICE); + syncContext.add(context); } return syncContext; } 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 9db80d57abd..46cabbec2e7 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 @@ -65,7 +65,6 @@ private class FuzzyWatchChangeNotifyCallback implements PushCallBack { private String changedType; - private FuzzyWatchChangeNotifyCallback(String clientId, String serviceKey, String changedType) { this.clientId = clientId; this.serviceKey = serviceKey; @@ -81,15 +80,15 @@ public long getTimeout() { public void onSuccess() { Loggers.PUSH.info("[FUZZY-WATCH] change notify success ,clientId {}, serviceKey {] ,changedType {} ", clientId, clientId, changedType); - + } @Override public void onFail(Throwable e) { - - Loggers.PUSH.warn("[FUZZY-WATCH] change notify fail ,clientId {}, serviceKey {] ,changedType {} ", - clientId, clientId, changedType,e); - + + 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, diff --git a/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchChangeNotifyTask.java b/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchChangeNotifyTask.java index 317d98ce48c..502cccdd9d8 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchChangeNotifyTask.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchChangeNotifyTask.java @@ -18,13 +18,12 @@ import com.alibaba.nacos.common.task.AbstractDelayTask; - /** * Nacos naming fuzzy watch notify service change push delay task. * * @author tanyongquan */ -public class FuzzyWatchChangeNotifyTask extends AbstractDelayTask{ +public class FuzzyWatchChangeNotifyTask extends AbstractDelayTask { private final String serviceKey; @@ -34,14 +33,14 @@ public class FuzzyWatchChangeNotifyTask extends AbstractDelayTask{ private final long delay; - public FuzzyWatchChangeNotifyTask(String serviceKey, String changedType,String clientId,long delay) { + public FuzzyWatchChangeNotifyTask(String serviceKey, String changedType, String clientId, long delay) { this.serviceKey = serviceKey; this.changedType = changedType; - this.delay=delay; - this.clientId=clientId; - + this.delay = delay; + this.clientId = clientId; + } - + public String getChangedType() { return changedType; } diff --git a/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchPushDelayTaskEngine.java b/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchPushDelayTaskEngine.java index e1da09d700f..94e64d42f43 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchPushDelayTaskEngine.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchPushDelayTaskEngine.java @@ -20,7 +20,6 @@ import com.alibaba.nacos.common.task.NacosTask; import com.alibaba.nacos.common.task.NacosTaskProcessor; import com.alibaba.nacos.common.task.engine.NacosDelayTaskExecuteEngine; - import com.alibaba.nacos.naming.misc.Loggers; import com.alibaba.nacos.naming.misc.NamingExecuteTaskDispatcher; import com.alibaba.nacos.naming.misc.SwitchDomain; @@ -36,20 +35,17 @@ @Component public class FuzzyWatchPushDelayTaskEngine extends NacosDelayTaskExecuteEngine { - private final PushExecutorDelegate pushExecutor; private final SwitchDomain switchDomain; - public FuzzyWatchPushDelayTaskEngine( - PushExecutorDelegate pushExecutor, - SwitchDomain switchDomain) { + public FuzzyWatchPushDelayTaskEngine(PushExecutorDelegate pushExecutor, SwitchDomain switchDomain) { super(FuzzyWatchPushDelayTaskEngine.class.getSimpleName(), Loggers.PUSH); this.pushExecutor = pushExecutor; this.switchDomain = switchDomain; setDefaultTaskProcessor(new WatchPushDelayTaskProcessor(this)); } - + public PushExecutor getPushExecutor() { return pushExecutor; } @@ -62,7 +58,6 @@ protected void processTasks() { super.processTasks(); } - private static class WatchPushDelayTaskProcessor implements NacosTaskProcessor { private final FuzzyWatchPushDelayTaskEngine fuzzyWatchPushExecuteEngine; @@ -77,31 +72,34 @@ public boolean process(NacosTask task) { if (task instanceof FuzzyWatchChangeNotifyTask) { //process fuzzy watch change notify when a service changed FuzzyWatchChangeNotifyTask fuzzyWatchChangeNotifyTask = (FuzzyWatchChangeNotifyTask) task; - NamingExecuteTaskDispatcher.getInstance().dispatchAndExecuteTask( - getTaskKey(task), - new FuzzyWatchChangeNotifyExecuteTask(fuzzyWatchPushExecuteEngine, fuzzyWatchChangeNotifyTask.getServiceKey(), - fuzzyWatchChangeNotifyTask.getChangedType(), fuzzyWatchChangeNotifyTask.getClientId())); + NamingExecuteTaskDispatcher.getInstance().dispatchAndExecuteTask(getTaskKey(task), + new FuzzyWatchChangeNotifyExecuteTask(fuzzyWatchPushExecuteEngine, + fuzzyWatchChangeNotifyTask.getServiceKey(), fuzzyWatchChangeNotifyTask.getChangedType(), + fuzzyWatchChangeNotifyTask.getClientId())); } else if (task instanceof FuzzyWatchSyncNotifyTask) { - //process fuzzy watch init notify when a new client fuzzy watch a pattern + //process fuzzy watch sync notify when a new client fuzzy watch a pattern FuzzyWatchSyncNotifyTask fuzzyWatchSyncNotifyTask = (FuzzyWatchSyncNotifyTask) task; String pattern = fuzzyWatchSyncNotifyTask.getPattern(); String clientId = fuzzyWatchSyncNotifyTask.getClientId(); NamingExecuteTaskDispatcher.getInstance().dispatchAndExecuteTask(getTaskKey(task), - new FuzzyWatchSyncNotifyExecuteTask(clientId, pattern, - fuzzyWatchPushExecuteEngine, fuzzyWatchSyncNotifyTask)); + new FuzzyWatchSyncNotifyExecuteTask(clientId, pattern, fuzzyWatchPushExecuteEngine, + fuzzyWatchSyncNotifyTask)); } return true; } } - public static String getTaskKey(NacosTask task){ - if(task instanceof FuzzyWatchChangeNotifyTask){ - return "fwcnT-"+((FuzzyWatchChangeNotifyTask) task).getClientId()+((FuzzyWatchChangeNotifyTask) task).getServiceKey(); - }else if (task instanceof FuzzyWatchSyncNotifyTask){ - return "fwsnT-"+((FuzzyWatchSyncNotifyTask) task).getSyncType()+"-"+((FuzzyWatchSyncNotifyTask) task).getClientId()+((FuzzyWatchSyncNotifyTask) task).getPattern()+"-"+((FuzzyWatchSyncNotifyTask) task).getCurrentBatch(); - }else { - throw new NacosRuntimeException(500,"unknown fuzzy task type"); + public static String getTaskKey(NacosTask task) { + if (task instanceof FuzzyWatchChangeNotifyTask) { + return "fwcnT-" + ((FuzzyWatchChangeNotifyTask) task).getClientId() + + ((FuzzyWatchChangeNotifyTask) task).getServiceKey(); + } else if (task instanceof FuzzyWatchSyncNotifyTask) { + return "fwsnT-" + ((FuzzyWatchSyncNotifyTask) task).getSyncType() + "-" + + ((FuzzyWatchSyncNotifyTask) task).getClientId() + ((FuzzyWatchSyncNotifyTask) task).getPattern() + + "-" + ((FuzzyWatchSyncNotifyTask) task).getCurrentBatch(); + } else { + throw new NacosRuntimeException(500, "unknown fuzzy task type"); } } } 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 5588f89718a..243cb6336a8 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 @@ -19,10 +19,11 @@ 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 com.alibaba.nacos.common.task.BatchTaskCounter; + 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; @@ -42,8 +43,8 @@ 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. + * 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; @@ -51,7 +52,7 @@ public class FuzzyWatchSyncNotifyExecuteTask extends AbstractExecuteTask { * TODO set batch size from config. */ - public FuzzyWatchSyncNotifyExecuteTask( String clientId, String pattern, + public FuzzyWatchSyncNotifyExecuteTask(String clientId, String pattern, FuzzyWatchPushDelayTaskEngine fuzzyWatchPushDelayTaskEngine, FuzzyWatchSyncNotifyTask delayTask) { this.clientId = clientId; this.pattern = pattern; @@ -64,28 +65,30 @@ public FuzzyWatchSyncNotifyExecuteTask( String clientId, String pattern, public void run() { NamingFuzzyWatchSyncRequest namingFuzzyWatchSyncRequest = NamingFuzzyWatchSyncRequest.buildSyncNotifyRequest( - pattern, delayTask.getSyncType(), delayTask.getSyncServiceKeys(), delayTask.getTotalBatch(), delayTask.getCurrentBatch()); - fuzzyWatchPushDelayTaskEngine.getPushExecutor().doFuzzyWatchNotifyPushWithCallBack(clientId ,namingFuzzyWatchSyncRequest, - new FuzzyWatchSyncNotifyExecuteTask.FuzzyWatchSyncNotifyCallback(namingFuzzyWatchSyncRequest, delayTask.getBatchTaskCounter())); + pattern, delayTask.getSyncType(), delayTask.getSyncServiceKeys(), delayTask.getTotalBatch(), + delayTask.getCurrentBatch()); + fuzzyWatchPushDelayTaskEngine.getPushExecutor() + .doFuzzyWatchNotifyPushWithCallBack(clientId, namingFuzzyWatchSyncRequest, + new FuzzyWatchSyncNotifyExecuteTask.FuzzyWatchSyncNotifyCallback(namingFuzzyWatchSyncRequest, + delayTask.getBatchTaskCounter())); } - private class FuzzyWatchSyncNotifyCallback implements PushCallBack { - private NamingFuzzyWatchSyncRequest namingFuzzyWatchSyncRequest; + private NamingFuzzyWatchSyncRequest namingFuzzyWatchSyncRequest; /** * Record the push task execute start time. */ private final long executeStartTime; - private BatchTaskCounter batchTaskCounter ; + private BatchTaskCounter batchTaskCounter; private FuzzyWatchSyncNotifyCallback(NamingFuzzyWatchSyncRequest namingFuzzyWatchSyncRequest, BatchTaskCounter batchTaskCounter) { - this.namingFuzzyWatchSyncRequest=namingFuzzyWatchSyncRequest; + this.namingFuzzyWatchSyncRequest = namingFuzzyWatchSyncRequest; this.executeStartTime = System.currentTimeMillis(); - this.batchTaskCounter=batchTaskCounter; + this.batchTaskCounter = batchTaskCounter; } @Override @@ -100,37 +103,44 @@ public void onSuccess() { 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); + 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()); + 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()){ + 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())); + fuzzyWatchPushDelayTaskEngine.addTask(System.currentTimeMillis(), + new FuzzyWatchSyncNotifyTask(clientId, pattern, FINISH_FUZZY_WATCH_INIT_NOTIFY, null, + PushConfig.getInstance().getPushTaskDelay())); } } - + } } - private boolean isFinishInitTask(){ + private boolean isFinishInitTask() { return FINISH_FUZZY_WATCH_INIT_NOTIFY.equals(namingFuzzyWatchSyncRequest.getSyncType()); } - - private boolean isInitNotifyTask(){ + + 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); + 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 @@ -139,11 +149,10 @@ public void onFail(Throwable e) { PushConfig.getInstance().getPushTaskRetryDelay()); fuzzyWatchSyncNotifyTask.setBatchTaskCounter(batchTaskCounter); - fuzzyWatchPushDelayTaskEngine.addTask(System.currentTimeMillis(),fuzzyWatchSyncNotifyTask); + fuzzyWatchPushDelayTaskEngine.addTask(System.currentTimeMillis(), fuzzyWatchSyncNotifyTask); } } } - } \ 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 a9b95a6cfff..fa4c633f206 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 @@ -21,7 +21,6 @@ import com.alibaba.nacos.common.task.BatchTaskCounter; import com.alibaba.nacos.naming.misc.Loggers; - import java.util.HashSet; import java.util.Set; @@ -46,14 +45,15 @@ public class FuzzyWatchSyncNotifyTask extends AbstractDelayTask { private BatchTaskCounter batchTaskCounter; - public FuzzyWatchSyncNotifyTask(String clientId, String pattern, String syncType, Set syncServiceKeys, long delay) { + public FuzzyWatchSyncNotifyTask(String clientId, String pattern, String syncType, + Set syncServiceKeys, long delay) { this.clientId = clientId; this.pattern = pattern; - this.syncType=syncType; - if (syncServiceKeys!=null) { + this.syncType = syncType; + if (syncServiceKeys != null) { this.syncServiceKeys = syncServiceKeys; - }else{ - this.syncServiceKeys=new HashSet<>(); + } else { + this.syncServiceKeys = new HashSet<>(); } setTaskInterval(delay); setLastProcessTime(System.currentTimeMillis()); @@ -82,7 +82,7 @@ public void merge(AbstractDelayTask task) { } FuzzyWatchSyncNotifyTask oldTask = (FuzzyWatchSyncNotifyTask) task; - if (oldTask.getSyncServiceKeys()!=null){ + if (oldTask.getSyncServiceKeys() != null) { syncServiceKeys.addAll(oldTask.getSyncServiceKeys()); } setLastProcessTime(Math.min(getLastProcessTime(), task.getLastProcessTime())); 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 9658e643298..782038fdac8 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 @@ -27,9 +27,8 @@ import com.alibaba.nacos.plugin.auth.constant.ActionTypes; import org.springframework.stereotype.Component; - -import static com.alibaba.nacos.api.common.Constants.WATCH_TYPE_WATCH; import static com.alibaba.nacos.api.common.Constants.WATCH_TYPE_CANCEL_WATCH; +import static com.alibaba.nacos.api.common.Constants.WATCH_TYPE_WATCH; /** * Fuzzy watch service request handler. @@ -40,9 +39,9 @@ public class NamingFuzzyWatchRequestHandler extends RequestHandler { public NamingFuzzyWatchRequestHandler() { - NotifyCenter.registerToPublisher(ClientOperationEvent.ClientFuzzyWatchEvent.class,1000); - NotifyCenter.registerToPublisher(ClientOperationEvent.ClientCancelFuzzyWatchEvent.class,1000); - + NotifyCenter.registerToPublisher(ClientOperationEvent.ClientFuzzyWatchEvent.class, 1000); + NotifyCenter.registerToPublisher(ClientOperationEvent.ClientCancelFuzzyWatchEvent.class, 1000); + } @Override @@ -52,10 +51,13 @@ public NamingFuzzyWatchResponse handle(NamingFuzzyWatchRequest request, RequestM String groupKeyPattern = request.getGroupKeyPattern(); switch (request.getWatchType()) { case WATCH_TYPE_WATCH: - NotifyCenter.publishEvent(new ClientOperationEvent.ClientFuzzyWatchEvent(groupKeyPattern, meta.getConnectionId(),request.getReceivedGroupKeys(),request.isInitializing())); + 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())); + NotifyCenter.publishEvent( + new ClientOperationEvent.ClientCancelFuzzyWatchEvent(groupKeyPattern, meta.getConnectionId())); return NamingFuzzyWatchResponse.buildSuccessResponse(); default: throw new NacosException(NacosException.INVALID_PARAM, diff --git a/naming/src/test/java/com/alibaba/nacos/naming/core/v2/index/ClientServiceIndexesManagerTest.java b/naming/src/test/java/com/alibaba/nacos/naming/core/v2/index/ClientServiceIndexesManagerTest.java index b83a9ea6604..c6a3c6c7687 100644 --- a/naming/src/test/java/com/alibaba/nacos/naming/core/v2/index/ClientServiceIndexesManagerTest.java +++ b/naming/src/test/java/com/alibaba/nacos/naming/core/v2/index/ClientServiceIndexesManagerTest.java @@ -119,7 +119,6 @@ void testRemovePublisherIndexesByEmptyService() throws NoSuchFieldException, Ill void testSubscribeTypes() { List> classes = clientServiceIndexesManager.subscribeTypes(); - assertNotNull(classes); assertEquals(7, classes.size()); } diff --git a/naming/src/test/java/com/alibaba/nacos/naming/push/v2/NamingSubscriberServiceV2ImplTest.java b/naming/src/test/java/com/alibaba/nacos/naming/push/v2/NamingSubscriberServiceV2ImplTest.java index 66dee3130f1..abdce5ad864 100644 --- a/naming/src/test/java/com/alibaba/nacos/naming/push/v2/NamingSubscriberServiceV2ImplTest.java +++ b/naming/src/test/java/com/alibaba/nacos/naming/push/v2/NamingSubscriberServiceV2ImplTest.java @@ -20,8 +20,8 @@ import com.alibaba.nacos.naming.core.v2.client.Client; import com.alibaba.nacos.naming.core.v2.client.manager.ClientManagerDelegate; 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.index.ClientServiceIndexesManager; +import com.alibaba.nacos.naming.core.v2.index.NamingFuzzyWatchContextService; import com.alibaba.nacos.naming.core.v2.pojo.Service; import com.alibaba.nacos.naming.misc.SwitchDomain; import com.alibaba.nacos.naming.pojo.Subscriber; @@ -79,10 +79,12 @@ class NamingSubscriberServiceV2ImplTest { @BeforeEach void setUp() throws Exception { - subscriberService = new NamingSubscriberServiceV2Impl(clientManager, indexesManager, null, null,null, switchDomain); + subscriberService = new NamingSubscriberServiceV2Impl(clientManager, indexesManager, null, null, null, + switchDomain); ReflectionTestUtils.setField(subscriberService, "delayTaskEngine", delayTaskEngine); when(indexesManager.getAllClientsSubscribeService(service)).thenReturn(Collections.singletonList(testClientId)); - when(indexesManager.getAllClientsSubscribeService(service1)).thenReturn(Collections.singletonList(testClientId)); + when(indexesManager.getAllClientsSubscribeService(service1)).thenReturn( + Collections.singletonList(testClientId)); Collection services = new LinkedList<>(); services.add(service); services.add(service1); @@ -96,7 +98,8 @@ void setUp() throws Exception { @Test void testGetSubscribersByString() { - Collection actual = subscriberService.getSubscribers(service.getNamespace(), service.getGroupedServiceName()); + Collection actual = subscriberService.getSubscribers(service.getNamespace(), + service.getGroupedServiceName()); assertEquals(1, actual.size()); assertEquals(service.getGroupedServiceName(), actual.iterator().next().getServiceName()); } @@ -110,7 +113,8 @@ void testGetSubscribersByService() { @Test void testGetFuzzySubscribersByString() { - Collection actual = subscriberService.getFuzzySubscribers(service.getNamespace(), service.getGroupedServiceName()); + Collection actual = subscriberService.getFuzzySubscribers(service.getNamespace(), + service.getGroupedServiceName()); assertEquals(2, actual.size()); } @@ -122,7 +126,8 @@ void testGetFuzzySubscribersByService() { @Test public void onEvent() { - subscriberService.onEvent(new ServiceEvent.ServiceChangedEvent(service, Constants.ServiceChangedType.ADD_SERVICE)); + subscriberService.onEvent( + new ServiceEvent.ServiceChangedEvent(service, Constants.ServiceChangedType.ADD_SERVICE)); verify(delayTaskEngine).addTask(eq(service), any(PushDelayTask.class)); } } From b4d3b233e45c2937fa61c1917f878d0308d16ed5 Mon Sep 17 00:00:00 2001 From: shiyiyue1102 Date: Fri, 27 Dec 2024 10:36:45 +0800 Subject: [PATCH 11/15] optimize fuzzy watch of config&naming. --- .../alibaba/nacos/api/common/Constants.java | 76 +- .../nacos/api/config/ConfigService.java | 167 ++--- .../listener/AbstractFuzzyListenListener.java | 98 --- .../AbstractFuzzyWatchEventWatcher.java | 38 + .../api/config/listener/AbstractListener.java | 1 - .../listener/ConfigFuzzyWatchChangeEvent.java | 113 +++ .../FuzzyListenConfigChangeEvent.java | 109 --- .../listener/FuzzyWatchEventWatcher.java | 43 ++ ...a => AbstractFuzzyWatchNotifyRequest.java} | 18 +- .../ConfigBatchFuzzyListenRequest.java | 233 ------ .../ConfigFuzzyWatchChangeNotifyRequest.java | 83 +++ .../request/ConfigFuzzyWatchRequest.java | 100 +++ ....java => ConfigFuzzyWatchSyncRequest.java} | 140 ++-- .../FuzzyListenNotifyChangeRequest.java | 113 --- ...ConfigFuzzyWatchChangeNotifyResponse.java} | 4 +- ...nse.java => ConfigFuzzyWatchResponse.java} | 2 +- ...java => ConfigFuzzyWatchSyncResponse.java} | 2 +- .../alibaba/nacos/api/model/v2/ErrorCode.java | 70 +- .../nacos/api/naming/NamingService.java | 76 +- .../AbstractFuzzyWatchEventWatcher.java | 32 + .../listener/FuzzyWatchChangeEvent.java | 83 +++ .../listener/FuzzyWatchEventWatcher.java | 40 ++ .../AbstractFuzzyWatchNotifyRequest.java | 51 ++ .../NamingFuzzyWatchChangeNotifyRequest.java | 55 ++ .../request/NamingFuzzyWatchRequest.java | 98 +++ .../request/NamingFuzzyWatchSyncRequest.java | 167 +++++ .../NamingFuzzyWatchChangeNotifyResponse.java | 28 + .../response/NamingFuzzyWatchResponse.java | 48 ++ .../NamingFuzzyWatchSyncResponse.java | 48 ++ .../nacos/api/naming/utils/NamingUtils.java | 26 +- .../com.alibaba.nacos.api.remote.Payload | 19 +- .../client/config/NacosConfigService.java | 114 +-- .../ClientFuzzyWatchNotifyRequestHandler.java | 53 ++ .../client/config/impl/ClientWorker.java | 669 ++---------------- .../config/impl/ConfigFuzzyWatchContext.java | 447 ++++++++++++ .../impl/ConfigFuzzyWatchGroupKeyHolder.java | 485 +++++++++++++ .../impl/ConfigFuzzyWatchNotifyEvent.java | 133 ++++ .../impl/ConfigFuzzyWatcherWrapper.java | 75 ++ .../config/impl/ConfigTransportClient.java | 27 +- .../config/impl/FuzzyListenContext.java | 447 ------------ .../config/impl/FuzzyListenNotifyEvent.java | 200 ------ .../client/naming/NacosNamingService.java | 79 ++- .../cache/FuzzyWatchEventWatcherWrapper.java | 66 ++ .../naming/cache/NamingFuzzyWatchContext.java | 369 ++++++++++ .../NamingFuzzyWatchServiceListHolder.java | 340 +++++++++ .../event/NamingFuzzyWatchNotifyEvent.java | 88 +++ .../remote/NamingClientProxyDelegate.java | 9 +- .../NamingFuzzyWatchNotifyRequestHandler.java | 126 ++++ .../remote/gprc/NamingGrpcClientProxy.java | 56 +- .../gprc/redo/NamingGrpcRedoService.java | 10 +- .../gprc/redo/data/FuzzyWatcherRedoData.java | 33 + .../alibaba/nacos/client/utils/ParamUtil.java | 35 +- .../nacos-client/reflect-config.json | 35 +- .../client/config/NacosConfigServiceTest.java | 100 +-- .../remote/AbstractNamingClientProxyTest.java | 2 +- .../remote/NamingClientProxyDelegateTest.java | 7 +- .../gprc/NamingGrpcClientProxyTest.java | 10 +- .../gprc/NamingPushRequestHandlerTest.java | 2 + .../gprc/redo/NamingGrpcRedoServiceTest.java | 8 +- .../nacos/common/task/BatchTaskCounter.java | 73 ++ .../common/utils/FuzzyGroupKeyPattern.java | 240 +++++++ .../alibaba/nacos/common/utils/GroupKey.java | 140 ---- .../nacos/common/utils/GroupKeyPattern.java | 259 ------- .../utils/FuzzyGroupKeyPatternTest.java | 43 ++ .../common/utils/GroupKeyPatternTest.java | 136 ---- config/pom.xml | 4 + .../configuration/ConfigCommonConfig.java | 7 +- .../nacos/config/server/model/CacheItem.java | 16 - .../event/ConfigCancelFuzzyWatchEvent.java | 76 ++ ...nEvent.java => ConfigFuzzyWatchEvent.java} | 36 +- .../ConfigBatchFuzzyListenRequestHandler.java | 107 --- .../remote/ConfigChangeListenContext.java | 107 +-- .../remote/ConfigConnectionEventListener.java | 8 +- ...va => ConfigFuzzyWatchChangeNotifier.java} | 101 +-- .../ConfigFuzzyWatchRequestHandler.java | 95 +++ .../remote/ConfigFuzzyWatchSyncNotifier.java | 367 ++++++++++ .../RpcFuzzyListenConfigDiffNotifier.java | 488 ------------- .../server/service/ConfigCacheService.java | 43 +- .../ConfigFuzzyWatchContextService.java | 251 +++++++ ...nfigFuzzyWatchRequestParamsExtractor.java} | 37 +- .../nacos/core/utils/GlobalExecutor.java | 7 + .../alibaba/nacos/core/utils/StringPool.java | 6 +- ...mple.java => ConfigFuzzyWatchExample.java} | 13 +- .../example/NamingFuzzyWatchExample.java | 120 ++++ .../v2/event/client/ClientOperationEvent.java | 80 ++- .../core/v2/event/service/ServiceEvent.java | 18 +- .../v2/index/ClientServiceIndexesManager.java | 22 +- .../index/NamingFuzzyWatchContextService.java | 265 +++++++ .../metadata/InstanceMetadataProcessor.java | 3 +- .../heartbeat/ClientBeatProcessorV2.java | 3 +- .../heartbeat/UnhealthyInstanceChecker.java | 2 +- .../push/NamingFuzzyWatchChangeNotifier.java | 90 +++ .../push/NamingFuzzyWatchSyncNotifier.java | 157 ++++ .../naming/push/v2/executor/PushExecutor.java | 12 + .../v2/executor/PushExecutorDelegate.java | 9 + .../push/v2/executor/PushExecutorRpcImpl.java | 8 + .../push/v2/executor/PushExecutorUdpImpl.java | 7 + .../FuzzyWatchChangeNotifyExecuteTask.java | 99 +++ .../v2/task/FuzzyWatchChangeNotifyTask.java | 64 ++ .../task/FuzzyWatchPushDelayTaskEngine.java | 105 +++ .../task/FuzzyWatchSyncNotifyExecuteTask.java | 158 +++++ .../v2/task/FuzzyWatchSyncNotifyTask.java | 116 +++ .../NamingFuzzyWatchRequestHandler.java | 67 ++ .../ClientServiceIndexesManagerTest.java | 2 +- .../v2/NamingSubscriberServiceV2ImplTest.java | 22 +- .../push/v2/task/FixturePushExecutor.java | 12 + pom.xml | 2 +- 107 files changed, 6642 insertions(+), 3667 deletions(-) delete mode 100644 api/src/main/java/com/alibaba/nacos/api/config/listener/AbstractFuzzyListenListener.java create mode 100644 api/src/main/java/com/alibaba/nacos/api/config/listener/AbstractFuzzyWatchEventWatcher.java create mode 100644 api/src/main/java/com/alibaba/nacos/api/config/listener/ConfigFuzzyWatchChangeEvent.java delete mode 100644 api/src/main/java/com/alibaba/nacos/api/config/listener/FuzzyListenConfigChangeEvent.java create mode 100644 api/src/main/java/com/alibaba/nacos/api/config/listener/FuzzyWatchEventWatcher.java rename api/src/main/java/com/alibaba/nacos/api/config/remote/request/{AbstractFuzzyListenNotifyRequest.java => AbstractFuzzyWatchNotifyRequest.java} (65%) delete mode 100644 api/src/main/java/com/alibaba/nacos/api/config/remote/request/ConfigBatchFuzzyListenRequest.java create mode 100644 api/src/main/java/com/alibaba/nacos/api/config/remote/request/ConfigFuzzyWatchChangeNotifyRequest.java create mode 100644 api/src/main/java/com/alibaba/nacos/api/config/remote/request/ConfigFuzzyWatchRequest.java rename api/src/main/java/com/alibaba/nacos/api/config/remote/request/{FuzzyListenNotifyDiffRequest.java => ConfigFuzzyWatchSyncRequest.java} (50%) delete mode 100644 api/src/main/java/com/alibaba/nacos/api/config/remote/request/FuzzyListenNotifyChangeRequest.java rename api/src/main/java/com/alibaba/nacos/api/config/remote/response/{FuzzyListenNotifyDiffResponse.java => ConfigFuzzyWatchChangeNotifyResponse.java} (88%) rename api/src/main/java/com/alibaba/nacos/api/config/remote/response/{ConfigBatchFuzzyListenResponse.java => ConfigFuzzyWatchResponse.java} (92%) rename api/src/main/java/com/alibaba/nacos/api/config/remote/response/{FuzzyListenNotifyChangeResponse.java => ConfigFuzzyWatchSyncResponse.java} (92%) create mode 100644 api/src/main/java/com/alibaba/nacos/api/naming/listener/AbstractFuzzyWatchEventWatcher.java create mode 100644 api/src/main/java/com/alibaba/nacos/api/naming/listener/FuzzyWatchChangeEvent.java create mode 100644 api/src/main/java/com/alibaba/nacos/api/naming/listener/FuzzyWatchEventWatcher.java create mode 100644 api/src/main/java/com/alibaba/nacos/api/naming/remote/request/AbstractFuzzyWatchNotifyRequest.java create mode 100644 api/src/main/java/com/alibaba/nacos/api/naming/remote/request/NamingFuzzyWatchChangeNotifyRequest.java create mode 100644 api/src/main/java/com/alibaba/nacos/api/naming/remote/request/NamingFuzzyWatchRequest.java create mode 100644 api/src/main/java/com/alibaba/nacos/api/naming/remote/request/NamingFuzzyWatchSyncRequest.java create mode 100644 api/src/main/java/com/alibaba/nacos/api/naming/remote/response/NamingFuzzyWatchChangeNotifyResponse.java create mode 100644 api/src/main/java/com/alibaba/nacos/api/naming/remote/response/NamingFuzzyWatchResponse.java create mode 100644 api/src/main/java/com/alibaba/nacos/api/naming/remote/response/NamingFuzzyWatchSyncResponse.java create mode 100644 client/src/main/java/com/alibaba/nacos/client/config/impl/ClientFuzzyWatchNotifyRequestHandler.java create mode 100644 client/src/main/java/com/alibaba/nacos/client/config/impl/ConfigFuzzyWatchContext.java create mode 100644 client/src/main/java/com/alibaba/nacos/client/config/impl/ConfigFuzzyWatchGroupKeyHolder.java create mode 100644 client/src/main/java/com/alibaba/nacos/client/config/impl/ConfigFuzzyWatchNotifyEvent.java create mode 100644 client/src/main/java/com/alibaba/nacos/client/config/impl/ConfigFuzzyWatcherWrapper.java delete mode 100644 client/src/main/java/com/alibaba/nacos/client/config/impl/FuzzyListenContext.java delete mode 100644 client/src/main/java/com/alibaba/nacos/client/config/impl/FuzzyListenNotifyEvent.java create mode 100644 client/src/main/java/com/alibaba/nacos/client/naming/cache/FuzzyWatchEventWatcherWrapper.java create mode 100644 client/src/main/java/com/alibaba/nacos/client/naming/cache/NamingFuzzyWatchContext.java create mode 100644 client/src/main/java/com/alibaba/nacos/client/naming/cache/NamingFuzzyWatchServiceListHolder.java create mode 100644 client/src/main/java/com/alibaba/nacos/client/naming/event/NamingFuzzyWatchNotifyEvent.java create mode 100644 client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/NamingFuzzyWatchNotifyRequestHandler.java create mode 100644 client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/redo/data/FuzzyWatcherRedoData.java create mode 100644 common/src/main/java/com/alibaba/nacos/common/task/BatchTaskCounter.java create mode 100644 common/src/main/java/com/alibaba/nacos/common/utils/FuzzyGroupKeyPattern.java delete mode 100644 common/src/main/java/com/alibaba/nacos/common/utils/GroupKey.java delete mode 100644 common/src/main/java/com/alibaba/nacos/common/utils/GroupKeyPattern.java create mode 100644 common/src/test/java/com/alibaba/nacos/common/utils/FuzzyGroupKeyPatternTest.java delete mode 100644 common/src/test/java/com/alibaba/nacos/common/utils/GroupKeyPatternTest.java create mode 100644 config/src/main/java/com/alibaba/nacos/config/server/model/event/ConfigCancelFuzzyWatchEvent.java rename config/src/main/java/com/alibaba/nacos/config/server/model/event/{ConfigBatchFuzzyListenEvent.java => ConfigFuzzyWatchEvent.java} (79%) delete mode 100644 config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigBatchFuzzyListenRequestHandler.java rename config/src/main/java/com/alibaba/nacos/config/server/remote/{RpcFuzzyListenConfigChangeNotifier.java => ConfigFuzzyWatchChangeNotifier.java} (63%) create mode 100644 config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigFuzzyWatchRequestHandler.java create mode 100644 config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigFuzzyWatchSyncNotifier.java delete mode 100644 config/src/main/java/com/alibaba/nacos/config/server/remote/RpcFuzzyListenConfigDiffNotifier.java create mode 100644 config/src/main/java/com/alibaba/nacos/config/server/service/ConfigFuzzyWatchContextService.java rename core/src/main/java/com/alibaba/nacos/core/paramcheck/impl/{ConfigBatchFuzzyListenRequestParamsExtractor.java => ConfigFuzzyWatchRequestParamsExtractor.java} (51%) rename example/src/main/java/com/alibaba/nacos/example/{FuzzyListenExample.java => ConfigFuzzyWatchExample.java} (88%) create mode 100644 example/src/main/java/com/alibaba/nacos/example/NamingFuzzyWatchExample.java create mode 100644 naming/src/main/java/com/alibaba/nacos/naming/core/v2/index/NamingFuzzyWatchContextService.java create mode 100644 naming/src/main/java/com/alibaba/nacos/naming/push/NamingFuzzyWatchChangeNotifier.java create mode 100644 naming/src/main/java/com/alibaba/nacos/naming/push/NamingFuzzyWatchSyncNotifier.java create mode 100644 naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchChangeNotifyExecuteTask.java create mode 100644 naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchChangeNotifyTask.java create mode 100644 naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchPushDelayTaskEngine.java create mode 100644 naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchSyncNotifyExecuteTask.java create mode 100644 naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchSyncNotifyTask.java create mode 100644 naming/src/main/java/com/alibaba/nacos/naming/remote/rpc/handler/NamingFuzzyWatchRequestHandler.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 509972f07f0..c903a54ba2d 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 @@ -99,10 +99,6 @@ public class Constants { public static final Integer CLUSTER_GRPC_PORT_DEFAULT_OFFSET = 1001; - public static final String NAMESPACE_ID_SPLITTER = ">>"; - - public static final String DATA_ID_SPLITTER = "@@"; - /** * second. */ @@ -212,8 +208,6 @@ public class Constants { public static final String ALL_PATTERN = "*"; - public static final String FUZZY_LISTEN_PATTERN_WILDCARD = "*"; - public static final String COLON = ":"; public static final String LINE_BREAK = "\n"; @@ -237,16 +231,6 @@ public class Constants { public static final int DEFAULT_REDO_THREAD_COUNT = 1; - public static class ConfigChangeType { - - public static final String ADD_CONFIG = "ADD_CONFIG"; - - public static final String DELETE_CONFIG = "DELETE_CONFIG"; - - public static final String FINISH_LISTEN_INIT = "FINISH_LISTEN_INIT"; - - public static final String LISTEN_INIT = "LISTEN_INIT"; - } public static final String APP_CONN_LABELS_KEY = "nacos.app.conn.labels"; public static final String DOT = "."; @@ -292,6 +276,66 @@ public static class Naming { public static final String CMDB_CONTEXT_TYPE = "CMDB"; } + public static final String FUZZY_WATCH_PATTERN_SPLITTER = ">>"; + + /** + * fuzzy watch sync type of watch init notify. + */ + public static final String FUZZY_WATCH_INIT_NOTIFY = "FUZZY_WATCH_INIT_NOTIFY"; + + /** + * fuzzy watch sync type of watch init notify finish. + */ + public static final String FINISH_FUZZY_WATCH_INIT_NOTIFY = "FINISH_FUZZY_WATCH_INIT_NOTIFY"; + + /** + * fuzzy watch sync type of watch diff sync notify. + */ + public static final String FUZZY_WATCH_DIFF_SYNC_NOTIFY = "FUZZY_WATCH_DIFF_SYNC_NOTIFY"; + + /** + * fuzzy watch sync type of watch resource changed. + */ + public static final String FUZZY_WATCH_RESOURCE_CHANGED = "FUZZY_WATCH_RESOURCE_CHANGED"; + + /** + * watch type of watch. + */ + public static final String WATCH_TYPE_WATCH = "WATCH"; + + /** + * watch type of cancel watch. + */ + public static final String WATCH_TYPE_CANCEL_WATCH = "CANCEL_WATCH"; + + /** + * The constants in config fuzzy watch changed type directory. + */ + public static class ConfigChangedType { + + public static final String ADD_CONFIG = "ADD_CONFIG"; + + public static final String DELETE_CONFIG = "DELETE_CONFIG"; + + public static final String CONFIG_CHANGED = "CONFIG_CHANGED"; + + } + + /** + * The constants in naming fuzzy watch changed type directory. + */ + public static class ServiceChangedType { + + public static final String ADD_SERVICE = "ADD_SERVICE"; + + public static final String DELETE_SERVICE = "DELETE_SERVICE"; + + public static final String INSTANCE_CHANGED = "INSTANCE_CHANGED"; + + public static final String HEART_BEAT = "HEART_BEAT"; + + } + /** * The constants in remote directory. */ diff --git a/api/src/main/java/com/alibaba/nacos/api/config/ConfigService.java b/api/src/main/java/com/alibaba/nacos/api/config/ConfigService.java index a1017860f61..2c6699a1392 100644 --- a/api/src/main/java/com/alibaba/nacos/api/config/ConfigService.java +++ b/api/src/main/java/com/alibaba/nacos/api/config/ConfigService.java @@ -17,12 +17,12 @@ package com.alibaba.nacos.api.config; import com.alibaba.nacos.api.config.filter.IConfigFilter; -import com.alibaba.nacos.api.config.listener.AbstractFuzzyListenListener; +import com.alibaba.nacos.api.config.listener.FuzzyWatchEventWatcher; import com.alibaba.nacos.api.config.listener.Listener; import com.alibaba.nacos.api.exception.NacosException; -import java.util.Collection; -import java.util.concurrent.CompletableFuture; +import java.util.Set; +import java.util.concurrent.Future; /** * Config Service Interface. @@ -63,8 +63,8 @@ String getConfigAndSignListener(String dataId, String group, long timeoutMs, Lis /** * Add a listener to the configuration, after the server modified the configuration, the client will use the * incoming listener callback. Recommended asynchronous processing, the application can implement the getExecutor - * method in the ManagerListener, provide a thread pool of execution. If not provided, use the main thread callback, - * May block other configurations or be blocked by other configurations. + * method in the ManagerListener, provide a thread pool of execution. If not provided, use the main thread callback, May + * block other configurations or be blocked by other configurations. * * @param dataId dataId * @param group group @@ -73,78 +73,6 @@ String getConfigAndSignListener(String dataId, String group, long timeoutMs, Lis */ void addListener(String dataId, String group, Listener listener) throws NacosException; - /** - * Add a fuzzy listener to the configuration. After the server modifies the configuration matching the specified - * fixed group name, the client will utilize the incoming fuzzy listener callback. Fuzzy listeners allow for - * pattern-based subscription to configurations, where the fixed group name represents the group and dataId patterns - * specified for subscription. - * - * @param fixedGroupName The fixed group name representing the group and dataId patterns to subscribe to. - * @param listener The fuzzy listener to be added. - * @throws NacosException NacosException - */ - void addFuzzyListener(String fixedGroupName, AbstractFuzzyListenListener listener) throws NacosException; - - /** - * Add a fuzzy listener to the configuration. After the server modifies the configuration matching the specified - * dataId pattern and fixed group name, the client will utilize the incoming fuzzy listener callback. Fuzzy - * listeners allow for pattern-based subscription to configurations. - * - * @param dataIdPattern The pattern to match dataIds for subscription. - * @param fixedGroupName The fixed group name representing the group and dataId patterns to subscribe to. - * @param listener The fuzzy listener to be added. - * @throws NacosException NacosException - */ - void addFuzzyListener(String dataIdPattern, String fixedGroupName, AbstractFuzzyListenListener listener) - throws NacosException; - - /** - * Add a fuzzy listener to the configuration and retrieve all configs that match the specified fixed group name. - * Fuzzy listeners allow for pattern-based subscription to configs, where the fixed group name represents the group - * and dataId patterns specified for subscription. - * - * @param fixedGroupName The fixed group name representing the group and dataId patterns to subscribe to. - * @param listener The fuzzy listener to be added. - * @return CompletableFuture containing collection of configs that match the specified fixed group name. - * @throws NacosException NacosException - */ - CompletableFuture> addFuzzyListenerAndGetConfigs(String fixedGroupName, - AbstractFuzzyListenListener listener) throws NacosException; - - /** - * Add a fuzzy listener to the configuration and retrieve all configs that match the specified dataId pattern and - * fixed group name. Fuzzy listeners allow for pattern-based subscription to configs. - * - * @param dataIdPattern The pattern to match dataIds for subscription. - * @param fixedGroupName The fixed group name representing the group and dataId patterns to subscribe to. - * @param listener The fuzzy listener to be added. - * @return CompletableFuture containing collection of configs that match the specified dataId pattern and fixed - * group name. - * @throws NacosException NacosException - */ - CompletableFuture> addFuzzyListenerAndGetConfigs(String dataIdPattern, String fixedGroupName, - AbstractFuzzyListenListener listener) throws NacosException; - - /** - * Cancel fuzzy listen and remove the event listener for a specified fixed group name. - * - * @param fixedGroupName The fixed group name for fuzzy watch. - * @param listener The event listener to be removed. - * @throws NacosException If an error occurs during the cancellation process. - */ - void cancelFuzzyListen(String fixedGroupName, AbstractFuzzyListenListener listener) throws NacosException; - - /** - * Cancel fuzzy listen and remove the event listener for a specified service name pattern and fixed group name. - * - * @param dataIdPatter The pattern to match dataId for fuzzy watch. - * @param fixedGroupName The fixed group name for fuzzy watch. - * @param listener The event listener to be removed. - * @throws NacosException If an error occurs during the cancellation process. - */ - void cancelFuzzyListen(String dataIdPatter, String fixedGroupName, AbstractFuzzyListenListener listener) - throws NacosException; - /** * Publish config. * @@ -220,10 +148,10 @@ boolean publishConfigCas(String dataId, String group, String content, String cas * @return whether health */ String getServerStatus(); - + /** - * add config filter. It is recommended to use {@link com.alibaba.nacos.api.config.filter.AbstractConfigFilter} to - * expand the filter. + * add config filter. + * It is recommended to use {@link com.alibaba.nacos.api.config.filter.AbstractConfigFilter} to expand the filter. * * @param configFilter filter * @since 2.3.0 @@ -236,4 +164,83 @@ boolean publishConfigCas(String dataId, String group, String content, String cas * @throws NacosException exception. */ void shutDown() throws NacosException; + + /** + * Add a fuzzy listener to the configuration. After the server modifies the configuration matching the specified + * fixed group name, the client will utilize the incoming fuzzy listener callback. Fuzzy listeners allow for + * pattern-based subscription to configurations, where the fixed group name represents the group and dataId patterns + * specified for subscription. + * + * @param fixedGroupName The fixed group name representing the group and dataId patterns to subscribe to. + * @param watcher The fuzzy watcher to be added. + * @throws NacosException NacosException + * @since 3.0 + */ + void fuzzyWatch(String fixedGroupName, FuzzyWatchEventWatcher watcher) throws NacosException; + + /** + * Add a fuzzy listener to the configuration. After the server modifies the configuration matching the specified + * dataId pattern and fixed group name, the client will utilize the incoming fuzzy listener callback. Fuzzy + * listeners allow for pattern-based subscription to configurations. + * + * @param dataIdPattern The pattern to match dataIds for subscription. + * @param fixedGroupName The fixed group name representing the group and dataId patterns to subscribe to. + * @param watcher The fuzzy listener to be added. + * @throws NacosException NacosException + * @since 3.0 + */ + void fuzzyWatch(String dataIdPattern, String fixedGroupName, FuzzyWatchEventWatcher watcher) + throws NacosException; + + /** + * Add a fuzzy listener to the configuration and retrieve all configs that match the specified fixed group name. + * Fuzzy listeners allow for pattern-based subscription to configs, where the fixed group name represents the group + * and dataId patterns specified for subscription. + * + * @param fixedGroupName The fixed group name representing the group and dataId patterns to subscribe to. + * @param watcher The fuzzy watcher to be added. + * @return CompletableFuture containing collection of configs that match the specified fixed group name. + * @throws NacosException NacosException + * @since 3.0 + */ + Future> fuzzyWatchWithGroupKeys(String fixedGroupName, + FuzzyWatchEventWatcher watcher) throws NacosException; + + /** + * Add a fuzzy listener to the configuration and retrieve all configs that match the specified dataId pattern and + * fixed group name. Fuzzy listeners allow for pattern-based subscription to configs. + * + * @param dataIdPattern The pattern to match dataIds for subscription. + * @param fixedGroupName The fixed group name representing the group and dataId patterns to subscribe to. + * @param watcher The fuzzy watcher to be added. + * @return CompletableFuture containing collection of configs that match the specified dataId pattern and fixed + * group name. + * @throws NacosException NacosException + * @since 3.0 + */ + Future> fuzzyWatchWithGroupKeys(String dataIdPattern, String fixedGroupName, + FuzzyWatchEventWatcher watcher) throws NacosException; + + /** + * Cancel fuzzy listen and remove the event listener for a specified fixed group name. + * + * @param fixedGroupName The fixed group name for fuzzy watch. + * @param watcher The event watcher to be removed. + * @throws NacosException If an error occurs during the cancellation process. + * @since 3.0 + */ + void cancelFuzzyWatch(String fixedGroupName, FuzzyWatchEventWatcher watcher) throws NacosException; + + /** + * Cancel fuzzy listen and remove the event listener for a specified service name pattern and fixed group name. + * + * @param dataIdPattern The pattern to match dataId for fuzzy watch. + * @param fixedGroupName The fixed group name for fuzzy watch. + * @param watcher The event listener to be removed. + * @throws NacosException If an error occurs during the cancellation process. + * @since 3.0 + */ + void cancelFuzzyWatch(String dataIdPattern, String fixedGroupName, FuzzyWatchEventWatcher watcher) + throws NacosException; + } diff --git a/api/src/main/java/com/alibaba/nacos/api/config/listener/AbstractFuzzyListenListener.java b/api/src/main/java/com/alibaba/nacos/api/config/listener/AbstractFuzzyListenListener.java deleted file mode 100644 index bd317884a48..00000000000 --- a/api/src/main/java/com/alibaba/nacos/api/config/listener/AbstractFuzzyListenListener.java +++ /dev/null @@ -1,98 +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.api.config.listener; - -import java.util.Objects; - -/** - * AbstractFuzzyListenListener is an abstract class that provides basic functionality for listening to fuzzy - * configuration changes in Nacos. - * - * @author stone-98 - * @date 2024/3/4 - */ -public abstract class AbstractFuzzyListenListener extends AbstractListener { - - /** - * Unique identifier for the listener. - */ - private String uuid; - - /** - * Get the UUID (Unique Identifier) of the listener. - * - * @return The UUID of the listener - */ - public String getUuid() { - return uuid; - } - - /** - * Set the UUID (Unique Identifier) of the listener. - * - * @param uuid The UUID to be set - */ - public void setUuid(String uuid) { - this.uuid = uuid; - } - - /** - * Callback method invoked when a fuzzy configuration change event occurs. - * - * @param event The fuzzy configuration change event - */ - public abstract void onEvent(FuzzyListenConfigChangeEvent event); - - /** - * Receive the configuration information. This method is overridden but does nothing in this abstract class. - * - * @param configInfo The configuration information - */ - @Override - public void receiveConfigInfo(String configInfo) { - // Do nothing by default - } - - /** - * Compute the hash code for this listener based on its UUID. - * - * @return The hash code value for this listener - */ - @Override - public int hashCode() { - return Objects.hashCode(uuid); - } - - /** - * Compare this listener to the specified object for equality. Two listeners are considered equal if they have the - * same UUID. - * - * @param o The object to compare to - * @return true if the specified object is equal to this listener, false otherwise - */ - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - AbstractFuzzyListenListener that = (AbstractFuzzyListenListener) o; - return Objects.equals(uuid, that.uuid); - } -} 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 new file mode 100644 index 00000000000..b62cca652af --- /dev/null +++ b/api/src/main/java/com/alibaba/nacos/api/config/listener/AbstractFuzzyWatchEventWatcher.java @@ -0,0 +1,38 @@ +/* + * 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; + +import java.util.concurrent.Executor; + +/** + * AbstractFuzzyListenListener is an abstract class that provides basic functionality for listening to fuzzy + * configuration changes in Nacos. + * + * @author stone-98 + * @date 2024/3/4 + */ +public abstract class AbstractFuzzyWatchEventWatcher implements FuzzyWatchEventWatcher { + + /** + * Get executor for execute this receive. + * + * @return Executor + */ + public Executor getExecutor() { + return null; + } +} diff --git a/api/src/main/java/com/alibaba/nacos/api/config/listener/AbstractListener.java b/api/src/main/java/com/alibaba/nacos/api/config/listener/AbstractListener.java index e14008d01d9..3f4c968f378 100644 --- a/api/src/main/java/com/alibaba/nacos/api/config/listener/AbstractListener.java +++ b/api/src/main/java/com/alibaba/nacos/api/config/listener/AbstractListener.java @@ -23,7 +23,6 @@ * * @author water.lyl */ -@SuppressWarnings("PMD.AbstractClassShouldStartWithAbstractNamingRule") public abstract class AbstractListener implements Listener { /** diff --git a/api/src/main/java/com/alibaba/nacos/api/config/listener/ConfigFuzzyWatchChangeEvent.java b/api/src/main/java/com/alibaba/nacos/api/config/listener/ConfigFuzzyWatchChangeEvent.java new file mode 100644 index 00000000000..ac90ec4caaf --- /dev/null +++ b/api/src/main/java/com/alibaba/nacos/api/config/listener/ConfigFuzzyWatchChangeEvent.java @@ -0,0 +1,113 @@ +/* + * 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; + +import com.alibaba.nacos.api.common.Constants; + +/** + * Represents a fuzzy listening configuration change event. + * + *

This event indicates that a change has occurred in a configuration that matches a fuzzy listening pattern. + * + * @author stone-98 + * @date 2024/3/12 + */ +public class ConfigFuzzyWatchChangeEvent { + + /** + * The group of the configuration that has changed. + */ + private String group; + + /** + * The data ID of the configuration that has changed. + */ + private String dataId; + + /** + * The namespace of the configuration that has changed. + */ + private String namespace; + + /** + * The change type of local watcher , contains {"ADD_CONFIG", "DELETE_CONFIG"}. + * {@link Constants.ConfigChangedType}. + */ + private String changedType; + + /** + * the sync type that trigger this changed,contains {"FUZZY_WATCH_INIT_NOTIFY","FUZZY_WATCH_RESOURCE_CHANGED", + * "FUZZY_WATCH_DIFF_SYNC_NOTIFY"}. + */ + private String syncType; + + /** + * Constructs a FuzzyListenConfigChangeEvent with the specified parameters. + * + * @param group The group of the configuration that has changed + * @param dataId The data ID of the configuration that has changed + * @param changedType The type of change that has occurred + */ + private ConfigFuzzyWatchChangeEvent(String namespace, String group, String dataId, String changedType, + String syncType) { + this.group = group; + this.dataId = dataId; + this.namespace = namespace; + this.changedType = changedType; + this.syncType = syncType; + } + + /** + * Constructs and returns a new FuzzyListenConfigChangeEvent with the specified parameters. + * + * @param group The group of the configuration that has changed + * @param dataId The data ID of the configuration that has changed + * @param changedType The type of change that has occurred + * @return A new FuzzyListenConfigChangeEvent instance + */ + public static ConfigFuzzyWatchChangeEvent build(String namespace, String group, String dataId, String changedType, + String syncType) { + return new ConfigFuzzyWatchChangeEvent(namespace, group, dataId, changedType, syncType); + } + + public String getNamespace() { + return namespace; + } + + public String getGroup() { + return group; + } + + public String getDataId() { + return dataId; + } + + public String getChangedType() { + return changedType; + } + + public String getSyncType() { + return syncType; + } + + @Override + public String toString() { + return "ConfigFuzzyWatchChangeEvent{" + "group='" + group + '\'' + ", dataId='" + dataId + '\'' + + ", namespace='" + namespace + '\'' + ", changedType='" + changedType + '\'' + ", syncType='" + + syncType + '\'' + '}'; + } +} diff --git a/api/src/main/java/com/alibaba/nacos/api/config/listener/FuzzyListenConfigChangeEvent.java b/api/src/main/java/com/alibaba/nacos/api/config/listener/FuzzyListenConfigChangeEvent.java deleted file mode 100644 index 6e2c3e319ca..00000000000 --- a/api/src/main/java/com/alibaba/nacos/api/config/listener/FuzzyListenConfigChangeEvent.java +++ /dev/null @@ -1,109 +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.api.config.listener; - -/** - * Represents a fuzzy listening configuration change event. - * - *

This event indicates that a change has occurred in a configuration that matches a fuzzy listening pattern. - * - * @author stone-98 - * @date 2024/3/12 - */ -public class FuzzyListenConfigChangeEvent { - - /** - * The group of the configuration that has changed. - */ - private String group; - - /** - * The data ID of the configuration that has changed. - */ - private String dataId; - - /** - * The type of change that has occurred (e.g., "ADD_CONFIG", "DELETE_CONFIG"). - */ - private String type; - - /** - * Constructs an empty FuzzyListenConfigChangeEvent. - */ - public FuzzyListenConfigChangeEvent() { - } - - /** - * Constructs a FuzzyListenConfigChangeEvent with the specified parameters. - * - * @param group The group of the configuration that has changed - * @param dataId The data ID of the configuration that has changed - * @param type The type of change that has occurred - */ - public FuzzyListenConfigChangeEvent(String group, String dataId, String type) { - this.group = group; - this.dataId = dataId; - this.type = type; - } - - /** - * Constructs and returns a new FuzzyListenConfigChangeEvent with the specified parameters. - * - * @param group The group of the configuration that has changed - * @param dataId The data ID of the configuration that has changed - * @param type The type of change that has occurred - * @return A new FuzzyListenConfigChangeEvent instance - */ - public static FuzzyListenConfigChangeEvent build(String group, String dataId, String type) { - return new FuzzyListenConfigChangeEvent(group, dataId, type); - } - - public String getGroup() { - return group; - } - - public void setGroup(String group) { - this.group = group; - } - - public String getDataId() { - return dataId; - } - - public void setDataId(String dataId) { - this.dataId = dataId; - } - - public String getType() { - return type; - } - - public void setType(String type) { - this.type = type; - } - - /** - * Returns a string representation of the FuzzyListenConfigChangeEvent. - * - * @return A string representation of the event - */ - @Override - public String toString() { - return "FuzzyListenConfigChangeEvent{" + "group='" + group + '\'' + ", dataId='" + dataId + '\'' + ", type='" - + type + '\'' + '}'; - } -} diff --git a/api/src/main/java/com/alibaba/nacos/api/config/listener/FuzzyWatchEventWatcher.java b/api/src/main/java/com/alibaba/nacos/api/config/listener/FuzzyWatchEventWatcher.java new file mode 100644 index 00000000000..56c1bbe5ed9 --- /dev/null +++ b/api/src/main/java/com/alibaba/nacos/api/config/listener/FuzzyWatchEventWatcher.java @@ -0,0 +1,43 @@ +/* + * 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; + +import java.util.concurrent.Executor; + +/** + * fuzzy watch config changes. + * + * @author stone-98 + * @date 2024/3/4 + */ +public interface FuzzyWatchEventWatcher { + + /** + * Callback method invoked when a fuzzy configuration change event occurs. + * + * @param event The fuzzy configuration change event + */ + void onEvent(ConfigFuzzyWatchChangeEvent event); + + /** + * Get executor for execute this receive. + * + * @return Executor + */ + Executor getExecutor(); + +} diff --git a/api/src/main/java/com/alibaba/nacos/api/config/remote/request/AbstractFuzzyListenNotifyRequest.java b/api/src/main/java/com/alibaba/nacos/api/config/remote/request/AbstractFuzzyWatchNotifyRequest.java similarity index 65% rename from api/src/main/java/com/alibaba/nacos/api/config/remote/request/AbstractFuzzyListenNotifyRequest.java rename to api/src/main/java/com/alibaba/nacos/api/config/remote/request/AbstractFuzzyWatchNotifyRequest.java index 1ba2b7bda14..67a22264e70 100644 --- a/api/src/main/java/com/alibaba/nacos/api/config/remote/request/AbstractFuzzyListenNotifyRequest.java +++ b/api/src/main/java/com/alibaba/nacos/api/config/remote/request/AbstractFuzzyWatchNotifyRequest.java @@ -26,23 +26,9 @@ * @author stone-98 * @date 2024/3/14 */ -public abstract class AbstractFuzzyListenNotifyRequest extends ServerRequest { +public abstract class AbstractFuzzyWatchNotifyRequest extends ServerRequest { - private String serviceChangedType; - - public AbstractFuzzyListenNotifyRequest() { - } - - public AbstractFuzzyListenNotifyRequest(String serviceChangedType) { - this.serviceChangedType = serviceChangedType; - } - - public String getServiceChangedType() { - return serviceChangedType; - } - - public void setServiceChangedType(String serviceChangedType) { - this.serviceChangedType = serviceChangedType; + public AbstractFuzzyWatchNotifyRequest() { } @Override diff --git a/api/src/main/java/com/alibaba/nacos/api/config/remote/request/ConfigBatchFuzzyListenRequest.java b/api/src/main/java/com/alibaba/nacos/api/config/remote/request/ConfigBatchFuzzyListenRequest.java deleted file mode 100644 index fd5ce81e1cb..00000000000 --- a/api/src/main/java/com/alibaba/nacos/api/config/remote/request/ConfigBatchFuzzyListenRequest.java +++ /dev/null @@ -1,233 +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.api.config.remote.request; - -import com.alibaba.nacos.api.common.Constants; -import com.alibaba.nacos.api.remote.request.Request; -import com.alibaba.nacos.api.utils.StringUtils; - -import java.util.HashSet; -import java.util.Objects; -import java.util.Set; - -/** - * Represents a request for batch fuzzy listening configurations. - * - *

This request is used to request batch fuzzy listening configurations from the server. It contains a set of - * contexts, each representing a fuzzy listening context. - * - * @author stone-98 - * @date 2024/3/4 - */ -public class ConfigBatchFuzzyListenRequest extends Request { - - /** - * Set of fuzzy listening contexts. - */ - private Set contexts = new HashSet<>(); - - /** - * Constructs an empty ConfigBatchFuzzyListenRequest. - */ - public ConfigBatchFuzzyListenRequest() { - } - - /** - * Adds a new context to the request. - * - * @param tenant The namespace or tenant associated with the configurations - * @param group The group associated with the configurations - * @param dataIdPattern The pattern for matching data IDs - * @param dataIds Set of data IDs - * @param listen Flag indicating whether to listen for changes - * @param isInitializing Flag indicating whether the client is initializing - */ - public void addContext(String tenant, String group, String dataIdPattern, Set dataIds, boolean listen, - boolean isInitializing) { - contexts.add( - new Context(StringUtils.isEmpty(tenant) ? Constants.DEFAULT_NAMESPACE_ID : tenant, group, dataIdPattern, - dataIds, listen, isInitializing)); - } - - /** - * Get the set of fuzzy listening contexts. - * - * @return The set of contexts - */ - public Set getContexts() { - return contexts; - } - - /** - * Set the set of fuzzy listening contexts. - * - * @param contexts The set of contexts to be set - */ - public void setContexts(Set contexts) { - this.contexts = contexts; - } - - /** - * Get the module name for this request. - * - * @return The module name - */ - @Override - public String getModule() { - return Constants.Config.CONFIG_MODULE; - } - - /** - * Represents a fuzzy listening context. - */ - public static class Context { - - /** - * The namespace or tenant associated with the configurations. - */ - private String tenant; - - /** - * The group associated with the configurations. - */ - private String group; - - /** - * The pattern for matching data IDs. - */ - private String dataIdPattern; - - /** - * Set of data IDs. - */ - private Set dataIds; - - /** - * Flag indicating whether to listen for changes. - */ - private boolean listen; - - /** - * Flag indicating whether the client is initializing. - */ - private boolean isInitializing; - - /** - * Constructs an empty Context. - */ - public Context() { - } - - /** - * Constructs a Context with the specified parameters. - * - * @param tenant The namespace or tenant associated with the configurations - * @param group The group associated with the configurations - * @param dataIdPattern The pattern for matching data IDs - * @param dataIds Set of data IDs - * @param listen Flag indicating whether to listen for changes - * @param isInitializing Flag indicating whether the client is initializing - */ - public Context(String tenant, String group, String dataIdPattern, Set dataIds, boolean listen, - boolean isInitializing) { - this.tenant = tenant; - this.group = group; - this.dataIdPattern = dataIdPattern; - this.dataIds = dataIds; - this.listen = listen; - this.isInitializing = isInitializing; - } - - public String getTenant() { - return tenant; - } - - public void setTenant(String tenant) { - this.tenant = tenant; - } - - public String getGroup() { - return group; - } - - public void setGroup(String group) { - this.group = group; - } - - public String getDataIdPattern() { - return dataIdPattern; - } - - public void setDataIdPattern(String dataIdPattern) { - this.dataIdPattern = dataIdPattern; - } - - public Set getDataIds() { - return dataIds; - } - - public void setDataIds(Set dataIds) { - this.dataIds = dataIds; - } - - public boolean isListen() { - return listen; - } - - public void setListen(boolean listen) { - this.listen = listen; - } - - public boolean isInitializing() { - return isInitializing; - } - - public void setInitializing(boolean initializing) { - isInitializing = initializing; - } - - /** - * Indicates whether some other object is "equal to" this one. - * - * @param o The reference object with which to compare - * @return True if this object is the same as the obj argument, false otherwise - */ - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - Context that = (Context) o; - return Objects.equals(tenant, that.tenant) && Objects.equals(group, that.group) && Objects.equals( - dataIdPattern, that.dataIdPattern) && Objects.equals(dataIds, that.dataIds) && Objects.equals( - listen, that.listen) && Objects.equals(isInitializing, that.isInitializing); - } - - /** - * Returns a hash code value for the object. - * - * @return A hash code value for this object - */ - @Override - public int hashCode() { - return Objects.hash(tenant, group, dataIdPattern, dataIds, listen, isInitializing); - } - } -} diff --git a/api/src/main/java/com/alibaba/nacos/api/config/remote/request/ConfigFuzzyWatchChangeNotifyRequest.java b/api/src/main/java/com/alibaba/nacos/api/config/remote/request/ConfigFuzzyWatchChangeNotifyRequest.java new file mode 100644 index 00000000000..aaa64a705db --- /dev/null +++ b/api/src/main/java/com/alibaba/nacos/api/config/remote/request/ConfigFuzzyWatchChangeNotifyRequest.java @@ -0,0 +1,83 @@ +/* + * 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.remote.request; + +/** + * Represents a request to notify changes when a fuzzy watched configuration changed. + * + *

This request is used to notify clients about changes in configurations that match fuzzy listening patterns. + * + * @author stone-98 + * @date 2024/3/13 + */ +public class ConfigFuzzyWatchChangeNotifyRequest extends AbstractFuzzyWatchNotifyRequest { + + /** + * The groupKey of the configuration that has changed. + */ + private String groupKey; + + /** + * Indicates whether the configuration exists or not. + */ + private String changeType; + + /** + * Constructs an empty FuzzyListenNotifyChangeRequest. + */ + public ConfigFuzzyWatchChangeNotifyRequest() { + } + + /** + * Constructs a FuzzyListenNotifyChangeRequest with the specified parameters. + * + * @param groupKey The group of the configuration that has changed + * @param changeType Indicates whether the configuration exists or not + */ + public ConfigFuzzyWatchChangeNotifyRequest(String groupKey, String changeType) { + this.groupKey = groupKey; + this.changeType = changeType; + } + + public String getGroupKey() { + return groupKey; + } + + public void setGroupKey(String groupKey) { + this.groupKey = groupKey; + } + + public String getChangeType() { + return changeType; + } + + public void setChangeType(String changeType) { + this.changeType = changeType; + } + + /** + * Returns a string representation of the FuzzyListenNotifyChangeRequest. + * + * @return A string representation of the request + */ + @Override + public String toString() { + return "FuzzyListenNotifyChangeRequest{" + '\'' + ", groupKey='" + groupKey + '\'' + ", changeType=" + + changeType + '}'; + } + +} diff --git a/api/src/main/java/com/alibaba/nacos/api/config/remote/request/ConfigFuzzyWatchRequest.java b/api/src/main/java/com/alibaba/nacos/api/config/remote/request/ConfigFuzzyWatchRequest.java new file mode 100644 index 00000000000..f61e36baac2 --- /dev/null +++ b/api/src/main/java/com/alibaba/nacos/api/config/remote/request/ConfigFuzzyWatchRequest.java @@ -0,0 +1,100 @@ +/* + * 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.remote.request; + +import com.alibaba.nacos.api.common.Constants; +import com.alibaba.nacos.api.remote.request.Request; + +import java.util.Set; + +/** + * Represents a request for batch fuzzy listening configurations. + * + *

This request is used to request batch fuzzy listening configurations from the server. It contains a set of + * contexts, each representing a fuzzy listening context. + * + * @author stone-98 + * @date 2024/3/4 + */ +public class ConfigFuzzyWatchRequest extends Request { + + /** + * The namespace or tenant associated with the configurations. + */ + private String groupKeyPattern; + + private Set receivedGroupKeys; + + /** + * Flag indicating whether to listen for changes. + */ + private String watchType; + + /** + * Flag indicating whether the client is initializing. + */ + private boolean isInitializing; + + /** + * Constructs an empty ConfigBatchFuzzyListenRequest. + */ + public ConfigFuzzyWatchRequest() { + } + + public String getGroupKeyPattern() { + return groupKeyPattern; + } + + public void setGroupKeyPattern(String groupKeyPattern) { + this.groupKeyPattern = groupKeyPattern; + } + + public Set getReceivedGroupKeys() { + return receivedGroupKeys; + } + + public void setReceivedGroupKeys(Set receivedGroupKeys) { + this.receivedGroupKeys = receivedGroupKeys; + } + + public String getWatchType() { + return watchType; + } + + public void setWatchType(String watchType) { + this.watchType = watchType; + } + + public boolean isInitializing() { + return isInitializing; + } + + public void setInitializing(boolean initializing) { + isInitializing = initializing; + } + + /** + * Get the module name for this request. + * + * @return The module name + */ + @Override + public String getModule() { + return Constants.Config.CONFIG_MODULE; + } + +} diff --git a/api/src/main/java/com/alibaba/nacos/api/config/remote/request/FuzzyListenNotifyDiffRequest.java b/api/src/main/java/com/alibaba/nacos/api/config/remote/request/ConfigFuzzyWatchSyncRequest.java similarity index 50% rename from api/src/main/java/com/alibaba/nacos/api/config/remote/request/FuzzyListenNotifyDiffRequest.java rename to api/src/main/java/com/alibaba/nacos/api/config/remote/request/ConfigFuzzyWatchSyncRequest.java index 0c925a3dad9..e6ead123c78 100644 --- a/api/src/main/java/com/alibaba/nacos/api/config/remote/request/FuzzyListenNotifyDiffRequest.java +++ b/api/src/main/java/com/alibaba/nacos/api/config/remote/request/ConfigFuzzyWatchSyncRequest.java @@ -18,12 +18,10 @@ import com.alibaba.nacos.api.common.Constants; -import java.util.HashSet; -import java.util.Objects; import java.util.Set; /** - * Represents a request to notify the difference in configurations for fuzzy listening. + * Represents a request to notify the difference between client and server side. * *

This request is used to notify clients about the difference in configurations that match fuzzy listening * patterns. @@ -31,7 +29,7 @@ * @author stone-98 * @date 2024/3/6 */ -public class FuzzyListenNotifyDiffRequest extends AbstractFuzzyListenNotifyRequest { +public class ConfigFuzzyWatchSyncRequest extends AbstractFuzzyWatchNotifyRequest { /** * The pattern used to match group keys for the configurations. @@ -43,23 +41,59 @@ public class FuzzyListenNotifyDiffRequest extends AbstractFuzzyListenNotifyReque */ private Set contexts; + /** + * see FUZZY_WATCH_INIT_NOTIFY,FINISH_FUZZY_WATCH_INIT_NOTIFY,FUZZY_WATCH_DIFF_SYNC_NOTIFY. + */ + private String syncType; + + private int totalBatch; + + private int currentBatch; + + public String getSyncType() { + return syncType; + } + + public void setSyncType(String syncType) { + this.syncType = syncType; + } + + public int getTotalBatch() { + return totalBatch; + } + + public void setTotalBatch(int totalBatch) { + this.totalBatch = totalBatch; + } + + public int getCurrentBatch() { + return currentBatch; + } + + public void setCurrentBatch(int currentBatch) { + this.currentBatch = currentBatch; + } + /** * Constructs an empty FuzzyListenNotifyDiffRequest. */ - public FuzzyListenNotifyDiffRequest() { + public ConfigFuzzyWatchSyncRequest() { } /** * Constructs a FuzzyListenNotifyDiffRequest with the specified parameters. * - * @param serviceChangedType The type of service change - * @param groupKeyPattern The pattern used to match group keys for the configurations - * @param contexts The set of contexts containing information about the configurations + * @param groupKeyPattern The pattern used to match group keys for the configurations + * @param contexts The set of contexts containing information about the configurations */ - public FuzzyListenNotifyDiffRequest(String serviceChangedType, String groupKeyPattern, Set contexts) { - super(serviceChangedType); + private ConfigFuzzyWatchSyncRequest(String syncType, String groupKeyPattern, Set contexts, int totalBatch, + int currentBatch) { this.groupKeyPattern = groupKeyPattern; this.contexts = contexts; + this.syncType = syncType; + this.currentBatch = currentBatch; + this.totalBatch = totalBatch; + } /** @@ -69,8 +103,9 @@ public FuzzyListenNotifyDiffRequest(String serviceChangedType, String groupKeyPa * @param groupKeyPattern The pattern used to match group keys for the configurations * @return An initial FuzzyListenNotifyDiffRequest */ - public static FuzzyListenNotifyDiffRequest buildInitRequest(Set contexts, String groupKeyPattern) { - return new FuzzyListenNotifyDiffRequest(Constants.ConfigChangeType.LISTEN_INIT, groupKeyPattern, contexts); + public static ConfigFuzzyWatchSyncRequest buildSyncRequest(String syncType, Set contexts, + String groupKeyPattern, int totalBatch, int currentBatch) { + return new ConfigFuzzyWatchSyncRequest(syncType, groupKeyPattern, contexts, totalBatch, currentBatch); } /** @@ -79,9 +114,8 @@ public static FuzzyListenNotifyDiffRequest buildInitRequest(Set context * @param groupKeyPattern The pattern used to match group keys for the configurations * @return A final FuzzyListenNotifyDiffRequest */ - public static FuzzyListenNotifyDiffRequest buildInitFinishRequest(String groupKeyPattern) { - return new FuzzyListenNotifyDiffRequest(Constants.ConfigChangeType.FINISH_LISTEN_INIT, groupKeyPattern, - new HashSet<>()); + public static ConfigFuzzyWatchSyncRequest buildInitFinishRequest(String groupKeyPattern) { + return new ConfigFuzzyWatchSyncRequest(Constants.FINISH_FUZZY_WATCH_INIT_NOTIFY, groupKeyPattern, null, 0, 0); } public String getGroupKeyPattern() { @@ -105,13 +139,13 @@ public void setContexts(Set contexts) { */ public static class Context { - private String tenant; - - private String group; + String groupKey; - private String dataId; - - private String type; + /** + * see {@link com.alibaba.nacos.api.common.Constants.ConfigChangedType ADD_CONFIG&} ADD_CONFIG: a new config + * should be added for clientside . DELETE_CONFIG: a config should be removed for clientside . + */ + private String changedType; /** * Constructs an empty Context object. @@ -122,69 +156,31 @@ public Context() { /** * Builds a new context object with the provided parameters. * - * @param tenant The tenant associated with the configuration. - * @param group The group associated with the configuration. - * @param dataId The data ID of the configuration. - * @param type The type of the configuration change event. + * @param groupKey The groupKey associated of the configuration. + * @param changedType The type of the configuration change event. * @return A new context object initialized with the provided parameters. */ - public static Context build(String tenant, String group, String dataId, String type) { + public static Context build(String groupKey, String changedType) { Context context = new Context(); - context.setTenant(tenant); - context.setGroup(group); - context.setDataId(dataId); - context.setType(type); + context.setGroupKey(groupKey); + context.setChangedType(changedType); return context; } - @Override - public int hashCode() { - return Objects.hash(tenant, group, dataId, tenant); - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - Context that = (Context) o; - return Objects.equals(tenant, that.tenant) && Objects.equals(group, that.group) && Objects.equals(dataId, - that.dataId) && Objects.equals(type, that.type); - } - - public String getTenant() { - return tenant; - } - - public void setTenant(String tenant) { - this.tenant = tenant; - } - - public String getGroup() { - return group; - } - - public void setGroup(String group) { - this.group = group; - } - - public String getDataId() { - return dataId; + public String getGroupKey() { + return groupKey; } - public void setDataId(String dataId) { - this.dataId = dataId; + public void setGroupKey(String groupKey) { + this.groupKey = groupKey; } - public String getType() { - return type; + public String getChangedType() { + return changedType; } - public void setType(String type) { - this.type = type; + public void setChangedType(String changedType) { + this.changedType = changedType; } } diff --git a/api/src/main/java/com/alibaba/nacos/api/config/remote/request/FuzzyListenNotifyChangeRequest.java b/api/src/main/java/com/alibaba/nacos/api/config/remote/request/FuzzyListenNotifyChangeRequest.java deleted file mode 100644 index b149d70689e..00000000000 --- a/api/src/main/java/com/alibaba/nacos/api/config/remote/request/FuzzyListenNotifyChangeRequest.java +++ /dev/null @@ -1,113 +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.api.config.remote.request; - -/** - * Represents a request to notify changes in a fuzzy listening configuration. - * - *

This request is used to notify clients about changes in configurations that match fuzzy listening patterns. - * - * @author stone-98 - * @date 2024/3/13 - */ -public class FuzzyListenNotifyChangeRequest extends AbstractFuzzyListenNotifyRequest { - - /** - * The tenant of the configuration that has changed. - */ - private String tenant; - - /** - * The group of the configuration that has changed. - */ - private String group; - - /** - * The data ID of the configuration that has changed. - */ - private String dataId; - - /** - * Indicates whether the configuration exists or not. - */ - private boolean isExist; - - /** - * Constructs an empty FuzzyListenNotifyChangeRequest. - */ - public FuzzyListenNotifyChangeRequest() { - } - - /** - * Constructs a FuzzyListenNotifyChangeRequest with the specified parameters. - * - * @param tenant The tenant of the configuration that has changed - * @param group The group of the configuration that has changed - * @param dataId The data ID of the configuration that has changed - * @param isExist Indicates whether the configuration exists or not - */ - public FuzzyListenNotifyChangeRequest(String tenant, String group, String dataId, boolean isExist) { - this.tenant = tenant; - this.group = group; - this.dataId = dataId; - this.isExist = isExist; - } - - public String getTenant() { - return tenant; - } - - public void setTenant(String tenant) { - this.tenant = tenant; - } - - public String getGroup() { - return group; - } - - public void setGroup(String group) { - this.group = group; - } - - public String getDataId() { - return dataId; - } - - public void setDataId(String dataId) { - this.dataId = dataId; - } - - public boolean isExist() { - return isExist; - } - - public void setExist(boolean exist) { - isExist = exist; - } - - /** - * Returns a string representation of the FuzzyListenNotifyChangeRequest. - * - * @return A string representation of the request - */ - @Override - public String toString() { - return "FuzzyListenNotifyChangeRequest{" + "tenant='" + tenant + '\'' + ", group='" + group + '\'' - + ", dataId='" + dataId + '\'' + ", isExist=" + isExist + '}'; - } - -} diff --git a/api/src/main/java/com/alibaba/nacos/api/config/remote/response/FuzzyListenNotifyDiffResponse.java b/api/src/main/java/com/alibaba/nacos/api/config/remote/response/ConfigFuzzyWatchChangeNotifyResponse.java similarity index 88% rename from api/src/main/java/com/alibaba/nacos/api/config/remote/response/FuzzyListenNotifyDiffResponse.java rename to api/src/main/java/com/alibaba/nacos/api/config/remote/response/ConfigFuzzyWatchChangeNotifyResponse.java index 672d8dfc800..abb8e8449d7 100644 --- a/api/src/main/java/com/alibaba/nacos/api/config/remote/response/FuzzyListenNotifyDiffResponse.java +++ b/api/src/main/java/com/alibaba/nacos/api/config/remote/response/ConfigFuzzyWatchChangeNotifyResponse.java @@ -19,11 +19,11 @@ import com.alibaba.nacos.api.remote.response.Response; /** - * FuzzyListenNotifyDiffResponse. + * FuzzyListenNotifyChangeResponse. * * @author stone-98 * @date 2024/3/18 */ -public class FuzzyListenNotifyDiffResponse extends Response { +public class ConfigFuzzyWatchChangeNotifyResponse extends Response { } diff --git a/api/src/main/java/com/alibaba/nacos/api/config/remote/response/ConfigBatchFuzzyListenResponse.java b/api/src/main/java/com/alibaba/nacos/api/config/remote/response/ConfigFuzzyWatchResponse.java similarity index 92% rename from api/src/main/java/com/alibaba/nacos/api/config/remote/response/ConfigBatchFuzzyListenResponse.java rename to api/src/main/java/com/alibaba/nacos/api/config/remote/response/ConfigFuzzyWatchResponse.java index f1eca4df7c5..bfcf065ed81 100644 --- a/api/src/main/java/com/alibaba/nacos/api/config/remote/response/ConfigBatchFuzzyListenResponse.java +++ b/api/src/main/java/com/alibaba/nacos/api/config/remote/response/ConfigFuzzyWatchResponse.java @@ -24,6 +24,6 @@ * @author stone-98 * @date 2024/3/4 */ -public class ConfigBatchFuzzyListenResponse extends Response { +public class ConfigFuzzyWatchResponse extends Response { } diff --git a/api/src/main/java/com/alibaba/nacos/api/config/remote/response/FuzzyListenNotifyChangeResponse.java b/api/src/main/java/com/alibaba/nacos/api/config/remote/response/ConfigFuzzyWatchSyncResponse.java similarity index 92% rename from api/src/main/java/com/alibaba/nacos/api/config/remote/response/FuzzyListenNotifyChangeResponse.java rename to api/src/main/java/com/alibaba/nacos/api/config/remote/response/ConfigFuzzyWatchSyncResponse.java index 1e58ec3f7dd..9b91e010e2b 100644 --- a/api/src/main/java/com/alibaba/nacos/api/config/remote/response/FuzzyListenNotifyChangeResponse.java +++ b/api/src/main/java/com/alibaba/nacos/api/config/remote/response/ConfigFuzzyWatchSyncResponse.java @@ -24,6 +24,6 @@ * @author stone-98 * @date 2024/3/18 */ -public class FuzzyListenNotifyChangeResponse extends Response { +public class ConfigFuzzyWatchSyncResponse extends Response { } 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 4ee3ee2e188..c810a75440c 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 @@ -26,67 +26,67 @@ public enum ErrorCode { /** - * success. + * success. */ SUCCESS(0, "success"), /** - * parameter missing. + * parameter missing. */ PARAMETER_MISSING(10000, "parameter missing"), /** - * access denied. + * access denied. */ ACCESS_DENIED(10001, "access denied"), /** - * data access error. + * data access error. */ DATA_ACCESS_ERROR(10002, "data access error"), /** - * 'tenant' parameter error. + * 'tenant' parameter error. */ TENANT_PARAM_ERROR(20001, "'tenant' parameter error"), /** - * parameter validate error. + * parameter validate error. */ PARAMETER_VALIDATE_ERROR(20002, "parameter validate error"), /** - * MediaType Error. + * MediaType Error. */ MEDIA_TYPE_ERROR(20003, "MediaType Error"), /** - * resource not found. + * resource not found. */ RESOURCE_NOT_FOUND(20004, "resource not found"), /** - * resource conflict. + * resource conflict. */ RESOURCE_CONFLICT(20005, "resource conflict"), /** - * config listener is null. + * config listener is null. */ CONFIG_LISTENER_IS_NULL(20006, "config listener is null"), /** - * config listener error. + * config listener error. */ CONFIG_LISTENER_ERROR(20007, "config listener error"), /** - * invalid dataId. + * invalid dataId. */ INVALID_DATA_ID(20008, "invalid dataId"), /** - * parameter mismatch. + * parameter mismatch. */ PARAMETER_MISMATCH(20009, "parameter mismatch"), @@ -111,97 +111,97 @@ public enum ErrorCode { CONFIG_GRAY_NAME_UNRECOGNIZED_ERROR(20013, "config gray name not recognized"), /** - * service name error. + * service name error. */ SERVICE_NAME_ERROR(21000, "service name error"), /** - * weight error. + * weight error. */ WEIGHT_ERROR(21001, "weight error"), /** - * instance metadata error. + * instance metadata error. */ INSTANCE_METADATA_ERROR(21002, "instance metadata error"), /** - * instance not found. + * instance not found. */ INSTANCE_NOT_FOUND(21003, "instance not found"), /** - * instance error. + * instance error. */ INSTANCE_ERROR(21004, "instance error"), /** - * service metadata error. + * service metadata error. */ SERVICE_METADATA_ERROR(21005, "service metadata error"), /** - * selector error. + * selector error. */ SELECTOR_ERROR(21006, "selector error"), /** - * service already exist. + * service already exist. */ SERVICE_ALREADY_EXIST(21007, "service already exist"), /** - * service not exist. + * service not exist. */ SERVICE_NOT_EXIST(21008, "service not exist"), /** - * service delete failure. + * service delete failure. */ SERVICE_DELETE_FAILURE(21009, "service delete failure"), /** - * healthy param miss. + * healthy param miss. */ HEALTHY_PARAM_MISS(21010, "healthy param miss"), /** - * health check still running. + * health check still running. */ HEALTH_CHECK_STILL_RUNNING(21011, "health check still running"), /** - * illegal namespace. + * illegal namespace. */ ILLEGAL_NAMESPACE(22000, "illegal namespace"), /** - * namespace not exist. + * namespace not exist. */ NAMESPACE_NOT_EXIST(22001, "namespace not exist"), /** - * namespace already exist. + * namespace already exist. */ NAMESPACE_ALREADY_EXIST(22002, "namespace already exist"), /** - * illegal state. + * illegal state. */ ILLEGAL_STATE(23000, "illegal state"), /** - * node info error. + * node info error. */ NODE_INFO_ERROR(23001, "node info error"), /** - * node down failure. + * node down failure. */ NODE_DOWN_FAILURE(23002, "node down failure"), /** - * server error. + * server error. */ SERVER_ERROR(30000, "server error"), @@ -221,7 +221,11 @@ public enum ErrorCode { DATA_EMPTY(100005, "导入的文件数据为空"), - NO_SELECTED_CONFIG(100006, "没有选择任何配置"); + NO_SELECTED_CONFIG(100006, "没有选择任何配置"), + + 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"); private final Integer code; diff --git a/api/src/main/java/com/alibaba/nacos/api/naming/NamingService.java b/api/src/main/java/com/alibaba/nacos/api/naming/NamingService.java index f10c25d3029..20034bae97e 100644 --- a/api/src/main/java/com/alibaba/nacos/api/naming/NamingService.java +++ b/api/src/main/java/com/alibaba/nacos/api/naming/NamingService.java @@ -17,6 +17,7 @@ package com.alibaba.nacos.api.naming; import com.alibaba.nacos.api.exception.NacosException; +import com.alibaba.nacos.api.naming.listener.FuzzyWatchEventWatcher; import com.alibaba.nacos.api.naming.listener.EventListener; import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.api.naming.pojo.ListView; @@ -25,6 +26,7 @@ import com.alibaba.nacos.api.selector.AbstractSelector; import java.util.List; +import java.util.concurrent.Future; /** * Naming Service. @@ -554,7 +556,7 @@ void subscribe(String serviceName, String groupName, NamingSelector selector, Ev */ void unsubscribe(String serviceName, String groupName, List clusters, EventListener listener) throws NacosException; - + /** * Unsubscribe event listener of service. * @@ -577,6 +579,78 @@ void unsubscribe(String serviceName, String groupName, List clusters, Ev void unsubscribe(String serviceName, String groupName, NamingSelector selector, EventListener listener) throws NacosException; + + /** + * According to matching rules, watch services within a specific scope, and receive notifications when + * changes occur in the services within the scope. + * When given a fixed group name, watch changes in all services under this group. + * + * @param groupNamePattern group name pattern for fuzzy watch + * @param listener event listener + * @throws NacosException nacos exception + */ + void fuzzyWatch(String groupNamePattern, FuzzyWatchEventWatcher listener) throws NacosException; + + /** + * According to matching rules, watch services within a specific scope, and receive notifications when + * changes occur in the services within the scope. + * When provided with a fixed group name and pattern of service name, watch changes in services under + * this group that match the specified pattern. + * + * @param serviceNamePattern service name pattern for fuzzy watch + * @param groupNamePattern group name pattern for fuzzy watch + * @param listener event listener + * @throws NacosException nacos exception + */ + void fuzzyWatch(String serviceNamePattern, String groupNamePattern, + FuzzyWatchEventWatcher listener) throws NacosException; + + /** + * According to matching rules, watch services within a specific scope, and receive notifications when + * changes occur in the services within the scope. + * When given a fixed group name, watch changes in all services under this group. + * + * @param groupNamePattern group name pattern for fuzzy watch + * @param listener event listener + * @return matched service keys. + * @throws NacosException nacos exception + */ + Future> fuzzyWatchWithServiceKeys(String groupNamePattern, FuzzyWatchEventWatcher listener) throws NacosException; + + /** + * According to matching rules, watch services within a specific scope, and receive notifications when + * changes occur in the services within the scope. + * When provided with a fixed group name and pattern of service name, watch changes in services under + * this group that match the specified pattern. + * + * @param serviceNamePattern service name pattern for fuzzy watch + * @param groupNamePattern group name pattern for fuzzy watch + * @param listener event listener + * @return matched service keys. + * @throws NacosException nacos exception + */ + Future> fuzzyWatchWithServiceKeys(String serviceNamePattern, String groupNamePattern, + FuzzyWatchEventWatcher listener) throws NacosException; + + /** + * Cancel fuzzy watch, and remove event listener of a pattern. + * + * @param groupNamePattern group name for fuzzy watch + * @param listener event listener + * @throws NacosException nacos exception + */ + void cancelFuzzyWatch(String groupNamePattern, FuzzyWatchEventWatcher listener) throws NacosException; + + /** + * Cancel fuzzy watch, and remove event listener of a pattern. + * + * @param serviceNamePattern service name pattern for fuzzy watch + * @param groupNamePattern fixed group name for fuzzy watch + * @param listener event listener + * @throws NacosException nacos exception + */ + void cancelFuzzyWatch(String serviceNamePattern, String groupNamePattern, FuzzyWatchEventWatcher listener) throws NacosException; + /** * Get all service names from server. * 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 new file mode 100644 index 00000000000..fa10d05f1ea --- /dev/null +++ b/api/src/main/java/com/alibaba/nacos/api/naming/listener/AbstractFuzzyWatchEventWatcher.java @@ -0,0 +1,32 @@ +/* + * 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; + +import java.util.concurrent.Executor; + +/** + * Abstract fuzzy watch event listener, to support handle event by user custom executor. + * + * @author tanyongquan + */ +public abstract class AbstractFuzzyWatchEventWatcher implements FuzzyWatchEventWatcher { + + @Override + public Executor getExecutor() { + return null; + } +} diff --git a/api/src/main/java/com/alibaba/nacos/api/naming/listener/FuzzyWatchChangeEvent.java b/api/src/main/java/com/alibaba/nacos/api/naming/listener/FuzzyWatchChangeEvent.java new file mode 100644 index 00000000000..c76e20d2ae9 --- /dev/null +++ b/api/src/main/java/com/alibaba/nacos/api/naming/listener/FuzzyWatchChangeEvent.java @@ -0,0 +1,83 @@ +/* + * 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; + +/** + * Fuzzy Watch Notify Event. + * + * @author tanyongquan + */ +public class FuzzyWatchChangeEvent implements Event { + + private String serviceName; + + private String groupName; + + private String namespace; + + private String changeType; + + private String syncType; + + public FuzzyWatchChangeEvent() { + } + + public FuzzyWatchChangeEvent(String serviceName, String groupName, String namespace, String changeType, + String syncType) { + this.changeType = changeType; + this.serviceName = serviceName; + this.groupName = groupName; + this.namespace = namespace; + this.syncType = syncType; + } + + public String getServiceName() { + return serviceName; + } + + public String getGroupName() { + return groupName; + } + + public String getNamespace() { + return namespace; + } + + /** + * The change type of local watcher , contains {"ADD_SERVICE", "DELETE_SERVICE"}. see Constants.ServiceChangedType + */ + public String getChangeType() { + return changeType; + } + + /** + * the sync type that trigger this changed,contains {"FUZZY_WATCH_INIT_NOTIFY","FUZZY_WATCH_RESOURCE_CHANGED", + * "FUZZY_WATCH_DIFF_SYNC_NOTIFY"}. + * + * @return + */ + public String getSyncType() { + return syncType; + } + + @Override + public String toString() { + return "FuzzyWatchChangeEvent{" + "serviceName='" + serviceName + '\'' + ", groupName='" + groupName + '\'' + + ", namespace='" + namespace + '\'' + ", changeType='" + changeType + '\'' + ", syncType='" + syncType + + '\'' + '}'; + } +} diff --git a/api/src/main/java/com/alibaba/nacos/api/naming/listener/FuzzyWatchEventWatcher.java b/api/src/main/java/com/alibaba/nacos/api/naming/listener/FuzzyWatchEventWatcher.java new file mode 100644 index 00000000000..edd6c2a3603 --- /dev/null +++ b/api/src/main/java/com/alibaba/nacos/api/naming/listener/FuzzyWatchEventWatcher.java @@ -0,0 +1,40 @@ +/* + * 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; + +import java.util.concurrent.Executor; + +/** + * Abstract fuzzy watch event listener, to support handle event by user custom executor. + * + * @author tanyongquan + */ +public interface FuzzyWatchEventWatcher { + + /** + * executor to notify event, using nacos internal notifier if null . + * @return + */ + Executor getExecutor(); + + /** + * handle FuzzyWatchChangeEvent. + * @param event fuzzy watch change event. + */ + void onEvent(FuzzyWatchChangeEvent event); + +} diff --git a/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/AbstractFuzzyWatchNotifyRequest.java b/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/AbstractFuzzyWatchNotifyRequest.java new file mode 100644 index 00000000000..c0a776d54bb --- /dev/null +++ b/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/AbstractFuzzyWatchNotifyRequest.java @@ -0,0 +1,51 @@ +/* + * 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.remote.request; + +import com.alibaba.nacos.api.remote.request.ServerRequest; + +import static com.alibaba.nacos.api.common.Constants.Naming.NAMING_MODULE; + +/** + * Abstract fuzzy watch notify request, including basic fuzzy watch notify information. + * + * @author tanyongquan + */ +public abstract class AbstractFuzzyWatchNotifyRequest extends ServerRequest { + + private String syncType; + + public AbstractFuzzyWatchNotifyRequest() { + } + + public AbstractFuzzyWatchNotifyRequest(String syncType) { + this.syncType = syncType; + } + + public String getSyncType() { + return syncType; + } + + public void setSyncType(String syncType) { + this.syncType = syncType; + } + + @Override + public String getModule() { + return NAMING_MODULE; + } +} 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 new file mode 100644 index 00000000000..3abade19bc2 --- /dev/null +++ b/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/NamingFuzzyWatchChangeNotifyRequest.java @@ -0,0 +1,55 @@ +/* + * 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.remote.request; + +/** + * Nacos fuzzy watch notify service change request, use it when one of the services changes. + * + * @author tanyongquan + */ +public class NamingFuzzyWatchChangeNotifyRequest extends AbstractFuzzyWatchNotifyRequest { + + private String serviceKey; + + private String changedType; + + public NamingFuzzyWatchChangeNotifyRequest() { + + } + + public NamingFuzzyWatchChangeNotifyRequest(String serviceKey, String changedType, String syncType) { + super(syncType); + this.serviceKey = serviceKey; + this.changedType = changedType; + } + + public String getServiceKey() { + return serviceKey; + } + + public void setServiceKey(String serviceKey) { + this.serviceKey = serviceKey; + } + + public String getChangedType() { + return changedType; + } + + public void setChangedType(String changedType) { + this.changedType = changedType; + } +} diff --git a/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/NamingFuzzyWatchRequest.java b/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/NamingFuzzyWatchRequest.java new file mode 100644 index 00000000000..695348f8b00 --- /dev/null +++ b/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/NamingFuzzyWatchRequest.java @@ -0,0 +1,98 @@ +/* + * 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.remote.request; + +import com.alibaba.nacos.api.remote.request.Request; + +import java.util.Set; + +import static com.alibaba.nacos.api.common.Constants.Naming.NAMING_MODULE; + +/** + * Nacos naming fuzzy watch service request. + * + * @author tanyongquan + */ +public class NamingFuzzyWatchRequest extends Request { + + private boolean isInitializing; + + private String namespace; + + /** + * The namespace or tenant associated with the configurations. + */ + private String groupKeyPattern; + + private Set receivedGroupKeys; + + private String watchType; + + public NamingFuzzyWatchRequest() { + } + + public NamingFuzzyWatchRequest(String groupKeyPattern, String watchType) { + this.watchType = watchType; + this.groupKeyPattern = groupKeyPattern; + } + + public String getGroupKeyPattern() { + return groupKeyPattern; + } + + public void setGroupKeyPattern(String groupKeyPattern) { + this.groupKeyPattern = groupKeyPattern; + } + + public String getWatchType() { + return watchType; + } + + public void setWatchType(String watchType) { + this.watchType = watchType; + } + + public Set getReceivedGroupKeys() { + return receivedGroupKeys; + } + + public String getNamespace() { + return namespace; + } + + public void setNamespace(String namespace) { + this.namespace = namespace; + } + + public boolean isInitializing() { + return isInitializing; + } + + public void setInitializing(boolean initializing) { + isInitializing = initializing; + } + + public void setReceivedGroupKeys(Set receivedGroupKeys) { + this.receivedGroupKeys = receivedGroupKeys; + } + + @Override + public String getModule() { + return NAMING_MODULE; + } + +} 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 new file mode 100644 index 00000000000..c7ac6ca52a4 --- /dev/null +++ b/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/NamingFuzzyWatchSyncRequest.java @@ -0,0 +1,167 @@ +/* + * 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.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 { + + /** + * The pattern used to match service keys for the services. + */ + private String groupKeyPattern; + + /** + * The set of contexts containing information about the service. + */ + private Set contexts; + + private int totalBatch; + + private int currentBatch; + + public NamingFuzzyWatchSyncRequest() { + + } + + public NamingFuzzyWatchSyncRequest(String pattern, String syncType, Set contexts) { + super(syncType); + this.groupKeyPattern = pattern; + this.contexts = contexts; + } + + public int getTotalBatch() { + return totalBatch; + } + + public void setTotalBatch(int totalBatch) { + this.totalBatch = totalBatch; + } + + public int getCurrentBatch() { + return currentBatch; + } + + 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. + * + * @param pattern pattern + * @param syncType syncType + * @param contexts contexts + * @param totalBatch totalBatch + * @param currentBatch currentBatch + * @return + */ + public static NamingFuzzyWatchSyncRequest buildSyncNotifyRequest(String pattern, String syncType, + Set contexts, int totalBatch, int currentBatch) { + NamingFuzzyWatchSyncRequest namingFuzzyWatchSyncRequest = new NamingFuzzyWatchSyncRequest(pattern, syncType, + contexts); + namingFuzzyWatchSyncRequest.currentBatch = currentBatch; + namingFuzzyWatchSyncRequest.totalBatch = totalBatch; + return namingFuzzyWatchSyncRequest; + } + + public String getGroupKeyPattern() { + return groupKeyPattern; + } + + public void setGroupKeyPattern(String groupKeyPattern) { + this.groupKeyPattern = groupKeyPattern; + } + + public Set getContexts() { + return contexts; + } + + public void setContexts(Set contexts) { + this.contexts = contexts; + } + + @Override + public String getModule() { + return NAMING_MODULE; + } + + /** + * fuzzy watch sync context. + */ + public static class Context { + + /** + * service key. + */ + String serviceKey; + + /** + * changed type. + */ + private String changedType; + + /** + * Constructs an empty Context object. + */ + public Context() { + } + + /** + * Builds a new context object with the provided parameters. + * + * @param serviceKey The groupKey associated of the configuration. + * @param changedType The type of the configuration change event. + * @return A new context object initialized with the provided parameters. + */ + public static NamingFuzzyWatchSyncRequest.Context build(String serviceKey, String changedType) { + NamingFuzzyWatchSyncRequest.Context context = new NamingFuzzyWatchSyncRequest.Context(); + context.setServiceKey(serviceKey); + context.setChangedType(changedType); + return context; + } + + public String getServiceKey() { + return serviceKey; + } + + public void setServiceKey(String serviceKey) { + this.serviceKey = serviceKey; + } + + public String getChangedType() { + return changedType; + } + + public void setChangedType(String changedType) { + this.changedType = changedType; + } + } +} diff --git a/api/src/main/java/com/alibaba/nacos/api/naming/remote/response/NamingFuzzyWatchChangeNotifyResponse.java b/api/src/main/java/com/alibaba/nacos/api/naming/remote/response/NamingFuzzyWatchChangeNotifyResponse.java new file mode 100644 index 00000000000..3fdeefe0971 --- /dev/null +++ b/api/src/main/java/com/alibaba/nacos/api/naming/remote/response/NamingFuzzyWatchChangeNotifyResponse.java @@ -0,0 +1,28 @@ +/* + * 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.remote.response; + +import com.alibaba.nacos.api.remote.response.Response; + +/** + * Response for notify fuzzy watcher. + * + * @author tanyongquan + */ +public class NamingFuzzyWatchChangeNotifyResponse extends Response { + +} diff --git a/api/src/main/java/com/alibaba/nacos/api/naming/remote/response/NamingFuzzyWatchResponse.java b/api/src/main/java/com/alibaba/nacos/api/naming/remote/response/NamingFuzzyWatchResponse.java new file mode 100644 index 00000000000..a18f81b66ec --- /dev/null +++ b/api/src/main/java/com/alibaba/nacos/api/naming/remote/response/NamingFuzzyWatchResponse.java @@ -0,0 +1,48 @@ +/* + * 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.remote.response; + +import com.alibaba.nacos.api.remote.response.Response; +import com.alibaba.nacos.api.remote.response.ResponseCode; + +/** + * Nacos naming fuzzy watch service response. + * + * @author tanyongquan + */ +public class NamingFuzzyWatchResponse extends Response { + + public NamingFuzzyWatchResponse() { + } + + public static NamingFuzzyWatchResponse buildSuccessResponse() { + return new NamingFuzzyWatchResponse(); + } + + /** + * Build fail response. + * + * @param message error message + * @return fail response + */ + public static NamingFuzzyWatchResponse buildFailResponse(String message) { + NamingFuzzyWatchResponse result = new NamingFuzzyWatchResponse(); + result.setErrorInfo(ResponseCode.FAIL.getCode(), message); + return result; + } + +} diff --git a/api/src/main/java/com/alibaba/nacos/api/naming/remote/response/NamingFuzzyWatchSyncResponse.java b/api/src/main/java/com/alibaba/nacos/api/naming/remote/response/NamingFuzzyWatchSyncResponse.java new file mode 100644 index 00000000000..90180a3b74d --- /dev/null +++ b/api/src/main/java/com/alibaba/nacos/api/naming/remote/response/NamingFuzzyWatchSyncResponse.java @@ -0,0 +1,48 @@ +/* + * 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.remote.response; + +import com.alibaba.nacos.api.remote.response.Response; +import com.alibaba.nacos.api.remote.response.ResponseCode; + +/** + * Nacos naming fuzzy watch service response. + * + * @author tanyongquan + */ +public class NamingFuzzyWatchSyncResponse extends Response { + + public NamingFuzzyWatchSyncResponse() { + } + + public static NamingFuzzyWatchSyncResponse buildSuccessResponse() { + return new NamingFuzzyWatchSyncResponse(); + } + + /** + * Build fail response. + * + * @param message error message + * @return fail response + */ + public static NamingFuzzyWatchSyncResponse buildFailResponse(String message) { + NamingFuzzyWatchSyncResponse result = new NamingFuzzyWatchSyncResponse(); + result.setErrorInfo(ResponseCode.FAIL.getCode(), message); + return result; + } + +} diff --git a/api/src/main/java/com/alibaba/nacos/api/naming/utils/NamingUtils.java b/api/src/main/java/com/alibaba/nacos/api/naming/utils/NamingUtils.java index 074c41749e2..a74e94fb6e5 100644 --- a/api/src/main/java/com/alibaba/nacos/api/naming/utils/NamingUtils.java +++ b/api/src/main/java/com/alibaba/nacos/api/naming/utils/NamingUtils.java @@ -29,6 +29,7 @@ import java.util.regex.Pattern; import static com.alibaba.nacos.api.common.Constants.CLUSTER_NAME_PATTERN_STRING; +import static com.alibaba.nacos.api.common.Constants.DEFAULT_NAMESPACE_ID; import static com.alibaba.nacos.api.common.Constants.NUMBER_PATTERN_STRING; /** @@ -67,6 +68,23 @@ public static String getGroupedName(final String serviceName, final String group return resultGroupedName.intern(); } + public static String getServiceKey(String namespace, String group, String serviceName) { + if (StringUtils.isBlank(namespace)) { + namespace = DEFAULT_NAMESPACE_ID; + } + return namespace + Constants.SERVICE_INFO_SPLITER + group + Constants.SERVICE_INFO_SPLITER + serviceName; + } + + /** + * parse service key items for serviceKey. item[0] for namespace item[1] for group item[2] for service name + * + * @param serviceKey serviceKey. + * @return + */ + public static String[] parseServiceKey(String serviceKey) { + return serviceKey.split(Constants.SERVICE_INFO_SPLITER); + } + public static String getServiceName(final String serviceNameWithGroup) { if (StringUtils.isBlank(serviceNameWithGroup)) { return StringUtils.EMPTY; @@ -157,7 +175,8 @@ public static void checkInstanceIsLegal(Instance instance) throws NacosException throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.INSTANCE_ERROR, "Instance 'heart beat interval' must less than 'heart beat timeout' and 'ip delete timeout'."); } - if (!StringUtils.isEmpty(instance.getClusterName()) && !CLUSTER_NAME_PATTERN.matcher(instance.getClusterName()).matches()) { + if (!StringUtils.isEmpty(instance.getClusterName()) && !CLUSTER_NAME_PATTERN.matcher(instance.getClusterName()) + .matches()) { throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.INSTANCE_ERROR, String.format("Instance 'clusterName' should be characters with only 0-9a-zA-Z-. (current: %s)", instance.getClusterName())); @@ -166,18 +185,21 @@ public static void checkInstanceIsLegal(Instance instance) throws NacosException /** * check batch register is Ephemeral. + * * @param instance instance * @throws NacosException NacosException */ public static void checkInstanceIsEphemeral(Instance instance) throws NacosException { if (!instance.isEphemeral()) { throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.INSTANCE_ERROR, - String.format("Batch registration does not allow persistent instance registration , Instance:%s", instance)); + String.format("Batch registration does not allow persistent instance registration , Instance:%s", + instance)); } } /** * Batch verify the validity of instances. + * * @param instances List of instances to be registered * @throws NacosException Nacos */ diff --git a/api/src/main/resources/META-INF/services/com.alibaba.nacos.api.remote.Payload b/api/src/main/resources/META-INF/services/com.alibaba.nacos.api.remote.Payload index eba07c73d27..e82f9815f25 100644 --- a/api/src/main/resources/META-INF/services/com.alibaba.nacos.api.remote.Payload +++ b/api/src/main/resources/META-INF/services/com.alibaba.nacos.api.remote.Payload @@ -45,6 +45,13 @@ com.alibaba.nacos.api.config.remote.response.ConfigQueryResponse com.alibaba.nacos.api.config.remote.response.ConfigRemoveResponse com.alibaba.nacos.api.config.remote.request.cluster.ConfigChangeClusterSyncRequest com.alibaba.nacos.api.config.remote.response.cluster.ConfigChangeClusterSyncResponse +com.alibaba.nacos.api.config.remote.request.ConfigFuzzyWatchRequest +com.alibaba.nacos.api.config.remote.response.ConfigFuzzyWatchResponse +com.alibaba.nacos.api.config.remote.request.ConfigFuzzyWatchChangeNotifyRequest +com.alibaba.nacos.api.config.remote.response.ConfigFuzzyWatchChangeNotifyResponse +com.alibaba.nacos.api.config.remote.response.ConfigFuzzyWatchSyncResponse +com.alibaba.nacos.api.config.remote.request.ConfigFuzzyWatchSyncRequest + com.alibaba.nacos.api.naming.remote.request.BatchInstanceRequest com.alibaba.nacos.api.naming.remote.request.InstanceRequest com.alibaba.nacos.api.naming.remote.request.PersistentInstanceRequest @@ -58,9 +65,9 @@ com.alibaba.nacos.api.naming.remote.response.NotifySubscriberResponse com.alibaba.nacos.api.naming.remote.response.QueryServiceResponse com.alibaba.nacos.api.naming.remote.response.ServiceListResponse com.alibaba.nacos.api.naming.remote.response.SubscribeServiceResponse -com.alibaba.nacos.api.config.remote.request.ConfigBatchFuzzyListenRequest -com.alibaba.nacos.api.config.remote.response.ConfigBatchFuzzyListenResponse -com.alibaba.nacos.api.config.remote.request.FuzzyListenNotifyChangeRequest -com.alibaba.nacos.api.config.remote.response.FuzzyListenNotifyChangeResponse -com.alibaba.nacos.api.config.remote.request.FuzzyListenNotifyDiffRequest -com.alibaba.nacos.api.config.remote.response.FuzzyListenNotifyDiffResponse +com.alibaba.nacos.api.naming.remote.request.NamingFuzzyWatchRequest +com.alibaba.nacos.api.naming.remote.response.NamingFuzzyWatchResponse +com.alibaba.nacos.api.naming.remote.request.NamingFuzzyWatchChangeNotifyRequest +com.alibaba.nacos.api.naming.remote.response.NamingFuzzyWatchChangeNotifyResponse +com.alibaba.nacos.api.naming.remote.request.NamingFuzzyWatchSyncRequest +com.alibaba.nacos.api.naming.remote.response.NamingFuzzyWatchSyncResponse diff --git a/client/src/main/java/com/alibaba/nacos/client/config/NacosConfigService.java b/client/src/main/java/com/alibaba/nacos/client/config/NacosConfigService.java index 7e921e45ed7..23997b1e613 100644 --- a/client/src/main/java/com/alibaba/nacos/client/config/NacosConfigService.java +++ b/client/src/main/java/com/alibaba/nacos/client/config/NacosConfigService.java @@ -21,15 +21,14 @@ import com.alibaba.nacos.api.config.ConfigService; import com.alibaba.nacos.api.config.ConfigType; import com.alibaba.nacos.api.config.filter.IConfigFilter; -import com.alibaba.nacos.api.config.listener.AbstractFuzzyListenListener; +import com.alibaba.nacos.api.config.listener.FuzzyWatchEventWatcher; import com.alibaba.nacos.api.config.listener.Listener; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.client.config.filter.impl.ConfigFilterChainManager; import com.alibaba.nacos.client.config.filter.impl.ConfigRequest; import com.alibaba.nacos.client.config.filter.impl.ConfigResponse; -import com.alibaba.nacos.client.config.http.ServerHttpAgent; import com.alibaba.nacos.client.config.impl.ClientWorker; -import com.alibaba.nacos.client.config.impl.FuzzyListenContext; +import com.alibaba.nacos.client.config.impl.ConfigFuzzyWatchContext; import com.alibaba.nacos.client.config.impl.ConfigServerListManager; import com.alibaba.nacos.client.config.impl.LocalConfigInfoProcessor; import com.alibaba.nacos.client.config.impl.LocalEncryptedDataKeyProcessor; @@ -42,13 +41,12 @@ import com.alibaba.nacos.common.utils.StringUtils; import org.slf4j.Logger; -import java.util.Collection; import java.util.Collections; import java.util.Properties; -import java.util.UUID; -import java.util.concurrent.CompletableFuture; +import java.util.Set; +import java.util.concurrent.Future; -import static com.alibaba.nacos.api.common.Constants.FUZZY_LISTEN_PATTERN_WILDCARD; +import static com.alibaba.nacos.api.common.Constants.ANY_PATTERN; /** * Config Impl. @@ -64,12 +62,6 @@ public class NacosConfigService implements ConfigService { private static final String DOWN = "DOWN"; - /** - * will be deleted in 2.0 later versions - */ - @Deprecated - ServerHttpAgent agent = null; - /** * long polling. */ @@ -91,8 +83,6 @@ public NacosConfigService(Properties properties) throws NacosException { serverListManager.start(); this.worker = new ClientWorker(this.configFilterChainManager, serverListManager, clientProperties); - // will be deleted in 2.0 later versions - agent = new ServerHttpAgent(serverListManager); } @@ -133,84 +123,52 @@ public void addListener(String dataId, String group, Listener listener) throws N } @Override - public void addFuzzyListener(String fixedGroupName, AbstractFuzzyListenListener listener) throws NacosException { - doFuzzyListen(FUZZY_LISTEN_PATTERN_WILDCARD, fixedGroupName, listener); + public void fuzzyWatch(String fixedGroupName, FuzzyWatchEventWatcher watcher) throws NacosException { + doAddFuzzyWatch(ANY_PATTERN, fixedGroupName, watcher); } @Override - public void addFuzzyListener(String dataIdPattern, String fixedGroupName, AbstractFuzzyListenListener listener) + public void fuzzyWatch(String dataIdPattern, String fixedGroupName, FuzzyWatchEventWatcher watcher) throws NacosException { - // only support prefix match right now - if (!dataIdPattern.endsWith(FUZZY_LISTEN_PATTERN_WILDCARD)) { - if (dataIdPattern.startsWith(FUZZY_LISTEN_PATTERN_WILDCARD)) { - throw new UnsupportedOperationException("Suffix matching for dataId is not supported yet." - + " It will be supported in future updates if needed."); - } else { - throw new UnsupportedOperationException( - "Illegal dataId pattern, please read the documentation and pass a valid pattern."); - } - } - doFuzzyListen(dataIdPattern, fixedGroupName, listener); + doAddFuzzyWatch(dataIdPattern, fixedGroupName, watcher); } @Override - public CompletableFuture> addFuzzyListenerAndGetConfigs(String fixedGroupName, - AbstractFuzzyListenListener listener) throws NacosException { - return doAddFuzzyListenerAndGetConfigs(FUZZY_LISTEN_PATTERN_WILDCARD, fixedGroupName, listener); + public Future> fuzzyWatchWithGroupKeys(String fixedGroupName, FuzzyWatchEventWatcher watcher) + throws NacosException { + return doAddFuzzyWatch(ANY_PATTERN, fixedGroupName, watcher); } @Override - public CompletableFuture> addFuzzyListenerAndGetConfigs(String dataIdPattern, - String fixedGroupName, AbstractFuzzyListenListener listener) throws NacosException { - return doAddFuzzyListenerAndGetConfigs(dataIdPattern, fixedGroupName, listener); - } - - private CompletableFuture> doAddFuzzyListenerAndGetConfigs(String dataIdPattern, - String fixedGroupName, AbstractFuzzyListenListener listener) throws NacosException { - CompletableFuture> future = new CompletableFuture<>(); - if (listener == null) { - future.completeExceptionally(new IllegalArgumentException("Listener cannot be null")); - return future; - } - addFuzzyListener(dataIdPattern, fixedGroupName, listener); - FuzzyListenContext context = worker.getFuzzyListenContext(dataIdPattern, fixedGroupName); - if (context == null) { - future.complete(Collections.emptyList()); - return future; - } - return context.waitForInitializationComplete(future); + public Future> fuzzyWatchWithGroupKeys(String dataIdPattern, String fixedGroupName, + FuzzyWatchEventWatcher watcher) throws NacosException { + return doAddFuzzyWatch(dataIdPattern, fixedGroupName, watcher); } - private void doFuzzyListen(String dataIdPattern, String fixedGroupName, AbstractFuzzyListenListener listener) - throws NacosException { - if (listener == null) { - return; - } - listener.setUuid(UUID.randomUUID().toString()); - if (!worker.containsPatternMatchCache(dataIdPattern, fixedGroupName)) { - worker.addTenantFuzzyListenListens(dataIdPattern, fixedGroupName, Collections.singletonList(listener)); - } else { - worker.duplicateFuzzyListenInit(dataIdPattern, fixedGroupName, listener); - } + private Future> doAddFuzzyWatch(String dataIdPattern, String fixedGroupName, + FuzzyWatchEventWatcher watcher) throws NacosException { + ConfigFuzzyWatchContext configFuzzyWatchContext = worker.addTenantFuzzyWatcher(dataIdPattern, fixedGroupName, + watcher); + return configFuzzyWatchContext.createNewFuture(); } @Override - public void cancelFuzzyListen(String fixedGroupName, AbstractFuzzyListenListener listener) throws NacosException { - cancelFuzzyListen(FUZZY_LISTEN_PATTERN_WILDCARD, fixedGroupName, listener); + public void cancelFuzzyWatch(String fixedGroupName, FuzzyWatchEventWatcher watcher) throws NacosException { + cancelFuzzyWatch(ANY_PATTERN, fixedGroupName, watcher); } @Override - public void cancelFuzzyListen(String dataIdPattern, String fixedGroupName, AbstractFuzzyListenListener listener) + public void cancelFuzzyWatch(String dataIdPattern, String fixedGroupName, FuzzyWatchEventWatcher watcher) throws NacosException { - doCancelFuzzyListen(dataIdPattern, fixedGroupName, listener); + doCancelFuzzyListen(dataIdPattern, fixedGroupName, watcher); } - private void doCancelFuzzyListen(String dataIdPattern, String groupNamePattern, - AbstractFuzzyListenListener listener) throws NacosException { - if (null == listener) { + private void doCancelFuzzyListen(String dataIdPattern, String groupNamePattern, FuzzyWatchEventWatcher watcher) + throws NacosException { + if (null == watcher) { return; } - worker.removeFuzzyListenListener(dataIdPattern, groupNamePattern, listener); + worker.removeFuzzyListenListener(dataIdPattern, groupNamePattern, watcher); } @Override @@ -261,11 +219,11 @@ private String getConfigInner(String tenant, String dataId, String group, long t // changing config needed in the same time, while nacos server is down. String content = LocalConfigInfoProcessor.getFailover(worker.getAgentName(), dataId, group, tenant); if (content != null) { - LOGGER.warn("[{}] [get-config] get failover ok, dataId={}, group={}, tenant={}", - worker.getAgentName(), dataId, group, tenant); + LOGGER.warn("[{}] [get-config] get failover ok, dataId={}, group={}, tenant={}", worker.getAgentName(), + dataId, group, tenant); cr.setContent(content); - String encryptedDataKey = LocalEncryptedDataKeyProcessor.getEncryptDataKeyFailover(agent.getName(), dataId, - group, tenant); + String encryptedDataKey = LocalEncryptedDataKeyProcessor.getEncryptDataKeyFailover(worker.getAgentName(), + dataId, group, tenant); cr.setEncryptedDataKey(encryptedDataKey); configFilterChainManager.doFilter(null, cr); content = cr.getContent(); @@ -290,12 +248,12 @@ private String getConfigInner(String tenant, String dataId, String group, long t content = LocalConfigInfoProcessor.getSnapshot(worker.getAgentName(), dataId, group, tenant); if (content != null) { - LOGGER.warn("[{}] [get-config] get snapshot ok, dataId={}, group={}, tenant={}", - worker.getAgentName(), dataId, group, tenant); + LOGGER.warn("[{}] [get-config] get snapshot ok, dataId={}, group={}, tenant={}", worker.getAgentName(), + dataId, group, tenant); } cr.setContent(content); - String encryptedDataKey = LocalEncryptedDataKeyProcessor.getEncryptDataKeySnapshot(agent.getName(), dataId, - group, tenant); + String encryptedDataKey = LocalEncryptedDataKeyProcessor.getEncryptDataKeySnapshot(worker.getAgentName(), + dataId, group, tenant); cr.setEncryptedDataKey(encryptedDataKey); configFilterChainManager.doFilter(null, cr); content = cr.getContent(); 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 new file mode 100644 index 00000000000..f6b9a934afd --- /dev/null +++ b/client/src/main/java/com/alibaba/nacos/client/config/impl/ClientFuzzyWatchNotifyRequestHandler.java @@ -0,0 +1,53 @@ +/* + * 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.remote.request.ConfigFuzzyWatchChangeNotifyRequest; +import com.alibaba.nacos.api.config.remote.request.ConfigFuzzyWatchSyncRequest; +import com.alibaba.nacos.api.remote.request.Request; +import com.alibaba.nacos.api.remote.response.Response; +import com.alibaba.nacos.common.remote.client.Connection; +import com.alibaba.nacos.common.remote.client.ServerRequestHandler; + +/** + * fuzzy watch request from server . + * @author shiyiyue + */ +public class ClientFuzzyWatchNotifyRequestHandler implements ServerRequestHandler { + + ConfigFuzzyWatchGroupKeyHolder configFuzzyWatchGroupKeyHolder; + + public ClientFuzzyWatchNotifyRequestHandler(ConfigFuzzyWatchGroupKeyHolder configFuzzyWatchGroupKeyHolder) { + + this.configFuzzyWatchGroupKeyHolder = configFuzzyWatchGroupKeyHolder; + } + + @Override + public Response requestReply(Request request, Connection connection) { + //fuzzy watch diff reconciliation sync + if (request instanceof ConfigFuzzyWatchSyncRequest) { + return configFuzzyWatchGroupKeyHolder.handleFuzzyWatchNotifyDiffRequest( + (ConfigFuzzyWatchSyncRequest) request); + } + //fuzzy watch changed notify for a single config. include config changed or config delete. + if (request instanceof ConfigFuzzyWatchChangeNotifyRequest) { + return configFuzzyWatchGroupKeyHolder.handlerFuzzyWatchChangeNotifyRequest( + (ConfigFuzzyWatchChangeNotifyRequest) request); + } + return null; + } +} 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 b2ab88f7774..fe04c17623e 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 @@ -19,26 +19,20 @@ import com.alibaba.nacos.api.PropertyKeyConst; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.config.ConfigType; -import com.alibaba.nacos.api.config.listener.AbstractFuzzyListenListener; +import com.alibaba.nacos.api.config.listener.FuzzyWatchEventWatcher; import com.alibaba.nacos.api.config.listener.Listener; import com.alibaba.nacos.api.config.remote.request.ClientConfigMetricRequest; -import com.alibaba.nacos.api.config.remote.request.ConfigBatchFuzzyListenRequest; import com.alibaba.nacos.api.config.remote.request.ConfigBatchListenRequest; import com.alibaba.nacos.api.config.remote.request.ConfigChangeNotifyRequest; import com.alibaba.nacos.api.config.remote.request.ConfigPublishRequest; import com.alibaba.nacos.api.config.remote.request.ConfigQueryRequest; import com.alibaba.nacos.api.config.remote.request.ConfigRemoveRequest; -import com.alibaba.nacos.api.config.remote.request.FuzzyListenNotifyChangeRequest; -import com.alibaba.nacos.api.config.remote.request.FuzzyListenNotifyDiffRequest; import com.alibaba.nacos.api.config.remote.response.ClientConfigMetricResponse; -import com.alibaba.nacos.api.config.remote.response.ConfigBatchFuzzyListenResponse; import com.alibaba.nacos.api.config.remote.response.ConfigChangeBatchListenResponse; import com.alibaba.nacos.api.config.remote.response.ConfigChangeNotifyResponse; import com.alibaba.nacos.api.config.remote.response.ConfigPublishResponse; import com.alibaba.nacos.api.config.remote.response.ConfigQueryResponse; import com.alibaba.nacos.api.config.remote.response.ConfigRemoveResponse; -import com.alibaba.nacos.api.config.remote.response.FuzzyListenNotifyChangeResponse; -import com.alibaba.nacos.api.config.remote.response.FuzzyListenNotifyDiffResponse; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.remote.RemoteConstants; import com.alibaba.nacos.api.remote.request.Request; @@ -72,7 +66,6 @@ import com.alibaba.nacos.common.remote.client.ServerListFactory; import com.alibaba.nacos.common.utils.ConnLabelsUtils; import com.alibaba.nacos.common.utils.ConvertUtils; -import com.alibaba.nacos.common.utils.GroupKeyPattern; import com.alibaba.nacos.common.utils.JacksonUtils; import com.alibaba.nacos.common.utils.MD5Utils; import com.alibaba.nacos.common.utils.StringUtils; @@ -92,7 +85,6 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.Properties; import java.util.Set; import java.util.UUID; @@ -138,13 +130,10 @@ public class ClientWorker implements Closeable { */ private final AtomicReference> cacheMap = new AtomicReference<>(new HashMap<>()); - /** - * fuzzyListenGroupKey -> fuzzyListenContext. - */ - private final AtomicReference> fuzzyListenContextMap = new AtomicReference<>( - new HashMap<>()); private final DefaultLabelsCollectorManager defaultLabelsCollectorManager = new DefaultLabelsCollectorManager(); + private ConfigFuzzyWatchGroupKeyHolder configFuzzyWatchGroupKeyHolder; + private Map appLables = new HashMap<>(); private final ConfigFilterChainManager configFilterChainManager; @@ -170,49 +159,6 @@ public class ClientWorker implements Closeable { */ private final List taskIdCacheCountList = new ArrayList<>(); - /** - * index(taskId)-> total context count for this taskId. - */ - private final List taskIdContextCountList = new ArrayList<>(); - - @SuppressWarnings("PMD.ThreadPoolCreationRule") - public ClientWorker(final ConfigFilterChainManager configFilterChainManager, ServerListManager serverListManager, - final NacosClientProperties properties) throws NacosException { - this.configFilterChainManager = configFilterChainManager; - - init(properties); - - agent = new ConfigRpcTransportClient(properties, serverListManager); - ScheduledExecutorService executorService = Executors.newScheduledThreadPool(initWorkerThreadCount(properties), - new NameThreadFactory("com.alibaba.nacos.client.Worker")); - agent.setExecutor(executorService); - agent.start(); - - } - - /** - * Adds a list of fuzzy listen listeners for the specified data ID pattern and group. - * - * @param dataIdPattern The pattern of the data ID to listen for. - * @param group The group of the configuration. - * @param listeners The list of listeners to add. - * @throws NacosException If an error occurs while adding the listeners. - */ - public void addTenantFuzzyListenListens(String dataIdPattern, String group, - List listeners) throws NacosException { - group = blank2defaultGroup(group); - FuzzyListenContext context = addFuzzyListenContextIfAbsent(dataIdPattern, group); - synchronized (context) { - for (AbstractFuzzyListenListener listener : listeners) { - context.addListener(listener); - } - context.setInitializing(true); - context.setDiscard(false); - context.getIsConsistentWithServer().set(false); - agent.notifyFuzzyListenConfig(); - } - } - /** * Add listeners for data. * @@ -344,27 +290,16 @@ public void removeTenantListener(String dataId, String group, Listener listener) } /** - * Initializes a duplicate fuzzy listen for the specified data ID pattern, group, and listener. + * Adds a list of fuzzy listen listeners for the specified data ID pattern and group. * - * @param dataIdPattern The pattern of the data ID to listen for. - * @param group The group of the configuration. - * @param listener The listener to add. + * @param dataIdPattern The pattern of the data ID to listen for. + * @param groupPattern The group of the configuration. + * @param fuzzyWatchEventWatcher The configFuzzyWatcher to add. + * @throws NacosException If an error occurs while adding the listeners. */ - public void duplicateFuzzyListenInit(String dataIdPattern, String group, AbstractFuzzyListenListener listener) { - String groupKeyPattern = GroupKeyPattern.generateFuzzyListenGroupKeyPattern(dataIdPattern, group); - Map contextMap = fuzzyListenContextMap.get(); - FuzzyListenContext context = contextMap.get(groupKeyPattern); - if (Objects.isNull(context)) { - return; - } - synchronized (context) { - context.addListener(listener); - - for (String dataId : context.getDataIds()) { - NotifyCenter.publishEvent(FuzzyListenNotifyEvent.buildNotifyPatternSpecificListenerEvent(group, dataId, - Constants.ConfigChangeType.ADD_CONFIG, groupKeyPattern, listener.getUuid())); - } - } + public ConfigFuzzyWatchContext addTenantFuzzyWatcher(String dataIdPattern, String groupPattern, + FuzzyWatchEventWatcher fuzzyWatchEventWatcher) { + return configFuzzyWatchGroupKeyHolder.registerFuzzyWatcher(dataIdPattern, groupPattern, fuzzyWatchEventWatcher); } /** @@ -372,43 +307,26 @@ public void duplicateFuzzyListenInit(String dataIdPattern, String group, Abstrac * * @param dataIdPattern The pattern of the data ID. * @param group The group of the configuration. - * @param listener The listener to remove. + * @param watcher The listener to remove. * @throws NacosException If an error occurs while removing the listener. */ - public void removeFuzzyListenListener(String dataIdPattern, String group, AbstractFuzzyListenListener listener) - throws NacosException { - group = blank2defaultGroup(group); - FuzzyListenContext fuzzyListenContext = getFuzzyListenContext(dataIdPattern, group); - if (fuzzyListenContext != null) { - synchronized (fuzzyListenContext) { - fuzzyListenContext.removeListener(listener); - if (fuzzyListenContext.getListeners().isEmpty()) { - fuzzyListenContext.setDiscard(true); - fuzzyListenContext.getIsConsistentWithServer().set(false); - agent.removeFuzzyListenContext(dataIdPattern, group); - } - } - } + public void removeFuzzyListenListener(String dataIdPattern, String group, FuzzyWatchEventWatcher watcher) { + configFuzzyWatchGroupKeyHolder.removeFuzzyWatcher(dataIdPattern, group, watcher); } - /** - * Removes the fuzzy listen context for the specified data ID pattern and group. - * - * @param dataIdPattern The pattern of the data ID. - * @param group The group of the configuration. - */ - public void removeFuzzyListenContext(String dataIdPattern, String group) { - String groupKeyPattern = GroupKeyPattern.generateFuzzyListenGroupKeyPattern(dataIdPattern, group); - synchronized (fuzzyListenContextMap) { - Map copy = new HashMap<>(fuzzyListenContextMap.get()); - FuzzyListenContext removedContext = copy.remove(groupKeyPattern); - if (removedContext != null) { - decreaseContextTaskIdCount(removedContext.getTaskId()); + void removeCache(String dataId, String group, String tenant) { + String groupKey = GroupKey.getKeyTenant(dataId, group, tenant); + synchronized (cacheMap) { + Map copy = new HashMap<>(cacheMap.get()); + CacheData remove = copy.remove(groupKey); + if (remove != null) { + decreaseTaskIdCount(remove.getTaskId()); } - fuzzyListenContextMap.set(copy); + cacheMap.set(copy); } - LOGGER.info("[{}] [fuzzy-listen-unsubscribe] {}", agent.getName(), groupKeyPattern); - // TODO: Record metric for fuzzy listen unsubscribe. + LOGGER.info("[{}] [unsubscribe] {}", agent.getName(), groupKey); + + MetricsMonitor.getListenConfigCountMonitor().set(cacheMap.get().size()); } /** @@ -535,28 +453,6 @@ public CacheData addCacheDataIfAbsent(String dataId, String group, String tenant return cache; } - /** - * Removes the cache entry associated with the given data ID, group, and tenant. - * - * @param dataId The data ID. - * @param group The group name. - * @param tenant The tenant. - */ - public void removeCache(String dataId, String group, String tenant) { - String groupKey = GroupKey.getKeyTenant(dataId, group, tenant); - synchronized (cacheMap) { - Map copy = new HashMap<>(cacheMap.get()); - CacheData remove = copy.remove(groupKey); - if (remove != null) { - decreaseTaskIdCount(remove.getTaskId()); - } - cacheMap.set(copy); - } - LOGGER.info("[{}] [unsubscribe] {}", agent.getName(), groupKey); - - MetricsMonitor.getListenConfigCountMonitor().set(cacheMap.get().size()); - } - /** * Put cache. * @@ -571,114 +467,23 @@ private void putCache(String key, CacheData cache) { } } - /** - * Adds a fuzzy listen context if it doesn't already exist for the specified data ID pattern and group. If the - * context already exists, returns the existing context. - * - * @param dataIdPattern The pattern of the data ID. - * @param group The group of the configuration. - * @return The fuzzy listen context for the specified data ID pattern and group. - */ - public FuzzyListenContext addFuzzyListenContextIfAbsent(String dataIdPattern, String group) { - FuzzyListenContext context = getFuzzyListenContext(dataIdPattern, group); - if (context != null) { - return context; - } - synchronized (fuzzyListenContextMap) { - FuzzyListenContext contextFromMap = getFuzzyListenContext(dataIdPattern, group); - if (contextFromMap != null) { - context = contextFromMap; - context.getIsConsistentWithServer().set(false); - } else { - context = new FuzzyListenContext(agent.getName(), dataIdPattern, group); - int taskId = calculateContextTaskId(); - increaseContextTaskIdCount(taskId); - context.setTaskId(taskId); - } - } - - Map copy = new HashMap<>(fuzzyListenContextMap.get()); - String groupKeyPattern = GroupKeyPattern.generateFuzzyListenGroupKeyPattern(dataIdPattern, group); - copy.put(groupKeyPattern, context); - fuzzyListenContextMap.set(copy); - - // TODO: Record metrics - - return context; - } - - /** - * Increases the count for the specified task ID in the given count list. - * - * @param taskId The ID of the task for which the count needs to be increased. - */ private void increaseTaskIdCount(int taskId) { - increaseCount(taskId, taskIdCacheCountList); + taskIdCacheCountList.get(taskId).incrementAndGet(); } - /** - * Decreases the count for the specified task ID in the given count list. - * - * @param taskId The ID of the task for which the count needs to be decreased. - */ private void decreaseTaskIdCount(int taskId) { - decreaseCount(taskId, taskIdCacheCountList); + taskIdCacheCountList.get(taskId).decrementAndGet(); } - /** - * Increases the context task ID count in the corresponding list. - * - * @param taskId The ID of the context task for which the count needs to be increased. - */ - private void increaseContextTaskIdCount(int taskId) { - increaseCount(taskId, taskIdContextCountList); - } - - /** - * Decreases the context task ID count in the corresponding list. - * - * @param taskId The ID of the context task for which the count needs to be decreased. - */ - private void decreaseContextTaskIdCount(int taskId) { - decreaseCount(taskId, taskIdContextCountList); - } - - /** - * Calculates the task ID based on the configuration size. - * - * @return The calculated task ID. - */ private int calculateTaskId() { - return calculateId(taskIdCacheCountList, (long) ParamUtil.getPerTaskConfigSize()); - } - - /** - * Calculates the context task ID based on the configuration size. - * - * @return The calculated context task ID. - */ - private int calculateContextTaskId() { - return calculateId(taskIdContextCountList, (long) ParamUtil.getPerTaskContextSize()); - } - - /** - * Increases the count for the specified task ID in the given count list. - * - * @param taskId The ID of the task for which the count needs to be increased. - * @param countList The list containing the counts for different task IDs. - */ - private void increaseCount(int taskId, List countList) { - countList.get(taskId).incrementAndGet(); - } - - /** - * Decreases the count for the specified task ID in the given count list. - * - * @param taskId The ID of the task for which the count needs to be decreased. - * @param countList The list containing the counts for different task IDs. - */ - private void decreaseCount(int taskId, List countList) { - countList.get(taskId).decrementAndGet(); + int perTaskSize = (int) ParamUtil.getPerTaskConfigSize(); + for (int index = 0; index < taskIdCacheCountList.size(); index++) { + if (taskIdCacheCountList.get(index).get() < perTaskSize) { + return index; + } + } + taskIdCacheCountList.add(new AtomicInteger(0)); + return taskIdCacheCountList.size() - 1; } public CacheData getCache(String dataId, String group) { @@ -692,35 +497,6 @@ public CacheData getCache(String dataId, String group, String tenant) { return cacheMap.get().get(GroupKey.getKeyTenant(dataId, group, tenant)); } - /** - * Calculates the task ID based on the provided count list and per-task size. - * - * @param countList The list containing the counts for different task IDs. - * @param perTaskSize The size of each task. - * @return The calculated task ID. - */ - private int calculateId(List countList, long perTaskSize) { - for (int index = 0; index < countList.size(); index++) { - if (countList.get(index).get() < perTaskSize) { - return index; - } - } - countList.add(new AtomicInteger(0)); - return countList.size() - 1; - } - - /** - * Retrieves the FuzzyListenContext for the given data ID pattern and group. - * - * @param dataIdPattern The data ID pattern. - * @param group The group name. - * @return The corresponding FuzzyListenContext, or null if not found. - */ - public FuzzyListenContext getFuzzyListenContext(String dataIdPattern, String group) { - return fuzzyListenContextMap.get() - .get(GroupKeyPattern.generateFuzzyListenGroupKeyPattern(dataIdPattern, group)); - } - public ConfigResponse getServerConfig(String dataId, String group, String tenant, long readTimeout, boolean notify) throws NacosException { if (StringUtils.isBlank(group)) { @@ -733,31 +509,21 @@ private String blank2defaultGroup(String group) { return StringUtils.isBlank(group) ? Constants.DEFAULT_GROUP : group.trim(); } - /** - * Checks if the pattern match cache contains an entry for the specified data ID pattern and group. - * - * @param dataIdPattern The data ID pattern. - * @param group The group name. - * @return True if the cache contains an entry, false otherwise. - */ - public boolean containsPatternMatchCache(String dataIdPattern, String group) { - Map contextMap = fuzzyListenContextMap.get(); - String groupKeyPattern = GroupKeyPattern.generateFuzzyListenGroupKeyPattern(dataIdPattern, group); - return contextMap.containsKey(groupKeyPattern); - } - @SuppressWarnings("PMD.ThreadPoolCreationRule") - public ClientWorker(final ConfigFilterChainManager configFilterChainManager, ConfigServerListManager serverListManager, - final NacosClientProperties properties) throws NacosException { + public ClientWorker(final ConfigFilterChainManager configFilterChainManager, + ConfigServerListManager serverListManager, final NacosClientProperties properties) throws NacosException { this.configFilterChainManager = configFilterChainManager; init(properties); agent = new ConfigRpcTransportClient(properties, serverListManager); + + configFuzzyWatchGroupKeyHolder = new ConfigFuzzyWatchGroupKeyHolder(agent, uuid); ScheduledExecutorService executorService = Executors.newScheduledThreadPool(initWorkerThreadCount(properties), new NameThreadFactory("com.alibaba.nacos.client.Worker")); agent.setExecutor(executorService); agent.start(); + configFuzzyWatchGroupKeyHolder.start(); } @@ -852,28 +618,14 @@ public boolean isHealthServer() { public class ConfigRpcTransportClient extends ConfigTransportClient { - /** - * 5 minutes to check all fuzzy listen context. - */ - private static final long FUZZY_LISTEN_ALL_SYNC_INTERNAL = 5 * 60 * 1000L; - - private final String configListenerTaskPrefix = "nacos.client.config.listener.task"; - - private final String fuzzyListenerTaskPrefix = "nacos.client.config.fuzzyListener.task"; + Map multiTaskExecutor = new HashMap<>(); private final BlockingQueue listenExecutebell = new ArrayBlockingQueue<>(1); - private final Map multiTaskExecutor = new HashMap<>(); - private final Object bellItem = new Object(); private long lastAllSyncTime = System.currentTimeMillis(); - /** - * fuzzyListenExecuteBell. - */ - private final BlockingQueue fuzzyListenExecuteBell = new ArrayBlockingQueue<>(1); - Subscriber subscriber = null; /** @@ -881,11 +633,6 @@ public class ConfigRpcTransportClient extends ConfigTransportClient { */ private static final long ALL_SYNC_INTERNAL = 3 * 60 * 1000L; - /** - * fuzzyListenLastAllSyncTime. - */ - private long fuzzyListenLastAllSyncTime = System.currentTimeMillis(); - public ConfigRpcTransportClient(NacosClientProperties properties, ConfigServerListManager serverListManager) { super(properties, serverListManager); } @@ -949,85 +696,6 @@ private Map getLabels() { return labels; } - /** - * Handles a fuzzy listen init notify request. - * - *

This method processes the incoming fuzzy listen init notify request from a client. It updates the fuzzy - * listen context based on the request's information, and publishes events if necessary. - * - * @param request The fuzzy listen init notify request to handle. - * @param clientName The name of the client sending the request. - * @return A {@link FuzzyListenNotifyDiffResponse} indicating the result of handling the request. - */ - private FuzzyListenNotifyDiffResponse handleFuzzyListenNotifyDiffRequest(FuzzyListenNotifyDiffRequest request, - String clientName) { - LOGGER.info("[{}] [fuzzy-listen-config-push] config init.", clientName); - String groupKeyPattern = request.getGroupKeyPattern(); - FuzzyListenContext context = fuzzyListenContextMap.get().get(groupKeyPattern); - if (Constants.ConfigChangeType.FINISH_LISTEN_INIT.equals(request.getServiceChangedType())) { - context.markInitializationComplete(); - return new FuzzyListenNotifyDiffResponse(); - } - for (FuzzyListenNotifyDiffRequest.Context requestContext : request.getContexts()) { - Set existsDataIds = context.getDataIds(); - switch (requestContext.getType()) { - case Constants.ConfigChangeType.LISTEN_INIT: - case Constants.ConfigChangeType.ADD_CONFIG: - if (existsDataIds.add(requestContext.getDataId())) { - NotifyCenter.publishEvent(FuzzyListenNotifyEvent.buildNotifyPatternAllListenersEvent( - requestContext.getGroup(), requestContext.getDataId(), request.getGroupKeyPattern(), - Constants.ConfigChangeType.ADD_CONFIG)); - } - break; - case Constants.ConfigChangeType.DELETE_CONFIG: - if (existsDataIds.remove(requestContext.getDataId())) { - NotifyCenter.publishEvent(FuzzyListenNotifyEvent.buildNotifyPatternAllListenersEvent( - requestContext.getGroup(), requestContext.getDataId(), request.getGroupKeyPattern(), - Constants.ConfigChangeType.DELETE_CONFIG)); - } - break; - default: - LOGGER.error("Invalid config change type: {}", requestContext.getType()); - break; - } - } - return new FuzzyListenNotifyDiffResponse(); - } - - /** - * Handles a fuzzy listen notify change request. - * - *

This method processes the incoming fuzzy listen notify change request from a client. It updates the fuzzy - * listen context based on the request's information, and publishes events if necessary. - * - * @param request The fuzzy listen notify change request to handle. - * @param clientName The name of the client sending the request. - */ - private FuzzyListenNotifyChangeResponse handlerFuzzyListenNotifyChangeRequest( - FuzzyListenNotifyChangeRequest request, String clientName) { - LOGGER.info("[{}] [fuzzy-listen-config-push] config changed.", clientName); - Map listenContextMap = fuzzyListenContextMap.get(); - Set matchedPatterns = GroupKeyPattern.getConfigMatchedPatternsWithoutNamespace(request.getDataId(), - request.getGroup(), listenContextMap.keySet()); - for (String matchedPattern : matchedPatterns) { - FuzzyListenContext context = listenContextMap.get(matchedPattern); - if (request.isExist()) { - if (context.getDataIds().add(request.getDataId())) { - NotifyCenter.publishEvent( - FuzzyListenNotifyEvent.buildNotifyPatternAllListenersEvent(request.getGroup(), - request.getDataId(), matchedPattern, Constants.ConfigChangeType.ADD_CONFIG)); - } - } else { - if (context.getDataIds().remove(request.getDataId())) { - NotifyCenter.publishEvent( - FuzzyListenNotifyEvent.buildNotifyPatternAllListenersEvent(request.getGroup(), - request.getDataId(), matchedPattern, Constants.ConfigChangeType.DELETE_CONFIG)); - } - } - } - return new FuzzyListenNotifyChangeResponse(); - } - ConfigChangeNotifyResponse handleConfigChangeNotifyRequest(ConfigChangeNotifyRequest configChangeNotifyRequest, String clientName) { LOGGER.info("[{}] [server-push] config changed. dataId={}, group={},tenant={}", clientName, @@ -1059,18 +727,11 @@ private void initRpcClientHandler(final RpcClient rpcClientInner) { * Register Config Change /Config ReSync Handler */ rpcClientInner.registerServerRequestHandler((request, connection) -> { + //config change notify if (request instanceof ConfigChangeNotifyRequest) { return handleConfigChangeNotifyRequest((ConfigChangeNotifyRequest) request, rpcClientInner.getName()); } - if (request instanceof FuzzyListenNotifyDiffRequest) { - return handleFuzzyListenNotifyDiffRequest((FuzzyListenNotifyDiffRequest) request, - rpcClientInner.getName()); - } - if (request instanceof FuzzyListenNotifyChangeRequest) { - return handlerFuzzyListenNotifyChangeRequest((FuzzyListenNotifyChangeRequest) request, - rpcClientInner.getName()); - } return null; }); @@ -1081,6 +742,9 @@ private void initRpcClientHandler(final RpcClient rpcClientInner) { return null; }); + rpcClientInner.registerServerRequestHandler( + new ClientFuzzyWatchNotifyRequestHandler(configFuzzyWatchGroupKeyHolder)); + rpcClientInner.registerConnectionListener(new ConnectionEventListener() { @Override @@ -1089,13 +753,13 @@ public void onConnected(Connection connection) { notifyListenConfig(); LOGGER.info("[{}] Connected,notify fuzzy listen context...", rpcClientInner.getName()); - notifyFuzzyListenConfig(); + configFuzzyWatchGroupKeyHolder.notifyFuzzyWatchSync(); } @Override public void onDisConnect(Connection connection) { String taskId = rpcClientInner.getLabels().get("taskId"); - LOGGER.info("[{}] DisConnected,clear listen context...", rpcClientInner.getName()); + LOGGER.info("[{}] DisConnected,reset listen context", rpcClientInner.getName()); Collection values = cacheMap.get().values(); for (CacheData cacheData : values) { @@ -1108,17 +772,8 @@ public void onDisConnect(Connection connection) { } } - Collection fuzzyListenContexts = fuzzyListenContextMap.get().values(); - - for (FuzzyListenContext context : fuzzyListenContexts) { - if (StringUtils.isNotBlank(taskId)) { - if (Integer.valueOf(taskId).equals(context.getTaskId())) { - context.getIsConsistentWithServer().set(false); - } - } else { - context.getIsConsistentWithServer().set(false); - } - } + LOGGER.info("[{}] DisConnected,reset fuzzy watch consistence status", rpcClientInner.getName()); + configFuzzyWatchGroupKeyHolder.resetConsistenceStatus(); } }); @@ -1155,25 +810,6 @@ public Class subscribeType() { } }; NotifyCenter.registerSubscriber(subscriber); - - NotifyCenter.registerSubscriber(new Subscriber() { - @Override - public void onEvent(Event event) { - FuzzyListenNotifyEvent fuzzyListenNotifyEvent = (FuzzyListenNotifyEvent) event; - FuzzyListenContext context = fuzzyListenContextMap.get() - .get(fuzzyListenNotifyEvent.getGroupKeyPattern()); - if (context == null) { - return; - } - context.notifyListener(fuzzyListenNotifyEvent.getDataId(), fuzzyListenNotifyEvent.getType(), - fuzzyListenNotifyEvent.getUuid()); - } - - @Override - public Class subscribeType() { - return FuzzyListenNotifyEvent.class; - } - }); } @Override @@ -1198,26 +834,6 @@ public void startInternal() { } }, 0L, TimeUnit.MILLISECONDS); - executor.schedule(() -> { - while (!executor.isShutdown() && !executor.isTerminated()) { - try { - fuzzyListenExecuteBell.poll(5L, TimeUnit.SECONDS); - if (executor.isShutdown() || executor.isTerminated()) { - continue; - } - executeConfigFuzzyListen(); - } catch (Throwable e) { - LOGGER.error("[rpc-fuzzy-listen-execute] rpc fuzzy listen exception", e); - try { - Thread.sleep(50L); - } catch (InterruptedException interruptedException) { - //ignore - } - notifyFuzzyListenConfig(); - } - } - }, 0L, TimeUnit.MILLISECONDS); - } @Override @@ -1230,11 +846,6 @@ public void notifyListenConfig() { listenExecutebell.offer(bellItem); } - @Override - public void notifyFuzzyListenConfig() { - fuzzyListenExecuteBell.offer(bellItem); - } - @Override public void executeConfigListen() throws NacosException { @@ -1290,118 +901,6 @@ public void executeConfigListen() throws NacosException { } - /** - * Execute fuzzy listen configuration changes. - * - *

This method iterates through all fuzzy listen contexts and determines whether they need to be added or - * removed based on their consistency with the server and discard status. It then calls the appropriate method - * to execute the fuzzy listen operation. - * - * @throws NacosException If an error occurs during the execution of fuzzy listen configuration changes. - */ - @Override - public void executeConfigFuzzyListen() throws NacosException { - Map> needSyncContextMap = new HashMap<>(16); - - // Obtain the current timestamp - long now = System.currentTimeMillis(); - - // Determine whether a full synchronization is needed - boolean needAllSync = now - fuzzyListenLastAllSyncTime >= FUZZY_LISTEN_ALL_SYNC_INTERNAL; - - // Iterate through all fuzzy listen contexts - for (FuzzyListenContext context : fuzzyListenContextMap.get().values()) { - // Check if the context is consistent with the server - if (context.getIsConsistentWithServer().get()) { - // Skip if a full synchronization is not needed - if (!needAllSync) { - continue; - } - } - - List needSyncContexts = needSyncContextMap.computeIfAbsent( - String.valueOf(context.getTaskId()), k -> new LinkedList<>()); - needSyncContexts.add(context); - } - - // Execute fuzzy listen operation for addition - doExecuteConfigFuzzyListen(needSyncContextMap); - - // Update last all sync time if a full synchronization was performed - if (needAllSync) { - fuzzyListenLastAllSyncTime = now; - } - } - - /** - * Execute fuzzy listen configuration changes for a specific map of contexts. - * - *

This method submits tasks to execute fuzzy listen operations asynchronously for the provided contexts. It - * waits for all tasks to complete and logs any errors that occur. - * - * @param contextMap The map of contexts to execute fuzzy listen operations for. - * @throws NacosException If an error occurs during the execution of fuzzy listen configuration changes. - */ - private void doExecuteConfigFuzzyListen(Map> contextMap) - throws NacosException { - // Return if the context map is null or empty - if (contextMap == null || contextMap.isEmpty()) { - return; - } - - // List to hold futures for asynchronous tasks - List> listenFutures = new ArrayList<>(); - - // Iterate through the context map and submit tasks for execution - for (Map.Entry> entry : contextMap.entrySet()) { - String taskId = entry.getKey(); - List contexts = entry.getValue(); - RpcClient rpcClient = ensureRpcClient(taskId); - ExecutorService executorService = ensureSyncExecutor(fuzzyListenerTaskPrefix, taskId); - // Submit task for execution - Future future = executorService.submit(() -> { - ConfigBatchFuzzyListenRequest configBatchFuzzyListenRequest = buildFuzzyListenConfigRequest( - contexts); - try { - // Execute the fuzzy listen operation - ConfigBatchFuzzyListenResponse listenResponse = (ConfigBatchFuzzyListenResponse) requestProxy( - rpcClient, configBatchFuzzyListenRequest); - if (listenResponse != null && listenResponse.isSuccess()) { - for (FuzzyListenContext context : contexts) { - if (context.isDiscard()) { - ClientWorker.this.removeFuzzyListenContext(context.getDataIdPattern(), - context.getGroup()); - } else { - context.getIsConsistentWithServer().set(true); - } - } - } - } 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 - } - // Retry notification - notifyFuzzyListenConfig(); - } - }); - listenFutures.add(future); - } - - // Wait for all tasks to complete - for (Future future : listenFutures) { - try { - future.get(); - } catch (Throwable throwable) { - // Log async listen error - LOGGER.error("Async fuzzy listen config change error.", throwable); - } - } - } - /** * Checks and handles local configuration for a given CacheData object. This method evaluates the use of * failover files for local configuration storage and updates the CacheData accordingly. @@ -1450,41 +949,16 @@ public void checkLocalConfig(CacheData cacheData) { } } - /** - * Ensure to create a synchronous executor for the given task prefix and task ID. If an executor for the given - * task doesn't exist yet, a new executor will be created. - * - * @param taskPrefix The prefix of the task identifier - * @param taskId The ID of the task - * @return The created or existing executor - */ - private ExecutorService ensureSyncExecutor(String taskPrefix, String taskId) { - // Generate the unique task identifier - String taskIdentifier = generateTaskIdentifier(taskPrefix, taskId); - - // If the task identifier doesn't exist in the existing executors, create a new executor and add it to the multiTaskExecutor map - if (!multiTaskExecutor.containsKey(taskIdentifier)) { - multiTaskExecutor.put(taskIdentifier, + private ExecutorService ensureSyncExecutor(String taskId) { + if (!multiTaskExecutor.containsKey(taskId)) { + multiTaskExecutor.put(taskId, new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(), r -> { - Thread thread = new Thread(r, taskIdentifier); + Thread thread = new Thread(r, "nacos.client.config.listener.task-" + taskId); thread.setDaemon(true); return thread; })); } - - // Return the created or existing executor - return multiTaskExecutor.get(taskIdentifier); - } - - /** - * Generate a task identifier based on the task prefix and task ID. - * - * @param taskPrefix The prefix of the task identifier - * @param taskId The ID of the task - * @return The generated task identifier - */ - private String generateTaskIdentifier(String taskPrefix, String taskId) { - return taskPrefix + "-" + taskId; + return multiTaskExecutor.get(taskId); } private void refreshContentAndCheck(RpcClient rpcClient, String groupKey, boolean notify) { @@ -1524,7 +998,7 @@ private void checkRemoveListenCache(Map> removeListenCac String taskId = entry.getKey(); RpcClient rpcClient = ensureRpcClient(taskId); - ExecutorService executorService = ensureSyncExecutor(configListenerTaskPrefix, taskId); + ExecutorService executorService = ensureSyncExecutor(taskId); Future future = executorService.submit(() -> { List removeListenCaches = entry.getValue(); ConfigBatchListenRequest configChangeListenRequest = buildConfigRequest(removeListenCaches); @@ -1574,7 +1048,7 @@ private boolean checkListenCache(Map> listenCachesMap) t String taskId = entry.getKey(); RpcClient rpcClient = ensureRpcClient(taskId); - ExecutorService executorService = ensureSyncExecutor(configListenerTaskPrefix, taskId); + ExecutorService executorService = ensureSyncExecutor(taskId); Future future = executorService.submit(() -> { List listenCaches = entry.getValue(); //reset notify change flag. @@ -1655,7 +1129,7 @@ private boolean checkListenCache(Map> listenCachesMap) t return hasChangedKeys.get(); } - private RpcClient ensureRpcClient(String taskId) throws NacosException { + RpcClient ensureRpcClient(String taskId) throws NacosException { synchronized (ClientWorker.this) { Map labels = getLabels(); Map newLabels = new HashMap<>(labels); @@ -1669,7 +1143,7 @@ private RpcClient ensureRpcClient(String taskId) throws NacosException { rpcClient.setTenant(getTenant()); rpcClient.start(); } - + return rpcClient; } @@ -1691,33 +1165,12 @@ private ConfigBatchListenRequest buildConfigRequest(List caches) { return configChangeListenRequest; } - /** - * Builds a request for fuzzy listen configuration. - * - * @param contexts The list of fuzzy listen contexts. - * @return A {@code ConfigBatchFuzzyListenRequest} object representing the request. - */ - private ConfigBatchFuzzyListenRequest buildFuzzyListenConfigRequest(List contexts) { - ConfigBatchFuzzyListenRequest request = new ConfigBatchFuzzyListenRequest(); - for (FuzzyListenContext context : contexts) { - request.addContext(getTenant(), context.getGroup(), context.getDataIdPattern(), context.getDataIds(), - !context.isDiscard(), context.isInitializing()); - } - return request; - } - @Override public void removeCache(String dataId, String group) { // Notify to rpc un listen ,and remove cache if success. notifyListenConfig(); } - @Override - public void removeFuzzyListenContext(String dataIdPattern, String group) throws NacosException { - // Notify to rpc un fuzzy listen, and remove cache if success. - notifyFuzzyListenConfig(); - } - /** * send cancel listen config change request . * @@ -1789,7 +1242,7 @@ ConfigResponse queryConfigInner(RpcClient rpcClient, String dataId, String group } } - private Response requestProxy(RpcClient rpcClientInner, Request request) throws NacosException { + Response requestProxy(RpcClient rpcClientInner, Request request) throws NacosException { return requestProxy(rpcClientInner, request, requestTimeout); } @@ -1867,8 +1320,8 @@ public boolean publishConfig(String dataId, String group, String tenant, String this.getName(), dataId, group, tenant, response.getErrorCode(), response.getMessage()); return false; } else { - LOGGER.info("[{}] [publish-single] ok, dataId={}, group={}, tenant={}", getName(), - dataId, group, tenant); + LOGGER.info("[{}] [publish-single] ok, dataId={}, group={}, tenant={}", getName(), dataId, group, + tenant); return true; } } catch (Exception e) { 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 new file mode 100644 index 00000000000..e9a14ee1cf4 --- /dev/null +++ b/client/src/main/java/com/alibaba/nacos/client/config/impl/ConfigFuzzyWatchContext.java @@ -0,0 +1,447 @@ +/* + * 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.api.config.listener.ConfigFuzzyWatchChangeEvent; +import com.alibaba.nacos.api.config.listener.FuzzyWatchEventWatcher; +import com.alibaba.nacos.client.config.common.GroupKey; +import com.alibaba.nacos.client.utils.LogUtils; +import com.alibaba.nacos.common.utils.ConcurrentHashSet; +import com.alibaba.nacos.common.utils.FuzzyGroupKeyPattern; +import com.alibaba.nacos.common.utils.StringUtils; +import org.slf4j.Logger; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +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.AtomicBoolean; + +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; + +/** + * fuzzy wather 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. + *

+ * + * @author stone-98 + * @date 2024/3/4 + */ +public class ConfigFuzzyWatchContext { + + /** + * Logger for FuzzyListenContext. + */ + private static final Logger LOGGER = LogUtils.logger(ConfigFuzzyWatchContext.class); + + /** + * Environment name. + */ + private String envName; + + /** + * Task ID. + */ + private int taskId; + + private String groupKeyPattern; + + /** + * Set of data IDs associated with the context. + */ + private Set receivedGroupKeys = new ConcurrentHashSet<>(); + + /** + * Flag indicating whether the context is consistent with the server. + */ + private final AtomicBoolean isConsistentWithServer = new AtomicBoolean(); + + /** + * Condition object for waiting initialization completion. + */ + final AtomicBoolean initializationCompleted = new AtomicBoolean(false); + + /** + * Flag indicating whether the context is discarded. + */ + private volatile boolean isDiscard = false; + + /** + * Set of listeners associated with the context. + */ + private Set configFuzzyWatcherWrappers = new HashSet<>(); + + /** + * Constructor with environment name, data ID pattern, and group. + * + * @param envName Environment name + * @param groupKeyPattern groupKeyPattern + */ + public ConfigFuzzyWatchContext(String envName, String groupKeyPattern) { + this.envName = envName; + this.groupKeyPattern = groupKeyPattern; + } + + /** + * Calculate the listeners to notify based on the given UUID. + * + * @param uuid UUID to filter listeners + * @return Set of listeners to notify + */ + public Set calculateListenersToNotify(String uuid) { + Set listenersToNotify = new HashSet<>(); + if (StringUtils.isEmpty(uuid)) { + listenersToNotify = configFuzzyWatcherWrappers; + } else { + for (ConfigFuzzyWatcherWrapper listener : configFuzzyWatcherWrappers) { + if (uuid.equals(listener.getUuid())) { + listenersToNotify.add(listener); + } + } + } + return listenersToNotify; + } + + /** + * Notify the listener with the specified data ID, type, and UUID. + * + * @param groupKey groupKey + * @param uuid UUID to filter listeners + */ + public void notifyWatcher(final String groupKey, final String changedType, final String syncType, + final String uuid) { + Set listenersToNotify = calculateListenersToNotify(uuid); + doNotifyWatchers(groupKey, changedType, syncType, listenersToNotify); + } + + /** + * Perform the notification for the specified data ID, type, and listeners. + * + * @param groupKey groupKey + * @param listenersToNotify Set of listeners to notify + */ + private void doNotifyWatchers(final String groupKey, final String changedType, final String syncType, + Set listenersToNotify) { + for (ConfigFuzzyWatcherWrapper watcher : listenersToNotify) { + doNotifyWatcher(groupKey, changedType, syncType, watcher); + } + } + + private void doNotifyWatcher(final String groupKey, final String changedType, final String syncType, + ConfigFuzzyWatcherWrapper configFuzzyWatcher) { + + if (ADD_CONFIG.equals(changedType) && configFuzzyWatcher.getSyncGroupKeys().contains(groupKey)) { + return; + } + + if (DELETE_CONFIG.equals(changedType) && !configFuzzyWatcher.getSyncGroupKeys().contains(groupKey)) { + return; + } + + String[] parseKey = GroupKey.parseKey(groupKey); + String dataId = parseKey[0]; + String group = parseKey[1]; + + String tenant = parseKey[2]; + + final String resetSyncType = initializationCompleted.get() ? syncType : FUZZY_WATCH_INIT_NOTIFY; + AbstractFuzzyNotifyTask job = new AbstractFuzzyNotifyTask() { + @Override + public void run() { + long start = System.currentTimeMillis(); + ConfigFuzzyWatchChangeEvent event = ConfigFuzzyWatchChangeEvent.build(tenant, group, dataId, + changedType, resetSyncType); + if (configFuzzyWatcher != null) { + configFuzzyWatcher.fuzzyWatchEventWatcher.onEvent(event); + } + LOGGER.info( + "[{}] [notify-fuzzy-watcher-ok] dataId={}, group={}, tenant={}, watcher={}, job run cost={} millis.", + envName, dataId, group, tenant, configFuzzyWatcher, (System.currentTimeMillis() - start)); + if (changedType.equals(DELETE_CONFIG)) { + configFuzzyWatcher.getSyncGroupKeys().remove(GroupKey.getKey(dataId, group, tenant)); + } else if (changedType.equals(ADD_CONFIG)) { + configFuzzyWatcher.getSyncGroupKeys().add(GroupKey.getKey(dataId, group, tenant)); + + } + } + }; + + try { + if (null != configFuzzyWatcher.fuzzyWatchEventWatcher.getExecutor()) { + LOGGER.info( + "[{}] [notify-fuzzy-watcher] task submitted to user executor, dataId={}, group={}, tenant={}, listener={}.", + envName, dataId, group, tenant, configFuzzyWatcher); + job.async = true; + configFuzzyWatcher.fuzzyWatchEventWatcher.getExecutor().execute(job); + } else { + LOGGER.info( + "[{}] [notify-fuzzy-watcher] task execute in nacos thread, dataId={}, group={}, tenant={}, listener={}.", + envName, dataId, group, tenant, configFuzzyWatcher); + job.run(); + } + } catch (Throwable t) { + LOGGER.error("[{}] [notify-fuzzy-watcher-error] dataId={}, group={}, tenant={}, listener={}, throwable={}.", + envName, dataId, group, tenant, configFuzzyWatcher, t.getCause()); + } + } + + /** + * Mark initialization as complete and notify waiting threads. + */ + public void markInitializationComplete() { + initializationCompleted.set(true); + synchronized (this) { + this.notifyAll(); + } + } + + /** + * Remove a watcher from the context. + * + * @param watcher watcher to be removed + */ + public void removeWatcher(FuzzyWatchEventWatcher watcher) { + + Iterator iterator = configFuzzyWatcherWrappers.iterator(); + while (iterator.hasNext()) { + ConfigFuzzyWatcherWrapper next = iterator.next(); + if (next.fuzzyWatchEventWatcher.equals(watcher)) { + iterator.remove(); + LOGGER.info("[{}] [remove-fuzzy-watcher-ok] groupKeyPattern={}, watcher={},uuid={} ", getEnvName(), + this.groupKeyPattern, watcher, next.getUuid()); + } + } + + } + + /** + * Add a watcher to the context. + * + * @param configFuzzyWatcherWrapper watcher to be added + */ + public boolean addWatcher(ConfigFuzzyWatcherWrapper configFuzzyWatcherWrapper) { + boolean added = configFuzzyWatcherWrappers.add(configFuzzyWatcherWrapper); + if (added) { + LOGGER.info("[{}] [add-fuzzy-watcher-ok] groupKeyPattern={}, watcher={},uuid={} ", getEnvName(), + this.groupKeyPattern, configFuzzyWatcherWrapper.fuzzyWatchEventWatcher, + configFuzzyWatcherWrapper.getUuid()); + } + return added; + } + + /** + * Get the environment name. + * + * @return Environment name + */ + public String getEnvName() { + return envName; + } + + /** + * Set the environment name. + * + * @param envName Environment name to be set + */ + public void setEnvName(String envName) { + this.envName = envName; + } + + /** + * Get the task ID. + * + * @return Task ID + */ + public int getTaskId() { + return taskId; + } + + /** + * Set the task ID. + * + * @param taskId Task ID to be set + */ + public void setTaskId(int taskId) { + this.taskId = taskId; + } + + public String getGroupKeyPattern() { + return groupKeyPattern; + } + + /** + * Get the flag indicating whether the context is consistent with the server. + * + * @return AtomicBoolean indicating whether the context is consistent with the server + */ + public boolean isConsistentWithServer() { + return isConsistentWithServer.get(); + } + + public void setConsistentWithServer(boolean isConsistentWithServer) { + this.isConsistentWithServer.set(isConsistentWithServer); + } + + /** + * Check if the context is discarded. + * + * @return True if the context is discarded, otherwise false + */ + public boolean isDiscard() { + return isDiscard; + } + + /** + * Set the flag indicating whether the context is discarded. + * + * @param discard True to mark the context as discarded, otherwise false + */ + public void setDiscard(boolean discard) { + isDiscard = discard; + } + + /** + * Check if the context is initializing. + * + * @return True if the context is initializing, otherwise false + */ + public boolean isInitializing() { + return !initializationCompleted.get(); + } + + /** + * Get the set of data IDs associated with the context. zw + * + * @return Set of data IDs + */ + public Set getReceivedGroupKeys() { + return Collections.unmodifiableSet(receivedGroupKeys); + } + + public boolean addReceivedGroupKey(String groupKey) { + return receivedGroupKeys.add(groupKey); + } + + public boolean removeReceivedGroupKey(String groupKey) { + return receivedGroupKeys.remove(groupKey); + } + + /** + * Get the set of listeners associated with the context. + * + * @return Set of listeners + */ + public Set getConfigFuzzyWatcherWrappers() { + return configFuzzyWatcherWrappers; + } + + /** + * Abstract task for fuzzy notification. + */ + abstract static class AbstractFuzzyNotifyTask implements Runnable { + + /** + * Flag indicating whether the task is asynchronous. + */ + boolean async = false; + + /** + * Check if the task is asynchronous. + * + * @return True if the task is asynchronous, otherwise false + */ + public boolean isAsync() { + return async; + } + } + + void syncFuzzyWatchers() { + for (ConfigFuzzyWatcherWrapper configFuzzyWatcher : configFuzzyWatcherWrappers) { + Set receivedGroupKeysContext = receivedGroupKeys; + 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); + } + + } + } + + /** + * creat a new future of this context. + * @return + */ + public Future> createNewFuture() { + Future> completableFuture = new Future>() { + @Override + public boolean cancel(boolean mayInterruptIfRunning) { + throw new UnsupportedOperationException("not support to cancel fuzzy watch"); + } + + @Override + public boolean isCancelled() { + return false; + } + + @Override + public boolean isDone() { + return ConfigFuzzyWatchContext.this.initializationCompleted.get(); + } + + @Override + public Set get() throws InterruptedException, ExecutionException { + + if (!ConfigFuzzyWatchContext.this.initializationCompleted.get()) { + synchronized (ConfigFuzzyWatchContext.this) { + ConfigFuzzyWatchContext.this.wait(); + } + } + return new HashSet<>(ConfigFuzzyWatchContext.this.getReceivedGroupKeys()); + } + + public Set get(long timeout, TimeUnit unit) throws InterruptedException, TimeoutException { + if (!ConfigFuzzyWatchContext.this.initializationCompleted.get()) { + synchronized (ConfigFuzzyWatchContext.this) { + ConfigFuzzyWatchContext.this.wait(unit.toMillis(timeout)); + } + } + + if (!ConfigFuzzyWatchContext.this.initializationCompleted.get()) { + throw new TimeoutException( + "fuzzy watch result future timeout for " + unit.toMillis(timeout) + " millis"); + } + return new HashSet<>(ConfigFuzzyWatchContext.this.getReceivedGroupKeys()); + } + }; + + return completableFuture; + } +} + 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 new file mode 100644 index 00000000000..f64a8ccbff0 --- /dev/null +++ b/client/src/main/java/com/alibaba/nacos/client/config/impl/ConfigFuzzyWatchGroupKeyHolder.java @@ -0,0 +1,485 @@ +/* + * 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.common.Constants; +import com.alibaba.nacos.api.config.listener.FuzzyWatchEventWatcher; +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.ConfigFuzzyWatchChangeNotifyResponse; +import com.alibaba.nacos.api.config.remote.response.ConfigFuzzyWatchResponse; +import com.alibaba.nacos.api.config.remote.response.ConfigFuzzyWatchSyncResponse; +import com.alibaba.nacos.api.exception.NacosException; +import com.alibaba.nacos.client.config.common.GroupKey; +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.remote.client.RpcClient; +import com.alibaba.nacos.common.utils.CollectionUtils; +import com.alibaba.nacos.common.utils.FuzzyGroupKeyPattern; +import org.slf4j.Logger; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +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.ExecutorService; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.AtomicReference; + +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.common.Constants.FUZZY_WATCH_INIT_NOTIFY; +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; + +/** + * config fuzzy watch context holder. + * @author shiyiyue + */ +public class ConfigFuzzyWatchGroupKeyHolder { + + private static final Logger LOGGER = LogUtils.logger(ClientWorker.class); + + private final ClientWorker.ConfigRpcTransportClient agent; + + private final String clientUuid; + + /** + * fuzzyListenExecuteBell. + */ + private final BlockingQueue fuzzyListenExecuteBell = new ArrayBlockingQueue<>(1); + + private final Object bellItem = new Object(); + + private final AtomicLong fuzzyListenLastAllSyncTime = new AtomicLong(System.currentTimeMillis()); + + private static final long FUZZY_LISTEN_ALL_SYNC_INTERNAL = 3 * 60 * 1000; + + private String taskId = "0"; + + /** + * fuzzyListenGroupKey -> fuzzyListenContext. + */ + private final AtomicReference> fuzzyListenContextMap = new AtomicReference<>( + new HashMap<>()); + + public ConfigFuzzyWatchGroupKeyHolder(ClientWorker.ConfigRpcTransportClient agent, String clientUuid) { + this.clientUuid = clientUuid; + this.agent = agent; + NotifyCenter.registerSubscriber(new Subscriber() { + @Override + public void onEvent(Event event) { + ConfigFuzzyWatchNotifyEvent configFuzzyWatchNotifyEvent = (ConfigFuzzyWatchNotifyEvent) event; + + //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; + } + }); + } + + /** + * start. + */ + public void start() { + agent.executor.schedule(() -> { + while (!agent.executor.isShutdown() && !agent.executor.isTerminated()) { + try { + fuzzyListenExecuteBell.poll(5L, TimeUnit.SECONDS); + if (agent.executor.isShutdown() || agent.executor.isTerminated()) { + continue; + } + executeConfigFuzzyListen(); + } catch (Throwable e) { + LOGGER.error("[rpc-fuzzy-listen-execute] rpc fuzzy listen exception", e); + try { + Thread.sleep(50L); + } catch (InterruptedException interruptedException) { + //ignore + } + notifyFuzzyWatchSync(); + } + } + }, 0L, TimeUnit.MILLISECONDS); + } + + /** + * Removes the fuzzy listen context for the specified data ID pattern and group. + * + * @param groupKeyPattern The pattern of the data ID. + */ + public void removeFuzzyListenContext(String groupKeyPattern) { + synchronized (fuzzyListenContextMap) { + Map copy = new HashMap<>(fuzzyListenContextMap.get()); + copy.remove(groupKeyPattern); + fuzzyListenContextMap.set(copy); + } + LOGGER.info("[{}] [fuzzy-watch-unsubscribe] {}", agent.getName(), groupKeyPattern); + } + + /** + * register fuzzy watcher. + * + * @param dataIdPattern dataIdPattern. + * @param groupPattern groupPattern. + * @param fuzzyWatchEventWatcher fuzzyWatchEventWatcher. + * @return + */ + public ConfigFuzzyWatchContext registerFuzzyWatcher(String dataIdPattern, String groupPattern, + FuzzyWatchEventWatcher fuzzyWatchEventWatcher) { + ConfigFuzzyWatchContext configFuzzyWatchContext = initFuzzyWatchContextIfAbsent(dataIdPattern, groupPattern); + ConfigFuzzyWatcherWrapper configFuzzyWatcherWrapper = new ConfigFuzzyWatcherWrapper(fuzzyWatchEventWatcher); + if (configFuzzyWatchContext.addWatcher(configFuzzyWatcherWrapper)) { + if (configFuzzyWatchContext.getReceivedGroupKeys() != null) { + for (String groupKey : configFuzzyWatchContext.getReceivedGroupKeys()) { + ConfigFuzzyWatchNotifyEvent configFuzzyWatchNotifyEvent = ConfigFuzzyWatchNotifyEvent.buildNotifyPatternAllListenersEvent( + groupKey, configFuzzyWatchContext.getGroupKeyPattern(), ADD_CONFIG, FUZZY_WATCH_INIT_NOTIFY, + configFuzzyWatcherWrapper.getUuid()); + NotifyCenter.publishEvent(configFuzzyWatchNotifyEvent); + } + } + } + return configFuzzyWatchContext; + } + + /** + * Retrieves the FuzzyListenContext for the given data ID pattern and group. + * + * @param dataIdPattern The data ID pattern. + * @param groupPattern The group name pattern. + * @return The corresponding FuzzyListenContext, or null if not found. + */ + public ConfigFuzzyWatchContext getFuzzyListenContext(String dataIdPattern, String groupPattern) { + return fuzzyListenContextMap.get() + .get(FuzzyGroupKeyPattern.generatePattern(dataIdPattern, groupPattern, agent.getTenant())); + } + + /** + * Handles a fuzzy listen init notify request. + * + *

This method processes the incoming fuzzy listen init notify request from a client. It updates the fuzzy + * listen context based on the request's information, and publishes events if necessary. + * + * @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) { + String groupKeyPattern = request.getGroupKeyPattern(); + 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()); + context.markInitializationComplete(); + return new ConfigFuzzyWatchSyncResponse(); + } + + LOGGER.info( + "[{}] [fuzzy-watch-diff-sync-push] pattern ->{},syncType={},,syncCount={},totalBatch={},currentBatch={}", + agent.getName(), request.getGroupKeyPattern(), request.getSyncType(), request.getContexts().size(), + request.getTotalBatch(), request.getCurrentBatch()); + + for (ConfigFuzzyWatchSyncRequest.Context requestContext : request.getContexts()) { + switch (requestContext.getChangedType()) { + case ADD_CONFIG: + if (context.addReceivedGroupKey(requestContext.getGroupKey())) { + 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)); + } + break; + case DELETE_CONFIG: + if (context.removeReceivedGroupKey(requestContext.getGroupKey())) { + 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)); + } + break; + default: + LOGGER.warn("Invalid config change type: {}", requestContext.getChangedType()); + break; + } + } + return new ConfigFuzzyWatchSyncResponse(); + } + + /** + * Removes a fuzzy listen listener for the specified data ID pattern, group, and listener. + * + * @param dataIdPattern The pattern of the data ID. + * @param groupPattern The group of the configuration. + * @param watcher The listener to remove. + * @throws NacosException If an error occurs while removing the listener. + */ + public void removeFuzzyWatcher(String dataIdPattern, String groupPattern, FuzzyWatchEventWatcher watcher) { + ConfigFuzzyWatchContext configFuzzyWatchContext = getFuzzyListenContext(dataIdPattern, groupPattern); + if (configFuzzyWatchContext != null) { + synchronized (configFuzzyWatchContext) { + configFuzzyWatchContext.removeWatcher(watcher); + if (configFuzzyWatchContext.getConfigFuzzyWatcherWrappers().isEmpty()) { + configFuzzyWatchContext.setDiscard(true); + configFuzzyWatchContext.setConsistentWithServer(false); + } + } + } + } + + /** + * Handles a fuzzy listen notify change request. + * + *

This method processes the incoming fuzzy listen notify change request from a client. It updates the fuzzy + * listen context based on the request's information, and publishes events if necessary. + * + * @param request The fuzzy listen notify change request to handle. + */ + ConfigFuzzyWatchChangeNotifyResponse handlerFuzzyWatchChangeNotifyRequest( + ConfigFuzzyWatchChangeNotifyRequest request) { + + LOGGER.info("[{}] [fuzzy-watch-change-notify-push] changeType={},groupKey={}", agent.getName(), + request.getChangeType(), request.getGroupKey()); + + Map listenContextMap = fuzzyListenContextMap.get(); + String[] groupItems = GroupKey.parseKey(request.getGroupKey()); + Set matchedPatterns = FuzzyGroupKeyPattern.filterMatchedPatterns(listenContextMap.keySet(), + groupItems[0], groupItems[1], groupItems[2]); + for (String matchedPattern : matchedPatterns) { + ConfigFuzzyWatchContext context = listenContextMap.get(matchedPattern); + if (ADD_CONFIG.equals(request.getChangeType()) || CONFIG_CHANGED.equals(request.getChangeType())) { + if (context.addReceivedGroupKey(request.getGroupKey())) { + LOGGER.info("[{}] [fuzzy-watch-change-notify-push] match group key added ,pattern={},groupKey={}", + agent.getName(), request.getChangeType(), request.getGroupKey()); + + NotifyCenter.publishEvent( + ConfigFuzzyWatchNotifyEvent.buildNotifyPatternAllListenersEvent(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)); + + } + } + return new ConfigFuzzyWatchChangeNotifyResponse(); + } + + void notifyFuzzyWatchSync() { + fuzzyListenExecuteBell.offer(bellItem); + + } + + /** + * Execute fuzzy listen configuration changes. + * + *

This method iterates through all fuzzy listen contexts and determines whether they need to be added or + * removed based on their consistency with the server and discard status. It then calls the appropriate method to + * execute the fuzzy listen operation. + * + * @throws NacosException If an error occurs during the execution of fuzzy listen configuration changes. + */ + public void executeConfigFuzzyListen() throws NacosException { + + // Obtain the current timestamp + long now = System.currentTimeMillis(); + + // Determine whether a full synchronization is needed + boolean needAllSync = now - fuzzyListenLastAllSyncTime.get() >= FUZZY_LISTEN_ALL_SYNC_INTERNAL; + + List needSyncContexts = new ArrayList<>(); + // Iterate through all fuzzy listen contexts + for (ConfigFuzzyWatchContext context : fuzzyListenContextMap.get().values()) { + // Check if the context is consistent with the server + if (context.isConsistentWithServer()) { + // Skip if a full synchronization is not needed + if (!needAllSync) { + continue; + } else { + context.syncFuzzyWatchers(); + } + } + + needSyncContexts.add(context); + } + + // Execute fuzzy listen operation for addition + doExecuteConfigFuzzyListen(needSyncContexts); + + // Update last all sync time if a full synchronization was performed + if (needAllSync) { + fuzzyListenLastAllSyncTime.set(now); + } + } + + void resetConsistenceStatus() { + Collection configFuzzyWatchContexts = fuzzyListenContextMap.get().values(); + + for (ConfigFuzzyWatchContext context : configFuzzyWatchContexts) { + context.setConsistentWithServer(false); + } + } + + /** + * Execute fuzzy listen configuration changes for a specific map of contexts. + * + *

This method submits tasks to execute fuzzy listen operations asynchronously for the provided contexts. It + * waits for all tasks to complete and logs any errors that occur. + * + * @param contextLists The map of contexts to execute fuzzy listen operations for. + * @throws NacosException If an error occurs during the execution of fuzzy listen configuration changes. + */ + private void doExecuteConfigFuzzyListen(List contextLists) throws NacosException { + // Return if the context map is null or empty + if (CollectionUtils.isEmpty(contextLists)) { + return; + } + + // List to hold futures for asynchronous tasks + List> listenFutures = new ArrayList<>(); + + RpcClient rpcClient = agent.ensureRpcClient(taskId); + + // Iterate through the context map and submit tasks for execution + for (ConfigFuzzyWatchContext entry : contextLists) { + ExecutorService executorService = agent.executor; + // 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); + } + + } + } 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 + } + // Retry notification + notifyFuzzyWatchSync(); + } + }); + listenFutures.add(future); + } + + // Wait for all tasks to complete + for (Future future : listenFutures) { + try { + future.get(); + } catch (Throwable throwable) { + // Log async listen error + LOGGER.error("Async fuzzy listen config change error.", throwable); + } + } + } + + /** + * Builds a request for fuzzy listen configuration. + * + * @param context The list of fuzzy listen contexts. + * @return A {@code ConfigBatchFuzzyListenRequest} object representing the request. + */ + private ConfigFuzzyWatchRequest buildFuzzyListenConfigRequest(ConfigFuzzyWatchContext context) { + ConfigFuzzyWatchRequest request = new ConfigFuzzyWatchRequest(); + request.setGroupKeyPattern(context.getGroupKeyPattern()); + request.setInitializing(context.isInitializing()); + request.setWatchType((context.isDiscard() && CollectionUtils.isEmpty(context.getConfigFuzzyWatcherWrappers())) + ? WATCH_TYPE_CANCEL_WATCH : WATCH_TYPE_WATCH); + request.setReceivedGroupKeys(context.getReceivedGroupKeys()); + return request; + } + + /** + * Adds a fuzzy listen context if it doesn't already exist for the specified data ID pattern and group. If the + * context already exists, returns the existing context. + * + * @param dataIdPattern The pattern of the data ID. + * @param groupPattern The group of the configuration. + * @return The fuzzy listen context for the specified data ID pattern and group. + */ + private ConfigFuzzyWatchContext initFuzzyWatchContextIfAbsent(String dataIdPattern, String groupPattern) { + ConfigFuzzyWatchContext context = getFuzzyListenContext(dataIdPattern, groupPattern); + if (context != null) { + return context; + } + synchronized (fuzzyListenContextMap) { + ConfigFuzzyWatchContext contextFromMap = getFuzzyListenContext(dataIdPattern, groupPattern); + if (contextFromMap != null) { + context = contextFromMap; + } else { + String groupKeyPattern = FuzzyGroupKeyPattern.generatePattern(dataIdPattern, groupPattern, + agent.getTenant()); + + context = new ConfigFuzzyWatchContext(agent.getName(), groupKeyPattern); + context.setConsistentWithServer(false); + Map copy = new HashMap<>(fuzzyListenContextMap.get()); + copy.put(groupKeyPattern, context); + LOGGER.info("[{}][fuzzy-watch] init fuzzy watch context , groupKeyPattern={} ,notify fuzzy watch sync ", + agent.getName(), groupKeyPattern); + fuzzyListenContextMap.set(copy); + notifyFuzzyWatchSync(); + } + } + + return context; + } + +} 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 new file mode 100644 index 00000000000..3489e77ed40 --- /dev/null +++ b/client/src/main/java/com/alibaba/nacos/client/config/impl/ConfigFuzzyWatchNotifyEvent.java @@ -0,0 +1,133 @@ +/* + * 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 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 ConfigFuzzyWatchNotifyEvent extends Event { + + private String clientUuid; + + /** + * The uuid of this watcher for which that this notify event . + */ + private String watcherUuid; + + /** + * The groupKeyPattern of configuration. + */ + private String groupKeyPattern; + + private String groupKey; + + /** + * The type of notification (e.g., ADD_CONFIG, DELETE_CONFIG). + */ + private String changedType; + + private String syncType; + + /** + * Constructs a new FuzzyListenNotifyEvent. + */ + public ConfigFuzzyWatchNotifyEvent() { + } + + /** + * Constructs a new FuzzyListenNotifyEvent with the specified group, dataId, and type. + * + * @param groupKey The groupKey of the configuration. + * @param changedType The type of notification. + */ + private ConfigFuzzyWatchNotifyEvent(String groupKey, String changedType, String syncType, String groupKeyPattern, + String clientUuid, String watcherUuid) { + this.groupKey = groupKey; + this.syncType = syncType; + this.changedType = changedType; + this.groupKeyPattern = groupKeyPattern; + this.clientUuid = clientUuid; + this.watcherUuid = watcherUuid; + } + + /** + * Builds a new FuzzyListenNotifyEvent with the specified group, dataId, and type. + * + * @param groupKey The groupKey of the configuration. + * @return A new FuzzyListenNotifyEvent instance. + */ + public static ConfigFuzzyWatchNotifyEvent buildNotifyPatternAllListenersEvent(String groupKey, + String groupKeyPattern, String changedType, String syncType, String clientUuid) { + return buildNotifyPatternAllListenersEvent(groupKey, groupKeyPattern, changedType, syncType, clientUuid, null); + } + + /** + * Builds a new FuzzyListenNotifyEvent with the specified group, dataId, and type. + * + * @param groupKey The groupKey of the configuration. + * @return A new FuzzyListenNotifyEvent instance. + */ + public static ConfigFuzzyWatchNotifyEvent buildNotifyPatternAllListenersEvent(String groupKey, + String groupKeyPattern, String changedType, String syncType, String clientUuid, String watcherUuid) { + ConfigFuzzyWatchNotifyEvent configFuzzyWatchNotifyEvent = new ConfigFuzzyWatchNotifyEvent(groupKey, changedType, + syncType, groupKeyPattern, clientUuid, watcherUuid); + return configFuzzyWatchNotifyEvent; + } + + /** + * Gets the UUID (Unique Identifier) of the listener. + * + * @return The UUID of the listener. + */ + public String getWatcherUuid() { + return watcherUuid; + } + + public String getClientUuid() { + return clientUuid; + } + + public String getGroupKeyPattern() { + return groupKeyPattern; + } + + public String getGroupKey() { + return groupKey; + } + + public String getSyncType() { + return syncType; + } + + /** + * Gets the type of notification. + * + * @return The type of notification. + */ + public String getChangedType() { + return changedType; + } +} 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 new file mode 100644 index 00000000000..2d7ade95b71 --- /dev/null +++ b/client/src/main/java/com/alibaba/nacos/client/config/impl/ConfigFuzzyWatcherWrapper.java @@ -0,0 +1,75 @@ +/* + * 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.FuzzyWatchEventWatcher; + +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; +import java.util.UUID; + +/** + * ConfigFuzzyWatcherWrapper. + * @author shiyiyue + */ +public class ConfigFuzzyWatcherWrapper { + + FuzzyWatchEventWatcher fuzzyWatchEventWatcher; + + public ConfigFuzzyWatcherWrapper(FuzzyWatchEventWatcher fuzzyWatchEventWatcher) { + this.fuzzyWatchEventWatcher = fuzzyWatchEventWatcher; + } + + /** + * Unique identifier for the listener. + */ + String uuid = UUID.randomUUID().toString(); + + private Set syncGroupKeys = new HashSet<>(); + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + ConfigFuzzyWatcherWrapper that = (ConfigFuzzyWatcherWrapper) o; + return Objects.equals(fuzzyWatchEventWatcher, that.fuzzyWatchEventWatcher) && Objects.equals(uuid, that.uuid); + } + + @Override + public int hashCode() { + return Objects.hash(fuzzyWatchEventWatcher, uuid); + } + + Set getSyncGroupKeys() { + return syncGroupKeys; + } + + /** + * Get the UUID (Unique Identifier) of the listener. + * + * @return The UUID of the listener + */ + String getUuid() { + return uuid; + } + +} 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 28d49e59054..1bd5a8e10c5 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.config.filter.impl.ConfigResponse; 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.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.plugin.auth.api.RequestResource; +import com.alibaba.nacos.client.utils.ParamUtil; import java.util.HashMap; import java.util.Map; @@ -177,11 +177,6 @@ public String getTenant() { **/ public abstract void notifyListenConfig(); - /** - * notify fuzzy listen config. - */ - public abstract void notifyFuzzyListenConfig(); - /** * listen change . * @@ -189,13 +184,6 @@ public String getTenant() { */ public abstract void executeConfigListen() throws NacosException; - /** - * Fuzzy listen change. - * - * @throws NacosException nacos exception throws, should retry. - */ - public abstract void executeConfigFuzzyListen() throws NacosException; - /** * remove cache implements. * @@ -204,15 +192,6 @@ public String getTenant() { */ public abstract void removeCache(String dataId, String group); - /** - * Remove fuzzy listen context. - * - * @param dataIdPattern dataIdPattern - * @param group group - * @throws NacosException if an error occurs while removing the fuzzy listen context. - */ - public abstract void removeFuzzyListenContext(String dataIdPattern, String group) throws NacosException; - /** * query config. * diff --git a/client/src/main/java/com/alibaba/nacos/client/config/impl/FuzzyListenContext.java b/client/src/main/java/com/alibaba/nacos/client/config/impl/FuzzyListenContext.java deleted file mode 100644 index 93ddbcfbb7a..00000000000 --- a/client/src/main/java/com/alibaba/nacos/client/config/impl/FuzzyListenContext.java +++ /dev/null @@ -1,447 +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.client.config.impl; - -import com.alibaba.nacos.api.config.listener.AbstractFuzzyListenListener; -import com.alibaba.nacos.api.config.listener.FuzzyListenConfigChangeEvent; -import com.alibaba.nacos.client.utils.LogUtils; -import com.alibaba.nacos.common.utils.ConcurrentHashSet; -import com.alibaba.nacos.common.utils.StringUtils; -import org.slf4j.Logger; - -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.locks.Condition; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantLock; - -/** - * Context for fuzzy listening. - * - *

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. - *

- * - * @author stone-98 - * @date 2024/3/4 - */ -public class FuzzyListenContext { - - /** - * Logger for FuzzyListenContext. - */ - private static final Logger LOGGER = LogUtils.logger(FuzzyListenContext.class); - - /** - * Environment name. - */ - private String envName; - - /** - * Task ID. - */ - private int taskId; - - /** - * Data ID pattern. - */ - private String dataIdPattern; - - /** - * Group name. - */ - private String group; - - /** - * Tenant name. - */ - private String tenant; - - /** - * Flag indicating whether the context is consistent with the server. - */ - private final AtomicBoolean isConsistentWithServer = new AtomicBoolean(); - - /** - * Lock object for synchronization of initialization. - */ - private final Lock initializationLock = new ReentrantLock(); - - /** - * Condition object for waiting initialization completion. - */ - private final Condition initializationCompleted = initializationLock.newCondition(); - - /** - * Flag indicating whether the context is initializing. - */ - private boolean isInitializing = false; - - /** - * Flag indicating whether the context is discarded. - */ - private volatile boolean isDiscard = false; - - /** - * Set of data IDs associated with the context. - */ - private Set dataIds = new ConcurrentHashSet<>(); - - /** - * Set of listeners associated with the context. - */ - private Set listeners = new HashSet<>(); - - /** - * Constructor with environment name, data ID pattern, and group. - * - * @param envName Environment name - * @param dataIdPattern Data ID pattern - * @param group Group name - */ - public FuzzyListenContext(String envName, String dataIdPattern, String group) { - this.envName = envName; - this.dataIdPattern = dataIdPattern; - this.group = group; - } - - /** - * Calculate the listeners to notify based on the given UUID. - * - * @param uuid UUID to filter listeners - * @return Set of listeners to notify - */ - public Set calculateListenersToNotify(String uuid) { - Set listenersToNotify = new HashSet<>(); - if (StringUtils.isEmpty(uuid)) { - listenersToNotify = listeners; - } else { - for (AbstractFuzzyListenListener listener : listeners) { - if (uuid.equals(listener.getUuid())) { - listenersToNotify.add(listener); - } - } - } - return listenersToNotify; - } - - /** - * Notify the listener with the specified data ID, type, and UUID. - * - * @param dataId Data ID - * @param type Type of the event - * @param uuid UUID to filter listeners - */ - public void notifyListener(final String dataId, final String type, final String uuid) { - Set listenersToNotify = calculateListenersToNotify(uuid); - doNotifyListener(dataId, type, listenersToNotify); - } - - /** - * Perform the notification for the specified data ID, type, and listeners. - * - * @param dataId Data ID - * @param type Type of the event - * @param listenersToNotify Set of listeners to notify - */ - private void doNotifyListener(final String dataId, final String type, - Set listenersToNotify) { - for (AbstractFuzzyListenListener listener : listenersToNotify) { - AbstractFuzzyNotifyTask job = new AbstractFuzzyNotifyTask() { - @Override - public void run() { - long start = System.currentTimeMillis(); - FuzzyListenConfigChangeEvent event = FuzzyListenConfigChangeEvent.build(group, dataId, type); - if (listener != null) { - listener.onEvent(event); - } - LOGGER.info("[{}] [notify-ok] dataId={}, group={}, tenant={}, listener={}, job run cost={} millis.", - envName, dataId, group, tenant, listener, (System.currentTimeMillis() - start)); - } - }; - - try { - if (null != listener.getExecutor()) { - LOGGER.info( - "[{}] [notify-listener] task submitted to user executor, dataId={}, group={}, tenant={}, listener={}.", - envName, dataId, group, tenant, listener); - job.async = true; - listener.getExecutor().execute(job); - } else { - LOGGER.info( - "[{}] [notify-listener] task execute in nacos thread, dataId={}, group={}, tenant={}, listener={}.", - envName, dataId, group, tenant, listener); - job.run(); - } - } catch (Throwable t) { - LOGGER.error("[{}] [notify-listener-error] dataId={}, group={}, tenant={}, listener={}, throwable={}.", - envName, dataId, group, tenant, listener, t.getCause()); - } - } - } - - - /** - * Wait for initialization to be complete. - * - * @return CompletableFuture> Completes with the collection of data IDs if initialization is - * @return CompletableFuture> Completes with the collection of data IDs if initialization is - * complete, or completes exceptionally if an error occurs - */ - public CompletableFuture> waitForInitializationComplete( - CompletableFuture> future) { - initializationLock.lock(); - try { - while (isInitializing) { - initializationCompleted.await(); - } - future.complete(Collections.unmodifiableCollection(dataIds)); - } catch (InterruptedException e) { - future.completeExceptionally(e); - } finally { - initializationLock.unlock(); - } - return future; - } - - /** - * Mark initialization as complete and notify waiting threads. - */ - public void markInitializationComplete() { - initializationLock.lock(); - try { - isInitializing = false; - initializationCompleted.signalAll(); - } finally { - initializationLock.unlock(); - } - } - - /** - * Remove a listener from the context. - * - * @param listener Listener to be removed - */ - public void removeListener(AbstractFuzzyListenListener listener) { - listeners.remove(listener); - } - - /** - * Add a listener to the context. - * - * @param listener Listener to be added - */ - public void addListener(AbstractFuzzyListenListener listener) { - listeners.add(listener); - } - - /** - * Get the environment name. - * - * @return Environment name - */ - public String getEnvName() { - return envName; - } - - /** - * Set the environment name. - * - * @param envName Environment name to be set - */ - public void setEnvName(String envName) { - this.envName = envName; - } - - /** - * Get the task ID. - * - * @return Task ID - */ - public int getTaskId() { - return taskId; - } - - /** - * Set the task ID. - * - * @param taskId Task ID to be set - */ - public void setTaskId(int taskId) { - this.taskId = taskId; - } - - /** - * Get the data ID pattern. - * - * @return Data ID pattern - */ - public String getDataIdPattern() { - return dataIdPattern; - } - - /** - * Set the data ID pattern. - * - * @param dataIdPattern Data ID pattern to be set - */ - public void setDataIdPattern(String dataIdPattern) { - this.dataIdPattern = dataIdPattern; - } - - /** - * Get the group name. - * - * @return Group name - */ - public String getGroup() { - return group; - } - - /** - * Set the group name. - * - * @param group Group name to be set - */ - public void setGroup(String group) { - this.group = group; - } - - /** - * Get the tenant name. - * - * @return Tenant name - */ - public String getTenant() { - return tenant; - } - - /** - * Set the tenant name. - * - * @param tenant Tenant name to be set - */ - public void setTenant(String tenant) { - this.tenant = tenant; - } - - /** - * Get the flag indicating whether the context is consistent with the server. - * - * @return AtomicBoolean indicating whether the context is consistent with the server - */ - public AtomicBoolean getIsConsistentWithServer() { - return isConsistentWithServer; - } - - /** - * Check if the context is discarded. - * - * @return True if the context is discarded, otherwise false - */ - public boolean isDiscard() { - return isDiscard; - } - - /** - * Set the flag indicating whether the context is discarded. - * - * @param discard True to mark the context as discarded, otherwise false - */ - public void setDiscard(boolean discard) { - isDiscard = discard; - } - - /** - * Check if the context is initializing. - * - * @return True if the context is initializing, otherwise false - */ - public boolean isInitializing() { - return isInitializing; - } - - /** - * Set the flag indicating whether the context is initializing. - * - * @param initializing True to mark the context as initializing, otherwise false - */ - public void setInitializing(boolean initializing) { - isInitializing = initializing; - } - - /** - * Get the set of data IDs associated with the context. - * - * @return Set of data IDs - */ - public Set getDataIds() { - return Collections.unmodifiableSet(dataIds); - } - - /** - * Set the set of data IDs associated with the context. - * - * @param dataIds Set of data IDs to be set - */ - public void setDataIds(Set dataIds) { - this.dataIds = dataIds; - } - - /** - * Get the set of listeners associated with the context. - * - * @return Set of listeners - */ - public Set getListeners() { - return listeners; - } - - /** - * Set the set of listeners associated with the context. - * - * @param listeners Set of listeners to be set - */ - public void setListeners(Set listeners) { - this.listeners = listeners; - } - - /** - * Abstract task for fuzzy notification. - */ - abstract static class AbstractFuzzyNotifyTask implements Runnable { - - /** - * Flag indicating whether the task is asynchronous. - */ - boolean async = false; - - /** - * Check if the task is asynchronous. - * - * @return True if the task is asynchronous, otherwise false - */ - public boolean isAsync() { - return async; - } - } -} - diff --git a/client/src/main/java/com/alibaba/nacos/client/config/impl/FuzzyListenNotifyEvent.java b/client/src/main/java/com/alibaba/nacos/client/config/impl/FuzzyListenNotifyEvent.java deleted file mode 100644 index a710630c131..00000000000 --- a/client/src/main/java/com/alibaba/nacos/client/config/impl/FuzzyListenNotifyEvent.java +++ /dev/null @@ -1,200 +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.client.config.impl; - -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 FuzzyListenNotifyEvent extends Event { - - /** - * The unique identifier for the listener. - */ - private String uuid; - - /** - * The groupKeyPattern of configuration. - */ - private String groupKeyPattern; - - /** - * The group of the configuration. - */ - private String group; - - /** - * The dataId of the configuration. - */ - private String dataId; - - /** - * The type of notification (e.g., ADD_CONFIG, DELETE_CONFIG). - */ - private String type; - - /** - * Constructs a new FuzzyListenNotifyEvent. - */ - public FuzzyListenNotifyEvent() { - } - - /** - * Constructs a new FuzzyListenNotifyEvent with the specified group, dataId, type, and UUID. - * - * @param group The group of the configuration. - * @param dataId The dataId of the configuration. - * @param type The type of notification. - * @param uuid The UUID (Unique Identifier) of the listener. - */ - public FuzzyListenNotifyEvent(String group, String dataId, String type, String groupKeyPattern, String uuid) { - this.group = group; - this.dataId = dataId; - this.type = type; - this.groupKeyPattern = groupKeyPattern; - this.uuid = uuid; - } - - /** - * Constructs a new FuzzyListenNotifyEvent with the specified group, dataId, and type. - * - * @param group The group of the configuration. - * @param dataId The dataId of the configuration. - * @param type The type of notification. - */ - public FuzzyListenNotifyEvent(String group, String dataId, String type, String groupKeyPattern) { - this.group = group; - this.dataId = dataId; - this.type = type; - this.groupKeyPattern = groupKeyPattern; - } - - /** - * Builds a new FuzzyListenNotifyEvent with the specified group, dataId, type, and UUID. - * - * @param group The group of the configuration. - * @param dataId The dataId of the configuration. - * @param type The type of notification. - * @param uuid The UUID (Unique Identifier) of the listener. - * @return A new FuzzyListenNotifyEvent instance. - */ - public static FuzzyListenNotifyEvent buildNotifyPatternSpecificListenerEvent(String group, String dataId, - String type, String groupKeyPattern, String uuid) { - return new FuzzyListenNotifyEvent(group, dataId, type, groupKeyPattern, uuid); - } - - /** - * Builds a new FuzzyListenNotifyEvent with the specified group, dataId, and type. - * - * @param group The group of the configuration. - * @param dataId The dataId of the configuration. - * @param type The type of notification. - * @return A new FuzzyListenNotifyEvent instance. - */ - public static FuzzyListenNotifyEvent buildNotifyPatternAllListenersEvent(String group, String dataId, - String groupKeyPattern, String type) { - return new FuzzyListenNotifyEvent(group, dataId, type, groupKeyPattern); - } - - /** - * Gets the UUID (Unique Identifier) of the listener. - * - * @return The UUID of the listener. - */ - public String getUuid() { - return uuid; - } - - /** - * Sets the UUID (Unique Identifier) of the listener. - * - * @param uuid The UUID to set. - */ - public void setUuid(String uuid) { - this.uuid = uuid; - } - - public String getGroupKeyPattern() { - return groupKeyPattern; - } - - public void setGroupKeyPattern(String groupKeyPattern) { - this.groupKeyPattern = groupKeyPattern; - } - - /** - * Gets the group of the configuration. - * - * @return The group of the configuration. - */ - public String getGroup() { - return group; - } - - /** - * Sets the group of the configuration. - * - * @param group The group to set. - */ - public void setGroup(String group) { - this.group = group; - } - - /** - * Gets the dataId of the configuration. - * - * @return The dataId of the configuration. - */ - public String getDataId() { - return dataId; - } - - /** - * Sets the dataId of the configuration. - * - * @param dataId The dataId to set. - */ - public void setDataId(String dataId) { - this.dataId = dataId; - } - - /** - * Gets the type of notification. - * - * @return The type of notification. - */ - public String getType() { - return type; - } - - /** - * Sets the type of notification. - * - * @param type The type to set. - */ - public void setType(String type) { - this.type = type; - } -} diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/NacosNamingService.java b/client/src/main/java/com/alibaba/nacos/client/naming/NacosNamingService.java index bf382f02e27..1b0b24d9884 100644 --- a/client/src/main/java/com/alibaba/nacos/client/naming/NacosNamingService.java +++ b/client/src/main/java/com/alibaba/nacos/client/naming/NacosNamingService.java @@ -21,6 +21,7 @@ import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.naming.NamingService; import com.alibaba.nacos.api.naming.listener.EventListener; +import com.alibaba.nacos.api.naming.listener.FuzzyWatchEventWatcher; import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.api.naming.pojo.ListView; import com.alibaba.nacos.api.naming.pojo.ServiceInfo; @@ -30,11 +31,14 @@ import com.alibaba.nacos.api.naming.utils.NamingUtils; import com.alibaba.nacos.api.selector.AbstractSelector; import com.alibaba.nacos.client.env.NacosClientProperties; +import com.alibaba.nacos.client.naming.cache.NamingFuzzyWatchContext; +import com.alibaba.nacos.client.naming.cache.NamingFuzzyWatchServiceListHolder; import com.alibaba.nacos.client.naming.cache.ServiceInfoHolder; import com.alibaba.nacos.client.naming.core.Balancer; import com.alibaba.nacos.client.naming.event.InstancesChangeEvent; import com.alibaba.nacos.client.naming.event.InstancesChangeNotifier; import com.alibaba.nacos.client.naming.event.InstancesDiff; +import com.alibaba.nacos.client.naming.event.NamingFuzzyWatchNotifyEvent; import com.alibaba.nacos.client.naming.remote.NamingClientProxy; import com.alibaba.nacos.client.naming.remote.NamingClientProxyDelegate; import com.alibaba.nacos.client.naming.selector.NamingSelectorFactory; @@ -47,6 +51,7 @@ import com.alibaba.nacos.client.utils.PreInitUtils; import com.alibaba.nacos.client.utils.ValidatorUtils; import com.alibaba.nacos.common.notify.NotifyCenter; +import com.alibaba.nacos.common.utils.FuzzyGroupKeyPattern; import com.alibaba.nacos.common.utils.JacksonUtils; import com.alibaba.nacos.common.utils.StringUtils; @@ -55,7 +60,9 @@ import java.util.List; import java.util.Properties; import java.util.UUID; +import java.util.concurrent.Future; +import static com.alibaba.nacos.api.common.Constants.ANY_PATTERN; import static com.alibaba.nacos.client.naming.selector.NamingSelectorFactory.getUniqueClusterString; import static com.alibaba.nacos.client.utils.LogUtils.NAMING_LOGGER; @@ -83,6 +90,8 @@ public class NacosNamingService implements NamingService { private ServiceInfoHolder serviceInfoHolder; + private NamingFuzzyWatchServiceListHolder namingFuzzyWatchServiceListHolder; + private InstancesChangeNotifier changeNotifier; private NamingClientProxy clientProxy; @@ -114,8 +123,12 @@ private void init(Properties properties) throws NacosException { NotifyCenter.registerToPublisher(InstancesChangeEvent.class, 16384); NotifyCenter.registerSubscriber(changeNotifier); this.serviceInfoHolder = new ServiceInfoHolder(namespace, this.notifierEventScope, nacosClientProperties); + + NotifyCenter.registerToPublisher(NamingFuzzyWatchNotifyEvent.class, 16384); + this.namingFuzzyWatchServiceListHolder = new NamingFuzzyWatchServiceListHolder(this.notifierEventScope); + this.clientProxy = new NamingClientProxyDelegate(this.namespace, serviceInfoHolder, nacosClientProperties, - changeNotifier); + changeNotifier, namingFuzzyWatchServiceListHolder); } @Deprecated @@ -362,7 +375,8 @@ private ServiceInfo getServiceInfoBySubscribe(String serviceName, String groupNa return serviceInfo; } - private ServiceInfo tryToSubscribe(String serviceName, String groupName, ServiceInfo cachedServiceInfo) throws NacosException { + private ServiceInfo tryToSubscribe(String serviceName, String groupName, ServiceInfo cachedServiceInfo) + throws NacosException { // not found in cache, service never subscribed. if (null == cachedServiceInfo) { return clientProxy.subscribe(serviceName, groupName, StringUtils.EMPTY); @@ -525,6 +539,66 @@ private void doUnsubscribe(String serviceName, String groupName, NamingSelector } } + @Override + public void fuzzyWatch(String fixedGroupName, FuzzyWatchEventWatcher listener) throws NacosException { + doFuzzyWatch(ANY_PATTERN, fixedGroupName, listener); + } + + @Override + public void fuzzyWatch(String serviceNamePattern, String groupNamePattern, FuzzyWatchEventWatcher listener) + throws NacosException { + doFuzzyWatch(serviceNamePattern, groupNamePattern, listener); + } + + @Override + public Future> fuzzyWatchWithServiceKeys(String fixedGroupName, FuzzyWatchEventWatcher listener) + throws NacosException { + return doFuzzyWatch(ANY_PATTERN, fixedGroupName, listener); + } + + @Override + public Future> fuzzyWatchWithServiceKeys(String serviceNamePattern, String groupNamePattern, + FuzzyWatchEventWatcher listener) throws NacosException { + return doFuzzyWatch(serviceNamePattern, groupNamePattern, listener); + } + + private Future> doFuzzyWatch(String serviceNamePattern, String groupNamePattern, + FuzzyWatchEventWatcher watcher) { + if (null == watcher) { + return null; + } + + String groupKeyPattern = FuzzyGroupKeyPattern.generatePattern(serviceNamePattern, groupNamePattern, namespace); + NamingFuzzyWatchContext namingFuzzyWatchContext = namingFuzzyWatchServiceListHolder.registerFuzzyWatcher( + groupKeyPattern, watcher); + return namingFuzzyWatchContext.createNewFuture(); + } + + @Override + public void cancelFuzzyWatch(String fixedGroupName, FuzzyWatchEventWatcher listener) throws NacosException { + doCancelFuzzyWatch(ANY_PATTERN, fixedGroupName, listener); + } + + @Override + public void cancelFuzzyWatch(String serviceNamePattern, String fixedGroupName, FuzzyWatchEventWatcher listener) + throws NacosException { + doCancelFuzzyWatch(serviceNamePattern, fixedGroupName, listener); + } + + private void doCancelFuzzyWatch(String serviceNamePattern, String groupNamePattern, FuzzyWatchEventWatcher watcher) + throws NacosException { + if (null == watcher) { + return; + } + String groupKeyPattern = FuzzyGroupKeyPattern.generatePattern(serviceNamePattern, groupNamePattern, namespace); + + NamingFuzzyWatchContext namingFuzzyWatchContext = namingFuzzyWatchServiceListHolder.getFuzzyWatchContext( + groupKeyPattern); + if (namingFuzzyWatchContext != null) { + namingFuzzyWatchContext.removeWatcher(watcher); + } + } + @Override public ListView getServicesOfServer(int pageNo, int pageSize) throws NacosException { return getServicesOfServer(pageNo, pageSize, Constants.DEFAULT_GROUP); @@ -561,6 +635,7 @@ public String getServerStatus() { public void shutDown() throws NacosException { serviceInfoHolder.shutdown(); clientProxy.shutdown(); + namingFuzzyWatchServiceListHolder.shutdown(); NotifyCenter.deregisterSubscriber(changeNotifier); } 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 new file mode 100644 index 00000000000..c607c8e9a44 --- /dev/null +++ b/client/src/main/java/com/alibaba/nacos/client/naming/cache/FuzzyWatchEventWatcherWrapper.java @@ -0,0 +1,66 @@ +/* + * 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.cache; + +import com.alibaba.nacos.api.naming.listener.FuzzyWatchEventWatcher; + +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; +import java.util.UUID; + +/** + * fuzzy watcher wrapper. + * @author shiyiyue + */ +public class FuzzyWatchEventWatcherWrapper { + + FuzzyWatchEventWatcher fuzzyWatchEventWatcher; + + public FuzzyWatchEventWatcherWrapper(FuzzyWatchEventWatcher fuzzyWatchEventWatcher) { + this.fuzzyWatchEventWatcher = fuzzyWatchEventWatcher; + } + + String uuid = UUID.randomUUID().toString(); + + private Set syncServiceKeys = new HashSet<>(); + + final String getUuid() { + return uuid; + } + + Set getSyncServiceKeys() { + return syncServiceKeys; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + FuzzyWatchEventWatcherWrapper that = (FuzzyWatchEventWatcherWrapper) o; + return Objects.equals(fuzzyWatchEventWatcher, that.fuzzyWatchEventWatcher); + } + + @Override + public int hashCode() { + return Objects.hash(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 new file mode 100644 index 00000000000..e5173614bda --- /dev/null +++ b/client/src/main/java/com/alibaba/nacos/client/naming/cache/NamingFuzzyWatchContext.java @@ -0,0 +1,369 @@ +/* + * 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.cache; + +import com.alibaba.nacos.api.naming.listener.FuzzyWatchChangeEvent; +import com.alibaba.nacos.api.naming.listener.FuzzyWatchEventWatcher; +import com.alibaba.nacos.api.naming.pojo.ListView; +import com.alibaba.nacos.api.naming.utils.NamingUtils; +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; +import org.slf4j.Logger; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +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; + +/** + * fuzzy wather 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. + *

+ * + * @author stone-98 + * @date 2024/3/4 + */ +public class NamingFuzzyWatchContext { + + /** + * Logger for FuzzyListenContext. + */ + private static final Logger LOGGER = LogUtils.logger(NamingFuzzyWatchContext.class); + + /** + * Environment name. + */ + private String envName; + + private String groupKeyPattern; + + /** + * Set of service keys associated with the context. + */ + private Set receivedServiceKeys = new ConcurrentHashSet<>(); + + /** + * Flag indicating whether the context is consistent with the server. + */ + private final AtomicBoolean isConsistentWithServer = new AtomicBoolean(); + + /** + * Condition object for waiting initialization completion. + */ + final AtomicBoolean initializationCompleted = new AtomicBoolean(false); + + /** + * Flag indicating whether the context is discarded. + */ + private volatile boolean isDiscard = false; + + /** + * Set of listeners associated with the context. + */ + private final Set fuzzyWatchEventWatcherWrappers = new HashSet<>(); + + /** + * Constructor with environment name, data ID pattern, and group. + * + * @param envName Environment name + * @param groupKeyPattern groupKeyPattern + */ + public NamingFuzzyWatchContext(String envName, String groupKeyPattern) { + this.envName = envName; + this.groupKeyPattern = groupKeyPattern; + } + + private void doNotifyWatcher(final String serviceKey, final String changedType, final String syncType, + FuzzyWatchEventWatcherWrapper fuzzyWatchEventWatcherWrapper) { + + if (ADD_SERVICE.equals(changedType) && fuzzyWatchEventWatcherWrapper.getSyncServiceKeys() + .contains(serviceKey)) { + return; + } + + if (DELETE_SERVICE.equals(changedType) && !fuzzyWatchEventWatcherWrapper.getSyncServiceKeys() + .contains(serviceKey)) { + return; + } + + String[] serviceKeyItems = NamingUtils.parseServiceKey(serviceKey); + String namespace = serviceKeyItems[0]; + String groupName = serviceKeyItems[1]; + String serviceName = serviceKeyItems[2]; + + final String resetSyncType = !initializationCompleted.get() ? FUZZY_WATCH_INIT_NOTIFY : syncType; + + Runnable job = () -> { + long start = System.currentTimeMillis(); + FuzzyWatchChangeEvent event = new FuzzyWatchChangeEvent(serviceName, groupName, namespace, changedType, + resetSyncType); + if (fuzzyWatchEventWatcherWrapper != null) { + fuzzyWatchEventWatcherWrapper.fuzzyWatchEventWatcher.onEvent(event); + } + LOGGER.info( + "[{}] [notify-watcher-ok] serviceName={}, groupName={}, namespace={}, watcher={},changedType={}, job run cost={} millis.", + envName, serviceName, groupName, namespace, fuzzyWatchEventWatcherWrapper.fuzzyWatchEventWatcher, + changedType, (System.currentTimeMillis() - start)); + if (changedType.equals(DELETE_SERVICE)) { + fuzzyWatchEventWatcherWrapper.getSyncServiceKeys() + .remove(NamingUtils.getServiceKey(namespace, groupName, serviceName)); + } else if (changedType.equals(ADD_SERVICE)) { + fuzzyWatchEventWatcherWrapper.getSyncServiceKeys() + .add(NamingUtils.getServiceKey(namespace, groupName, serviceName)); + } + }; + + try { + if (null != fuzzyWatchEventWatcherWrapper.fuzzyWatchEventWatcher.getExecutor()) { + LOGGER.info( + "[{}] [notify-watcher] task submitted to user executor, serviceName={}, groupName={}, namespace={}, listener={}.", + envName, serviceName, groupName, namespace, fuzzyWatchEventWatcherWrapper); + fuzzyWatchEventWatcherWrapper.fuzzyWatchEventWatcher.getExecutor().execute(job); + } else { + LOGGER.info( + "[{}] [notify-watcher] task execute in nacos thread, serviceName={}, groupName={}, namespace={}, listener={}.", + envName, serviceName, groupName, namespace, fuzzyWatchEventWatcherWrapper); + job.run(); + } + } catch (Throwable t) { + LOGGER.error( + "[{}] [notify-watcher-error] serviceName={}, groupName={}, namespace={}, listener={}, throwable={}.", + envName, serviceName, groupName, namespace, fuzzyWatchEventWatcherWrapper, t.getCause()); + } + } + + /** + * Mark initialization as complete and notify waiting threads. + */ + public void markInitializationComplete() { + LOGGER.info("[{}] [fuzzy-watch] pattern init notify finish pattern={},match service count {}", envName, + groupKeyPattern, receivedServiceKeys.size()); + initializationCompleted.set(true); + synchronized (this) { + notifyAll(); + } + } + + /** + * Remove a watcher from the context. + * + * @param watcher watcher to be removed + */ + public void removeWatcher(FuzzyWatchEventWatcher watcher) { + Iterator iterator = fuzzyWatchEventWatcherWrappers.iterator(); + while (iterator.hasNext()) { + FuzzyWatchEventWatcherWrapper next = iterator.next(); + if (next.fuzzyWatchEventWatcher.equals(watcher)) { + iterator.remove(); + LOGGER.info("[{}] [remove-watcher-ok] groupKeyPattern={}, watcher={},uuid={} ", getEnvName(), + this.groupKeyPattern, watcher, next.getUuid()); + } + } + + } + + /** + * Get the environment name. + * + * @return Environment name + */ + public String getEnvName() { + return envName; + } + + /** + * Set the environment name. + * + * @param envName Environment name to be set + */ + public void setEnvName(String envName) { + this.envName = envName; + } + + public String getGroupKeyPattern() { + return groupKeyPattern; + } + + /** + * Get the flag indicating whether the context is consistent with the server. + * + * @return AtomicBoolean indicating whether the context is consistent with the server + */ + public boolean isConsistentWithServer() { + return isConsistentWithServer.get(); + } + + public void setConsistentWithServer(boolean isConsistentWithServer) { + this.isConsistentWithServer.set(isConsistentWithServer); + } + + /** + * Check if the context is discarded. + * + * @return True if the context is discarded, otherwise false + */ + public boolean isDiscard() { + return isDiscard; + } + + /** + * Set the flag indicating whether the context is discarded. + * + * @param discard True to mark the context as discarded, otherwise false + */ + public void setDiscard(boolean discard) { + isDiscard = discard; + } + + /** + * Check if the context is initializing. + * + * @return True if the context is initializing, otherwise false + */ + public boolean isInitializing() { + return !initializationCompleted.get(); + } + + /** + * Get the set of data IDs associated with the context. + * + * @return Set of data IDs + */ + public Set getReceivedServiceKeys() { + return Collections.unmodifiableSet(receivedServiceKeys); + } + + public boolean addReceivedServiceKey(String serviceKey) { + return receivedServiceKeys.add(serviceKey); + } + + public boolean removeReceivedServiceKey(String serviceKey) { + return receivedServiceKeys.remove(serviceKey); + } + + /** + * Get the set of listeners associated with the context. + * + * @return Set of listeners + */ + public Set getFuzzyWatchEventWatcherWrappers() { + return fuzzyWatchEventWatcherWrappers; + } + + void syncFuzzyWatchers() { + for (FuzzyWatchEventWatcherWrapper namingFuzzyWatcher : fuzzyWatchEventWatcherWrappers) { + Set receivedServiceKeysContext = 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); + } + } + } + + void notifyFuzzyWatchers(String serviceKey, String changedType, String syncType, String watcherUuid) { + for (FuzzyWatchEventWatcherWrapper namingFuzzyWatcher : filterWatchers(watcherUuid)) { + doNotifyWatcher(serviceKey, changedType, syncType, namingFuzzyWatcher); + } + } + + private Set filterWatchers(String uuid) { + if (StringUtils.isBlank(uuid) || CollectionUtils.isEmpty(getFuzzyWatchEventWatcherWrappers())) { + return getFuzzyWatchEventWatcherWrappers(); + } else { + return getFuzzyWatchEventWatcherWrappers().stream().filter(a -> a.getUuid().equals(uuid)) + .collect(Collectors.toSet()); + } + } + + /** + * create a new future of this context. + * @return + */ + public Future> createNewFuture() { + Future> completableFuture = new Future>() { + @Override + public boolean cancel(boolean mayInterruptIfRunning) { + throw new UnsupportedOperationException("not support to cancel fuzzy watch"); + } + + @Override + public boolean isCancelled() { + return false; + } + + @Override + public boolean isDone() { + return NamingFuzzyWatchContext.this.initializationCompleted.get(); + } + + @Override + public ListView get() throws InterruptedException { + synchronized (NamingFuzzyWatchContext.this) { + while (!NamingFuzzyWatchContext.this.initializationCompleted.get()) { + NamingFuzzyWatchContext.this.wait(); + } + } + + ListView result = new ListView<>(); + result.setData(Arrays.asList(NamingFuzzyWatchContext.this.receivedServiceKeys.toArray(new String[0]))); + result.setCount(result.getData().size()); + return result; + } + + @Override + public ListView get(long timeout, TimeUnit unit) throws InterruptedException, TimeoutException { + + if (!NamingFuzzyWatchContext.this.initializationCompleted.get()) { + synchronized (NamingFuzzyWatchContext.this) { + NamingFuzzyWatchContext.this.wait(unit.toMillis(timeout)); + } + } + + if (!NamingFuzzyWatchContext.this.initializationCompleted.get()) { + throw new TimeoutException( + "fuzzy watch result future timeout for " + unit.toMillis(timeout) + " millis"); + } + + ListView result = new ListView<>(); + result.setData(Arrays.asList(NamingFuzzyWatchContext.this.receivedServiceKeys.toArray(new String[0]))); + result.setCount(result.getData().size()); + return result; + } + }; + return completableFuture; + } +} 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 new file mode 100644 index 00000000000..64fcc984eec --- /dev/null +++ b/client/src/main/java/com/alibaba/nacos/client/naming/cache/NamingFuzzyWatchServiceListHolder.java @@ -0,0 +1,340 @@ +/* + * 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.cache; + +import com.alibaba.nacos.api.exception.NacosException; +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.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.utils.CollectionUtils; +import org.slf4j.Logger; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; + +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.WATCH_TYPE_CANCEL_WATCH; +import static com.alibaba.nacos.api.common.Constants.WATCH_TYPE_WATCH; + +/** + * Naming client fuzzy watch service list holder. + * + * @author tanyongquan + */ +public class NamingFuzzyWatchServiceListHolder extends Subscriber { + + private static final Logger LOGGER = LogUtils.logger(NamingFuzzyWatchServiceListHolder.class); + + private String notifierEventScope; + + private NamingGrpcClientProxy namingGrpcClientProxy; + + /** + * fuzzyListenExecuteBell. + */ + private final BlockingQueue fuzzyWatchExecuteBell = new ArrayBlockingQueue<>(1); + + private final Object bellItem = new Object(); + + private final AtomicLong fuzzyWatchLastAllSyncTime = new AtomicLong(System.currentTimeMillis()); + + private static final long FUZZY_LISTEN_ALL_SYNC_INTERNAL = 3 * 60 * 1000; + + ScheduledExecutorService executorService; + + /** + * The contents of {@code patternMatchMap} are Map{pattern -> Set[matched services]}. + */ + private Map fuzzyMatchContextMap = new ConcurrentHashMap<>(); + + public NamingFuzzyWatchServiceListHolder(String notifierEventScope) { + this.notifierEventScope = notifierEventScope; + NotifyCenter.registerSubscriber(this); + } + + /** + * shut down. + */ + public void shutdown() { + if (executorService != null && !executorService.isShutdown()) { + executorService.shutdown(); + } + } + + /** + * start. + */ + @SuppressWarnings("PMD.ThreadPoolCreationRule") + public void start() { + + executorService = Executors.newSingleThreadScheduledExecutor( + new NameThreadFactory("com.alibaba.nacos.client.naming.fuzzy.watch.Worker")); + executorService.submit(() -> { + while (!executorService.isShutdown() && !executorService.isTerminated()) { + try { + fuzzyWatchExecuteBell.poll(5L, TimeUnit.SECONDS); + if (executorService.isShutdown() || executorService.isTerminated()) { + continue; + } + executeNamingFuzzyWatch(); + } catch (Throwable e) { + LOGGER.error("[rpc-fuzzy-watch-execute] rpc fuzzy watch exception", e); + try { + Thread.sleep(50L); + } catch (InterruptedException interruptedException) { + //ignore + } + notifyFuzzyWatchSync(); + } + } + }); + } + + public void registerNamingGrpcClientProxy(NamingGrpcClientProxy namingGrpcClientProxy) { + this.namingGrpcClientProxy = namingGrpcClientProxy; + } + + public NamingFuzzyWatchContext getFuzzyWatchContext(String groupKeyPattern) { + return fuzzyMatchContextMap.get(groupKeyPattern); + } + + /** + * Add a watcher to the context. + * + * @param watcher watcher to be added + */ + 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); + } + } + } + return namingFuzzyWatchContext; + } + + /** + * init fuzzy watch context. + * @param groupKeyPattern groupKeyPattern. + * @return fuzzy context. + */ + public NamingFuzzyWatchContext initFuzzyWatchContextIfNeed(String groupKeyPattern) { + if (!fuzzyMatchContextMap.containsKey(groupKeyPattern)) { + synchronized (fuzzyMatchContextMap) { + if (fuzzyMatchContextMap.containsKey(groupKeyPattern)) { + return fuzzyMatchContextMap.get(groupKeyPattern); + } + LOGGER.info("[fuzzy-watch] init fuzzy watch context for pattern {}", groupKeyPattern); + fuzzyMatchContextMap.putIfAbsent(groupKeyPattern, + new NamingFuzzyWatchContext(notifierEventScope, groupKeyPattern)); + notifyFuzzyWatchSync(); + } + } + return fuzzyMatchContextMap.get(groupKeyPattern); + } + + /** + * remove fuzzy watch context for pattern. + * @param groupKeyPattern group key pattern. + */ + public synchronized void removePatternMatchCache(String groupKeyPattern) { + NamingFuzzyWatchContext namingFuzzyWatchContext = fuzzyMatchContextMap.get(groupKeyPattern); + if (namingFuzzyWatchContext == null) { + return; + } + if (namingFuzzyWatchContext.isDiscard() && namingFuzzyWatchContext.getFuzzyWatchEventWatcherWrappers() + .isEmpty()) { + LOGGER.info("[fuzzy-watch] remove fuzzy watch context for pattern {}", groupKeyPattern); + fuzzyMatchContextMap.remove(groupKeyPattern); + } + } + + /** + * notify sync fuzzy watch with server. + */ + void notifyFuzzyWatchSync() { + fuzzyWatchExecuteBell.offer(bellItem); + } + + /** + * Execute fuzzy listen configuration changes. + * + *

This method iterates through all fuzzy listen contexts and determines whether they need to be added or + * removed based on their consistency with the server and discard status. It then calls the appropriate method to + * execute the fuzzy listen operation. + * + * @throws NacosException If an error occurs during the execution of fuzzy listen configuration changes. + */ + public void executeNamingFuzzyWatch() throws NacosException { + + // Obtain the current timestamp + long now = System.currentTimeMillis(); + + // Determine whether a full synchronization is needed + boolean needAllSync = now - fuzzyWatchLastAllSyncTime.get() >= FUZZY_LISTEN_ALL_SYNC_INTERNAL; + + List needSyncContexts = new ArrayList<>(); + // Iterate through all fuzzy listen contexts + for (NamingFuzzyWatchContext context : fuzzyMatchContextMap.values()) { + // Check if the context is consistent with the server + if (context.isConsistentWithServer()) { + // Skip if a full synchronization is not needed + if (!needAllSync) { + continue; + } else { + context.syncFuzzyWatchers(); + } + } + + needSyncContexts.add(context); + } + + // Execute fuzzy listen operation for addition + doExecuteNamingFuzzyWatch(needSyncContexts); + + // Update last all sync time if a full synchronization was performed + if (needAllSync) { + fuzzyWatchLastAllSyncTime.set(now); + } + } + + public void resetConsistenceStatus() { + fuzzyMatchContextMap.values() + .forEach(fuzzyWatcherContext -> fuzzyWatcherContext.setConsistentWithServer(false)); + } + + /** + * Execute fuzzy listen configuration changes for a specific map of contexts. + * + *

This method submits tasks to execute fuzzy listen operations asynchronously for the provided contexts. It + * waits for all tasks to complete and logs any errors that occur. + * + * @param contextLists The map of contexts to execute fuzzy listen operations for. + * @throws NacosException If an error occurs during the execution of fuzzy listen configuration changes. + */ + private void doExecuteNamingFuzzyWatch(List contextLists) throws NacosException { + // Return if the context map is null or empty + if (CollectionUtils.isEmpty(contextLists)) { + return; + } + + // Iterate through the context map and submit tasks for execution + for (NamingFuzzyWatchContext entry : contextLists) { + // Submit task for execution + NamingFuzzyWatchRequest configFuzzyWatchRequest = buildFuzzyWatchNamingRequest(entry); + try { + + // Execute the fuzzy listen operation + NamingFuzzyWatchResponse listenResponse = namingGrpcClientProxy.fuzzyWatchRequest( + configFuzzyWatchRequest); + if (listenResponse != null && listenResponse.isSuccess()) { + + if (configFuzzyWatchRequest.getWatchType().equals(WATCH_TYPE_CANCEL_WATCH)) { + removePatternMatchCache(entry.getGroupKeyPattern()); + } 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 + } + // Retry notification + notifyFuzzyWatchSync(); + } + } + + } + + private NamingFuzzyWatchRequest buildFuzzyWatchNamingRequest(NamingFuzzyWatchContext namingFuzzyWatchContext) { + NamingFuzzyWatchRequest namingFuzzyWatchRequest = new NamingFuzzyWatchRequest(); + namingFuzzyWatchRequest.setInitializing(namingFuzzyWatchContext.isInitializing()); + namingFuzzyWatchRequest.setNamespace(namingGrpcClientProxy.getNamespaceId()); + namingFuzzyWatchRequest.setReceivedGroupKeys(namingFuzzyWatchContext.getReceivedServiceKeys()); + namingFuzzyWatchRequest.setGroupKeyPattern(namingFuzzyWatchContext.getGroupKeyPattern()); + if (namingFuzzyWatchContext.isDiscard() && namingFuzzyWatchContext.getFuzzyWatchEventWatcherWrappers() + .isEmpty()) { + namingFuzzyWatchRequest.setWatchType(WATCH_TYPE_CANCEL_WATCH); + } else { + namingFuzzyWatchRequest.setWatchType(WATCH_TYPE_WATCH); + } + return namingFuzzyWatchRequest; + } + + public Map getFuzzyMatchContextMap() { + return fuzzyMatchContextMap; + } + + @Override + public void onEvent(NamingFuzzyWatchNotifyEvent event) { + if (!event.scope().equals(notifierEventScope)) { + return; + } + + 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; + } + namingFuzzyWatchContext.notifyFuzzyWatchers(serviceKey, changedType, syncType, watchUuid); + + } + + @Override + public Class subscribeType() { + return NamingFuzzyWatchNotifyEvent.class; + } + + public String getNotifierEventScope() { + return notifierEventScope; + } +} diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/event/NamingFuzzyWatchNotifyEvent.java b/client/src/main/java/com/alibaba/nacos/client/naming/event/NamingFuzzyWatchNotifyEvent.java new file mode 100644 index 00000000000..072215f5bce --- /dev/null +++ b/client/src/main/java/com/alibaba/nacos/client/naming/event/NamingFuzzyWatchNotifyEvent.java @@ -0,0 +1,88 @@ +/* + * 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; + +/** + * Watch notify event, including service change/watch initial. + * + * @author tanyongquan + */ +public class NamingFuzzyWatchNotifyEvent extends Event { + + private final String scope; + + private String watcherUuid; + + private String serviceKey; + + private String pattern; + + private final String changedType; + + private final String syncType; + + private NamingFuzzyWatchNotifyEvent(String scope, String pattern, String serviceKey, String changedType, + String syncType, String watcherUuid) { + this.scope = scope; + this.pattern = pattern; + this.serviceKey = serviceKey; + this.changedType = changedType; + this.syncType = syncType; + this.watcherUuid = watcherUuid; + } + + public static NamingFuzzyWatchNotifyEvent build(String eventScope, String pattern, String serviceKey, + String changedType, String syncType) { + return new NamingFuzzyWatchNotifyEvent(eventScope, pattern, serviceKey, changedType, syncType, null); + } + + public static NamingFuzzyWatchNotifyEvent build(String eventScope, String pattern, String serviceKey, + String changedType, String syncType, String watcherUuid) { + return new NamingFuzzyWatchNotifyEvent(eventScope, pattern, serviceKey, changedType, syncType, watcherUuid); + } + + public String getPattern() { + return pattern; + } + + public String getChangedType() { + return changedType; + } + + @Override + public String scope() { + return this.scope; + } + + public String getWatcherUuid() { + return watcherUuid; + } + + public String getServiceKey() { + return serviceKey; + } + + public String getScope() { + return scope; + } + + public String getSyncType() { + return syncType; + } +} diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/remote/NamingClientProxyDelegate.java b/client/src/main/java/com/alibaba/nacos/client/naming/remote/NamingClientProxyDelegate.java index efd7bdabf46..bea1ee38ee9 100644 --- a/client/src/main/java/com/alibaba/nacos/client/naming/remote/NamingClientProxyDelegate.java +++ b/client/src/main/java/com/alibaba/nacos/client/naming/remote/NamingClientProxyDelegate.java @@ -25,6 +25,7 @@ import com.alibaba.nacos.api.naming.utils.NamingUtils; import com.alibaba.nacos.api.selector.AbstractSelector; import com.alibaba.nacos.client.env.NacosClientProperties; +import com.alibaba.nacos.client.naming.cache.NamingFuzzyWatchServiceListHolder; import com.alibaba.nacos.client.naming.cache.ServiceInfoHolder; import com.alibaba.nacos.client.naming.core.NamingServerListManager; import com.alibaba.nacos.client.naming.core.ServiceInfoUpdateService; @@ -68,7 +69,8 @@ public class NamingClientProxyDelegate implements NamingClientProxy { private ScheduledExecutorService executorService; public NamingClientProxyDelegate(String namespace, ServiceInfoHolder serviceInfoHolder, - NacosClientProperties properties, InstancesChangeNotifier changeNotifier) throws NacosException { + NacosClientProperties properties, InstancesChangeNotifier changeNotifier, + NamingFuzzyWatchServiceListHolder namingFuzzyWatchServiceListHolder) throws NacosException { this.serviceInfoUpdateService = new ServiceInfoUpdateService(properties, serviceInfoHolder, this, changeNotifier); this.serverListManager = new NamingServerListManager(properties, namespace); @@ -79,7 +81,7 @@ public NamingClientProxyDelegate(String namespace, ServiceInfoHolder serviceInfo initSecurityProxy(properties); this.httpClientProxy = new NamingHttpClientProxy(namespace, securityProxy, serverListManager, properties); this.grpcClientProxy = new NamingGrpcClientProxy(namespace, securityProxy, serverListManager, properties, - serviceInfoHolder); + serviceInfoHolder, namingFuzzyWatchServiceListHolder); } private void initSecurityProxy(NacosClientProperties properties) { @@ -193,7 +195,8 @@ public boolean serverHealthy() { } private NamingClientProxy getExecuteClientProxy(Instance instance) { - if (instance.isEphemeral() || grpcClientProxy.isAbilitySupportedByServer(AbilityKey.SERVER_SUPPORT_PERSISTENT_INSTANCE_BY_GRPC)) { + if (instance.isEphemeral() || grpcClientProxy.isAbilitySupportedByServer( + AbilityKey.SERVER_SUPPORT_PERSISTENT_INSTANCE_BY_GRPC)) { return grpcClientProxy; } return httpClientProxy; 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 new file mode 100644 index 00000000000..aa40d048a01 --- /dev/null +++ b/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/NamingFuzzyWatchNotifyRequestHandler.java @@ -0,0 +1,126 @@ +/* + * 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.remote.gprc; + +import com.alibaba.nacos.api.common.Constants; +import com.alibaba.nacos.api.naming.remote.request.NamingFuzzyWatchChangeNotifyRequest; +import com.alibaba.nacos.api.naming.remote.request.NamingFuzzyWatchSyncRequest; +import com.alibaba.nacos.api.naming.remote.response.NamingFuzzyWatchChangeNotifyResponse; +import com.alibaba.nacos.api.naming.utils.NamingUtils; +import com.alibaba.nacos.api.remote.request.Request; +import com.alibaba.nacos.api.remote.response.Response; +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.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 java.util.Collection; + +import static com.alibaba.nacos.api.common.Constants.FUZZY_WATCH_RESOURCE_CHANGED; + +/** + * handle fuzzy watch request from server. + * @author shiyiyue + */ +public class NamingFuzzyWatchNotifyRequestHandler implements ServerRequestHandler { + + NamingFuzzyWatchServiceListHolder namingFuzzyWatchServiceListHolder; + + public NamingFuzzyWatchNotifyRequestHandler(NamingFuzzyWatchServiceListHolder namingFuzzyWatchServiceListHolder) { + this.namingFuzzyWatchServiceListHolder = namingFuzzyWatchServiceListHolder; + NotifyCenter.registerToPublisher(NamingFuzzyWatchNotifyEvent.class, 1000); + } + + @Override + public Response requestReply(Request request, Connection connection) { + + if (request instanceof NamingFuzzyWatchSyncRequest) { + NamingFuzzyWatchSyncRequest watchNotifySyncRequest = (NamingFuzzyWatchSyncRequest) request; + NamingFuzzyWatchContext namingFuzzyWatchContext = namingFuzzyWatchServiceListHolder.getFuzzyMatchContextMap() + .get(watchNotifySyncRequest.getGroupKeyPattern()); + if (namingFuzzyWatchContext != null) { + Collection serviceKeys = watchNotifySyncRequest.getContexts(); + if (watchNotifySyncRequest.getSyncType().equals(Constants.FUZZY_WATCH_INIT_NOTIFY) + || watchNotifySyncRequest.getSyncType().equals(Constants.FUZZY_WATCH_DIFF_SYNC_NOTIFY)) { + for (NamingFuzzyWatchSyncRequest.Context serviceKey : serviceKeys) { + // may have a 'change event' sent to client before 'init event' + if (namingFuzzyWatchContext.addReceivedServiceKey(serviceKey.getServiceKey())) { + NotifyCenter.publishEvent(NamingFuzzyWatchNotifyEvent.build( + namingFuzzyWatchServiceListHolder.getNotifierEventScope(), + watchNotifySyncRequest.getGroupKeyPattern(), serviceKey.getServiceKey(), + serviceKey.getChangedType(), watchNotifySyncRequest.getSyncType())); + } + } + } else if (watchNotifySyncRequest.getSyncType().equals(Constants.FINISH_FUZZY_WATCH_INIT_NOTIFY)) { + namingFuzzyWatchContext.markInitializationComplete(); + } + } + + return new NamingFuzzyWatchChangeNotifyResponse(); + + } else if (request instanceof NamingFuzzyWatchChangeNotifyRequest) { + NamingFuzzyWatchChangeNotifyRequest notifyChangeRequest = (NamingFuzzyWatchChangeNotifyRequest) request; + String[] serviceKeyItems = NamingUtils.parseServiceKey(notifyChangeRequest.getServiceKey()); + String namespace = serviceKeyItems[0]; + String groupName = serviceKeyItems[1]; + String serviceName = serviceKeyItems[2]; + + Collection matchedPattern = FuzzyGroupKeyPattern.filterMatchedPatterns( + namingFuzzyWatchServiceListHolder.getFuzzyMatchContextMap().keySet(), serviceName, groupName, + namespace); + String serviceChangeType = notifyChangeRequest.getChangedType(); + + switch (serviceChangeType) { + case Constants.ServiceChangedType.ADD_SERVICE: + case Constants.ServiceChangedType.INSTANCE_CHANGED: + for (String pattern : matchedPattern) { + NamingFuzzyWatchContext namingFuzzyWatchContext = namingFuzzyWatchServiceListHolder.getFuzzyMatchContextMap() + .get(pattern); + if (namingFuzzyWatchContext != null && namingFuzzyWatchContext.addReceivedServiceKey( + ((NamingFuzzyWatchChangeNotifyRequest) request).getServiceKey())) { + //publish local service add event + NotifyCenter.publishEvent(NamingFuzzyWatchNotifyEvent.build( + namingFuzzyWatchServiceListHolder.getNotifierEventScope(), pattern, + notifyChangeRequest.getServiceKey(), Constants.ServiceChangedType.ADD_SERVICE, + FUZZY_WATCH_RESOURCE_CHANGED)); + } + } + break; + case Constants.ServiceChangedType.DELETE_SERVICE: + for (String pattern : matchedPattern) { + NamingFuzzyWatchContext namingFuzzyWatchContext = namingFuzzyWatchServiceListHolder.getFuzzyMatchContextMap() + .get(pattern); + if (namingFuzzyWatchContext != null && namingFuzzyWatchContext.removeReceivedServiceKey( + notifyChangeRequest.getServiceKey())) { + NotifyCenter.publishEvent(NamingFuzzyWatchNotifyEvent.build( + namingFuzzyWatchServiceListHolder.getNotifierEventScope(), pattern, + notifyChangeRequest.getServiceKey(), Constants.ServiceChangedType.DELETE_SERVICE, + FUZZY_WATCH_RESOURCE_CHANGED)); + } + } + break; + default: + break; + } + return new NamingFuzzyWatchChangeNotifyResponse(); + } + return null; + } +} diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/NamingGrpcClientProxy.java b/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/NamingGrpcClientProxy.java index 508b76f0ab3..29d7e480486 100644 --- a/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/NamingGrpcClientProxy.java +++ b/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/NamingGrpcClientProxy.java @@ -29,24 +29,28 @@ import com.alibaba.nacos.api.naming.remote.request.AbstractNamingRequest; import com.alibaba.nacos.api.naming.remote.request.BatchInstanceRequest; import com.alibaba.nacos.api.naming.remote.request.InstanceRequest; +import com.alibaba.nacos.api.naming.remote.request.NamingFuzzyWatchRequest; import com.alibaba.nacos.api.naming.remote.request.PersistentInstanceRequest; import com.alibaba.nacos.api.naming.remote.request.ServiceListRequest; import com.alibaba.nacos.api.naming.remote.request.ServiceQueryRequest; import com.alibaba.nacos.api.naming.remote.request.SubscribeServiceRequest; import com.alibaba.nacos.api.naming.remote.response.BatchInstanceResponse; +import com.alibaba.nacos.api.naming.remote.response.NamingFuzzyWatchResponse; import com.alibaba.nacos.api.naming.remote.response.QueryServiceResponse; import com.alibaba.nacos.api.naming.remote.response.ServiceListResponse; import com.alibaba.nacos.api.naming.remote.response.SubscribeServiceResponse; import com.alibaba.nacos.api.naming.utils.NamingUtils; import com.alibaba.nacos.api.remote.RemoteConstants; +import com.alibaba.nacos.api.remote.request.Request; import com.alibaba.nacos.api.remote.response.Response; import com.alibaba.nacos.api.remote.response.ResponseCode; import com.alibaba.nacos.api.selector.AbstractSelector; import com.alibaba.nacos.api.selector.SelectorType; +import com.alibaba.nacos.client.address.ServerListChangeEvent; import com.alibaba.nacos.client.env.NacosClientProperties; import com.alibaba.nacos.client.monitor.MetricsMonitor; +import com.alibaba.nacos.client.naming.cache.NamingFuzzyWatchServiceListHolder; import com.alibaba.nacos.client.naming.cache.ServiceInfoHolder; -import com.alibaba.nacos.client.address.ServerListChangeEvent; import com.alibaba.nacos.client.naming.remote.AbstractNamingClientProxy; import com.alibaba.nacos.client.naming.remote.gprc.redo.NamingGrpcRedoService; import com.alibaba.nacos.client.naming.remote.gprc.redo.data.BatchInstanceRedoData; @@ -94,7 +98,8 @@ public class NamingGrpcClientProxy extends AbstractNamingClientProxy { private final NamingGrpcRedoService redoService; public NamingGrpcClientProxy(String namespaceId, SecurityProxy securityProxy, ServerListFactory serverListFactory, - NacosClientProperties properties, ServiceInfoHolder serviceInfoHolder) throws NacosException { + NacosClientProperties properties, ServiceInfoHolder serviceInfoHolder, + NamingFuzzyWatchServiceListHolder namingFuzzyWatchServiceListHolder) throws NacosException { super(securityProxy); this.namespaceId = namespaceId; this.uuid = UUID.randomUUID().toString(); @@ -103,18 +108,23 @@ public NamingGrpcClientProxy(String namespaceId, SecurityProxy securityProxy, Se labels.put(RemoteConstants.LABEL_SOURCE, RemoteConstants.LABEL_SOURCE_SDK); labels.put(RemoteConstants.LABEL_MODULE, RemoteConstants.LABEL_MODULE_NAMING); labels.put(Constants.APPNAME, AppNameUtils.getAppName()); + namingFuzzyWatchServiceListHolder.registerNamingGrpcClientProxy(this); this.rpcClient = RpcClientFactory.createClient(uuid, ConnectionType.GRPC, labels, RpcClientTlsConfigFactory.getInstance().createSdkConfig(properties.asProperties())); - this.redoService = new NamingGrpcRedoService(this, properties); + this.redoService = new NamingGrpcRedoService(this, namingFuzzyWatchServiceListHolder, properties); NAMING_LOGGER.info("Create naming rpc client for uuid->{}", uuid); - start(serverListFactory, serviceInfoHolder); + start(serverListFactory, serviceInfoHolder, namingFuzzyWatchServiceListHolder); } - private void start(ServerListFactory serverListFactory, ServiceInfoHolder serviceInfoHolder) throws NacosException { + private void start(ServerListFactory serverListFactory, ServiceInfoHolder serviceInfoHolder, + NamingFuzzyWatchServiceListHolder namingFuzzyWatchServiceListHolder) throws NacosException { rpcClient.serverListFactory(serverListFactory); rpcClient.registerConnectionListener(redoService); rpcClient.registerServerRequestHandler(new NamingPushRequestHandler(serviceInfoHolder)); + rpcClient.registerServerRequestHandler( + new NamingFuzzyWatchNotifyRequestHandler(namingFuzzyWatchServiceListHolder)); rpcClient.start(); + namingFuzzyWatchServiceListHolder.start(); NotifyCenter.registerSubscriber(this); } @@ -268,9 +278,8 @@ public void doRegisterServiceForPersistent(String serviceName, String groupName, @Override public void deregisterService(String serviceName, String groupName, Instance instance) throws NacosException { - NAMING_LOGGER - .info("[DEREGISTER-SERVICE] {} deregistering service {} with instance: {}", namespaceId, serviceName, - instance); + NAMING_LOGGER.info("[DEREGISTER-SERVICE] {} deregistering service {} with instance: {}", namespaceId, + serviceName, instance); if (instance.isEphemeral()) { deregisterServiceForEphemeral(serviceName, groupName, instance); } else { @@ -438,12 +447,31 @@ public boolean isAbilitySupportedByServer(AbilityKey abilityKey) { return rpcClient.getConnectionAbility(abilityKey) == AbilityStatus.SUPPORTED; } - private T requestToServer(AbstractNamingRequest request, Class responseClass) + /** + * Execute unsubscribe operation. + * + * @param namingFuzzyWatchRequest namingFuzzyWatchRequest + * @throws NacosException nacos exception + */ + public NamingFuzzyWatchResponse fuzzyWatchRequest(NamingFuzzyWatchRequest namingFuzzyWatchRequest) throws NacosException { + return requestToServer(namingFuzzyWatchRequest, NamingFuzzyWatchResponse.class); + } + + private T requestToServer(Request request, Class responseClass) throws NacosException { Response response = null; try { - request.putAllHeader( - getSecurityHeaders(request.getNamespace(), request.getGroupName(), request.getServiceName())); + if (request instanceof AbstractNamingRequest) { + request.putAllHeader(getSecurityHeaders(((AbstractNamingRequest) request).getNamespace(), + ((AbstractNamingRequest) request).getGroupName(), + ((AbstractNamingRequest) request).getServiceName())); + } else if (request instanceof NamingFuzzyWatchRequest) { + request.putAllHeader( + getSecurityHeaders(((NamingFuzzyWatchRequest) request).getNamespace(), null, null)); + } else { + throw new NacosException(400, "unknown naming request type"); + } + response = requestTimeout < 0 ? rpcClient.request(request) : rpcClient.request(request, requestTimeout); if (ResponseCode.SUCCESS.getCode() != response.getResultCode()) { // If the 403 login operation is triggered, refresh the accessToken of the client @@ -475,7 +503,7 @@ private T requestToServer(AbstractNamingRequest request, Cl * successful. * @param response The response object containing registration result information, or null if registration failed. */ - private void recordRequestFailedMetrics(AbstractNamingRequest request, Exception exception, Response response) { + private void recordRequestFailedMetrics(Request request, Exception exception, Response response) { if (Objects.isNull(response)) { MetricsMonitor.getNamingRequestFailedMonitor(request.getClass().getSimpleName(), MONITOR_LABEL_NONE, MONITOR_LABEL_NONE, exception.getClass().getSimpleName()).inc(); @@ -508,4 +536,8 @@ private void shutDownAndRemove(String uuid) { public boolean isEnable() { return rpcClient.isRunning(); } + + public String getNamespaceId() { + return namespaceId; + } } diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/redo/NamingGrpcRedoService.java b/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/redo/NamingGrpcRedoService.java index aad883ddabe..7527b9d00ce 100644 --- a/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/redo/NamingGrpcRedoService.java +++ b/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/redo/NamingGrpcRedoService.java @@ -22,6 +22,7 @@ import com.alibaba.nacos.api.naming.pojo.ServiceInfo; import com.alibaba.nacos.api.naming.utils.NamingUtils; import com.alibaba.nacos.client.env.NacosClientProperties; +import com.alibaba.nacos.client.naming.cache.NamingFuzzyWatchServiceListHolder; import com.alibaba.nacos.client.naming.remote.gprc.NamingGrpcClientProxy; import com.alibaba.nacos.client.naming.remote.gprc.redo.data.BatchInstanceRedoData; import com.alibaba.nacos.client.naming.remote.gprc.redo.data.InstanceRedoData; @@ -59,12 +60,16 @@ public class NamingGrpcRedoService implements ConnectionEventListener { private final ConcurrentMap subscribes = new ConcurrentHashMap<>(); + private final NamingFuzzyWatchServiceListHolder namingFuzzyWatchServiceListHolder; + private final ScheduledExecutorService redoExecutor; private volatile boolean connected = false; - public NamingGrpcRedoService(NamingGrpcClientProxy clientProxy, NacosClientProperties properties) { + public NamingGrpcRedoService(NamingGrpcClientProxy clientProxy, + NamingFuzzyWatchServiceListHolder namingFuzzyWatchServiceListHolder, NacosClientProperties properties) { setProperties(properties); + this.namingFuzzyWatchServiceListHolder = namingFuzzyWatchServiceListHolder; this.redoExecutor = new ScheduledThreadPoolExecutor(redoThreadCount, new NameThreadFactory(REDO_THREAD_NAME)); this.redoExecutor.scheduleWithFixedDelay(new RedoScheduledTask(clientProxy, this), redoDelayTime, redoDelayTime, TimeUnit.MILLISECONDS); @@ -100,6 +105,9 @@ public void onDisConnect(Connection connection) { synchronized (subscribes) { subscribes.values().forEach(subscriberRedoData -> subscriberRedoData.setRegistered(false)); } + synchronized (namingFuzzyWatchServiceListHolder) { + namingFuzzyWatchServiceListHolder.resetConsistenceStatus(); + } LogUtils.NAMING_LOGGER.warn("mark to redo completed"); } diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/redo/data/FuzzyWatcherRedoData.java b/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/redo/data/FuzzyWatcherRedoData.java new file mode 100644 index 00000000000..f0e820910dd --- /dev/null +++ b/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/redo/data/FuzzyWatcherRedoData.java @@ -0,0 +1,33 @@ +/* + * 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.remote.gprc.redo.data; + +/** + * Redo data for fuzzy watcher. + * + * @author tanyongquan + */ +public class FuzzyWatcherRedoData extends RedoData { + + private FuzzyWatcherRedoData(String serviceNamePattern, String groupNamePattern) { + super(serviceNamePattern, groupNamePattern); + } + + public static FuzzyWatcherRedoData build(String serviceNamePattern, String groupNamePattern) { + return new FuzzyWatcherRedoData(serviceNamePattern, groupNamePattern); + } +} diff --git a/client/src/main/java/com/alibaba/nacos/client/utils/ParamUtil.java b/client/src/main/java/com/alibaba/nacos/client/utils/ParamUtil.java index f6f8be2b4cc..4325d60adcc 100644 --- a/client/src/main/java/com/alibaba/nacos/client/utils/ParamUtil.java +++ b/client/src/main/java/com/alibaba/nacos/client/utils/ParamUtil.java @@ -62,8 +62,6 @@ public class ParamUtil { private static double perTaskConfigSize = 3000; - private static final String PER_TASK_CONTEXT_SIZE_KEY = "PER_TASK_CONTEXT_SIZE_KEY"; - private static final String NACOS_CLIENT_APP_KEY = "nacos.client.appKey"; private static final String BLANK_STR = ""; @@ -86,9 +84,6 @@ public class ParamUtil { private static final String DEFAULT_PER_TASK_CONFIG_SIZE_KEY = "3000"; - private static final String DEFAULT_PER_TASK_CONTEXT_SIZE_KEY = "3000"; - - private static double perTaskContextSize = 3000; private static final int DESENSITISE_PARAMETER_MIN_LENGTH = 2; private static final int DESENSITISE_PARAMETER_KEEP_ONE_CHAR_LENGTH = 8; @@ -115,9 +110,6 @@ public class ParamUtil { perTaskConfigSize = initPerTaskConfigSize(); LOGGER.info("PER_TASK_CONFIG_SIZE: {}", perTaskConfigSize); - - perTaskContextSize = initPerTaskContextSize(); - LOGGER.info("PER_TASK_CONTEXT_SIZE: {}", perTaskContextSize); } private static int initConnectionTimeout() { @@ -154,16 +146,6 @@ private static double initPerTaskConfigSize() { } } - private static double initPerTaskContextSize() { - try { - return Double.parseDouble(NacosClientProperties.PROTOTYPE.getProperty(PER_TASK_CONTEXT_SIZE_KEY, - DEFAULT_PER_TASK_CONTEXT_SIZE_KEY)); - } catch (NumberFormatException e) { - LOGGER.error("[PER_TASK_CONTEXT_SIZE] PER_TASK_CONTEXT_SIZE invalid", e); - throw new IllegalArgumentException("invalid PER_TASK_CONTEXT_SIZE, expected value type double", e); - } - } - public static String getAppKey() { return appKey; } @@ -220,14 +202,6 @@ public static void setPerTaskConfigSize(double perTaskConfigSize) { ParamUtil.perTaskConfigSize = perTaskConfigSize; } - public static double getPerTaskContextSize() { - return perTaskContextSize; - } - - public static void setPerTaskContextSize(double perTaskContextSize) { - ParamUtil.perTaskContextSize = perTaskContextSize; - } - public static String getDefaultServerPort() { return serverPort; } @@ -283,10 +257,10 @@ public static String parsingEndpointRule(String endpointUrl) { if (StringUtils.isNotBlank(endpointUrlSource)) { endpointUrl = endpointUrlSource; } - + return StringUtils.isNotBlank(endpointUrl) ? endpointUrl : ""; } - + endpointUrl = endpointUrl.substring(endpointUrl.indexOf("${") + 2, endpointUrl.lastIndexOf("}")); int defStartOf = endpointUrl.indexOf(":"); String defaultEndpointUrl = null; @@ -294,13 +268,12 @@ public static String parsingEndpointRule(String endpointUrl) { defaultEndpointUrl = endpointUrl.substring(defStartOf + 1); endpointUrl = endpointUrl.substring(0, defStartOf); } - String endpointUrlSource = TemplateUtils.stringBlankAndThenExecute( NacosClientProperties.PROTOTYPE.getProperty(endpointUrl), () -> NacosClientProperties.PROTOTYPE.getProperty( PropertyKeyConst.SystemEnv.ALIBABA_ALIWARE_ENDPOINT_URL)); - + if (StringUtils.isBlank(endpointUrlSource)) { if (StringUtils.isNotBlank(defaultEndpointUrl)) { endpointUrl = defaultEndpointUrl; @@ -308,7 +281,7 @@ public static String parsingEndpointRule(String endpointUrl) { } else { endpointUrl = endpointUrlSource; } - + return StringUtils.isNotBlank(endpointUrl) ? endpointUrl : ""; } diff --git a/client/src/main/resources/META-INF/native-image/com.alibaba.nacos/nacos-client/reflect-config.json b/client/src/main/resources/META-INF/native-image/com.alibaba.nacos/nacos-client/reflect-config.json index 3734fbb2843..da2ba912161 100644 --- a/client/src/main/resources/META-INF/native-image/com.alibaba.nacos/nacos-client/reflect-config.json +++ b/client/src/main/resources/META-INF/native-image/com.alibaba.nacos/nacos-client/reflect-config.json @@ -535,14 +535,23 @@ {"name":"getServiceName","parameterTypes":[] } ] }, +{ + "name":"com.alibaba.nacos.api.naming.remote.request.AbstractFuzzyWatchNotifyRequest", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[ {"name":"getInstances","parameterTypes":[] }, + {"name":"getType","parameterTypes":[] }] +}, { "name":"com.alibaba.nacos.api.naming.remote.request.BatchInstanceRequest", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[ - {"name":"getInstances","parameterTypes":[] }, - {"name":"getType","parameterTypes":[] } + {"name":"","parameterTypes":[] }, + {"name":"getModule","parameterTypes":[] }, + {"name":"getServiceChangedType","parameterTypes":[] } ] }, { @@ -603,6 +612,28 @@ {"name":"isHealthyOnly","parameterTypes":[] } ] }, +{ + "name":"com.alibaba.nacos.api.naming.remote.request.FuzzyWatchNotifyChangeRequest", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[ + {"name":"","parameterTypes":[] }, + {"name":"getServiceName","parameterTypes":[] }, + {"name":"getGroupName","parameterTypes":[] } + ] +}, +{ + "name":"com.alibaba.nacos.api.naming.remote.request.FuzzyWatchNotifyInitRequest", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[ + {"name":"","parameterTypes":[] }, + {"name":"getServicesName","parameterTypes":[] }, + {"name":"getPattern","parameterTypes":[] } + ] +}, { "name":"com.alibaba.nacos.api.naming.remote.request.SubscribeServiceRequest", "allDeclaredFields":true, diff --git a/client/src/test/java/com/alibaba/nacos/client/config/NacosConfigServiceTest.java b/client/src/test/java/com/alibaba/nacos/client/config/NacosConfigServiceTest.java index 4cf62936894..6dcc4e368cf 100644 --- a/client/src/test/java/com/alibaba/nacos/client/config/NacosConfigServiceTest.java +++ b/client/src/test/java/com/alibaba/nacos/client/config/NacosConfigServiceTest.java @@ -38,6 +38,7 @@ import org.mockito.quality.Strictness; import java.lang.reflect.Field; +import java.util.Arrays; import java.util.Collections; import java.util.Properties; import java.util.concurrent.Executor; @@ -98,10 +99,12 @@ void testGetConfigFromFailOver() throws NacosException { final String group = "2"; final String tenant = "public"; - MockedStatic localConfigInfoProcessorMockedStatic = Mockito.mockStatic(LocalConfigInfoProcessor.class); + MockedStatic localConfigInfoProcessorMockedStatic = Mockito.mockStatic( + LocalConfigInfoProcessor.class); try { String contentFailOver = "failOverContent" + System.currentTimeMillis(); - localConfigInfoProcessorMockedStatic.when(() -> LocalConfigInfoProcessor.getFailover(any(), eq(dataId), eq(group), eq(tenant))) + localConfigInfoProcessorMockedStatic.when( + () -> LocalConfigInfoProcessor.getFailover(any(), eq(dataId), eq(group), eq(tenant))) .thenReturn(contentFailOver); final int timeout = 3000; @@ -118,18 +121,22 @@ void testGetConfigFromLocalCache() throws NacosException { final String group = "2"; final String tenant = "public"; - MockedStatic localConfigInfoProcessorMockedStatic = Mockito.mockStatic(LocalConfigInfoProcessor.class); + MockedStatic localConfigInfoProcessorMockedStatic = Mockito.mockStatic( + LocalConfigInfoProcessor.class); try { String contentFailOver = "localCacheContent" + System.currentTimeMillis(); //fail over null - localConfigInfoProcessorMockedStatic.when(() -> LocalConfigInfoProcessor.getFailover(any(), eq(dataId), eq(group), eq(tenant))) + localConfigInfoProcessorMockedStatic.when( + () -> LocalConfigInfoProcessor.getFailover(any(), eq(dataId), eq(group), eq(tenant))) .thenReturn(null); //snapshot content - localConfigInfoProcessorMockedStatic.when(() -> LocalConfigInfoProcessor.getSnapshot(any(), eq(dataId), eq(group), eq(tenant))) + localConfigInfoProcessorMockedStatic.when( + () -> LocalConfigInfoProcessor.getSnapshot(any(), eq(dataId), eq(group), eq(tenant))) .thenReturn(contentFailOver); //form server error. final int timeout = 3000; - Mockito.when(mockWoker.getServerConfig(dataId, group, tenant, timeout, false)).thenThrow(new NacosException()); + Mockito.when(mockWoker.getServerConfig(dataId, group, tenant, timeout, false)) + .thenThrow(new NacosException()); final String config = nacosConfigService.getConfig(dataId, group, timeout); assertEquals(contentFailOver, config); @@ -145,10 +152,12 @@ void testGetConfig403() throws NacosException { final String group = "2"; final String tenant = "public"; - MockedStatic localConfigInfoProcessorMockedStatic = Mockito.mockStatic(LocalConfigInfoProcessor.class); + MockedStatic localConfigInfoProcessorMockedStatic = Mockito.mockStatic( + LocalConfigInfoProcessor.class); try { //fail over null - localConfigInfoProcessorMockedStatic.when(() -> LocalConfigInfoProcessor.getFailover(any(), eq(dataId), eq(group), eq(tenant))) + localConfigInfoProcessorMockedStatic.when( + () -> LocalConfigInfoProcessor.getFailover(any(), eq(dataId), eq(group), eq(tenant))) .thenReturn(null); //form server error. @@ -157,7 +166,6 @@ void testGetConfig403() throws NacosException { .thenThrow(new NacosException(NacosException.NO_RIGHT, "no right")); try { nacosConfigService.getConfig(dataId, group, timeout); - Assert.fail(); assertTrue(false); } catch (NacosException e) { assertEquals(NacosException.NO_RIGHT, e.getErrCode()); @@ -196,45 +204,30 @@ public void receiveConfigInfo(String configInfo) { public void startInternal() throws NacosException { // NOOP } - + @Override public String getName() { return "TestConfigTransportClient"; } - + @Override public void notifyListenConfig() { // NOOP } - - @Override - public void notifyFuzzyListenConfig() { - // NOOP - } - + @Override public void executeConfigListen() { // NOOP } - - @Override - public void executeConfigFuzzyListen() throws NacosException { - // NOOP - } - + @Override public void removeCache(String dataId, String group) { // NOOP } - - @Override - public void removeFuzzyListenContext(String dataIdPattern, String group) { - // NOOP - } - + @Override - public ConfigResponse queryConfig(String dataId, String group, String tenant, long readTimeous, boolean notify) - throws NacosException { + public ConfigResponse queryConfig(String dataId, String group, String tenant, long readTimeous, + boolean notify) throws NacosException { ConfigResponse configResponse = new ConfigResponse(); configResponse.setContent(content); configResponse.setDataId(dataId); @@ -244,8 +237,9 @@ public ConfigResponse queryConfig(String dataId, String group, String tenant, lo } @Override - public boolean publishConfig(String dataId, String group, String tenant, String appName, String tag, String betaIps, - String content, String encryptedDataKey, String casMd5, String type) throws NacosException { + public boolean publishConfig(String dataId, String group, String tenant, String appName, String tag, + String betaIps, String content, String encryptedDataKey, String casMd5, String type) + throws NacosException { return false; } @@ -257,13 +251,14 @@ public boolean removeConfig(String dataId, String group, String tenant, String t Mockito.when(mockWoker.getAgent()).thenReturn(client); final String config = nacosConfigService.getConfigAndSignListener(dataId, group, timeout, listener); - Assert.assertEquals(content, config); - + assertEquals(content, config); + Mockito.verify(mockWoker, Mockito.times(1)) .addTenantListenersWithContent(dataId, group, content, null, Collections.singletonList(listener)); assertEquals(content, config); - Mockito.verify(mockWoker, Mockito.times(1)).addTenantListenersWithContent(dataId, group, content, null, Arrays.asList(listener)); + Mockito.verify(mockWoker, Mockito.times(1)) + .addTenantListenersWithContent(dataId, group, content, null, Arrays.asList(listener)); } @Test @@ -275,16 +270,15 @@ void testAddListener() throws NacosException { public Executor getExecutor() { return null; } - + @Override public void receiveConfigInfo(String configInfo) { - + } }; - + nacosConfigService.addListener(dataId, group, listener); - Mockito.verify(mockWoker, Mockito.times(1)) - .addTenantListeners(dataId, group, Collections.singletonList(listener)); + Mockito.verify(mockWoker, Mockito.times(1)).addTenantListeners(dataId, group, Arrays.asList(listener)); } @Test @@ -294,12 +288,14 @@ void testPublishConfig() throws NacosException { String content = "123"; String namespace = "public"; String type = ConfigType.getDefaultType().getType(); - Mockito.when(mockWoker.publishConfig(dataId, group, namespace, null, null, null, content, "", null, type)).thenReturn(true); + Mockito.when(mockWoker.publishConfig(dataId, group, namespace, null, null, null, content, "", null, type)) + .thenReturn(true); final boolean b = nacosConfigService.publishConfig(dataId, group, content); assertTrue(b); - Mockito.verify(mockWoker, Mockito.times(1)).publishConfig(dataId, group, namespace, null, null, null, content, "", null, type); + Mockito.verify(mockWoker, Mockito.times(1)) + .publishConfig(dataId, group, namespace, null, null, null, content, "", null, type); } @Test @@ -310,12 +306,14 @@ void testPublishConfig2() throws NacosException { String namespace = "public"; String type = ConfigType.PROPERTIES.getType(); - Mockito.when(mockWoker.publishConfig(dataId, group, namespace, null, null, null, content, "", null, type)).thenReturn(true); + Mockito.when(mockWoker.publishConfig(dataId, group, namespace, null, null, null, content, "", null, type)) + .thenReturn(true); final boolean b = nacosConfigService.publishConfig(dataId, group, content, type); assertTrue(b); - Mockito.verify(mockWoker, Mockito.times(1)).publishConfig(dataId, group, namespace, null, null, null, content, "", null, type); + Mockito.verify(mockWoker, Mockito.times(1)) + .publishConfig(dataId, group, namespace, null, null, null, content, "", null, type); } @Test @@ -327,12 +325,14 @@ void testPublishConfigCas() throws NacosException { String casMd5 = "96147704e3cb8be8597d55d75d244a02"; String type = ConfigType.getDefaultType().getType(); - Mockito.when(mockWoker.publishConfig(dataId, group, namespace, null, null, null, content, "", casMd5, type)).thenReturn(true); + Mockito.when(mockWoker.publishConfig(dataId, group, namespace, null, null, null, content, "", casMd5, type)) + .thenReturn(true); final boolean b = nacosConfigService.publishConfigCas(dataId, group, content, casMd5); assertTrue(b); - Mockito.verify(mockWoker, Mockito.times(1)).publishConfig(dataId, group, namespace, null, null, null, content, "", casMd5, type); + Mockito.verify(mockWoker, Mockito.times(1)) + .publishConfig(dataId, group, namespace, null, null, null, content, "", casMd5, type); } @Test @@ -344,12 +344,14 @@ void testPublishConfigCas2() throws NacosException { String casMd5 = "96147704e3cb8be8597d55d75d244a02"; String type = ConfigType.PROPERTIES.getType(); - Mockito.when(mockWoker.publishConfig(dataId, group, namespace, null, null, null, content, "", casMd5, type)).thenReturn(true); + Mockito.when(mockWoker.publishConfig(dataId, group, namespace, null, null, null, content, "", casMd5, type)) + .thenReturn(true); final boolean b = nacosConfigService.publishConfigCas(dataId, group, content, casMd5, type); assertTrue(b); - Mockito.verify(mockWoker, Mockito.times(1)).publishConfig(dataId, group, namespace, null, null, null, content, "", casMd5, type); + Mockito.verify(mockWoker, Mockito.times(1)) + .publishConfig(dataId, group, namespace, null, null, null, content, "", casMd5, type); } @Test diff --git a/client/src/test/java/com/alibaba/nacos/client/naming/remote/AbstractNamingClientProxyTest.java b/client/src/test/java/com/alibaba/nacos/client/naming/remote/AbstractNamingClientProxyTest.java index 7f570c2780b..cba87c980c2 100644 --- a/client/src/test/java/com/alibaba/nacos/client/naming/remote/AbstractNamingClientProxyTest.java +++ b/client/src/test/java/com/alibaba/nacos/client/naming/remote/AbstractNamingClientProxyTest.java @@ -25,8 +25,8 @@ import com.alibaba.nacos.api.naming.pojo.Service; import com.alibaba.nacos.api.naming.pojo.ServiceInfo; import com.alibaba.nacos.api.selector.AbstractSelector; -import com.alibaba.nacos.client.auth.ram.utils.SignUtil; import com.alibaba.nacos.client.address.ServerListChangeEvent; +import com.alibaba.nacos.client.auth.ram.utils.SignUtil; import com.alibaba.nacos.client.security.SecurityProxy; import com.alibaba.nacos.client.utils.AppNameUtils; import com.alibaba.nacos.common.notify.Event; diff --git a/client/src/test/java/com/alibaba/nacos/client/naming/remote/NamingClientProxyDelegateTest.java b/client/src/test/java/com/alibaba/nacos/client/naming/remote/NamingClientProxyDelegateTest.java index bdc3ac6cec1..7e1031cba34 100644 --- a/client/src/test/java/com/alibaba/nacos/client/naming/remote/NamingClientProxyDelegateTest.java +++ b/client/src/test/java/com/alibaba/nacos/client/naming/remote/NamingClientProxyDelegateTest.java @@ -27,6 +27,7 @@ import com.alibaba.nacos.api.selector.ExpressionSelector; import com.alibaba.nacos.api.selector.NoneSelector; import com.alibaba.nacos.client.env.NacosClientProperties; +import com.alibaba.nacos.client.naming.cache.NamingFuzzyWatchServiceListHolder; import com.alibaba.nacos.client.naming.cache.ServiceInfoHolder; import com.alibaba.nacos.client.naming.event.InstancesChangeNotifier; import com.alibaba.nacos.client.naming.remote.gprc.NamingGrpcClientProxy; @@ -65,6 +66,9 @@ class NamingClientProxyDelegateTest { @Mock NamingGrpcClientProxy mockGrpcClient; + @Mock + NamingFuzzyWatchServiceListHolder namingFuzzyWatchServiceListHolder; + NamingClientProxyDelegate delegate; InstancesChangeNotifier notifier; @@ -77,7 +81,8 @@ void setUp() throws NacosException, NoSuchFieldException, IllegalAccessException props.setProperty("serverAddr", "localhost"); nacosClientProperties = NacosClientProperties.PROTOTYPE.derive(props); notifier = new InstancesChangeNotifier(); - delegate = new NamingClientProxyDelegate(TEST_NAMESPACE, holder, nacosClientProperties, notifier); + delegate = new NamingClientProxyDelegate(TEST_NAMESPACE, holder, nacosClientProperties, notifier, + namingFuzzyWatchServiceListHolder); Field grpcClientProxyField = NamingClientProxyDelegate.class.getDeclaredField("grpcClientProxy"); grpcClientProxyField.setAccessible(true); grpcClientProxyField.set(delegate, mockGrpcClient); diff --git a/client/src/test/java/com/alibaba/nacos/client/naming/remote/gprc/NamingGrpcClientProxyTest.java b/client/src/test/java/com/alibaba/nacos/client/naming/remote/gprc/NamingGrpcClientProxyTest.java index bdb9472cfbc..0efeba8f6db 100644 --- a/client/src/test/java/com/alibaba/nacos/client/naming/remote/gprc/NamingGrpcClientProxyTest.java +++ b/client/src/test/java/com/alibaba/nacos/client/naming/remote/gprc/NamingGrpcClientProxyTest.java @@ -47,6 +47,7 @@ import com.alibaba.nacos.api.selector.NoneSelector; import com.alibaba.nacos.client.env.NacosClientProperties; import com.alibaba.nacos.client.naming.cache.ServiceInfoHolder; +import com.alibaba.nacos.client.naming.cache.NamingFuzzyWatchServiceListHolder; import com.alibaba.nacos.client.address.ServerListChangeEvent; import com.alibaba.nacos.client.naming.remote.gprc.redo.NamingGrpcRedoService; import com.alibaba.nacos.client.security.SecurityProxy; @@ -120,6 +121,9 @@ class NamingGrpcClientProxyTest { @Mock private ServiceInfoHolder holder; + @Mock + private NamingFuzzyWatchServiceListHolder namingFuzzyWatchServiceListHolder; + @Mock private RpcClient rpcClient; @@ -145,7 +149,8 @@ void setUp() throws NacosException, NoSuchFieldException, IllegalAccessException prop = new Properties(); final NacosClientProperties nacosClientProperties = NacosClientProperties.PROTOTYPE.derive(prop); - client = new NamingGrpcClientProxy(NAMESPACE_ID, proxy, factory, nacosClientProperties, holder); + client = new NamingGrpcClientProxy(NAMESPACE_ID, proxy, factory, nacosClientProperties, holder, + namingFuzzyWatchServiceListHolder); Field uuidField = NamingGrpcClientProxy.class.getDeclaredField("uuid"); uuidField.setAccessible(true); @@ -699,7 +704,8 @@ public void close() { @Test void testConfigAppNameLabels() throws Exception { final NacosClientProperties nacosClientProperties = NacosClientProperties.PROTOTYPE.derive(prop); - client = new NamingGrpcClientProxy(NAMESPACE_ID, proxy, factory, nacosClientProperties, holder); + client = new NamingGrpcClientProxy(NAMESPACE_ID, proxy, factory, nacosClientProperties, holder, + namingFuzzyWatchServiceListHolder); Field rpcClientField = NamingGrpcClientProxy.class.getDeclaredField("rpcClient"); rpcClientField.setAccessible(true); RpcClient rpcClient = (RpcClient) rpcClientField.get(client); diff --git a/client/src/test/java/com/alibaba/nacos/client/naming/remote/gprc/NamingPushRequestHandlerTest.java b/client/src/test/java/com/alibaba/nacos/client/naming/remote/gprc/NamingPushRequestHandlerTest.java index 341c26e1dcd..40855f67ae6 100644 --- a/client/src/test/java/com/alibaba/nacos/client/naming/remote/gprc/NamingPushRequestHandlerTest.java +++ b/client/src/test/java/com/alibaba/nacos/client/naming/remote/gprc/NamingPushRequestHandlerTest.java @@ -25,6 +25,7 @@ import com.alibaba.nacos.api.remote.request.Request; import com.alibaba.nacos.api.remote.response.Response; import com.alibaba.nacos.client.naming.cache.ServiceInfoHolder; + import com.alibaba.nacos.client.naming.remote.TestConnection; import com.alibaba.nacos.common.remote.client.RpcClient; import org.junit.jupiter.api.Test; @@ -54,6 +55,7 @@ void testRequestReply() { @Test void testRequestReplyOtherType() { ServiceInfoHolder holder = mock(ServiceInfoHolder.class); + NamingPushRequestHandler handler = new NamingPushRequestHandler(holder); assertNull(handler.requestReply(new HealthCheckRequest(), new TestConnection(new RpcClient.ServerInfo()))); } diff --git a/client/src/test/java/com/alibaba/nacos/client/naming/remote/gprc/redo/NamingGrpcRedoServiceTest.java b/client/src/test/java/com/alibaba/nacos/client/naming/remote/gprc/redo/NamingGrpcRedoServiceTest.java index 61c31fc51ef..60fe8c8eab8 100644 --- a/client/src/test/java/com/alibaba/nacos/client/naming/remote/gprc/redo/NamingGrpcRedoServiceTest.java +++ b/client/src/test/java/com/alibaba/nacos/client/naming/remote/gprc/redo/NamingGrpcRedoServiceTest.java @@ -19,6 +19,7 @@ import com.alibaba.nacos.api.PropertyKeyConst; import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.client.env.NacosClientProperties; +import com.alibaba.nacos.client.naming.cache.NamingFuzzyWatchServiceListHolder; import com.alibaba.nacos.client.naming.remote.TestConnection; import com.alibaba.nacos.client.naming.remote.gprc.NamingGrpcClientProxy; import com.alibaba.nacos.client.naming.remote.gprc.redo.data.BatchInstanceRedoData; @@ -56,13 +57,16 @@ class NamingGrpcRedoServiceTest { @Mock private NamingGrpcClientProxy clientProxy; + @Mock + private NamingFuzzyWatchServiceListHolder namingFuzzyWatchServiceListHolder; + private NamingGrpcRedoService redoService; @BeforeEach void setUp() throws Exception { Properties prop = new Properties(); NacosClientProperties nacosClientProperties = NacosClientProperties.PROTOTYPE.derive(prop); - redoService = new NamingGrpcRedoService(clientProxy, nacosClientProperties); + redoService = new NamingGrpcRedoService(clientProxy, namingFuzzyWatchServiceListHolder, nacosClientProperties); ScheduledExecutorService redoExecutor = (ScheduledExecutorService) ReflectUtils.getFieldValue(redoService, "redoExecutor"); redoExecutor.shutdownNow(); @@ -95,7 +99,7 @@ void testCustomProperties() throws Exception { prop.setProperty(PropertyKeyConst.REDO_DELAY_THREAD_COUNT, "2"); NacosClientProperties nacosClientProperties = NacosClientProperties.PROTOTYPE.derive(prop); - NamingGrpcRedoService redoService = new NamingGrpcRedoService(clientProxy, nacosClientProperties); + NamingGrpcRedoService redoService = new NamingGrpcRedoService(clientProxy, namingFuzzyWatchServiceListHolder, nacosClientProperties); Field redoThreadCountField = NamingGrpcRedoService.class.getDeclaredField("redoThreadCount"); redoThreadCountField.setAccessible(true); diff --git a/common/src/main/java/com/alibaba/nacos/common/task/BatchTaskCounter.java b/common/src/main/java/com/alibaba/nacos/common/task/BatchTaskCounter.java new file mode 100644 index 00000000000..1d049d14cb0 --- /dev/null +++ b/common/src/main/java/com/alibaba/nacos/common/task/BatchTaskCounter.java @@ -0,0 +1,73 @@ +/* + * 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.common.task; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * batch task counter. + * + * @author shiyiyue + */ +public class BatchTaskCounter { + + List batchCounter; + + public BatchTaskCounter(int totalBatch) { + initBatchCounter(totalBatch); + } + + /** + * init counter. + * @param totalBatch totalBatch. + */ + private void initBatchCounter(int totalBatch) { + batchCounter = new ArrayList<>(totalBatch); + for (int i = 0; i < totalBatch; i++) { + batchCounter.add(i, new AtomicBoolean(false)); + } + } + + /** + * set bath succeed. + * @param batch succeed batch. + */ + public void batchSuccess(int batch) { + if (batch <= batchCounter.size()) { + batchCounter.get(batch - 1).set(true); + } + } + + /** + * check all completed. + * @return + */ + public boolean batchCompleted() { + for (AtomicBoolean atomicBoolean : batchCounter) { + if (!atomicBoolean.get()) { + return false; + } + } + return true; + } + + public int getTotalBatch() { + return batchCounter.size(); + } +} diff --git a/common/src/main/java/com/alibaba/nacos/common/utils/FuzzyGroupKeyPattern.java b/common/src/main/java/com/alibaba/nacos/common/utils/FuzzyGroupKeyPattern.java new file mode 100644 index 00000000000..fae7accbd90 --- /dev/null +++ b/common/src/main/java/com/alibaba/nacos/common/utils/FuzzyGroupKeyPattern.java @@ -0,0 +1,240 @@ +/* + * 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.common.utils; + +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static com.alibaba.nacos.api.common.Constants.ALL_PATTERN; +import static com.alibaba.nacos.api.common.Constants.DEFAULT_NAMESPACE_ID; +import static com.alibaba.nacos.api.common.Constants.FUZZY_WATCH_PATTERN_SPLITTER; + +/** + * Utility class for matching group keys against a given pattern. + * + *

This class provides methods to match group keys based on a pattern specified. It supports matching based on + * dataId, group, and namespace components of the group key. + * + * @author stone-98 + * @date 2024/3/14 + */ +public class FuzzyGroupKeyPattern { + + /** + * Generates a fuzzy listen group key pattern based on the given dataId pattern, group, and optional tenant. pattern + * result as: fixNamespace>>groupPattern>>dataIdPattern + * + * @param resourcePattern The pattern for matching dataIds or service names. + * @param groupPattern The groupPattern associated with the groups. + * @param fixNamespace (Optional) The tenant associated with the dataIds (can be null or empty). + * @return A unique group key pattern for fuzzy listen. + * @throws IllegalArgumentException If the dataId pattern or group is blank. + */ + public static String generatePattern(final String resourcePattern, final String groupPattern, String fixNamespace) { + if (StringUtils.isBlank(resourcePattern)) { + throw new IllegalArgumentException("Param 'resourcePattern' is illegal, resourcePattern is blank"); + } + if (StringUtils.isBlank(groupPattern)) { + throw new IllegalArgumentException("Param 'groupPattern' is illegal, group is blank"); + } + if (StringUtils.isBlank(fixNamespace)) { + fixNamespace = DEFAULT_NAMESPACE_ID; + } + StringBuilder sb = new StringBuilder(); + sb.append(fixNamespace); + sb.append(FUZZY_WATCH_PATTERN_SPLITTER); + sb.append(groupPattern); + sb.append(FUZZY_WATCH_PATTERN_SPLITTER); + sb.append(resourcePattern); + return sb.toString().intern(); + } + + /** + * Given a dataId, group, and a collection of completed group key patterns, returns the patterns that match. + * + * @param resourceName The dataId or service name to match. + * @param group The group to match. + * @param namespace The group to match. + * @param groupKeyPatterns The collection of completed group key patterns to match against. + * @return A set of patterns that match the dataId and group. + */ + public static Set filterMatchedPatterns(Collection groupKeyPatterns, String resourceName, + String group, String namespace) { + if (CollectionUtils.isEmpty(groupKeyPatterns)) { + return new HashSet<>(1); + } + Set matchedPatternList = new HashSet<>(); + for (String keyPattern : groupKeyPatterns) { + if (matchPattern(keyPattern, resourceName, group, namespace)) { + matchedPatternList.add(keyPattern); + } + } + return matchedPatternList; + } + + /** + * check if the resource match the groupKeyPattern. + * @param resourceName The dataId or service name to match. + * @param group The group to match. + * @param namespace The group to match. + * @param groupKeyPattern The pattern to match. + * @return matched or not. + */ + public static boolean matchPattern(String groupKeyPattern, String resourceName, String group, String namespace) { + if (StringUtils.isBlank(namespace)) { + namespace = DEFAULT_NAMESPACE_ID; + } + String[] splitPatterns = groupKeyPattern.split(FUZZY_WATCH_PATTERN_SPLITTER); + return splitPatterns[0].equals(namespace) && itemMatched(splitPatterns[1], group) && itemMatched( + splitPatterns[2], resourceName); + } + + public static String getNamespaceFromPattern(String groupKeyPattern) { + return groupKeyPattern.split(FUZZY_WATCH_PATTERN_SPLITTER)[0]; + } + + /** + * check pattern matched the resource. + * @param pattern pattern contain *. + * @param resource resource to check. + * @return + */ + private static boolean itemMatched(String pattern, String resource) { + + //accurate match without * + if (!pattern.contains(ALL_PATTERN)) { + return pattern.equals(resource); + } + + //match for '*' pattern + if (pattern.equals(ALL_PATTERN)) { + return true; + } + + //match for *{string}* + if (pattern.startsWith(ALL_PATTERN) && pattern.endsWith(ALL_PATTERN)) { + String pureString = pattern.replace(ALL_PATTERN, ""); + return resource.contains(pureString); + } + + //match for postfix match *{string} + if (pattern.startsWith(ALL_PATTERN)) { + String pureString = pattern.replace(ALL_PATTERN, ""); + return resource.endsWith(pureString); + } + + //match for prefix match {string}* + if (pattern.endsWith(ALL_PATTERN)) { + String pureString = pattern.replace(ALL_PATTERN, ""); + return resource.startsWith(pureString); + } + + return false; + } + + /** + * Calculates and merges the differences between the matched group keys and the client's existing group keys into a + * list of ConfigState objects. + * + * @param basedGroupKeys The matched group keys set + * @param followedGroupKeys The followed existing group keys set + * @return a different list of GroupKeyState objects representing the states which the followed sets should be added + * or removed GroupKeyState#exist true presents follow set should add,GroupKeyState#exist false presents follow set + * should removed. + */ + public static List diffGroupKeys(Set basedGroupKeys, Set followedGroupKeys) { + // Calculate the set of group keys to be added and removed + Set addGroupKeys = new HashSet<>(); + if (CollectionUtils.isNotEmpty(basedGroupKeys)) { + addGroupKeys.addAll(basedGroupKeys); + } + if (CollectionUtils.isNotEmpty(followedGroupKeys)) { + addGroupKeys.removeAll(followedGroupKeys); + } + + Set removeGroupKeys = new HashSet<>(); + if (CollectionUtils.isNotEmpty(followedGroupKeys)) { + removeGroupKeys.addAll(followedGroupKeys); + } + if (CollectionUtils.isNotEmpty(basedGroupKeys)) { + removeGroupKeys.removeAll(basedGroupKeys); + } + + // Convert the group keys to be added and removed into corresponding ConfigState objects and merge them into a list + return Stream.concat(addGroupKeys.stream().map(groupKey -> new GroupKeyState(groupKey, true)), + removeGroupKeys.stream().map(groupKey -> new GroupKeyState(groupKey, false))) + .collect(Collectors.toList()); + } + + public static class GroupKeyState { + + String groupKey; + + boolean exist; + + /** + * Constructs a new ConfigState instance with the given group key and existence flag. + * + * @param groupKey The group key associated with the configuration. + * @param exist {@code true} if the configuration exists, {@code false} otherwise. + */ + public GroupKeyState(String groupKey, boolean exist) { + this.groupKey = groupKey; + this.exist = exist; + } + + /** + * Retrieves the group key associated with the configuration. + * + * @return The group key. + */ + public String getGroupKey() { + return groupKey; + } + + /** + * Sets the group key associated with the configuration. + * + * @param groupKey The group key to set. + */ + public void setGroupKey(String groupKey) { + this.groupKey = groupKey; + } + + /** + * Checks whether the configuration exists or not. + * + * @return {@code true} if the configuration exists, {@code false} otherwise. + */ + public boolean isExist() { + return exist; + } + + /** + * Sets the existence flag of the configuration. + * + * @param exist {@code true} if the configuration exists, {@code false} otherwise. + */ + public void setExist(boolean exist) { + this.exist = exist; + } + } +} diff --git a/common/src/main/java/com/alibaba/nacos/common/utils/GroupKey.java b/common/src/main/java/com/alibaba/nacos/common/utils/GroupKey.java deleted file mode 100644 index c821876ed55..00000000000 --- a/common/src/main/java/com/alibaba/nacos/common/utils/GroupKey.java +++ /dev/null @@ -1,140 +0,0 @@ -/* - * 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.common.utils; - -/** - * Synthesize the form of dataId+groupId. Escapes reserved characters in dataId and groupId. - * - * @author Nacos - */ -public class GroupKey { - - private static final char PLUS = '+'; - - private static final char PERCENT = '%'; - - private static final char TWO = '2'; - - private static final char B = 'B'; - - private static final char FIVE = '5'; - - public static String getKey(String dataId, String group) { - return getKey(dataId, group, ""); - } - - public static String getKey(String dataId, String group, String datumStr) { - return doGetKey(dataId, group, datumStr); - } - - public static String getKeyTenant(String dataId, String group, String tenant) { - return doGetKey(dataId, group, tenant); - } - - private static String doGetKey(String dataId, String group, String datumStr) { - if (StringUtils.isBlank(dataId)) { - throw new IllegalArgumentException("invalid dataId"); - } - if (StringUtils.isBlank(group)) { - throw new IllegalArgumentException("invalid group"); - } - StringBuilder sb = new StringBuilder(); - urlEncode(dataId, sb); - sb.append(PLUS); - urlEncode(group, sb); - if (StringUtils.isNotEmpty(datumStr)) { - sb.append(PLUS); - urlEncode(datumStr, sb); - } - - return sb.toString(); - } - - /** - * Parse key. - * - * @param groupKey group key - * @return parsed key - */ - public static String[] parseKey(String groupKey) { - StringBuilder sb = new StringBuilder(); - String dataId = null; - String group = null; - String tenant = null; - - for (int i = 0; i < groupKey.length(); ++i) { - char c = groupKey.charAt(i); - if (PLUS == c) { - if (null == dataId) { - dataId = sb.toString(); - sb.setLength(0); - } else if (null == group) { - group = sb.toString(); - sb.setLength(0); - } else { - throw new IllegalArgumentException("invalid groupkey:" + groupKey); - } - } else if (PERCENT == c) { - char next = groupKey.charAt(++i); - char nextnext = groupKey.charAt(++i); - if (TWO == next && B == nextnext) { - sb.append(PLUS); - } else if (TWO == next && FIVE == nextnext) { - sb.append(PERCENT); - } else { - throw new IllegalArgumentException("invalid groupkey:" + groupKey); - } - } else { - sb.append(c); - } - } - - if (group == null) { - group = sb.toString(); - } else { - tenant = sb.toString(); - } - - if (StringUtils.isBlank(dataId)) { - throw new IllegalArgumentException("invalid dataId"); - } - if (StringUtils.isBlank(group)) { - throw new IllegalArgumentException("invalid group"); - } - if (StringUtils.isBlank(tenant)) { - return new String[] {dataId, group}; - } - return new String[] {dataId, group, tenant}; - } - - /** - * + -> %2B % -> %25. - */ - static void urlEncode(String str, StringBuilder sb) { - for (int idx = 0; idx < str.length(); ++idx) { - char c = str.charAt(idx); - if (PLUS == c) { - sb.append("%2B"); - } else if (PERCENT == c) { - sb.append("%25"); - } else { - sb.append(c); - } - } - } - -} diff --git a/common/src/main/java/com/alibaba/nacos/common/utils/GroupKeyPattern.java b/common/src/main/java/com/alibaba/nacos/common/utils/GroupKeyPattern.java deleted file mode 100644 index f00d50e7db2..00000000000 --- a/common/src/main/java/com/alibaba/nacos/common/utils/GroupKeyPattern.java +++ /dev/null @@ -1,259 +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.common.utils; - -import com.alibaba.nacos.api.common.Constants; - -import java.util.Collection; -import java.util.HashSet; -import java.util.Set; - -import static com.alibaba.nacos.api.common.Constants.DATA_ID_SPLITTER; -import static com.alibaba.nacos.api.common.Constants.FUZZY_LISTEN_PATTERN_WILDCARD; -import static com.alibaba.nacos.api.common.Constants.NAMESPACE_ID_SPLITTER; - -/** - * Utility class for matching group keys against a given pattern. - * - *

This class provides methods to match group keys based on a pattern specified. It supports matching based on - * dataId, group, and namespace components of the group key. - * - * @author stone-98 - * @date 2024/3/14 - */ -public class GroupKeyPattern { - - /** - * Generates a fuzzy listen group key pattern based on the given dataId pattern, group, and optional tenant. - * - *

This method generates a unique group key pattern for fuzzy listen based on the specified dataId pattern, - * group, and optional tenant. It concatenates the dataId pattern, group, and tenant (if provided) with a delimiter - * and returns the resulting string. The resulting string is interned to improve memory efficiency. - * - * @param dataIdPattern The pattern for matching dataIds. - * @param group The group associated with the dataIds. - * @param namespace (Optional) The tenant associated with the dataIds (can be null or empty). - * @return A unique group key pattern for fuzzy listen. - * @throws IllegalArgumentException If the dataId pattern or group is blank. - */ - public static String generateFuzzyListenGroupKeyPattern(final String dataIdPattern, final String group, - final String namespace) { - if (StringUtils.isBlank(dataIdPattern)) { - throw new IllegalArgumentException("Param 'dataIdPattern' is illegal, dataIdPattern is blank"); - } - if (StringUtils.isBlank(group)) { - throw new IllegalArgumentException("Param 'group' is illegal, group is blank"); - } - StringBuilder sb = new StringBuilder(); - if (StringUtils.isNotBlank(namespace)) { - sb.append(namespace); - } - sb.append(NAMESPACE_ID_SPLITTER); - sb.append(group); - sb.append(DATA_ID_SPLITTER); - sb.append(dataIdPattern); - return sb.toString().intern(); - } - - /** - * Generates a fuzzy listen group key pattern based on the given dataId pattern and group. - * - *

This method generates a unique group key pattern for fuzzy listen based on the specified dataId pattern and - * group. It concatenates the dataId pattern and group with a delimiter and returns the resulting string. The - * resulting string is interned to improve memory efficiency. - * - * @param dataIdPattern The pattern for matching dataIds. - * @param group The group associated with the dataIds. - * @return A unique group key pattern for fuzzy listen. - * @throws IllegalArgumentException If the dataId pattern or group is blank. - */ - public static String generateFuzzyListenGroupKeyPattern(final String dataIdPattern, final String group) { - if (StringUtils.isBlank(dataIdPattern)) { - throw new IllegalArgumentException("Param 'dataIdPattern' is illegal, dataIdPattern is blank"); - } - if (StringUtils.isBlank(group)) { - throw new IllegalArgumentException("Param 'group' is illegal, group is blank"); - } - final String fuzzyListenGroupKey = group + DATA_ID_SPLITTER + dataIdPattern; - return fuzzyListenGroupKey.intern(); - } - - /** - * Checks whether a group key matches the specified pattern. - * - * @param groupKey The group key to match. - * @param groupKeyPattern The pattern to match against. - * @return {@code true} if the group key matches the pattern, otherwise {@code false}. - */ - public static boolean isMatchPatternWithNamespace(String groupKey, String groupKeyPattern) { - String[] parseKey = GroupKey.parseKey(groupKey); - String dataId = parseKey[0]; - String group = parseKey[1]; - String namespace = parseKey.length > 2 ? parseKey[2] : Constants.DEFAULT_NAMESPACE_ID; - - String namespacePattern = getNamespace(groupKeyPattern); - String groupPattern = getGroup(groupKeyPattern); - String dataIdPattern = getDataIdPattern(groupKeyPattern); - - if (dataIdPattern.equals(FUZZY_LISTEN_PATTERN_WILDCARD)) { - return namespace.equals(namespacePattern) && group.equals(groupPattern); - } - - if (dataIdPattern.endsWith(FUZZY_LISTEN_PATTERN_WILDCARD)) { - String dataIdPrefix = dataIdPattern.substring(0, dataIdPattern.length() - 1); - return namespace.equals(namespacePattern) && groupPattern.equals(group) && dataId.startsWith(dataIdPrefix); - } - - return namespace.equals(namespacePattern) && group.equals(groupPattern) && dataId.equals(dataIdPattern); - } - - /** - * Checks whether a group key matches the specified pattern. - * - * @param groupKey The group key to match. - * @param groupKeyPattern The pattern to match against. - * @return {@code true} if the group key matches the pattern, otherwise {@code false}. - */ - public static boolean isMatchPatternWithoutNamespace(String groupKey, String groupKeyPattern) { - String[] parseKey = GroupKey.parseKey(groupKey); - String dataId = parseKey[0]; - String group = parseKey[1]; - - String groupPattern = getGroup(groupKeyPattern); - String dataIdPattern = getDataIdPattern(groupKeyPattern); - - if (dataIdPattern.equals(FUZZY_LISTEN_PATTERN_WILDCARD)) { - return group.equals(groupPattern); - } - - if (dataIdPattern.endsWith(FUZZY_LISTEN_PATTERN_WILDCARD)) { - String dataIdPrefix = dataIdPattern.substring(0, dataIdPattern.length() - 1); - return groupPattern.equals(group) && dataId.startsWith(dataIdPrefix); - } - - return group.equals(groupPattern) && dataId.equals(dataIdPattern); - } - - /** - * Given a dataId, group, dataId pattern, and group pattern, determines whether it can match. - * - * @param dataId The dataId to match. - * @param group The group to match. - * @param dataIdPattern The dataId pattern to match against. - * @param groupPattern The group pattern to match against. - * @return {@code true} if the dataId and group match the patterns, otherwise {@code false}. - */ - public static boolean isMatchPatternWithoutNamespace(String dataId, String group, String dataIdPattern, - String groupPattern) { - String groupKey = GroupKey.getKey(dataId, group); - String groupKeyPattern = generateFuzzyListenGroupKeyPattern(dataIdPattern, groupPattern); - return isMatchPatternWithoutNamespace(groupKey, groupKeyPattern); - } - - /** - * Given a dataId, group, and a collection of completed group key patterns, returns the patterns that match. - * - * @param dataId The dataId to match. - * @param group The group to match. - * @param groupKeyPatterns The collection of completed group key patterns to match against. - * @return A set of patterns that match the dataId and group. - */ - public static Set getConfigMatchedPatternsWithoutNamespace(String dataId, String group, - Collection groupKeyPatterns) { - if (CollectionUtils.isEmpty(groupKeyPatterns)) { - return new HashSet<>(1); - } - Set matchedPatternList = new HashSet<>(); - for (String keyPattern : groupKeyPatterns) { - if (isMatchPatternWithoutNamespace(dataId, group, getDataIdPattern(keyPattern), getGroup(keyPattern))) { - matchedPatternList.add(keyPattern); - } - } - return matchedPatternList; - } - - /** - * Extracts the namespace from the given group key pattern. - * - * @param groupKeyPattern The group key pattern from which to extract the namespace. - * @return The namespace extracted from the group key pattern. - */ - public static String getNamespace(final String groupKeyPattern) { - if (StringUtils.isBlank(groupKeyPattern)) { - return StringUtils.EMPTY; - } - if (!groupKeyPattern.contains(NAMESPACE_ID_SPLITTER)) { - return StringUtils.EMPTY; - } - return groupKeyPattern.split(NAMESPACE_ID_SPLITTER)[0]; - } - - /** - * Extracts the group from the given group key pattern. - * - * @param groupKeyPattern The group key pattern from which to extract the group. - * @return The group extracted from the group key pattern. - */ - public static String getGroup(final String groupKeyPattern) { - if (StringUtils.isBlank(groupKeyPattern)) { - return StringUtils.EMPTY; - } - String groupWithNamespace; - if (!groupKeyPattern.contains(DATA_ID_SPLITTER)) { - groupWithNamespace = groupKeyPattern; - } else { - groupWithNamespace = groupKeyPattern.split(DATA_ID_SPLITTER)[0]; - } - - if (!groupKeyPattern.contains(NAMESPACE_ID_SPLITTER)) { - return groupWithNamespace; - } - return groupWithNamespace.split(NAMESPACE_ID_SPLITTER)[1]; - } - - /** - * Extracts the dataId pattern from the given group key pattern. - * - * @param groupKeyPattern The group key pattern from which to extract the dataId pattern. - * @return The dataId pattern extracted from the group key pattern. - */ - public static String getDataIdPattern(final String groupKeyPattern) { - if (StringUtils.isBlank(groupKeyPattern)) { - return StringUtils.EMPTY; - } - if (!groupKeyPattern.contains(DATA_ID_SPLITTER)) { - return StringUtils.EMPTY; - } - return groupKeyPattern.split(DATA_ID_SPLITTER)[1]; - } - - /** - * Given a completed pattern, removes the namespace. - * - * @param completedPattern The completed pattern from which to remove the namespace. - * @return The pattern with the namespace removed. - */ - public static String getPatternRemovedNamespace(String completedPattern) { - if (StringUtils.isBlank(completedPattern)) { - return StringUtils.EMPTY; - } - if (!completedPattern.contains(NAMESPACE_ID_SPLITTER)) { - return completedPattern; - } - return completedPattern.split(NAMESPACE_ID_SPLITTER)[1]; - } -} diff --git a/common/src/test/java/com/alibaba/nacos/common/utils/FuzzyGroupKeyPatternTest.java b/common/src/test/java/com/alibaba/nacos/common/utils/FuzzyGroupKeyPatternTest.java new file mode 100644 index 00000000000..374d429eac0 --- /dev/null +++ b/common/src/test/java/com/alibaba/nacos/common/utils/FuzzyGroupKeyPatternTest.java @@ -0,0 +1,43 @@ +/* + * 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.common.utils; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * GroupKeyPatternUtilsTest. + * + * @author stone-98 + * @date 2024/3/19 + */ +public class FuzzyGroupKeyPatternTest { + + @Test + public void testGetGroupKeyPattern() { + String dataIdPattern = "examplePattern*"; + String group = "exampleGroup"; + String namespace = "exampleNamespace"; + + String groupKeyPattern = FuzzyGroupKeyPattern.generatePattern(dataIdPattern, group, namespace); + + assertEquals("exampleNamespace>>exampleGroup@@examplePattern*", groupKeyPattern); + } + +} + diff --git a/common/src/test/java/com/alibaba/nacos/common/utils/GroupKeyPatternTest.java b/common/src/test/java/com/alibaba/nacos/common/utils/GroupKeyPatternTest.java deleted file mode 100644 index a16e222ecbc..00000000000 --- a/common/src/test/java/com/alibaba/nacos/common/utils/GroupKeyPatternTest.java +++ /dev/null @@ -1,136 +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.common.utils; - -import org.junit.Assert; -import org.junit.Test; - -import java.util.HashSet; -import java.util.Set; - -/** - * GroupKeyPatternUtilsTest. - * - * @author stone-98 - * @date 2024/3/19 - */ -public class GroupKeyPatternTest { - - @Test - public void testGetGroupKeyPatternWithNamespace() { - String dataIdPattern = "examplePattern*"; - String group = "exampleGroup"; - String namespace = "exampleNamespace"; - - String groupKeyPattern = GroupKeyPattern.generateFuzzyListenGroupKeyPattern(dataIdPattern, group, namespace); - - Assert.assertEquals("exampleNamespace>>exampleGroup@@examplePattern*", groupKeyPattern); - } - - @Test - public void testGetGroupKeyPatternWithoutNamespace() { - String dataIdPattern = "examplePattern*"; - String group = "exampleGroup"; - - String groupKeyPattern = GroupKeyPattern.generateFuzzyListenGroupKeyPattern(dataIdPattern, group); - - Assert.assertEquals("exampleGroup@@examplePattern*", groupKeyPattern); - } - - @Test - public void testIsMatchPatternWithNamespace() { - String groupKey = "examplePattern+exampleGroup+exampleNamespace"; - String groupKeyPattern = "exampleNamespace>>exampleGroup@@examplePattern*"; - - boolean result = GroupKeyPattern.isMatchPatternWithNamespace(groupKey, groupKeyPattern); - - Assert.assertTrue(result); - } - - @Test - public void testIsMatchPatternWithoutNamespace() { - String groupKey = "examplePattern+exampleGroup+exampleNamespace"; - String groupKeyPattern = "exampleNamespace>>exampleGroup@@*"; - - boolean result = GroupKeyPattern.isMatchPatternWithoutNamespace(groupKey, groupKeyPattern); - - Assert.assertTrue(result); - } - - @Test - public void testIsMatchPatternWithoutNamespaceWithDataIdPrefix() { - String groupKey = "examplePattern+exampleGroup+exampleNamespace"; - String groupKeyPattern = "exampleNamespace>>exampleGroup@@examplePattern*"; - - boolean result = GroupKeyPattern.isMatchPatternWithoutNamespace(groupKey, groupKeyPattern); - - Assert.assertTrue(result); - } - - @Test - public void testGetConfigMatchedPatternsWithoutNamespace() { - String dataId = "exampleDataId"; - String group = "exampleGroup"; - Set groupKeyPatterns = new HashSet<>(); - groupKeyPatterns.add("exampleGroup@@exampleDataId*"); - groupKeyPatterns.add("exampleGroup@@exampleDataI*"); - - Set matchedPatterns = GroupKeyPattern.getConfigMatchedPatternsWithoutNamespace(dataId, group, - groupKeyPatterns); - - Assert.assertEquals(2, matchedPatterns.size()); - Assert.assertTrue(matchedPatterns.contains("exampleGroup@@exampleDataId*")); - Assert.assertTrue(matchedPatterns.contains("exampleGroup@@exampleDataI*")); - } - - @Test - public void testGetNamespace() { - String groupKeyPattern = "exampleNamespace>>exampleGroup@@examplePattern"; - - String namespace = GroupKeyPattern.getNamespace(groupKeyPattern); - - Assert.assertEquals("exampleNamespace", namespace); - } - - @Test - public void testGetGroup() { - String groupKeyPattern = "exampleNamespace>>exampleGroup@@examplePattern"; - - String group = GroupKeyPattern.getGroup(groupKeyPattern); - - Assert.assertEquals("exampleGroup", group); - } - - @Test - public void testGetDataIdPattern() { - String groupKeyPattern = "exampleNamespace>>exampleGroup@@examplePattern"; - - String dataIdPattern = GroupKeyPattern.getDataIdPattern(groupKeyPattern); - - Assert.assertEquals("examplePattern", dataIdPattern); - } - - @Test - public void testGetPatternRemovedNamespace() { - String groupKeyPattern = "exampleNamespace>>exampleGroup@@examplePattern"; - - String patternRemovedNamespace = GroupKeyPattern.getPatternRemovedNamespace(groupKeyPattern); - - Assert.assertEquals("exampleGroup@@examplePattern", patternRemovedNamespace); - } -} - diff --git a/config/pom.xml b/config/pom.xml index c10e7df3796..521f7d1b515 100644 --- a/config/pom.xml +++ b/config/pom.xml @@ -134,6 +134,10 @@ hamcrest test + + com.alibaba.nacos + nacos-control-plugin + 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 722e5818493..b6af4a6f805 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 @@ -35,7 +35,8 @@ public class ConfigCommonConfig extends AbstractDynamicConfig { private long pushTimeout = 3000L; - private int batchSize = 10; + private int batchSize = 20; + private boolean derbyOpsEnabled = false; private ConfigCommonConfig() { @@ -69,6 +70,8 @@ public int getBatchSize() { public void setBatchSize(int batchSize) { this.batchSize = batchSize; + } + public boolean isDerbyOpsEnabled() { return derbyOpsEnabled; } @@ -81,7 +84,7 @@ public void setDerbyOpsEnabled(boolean derbyOpsEnabled) { protected void getConfigFromEnv() { maxPushRetryTimes = EnvUtil.getProperty("nacos.config.push.maxRetryTime", Integer.class, 50); pushTimeout = EnvUtil.getProperty("nacos.config.push.timeout", Long.class, 3000L); - pushTimeout = EnvUtil.getProperty("nacos.config.push.batchSize", Integer.class, 10); + batchSize = EnvUtil.getProperty("nacos.config.push.batchSize", Integer.class, 20); derbyOpsEnabled = EnvUtil.getProperty("nacos.config.derby.ops.enabled", Boolean.class, false); } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/model/CacheItem.java b/config/src/main/java/com/alibaba/nacos/config/server/model/CacheItem.java index 836713a3be4..e0423f3daa7 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/model/CacheItem.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/model/CacheItem.java @@ -16,8 +16,6 @@ package com.alibaba.nacos.config.server.model; -import com.alibaba.nacos.common.utils.CollectionUtils; -import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.config.server.utils.SimpleReadWriteLock; import com.alibaba.nacos.core.utils.StringPool; @@ -129,19 +127,5 @@ public void clearConfigGrays() { this.configCacheGray = null; this.sortedConfigCacheGrayList = null; } - - /** - * Checks if the configuration is effective for the specified client IP and tag. - * - * @param tag The tag associated with the configuration. - * @param clientIp The IP address of the client. - * @return true if the configuration is effective for the client, false otherwise. - */ - public boolean effectiveForClient(String tag, String clientIp) { - if (isBeta && CollectionUtils.isNotEmpty(ips4Beta) && !ips4Beta.contains(clientIp)) { - return false; - } - return StringUtils.isBlank(tag) || (getConfigCacheTags() != null && getConfigCacheTags().containsKey(tag)); - } } 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 new file mode 100644 index 00000000000..77fbace8cc4 --- /dev/null +++ b/config/src/main/java/com/alibaba/nacos/config/server/model/event/ConfigCancelFuzzyWatchEvent.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.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/model/event/ConfigBatchFuzzyListenEvent.java b/config/src/main/java/com/alibaba/nacos/config/server/model/event/ConfigFuzzyWatchEvent.java similarity index 79% rename from config/src/main/java/com/alibaba/nacos/config/server/model/event/ConfigBatchFuzzyListenEvent.java rename to config/src/main/java/com/alibaba/nacos/config/server/model/event/ConfigFuzzyWatchEvent.java index 2e44763ee9e..c6a75210c50 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/model/event/ConfigBatchFuzzyListenEvent.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/model/event/ConfigFuzzyWatchEvent.java @@ -28,19 +28,19 @@ * @author stone-98 * @date 2024/3/5 */ -public class ConfigBatchFuzzyListenEvent extends Event { +public class ConfigFuzzyWatchEvent extends Event { private static final long serialVersionUID = 1953965691384930209L; /** * ID of the client making the request. */ - private String clientId; + private String connectionId; /** * Pattern for matching group keys. */ - private String keyGroupPattern; + private String groupKeyPattern; /** * Set of existing group keys associated with the client. @@ -55,16 +55,16 @@ public class ConfigBatchFuzzyListenEvent extends Event { /** * Constructs a new ConfigBatchFuzzyListenEvent with the specified parameters. * - * @param clientId ID of the client making the request + * @param connectionId ID of the client making the request * @param clientExistingGroupKeys Set of existing group keys associated with the client - * @param keyGroupPattern Pattern for matching group keys + * @param groupKeyPattern Pattern for matching group keys * @param isInitializing Flag indicating whether the client is initializing */ - public ConfigBatchFuzzyListenEvent(String clientId, Set clientExistingGroupKeys, String keyGroupPattern, + public ConfigFuzzyWatchEvent(String connectionId, Set clientExistingGroupKeys, String groupKeyPattern, boolean isInitializing) { - this.clientId = clientId; + this.connectionId = connectionId; this.clientExistingGroupKeys = clientExistingGroupKeys; - this.keyGroupPattern = keyGroupPattern; + this.groupKeyPattern = groupKeyPattern; this.isInitializing = isInitializing; } @@ -73,17 +73,17 @@ public ConfigBatchFuzzyListenEvent(String clientId, Set clientExistingGr * * @return The client ID */ - public String getClientId() { - return clientId; + public String getConnectionId() { + return connectionId; } /** * Set the ID of the client making the request. * - * @param clientId The client ID to be set + * @param connectionId The client ID to be set */ - public void setClientId(String clientId) { - this.clientId = clientId; + public void setConnectionId(String connectionId) { + this.connectionId = connectionId; } /** @@ -91,17 +91,17 @@ public void setClientId(String clientId) { * * @return The key group pattern */ - public String getKeyGroupPattern() { - return keyGroupPattern; + public String getGroupKeyPattern() { + return groupKeyPattern; } /** * Set the pattern for matching group keys. * - * @param keyGroupPattern The key group pattern to be set + * @param groupKeyPattern The key group pattern to be set */ - public void setKeyGroupPattern(String keyGroupPattern) { - this.keyGroupPattern = keyGroupPattern; + public void setGroupKeyPattern(String groupKeyPattern) { + this.groupKeyPattern = groupKeyPattern; } /** diff --git a/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigBatchFuzzyListenRequestHandler.java b/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigBatchFuzzyListenRequestHandler.java deleted file mode 100644 index 0a344ba2123..00000000000 --- a/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigBatchFuzzyListenRequestHandler.java +++ /dev/null @@ -1,107 +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.remote; - -import com.alibaba.nacos.api.config.remote.request.ConfigBatchFuzzyListenRequest; -import com.alibaba.nacos.api.config.remote.response.ConfigBatchFuzzyListenResponse; -import com.alibaba.nacos.api.exception.NacosException; -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.common.utils.CollectionUtils; -import com.alibaba.nacos.common.utils.GroupKeyPattern; -import com.alibaba.nacos.config.server.model.event.ConfigBatchFuzzyListenEvent; -import com.alibaba.nacos.config.server.utils.GroupKey; -import com.alibaba.nacos.core.control.TpsControl; -import com.alibaba.nacos.core.paramcheck.ExtractorManager; -import com.alibaba.nacos.core.paramcheck.impl.ConfigBatchFuzzyListenRequestParamsExtractor; -import com.alibaba.nacos.core.remote.RequestHandler; -import com.alibaba.nacos.core.utils.StringPool; -import com.alibaba.nacos.plugin.auth.constant.ActionTypes; -import com.alibaba.nacos.plugin.auth.constant.SignType; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - -import java.util.Set; -import java.util.stream.Collectors; - -/** - * Handler for processing batch fuzzy listen requests. - *

- * This handler is responsible for processing batch fuzzy listen requests sent by clients. It adds or removes clients - * from the fuzzy listening context based on the request, and publishes corresponding events to notify interested - * parties. - *

- * - * @author stone-98 - * @date 2024/3/4 - */ -@Component -public class ConfigBatchFuzzyListenRequestHandler - extends RequestHandler { - - /** - * Context for managing fuzzy listen changes. - */ - @Autowired - private ConfigChangeListenContext configChangeListenContext; - - /** - * Handles the batch fuzzy listen request. - *

- * This method processes the batch fuzzy listen request by adding or removing clients from the fuzzy listening - * context based on the request, and publishes corresponding events to notify interested parties. - *

- * - * @param request The batch fuzzy listen request - * @param meta Request meta information - * @return The response to the batch fuzzy listen request - * @throws NacosException If an error occurs while processing the request - */ - @Override - @TpsControl(pointName = "ConfigFuzzyListen") - @Secured(action = ActionTypes.READ, signType = SignType.CONFIG) - @ExtractorManager.Extractor(rpcExtractor = ConfigBatchFuzzyListenRequestParamsExtractor.class) - public ConfigBatchFuzzyListenResponse handle(ConfigBatchFuzzyListenRequest request, RequestMeta meta) - throws NacosException { - String connectionId = StringPool.get(meta.getConnectionId()); - for (ConfigBatchFuzzyListenRequest.Context context : request.getContexts()) { - String groupKeyPattern = GroupKeyPattern.generateFuzzyListenGroupKeyPattern(context.getDataIdPattern(), - context.getGroup(), context.getTenant()); - groupKeyPattern = StringPool.get(groupKeyPattern); - if (context.isListen()) { - // Add client to the fuzzy listening context - configChangeListenContext.addFuzzyListen(groupKeyPattern, connectionId); - // Get existing group keys for the client and publish initialization event - Set clientExistingGroupKeys = null; - if (CollectionUtils.isNotEmpty(context.getDataIds())) { - clientExistingGroupKeys = context.getDataIds().stream() - .map(dataId -> GroupKey.getKeyTenant(dataId, context.getGroup(), context.getTenant())) - .collect(Collectors.toSet()); - } - NotifyCenter.publishEvent( - new ConfigBatchFuzzyListenEvent(connectionId, clientExistingGroupKeys, groupKeyPattern, - context.isInitializing())); - } else { - // Remove client from the fuzzy listening context - configChangeListenContext.removeFuzzyListen(groupKeyPattern, connectionId); - } - } - // Return response - return new ConfigBatchFuzzyListenResponse(); - } -} diff --git a/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigChangeListenContext.java b/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigChangeListenContext.java index c71ef288f72..adf7453558a 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigChangeListenContext.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigChangeListenContext.java @@ -17,7 +17,6 @@ package com.alibaba.nacos.config.server.remote; import com.alibaba.nacos.common.utils.CollectionUtils; -import com.alibaba.nacos.common.utils.GroupKeyPattern; import org.springframework.stereotype.Component; import java.util.Collection; @@ -37,100 +36,18 @@ @Component public class ConfigChangeListenContext { - /** - * groupKeyPattern -> connection set. - */ - private final Map> keyPatternContext = new ConcurrentHashMap<>(); - /** * groupKey-> connection set. */ - private final ConcurrentHashMap> groupKeyContext = new ConcurrentHashMap<>(); + private ConcurrentHashMap> groupKeyContext = new ConcurrentHashMap<>(); /** * connectionId-> group key set. */ - private final ConcurrentHashMap> connectionIdContext = new ConcurrentHashMap<>(); + private ConcurrentHashMap> connectionIdContext = new ConcurrentHashMap<>(); /** - * Adds a fuzzy listen connection ID associated with the specified group key pattern. If the key pattern does not - * exist in the context, a new entry will be created. If the key pattern already exists, the connection ID will be - * added to the existing set. - * - * @param groupKeyPattern The group key pattern to associate with the listen connection. - * @param connectId The connection ID to be added. - */ - public synchronized void addFuzzyListen(String groupKeyPattern, String connectId) { - // Add the connection ID to the set associated with the key pattern in keyPatternContext - keyPatternContext.computeIfAbsent(groupKeyPattern, k -> new HashSet<>()).add(connectId); - } - - /** - * Removes a fuzzy listen connection ID associated with the specified group key pattern. If the group key pattern - * exists in the context and the connection ID is found in the associated set, the connection ID will be removed - * from the set. If the set becomes empty after removal, the entry for the group key pattern will be removed from - * the context. - * - * @param groupKeyPattern The group key pattern associated with the listen connection to be removed. - * @param connectionId The connection ID to be removed. - */ - public synchronized void removeFuzzyListen(String groupKeyPattern, String connectionId) { - // Retrieve the set of connection IDs associated with the group key pattern - Set connectIds = keyPatternContext.get(groupKeyPattern); - if (CollectionUtils.isNotEmpty(connectIds)) { - // Remove the connection ID from the set if it exists - connectIds.remove(connectionId); - // Remove the entry for the group key pattern if the set becomes empty after removal - if (connectIds.isEmpty()) { - keyPatternContext.remove(groupKeyPattern); - } - } - } - - /** - * Retrieves the set of fuzzy listen connection IDs associated with the specified group key pattern. - * - * @param groupKeyPattern The group key pattern to retrieve the associated connection IDs. - * @return The set of connection IDs associated with the group key pattern, or null if no connections are found. - */ - public synchronized Set getFuzzyListeners(String groupKeyPattern) { - // Retrieve the set of connection IDs associated with the group key pattern - Set connectionIds = keyPatternContext.get(groupKeyPattern); - // If the set is not empty, create a new set and safely copy the connection IDs into it - if (CollectionUtils.isNotEmpty(connectionIds)) { - Set listenConnections = new HashSet<>(); - safeCopy(connectionIds, listenConnections); - return listenConnections; - } - // Return null if no connections are found for the specified group key pattern - return null; - } - - /** - * Retrieves the set of connection IDs matched with the specified group key. - * - * @param groupKey The group key to match with the key patterns. - * @return The set of connection IDs matched with the group key. - */ - public Set getConnectIdMatchedPatterns(String groupKey) { - // Initialize a set to store the matched connection IDs - Set connectIds = new HashSet<>(); - // Iterate over each key pattern in the context - for (String keyPattern : keyPatternContext.keySet()) { - // Check if the group key matches the current key pattern - if (GroupKeyPattern.isMatchPatternWithNamespace(groupKey, keyPattern)) { - // If matched, add the associated connection IDs to the set - Set connectIdSet = keyPatternContext.get(keyPattern); - if (CollectionUtils.isNotEmpty(connectIdSet)) { - connectIds.addAll(connectIdSet); - } - } - } - return connectIds; - } - - /** - * Add listen. + * add listen. * * @param groupKey groupKey. * @param connectionId connectionId. @@ -210,7 +127,7 @@ public synchronized void clearContextForConnectionId(final String connectionId) return; } for (Map.Entry groupKey : listenKeys.entrySet()) { - + Set connectionIds = groupKeyContext.get(groupKey.getKey()); if (CollectionUtils.isNotEmpty(connectionIds)) { connectionIds.remove(connectionId); @@ -220,23 +137,9 @@ public synchronized void clearContextForConnectionId(final String connectionId) } else { groupKeyContext.remove(groupKey.getKey()); } - + } connectionIdContext.remove(connectionId); - - // Remove any remaining fuzzy listen connections - for (Map.Entry> keyPatternContextEntry : keyPatternContext.entrySet()) { - String keyPattern = keyPatternContextEntry.getKey(); - Set connectionIds = keyPatternContextEntry.getValue(); - if (CollectionUtils.isEmpty(connectionIds)) { - keyPatternContext.remove(keyPattern); - } else { - connectionIds.remove(keyPattern); - if (CollectionUtils.isEmpty(connectionIds)) { - keyPatternContext.remove(keyPattern); - } - } - } } /** diff --git a/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigConnectionEventListener.java b/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigConnectionEventListener.java index bd9ce128f0b..a007b1c463b 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigConnectionEventListener.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigConnectionEventListener.java @@ -16,6 +16,7 @@ package com.alibaba.nacos.config.server.remote; +import com.alibaba.nacos.config.server.service.ConfigFuzzyWatchContextService; import com.alibaba.nacos.core.remote.ClientConnectionEventListener; import com.alibaba.nacos.core.remote.Connection; import com.alibaba.nacos.core.utils.Loggers; @@ -32,8 +33,12 @@ public class ConfigConnectionEventListener extends ClientConnectionEventListener final ConfigChangeListenContext configChangeListenContext; - public ConfigConnectionEventListener(ConfigChangeListenContext configChangeListenContext) { + final ConfigFuzzyWatchContextService configFuzzyWatchContextService; + + public ConfigConnectionEventListener(ConfigChangeListenContext configChangeListenContext, + ConfigFuzzyWatchContextService configFuzzyWatchContextService) { this.configChangeListenContext = configChangeListenContext; + this.configFuzzyWatchContextService = configFuzzyWatchContextService; } @Override @@ -46,6 +51,7 @@ public void clientDisConnected(Connection connect) { String connectionId = connect.getMetaInfo().getConnectionId(); Loggers.REMOTE_DIGEST.info("[{}]client disconnected,clear config listen context", connectionId); configChangeListenContext.clearContextForConnectionId(connectionId); + configFuzzyWatchContextService.clearFuzzyWatchContext(connectionId); } } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/remote/RpcFuzzyListenConfigChangeNotifier.java b/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigFuzzyWatchChangeNotifier.java similarity index 63% rename from config/src/main/java/com/alibaba/nacos/config/server/remote/RpcFuzzyListenConfigChangeNotifier.java rename to config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigFuzzyWatchChangeNotifier.java index 3724a0eaf69..7478e7616c5 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/remote/RpcFuzzyListenConfigChangeNotifier.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigFuzzyWatchChangeNotifier.java @@ -16,7 +16,7 @@ package com.alibaba.nacos.config.server.remote; -import com.alibaba.nacos.api.config.remote.request.FuzzyListenNotifyChangeRequest; +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; @@ -24,8 +24,8 @@ import com.alibaba.nacos.config.server.configuration.ConfigCommonConfig; 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.Connection; import com.alibaba.nacos.core.remote.ConnectionManager; import com.alibaba.nacos.core.remote.ConnectionMeta; @@ -38,6 +38,9 @@ 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; + /** * Notify remote clients about fuzzy listen configuration changes. Use subscriber mode to monitor local data changes, * and push notifications to remote clients accordingly. @@ -45,16 +48,14 @@ * @author stone-98 * @date 2024/3/18 */ -@Component(value = "rpcFuzzyListenConfigChangeNotifier") -public class RpcFuzzyListenConfigChangeNotifier extends Subscriber { - - private static final String POINT_FUZZY_LISTEN_CONFIG_PUSH = "POINT_FUZZY_LISTEN_CONFIG_PUSH"; +@Component(value = "fuzzyWatchConfigChangeNotifier") +public class ConfigFuzzyWatchChangeNotifier extends Subscriber { - private static final String POINT_FUZZY_LISTEN_CONFIG_PUSH_SUCCESS = "POINT_FUZZY_LISTEN_CONFIG_PUSH_SUCCESS"; + private static final String POINT_FUZZY_WATCH_CONFIG_PUSH = "POINT_FUZZY_WATCH_CONFIG_PUSH"; - private static final String POINT_FUZZY_LISTEN_CONFIG_PUSH_FAIL = "POINT_FUZZY_LISTEN_CONFIG_PUSH_FAIL"; + private static final String POINT_FUZZY_WATCH_CONFIG_PUSH_SUCCESS = "POINT_FUZZY_WATCH_CONFIG_PUSH_SUCCESS"; - private final ConfigChangeListenContext configChangeListenContext; + private static final String POINT_FUZZY_WATCH_CONFIG_PUSH_FAIL = "POINT_FUZZY_WATCH_CONFIG_PUSH_FAIL"; private final ConnectionManager connectionManager; @@ -62,48 +63,51 @@ public class RpcFuzzyListenConfigChangeNotifier extends Subscriber 2 ? parseKey[2] : ""; - for (String clientId : configChangeListenContext.getConnectIdMatchedPatterns(groupKey)) { - Connection connection = connectionManager.getConnection(clientId); - if (null == connection) { - Loggers.REMOTE_PUSH.warn( - "clientId not found, Config change notification not sent. clientId={},keyGroupPattern={}", - clientId, event.groupKey); - continue; + boolean exists = ConfigCacheService.getContentCache(event.groupKey) != null; + //can not recognize add or update, set config_changed here + String changedType = exists ? CONFIG_CHANGED : DELETE_CONFIG; + boolean needNotify = configFuzzyWatchContextService.syncGroupKeyContext(event.groupKey, changedType); + if (needNotify) { + for (String clientId : configFuzzyWatchContextService.getMatchedClients(event.groupKey)) { + Connection connection = connectionManager.getConnection(clientId); + if (null == connection) { + Loggers.REMOTE_PUSH.warn( + "clientId not found, Config change notification not sent. clientId={},keyGroupPattern={}", + clientId, event.groupKey); + continue; + } + ConnectionMeta metaInfo = connection.getMetaInfo(); + String clientIp = metaInfo.getClientIp(); + String appName = metaInfo.getAppName(); + + ConfigFuzzyWatchChangeNotifyRequest request = new ConfigFuzzyWatchChangeNotifyRequest(event.groupKey, + changedType); + int maxPushRetryTimes = ConfigCommonConfig.getInstance().getMaxPushRetryTimes(); + RpcPushTask rpcPushTask = new RpcPushTask(request, maxPushRetryTimes, clientId, clientIp, appName); + push(rpcPushTask); } - ConnectionMeta metaInfo = connection.getMetaInfo(); - String clientIp = metaInfo.getClientIp(); - String clientTag = metaInfo.getTag(); - String appName = metaInfo.getAppName(); - boolean exists = ConfigCacheService.containsAndEffectiveForClient(groupKey, clientIp, clientTag); - FuzzyListenNotifyChangeRequest request = new FuzzyListenNotifyChangeRequest(tenant, group, dataId, exists); - int maxPushRetryTimes = ConfigCommonConfig.getInstance().getMaxPushRetryTimes(); - RpcPushTask rpcPushTask = new RpcPushTask(request, maxPushRetryTimes, clientId, clientIp, appName); - push(rpcPushTask); } + } @Override @@ -117,12 +121,11 @@ public Class subscribeType() { * @param retryTask The task for retrying to push notification. */ private void push(RpcPushTask retryTask) { - FuzzyListenNotifyChangeRequest notifyRequest = retryTask.notifyRequest; + ConfigFuzzyWatchChangeNotifyRequest notifyRequest = retryTask.notifyRequest; if (retryTask.isOverTimes()) { Loggers.REMOTE_PUSH.warn( - "push callback retry fail over times. dataId={},group={},tenant={},clientId={}, will unregister client.", - notifyRequest.getDataId(), notifyRequest.getGroup(), notifyRequest.getTenant(), - retryTask.connectionId); + "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 @@ -131,9 +134,8 @@ private void push(RpcPushTask retryTask) { } else { // Client is already offline, ignore the task. Loggers.REMOTE_PUSH.warn( - "Client is already offline, ignore the task. dataId={},group={},tenant={},clientId={}", - notifyRequest.getDataId(), notifyRequest.getGroup(), notifyRequest.getTenant(), - retryTask.connectionId); + "Client is already offline, ignore the task. dataId={},groupKey={},tenant={},clientId={}", + notifyRequest.getGroupKey(), retryTask.connectionId); } } @@ -142,7 +144,7 @@ private void push(RpcPushTask retryTask) { */ class RpcPushTask implements Runnable { - FuzzyListenNotifyChangeRequest notifyRequest; + ConfigFuzzyWatchChangeNotifyRequest notifyRequest; int maxRetryTimes; @@ -163,7 +165,7 @@ class RpcPushTask implements Runnable { * @param clientIp The IP address of the client. * @param appName The name of the application. */ - public RpcPushTask(FuzzyListenNotifyChangeRequest notifyRequest, int maxRetryTimes, String connectionId, + public RpcPushTask(ConfigFuzzyWatchChangeNotifyRequest notifyRequest, int maxRetryTimes, String connectionId, String clientIp, String appName) { this.notifyRequest = notifyRequest; this.maxRetryTimes = maxRetryTimes; @@ -185,7 +187,7 @@ public boolean isOverTimes() { public void run() { tryTimes++; TpsCheckRequest tpsCheckRequest = new TpsCheckRequest(); - tpsCheckRequest.setPointName(POINT_FUZZY_LISTEN_CONFIG_PUSH); + tpsCheckRequest.setPointName(POINT_FUZZY_WATCH_CONFIG_PUSH); if (!tpsControlManager.check(tpsCheckRequest).isSuccess()) { push(this); } else { @@ -194,17 +196,16 @@ public void run() { @Override public void onSuccess() { TpsCheckRequest tpsCheckRequest = new TpsCheckRequest(); - tpsCheckRequest.setPointName(POINT_FUZZY_LISTEN_CONFIG_PUSH_SUCCESS); + 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_LISTEN_CONFIG_PUSH_FAIL); + tpsCheckRequest.setPointName(POINT_FUZZY_WATCH_CONFIG_PUSH_FAIL); tpsControlManager.check(tpsCheckRequest); - Loggers.REMOTE_PUSH.warn("Push fail, dataId={}, group={}, tenant={}, clientId={}", - notifyRequest.getDataId(), notifyRequest.getGroup(), notifyRequest.getTenant(), + Loggers.REMOTE_PUSH.warn("Push fail, groupKey={}, clientId={}", notifyRequest.getGroupKey(), connectionId, e); push(RpcPushTask.this); } 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 new file mode 100644 index 00000000000..5426b5accb2 --- /dev/null +++ b/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigFuzzyWatchRequestHandler.java @@ -0,0 +1,95 @@ +/* + * 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.ConfigFuzzyWatchRequest; +import com.alibaba.nacos.api.config.remote.response.ConfigFuzzyWatchResponse; +import com.alibaba.nacos.api.exception.NacosException; +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; +import com.alibaba.nacos.core.paramcheck.ExtractorManager; +import com.alibaba.nacos.core.paramcheck.impl.ConfigFuzzyWatchRequestParamsExtractor; +import com.alibaba.nacos.core.remote.RequestHandler; +import com.alibaba.nacos.core.utils.StringPool; +import com.alibaba.nacos.plugin.auth.constant.ActionTypes; +import com.alibaba.nacos.plugin.auth.constant.SignType; +import org.springframework.stereotype.Component; + +import java.util.Set; + +import static com.alibaba.nacos.api.common.Constants.WATCH_TYPE_CANCEL_WATCH; +import static com.alibaba.nacos.api.common.Constants.WATCH_TYPE_WATCH; + +/** + * Handler for processing batch fuzzy listen requests. + *

+ * This handler is responsible for processing batch fuzzy listen requests sent by clients. It adds or removes clients + * from the fuzzy listening context based on the request, and publishes corresponding events to notify interested + * parties. + *

+ * + * @author stone-98 + * @date 2024/3/4 + */ +@Component +public class ConfigFuzzyWatchRequestHandler extends RequestHandler { + + private ConfigFuzzyWatchContextService configFuzzyWatchContextService; + + public ConfigFuzzyWatchRequestHandler(ConfigFuzzyWatchContextService configFuzzyWatchContextService) { + this.configFuzzyWatchContextService = configFuzzyWatchContextService; + } + + /** + * Handles the batch fuzzy listen request. + *

+ * This method processes the batch fuzzy listen request by adding or removing clients from the fuzzy listening + * context based on the request, and publishes corresponding events to notify interested parties. + *

+ * + * @param request The batch fuzzy listen request + * @param meta Request meta information + * @return The response to the batch fuzzy listen request + * @throws NacosException If an error occurs while processing the request + */ + @Override + @TpsControl(pointName = "ConfigFuzzyWatch") + @Secured(action = ActionTypes.READ, signType = SignType.CONFIG) + @ExtractorManager.Extractor(rpcExtractor = ConfigFuzzyWatchRequestParamsExtractor.class) + public ConfigFuzzyWatchResponse handle(ConfigFuzzyWatchRequest request, RequestMeta meta) throws NacosException { + String connectionId = StringPool.get(meta.getConnectionId()); + 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())); + } else if (WATCH_TYPE_CANCEL_WATCH.equals(request.getWatchType())) { + NotifyCenter.publishEvent(new ConfigCancelFuzzyWatchEvent(connectionId, groupKeyPattern)); + } + + // Return response + return new 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 new file mode 100644 index 00000000000..4710cddb3a2 --- /dev/null +++ b/config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigFuzzyWatchSyncNotifier.java @@ -0,0 +1,367 @@ +/* + * 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.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; +import com.alibaba.nacos.common.task.BatchTaskCounter; +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; +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; +import java.util.Collection; +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; + +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; + +/** + * Handles batch fuzzy listen events and pushes corresponding notifications to clients. + * + * @author stone-98 + * @date 2024/3/18 + */ +@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; + + 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 + */ + private 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. + * + * @param event The ConfigBatchFuzzyListenEvent to handle + */ + public void handleFuzzyWatchEvent(ConfigFuzzyWatchEvent event) { + + // Match client effective group keys based on the event pattern, client IP, and tag + Set matchGroupKeys = configFuzzyWatchContextService.matchGroupKeys(event.getGroupKeyPattern()); + + // Retrieve existing group keys for the client from the event + Set clientExistingGroupKeys = event.getClientExistingGroupKeys(); + + // Calculate and merge configuration states based on matched and existing group keys + List configStates = FuzzyGroupKeyPattern.diffGroupKeys(matchGroupKeys, + clientExistingGroupKeys); + + if (CollectionUtils.isEmpty(configStates)) { + 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 { + String syncType = event.isInitializing() ? FUZZY_WATCH_INIT_NOTIFY : FUZZY_WATCH_DIFF_SYNC_NOTIFY; + + int batchSize = ConfigCommonConfig.getInstance().getBatchSize(); + // Divide config states into batches + List> divideConfigStatesIntoBatches = divideConfigStatesIntoBatches( + configStates, batchSize); + + // Calculate the number of batches and initialize push batch finish count + int totalBatch = divideConfigStatesIntoBatches.size(); + BatchTaskCounter batchTaskCounter = new BatchTaskCounter(divideConfigStatesIntoBatches.size()); + int currentBatch = 1; + for (List configStateList : divideConfigStatesIntoBatches) { + // Map config states to FuzzyListenNotifyDiffRequest.Context objects + Set contexts = configStateList.stream().map(state -> { + + String changeType = state.isExist() ? Constants.ConfigChangedType.ADD_CONFIG + : Constants.ConfigChangedType.DELETE_CONFIG; + return ConfigFuzzyWatchSyncRequest.Context.build(state.getGroupKey(), changeType); + }).collect(Collectors.toSet()); + + ConfigFuzzyWatchSyncRequest request = ConfigFuzzyWatchSyncRequest.buildSyncRequest(syncType, contexts, + 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); + currentBatch++; + } + } + + } + + @Override + public List> subscribeTypes() { + List> result = new LinkedList<>(); + result.add(ConfigFuzzyWatchEvent.class); + result.add(ConfigCancelFuzzyWatchEvent.class); + return result; + } + + @Override + 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()); + } + + } + + /** + * Divides a collection of items into batches. + * + * @param configStates The collection of items to be divided into batches + * @param batchSize The size of each batch + * @param The type of items in the collection + * @return A list of batches, each containing a sublist of items + */ + private List> divideConfigStatesIntoBatches(Collection configStates, int batchSize) { + // Initialize an index to track the current batch number + AtomicInteger index = new AtomicInteger(); + + // Group the elements into batches based on their index divided by the batch size + return new ArrayList<>( + configStates.stream().collect(Collectors.groupingBy(e -> index.getAndIncrement() / batchSize)) + .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/RpcFuzzyListenConfigDiffNotifier.java b/config/src/main/java/com/alibaba/nacos/config/server/remote/RpcFuzzyListenConfigDiffNotifier.java deleted file mode 100644 index 67e7a46de42..00000000000 --- a/config/src/main/java/com/alibaba/nacos/config/server/remote/RpcFuzzyListenConfigDiffNotifier.java +++ /dev/null @@ -1,488 +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.remote; - -import com.alibaba.nacos.api.common.Constants; -import com.alibaba.nacos.api.config.remote.request.FuzzyListenNotifyDiffRequest; -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; -import com.alibaba.nacos.common.utils.CollectionUtils; -import com.alibaba.nacos.common.utils.GroupKeyPattern; -import com.alibaba.nacos.config.server.configuration.ConfigCommonConfig; -import com.alibaba.nacos.config.server.model.event.ConfigBatchFuzzyListenEvent; -import com.alibaba.nacos.config.server.service.ConfigCacheService; -import com.alibaba.nacos.config.server.utils.ConfigExecutor; -import com.alibaba.nacos.config.server.utils.GroupKey; -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.ArrayList; -import java.util.Collection; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -/** - * Handles batch fuzzy listen events and pushes corresponding notifications to clients. - * - * @author stone-98 - * @date 2024/3/18 - */ -@Component(value = "rpcFuzzyListenConfigDiffNotifier") -public class RpcFuzzyListenConfigDiffNotifier extends Subscriber { - - 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; - - public RpcFuzzyListenConfigDiffNotifier(ConnectionManager connectionManager, RpcPushService rpcPushService) { - this.connectionManager = connectionManager; - this.tpsControlManager = ControlManagerCenter.getInstance().getTpsControlManager(); - this.rpcPushService = rpcPushService; - 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 - */ - private static void push(RpcPushTask retryTask, ConnectionManager connectionManager) { - FuzzyListenNotifyDiffRequest 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. - * - * @param event The ConfigBatchFuzzyListenEvent to handle - */ - @Override - public void onEvent(ConfigBatchFuzzyListenEvent event) { - // Get the connection for the client - Connection connection = connectionManager.getConnection(event.getClientId()); - if (connection == null) { - Loggers.REMOTE_PUSH.warn( - "clientId not found, Config diff notification not sent. clientId={},keyGroupPattern={}", - event.getClientId(), event.getKeyGroupPattern()); - // If connection is not available, return - return; - } - - // Retrieve meta information for the connection - ConnectionMeta metaInfo = connection.getMetaInfo(); - String clientIp = metaInfo.getClientIp(); - String clientTag = metaInfo.getTag(); - - // Match client effective group keys based on the event pattern, client IP, and tag - Set matchGroupKeys = ConfigCacheService.matchClientEffectiveGroupKeys(event.getKeyGroupPattern(), - clientIp, clientTag); - - // Retrieve existing group keys for the client from the event - Set clientExistingGroupKeys = event.getClientExistingGroupKeys(); - - // Check if both matched and existing group keys are empty, if so, return - if (CollectionUtils.isEmpty(matchGroupKeys) && CollectionUtils.isEmpty(clientExistingGroupKeys)) { - return; - } - - // Calculate and merge configuration states based on matched and existing group keys - List configStates = calculateAndMergeToConfigState(matchGroupKeys, clientExistingGroupKeys); - - // If no config states are available, return - if (CollectionUtils.isEmpty(configStates)) { - return; - } - - int batchSize = ConfigCommonConfig.getInstance().getBatchSize(); - // Divide config states into batches - List> divideConfigStatesIntoBatches = divideConfigStatesIntoBatches(configStates, batchSize); - - // Calculate the number of batches and initialize push batch finish count - int originBatchSize = divideConfigStatesIntoBatches.size(); - AtomicInteger pushBatchFinishCount = new AtomicInteger(0); - - // Iterate over each batch of config states - for (List configStateList : divideConfigStatesIntoBatches) { - // Map config states to FuzzyListenNotifyDiffRequest.Context objects - Set contexts = configStateList.stream().map(state -> { - String[] parseKey = GroupKey.parseKey(state.getGroupKey()); - String dataId = parseKey[0]; - String group = parseKey[1]; - String tenant = parseKey.length > 2 ? parseKey[2] : Constants.DEFAULT_NAMESPACE_ID; - String changeType = event.isInitializing() ? Constants.ConfigChangeType.LISTEN_INIT - : (state.isExist() ? Constants.ConfigChangeType.ADD_CONFIG - : Constants.ConfigChangeType.DELETE_CONFIG); - return FuzzyListenNotifyDiffRequest.Context.build(tenant, group, dataId, changeType); - }).collect(Collectors.toSet()); - - // Remove namespace from the pattern - String patternWithoutNameSpace = GroupKeyPattern.getPatternRemovedNamespace(event.getKeyGroupPattern()); - - // Build FuzzyListenNotifyDiffRequest with contexts and pattern - FuzzyListenNotifyDiffRequest request = FuzzyListenNotifyDiffRequest.buildInitRequest(contexts, - patternWithoutNameSpace); - - int maxPushRetryTimes = ConfigCommonConfig.getInstance().getMaxPushRetryTimes(); - // Create RPC push task and push the request to the client - RpcPushTask rpcPushTask = new RpcPushTask(request, pushBatchFinishCount, originBatchSize, maxPushRetryTimes, - event.getClientId(), clientIp, metaInfo.getAppName()); - push(rpcPushTask, connectionManager); - } - } - - /** - * Calculates and merges the differences between the matched group keys and the client's existing group keys into a - * list of ConfigState objects. - * - * @param matchGroupKeys The matched group keys set - * @param clientExistingGroupKeys The client's existing group keys set - * @return The merged list of ConfigState objects representing the states to be added or removed - */ - private List calculateAndMergeToConfigState(Set matchGroupKeys, - Set clientExistingGroupKeys) { - // Calculate the set of group keys to be added and removed - Set addGroupKeys = new HashSet<>(); - if (CollectionUtils.isNotEmpty(matchGroupKeys)) { - addGroupKeys.addAll(matchGroupKeys); - } - if (CollectionUtils.isNotEmpty(clientExistingGroupKeys)) { - addGroupKeys.removeAll(clientExistingGroupKeys); - } - - Set removeGroupKeys = new HashSet<>(); - if (CollectionUtils.isNotEmpty(clientExistingGroupKeys)) { - removeGroupKeys.addAll(clientExistingGroupKeys); - } - if (CollectionUtils.isNotEmpty(matchGroupKeys)) { - removeGroupKeys.removeAll(matchGroupKeys); - } - - // Convert the group keys to be added and removed into corresponding ConfigState objects and merge them into a list - return Stream.concat(addGroupKeys.stream().map(groupKey -> new ConfigState(groupKey, true)), - removeGroupKeys.stream().map(groupKey -> new ConfigState(groupKey, false))) - .collect(Collectors.toList()); - } - - @Override - public Class subscribeType() { - return ConfigBatchFuzzyListenEvent.class; - } - - /** - * Divides a collection of items into batches. - * - * @param configStates The collection of items to be divided into batches - * @param batchSize The size of each batch - * @param The type of items in the collection - * @return A list of batches, each containing a sublist of items - */ - private List> divideConfigStatesIntoBatches(Collection configStates, int batchSize) { - // Initialize an index to track the current batch number - AtomicInteger index = new AtomicInteger(); - - // Group the elements into batches based on their index divided by the batch size - return new ArrayList<>( - configStates.stream().collect(Collectors.groupingBy(e -> index.getAndIncrement() / batchSize)) - .values()); - } - - /** - * ConfigState. - */ - public static class ConfigState { - - /** - * The group key associated with the configuration. - */ - private String groupKey; - - /** - * Indicates whether the configuration exists or not. - */ - private boolean exist; - - /** - * Constructs a new ConfigState instance with the given group key and existence flag. - * - * @param groupKey The group key associated with the configuration. - * @param exist {@code true} if the configuration exists, {@code false} otherwise. - */ - public ConfigState(String groupKey, boolean exist) { - this.groupKey = groupKey; - this.exist = exist; - } - - /** - * Retrieves the group key associated with the configuration. - * - * @return The group key. - */ - public String getGroupKey() { - return groupKey; - } - - /** - * Sets the group key associated with the configuration. - * - * @param groupKey The group key to set. - */ - public void setGroupKey(String groupKey) { - this.groupKey = groupKey; - } - - /** - * Checks whether the configuration exists or not. - * - * @return {@code true} if the configuration exists, {@code false} otherwise. - */ - public boolean isExist() { - return exist; - } - - /** - * Sets the existence flag of the configuration. - * - * @param exist {@code true} if the configuration exists, {@code false} otherwise. - */ - public void setExist(boolean exist) { - this.exist = exist; - } - } - - /** - * Represents a task for pushing FuzzyListenNotifyDiffRequest to clients. - */ - class RpcPushTask implements Runnable { - - /** - * The FuzzyListenNotifyDiffRequest to be pushed. - */ - FuzzyListenNotifyDiffRequest 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; - - /** - * The IP address of the client. - */ - String clientIp; - - /** - * The name of the client's application. - */ - String appName; - - /** - * The counter for tracking the number of finished push batches. - */ - AtomicInteger pushBatchFinishCount; - - /** - * The original size of the batch before splitting. - */ - int originBatchSize; - - /** - * Constructs a new RpcPushTask with the specified parameters. - * - * @param notifyRequest The FuzzyListenNotifyDiffRequest to be pushed - * @param pushBatchFinishCount The counter for tracking the number of finished push batches - * @param originBatchSize The original size of the batch before splitting - * @param maxRetryTimes The maximum number of times to retry pushing the request - * @param connectionId The ID of the connection associated with the client - * @param clientIp The IP address of the client - * @param appName The name of the client's application - */ - public RpcPushTask(FuzzyListenNotifyDiffRequest notifyRequest, AtomicInteger pushBatchFinishCount, - int originBatchSize, int maxRetryTimes, String connectionId, String clientIp, String appName) { - this.notifyRequest = notifyRequest; - this.pushBatchFinishCount = pushBatchFinishCount; - this.originBatchSize = originBatchSize; - this.maxRetryTimes = maxRetryTimes; - this.connectionId = connectionId; - this.clientIp = clientIp; - this.appName = appName; - } - - /** - * 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 RpcPushCallback(this, tpsControlManager, connectionManager, pushBatchFinishCount, - originBatchSize), ConfigExecutor.getClientConfigNotifierServiceExecutor()); - } - } - } - - /** - * Represents a callback for handling the result of an RPC push operation. - */ - class RpcPushCallback extends AbstractPushCallBack { - - /** - * The RpcPushTask associated with the callback. - */ - RpcPushTask rpcPushTask; - - /** - * The TpsControlManager for checking TPS limits. - */ - TpsControlManager tpsControlManager; - - /** - * The ConnectionManager for managing client connections. - */ - ConnectionManager connectionManager; - - /** - * The counter for tracking the number of pushed batches. - */ - AtomicInteger pushBatchCount; - - /** - * The original size of the batch before splitting. - */ - int originBatchSize; - - /** - * Constructs a new RpcPushCallback with the specified parameters. - * - * @param rpcPushTask The RpcPushTask associated with the callback - * @param tpsControlManager The TpsControlManager for checking TPS limits - * @param connectionManager The ConnectionManager for managing client connections - * @param pushBatchCount The counter for tracking the number of pushed batches - * @param originBatchSize The original size of the batch before splitting - */ - public RpcPushCallback(RpcPushTask rpcPushTask, TpsControlManager tpsControlManager, - ConnectionManager connectionManager, AtomicInteger pushBatchCount, int originBatchSize) { - // Set the timeout for the callback - super(3000L); - this.rpcPushTask = rpcPushTask; - this.tpsControlManager = tpsControlManager; - this.connectionManager = connectionManager; - this.pushBatchCount = pushBatchCount; - this.originBatchSize = originBatchSize; - } - - /** - * 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 (pushBatchCount.get() < originBatchSize) { - pushBatchCount.incrementAndGet(); - } else if (pushBatchCount.get() == originBatchSize) { - FuzzyListenNotifyDiffRequest request = FuzzyListenNotifyDiffRequest.buildInitFinishRequest( - rpcPushTask.notifyRequest.getGroupKeyPattern()); - push(new RpcPushTask(request, pushBatchCount, originBatchSize, 50, rpcPushTask.connectionId, - rpcPushTask.clientIp, rpcPushTask.appName), 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={}", - rpcPushTask.notifyRequest.getGroupKeyPattern(), rpcPushTask.connectionId, e); - push(rpcPushTask, connectionManager); - } - } -} diff --git a/config/src/main/java/com/alibaba/nacos/config/server/service/ConfigCacheService.java b/config/src/main/java/com/alibaba/nacos/config/server/service/ConfigCacheService.java index e6e0fe3f060..89e12debc8f 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/service/ConfigCacheService.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/service/ConfigCacheService.java @@ -17,9 +17,6 @@ package com.alibaba.nacos.config.server.service; import com.alibaba.nacos.common.notify.NotifyCenter; -import com.alibaba.nacos.common.utils.CollectionUtils; -import com.alibaba.nacos.common.utils.GroupKeyPattern; -import com.alibaba.nacos.common.utils.InternetAddressUtil; import com.alibaba.nacos.common.utils.MD5Utils; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.config.server.model.CacheItem; @@ -34,14 +31,9 @@ import com.alibaba.nacos.sys.env.EnvUtil; import java.io.IOException; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Set; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; -import java.util.stream.Collectors; import static com.alibaba.nacos.api.common.Constants.CLIENT_IP; import static com.alibaba.nacos.api.common.Constants.VIPSERVER_TAG; @@ -70,43 +62,12 @@ public class ConfigCacheService { /** * groupKey -> cacheItem. */ - private static final ConcurrentHashMap CACHE = new ConcurrentHashMap<>(); + static final ConcurrentHashMap CACHE = new ConcurrentHashMap<>(); public static int groupCount() { return CACHE.size(); } - /** - * Matches the client effective group keys based on the specified group key pattern, client IP, and tag. - * - * @param groupKeyPattern The pattern to match group keys. - * @param clientIp The IP address of the client. - * @param tag The tag associated with the configuration. - * @return A set of group keys that match the pattern and are effective for the client. - */ - public static Set matchClientEffectiveGroupKeys(String groupKeyPattern, String clientIp, String tag) { - return CACHE.entrySet().stream() - .filter(entry -> GroupKeyPattern.isMatchPatternWithNamespace(entry.getKey(), groupKeyPattern)) - .filter(entry -> entry.getValue().effectiveForClient(tag, clientIp)).map(Map.Entry::getKey) - .collect(Collectors.toSet()); - } - - /** - * Checks if the specified group key is present in the cache and effective for the client. - * - * @param groupKey The group key to check. - * @param clientIp The IP address of the client. - * @param tag The tag associated with the configuration. - * @return true if the group key is present in the cache and effective for the client, false otherwise. - */ - public static boolean containsAndEffectiveForClient(String groupKey, String clientIp, String tag) { - if (!CACHE.containsKey(groupKey)) { - return false; - } - CacheItem cacheItem = CACHE.get(groupKey); - return cacheItem.effectiveForClient(tag, clientIp); - } - /** * Save config file and update md5 value in cache. * @@ -239,6 +200,8 @@ public static boolean dumpGray(String dataId, String group, String tenant, Strin //check timestamp long localGrayLastModifiedTs = ConfigCacheService.getGrayLastModifiedTs(groupKey, grayName); + + boolean timestampOutdated = lastModifiedTs < localGrayLastModifiedTs; if (timestampOutdated) { DUMP_LOG.warn("[dump-gray-ignore] timestamp is outdated,groupKey={}", groupKey); return true; 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 new file mode 100644 index 00000000000..673622d8f61 --- /dev/null +++ b/config/src/main/java/com/alibaba/nacos/config/server/service/ConfigFuzzyWatchContextService.java @@ -0,0 +1,251 @@ +/* + * 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.service; + +import com.alibaba.nacos.api.exception.runtime.NacosRuntimeException; +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.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 java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +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; + +/** + * fuzzy watch context for config. + * + * @author shiyiyue + */ +@Component +public class ConfigFuzzyWatchContextService { + + /** + * groupKeyPattern -> watched client id set. + */ + private final Map> watchedClients = new ConcurrentHashMap<>(); + + /** + * groupKeyPattern -> matched groupKeys set. + */ + 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); + } + + /** + * trim fuzzy watch context.
1.remove watchedClients if watched client is empty. 2.remove matchedServiceKeys + * if watchedClients is null. pattern matchedServiceKeys will be removed in second period to avoid frequently + * matchedServiceKeys init. + */ + private void trimFuzzyWatchContext() { + Iterator>> iterator = matchedGroupKeys.entrySet().iterator(); + while (iterator.hasNext()) { + Map.Entry> next = iterator.next(); + Set watchedClients = this.watchedClients.get(next.getKey()); + + if (watchedClients == null) { + iterator.remove(); + LogUtil.DEFAULT_LOG.info( + "[fuzzy-watch] no watchedClients context for pattern {},remove matchedGroupKeys context", + next.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()); + } + } + } + + /** + * get matched exist group keys with the groupKeyPattern. return null if not matched. + * + * @param groupKeyPattern groupKeyPattern. + * @return + */ + public Set matchGroupKeys(String groupKeyPattern) { + return matchedGroupKeys.get(groupKeyPattern); + } + + /** + * sync group key change to fuzzy context. + * + * @param groupKey groupKey. + * @param changedType changedType. + * @return need notify ot not. + */ + public boolean syncGroupKeyContext(String groupKey, String changedType) { + + boolean needNotify = false; + + String[] groupKeyItems = GroupKey.parseKey(groupKey); + 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); + while (iterator.hasNext()) { + Map.Entry> entry = iterator.next(); + if (FuzzyGroupKeyPattern.matchPattern(entry.getKey(), dataId, group, namespace)) { + if (tryAdd && entry.getValue().add(groupKey)) { + needNotify = true; + } + if (tryRemove && entry.getValue().remove(groupKey)) { + needNotify = true; + } + } + } + return needNotify; + } + + /** + * Matches the client effective group keys based on the specified group key pattern, client IP, and tag. + * + * @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) { + if (matchedGroupKeys.containsKey(groupKeyPattern)) { + return; + } + + if (matchedGroupKeys.size() >= FUZZY_WATCH_MAX_PATTERN_COUNT) { + 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()); + } + + matchedGroupKeys.computeIfAbsent(groupKeyPattern, k -> new HashSet<>()); + Set matchedGroupKeys = this.matchedGroupKeys.get(groupKeyPattern); + long matchBeginTime = System.currentTimeMillis(); + 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) { + 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()); + } + matchedGroupKeys.add(groupKey); + } + } + LogUtil.DEFAULT_LOG.info("[fuzzy-watch] pattern {} match {} group keys, cost {}ms", groupKeyPattern, + matchedGroupKeys.size(), System.currentTimeMillis() - matchBeginTime); + + } + + /** + * Adds a fuzzy listen connection ID associated with the specified group key pattern. If the key pattern does not + * exist in the context, a new entry will be created. If the key pattern already exists, the connection ID will be + * added to the existing set. + * + * @param groupKeyPattern The group key pattern to associate with the listen connection. + * @param connectId The connection ID to be added. + */ + public synchronized void addFuzzyListen(String groupKeyPattern, String connectId) { + + 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); + } + + /** + * Removes a fuzzy listen connection ID associated with the specified group key pattern. If the group key pattern + * exists in the context and the connection ID is found in the associated set, the connection ID will be removed + * from the set. If the set becomes empty after removal, the entry for the group key pattern will be removed from + * the context. + * + * @param groupKeyPattern The group key pattern associated with the listen connection to be removed. + * @param connectionId The connection ID to be removed. + */ + 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); + if (CollectionUtils.isNotEmpty(connectIds)) { + // Remove the connection ID from the set if it exists + connectIds.remove(connectionId); + } + } + + /** + * remove watch context for connection id. + * + * @param connectionId connection id. + */ + public void clearFuzzyWatchContext(String connectionId) { + for (Map.Entry> keyPatternContextEntry : watchedClients.entrySet()) { + Set connectionIds = keyPatternContextEntry.getValue(); + if (CollectionUtils.isNotEmpty(connectionIds)) { + connectionIds.remove(connectionId); + } + } + } + + /** + * Retrieves the set of connection IDs matched with the specified group key. + * + * @param groupKey The group key to match with the key patterns. + * @return The set of connection IDs matched with the group key. + */ + 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(); + + String[] groupItems = GroupKey2.parseKey(groupKey); + + while (watchClientIterator.hasNext()) { + Map.Entry> watchClientEntry = watchClientIterator.next(); + + String keyPattern = watchClientEntry.getKey(); + if (FuzzyGroupKeyPattern.matchPattern(keyPattern, groupItems[0], groupItems[1], groupItems[2])) { + if (CollectionUtils.isNotEmpty(watchClientEntry.getValue())) { + connectIds.addAll(watchClientEntry.getValue()); + } + } + } + return connectIds; + } + +} diff --git a/core/src/main/java/com/alibaba/nacos/core/paramcheck/impl/ConfigBatchFuzzyListenRequestParamsExtractor.java b/core/src/main/java/com/alibaba/nacos/core/paramcheck/impl/ConfigFuzzyWatchRequestParamsExtractor.java similarity index 51% rename from core/src/main/java/com/alibaba/nacos/core/paramcheck/impl/ConfigBatchFuzzyListenRequestParamsExtractor.java rename to core/src/main/java/com/alibaba/nacos/core/paramcheck/impl/ConfigFuzzyWatchRequestParamsExtractor.java index 73c3c01b834..d396986896e 100644 --- a/core/src/main/java/com/alibaba/nacos/core/paramcheck/impl/ConfigBatchFuzzyListenRequestParamsExtractor.java +++ b/core/src/main/java/com/alibaba/nacos/core/paramcheck/impl/ConfigFuzzyWatchRequestParamsExtractor.java @@ -16,26 +16,25 @@ package com.alibaba.nacos.core.paramcheck.impl; -import com.alibaba.nacos.api.config.remote.request.ConfigBatchFuzzyListenRequest; +import com.alibaba.nacos.api.config.remote.request.ConfigFuzzyWatchRequest; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.remote.request.Request; import com.alibaba.nacos.common.paramcheck.ParamInfo; -import com.alibaba.nacos.common.utils.CollectionUtils; +import com.alibaba.nacos.common.utils.FuzzyGroupKeyPattern; import com.alibaba.nacos.core.paramcheck.AbstractRpcParamExtractor; import java.util.ArrayList; import java.util.List; -import java.util.Set; /** - * Extractor for parameters of {@link ConfigBatchFuzzyListenRequest}. This extractor retrieves parameter information - * from the request object and constructs {@link ParamInfo} instances representing the namespace ID, group, and data IDs + * Extractor for parameters of {@link ConfigFuzzyWatchRequest}. This extractor retrieves parameter information from the + * request object and constructs {@link ParamInfo} instances representing the namespace ID, group, and data IDs * contained in the request's contexts. * * @author stone-98 * @date 2024/3/5 */ -public class ConfigBatchFuzzyListenRequestParamsExtractor extends AbstractRpcParamExtractor { +public class ConfigFuzzyWatchRequestParamsExtractor extends AbstractRpcParamExtractor { /** * Extracts parameter information from the given request. @@ -46,28 +45,12 @@ public class ConfigBatchFuzzyListenRequestParamsExtractor extends AbstractRpcPar */ @Override public List extractParam(Request request) throws NacosException { - ConfigBatchFuzzyListenRequest req = (ConfigBatchFuzzyListenRequest) request; - Set contexts = req.getContexts(); + ConfigFuzzyWatchRequest req = (ConfigFuzzyWatchRequest) request; List paramInfos = new ArrayList<>(); - if (contexts == null) { - return paramInfos; - } - for (ConfigBatchFuzzyListenRequest.Context context : contexts) { - // Extract namespace ID and group from the context - ParamInfo paramInfo1 = new ParamInfo(); - paramInfo1.setNamespaceId(context.getTenant()); - paramInfo1.setGroup(context.getGroup()); - paramInfos.add(paramInfo1); - - // Extract data IDs from the context if present - if (CollectionUtils.isNotEmpty(context.getDataIds())) { - for (String dataId : context.getDataIds()) { - ParamInfo paramInfo2 = new ParamInfo(); - paramInfo2.setDataId(dataId); - paramInfos.add(paramInfo2); - } - } - } + // Extract namespace ID and group from the context + ParamInfo paramInfo1 = new ParamInfo(); + paramInfo1.setNamespaceId(FuzzyGroupKeyPattern.getNamespaceFromPattern(req.getGroupKeyPattern())); + paramInfos.add(paramInfo1); return paramInfos; } } diff --git a/core/src/main/java/com/alibaba/nacos/core/utils/GlobalExecutor.java b/core/src/main/java/com/alibaba/nacos/core/utils/GlobalExecutor.java index 275b50f1b98..7559b326ccb 100644 --- a/core/src/main/java/com/alibaba/nacos/core/utils/GlobalExecutor.java +++ b/core/src/main/java/com/alibaba/nacos/core/utils/GlobalExecutor.java @@ -72,6 +72,13 @@ public static void scheduleByCommon(Runnable runnable, long delayMs) { COMMON_EXECUTOR.schedule(runnable, delayMs, TimeUnit.MILLISECONDS); } + public static void scheduleWithFixDelayByCommon(Runnable runnable, long delayMs) { + if (COMMON_EXECUTOR.isShutdown()) { + return; + } + COMMON_EXECUTOR.scheduleWithFixedDelay(runnable, delayMs, delayMs, TimeUnit.MILLISECONDS); + } + public static void submitLoadDataTask(Runnable runnable) { DISTRO_EXECUTOR.submit(runnable); } 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 d7f7fca3545..69920b9e0cd 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 @@ -31,7 +31,11 @@ public class StringPool { private static Cache groupKeyCache = CacheBuilder.newBuilder().maximumSize(5000000) - .expireAfterAccess(180, TimeUnit.SECONDS).build(); + .expireAfterAccess(60, TimeUnit.SECONDS).build(); + + static { + GlobalExecutor.scheduleWithFixDelayByCommon(() -> groupKeyCache.cleanUp(), 30000); + } /** * get singleton string value from the pool. diff --git a/example/src/main/java/com/alibaba/nacos/example/FuzzyListenExample.java b/example/src/main/java/com/alibaba/nacos/example/ConfigFuzzyWatchExample.java similarity index 88% rename from example/src/main/java/com/alibaba/nacos/example/FuzzyListenExample.java rename to example/src/main/java/com/alibaba/nacos/example/ConfigFuzzyWatchExample.java index 40622c4c4d7..a1cf4bb3d24 100644 --- a/example/src/main/java/com/alibaba/nacos/example/FuzzyListenExample.java +++ b/example/src/main/java/com/alibaba/nacos/example/ConfigFuzzyWatchExample.java @@ -18,8 +18,9 @@ import com.alibaba.nacos.api.config.ConfigFactory; import com.alibaba.nacos.api.config.ConfigService; -import com.alibaba.nacos.api.config.listener.AbstractFuzzyListenListener; -import com.alibaba.nacos.api.config.listener.FuzzyListenConfigChangeEvent; +import com.alibaba.nacos.api.config.listener.AbstractFuzzyWatchEventWatcher; +import com.alibaba.nacos.api.config.listener.FuzzyWatchEventWatcher; +import com.alibaba.nacos.api.config.listener.ConfigFuzzyWatchChangeEvent; import com.alibaba.nacos.api.exception.NacosException; import java.util.Properties; @@ -47,7 +48,7 @@ * @author stone-98 * @date 2024/3/14 */ -public class FuzzyListenExample { +public class ConfigFuzzyWatchExample { public static void main(String[] args) throws NacosException, InterruptedException { // Set up properties for Nacos Config Service @@ -66,15 +67,15 @@ public static void main(String[] args) throws NacosException, InterruptedExcepti } // Define a fuzzy listener to handle configuration changes - AbstractFuzzyListenListener listener = new AbstractFuzzyListenListener() { + FuzzyWatchEventWatcher listener = new AbstractFuzzyWatchEventWatcher() { @Override - public void onEvent(FuzzyListenConfigChangeEvent event) { + public void onEvent(ConfigFuzzyWatchChangeEvent event) { System.out.println("[fuzzy listen config change]" + event.toString()); } }; // Add the fuzzy listener to monitor configurations starting with "test" - configService.addFuzzyListener("test*", "DEFAULT_GROUP", listener); + configService.fuzzyWatch("test*", "DEFAULT_GROUP", listener); System.out.println("[Fuzzy listening started.]"); // Publish more configurations to trigger the listener diff --git a/example/src/main/java/com/alibaba/nacos/example/NamingFuzzyWatchExample.java b/example/src/main/java/com/alibaba/nacos/example/NamingFuzzyWatchExample.java new file mode 100644 index 00000000000..c7efac16a9b --- /dev/null +++ b/example/src/main/java/com/alibaba/nacos/example/NamingFuzzyWatchExample.java @@ -0,0 +1,120 @@ +/* + * 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.example; + +import com.alibaba.nacos.api.exception.NacosException; +import com.alibaba.nacos.api.naming.NamingFactory; +import com.alibaba.nacos.api.naming.NamingService; +import com.alibaba.nacos.api.naming.listener.FuzzyWatchChangeEvent; +import com.alibaba.nacos.api.naming.listener.FuzzyWatchEventWatcher; +import com.alibaba.nacos.api.naming.utils.NamingUtils; + +import java.util.Properties; +import java.util.concurrent.Executor; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +import static com.alibaba.nacos.api.common.Constants.DEFAULT_GROUP; + +/** + * Nacos naming fuzzy watch example. + *

Add the JVM parameter to run the NamingExample:

+ * {@code -DserverAddr=${nacos.server.ip}:${nacos.server.port} -Dnamespace=${namespaceId}} + * + * @author tanyongquan + */ +public class NamingFuzzyWatchExample { + + public static void main(String[] args) throws NacosException, InterruptedException { + + Properties properties = new Properties(); + properties.setProperty("serverAddr", System.getProperty("serverAddr", "localhost")); + properties.setProperty("namespace", System.getProperty("namespace", "public")); + + NamingService naming = NamingFactory.createNamingService(properties); + + int num = 5; + for (int i = 1; i <= num; i++) { + String s = "nacos.test." + i; + naming.registerInstance(s, "11.11.11.11", 8888); + } + + System.out.println(num + " instance have been registered"); + + Executor executor = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue(), + runnable -> { + Thread thread = new Thread(runnable); + thread.setName("test-thread"); + return thread; + }); + + naming.fuzzyWatch(DEFAULT_GROUP, new FuzzyWatchEventWatcher() { + + //EventListener onEvent is sync to handle, If process too low in onEvent, maybe block other onEvent callback. + //So you can override getExecutor() to async handle event. + @Override + public Executor getExecutor() { + return executor; + } + + @Override + public void onEvent(FuzzyWatchChangeEvent event) { + System.out.println( + "[Fuzzy-Watch-GROUP]changed service name: " + NamingUtils.getServiceKey(event.getNamespace(), + event.getGroupName(), event.getServiceName())); + System.out.println("[Fuzzy-Watch-GROUP]change type: " + event.getChangeType()); + } + }); + + naming.fuzzyWatch("nacos.test.*", DEFAULT_GROUP, new FuzzyWatchEventWatcher() { + + @Override + public Executor getExecutor() { + return executor; + } + + @Override + public void onEvent(FuzzyWatchChangeEvent event) { + System.out.println( + "[Prefix-Fuzzy-Watch]changed service name: " + NamingUtils.getServiceKey(event.getNamespace(), + event.getGroupName(), event.getServiceName())); + System.out.println("[Prefix-Fuzzy-Watch]change type: " + event.getChangeType()); + } + }); + + naming.registerInstance("nacos.test.-1", "11.11.11.11", 8888); + + Thread.sleep(1000); + + naming.registerInstance("nacos.OTHER-PREFIX", "11.11.11.11", 8888); + + Thread.sleep(1000); + + naming.registerInstance("nacos.OTHER-GROUP", "OTHER-GROUP", "11.11.11.11", 8888); + + Thread.sleep(1000); + + for (int i = 1; i <= num; i++) { + String s = "nacos.test." + i; + naming.deregisterInstance(s, "11.11.11.11", 8888); + } + + Thread.sleep(1000); + + } +} 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 93ecd25cdea..b08d2d0c496 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 @@ -20,6 +20,8 @@ import com.alibaba.nacos.naming.core.v2.client.Client; import com.alibaba.nacos.naming.core.v2.pojo.Service; +import java.util.Set; + /** * Operation client event. * @@ -94,10 +96,80 @@ public ClientUnsubscribeServiceEvent(Service service, String clientId) { } } - public static class ClientReleaseEvent extends ClientOperationEvent { + /** + * Client fuzzy watch service event. + */ + public static class ClientFuzzyWatchEvent extends ClientOperationEvent { + + private static final long serialVersionUID = -4518919987813223119L; + + /** + * client watched pattern. + */ + private final String groupKeyPattern; + + /** + * client side received group keys. + */ + private Set clientReceivedServiceKeys; + + /** + * is fuzzy watch initializing. + */ + private boolean isInitializing; + + public ClientFuzzyWatchEvent(String groupKeyPattern, String clientId, Set clientReceivedServiceKeys, + boolean isInitializing) { + super(clientId, null); + this.groupKeyPattern = groupKeyPattern; + this.clientReceivedServiceKeys = clientReceivedServiceKeys; + this.isInitializing = isInitializing; + } + + public String getGroupKeyPattern() { + return groupKeyPattern; + } + + 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; + } + } - private static final long serialVersionUID = -281486927726245701L; + /** + * 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 { + + private static final long serialVersionUID = -281486927726245701L; + private final Client client; private final boolean isNative; @@ -107,11 +179,11 @@ public ClientReleaseEvent(Client client, boolean isNative) { this.client = client; this.isNative = isNative; } - + public Client getClient() { return client; } - + public boolean isNative() { return isNative; } diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/event/service/ServiceEvent.java b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/event/service/ServiceEvent.java index 6d559529880..9530f95b377 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/event/service/ServiceEvent.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/event/service/ServiceEvent.java @@ -45,17 +45,25 @@ public static class ServiceChangedEvent extends ServiceEvent { private static final long serialVersionUID = 2123694271992630822L; - public ServiceChangedEvent(Service service) { - this(service, false); + private final String changedType; + + public ServiceChangedEvent(Service service, String changedType) { + this(service, changedType, false); } - public ServiceChangedEvent(Service service, boolean incrementRevision) { + public ServiceChangedEvent(Service service, String changedType, boolean incrementRevision) { super(service); + this.changedType = changedType; service.renewUpdateTime(); if (incrementRevision) { service.incrementRevision(); } } + + public String getChangedType() { + return changedType; + } + } /** @@ -64,14 +72,14 @@ public ServiceChangedEvent(Service service, boolean incrementRevision) { public static class ServiceSubscribedEvent extends ServiceEvent { private static final long serialVersionUID = -2645441445867337345L; - + private final String clientId; public ServiceSubscribedEvent(Service service, String clientId) { super(service); this.clientId = clientId; } - + public String getClientId() { return clientId; } diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/index/ClientServiceIndexesManager.java b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/index/ClientServiceIndexesManager.java index 48ea5611ae6..2f3bae809c1 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/index/ClientServiceIndexesManager.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/index/ClientServiceIndexesManager.java @@ -16,6 +16,7 @@ package com.alibaba.nacos.naming.core.v2.index; +import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.common.notify.Event; import com.alibaba.nacos.common.notify.NotifyCenter; import com.alibaba.nacos.common.notify.listener.SmartSubscriber; @@ -101,15 +102,15 @@ private void handleClientDisconnect(ClientOperationEvent.ClientReleaseEvent even for (Service each : client.getAllSubscribeService()) { removeSubscriberIndexes(each, client.getClientId()); } - DeregisterInstanceReason reason = event.isNative() - ? DeregisterInstanceReason.NATIVE_DISCONNECTED : DeregisterInstanceReason.SYNCED_DISCONNECTED; + DeregisterInstanceReason reason = event.isNative() ? DeregisterInstanceReason.NATIVE_DISCONNECTED + : DeregisterInstanceReason.SYNCED_DISCONNECTED; long currentTimeMillis = System.currentTimeMillis(); for (Service each : client.getAllPublishedService()) { removePublisherIndexes(each, client.getClientId()); InstancePublishInfo instance = client.getInstancePublishInfo(each); - NotifyCenter.publishEvent(new DeregisterInstanceTraceEvent(currentTimeMillis, - "", false, reason, each.getNamespace(), each.getGroup(), each.getName(), - instance.getIp(), instance.getPort())); + NotifyCenter.publishEvent( + new DeregisterInstanceTraceEvent(currentTimeMillis, "", false, reason, each.getNamespace(), + each.getGroup(), each.getName(), instance.getIp(), instance.getPort())); } } @@ -128,14 +129,21 @@ private void handleClientOperation(ClientOperationEvent event) { } private void addPublisherIndexes(Service service, String clientId) { + String serviceChangedType = Constants.ServiceChangedType.INSTANCE_CHANGED; + if (!publisherIndexes.containsKey(service)) { + // The only time the index needs to be updated is when the service is first created + serviceChangedType = Constants.ServiceChangedType.ADD_SERVICE; + } + NotifyCenter.publishEvent(new ServiceEvent.ServiceChangedEvent(service, serviceChangedType, true)); publisherIndexes.computeIfAbsent(service, key -> new ConcurrentHashSet<>()).add(clientId); - NotifyCenter.publishEvent(new ServiceEvent.ServiceChangedEvent(service, true)); } private void removePublisherIndexes(Service service, String clientId) { publisherIndexes.computeIfPresent(service, (s, ids) -> { ids.remove(clientId); - NotifyCenter.publishEvent(new ServiceEvent.ServiceChangedEvent(service, true)); + String serviceChangedType = ids.isEmpty() ? Constants.ServiceChangedType.DELETE_SERVICE + : Constants.ServiceChangedType.INSTANCE_CHANGED; + NotifyCenter.publishEvent(new ServiceEvent.ServiceChangedEvent(service, serviceChangedType, true)); return ids.isEmpty() ? null : ids; }); } 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 new file mode 100644 index 00000000000..6f0b0cd5c55 --- /dev/null +++ b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/index/NamingFuzzyWatchContextService.java @@ -0,0 +1,265 @@ +/* + * 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.runtime.NacosRuntimeException; +import com.alibaba.nacos.api.naming.utils.NamingUtils; +import com.alibaba.nacos.common.notify.Event; +import com.alibaba.nacos.common.notify.NotifyCenter; +import com.alibaba.nacos.common.notify.listener.SmartSubscriber; +import com.alibaba.nacos.common.utils.ConcurrentHashSet; +import com.alibaba.nacos.common.utils.FuzzyGroupKeyPattern; +import com.alibaba.nacos.core.utils.GlobalExecutor; +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.Loggers; +import org.springframework.stereotype.Component; + +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +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; + +/** + * naming fuzzy watch context service. + * + * @author shiyiyue + */ +@Component +public class NamingFuzzyWatchContextService extends SmartSubscriber { + + /** + * watched client ids of a pattern, {fuzzy watch pattern -> Set[watched clientID]}. + */ + private final ConcurrentMap> watchedClients = 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 static final int FUZZY_WATCH_MAX_PATTERN_COUNT = 50; + + private static final int FUZZY_WATCH_MAX_PATTERN_MATCHED_GROUP_KEY_COUNT = 200; + + public NamingFuzzyWatchContextService() { + GlobalExecutor.scheduleWithFixDelayByCommon(() -> trimFuzzyWatchContext(), 30000); + NotifyCenter.registerSubscriber(this); + } + + /** + * trim fuzzy watch context.
1.remove watchedClients if watched client is empty. 2.remove matchedServiceKeys + * if watchedClients is null. pattern matchedServiceKeys will be removed in second period to avoid frequently + * matchedServiceKeys init. + */ + private void trimFuzzyWatchContext() { + try { + Iterator>> iterator = matchedServiceKeys.entrySet().iterator(); + while (iterator.hasNext()) { + Map.Entry> next = iterator.next(); + Set watchedClients = this.watchedClients.get(next.getKey()); + if (watchedClients == null) { + iterator.remove(); + } else if (watchedClients.isEmpty()) { + this.watchedClients.remove(next.getKey()); + } + } + } catch (Throwable throwable) { + throwable.printStackTrace(); + } + } + + @Override + public List> subscribeTypes() { + List> result = new LinkedList<>(); + result.add(ClientOperationEvent.ClientReleaseEvent.class); + return result; + } + + @Override + public void onEvent(Event event) { + + //handle client disconnected event. + if (event instanceof ClientOperationEvent.ClientReleaseEvent) { + removeFuzzyWatchContext(((ClientOperationEvent.ClientReleaseEvent) event).getClientId()); + } + } + + /** + * get client that fuzzy watch this service. + * + * @param service service to check fuzzy watcher. + * @return client ids. + */ + public Set getFuzzyWatchedClients(Service service) { + Set matchedClients = new HashSet<>(); + Iterator>> iterator = watchedClients.entrySet().iterator(); + while (iterator.hasNext()) { + Map.Entry> entry = iterator.next(); + if (FuzzyGroupKeyPattern.matchPattern(entry.getKey(), service.getName(), service.getGroup(), + service.getNamespace())) { + matchedClients.addAll(entry.getValue()); + } + } + return matchedClients; + } + + /** + * sync changed service to fuzzy watch context. + * + * @param changedService changed service. + * @param changedType change type. + * @return + */ + public boolean syncServiceContext(Service changedService, String changedType) { + + boolean needNotify = false; + if (!changedType.equals(ADD_SERVICE) && !changedType.equals(DELETE_SERVICE)) { + return false; + } + + String serviceKey = NamingUtils.getServiceKey(changedService.getNamespace(), changedService.getGroup(), + changedService.getName()); + Loggers.SRV_LOG.warn("FUZZY_WATCH: service change matched,service key {},changed type {} ", serviceKey, + changedType); + + Iterator>> iterator = matchedServiceKeys.entrySet().iterator(); + + 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() >= FUZZY_WATCH_MAX_PATTERN_MATCHED_GROUP_KEY_COUNT) { + 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; + } + + } 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; + } + } + } + } + return needNotify; + } + + /** + * sync fuzzy watch context. + * + * @param groupKeyPattern group key pattern. + * @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; + } + + private void removeFuzzyWatchContext(String clientId) { + Iterator>> iterator = watchedClients.entrySet().iterator(); + while (iterator.hasNext()) { + Map.Entry> next = iterator.next(); + next.getValue().remove(clientId); + } + } + + /** + * remove fuzzy watch context for a pattern and client id. + * + * @param groupKeyPattern group key pattern. + * @param clientId client id. + */ + public void removeFuzzyWatchContext(String groupKeyPattern, String clientId) { + if (watchedClients.containsKey(groupKeyPattern)) { + watchedClients.get(groupKeyPattern).remove(clientId); + } + } + + /** + * This method will build/update the fuzzy watch match index for given patterns. + * + * @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) { + + if (!matchedServiceKeys.containsKey(completedPattern)) { + if (matchedServiceKeys.size() >= FUZZY_WATCH_MAX_PATTERN_COUNT) { + 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(), + FUZZY_WATCH_PATTERN_OVER_LIMIT.getMsg()); + } + + long matchBeginTime = System.currentTimeMillis(); + Set namespaceServices = ServiceManager.getInstance() + .getSingletons(getNamespaceFromPattern(completedPattern)); + Set matchedServices = matchedServiceKeys.computeIfAbsent(completedPattern, k -> new HashSet<>()); + + 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) { + + 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()); + } + 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); + + } + + return new HashSet(matchedServiceKeys.get(completedPattern)); + } + +} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/metadata/InstanceMetadataProcessor.java b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/metadata/InstanceMetadataProcessor.java index 92cb1ee4f51..d7e6514f287 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/metadata/InstanceMetadataProcessor.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/metadata/InstanceMetadataProcessor.java @@ -108,7 +108,8 @@ private void updateInstanceMetadata(MetadataOperation op) { Service service = Service.newService(op.getNamespace(), op.getGroup(), op.getServiceName()); service = ServiceManager.getInstance().getSingleton(service); namingMetadataManager.updateInstanceMetadata(service, op.getTag(), op.getMetadata()); - NotifyCenter.publishEvent(new ServiceEvent.ServiceChangedEvent(service, true)); + NotifyCenter.publishEvent(new ServiceEvent.ServiceChangedEvent(service, + com.alibaba.nacos.api.common.Constants.ServiceChangedType.INSTANCE_CHANGED, true)); } private void deleteInstanceMetadata(MetadataOperation op) { diff --git a/naming/src/main/java/com/alibaba/nacos/naming/healthcheck/heartbeat/ClientBeatProcessorV2.java b/naming/src/main/java/com/alibaba/nacos/naming/healthcheck/heartbeat/ClientBeatProcessorV2.java index dee58b8c67f..d1125aac39a 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/healthcheck/heartbeat/ClientBeatProcessorV2.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/healthcheck/heartbeat/ClientBeatProcessorV2.java @@ -16,6 +16,7 @@ package com.alibaba.nacos.naming.healthcheck.heartbeat; +import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.naming.utils.NamingUtils; import com.alibaba.nacos.common.notify.NotifyCenter; import com.alibaba.nacos.common.trace.event.naming.HealthStateChangeTraceEvent; @@ -67,7 +68,7 @@ public void run() { instance.setHealthy(true); Loggers.EVT_LOG.info("service: {} {POS} {IP-ENABLED} valid: {}:{}@{}, region: {}, msg: client beat ok", rsInfo.getServiceName(), ip, port, rsInfo.getCluster(), UtilsAndCommons.LOCALHOST_SITE); - NotifyCenter.publishEvent(new ServiceEvent.ServiceChangedEvent(service)); + NotifyCenter.publishEvent(new ServiceEvent.ServiceChangedEvent(service, Constants.ServiceChangedType.HEART_BEAT)); NotifyCenter.publishEvent(new ClientEvent.ClientChangedEvent(client)); NotifyCenter.publishEvent(new HealthStateChangeTraceEvent(System.currentTimeMillis(), service.getNamespace(), service.getGroup(), service.getName(), instance.getIp(), diff --git a/naming/src/main/java/com/alibaba/nacos/naming/healthcheck/heartbeat/UnhealthyInstanceChecker.java b/naming/src/main/java/com/alibaba/nacos/naming/healthcheck/heartbeat/UnhealthyInstanceChecker.java index 1614a5cf7aa..b3d65847d56 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/healthcheck/heartbeat/UnhealthyInstanceChecker.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/healthcheck/heartbeat/UnhealthyInstanceChecker.java @@ -76,7 +76,7 @@ private void changeHealthyStatus(Client client, Service service, HealthCheckInst .info("{POS} {IP-DISABLED} valid: {}:{}@{}@{}, region: {}, msg: client last beat: {}", instance.getIp(), instance.getPort(), instance.getCluster(), service.getName(), UtilsAndCommons.LOCALHOST_SITE, instance.getLastHeartBeatTime()); - NotifyCenter.publishEvent(new ServiceEvent.ServiceChangedEvent(service)); + NotifyCenter.publishEvent(new ServiceEvent.ServiceChangedEvent(service, Constants.ServiceChangedType.HEART_BEAT)); NotifyCenter.publishEvent(new ClientEvent.ClientChangedEvent(client)); NotifyCenter.publishEvent(new HealthStateChangeTraceEvent(System.currentTimeMillis(), service.getNamespace(), service.getGroup(), service.getName(), instance.getIp(), instance.getPort(), diff --git a/naming/src/main/java/com/alibaba/nacos/naming/push/NamingFuzzyWatchChangeNotifier.java b/naming/src/main/java/com/alibaba/nacos/naming/push/NamingFuzzyWatchChangeNotifier.java new file mode 100644 index 00000000000..28a693f4435 --- /dev/null +++ b/naming/src/main/java/com/alibaba/nacos/naming/push/NamingFuzzyWatchChangeNotifier.java @@ -0,0 +1,90 @@ +/* + * 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.push; + +import com.alibaba.nacos.api.naming.utils.NamingUtils; +import com.alibaba.nacos.common.notify.Event; +import com.alibaba.nacos.common.notify.NotifyCenter; +import com.alibaba.nacos.common.notify.listener.SmartSubscriber; +import com.alibaba.nacos.naming.core.v2.event.service.ServiceEvent; +import com.alibaba.nacos.naming.core.v2.index.NamingFuzzyWatchContextService; +import com.alibaba.nacos.naming.misc.Loggers; +import com.alibaba.nacos.naming.push.v2.PushConfig; +import com.alibaba.nacos.naming.push.v2.task.FuzzyWatchChangeNotifyTask; +import com.alibaba.nacos.naming.push.v2.task.FuzzyWatchPushDelayTaskEngine; +import org.springframework.stereotype.Service; + +import java.util.LinkedList; +import java.util.List; +import java.util.Set; + +/** + * service change notify for fuzzy watch. + * + * @author shiyiyue + */ +@Service +public class NamingFuzzyWatchChangeNotifier extends SmartSubscriber { + + private NamingFuzzyWatchContextService namingFuzzyWatchContextService; + + private FuzzyWatchPushDelayTaskEngine fuzzyWatchPushDelayTaskEngine; + + public NamingFuzzyWatchChangeNotifier(NamingFuzzyWatchContextService namingFuzzyWatchContextService, + FuzzyWatchPushDelayTaskEngine fuzzyWatchPushDelayTaskEngine) { + this.fuzzyWatchPushDelayTaskEngine = fuzzyWatchPushDelayTaskEngine; + this.namingFuzzyWatchContextService = namingFuzzyWatchContextService; + NotifyCenter.registerSubscriber(this); + } + + @Override + public List> subscribeTypes() { + List> result = new LinkedList<>(); + result.add(ServiceEvent.ServiceChangedEvent.class); + return result; + } + + @Override + public void onEvent(Event event) { + if (event instanceof ServiceEvent.ServiceChangedEvent) { + ServiceEvent.ServiceChangedEvent serviceChangedEvent = (ServiceEvent.ServiceChangedEvent) event; + if (namingFuzzyWatchContextService.syncServiceContext(serviceChangedEvent.getService(), + serviceChangedEvent.getChangedType())) { + generateFuzzyWatchChangeNotifyTask(serviceChangedEvent.getService(), + serviceChangedEvent.getChangedType()); + } + } + } + + private void generateFuzzyWatchChangeNotifyTask(com.alibaba.nacos.naming.core.v2.pojo.Service service, + String changedType) { + + String serviceKey = NamingUtils.getServiceKey(service.getNamespace(), service.getGroup(), service.getName()); + Set fuzzyWatchedClients = namingFuzzyWatchContextService.getFuzzyWatchedClients(service); + + Loggers.SRV_LOG.info("FUZZY_WATCH:serviceKey {} has {} clients fuzzy watched", serviceKey, + fuzzyWatchedClients == null ? 0 : fuzzyWatchedClients.size()); + // watch notify push task specify by service + for (String clientId : fuzzyWatchedClients) { + FuzzyWatchChangeNotifyTask fuzzyWatchChangeNotifyTask = new FuzzyWatchChangeNotifyTask(serviceKey, + changedType, clientId, PushConfig.getInstance().getPushTaskDelay()); + fuzzyWatchPushDelayTaskEngine.addTask(FuzzyWatchPushDelayTaskEngine.getTaskKey(fuzzyWatchChangeNotifyTask), + fuzzyWatchChangeNotifyTask); + } + } + +} 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 new file mode 100644 index 00000000000..c9e8f897257 --- /dev/null +++ b/naming/src/main/java/com/alibaba/nacos/naming/push/NamingFuzzyWatchSyncNotifier.java @@ -0,0 +1,157 @@ +/* + * 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.push; + +import com.alibaba.nacos.api.naming.remote.request.NamingFuzzyWatchSyncRequest; +import com.alibaba.nacos.common.notify.Event; +import com.alibaba.nacos.common.notify.NotifyCenter; +import com.alibaba.nacos.common.notify.listener.SmartSubscriber; +import com.alibaba.nacos.common.task.BatchTaskCounter; +import com.alibaba.nacos.common.utils.CollectionUtils; +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.PushConfig; +import com.alibaba.nacos.naming.push.v2.task.FuzzyWatchPushDelayTaskEngine; +import com.alibaba.nacos.naming.push.v2.task.FuzzyWatchSyncNotifyTask; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +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.api.common.Constants.ServiceChangedType.DELETE_SERVICE; + +/** + * fuzzy watch event for fuzzy watch. + * @author shiyiyue + */ +@Service +public class NamingFuzzyWatchSyncNotifier extends SmartSubscriber { + + private NamingFuzzyWatchContextService namingFuzzyWatchContextService; + + private FuzzyWatchPushDelayTaskEngine fuzzyWatchPushDelayTaskEngine; + + private static final int BATCH_SIZE = 10; + + public NamingFuzzyWatchSyncNotifier(NamingFuzzyWatchContextService namingFuzzyWatchContextService, + FuzzyWatchPushDelayTaskEngine fuzzyWatchPushDelayTaskEngine) { + this.namingFuzzyWatchContextService = namingFuzzyWatchContextService; + this.fuzzyWatchPushDelayTaskEngine = fuzzyWatchPushDelayTaskEngine; + NotifyCenter.registerSubscriber(this); + } + + @Override + public List> subscribeTypes() { + List> result = new LinkedList<>(); + result.add(ClientOperationEvent.ClientFuzzyWatchEvent.class); + result.add(ClientOperationEvent.ClientCancelFuzzyWatchEvent.class); + return result; + } + + @Override + public void onEvent(Event event) { + + if (event instanceof ClientOperationEvent.ClientFuzzyWatchEvent) { + //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 clientReceivedGroupKeys = clientFuzzyWatchEvent.getClientReceivedServiceKeys(); + List groupKeyStates = FuzzyGroupKeyPattern.diffGroupKeys( + patternMatchedServiceKeys, clientReceivedGroupKeys); + Set syncContext = convert(groupKeyStates); + String syncType = + clientFuzzyWatchEvent.isInitializing() ? FUZZY_WATCH_INIT_NOTIFY : FUZZY_WATCH_DIFF_SYNC_NOTIFY; + + if (CollectionUtils.isNotEmpty(groupKeyStates)) { + Set> dividedServices = divideServiceByBatch(syncContext); + BatchTaskCounter batchTaskCounter = new BatchTaskCounter(dividedServices.size()); + int currentBatch = 1; + for (Set batchData : dividedServices) { + FuzzyWatchSyncNotifyTask fuzzyWatchSyncNotifyTask = new FuzzyWatchSyncNotifyTask( + clientFuzzyWatchEvent.getClientId(), completedPattern, syncType, batchData, + PushConfig.getInstance().getPushTaskRetryDelay()); + fuzzyWatchSyncNotifyTask.setBatchTaskCounter(batchTaskCounter); + fuzzyWatchSyncNotifyTask.setTotalBatch(dividedServices.size()); + fuzzyWatchSyncNotifyTask.setCurrentBatch(currentBatch); + fuzzyWatchPushDelayTaskEngine.addTask( + FuzzyWatchPushDelayTaskEngine.getTaskKey(fuzzyWatchSyncNotifyTask), fuzzyWatchSyncNotifyTask); + currentBatch++; + } + } else if (FUZZY_WATCH_INIT_NOTIFY.equals(syncType)) { + FuzzyWatchSyncNotifyTask fuzzyWatchSyncNotifyTask = new FuzzyWatchSyncNotifyTask( + clientFuzzyWatchEvent.getClientId(), completedPattern, FINISH_FUZZY_WATCH_INIT_NOTIFY, null, + PushConfig.getInstance().getPushTaskRetryDelay()); + fuzzyWatchPushDelayTaskEngine.addTask(FuzzyWatchPushDelayTaskEngine.getTaskKey(fuzzyWatchSyncNotifyTask), + fuzzyWatchSyncNotifyTask); + + } + } + + private Set> divideServiceByBatch( + Collection matchedService) { + Set> result = new HashSet<>(); + if (matchedService.isEmpty()) { + return result; + } + Set currentBatch = new HashSet<>(); + for (NamingFuzzyWatchSyncRequest.Context groupedServiceName : matchedService) { + currentBatch.add(groupedServiceName); + if (currentBatch.size() >= this.BATCH_SIZE) { + result.add(currentBatch); + currentBatch = new HashSet<>(); + } + } + if (!currentBatch.isEmpty()) { + result.add(currentBatch); + } + return result; + } + + private Set convert(List diffGroupKeys) { + Set syncContext = new HashSet<>(); + for (FuzzyGroupKeyPattern.GroupKeyState groupKeyState : diffGroupKeys) { + NamingFuzzyWatchSyncRequest.Context context = new NamingFuzzyWatchSyncRequest.Context(); + context.setServiceKey(groupKeyState.getGroupKey()); + context.setChangedType(groupKeyState.isExist() ? ADD_SERVICE : DELETE_SERVICE); + syncContext.add(context); + } + return syncContext; + } +} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/push/v2/executor/PushExecutor.java b/naming/src/main/java/com/alibaba/nacos/naming/push/v2/executor/PushExecutor.java index 21bcfab0d4b..f081cc32b59 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/push/v2/executor/PushExecutor.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/push/v2/executor/PushExecutor.java @@ -16,6 +16,8 @@ package com.alibaba.nacos.naming.push.v2.executor; +import com.alibaba.nacos.api.naming.remote.request.AbstractFuzzyWatchNotifyRequest; +import com.alibaba.nacos.api.remote.PushCallBack; import com.alibaba.nacos.naming.pojo.Subscriber; import com.alibaba.nacos.naming.push.v2.PushDataWrapper; import com.alibaba.nacos.naming.push.v2.task.NamingPushCallback; @@ -45,4 +47,14 @@ public interface PushExecutor { * @param callBack callback */ void doPushWithCallback(String clientId, Subscriber subscriber, PushDataWrapper data, NamingPushCallback callBack); + + /** + * Do push to notify fuzzy watcher with call back. + * + * @param clientId client id + * @param fuzzyWatchNotifyRequest request for fuzzy watch notification + * @param callBack callback + */ + void doFuzzyWatchNotifyPushWithCallBack(String clientId, AbstractFuzzyWatchNotifyRequest fuzzyWatchNotifyRequest, PushCallBack callBack); + } diff --git a/naming/src/main/java/com/alibaba/nacos/naming/push/v2/executor/PushExecutorDelegate.java b/naming/src/main/java/com/alibaba/nacos/naming/push/v2/executor/PushExecutorDelegate.java index f1fa7288e07..34006f21092 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/push/v2/executor/PushExecutorDelegate.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/push/v2/executor/PushExecutorDelegate.java @@ -16,6 +16,8 @@ package com.alibaba.nacos.naming.push.v2.executor; +import com.alibaba.nacos.api.naming.remote.request.AbstractFuzzyWatchNotifyRequest; +import com.alibaba.nacos.api.remote.PushCallBack; import com.alibaba.nacos.naming.core.v2.client.impl.IpPortBasedClient; import com.alibaba.nacos.naming.pojo.Subscriber; import com.alibaba.nacos.naming.push.v2.PushDataWrapper; @@ -53,6 +55,13 @@ public void doPushWithCallback(String clientId, Subscriber subscriber, PushDataW getPushExecuteService(clientId, subscriber).doPushWithCallback(clientId, subscriber, data, callBack); } + @Override + public void doFuzzyWatchNotifyPushWithCallBack(String clientId, AbstractFuzzyWatchNotifyRequest watchNotifyRequest, + PushCallBack callBack) { + // only support fuzzy watch by rpc + rpcPushExecuteService.doFuzzyWatchNotifyPushWithCallBack(clientId, watchNotifyRequest, callBack); + } + private PushExecutor getPushExecuteService(String clientId, Subscriber subscriber) { Optional result = SpiImplPushExecutorHolder.getInstance() .findPushExecutorSpiImpl(clientId, subscriber); diff --git a/naming/src/main/java/com/alibaba/nacos/naming/push/v2/executor/PushExecutorRpcImpl.java b/naming/src/main/java/com/alibaba/nacos/naming/push/v2/executor/PushExecutorRpcImpl.java index 143d4715bdf..a2084b5d380 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/push/v2/executor/PushExecutorRpcImpl.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/push/v2/executor/PushExecutorRpcImpl.java @@ -17,7 +17,9 @@ package com.alibaba.nacos.naming.push.v2.executor; import com.alibaba.nacos.api.naming.pojo.ServiceInfo; +import com.alibaba.nacos.api.naming.remote.request.AbstractFuzzyWatchNotifyRequest; import com.alibaba.nacos.api.naming.remote.request.NotifySubscriberRequest; +import com.alibaba.nacos.api.remote.PushCallBack; import com.alibaba.nacos.core.remote.RpcPushService; import com.alibaba.nacos.naming.misc.GlobalExecutor; import com.alibaba.nacos.naming.pojo.Subscriber; @@ -60,4 +62,10 @@ private ServiceInfo getServiceInfo(PushDataWrapper data, Subscriber subscriber) .selectInstancesWithHealthyProtection(data.getOriginalData(), data.getServiceMetadata(), false, true, subscriber); } + + @Override + public void doFuzzyWatchNotifyPushWithCallBack(String clientId, AbstractFuzzyWatchNotifyRequest watchNotifyRequest, PushCallBack callBack) { + pushService.pushWithCallback(clientId, watchNotifyRequest, callBack, GlobalExecutor.getCallbackExecutor()); + } + } diff --git a/naming/src/main/java/com/alibaba/nacos/naming/push/v2/executor/PushExecutorUdpImpl.java b/naming/src/main/java/com/alibaba/nacos/naming/push/v2/executor/PushExecutorUdpImpl.java index c60478166d9..431ea4f7050 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/push/v2/executor/PushExecutorUdpImpl.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/push/v2/executor/PushExecutorUdpImpl.java @@ -17,7 +17,9 @@ package com.alibaba.nacos.naming.push.v2.executor; import com.alibaba.nacos.api.naming.pojo.ServiceInfo; +import com.alibaba.nacos.api.naming.remote.request.AbstractFuzzyWatchNotifyRequest; import com.alibaba.nacos.api.naming.utils.NamingUtils; +import com.alibaba.nacos.api.remote.PushCallBack; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.naming.pojo.Subscriber; import com.alibaba.nacos.naming.push.UdpPushService; @@ -93,4 +95,9 @@ private ServiceInfo handleClusterData(ServiceInfo data, Subscriber subscriber) { return StringUtils.isBlank(subscriber.getCluster()) ? data : ServiceUtil.selectInstances(data, subscriber.getCluster()); } + + @Override + public void doFuzzyWatchNotifyPushWithCallBack(String clientId, AbstractFuzzyWatchNotifyRequest watchNotifyRequest, PushCallBack callBack) { + + } } 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 new file mode 100644 index 00000000000..46cabbec2e7 --- /dev/null +++ b/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchChangeNotifyExecuteTask.java @@ -0,0 +1,99 @@ +/* + * 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.naming.remote.request.NamingFuzzyWatchChangeNotifyRequest; +import com.alibaba.nacos.api.remote.PushCallBack; +import com.alibaba.nacos.common.task.AbstractExecuteTask; +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.FUZZY_WATCH_RESOURCE_CHANGED; + +/** + * Nacos naming fuzzy watch notify service change push delay task. + * + * @author tanyongquan + */ +public class FuzzyWatchChangeNotifyExecuteTask extends AbstractExecuteTask { + + private final String serviceKey; + + private final String changedType; + + private String clientId; + + private final FuzzyWatchPushDelayTaskEngine delayTaskEngine; + + public FuzzyWatchChangeNotifyExecuteTask(FuzzyWatchPushDelayTaskEngine delayTaskEngine, String serviceKey, + String changedType, String targetClient) { + this.serviceKey = serviceKey; + this.changedType = changedType; + this.clientId = targetClient; + this.delayTaskEngine = delayTaskEngine; + } + + @Override + public void run() { + + delayTaskEngine.getPushExecutor().doFuzzyWatchNotifyPushWithCallBack(clientId, + new NamingFuzzyWatchChangeNotifyRequest(serviceKey, changedType, FUZZY_WATCH_RESOURCE_CHANGED), + new FuzzyWatchChangeNotifyCallback(clientId, serviceKey, changedType)); + + } + + private class FuzzyWatchChangeNotifyCallback implements PushCallBack { + + private final String clientId; + + private String serviceKey; + + private String changedType; + + private FuzzyWatchChangeNotifyCallback(String clientId, String serviceKey, String changedType) { + this.clientId = clientId; + this.serviceKey = serviceKey; + this.changedType = changedType; + } + + @Override + public long getTimeout() { + return PushConfig.getInstance().getPushTaskTimeout(); + } + + @Override + public void onSuccess() { + Loggers.PUSH.info("[FUZZY-WATCH] change notify success ,clientId {}, serviceKey {] ,changedType {} ", + clientId, clientId, changedType); + + } + + @Override + public void onFail(Throwable e) { + + 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())); + } + } + } +} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchChangeNotifyTask.java b/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchChangeNotifyTask.java new file mode 100644 index 00000000000..502cccdd9d8 --- /dev/null +++ b/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchChangeNotifyTask.java @@ -0,0 +1,64 @@ +/* + * 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.common.task.AbstractDelayTask; + +/** + * Nacos naming fuzzy watch notify service change push delay task. + * + * @author tanyongquan + */ +public class FuzzyWatchChangeNotifyTask extends AbstractDelayTask { + + private final String serviceKey; + + private final String changedType; + + private final String clientId; + + private final long delay; + + public FuzzyWatchChangeNotifyTask(String serviceKey, String changedType, String clientId, long delay) { + this.serviceKey = serviceKey; + this.changedType = changedType; + this.delay = delay; + this.clientId = clientId; + + } + + public String getChangedType() { + return changedType; + } + + public String getClientId() { + return clientId; + } + + public long getDelay() { + return delay; + } + + public String getServiceKey() { + return serviceKey; + } + + @Override + public void merge(AbstractDelayTask task) { + + } +} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchPushDelayTaskEngine.java b/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchPushDelayTaskEngine.java new file mode 100644 index 00000000000..94e64d42f43 --- /dev/null +++ b/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchPushDelayTaskEngine.java @@ -0,0 +1,105 @@ +/* + * 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.common.task.NacosTask; +import com.alibaba.nacos.common.task.NacosTaskProcessor; +import com.alibaba.nacos.common.task.engine.NacosDelayTaskExecuteEngine; +import com.alibaba.nacos.naming.misc.Loggers; +import com.alibaba.nacos.naming.misc.NamingExecuteTaskDispatcher; +import com.alibaba.nacos.naming.misc.SwitchDomain; +import com.alibaba.nacos.naming.push.v2.executor.PushExecutor; +import com.alibaba.nacos.naming.push.v2.executor.PushExecutorDelegate; +import org.springframework.stereotype.Component; + +/** + * Nacos naming fuzzy watch notify service change push delay task execute engine. + * + * @author tanyongquan + */ +@Component +public class FuzzyWatchPushDelayTaskEngine extends NacosDelayTaskExecuteEngine { + + private final PushExecutorDelegate pushExecutor; + + private final SwitchDomain switchDomain; + + public FuzzyWatchPushDelayTaskEngine(PushExecutorDelegate pushExecutor, SwitchDomain switchDomain) { + super(FuzzyWatchPushDelayTaskEngine.class.getSimpleName(), Loggers.PUSH); + this.pushExecutor = pushExecutor; + this.switchDomain = switchDomain; + setDefaultTaskProcessor(new WatchPushDelayTaskProcessor(this)); + } + + public PushExecutor getPushExecutor() { + return pushExecutor; + } + + @Override + protected void processTasks() { + if (!switchDomain.isPushEnabled()) { + return; + } + super.processTasks(); + } + + private static class WatchPushDelayTaskProcessor implements NacosTaskProcessor { + + private final FuzzyWatchPushDelayTaskEngine fuzzyWatchPushExecuteEngine; + + public WatchPushDelayTaskProcessor(FuzzyWatchPushDelayTaskEngine fuzzyWatchPushExecuteEngine) { + this.fuzzyWatchPushExecuteEngine = fuzzyWatchPushExecuteEngine; + } + + @Override + public boolean process(NacosTask task) { + + if (task instanceof FuzzyWatchChangeNotifyTask) { + //process fuzzy watch change notify when a service changed + FuzzyWatchChangeNotifyTask fuzzyWatchChangeNotifyTask = (FuzzyWatchChangeNotifyTask) task; + NamingExecuteTaskDispatcher.getInstance().dispatchAndExecuteTask(getTaskKey(task), + new FuzzyWatchChangeNotifyExecuteTask(fuzzyWatchPushExecuteEngine, + fuzzyWatchChangeNotifyTask.getServiceKey(), fuzzyWatchChangeNotifyTask.getChangedType(), + fuzzyWatchChangeNotifyTask.getClientId())); + } else if (task instanceof FuzzyWatchSyncNotifyTask) { + //process fuzzy watch sync notify when a new client fuzzy watch a pattern + FuzzyWatchSyncNotifyTask fuzzyWatchSyncNotifyTask = (FuzzyWatchSyncNotifyTask) task; + String pattern = fuzzyWatchSyncNotifyTask.getPattern(); + String clientId = fuzzyWatchSyncNotifyTask.getClientId(); + NamingExecuteTaskDispatcher.getInstance().dispatchAndExecuteTask(getTaskKey(task), + new FuzzyWatchSyncNotifyExecuteTask(clientId, pattern, fuzzyWatchPushExecuteEngine, + fuzzyWatchSyncNotifyTask)); + } + return true; + } + + } + + public static String getTaskKey(NacosTask task) { + if (task instanceof FuzzyWatchChangeNotifyTask) { + return "fwcnT-" + ((FuzzyWatchChangeNotifyTask) task).getClientId() + + ((FuzzyWatchChangeNotifyTask) task).getServiceKey(); + } else if (task instanceof FuzzyWatchSyncNotifyTask) { + return "fwsnT-" + ((FuzzyWatchSyncNotifyTask) task).getSyncType() + "-" + + ((FuzzyWatchSyncNotifyTask) task).getClientId() + ((FuzzyWatchSyncNotifyTask) task).getPattern() + + "-" + ((FuzzyWatchSyncNotifyTask) task).getCurrentBatch(); + } else { + throw new NacosRuntimeException(500, "unknown fuzzy task type"); + } + } +} 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 new file mode 100644 index 00000000000..243cb6336a8 --- /dev/null +++ b/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchSyncNotifyExecuteTask.java @@ -0,0 +1,158 @@ +/* + * 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.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. + * + * @author tanyongquan + */ +public class FuzzyWatchSyncNotifyExecuteTask extends AbstractExecuteTask { + + private final String clientId; + + private final String pattern; + + private final FuzzyWatchPushDelayTaskEngine fuzzyWatchPushDelayTaskEngine; + + 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 + public void run() { + + NamingFuzzyWatchSyncRequest namingFuzzyWatchSyncRequest = NamingFuzzyWatchSyncRequest.buildSyncNotifyRequest( + pattern, delayTask.getSyncType(), delayTask.getSyncServiceKeys(), delayTask.getTotalBatch(), + 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); + + } + } + } + +} \ 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 new file mode 100644 index 00000000000..fa4c633f206 --- /dev/null +++ b/naming/src/main/java/com/alibaba/nacos/naming/push/v2/task/FuzzyWatchSyncNotifyTask.java @@ -0,0 +1,116 @@ +/* + * 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.naming.remote.request.NamingFuzzyWatchSyncRequest; +import com.alibaba.nacos.common.task.AbstractDelayTask; +import com.alibaba.nacos.common.task.BatchTaskCounter; +import com.alibaba.nacos.naming.misc.Loggers; + +import java.util.HashSet; +import java.util.Set; + +/** + * Nacos naming fuzzy watch initial push delay task. + * + * @author tanyongquan + */ +public class FuzzyWatchSyncNotifyTask extends AbstractDelayTask { + + private final String clientId; + + private final String pattern; + + private final Set syncServiceKeys; + + private final String syncType; + + private int totalBatch; + + private int currentBatch; + + private BatchTaskCounter batchTaskCounter; + + public FuzzyWatchSyncNotifyTask(String clientId, String pattern, String syncType, + Set syncServiceKeys, long delay) { + this.clientId = clientId; + this.pattern = pattern; + this.syncType = syncType; + if (syncServiceKeys != null) { + this.syncServiceKeys = syncServiceKeys; + } else { + this.syncServiceKeys = new HashSet<>(); + } + setTaskInterval(delay); + setLastProcessTime(System.currentTimeMillis()); + } + + public int getTotalBatch() { + return totalBatch; + } + + public void setTotalBatch(int totalBatch) { + this.totalBatch = totalBatch; + } + + public int getCurrentBatch() { + return currentBatch; + } + + public void setCurrentBatch(int currentBatch) { + this.currentBatch = currentBatch; + } + + @Override + public void merge(AbstractDelayTask task) { + if (!(task instanceof FuzzyWatchSyncNotifyTask)) { + return; + } + FuzzyWatchSyncNotifyTask oldTask = (FuzzyWatchSyncNotifyTask) task; + + if (oldTask.getSyncServiceKeys() != null) { + syncServiceKeys.addAll(oldTask.getSyncServiceKeys()); + } + setLastProcessTime(Math.min(getLastProcessTime(), task.getLastProcessTime())); + Loggers.PUSH.info("[FUZZY-WATCH-INIT-PUSH] Task merge for pattern {}", pattern); + } + + public String getPattern() { + return pattern; + } + + public Set getSyncServiceKeys() { + return syncServiceKeys; + } + + public String getSyncType() { + return syncType; + } + + public String getClientId() { + return clientId; + } + + public BatchTaskCounter getBatchTaskCounter() { + return batchTaskCounter; + } + + public void setBatchTaskCounter(BatchTaskCounter batchTaskCounter) { + this.batchTaskCounter = batchTaskCounter; + } + +} 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 new file mode 100644 index 00000000000..782038fdac8 --- /dev/null +++ b/naming/src/main/java/com/alibaba/nacos/naming/remote/rpc/handler/NamingFuzzyWatchRequestHandler.java @@ -0,0 +1,67 @@ +/* + * 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.remote.rpc.handler; + +import com.alibaba.nacos.api.exception.NacosException; +import com.alibaba.nacos.api.naming.remote.request.NamingFuzzyWatchRequest; +import com.alibaba.nacos.api.naming.remote.response.NamingFuzzyWatchResponse; +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.core.remote.RequestHandler; +import com.alibaba.nacos.naming.core.v2.event.client.ClientOperationEvent; +import com.alibaba.nacos.plugin.auth.constant.ActionTypes; +import org.springframework.stereotype.Component; + +import static com.alibaba.nacos.api.common.Constants.WATCH_TYPE_CANCEL_WATCH; +import static com.alibaba.nacos.api.common.Constants.WATCH_TYPE_WATCH; + +/** + * Fuzzy watch service request handler. + * + * @author tanyongquan + */ +@Component("namingFuzzyWatchRequestHandler") +public class NamingFuzzyWatchRequestHandler extends RequestHandler { + + public NamingFuzzyWatchRequestHandler() { + NotifyCenter.registerToPublisher(ClientOperationEvent.ClientFuzzyWatchEvent.class, 1000); + NotifyCenter.registerToPublisher(ClientOperationEvent.ClientCancelFuzzyWatchEvent.class, 1000); + + } + + @Override + @Secured(action = ActionTypes.READ) + public NamingFuzzyWatchResponse handle(NamingFuzzyWatchRequest request, RequestMeta meta) throws NacosException { + + String groupKeyPattern = request.getGroupKeyPattern(); + switch (request.getWatchType()) { + case WATCH_TYPE_WATCH: + 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())); + return NamingFuzzyWatchResponse.buildSuccessResponse(); + default: + throw new NacosException(NacosException.INVALID_PARAM, + String.format("Unsupported request type %s", request.getWatchType())); + } + } +} diff --git a/naming/src/test/java/com/alibaba/nacos/naming/core/v2/index/ClientServiceIndexesManagerTest.java b/naming/src/test/java/com/alibaba/nacos/naming/core/v2/index/ClientServiceIndexesManagerTest.java index 0c9bc8676a4..c6a3c6c7687 100644 --- a/naming/src/test/java/com/alibaba/nacos/naming/core/v2/index/ClientServiceIndexesManagerTest.java +++ b/naming/src/test/java/com/alibaba/nacos/naming/core/v2/index/ClientServiceIndexesManagerTest.java @@ -120,7 +120,7 @@ void testSubscribeTypes() { List> classes = clientServiceIndexesManager.subscribeTypes(); assertNotNull(classes); - assertEquals(5, classes.size()); + assertEquals(7, classes.size()); } @Test diff --git a/naming/src/test/java/com/alibaba/nacos/naming/push/v2/NamingSubscriberServiceV2ImplTest.java b/naming/src/test/java/com/alibaba/nacos/naming/push/v2/NamingSubscriberServiceV2ImplTest.java index 84db805b5e5..abdce5ad864 100644 --- a/naming/src/test/java/com/alibaba/nacos/naming/push/v2/NamingSubscriberServiceV2ImplTest.java +++ b/naming/src/test/java/com/alibaba/nacos/naming/push/v2/NamingSubscriberServiceV2ImplTest.java @@ -16,10 +16,12 @@ package com.alibaba.nacos.naming.push.v2; +import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.naming.core.v2.client.Client; import com.alibaba.nacos.naming.core.v2.client.manager.ClientManagerDelegate; import com.alibaba.nacos.naming.core.v2.event.service.ServiceEvent; import com.alibaba.nacos.naming.core.v2.index.ClientServiceIndexesManager; +import com.alibaba.nacos.naming.core.v2.index.NamingFuzzyWatchContextService; import com.alibaba.nacos.naming.core.v2.pojo.Service; import com.alibaba.nacos.naming.misc.SwitchDomain; import com.alibaba.nacos.naming.pojo.Subscriber; @@ -61,6 +63,9 @@ class NamingSubscriberServiceV2ImplTest { @Mock private ClientServiceIndexesManager indexesManager; + @Mock + private NamingFuzzyWatchContextService namingFuzzyWatchContextService; + @Mock private PushDelayTaskExecuteEngine delayTaskEngine; @@ -74,10 +79,12 @@ class NamingSubscriberServiceV2ImplTest { @BeforeEach void setUp() throws Exception { - subscriberService = new NamingSubscriberServiceV2Impl(clientManager, indexesManager, null, null, null, switchDomain); + subscriberService = new NamingSubscriberServiceV2Impl(clientManager, indexesManager, null, null, null, + switchDomain); ReflectionTestUtils.setField(subscriberService, "delayTaskEngine", delayTaskEngine); when(indexesManager.getAllClientsSubscribeService(service)).thenReturn(Collections.singletonList(testClientId)); - when(indexesManager.getAllClientsSubscribeService(service1)).thenReturn(Collections.singletonList(testClientId)); + when(indexesManager.getAllClientsSubscribeService(service1)).thenReturn( + Collections.singletonList(testClientId)); Collection services = new LinkedList<>(); services.add(service); services.add(service1); @@ -91,7 +98,8 @@ void setUp() throws Exception { @Test void testGetSubscribersByString() { - Collection actual = subscriberService.getSubscribers(service.getNamespace(), service.getGroupedServiceName()); + Collection actual = subscriberService.getSubscribers(service.getNamespace(), + service.getGroupedServiceName()); assertEquals(1, actual.size()); assertEquals(service.getGroupedServiceName(), actual.iterator().next().getServiceName()); } @@ -105,7 +113,8 @@ void testGetSubscribersByService() { @Test void testGetFuzzySubscribersByString() { - Collection actual = subscriberService.getFuzzySubscribers(service.getNamespace(), service.getGroupedServiceName()); + Collection actual = subscriberService.getFuzzySubscribers(service.getNamespace(), + service.getGroupedServiceName()); assertEquals(2, actual.size()); } @@ -116,8 +125,9 @@ void testGetFuzzySubscribersByService() { } @Test - void onEvent() { - subscriberService.onEvent(new ServiceEvent.ServiceChangedEvent(service)); + public void onEvent() { + subscriberService.onEvent( + new ServiceEvent.ServiceChangedEvent(service, Constants.ServiceChangedType.ADD_SERVICE)); verify(delayTaskEngine).addTask(eq(service), any(PushDelayTask.class)); } } diff --git a/naming/src/test/java/com/alibaba/nacos/naming/push/v2/task/FixturePushExecutor.java b/naming/src/test/java/com/alibaba/nacos/naming/push/v2/task/FixturePushExecutor.java index f26fb516a50..6a9db2583a0 100644 --- a/naming/src/test/java/com/alibaba/nacos/naming/push/v2/task/FixturePushExecutor.java +++ b/naming/src/test/java/com/alibaba/nacos/naming/push/v2/task/FixturePushExecutor.java @@ -16,6 +16,8 @@ package com.alibaba.nacos.naming.push.v2.task; +import com.alibaba.nacos.api.naming.remote.request.AbstractFuzzyWatchNotifyRequest; +import com.alibaba.nacos.api.remote.PushCallBack; import com.alibaba.nacos.naming.pojo.Subscriber; import com.alibaba.nacos.naming.push.v2.PushDataWrapper; import com.alibaba.nacos.naming.push.v2.executor.PushExecutor; @@ -39,6 +41,16 @@ public void doPushWithCallback(String clientId, Subscriber subscriber, PushDataW } } + @Override + public void doFuzzyWatchNotifyPushWithCallBack(String clientId, AbstractFuzzyWatchNotifyRequest watchNotifyRequest, + PushCallBack callBack) { + if (shouldSuccess) { + callBack.onSuccess(); + } else { + callBack.onFail(failedException); + } + } + public void setShouldSuccess(boolean shouldSuccess) { this.shouldSuccess = shouldSuccess; } diff --git a/pom.xml b/pom.xml index bd03acffbb1..1c96d9a234a 100644 --- a/pom.xml +++ b/pom.xml @@ -88,7 +88,7 @@ - 3.0.0-ALPHA + 3.0.0-ALPHA-lzf-SNAPSHOT UTF-8 UTF-8 From 21f3b666c83543b9f35d2122f80d9e13494c68ba Mon Sep 17 00:00:00 2001 From: "zunfei.lzf" Date: Thu, 9 Jan 2025 16:11:20 +0800 Subject: [PATCH 12/15] fuzzy watch optimize,check style pmd fix --- .../nacos/api/config/ConfigService.java | 24 +++++++-------- .../client/config/NacosConfigService.java | 30 +++++++++---------- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/api/src/main/java/com/alibaba/nacos/api/config/ConfigService.java b/api/src/main/java/com/alibaba/nacos/api/config/ConfigService.java index 2c6699a1392..146c0bf75ac 100644 --- a/api/src/main/java/com/alibaba/nacos/api/config/ConfigService.java +++ b/api/src/main/java/com/alibaba/nacos/api/config/ConfigService.java @@ -171,12 +171,12 @@ boolean publishConfigCas(String dataId, String group, String content, String cas * pattern-based subscription to configurations, where the fixed group name represents the group and dataId patterns * specified for subscription. * - * @param fixedGroupName The fixed group name representing the group and dataId patterns to subscribe to. + * @param groupNamePattern The group name pattern representing the group and dataId patterns to subscribe to. * @param watcher The fuzzy watcher to be added. * @throws NacosException NacosException * @since 3.0 */ - void fuzzyWatch(String fixedGroupName, FuzzyWatchEventWatcher watcher) throws NacosException; + void fuzzyWatch(String groupNamePattern, FuzzyWatchEventWatcher watcher) throws NacosException; /** * Add a fuzzy listener to the configuration. After the server modifies the configuration matching the specified @@ -184,12 +184,12 @@ boolean publishConfigCas(String dataId, String group, String content, String cas * listeners allow for pattern-based subscription to configurations. * * @param dataIdPattern The pattern to match dataIds for subscription. - * @param fixedGroupName The fixed group name representing the group and dataId patterns to subscribe to. + * @param groupNamePattern The pattern to match group name representing the group and dataId patterns to subscribe to. * @param watcher The fuzzy listener to be added. * @throws NacosException NacosException * @since 3.0 */ - void fuzzyWatch(String dataIdPattern, String fixedGroupName, FuzzyWatchEventWatcher watcher) + void fuzzyWatch(String dataIdPattern, String groupNamePattern, FuzzyWatchEventWatcher watcher) throws NacosException; /** @@ -197,13 +197,13 @@ void fuzzyWatch(String dataIdPattern, String fixedGroupName, FuzzyWatchEventWatc * Fuzzy listeners allow for pattern-based subscription to configs, where the fixed group name represents the group * and dataId patterns specified for subscription. * - * @param fixedGroupName The fixed group name representing the group and dataId patterns to subscribe to. + * @param groupNamePattern The group name pattern representing the group and dataId patterns to subscribe to. * @param watcher The fuzzy watcher to be added. * @return CompletableFuture containing collection of configs that match the specified fixed group name. * @throws NacosException NacosException * @since 3.0 */ - Future> fuzzyWatchWithGroupKeys(String fixedGroupName, + Future> fuzzyWatchWithGroupKeys(String groupNamePattern, FuzzyWatchEventWatcher watcher) throws NacosException; /** @@ -211,36 +211,36 @@ Future> fuzzyWatchWithGroupKeys(String fixedGroupName, * fixed group name. Fuzzy listeners allow for pattern-based subscription to configs. * * @param dataIdPattern The pattern to match dataIds for subscription. - * @param fixedGroupName The fixed group name representing the group and dataId patterns to subscribe to. + * @param groupNamePattern The group name pattern representing the group and dataId patterns to subscribe to. * @param watcher The fuzzy watcher to be added. * @return CompletableFuture containing collection of configs that match the specified dataId pattern and fixed * group name. * @throws NacosException NacosException * @since 3.0 */ - Future> fuzzyWatchWithGroupKeys(String dataIdPattern, String fixedGroupName, + Future> fuzzyWatchWithGroupKeys(String dataIdPattern, String groupNamePattern, FuzzyWatchEventWatcher watcher) throws NacosException; /** * Cancel fuzzy listen and remove the event listener for a specified fixed group name. * - * @param fixedGroupName The fixed group name for fuzzy watch. + * @param groupNamePattern The group name pattern for fuzzy watch. * @param watcher The event watcher to be removed. * @throws NacosException If an error occurs during the cancellation process. * @since 3.0 */ - void cancelFuzzyWatch(String fixedGroupName, FuzzyWatchEventWatcher watcher) throws NacosException; + void cancelFuzzyWatch(String groupNamePattern, FuzzyWatchEventWatcher watcher) throws NacosException; /** * Cancel fuzzy listen and remove the event listener for a specified service name pattern and fixed group name. * * @param dataIdPattern The pattern to match dataId for fuzzy watch. - * @param fixedGroupName The fixed group name for fuzzy watch. + * @param groupNamePattern The group name pattern for fuzzy watch. * @param watcher The event listener to be removed. * @throws NacosException If an error occurs during the cancellation process. * @since 3.0 */ - void cancelFuzzyWatch(String dataIdPattern, String fixedGroupName, FuzzyWatchEventWatcher watcher) + void cancelFuzzyWatch(String dataIdPattern, String groupNamePattern, FuzzyWatchEventWatcher watcher) throws NacosException; } diff --git a/client/src/main/java/com/alibaba/nacos/client/config/NacosConfigService.java b/client/src/main/java/com/alibaba/nacos/client/config/NacosConfigService.java index 23997b1e613..76758bd7190 100644 --- a/client/src/main/java/com/alibaba/nacos/client/config/NacosConfigService.java +++ b/client/src/main/java/com/alibaba/nacos/client/config/NacosConfigService.java @@ -123,47 +123,47 @@ public void addListener(String dataId, String group, Listener listener) throws N } @Override - public void fuzzyWatch(String fixedGroupName, FuzzyWatchEventWatcher watcher) throws NacosException { - doAddFuzzyWatch(ANY_PATTERN, fixedGroupName, watcher); + public void fuzzyWatch(String groupNamePattern, FuzzyWatchEventWatcher watcher) throws NacosException { + doAddFuzzyWatch(ANY_PATTERN, groupNamePattern, watcher); } @Override - public void fuzzyWatch(String dataIdPattern, String fixedGroupName, FuzzyWatchEventWatcher watcher) + public void fuzzyWatch(String dataIdPattern, String groupNamePattern, FuzzyWatchEventWatcher watcher) throws NacosException { - doAddFuzzyWatch(dataIdPattern, fixedGroupName, watcher); + doAddFuzzyWatch(dataIdPattern, groupNamePattern, watcher); } @Override - public Future> fuzzyWatchWithGroupKeys(String fixedGroupName, FuzzyWatchEventWatcher watcher) + public Future> fuzzyWatchWithGroupKeys(String groupNamePattern, FuzzyWatchEventWatcher watcher) throws NacosException { - return doAddFuzzyWatch(ANY_PATTERN, fixedGroupName, watcher); + return doAddFuzzyWatch(ANY_PATTERN, groupNamePattern, watcher); } @Override - public Future> fuzzyWatchWithGroupKeys(String dataIdPattern, String fixedGroupName, + public Future> fuzzyWatchWithGroupKeys(String dataIdPattern, String groupNamePattern, FuzzyWatchEventWatcher watcher) throws NacosException { - return doAddFuzzyWatch(dataIdPattern, fixedGroupName, watcher); + return doAddFuzzyWatch(dataIdPattern, groupNamePattern, watcher); } - private Future> doAddFuzzyWatch(String dataIdPattern, String fixedGroupName, + private Future> doAddFuzzyWatch(String dataIdPattern, String groupNamePattern, FuzzyWatchEventWatcher watcher) throws NacosException { - ConfigFuzzyWatchContext configFuzzyWatchContext = worker.addTenantFuzzyWatcher(dataIdPattern, fixedGroupName, + ConfigFuzzyWatchContext configFuzzyWatchContext = worker.addTenantFuzzyWatcher(dataIdPattern, groupNamePattern, watcher); return configFuzzyWatchContext.createNewFuture(); } @Override - public void cancelFuzzyWatch(String fixedGroupName, FuzzyWatchEventWatcher watcher) throws NacosException { - cancelFuzzyWatch(ANY_PATTERN, fixedGroupName, watcher); + public void cancelFuzzyWatch(String groupNamePattern, FuzzyWatchEventWatcher watcher) throws NacosException { + cancelFuzzyWatch(ANY_PATTERN, groupNamePattern, watcher); } @Override - public void cancelFuzzyWatch(String dataIdPattern, String fixedGroupName, FuzzyWatchEventWatcher watcher) + public void cancelFuzzyWatch(String dataIdPattern, String groupNamePattern, FuzzyWatchEventWatcher watcher) throws NacosException { - doCancelFuzzyListen(dataIdPattern, fixedGroupName, watcher); + doCancelFuzzyWatch(dataIdPattern, groupNamePattern, watcher); } - private void doCancelFuzzyListen(String dataIdPattern, String groupNamePattern, FuzzyWatchEventWatcher watcher) + private void doCancelFuzzyWatch(String dataIdPattern, String groupNamePattern, FuzzyWatchEventWatcher watcher) throws NacosException { if (null == watcher) { return; From 3b92322d3fa953112bd0ed65752576af59005db9 Mon Sep 17 00:00:00 2001 From: "zunfei.lzf" Date: Thu, 9 Jan 2025 16:18:51 +0800 Subject: [PATCH 13/15] fuzzy watch optimize,check style pmd fix --- .../gprc/redo/data/FuzzyWatcherRedoData.java | 33 ------------------- 1 file changed, 33 deletions(-) delete mode 100644 client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/redo/data/FuzzyWatcherRedoData.java diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/redo/data/FuzzyWatcherRedoData.java b/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/redo/data/FuzzyWatcherRedoData.java deleted file mode 100644 index f0e820910dd..00000000000 --- a/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/redo/data/FuzzyWatcherRedoData.java +++ /dev/null @@ -1,33 +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.client.naming.remote.gprc.redo.data; - -/** - * Redo data for fuzzy watcher. - * - * @author tanyongquan - */ -public class FuzzyWatcherRedoData extends RedoData { - - private FuzzyWatcherRedoData(String serviceNamePattern, String groupNamePattern) { - super(serviceNamePattern, groupNamePattern); - } - - public static FuzzyWatcherRedoData build(String serviceNamePattern, String groupNamePattern) { - return new FuzzyWatcherRedoData(serviceNamePattern, groupNamePattern); - } -} From 21cb265507510e90c957c6f9cc04f431e5cd2d24 Mon Sep 17 00:00:00 2001 From: "zunfei.lzf" Date: Thu, 9 Jan 2025 16:23:59 +0800 Subject: [PATCH 14/15] fuzzy watch optimize,check style pmd fix --- .../ConfigFuzzyWatchChangeNotifier.java | 2 +- .../ConfigFuzzyWatchContextService.java | 32 +++++++++++-------- 2 files changed, 19 insertions(+), 15 deletions(-) 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 7478e7616c5..05b9200172c 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 @@ -48,7 +48,7 @@ * @author stone-98 * @date 2024/3/18 */ -@Component(value = "fuzzyWatchConfigChangeNotifier") +@Component public class ConfigFuzzyWatchChangeNotifier extends Subscriber { private static final String POINT_FUZZY_WATCH_CONFIG_PUSH = "POINT_FUZZY_WATCH_CONFIG_PUSH"; 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 673622d8f61..bccc74d5152 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 @@ -70,21 +70,25 @@ public ConfigFuzzyWatchContextService() { * matchedServiceKeys init. */ private void trimFuzzyWatchContext() { - Iterator>> iterator = matchedGroupKeys.entrySet().iterator(); - while (iterator.hasNext()) { - Map.Entry> next = iterator.next(); - Set watchedClients = this.watchedClients.get(next.getKey()); - - if (watchedClients == null) { - iterator.remove(); - LogUtil.DEFAULT_LOG.info( - "[fuzzy-watch] no watchedClients context for pattern {},remove matchedGroupKeys context", - next.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()); + try { + Iterator>> iterator = matchedGroupKeys.entrySet().iterator(); + while (iterator.hasNext()) { + Map.Entry> next = iterator.next(); + Set watchedClients = this.watchedClients.get(next.getKey()); + + if (watchedClients == null) { + iterator.remove(); + LogUtil.DEFAULT_LOG.info( + "[fuzzy-watch] no watchedClients context for pattern {},remove matchedGroupKeys context", + next.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()); + } } + } catch (Throwable throwable) { + LogUtil.DEFAULT_LOG.warn("[fuzzy-watch] trim fuzzy watch context fail", throwable); } } From 4568cbb07d4b962b77b03d9d68ee3917dbe78231 Mon Sep 17 00:00:00 2001 From: "zunfei.lzf" Date: Thu, 9 Jan 2025 16:11:20 +0800 Subject: [PATCH 15/15] fuzzy watch optimize --- .../nacos/api/config/ConfigService.java | 24 +++++++------- .../client/config/NacosConfigService.java | 30 ++++++++--------- .../gprc/redo/data/FuzzyWatcherRedoData.java | 33 ------------------- .../ConfigFuzzyWatchChangeNotifier.java | 2 +- .../ConfigFuzzyWatchContextService.java | 32 ++++++++++-------- 5 files changed, 46 insertions(+), 75 deletions(-) delete mode 100644 client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/redo/data/FuzzyWatcherRedoData.java diff --git a/api/src/main/java/com/alibaba/nacos/api/config/ConfigService.java b/api/src/main/java/com/alibaba/nacos/api/config/ConfigService.java index 2c6699a1392..146c0bf75ac 100644 --- a/api/src/main/java/com/alibaba/nacos/api/config/ConfigService.java +++ b/api/src/main/java/com/alibaba/nacos/api/config/ConfigService.java @@ -171,12 +171,12 @@ boolean publishConfigCas(String dataId, String group, String content, String cas * pattern-based subscription to configurations, where the fixed group name represents the group and dataId patterns * specified for subscription. * - * @param fixedGroupName The fixed group name representing the group and dataId patterns to subscribe to. + * @param groupNamePattern The group name pattern representing the group and dataId patterns to subscribe to. * @param watcher The fuzzy watcher to be added. * @throws NacosException NacosException * @since 3.0 */ - void fuzzyWatch(String fixedGroupName, FuzzyWatchEventWatcher watcher) throws NacosException; + void fuzzyWatch(String groupNamePattern, FuzzyWatchEventWatcher watcher) throws NacosException; /** * Add a fuzzy listener to the configuration. After the server modifies the configuration matching the specified @@ -184,12 +184,12 @@ boolean publishConfigCas(String dataId, String group, String content, String cas * listeners allow for pattern-based subscription to configurations. * * @param dataIdPattern The pattern to match dataIds for subscription. - * @param fixedGroupName The fixed group name representing the group and dataId patterns to subscribe to. + * @param groupNamePattern The pattern to match group name representing the group and dataId patterns to subscribe to. * @param watcher The fuzzy listener to be added. * @throws NacosException NacosException * @since 3.0 */ - void fuzzyWatch(String dataIdPattern, String fixedGroupName, FuzzyWatchEventWatcher watcher) + void fuzzyWatch(String dataIdPattern, String groupNamePattern, FuzzyWatchEventWatcher watcher) throws NacosException; /** @@ -197,13 +197,13 @@ void fuzzyWatch(String dataIdPattern, String fixedGroupName, FuzzyWatchEventWatc * Fuzzy listeners allow for pattern-based subscription to configs, where the fixed group name represents the group * and dataId patterns specified for subscription. * - * @param fixedGroupName The fixed group name representing the group and dataId patterns to subscribe to. + * @param groupNamePattern The group name pattern representing the group and dataId patterns to subscribe to. * @param watcher The fuzzy watcher to be added. * @return CompletableFuture containing collection of configs that match the specified fixed group name. * @throws NacosException NacosException * @since 3.0 */ - Future> fuzzyWatchWithGroupKeys(String fixedGroupName, + Future> fuzzyWatchWithGroupKeys(String groupNamePattern, FuzzyWatchEventWatcher watcher) throws NacosException; /** @@ -211,36 +211,36 @@ Future> fuzzyWatchWithGroupKeys(String fixedGroupName, * fixed group name. Fuzzy listeners allow for pattern-based subscription to configs. * * @param dataIdPattern The pattern to match dataIds for subscription. - * @param fixedGroupName The fixed group name representing the group and dataId patterns to subscribe to. + * @param groupNamePattern The group name pattern representing the group and dataId patterns to subscribe to. * @param watcher The fuzzy watcher to be added. * @return CompletableFuture containing collection of configs that match the specified dataId pattern and fixed * group name. * @throws NacosException NacosException * @since 3.0 */ - Future> fuzzyWatchWithGroupKeys(String dataIdPattern, String fixedGroupName, + Future> fuzzyWatchWithGroupKeys(String dataIdPattern, String groupNamePattern, FuzzyWatchEventWatcher watcher) throws NacosException; /** * Cancel fuzzy listen and remove the event listener for a specified fixed group name. * - * @param fixedGroupName The fixed group name for fuzzy watch. + * @param groupNamePattern The group name pattern for fuzzy watch. * @param watcher The event watcher to be removed. * @throws NacosException If an error occurs during the cancellation process. * @since 3.0 */ - void cancelFuzzyWatch(String fixedGroupName, FuzzyWatchEventWatcher watcher) throws NacosException; + void cancelFuzzyWatch(String groupNamePattern, FuzzyWatchEventWatcher watcher) throws NacosException; /** * Cancel fuzzy listen and remove the event listener for a specified service name pattern and fixed group name. * * @param dataIdPattern The pattern to match dataId for fuzzy watch. - * @param fixedGroupName The fixed group name for fuzzy watch. + * @param groupNamePattern The group name pattern for fuzzy watch. * @param watcher The event listener to be removed. * @throws NacosException If an error occurs during the cancellation process. * @since 3.0 */ - void cancelFuzzyWatch(String dataIdPattern, String fixedGroupName, FuzzyWatchEventWatcher watcher) + void cancelFuzzyWatch(String dataIdPattern, String groupNamePattern, FuzzyWatchEventWatcher watcher) throws NacosException; } diff --git a/client/src/main/java/com/alibaba/nacos/client/config/NacosConfigService.java b/client/src/main/java/com/alibaba/nacos/client/config/NacosConfigService.java index 23997b1e613..76758bd7190 100644 --- a/client/src/main/java/com/alibaba/nacos/client/config/NacosConfigService.java +++ b/client/src/main/java/com/alibaba/nacos/client/config/NacosConfigService.java @@ -123,47 +123,47 @@ public void addListener(String dataId, String group, Listener listener) throws N } @Override - public void fuzzyWatch(String fixedGroupName, FuzzyWatchEventWatcher watcher) throws NacosException { - doAddFuzzyWatch(ANY_PATTERN, fixedGroupName, watcher); + public void fuzzyWatch(String groupNamePattern, FuzzyWatchEventWatcher watcher) throws NacosException { + doAddFuzzyWatch(ANY_PATTERN, groupNamePattern, watcher); } @Override - public void fuzzyWatch(String dataIdPattern, String fixedGroupName, FuzzyWatchEventWatcher watcher) + public void fuzzyWatch(String dataIdPattern, String groupNamePattern, FuzzyWatchEventWatcher watcher) throws NacosException { - doAddFuzzyWatch(dataIdPattern, fixedGroupName, watcher); + doAddFuzzyWatch(dataIdPattern, groupNamePattern, watcher); } @Override - public Future> fuzzyWatchWithGroupKeys(String fixedGroupName, FuzzyWatchEventWatcher watcher) + public Future> fuzzyWatchWithGroupKeys(String groupNamePattern, FuzzyWatchEventWatcher watcher) throws NacosException { - return doAddFuzzyWatch(ANY_PATTERN, fixedGroupName, watcher); + return doAddFuzzyWatch(ANY_PATTERN, groupNamePattern, watcher); } @Override - public Future> fuzzyWatchWithGroupKeys(String dataIdPattern, String fixedGroupName, + public Future> fuzzyWatchWithGroupKeys(String dataIdPattern, String groupNamePattern, FuzzyWatchEventWatcher watcher) throws NacosException { - return doAddFuzzyWatch(dataIdPattern, fixedGroupName, watcher); + return doAddFuzzyWatch(dataIdPattern, groupNamePattern, watcher); } - private Future> doAddFuzzyWatch(String dataIdPattern, String fixedGroupName, + private Future> doAddFuzzyWatch(String dataIdPattern, String groupNamePattern, FuzzyWatchEventWatcher watcher) throws NacosException { - ConfigFuzzyWatchContext configFuzzyWatchContext = worker.addTenantFuzzyWatcher(dataIdPattern, fixedGroupName, + ConfigFuzzyWatchContext configFuzzyWatchContext = worker.addTenantFuzzyWatcher(dataIdPattern, groupNamePattern, watcher); return configFuzzyWatchContext.createNewFuture(); } @Override - public void cancelFuzzyWatch(String fixedGroupName, FuzzyWatchEventWatcher watcher) throws NacosException { - cancelFuzzyWatch(ANY_PATTERN, fixedGroupName, watcher); + public void cancelFuzzyWatch(String groupNamePattern, FuzzyWatchEventWatcher watcher) throws NacosException { + cancelFuzzyWatch(ANY_PATTERN, groupNamePattern, watcher); } @Override - public void cancelFuzzyWatch(String dataIdPattern, String fixedGroupName, FuzzyWatchEventWatcher watcher) + public void cancelFuzzyWatch(String dataIdPattern, String groupNamePattern, FuzzyWatchEventWatcher watcher) throws NacosException { - doCancelFuzzyListen(dataIdPattern, fixedGroupName, watcher); + doCancelFuzzyWatch(dataIdPattern, groupNamePattern, watcher); } - private void doCancelFuzzyListen(String dataIdPattern, String groupNamePattern, FuzzyWatchEventWatcher watcher) + private void doCancelFuzzyWatch(String dataIdPattern, String groupNamePattern, FuzzyWatchEventWatcher watcher) throws NacosException { if (null == watcher) { return; diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/redo/data/FuzzyWatcherRedoData.java b/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/redo/data/FuzzyWatcherRedoData.java deleted file mode 100644 index f0e820910dd..00000000000 --- a/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/redo/data/FuzzyWatcherRedoData.java +++ /dev/null @@ -1,33 +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.client.naming.remote.gprc.redo.data; - -/** - * Redo data for fuzzy watcher. - * - * @author tanyongquan - */ -public class FuzzyWatcherRedoData extends RedoData { - - private FuzzyWatcherRedoData(String serviceNamePattern, String groupNamePattern) { - super(serviceNamePattern, groupNamePattern); - } - - public static FuzzyWatcherRedoData build(String serviceNamePattern, String groupNamePattern) { - return new FuzzyWatcherRedoData(serviceNamePattern, groupNamePattern); - } -} 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 7478e7616c5..05b9200172c 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 @@ -48,7 +48,7 @@ * @author stone-98 * @date 2024/3/18 */ -@Component(value = "fuzzyWatchConfigChangeNotifier") +@Component public class ConfigFuzzyWatchChangeNotifier extends Subscriber { private static final String POINT_FUZZY_WATCH_CONFIG_PUSH = "POINT_FUZZY_WATCH_CONFIG_PUSH"; 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 673622d8f61..bccc74d5152 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 @@ -70,21 +70,25 @@ public ConfigFuzzyWatchContextService() { * matchedServiceKeys init. */ private void trimFuzzyWatchContext() { - Iterator>> iterator = matchedGroupKeys.entrySet().iterator(); - while (iterator.hasNext()) { - Map.Entry> next = iterator.next(); - Set watchedClients = this.watchedClients.get(next.getKey()); - - if (watchedClients == null) { - iterator.remove(); - LogUtil.DEFAULT_LOG.info( - "[fuzzy-watch] no watchedClients context for pattern {},remove matchedGroupKeys context", - next.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()); + try { + Iterator>> iterator = matchedGroupKeys.entrySet().iterator(); + while (iterator.hasNext()) { + Map.Entry> next = iterator.next(); + Set watchedClients = this.watchedClients.get(next.getKey()); + + if (watchedClients == null) { + iterator.remove(); + LogUtil.DEFAULT_LOG.info( + "[fuzzy-watch] no watchedClients context for pattern {},remove matchedGroupKeys context", + next.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()); + } } + } catch (Throwable throwable) { + LogUtil.DEFAULT_LOG.warn("[fuzzy-watch] trim fuzzy watch context fail", throwable); } }