From cfad6409967fab2ddd78d9398f5f60dc224785bc Mon Sep 17 00:00:00 2001 From: Chionanthus <33491015+Chionanthus@users.noreply.github.com> Date: Tue, 26 Sep 2023 17:36:21 +0800 Subject: [PATCH 1/4] [ISSUE #10380] Add Fuzzy Watch Client Side Operation (#11157) * add fuzzy watch pattern build rule add fuzzy watch Listener and event * fix docs * fuzzy Watch Redo Service * Add watch service request and response Add watch nacos server notify request and response Add request SPI and reflect config * Add Watch match cache in nacos client Add watch pattern match rule * Add nacos naming watch interface Add naocs watch client proxy * fix bug --- .../alibaba/nacos/api/common/Constants.java | 35 ++++ .../nacos/api/naming/NamingService.java | 40 +++++ .../listener/AbstractWatchEventListener.java | 41 +++++ .../api/naming/listener/WatchListener.java | 32 ++++ .../api/naming/listener/WatchNotifyEvent.java | 55 ++++++ .../nacos/api/naming/pojo/Service.java | 23 +++ .../naming/remote/NamingRemoteConstants.java | 4 + .../request/AbstractWatchNotifyRequest.java | 72 ++++++++ .../request/WatchNotifyChangeRequest.java | 55 ++++++ .../request/WatchNotifyInitRequest.java | 56 ++++++ .../remote/request/WatchServiceRequest.java | 43 +++++ .../response/NotifyWatcherResponse.java | 28 +++ .../remote/response/WatchServiceResponse.java | 61 +++++++ .../nacos/api/naming/utils/NamingUtils.java | 167 ++++++++++++++++++ .../com.alibaba.nacos.api.remote.Payload | 7 +- .../client/naming/NacosNamingService.java | 63 ++++++- .../naming/cache/WatchServiceListHolder.java | 140 +++++++++++++++ .../client/naming/event/WatchNotifyEvent.java | 85 +++++++++ .../event/WatchServiceChangeNotifier.java | 147 +++++++++++++++ .../naming/remote/NamingClientProxy.java | 27 +++ .../remote/NamingClientProxyDelegate.java | 35 +++- .../remote/gprc/NamingGrpcClientProxy.java | 65 ++++++- .../remote/gprc/NamingPushRequestHandler.java | 12 +- .../gprc/redo/NamingGrpcRedoService.java | 96 ++++++++++ .../remote/gprc/redo/RedoScheduledTask.java | 38 ++++ .../gprc/redo/data/WatcherRedoData.java | 33 ++++ .../remote/http/NamingHttpClientProxy.java | 15 ++ .../nacos-client/reflect-config.json | 12 ++ .../remote/AbstractNamingClientProxyTest.java | 15 ++ .../remote/NamingClientProxyDelegateTest.java | 94 ++++++---- .../gprc/NamingGrpcClientProxyTest.java | 12 +- .../gprc/NamingPushRequestHandlerTest.java | 4 +- 32 files changed, 1567 insertions(+), 45 deletions(-) create mode 100644 api/src/main/java/com/alibaba/nacos/api/naming/listener/AbstractWatchEventListener.java create mode 100644 api/src/main/java/com/alibaba/nacos/api/naming/listener/WatchListener.java create mode 100644 api/src/main/java/com/alibaba/nacos/api/naming/listener/WatchNotifyEvent.java create mode 100644 api/src/main/java/com/alibaba/nacos/api/naming/remote/request/AbstractWatchNotifyRequest.java create mode 100644 api/src/main/java/com/alibaba/nacos/api/naming/remote/request/WatchNotifyChangeRequest.java create mode 100644 api/src/main/java/com/alibaba/nacos/api/naming/remote/request/WatchNotifyInitRequest.java create mode 100644 api/src/main/java/com/alibaba/nacos/api/naming/remote/request/WatchServiceRequest.java create mode 100644 api/src/main/java/com/alibaba/nacos/api/naming/remote/response/NotifyWatcherResponse.java create mode 100644 api/src/main/java/com/alibaba/nacos/api/naming/remote/response/WatchServiceResponse.java create mode 100644 client/src/main/java/com/alibaba/nacos/client/naming/cache/WatchServiceListHolder.java create mode 100644 client/src/main/java/com/alibaba/nacos/client/naming/event/WatchNotifyEvent.java create mode 100644 client/src/main/java/com/alibaba/nacos/client/naming/event/WatchServiceChangeNotifier.java create mode 100644 client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/redo/data/WatcherRedoData.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 1846e3189d1..87e424ee9d6 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 @@ -182,8 +182,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"; @@ -236,6 +240,37 @@ public static class Naming { public static final String CMDB_CONTEXT_TYPE = "CMDB"; } + /** + * The constants in Watch Pattern Match Rule directory. + */ + public static class WatchMatchRule { + + public static final String MATCH_ALL = "MATCH_ALL"; + + public static final String MATCH_PREFIX = "MATCH_PREFIX"; + + } + + /** + * The constants in Watch Notify Event directory. + */ + public static class WatchEventType { + + 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 d27ad5bd044..fe1f6b266f2 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.AbstractWatchEventListener; import com.alibaba.nacos.api.naming.listener.EventListener; import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.api.naming.pojo.ListView; @@ -532,6 +533,45 @@ void subscribe(String serviceName, String groupName, List clusters, Even void unsubscribe(String serviceName, String groupName, List clusters, EventListener listener) throws NacosException; + /** + * Watch a range of services by match rule to receive notify events of matched services alteration. + * + * @param fixedGroupName fixed group name for watch + * @param listener event listener + * @throws NacosException nacos exception + */ + void fuzzyWatch(String fixedGroupName, AbstractWatchEventListener listener) throws NacosException; + + /** + * Watch a range of services by match rule to receive notify events of matched services alteration. + * + * @param serviceNamePattern service name pattern for watch + * @param fixedGroupName fixed group name for watch + * @param listener event listener + * @throws NacosException nacos exception + */ + void fuzzyWatch(String serviceNamePattern, String fixedGroupName, + AbstractWatchEventListener listener) throws NacosException; + + /** + * Cancel watch event listener of a pattern. + * + * @param fixedGroupName fixed group name for watch + * @param listener event listener + * @throws NacosException nacos exception + */ + void cancelFuzzyWatch(String fixedGroupName, AbstractWatchEventListener listener) throws NacosException; + + /** + * Cancel watch event listener of a pattern. + * + * @param serviceNamePattern service name pattern for watch + * @param fixedGroupName fixed group name for watch + * @param listener event listener + * @throws NacosException nacos exception + */ + void cancelFuzzyWatch(String serviceNamePattern, String fixedGroupName, AbstractWatchEventListener listener) throws NacosException; + /** * Get all service names from server. * diff --git a/api/src/main/java/com/alibaba/nacos/api/naming/listener/AbstractWatchEventListener.java b/api/src/main/java/com/alibaba/nacos/api/naming/listener/AbstractWatchEventListener.java new file mode 100644 index 00000000000..1b36de6a2d0 --- /dev/null +++ b/api/src/main/java/com/alibaba/nacos/api/naming/listener/AbstractWatchEventListener.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 watch event listener, to support handle event by user custom executor. + * + * @author tanyongquan + */ +public abstract class AbstractWatchEventListener implements WatchListener { + + 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/WatchListener.java b/api/src/main/java/com/alibaba/nacos/api/naming/listener/WatchListener.java new file mode 100644 index 00000000000..fcf384ddcca --- /dev/null +++ b/api/src/main/java/com/alibaba/nacos/api/naming/listener/WatchListener.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; + +/** + * Watch Listener. + * + * @author tanyongquan + */ +public interface WatchListener { + + /** + * callback event. + * + * @param event event + */ + void onEvent(WatchNotifyEvent event); +} diff --git a/api/src/main/java/com/alibaba/nacos/api/naming/listener/WatchNotifyEvent.java b/api/src/main/java/com/alibaba/nacos/api/naming/listener/WatchNotifyEvent.java new file mode 100644 index 00000000000..65b9e79bd31 --- /dev/null +++ b/api/src/main/java/com/alibaba/nacos/api/naming/listener/WatchNotifyEvent.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; + +/** + * Watch Notify Event. + * + * @author tanyongquan + */ +public class WatchNotifyEvent implements Event { + + private Service service; + + private String changeType; + + public WatchNotifyEvent() { + } + + public WatchNotifyEvent(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..0e9bfa166be 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 @@ -19,6 +19,7 @@ import java.io.Serializable; import java.util.HashMap; import java.util.Map; +import java.util.Objects; /** * Service of Nacos. @@ -63,6 +64,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; } @@ -112,4 +118,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..6d5e4b91b02 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 WATCH_SERVICE = "watchService"; + + public static final String CANCEL_WATCH_SERVICE = "cancelWatchService"; + 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/AbstractWatchNotifyRequest.java b/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/AbstractWatchNotifyRequest.java new file mode 100644 index 00000000000..edc21ca707c --- /dev/null +++ b/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/AbstractWatchNotifyRequest.java @@ -0,0 +1,72 @@ +/* + * 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 watch notify request, including basic watch notify information. + * + * @author tanyongquan + */ +public abstract class AbstractWatchNotifyRequest extends ServerRequest { + private String namespace; + + private String pattern; + + private String serviceChangedType; + + public AbstractWatchNotifyRequest(){ + } + + public AbstractWatchNotifyRequest(String namespace, String pattern, String serviceChangedType) { + this.namespace = namespace; + this.pattern = pattern; + 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; + } + + public String getPattern() { + return pattern; + } + + public void setPattern(String pattern) { + this.pattern = pattern; + } + + @Override + public String getModule() { + return NAMING_MODULE; + } +} diff --git a/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/WatchNotifyChangeRequest.java b/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/WatchNotifyChangeRequest.java new file mode 100644 index 00000000000..88730bccaac --- /dev/null +++ b/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/WatchNotifyChangeRequest.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 watch notify service change request, use it when one of the services changes. + * + * @author tanyongquan + */ +public class WatchNotifyChangeRequest extends AbstractWatchNotifyRequest { + + String serviceName; + + String groupName; + + public WatchNotifyChangeRequest() { + } + + public WatchNotifyChangeRequest(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/WatchNotifyInitRequest.java b/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/WatchNotifyInitRequest.java new file mode 100644 index 00000000000..7889c2113e2 --- /dev/null +++ b/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/WatchNotifyInitRequest.java @@ -0,0 +1,56 @@ +/* + * 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 watch initial notify request, use it when init a watch request, push service by batch. + * + * @author tanyongquan + */ +public class WatchNotifyInitRequest extends AbstractWatchNotifyRequest { + + private Collection servicesName; + + public WatchNotifyInitRequest() { + } + + private WatchNotifyInitRequest(String namespace, String pattern, String serviceChangedType, Collection servicesName) { + super(namespace, pattern, serviceChangedType); + this.servicesName = servicesName; + } + + public static WatchNotifyInitRequest buildInitRequest(String namespace, String pattern, Collection servicesName) { + return new WatchNotifyInitRequest(namespace, pattern, Constants.WatchEventType.WATCH_INITIAL_MATCH, servicesName); + } + + public static WatchNotifyInitRequest buildInitFinishRequest(String namespace, String pattern) { + return new WatchNotifyInitRequest(namespace, pattern, Constants.WatchEventType.FINISH_WATCH_INIT, new HashSet<>(1)); + } + + 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/WatchServiceRequest.java b/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/WatchServiceRequest.java new file mode 100644 index 00000000000..ac7b00018f1 --- /dev/null +++ b/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/WatchServiceRequest.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 watch service request. + * + * @author tanyongquan + */ +public class WatchServiceRequest extends AbstractNamingRequest { + + private String type; + + public WatchServiceRequest() { + } + + public WatchServiceRequest(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/NotifyWatcherResponse.java b/api/src/main/java/com/alibaba/nacos/api/naming/remote/response/NotifyWatcherResponse.java new file mode 100644 index 00000000000..6ff93354da5 --- /dev/null +++ b/api/src/main/java/com/alibaba/nacos/api/naming/remote/response/NotifyWatcherResponse.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 watcher. + * + * @author tanyongquan + */ +public class NotifyWatcherResponse extends Response { + +} diff --git a/api/src/main/java/com/alibaba/nacos/api/naming/remote/response/WatchServiceResponse.java b/api/src/main/java/com/alibaba/nacos/api/naming/remote/response/WatchServiceResponse.java new file mode 100644 index 00000000000..ecf487e23fe --- /dev/null +++ b/api/src/main/java/com/alibaba/nacos/api/naming/remote/response/WatchServiceResponse.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 watch service response. + * + * @author tanyongquan + */ +public class WatchServiceResponse extends Response { + + private String type; + + public WatchServiceResponse(){ + } + + public WatchServiceResponse(String type) { + this.type = type; + } + + public static WatchServiceResponse buildSuccessResponse(String type) { + return new WatchServiceResponse(type); + } + + /** + * Build fail response. + * + * @param message error message + * @return fail response + */ + public static WatchServiceResponse buildFailResponse(String message) { + WatchServiceResponse result = new WatchServiceResponse(); + 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/utils/NamingUtils.java b/api/src/main/java/com/alibaba/nacos/api/naming/utils/NamingUtils.java index 7c88abc70ad..cb53846d22a 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,6 +23,7 @@ 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; @@ -175,6 +176,172 @@ 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 subscribed to under this NamespaceId. + * @param namespaceId name space id + * @param completedPattern a set of all watch pattern(with namespace id) + * @return filtered pattern set + */ + public static Set filterPatternWithNamespace(String namespaceId, Set completedPattern) { + Set patterns = new HashSet<>(); + for (String each : completedPattern) { + String eachId = getNamespaceFromPattern(each); + if (namespaceId.equals(eachId)) { + patterns.add(getPatternRemovedNamespace(each)); + } + } + return patterns; + } + + /** + * Returns a combined string with matchPattern and matchType. + * @param matchPattern a match pattern. Such as a 'serviceNamePrefix' + * @param matchType The match type want to use + * @return 'matchPattern##matchType' + */ + public static String getGroupedPattern(final String matchPattern, final String matchType) { + if (StringUtils.isBlank(matchPattern) && !matchType.equals(Constants.WatchMatchRule.MATCH_ALL)) { + throw new IllegalArgumentException("Param 'matchPattern' is illegal, matchPattern is blank"); + } + if (StringUtils.isBlank(matchType)) { + throw new IllegalArgumentException("Param 'matchType' is illegal, matchType is blank"); + } else if (matchType.equals(Constants.WatchMatchRule.MATCH_ALL)) { + return Constants.WatchMatchRule.MATCH_ALL; + } + final String resultGroupedName = matchPattern + Constants.MATCH_PATTERN_SPLITER + matchType; + return resultGroupedName.intern(); + } + + /** + * Given a Pattern, return the string to be used for the match. + * @param groupedPattern a grouped pattern (match string ## match type) + * @return the string to be used for the match. + */ + public static String getMatchName(String groupedPattern) { + if (StringUtils.isBlank(groupedPattern)) { + return StringUtils.EMPTY; + } + if (!groupedPattern.contains(Constants.MATCH_PATTERN_SPLITER)) { + return groupedPattern; + } + return groupedPattern.split(Constants.MATCH_PATTERN_SPLITER)[0]; + } + + /** + * Given a Pattern, return the matching rule type. + * @param groupedPattern a grouped pattern (match string ## match type) + * @return the matching rule type. + */ + public static String getMatchRule(String groupedPattern) { + if (StringUtils.isBlank(groupedPattern)) { + return StringUtils.EMPTY; + } + if (!groupedPattern.contains(Constants.MATCH_PATTERN_SPLITER)) { + return Constants.WatchMatchRule.MATCH_ALL; + } + return groupedPattern.split(Constants.MATCH_PATTERN_SPLITER)[1]; + } + + /** + * 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. + * + * @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) { + String serviceMatchName = getMatchName(serviceNamePattern); + String serviceMatchType = getMatchRule(serviceNamePattern); + // Only support prefix match or all match right now + // Only support fixed group name right now + if (serviceMatchType.equals(Constants.WatchMatchRule.MATCH_ALL)) { + return groupName.equals(groupNamePattern); + } else if (serviceMatchType.equals(Constants.WatchMatchRule.MATCH_PREFIX)) { + return prefixMatchWithFixedGroupName(serviceName, serviceMatchName, groupName, getMatchName(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 d838e65ea2c..ab21acfe2c0 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 @@ -46,12 +46,17 @@ com.alibaba.nacos.api.config.remote.response.cluster.ConfigChangeClusterSyncResp com.alibaba.nacos.api.naming.remote.request.BatchInstanceRequest com.alibaba.nacos.api.naming.remote.request.InstanceRequest com.alibaba.nacos.api.naming.remote.request.NotifySubscriberRequest +com.alibaba.nacos.api.naming.remote.request.WatchNotifyChangeRequest +com.alibaba.nacos.api.naming.remote.request.WatchNotifyInitRequest 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.WatchServiceRequest 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.NotifyWatcherResponse 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.naming.remote.response.WatchServiceResponse \ No newline at end of file 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 a03e35559e3..e978900c8c8 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.AbstractWatchEventListener; import com.alibaba.nacos.api.naming.listener.EventListener; import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.api.naming.pojo.ListView; @@ -28,9 +29,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.WatchServiceListHolder; 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.WatchNotifyEvent; +import com.alibaba.nacos.client.naming.event.WatchServiceChangeNotifier; import com.alibaba.nacos.client.naming.remote.NamingClientProxy; import com.alibaba.nacos.client.naming.remote.NamingClientProxyDelegate; import com.alibaba.nacos.client.naming.utils.CollectionUtils; @@ -69,8 +73,12 @@ public class NacosNamingService implements NamingService { private ServiceInfoHolder serviceInfoHolder; + private WatchServiceListHolder watchServiceListHolder; + private InstancesChangeNotifier changeNotifier; + private WatchServiceChangeNotifier watchServiceChangeNotifier; + private NamingClientProxy clientProxy; private String notifierEventScope; @@ -98,8 +106,14 @@ private void init(Properties properties) throws NacosException { this.changeNotifier = new InstancesChangeNotifier(this.notifierEventScope); NotifyCenter.registerToPublisher(InstancesChangeEvent.class, 16384); NotifyCenter.registerSubscriber(changeNotifier); + this.watchServiceChangeNotifier = new WatchServiceChangeNotifier(this.notifierEventScope); + NotifyCenter.registerToPublisher(WatchNotifyEvent.class, 16384); + NotifyCenter.registerSubscriber(watchServiceChangeNotifier); + this.serviceInfoHolder = new ServiceInfoHolder(namespace, this.notifierEventScope, nacosClientProperties); - this.clientProxy = new NamingClientProxyDelegate(this.namespace, serviceInfoHolder, nacosClientProperties, changeNotifier); + this.watchServiceListHolder = new WatchServiceListHolder(this.notifierEventScope, nacosClientProperties); + this.clientProxy = new NamingClientProxyDelegate(this.namespace, serviceInfoHolder, watchServiceListHolder, + nacosClientProperties, changeNotifier); } private void initLogName(NacosClientProperties properties) { @@ -430,6 +444,53 @@ public void unsubscribe(String serviceName, String groupName, List clust } } + @Override + public void fuzzyWatch(String fixedGroupName, AbstractWatchEventListener listener) throws NacosException { + // pattern e.g. DEFAULT_GROUP@@MATCH_ALL + doFuzzyWatch(Constants.WatchMatchRule.MATCH_ALL, fixedGroupName, listener); + } + + @Override + public void fuzzyWatch(String serviceNamePattern, String fixedGroupName, + AbstractWatchEventListener listener) throws NacosException { + // only support prefix match right now + // pattern e.g. DEFAULT_GROUP@@nacos.test##MATCH_PREFIX + String serviceNamePrefixPattern = NamingUtils.getGroupedPattern(serviceNamePattern, Constants.WatchMatchRule.MATCH_PREFIX); + doFuzzyWatch(serviceNamePrefixPattern, fixedGroupName, listener); + } + + private void doFuzzyWatch(String serviceNamePattern, String groupNamePattern, + AbstractWatchEventListener listener) throws NacosException { + if (null == listener) { + return; + } + String uuid = UUID.randomUUID().toString(); + listener.setUuid(uuid); + watchServiceChangeNotifier.registerWatchListener(serviceNamePattern, groupNamePattern, listener); + clientProxy.fuzzyWatch(serviceNamePattern, groupNamePattern, uuid); + } + + @Override + public void cancelFuzzyWatch(String fixedGroupName, AbstractWatchEventListener listener) throws NacosException { + doCancelFuzzyWatch(Constants.WatchMatchRule.MATCH_ALL, fixedGroupName, listener); + } + + @Override + public void cancelFuzzyWatch(String serviceNamePattern, String fixedGroupName, AbstractWatchEventListener listener) throws NacosException { + String serviceNamePrefixPattern = NamingUtils.getGroupedPattern(serviceNamePattern, Constants.WatchMatchRule.MATCH_PREFIX); + doCancelFuzzyWatch(serviceNamePrefixPattern, fixedGroupName, listener); + } + + private void doCancelFuzzyWatch(String serviceNamePattern, String groupNamePattern, AbstractWatchEventListener listener) throws NacosException { + if (null == listener) { + return; + } + watchServiceChangeNotifier.deregisterWatchListener(serviceNamePattern, groupNamePattern, listener); + if (!watchServiceChangeNotifier.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/WatchServiceListHolder.java b/client/src/main/java/com/alibaba/nacos/client/naming/cache/WatchServiceListHolder.java new file mode 100644 index 00000000000..293849cabce --- /dev/null +++ b/client/src/main/java/com/alibaba/nacos/client/naming/cache/WatchServiceListHolder.java @@ -0,0 +1,140 @@ +/* + * 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.AbstractWatchNotifyRequest; +import com.alibaba.nacos.api.naming.remote.request.WatchNotifyChangeRequest; +import com.alibaba.nacos.api.naming.remote.request.WatchNotifyInitRequest; +import com.alibaba.nacos.api.naming.utils.NamingUtils; +import com.alibaba.nacos.client.env.NacosClientProperties; +import com.alibaba.nacos.client.naming.event.WatchNotifyEvent; +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 watch service list holder. + * + * @author tanyongquan + */ +public class WatchServiceListHolder { + + private String notifierEventScope; + + /** + * The contents of {@code patternMatchMap} are Map{pattern -> Set[matched services]}. + */ + private Map> patternMatchMap = new ConcurrentHashMap<>(); + + public WatchServiceListHolder(String notifierEventScope, NacosClientProperties properties) { + this.notifierEventScope = notifierEventScope; + } + + /** + * Publish service change event notify by watch. + * + * @param request watch notify request from Nacos server + */ + public void processServiceChange(AbstractWatchNotifyRequest request) { + if (request instanceof WatchNotifyInitRequest) { + WatchNotifyInitRequest watchNotifyInitRequest = (WatchNotifyInitRequest) request; + Set cacheService = patternMatchMap.computeIfAbsent(request.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 (cacheService.add(service)) { + NotifyCenter.publishEvent(WatchNotifyEvent.buildNotifyPatternAllListenersEvent(notifierEventScope, + service, request.getPattern(), Constants.WatchEventType.ADD_SERVICE)); + } + } + } else if (request instanceof WatchNotifyChangeRequest) { + WatchNotifyChangeRequest notifyChangeRequest = (WatchNotifyChangeRequest) 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.WatchEventType.ADD_SERVICE: + case Constants.WatchEventType.INSTANCE_CHANGED: + for (String pattern : matchedPattern) { + Set matchedServiceSet = patternMatchMap.get(pattern); + if (matchedServiceSet != null && matchedServiceSet.add(service)) { + NotifyCenter.publishEvent( + WatchNotifyEvent.buildNotifyPatternAllListenersEvent(notifierEventScope, + service, pattern, serviceChangeType)); + } + } + break; + case Constants.WatchEventType.DELETE_SERVICE: + for (String pattern : matchedPattern) { + Set matchedServiceSet = patternMatchMap.get(pattern); + if (matchedServiceSet != null && matchedServiceSet.remove(service)) { + NotifyCenter.publishEvent( + WatchNotifyEvent.buildNotifyPatternAllListenersEvent(notifierEventScope, + service, pattern, serviceChangeType)); + } + } + break; + default: + break; + } + } + } + + /** + * For a duplicate 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 duplicateWatchInit(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(WatchNotifyEvent.buildNotifyPatternSpecificListenerEvent(notifierEventScope, service, + pattern, uuid, Constants.WatchEventType.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/WatchNotifyEvent.java b/client/src/main/java/com/alibaba/nacos/client/naming/event/WatchNotifyEvent.java new file mode 100644 index 00000000000..7d738369678 --- /dev/null +++ b/client/src/main/java/com/alibaba/nacos/client/naming/event/WatchNotifyEvent.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 WatchNotifyEvent extends Event { + + private final String eventScope; + + private final Service changedService; + + private String pattern; + + private String uuid; + + private final String serviceChangedType; + + private WatchNotifyEvent(String eventScope, Service changedService, String pattern, String uuid, String serviceChangedType) { + this(eventScope, changedService, pattern, serviceChangedType); + this.uuid = uuid; + } + + private WatchNotifyEvent(String eventScope, Service changedService, String pattern, String serviceChangedType) { + this.eventScope = eventScope; + this.changedService = changedService; + this.serviceChangedType = serviceChangedType; + this.pattern = pattern; + } + + public static WatchNotifyEvent buildNotifyPatternSpecificListenerEvent(String eventScope, Service changedService, + String pattern, String uuid, String serviceChangedType) { + return new WatchNotifyEvent(eventScope, changedService, pattern, uuid, serviceChangedType); + } + + public static WatchNotifyEvent buildNotifyPatternAllListenersEvent(String eventScope, Service changedService, + String pattern, String serviceChangedType) { + return new WatchNotifyEvent(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/WatchServiceChangeNotifier.java b/client/src/main/java/com/alibaba/nacos/client/naming/event/WatchServiceChangeNotifier.java new file mode 100644 index 00000000000..e9e18415172 --- /dev/null +++ b/client/src/main/java/com/alibaba/nacos/client/naming/event/WatchServiceChangeNotifier.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.AbstractWatchEventListener; +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 watch event Listener callback. + * + * @author tanyongquan + */ +public class WatchServiceChangeNotifier extends Subscriber { + + private final String eventScope; + + @JustForTest + public WatchServiceChangeNotifier() { + this.eventScope = UUID.randomUUID().toString(); + } + + public WatchServiceChangeNotifier(String eventScope) { + this.eventScope = eventScope; + } + + /** + * pattern -> Set[Listener]. + */ + private final Map> watchListenerMap = new ConcurrentHashMap<>(); + + /** register watch listener. + * This listener responds to changes of the services (not the instance). + * + * @param serviceNamePattern service name pattern + * @param groupNamePattern group name pattern + * @param listener custom listener + */ + public void registerWatchListener(String serviceNamePattern, String groupNamePattern, AbstractWatchEventListener listener) { + String key = NamingUtils.getGroupedName(serviceNamePattern, groupNamePattern); + Set eventListeners = watchListenerMap.computeIfAbsent(key, keyInner -> new ConcurrentHashSet<>()); + eventListeners.add(listener); + } + + /** remove watch listener. + * + * @param serviceNamePattern service name pattern + * @param groupNamePattern group name pattern + */ + public void deregisterWatchListener(String serviceNamePattern, String groupNamePattern, AbstractWatchEventListener listener) { + String key = NamingUtils.getGroupedName(serviceNamePattern, groupNamePattern); + ConcurrentHashSet eventListeners = watchListenerMap.get(key); + if (eventListeners == null) { + return; + } + eventListeners.remove(listener); + if (CollectionUtils.isEmpty(eventListeners)) { + watchListenerMap.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 = watchListenerMap.get(key); + return CollectionUtils.isNotEmpty(eventListeners); + } + + /** + * receive watch notify (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(WatchNotifyEvent event) { + String uuid = event.getUuid(); + Collection listeners = watchListenerMap.get(event.getPattern()); + final com.alibaba.nacos.api.naming.listener.WatchNotifyEvent watchNotifyEvent = transferToWatchNotifyEvent(event); + for (AbstractWatchEventListener each : listeners) { + // notify all listener watch this pattern + if (StringUtils.isEmpty(uuid)) { + if (each.getExecutor() != null) { + each.getExecutor().execute(() -> each.onEvent(watchNotifyEvent)); + } else { + each.onEvent(watchNotifyEvent); + } + } 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(watchNotifyEvent)); + } else { + each.onEvent(watchNotifyEvent); + } + return; + } + } + } + + private com.alibaba.nacos.api.naming.listener.WatchNotifyEvent transferToWatchNotifyEvent( + WatchNotifyEvent watchNotifyEvent) { + return new com.alibaba.nacos.api.naming.listener.WatchNotifyEvent(watchNotifyEvent.getChangedService(), + watchNotifyEvent.getServiceChangedType()); + } + + @Override + public Class subscribeType() { + return WatchNotifyEvent.class; + } + + @Override + public boolean scopeMatches(WatchNotifyEvent 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 023621f4223..e6965a7af5c 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 @@ -182,6 +182,33 @@ ListView getServiceList(int pageNo, int pageSize, String groupName, Abst */ boolean isSubscribed(String serviceName, String groupName, String clusters) throws NacosException; + /** + * 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 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 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 33013f842ed..1ca9da81731 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.selector.AbstractSelector; import com.alibaba.nacos.client.env.NacosClientProperties; import com.alibaba.nacos.client.naming.cache.ServiceInfoHolder; +import com.alibaba.nacos.client.naming.cache.WatchServiceListHolder; import com.alibaba.nacos.client.naming.core.ServerListManager; import com.alibaba.nacos.client.naming.core.ServiceInfoUpdateService; import com.alibaba.nacos.client.naming.event.InstancesChangeNotifier; @@ -57,6 +58,8 @@ public class NamingClientProxyDelegate implements NamingClientProxy { private final ServiceInfoHolder serviceInfoHolder; + private final WatchServiceListHolder watchServiceListHolder; + private final NamingHttpClientProxy httpClientProxy; private final NamingGrpcClientProxy grpcClientProxy; @@ -65,18 +68,19 @@ public class NamingClientProxyDelegate implements NamingClientProxy { private ScheduledExecutorService executorService; - public NamingClientProxyDelegate(String namespace, ServiceInfoHolder serviceInfoHolder, NacosClientProperties properties, - InstancesChangeNotifier changeNotifier) throws NacosException { + public NamingClientProxyDelegate(String namespace, ServiceInfoHolder serviceInfoHolder, WatchServiceListHolder watchServiceListHolder, + NacosClientProperties properties, InstancesChangeNotifier changeNotifier) throws NacosException { this.serviceInfoUpdateService = new ServiceInfoUpdateService(properties, serviceInfoHolder, this, changeNotifier); this.serverListManager = new ServerListManager(properties, namespace); this.serviceInfoHolder = serviceInfoHolder; + this.watchServiceListHolder = watchServiceListHolder; this.securityProxy = new SecurityProxy(this.serverListManager.getServerList(), NamingHttpClientManager.getInstance().getNacosRestTemplate()); initSecurityProxy(properties); this.httpClientProxy = new NamingHttpClientProxy(namespace, securityProxy, serverListManager, properties); this.grpcClientProxy = new NamingGrpcClientProxy(namespace, securityProxy, serverListManager, properties, - serviceInfoHolder); + serviceInfoHolder, watchServiceListHolder); } private void initSecurityProxy(NacosClientProperties properties) { @@ -189,6 +193,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("[WATCH] serviceNamePattern:{}, groupNamePattern:{}", serviceNamePattern, groupNamePattern); + if (!watchServiceListHolder.containsPatternMatchCache(serviceNamePattern, groupNamePattern) + || !isFuzzyWatched(serviceNamePattern, groupNamePattern)) { + watchServiceListHolder.addPatternMatchCache(serviceNamePattern, groupNamePattern); + grpcClientProxy.fuzzyWatch(serviceNamePattern, groupNamePattern, ""); + } else { + watchServiceListHolder.duplicateWatchInit(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-WATCH] serviceNamePattern:{}, groupNamePattern:{} ", serviceNamePattern, groupNamePattern); + watchServiceListHolder.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 ad2846b2db2..a6443ce0192 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 @@ -30,10 +30,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.WatchServiceRequest; 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.WatchServiceResponse; import com.alibaba.nacos.api.naming.utils.NamingUtils; import com.alibaba.nacos.api.remote.RemoteConstants; import com.alibaba.nacos.api.remote.response.Response; @@ -42,6 +44,7 @@ import com.alibaba.nacos.api.selector.SelectorType; import com.alibaba.nacos.client.env.NacosClientProperties; import com.alibaba.nacos.client.naming.cache.ServiceInfoHolder; +import com.alibaba.nacos.client.naming.cache.WatchServiceListHolder; import com.alibaba.nacos.client.naming.event.ServerListChangedEvent; import com.alibaba.nacos.client.naming.remote.AbstractNamingClientProxy; import com.alibaba.nacos.client.naming.remote.gprc.redo.NamingGrpcRedoService; @@ -87,7 +90,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, + WatchServiceListHolder watchServiceListHolder) throws NacosException { super(securityProxy); this.namespaceId = namespaceId; this.uuid = UUID.randomUUID().toString(); @@ -100,13 +104,14 @@ public NamingGrpcClientProxy(String namespaceId, SecurityProxy securityProxy, Se RpcClientTlsConfig.properties(properties.asProperties())); this.redoService = new NamingGrpcRedoService(this); NAMING_LOGGER.info("Create naming rpc client for uuid->{}", uuid); - start(serverListFactory, serviceInfoHolder); + start(serverListFactory, serviceInfoHolder, watchServiceListHolder); } - private void start(ServerListFactory serverListFactory, ServiceInfoHolder serviceInfoHolder) throws NacosException { + private void start(ServerListFactory serverListFactory, ServiceInfoHolder serviceInfoHolder, + WatchServiceListHolder watchServiceListHolder) throws NacosException { rpcClient.serverListFactory(serverListFactory); rpcClient.registerConnectionListener(redoService); - rpcClient.registerServerRequestHandler(new NamingPushRequestHandler(serviceInfoHolder)); + rpcClient.registerServerRequestHandler(new NamingPushRequestHandler(serviceInfoHolder, watchServiceListHolder)); rpcClient.start(); NotifyCenter.registerSubscriber(this); } @@ -348,6 +353,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-WATCH] servicePattern:{}, groupPattern:{}", serviceNamePattern, groupNamePattern); + } + redoService.cacheWatcherForRedo(serviceNamePattern, groupNamePattern); + doFuzzyWatch(serviceNamePattern, groupNamePattern); + } + + /** + * Execute 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 { + WatchServiceRequest request = new WatchServiceRequest(namespaceId, serviceNamePattern, groupNamePattern, + NamingRemoteConstants.WATCH_SERVICE); + requestToServer(request, WatchServiceResponse.class); + redoService.watcherRegistered(serviceNamePattern, groupNamePattern); + } + + @Override + public boolean isFuzzyWatched(String serviceNamePattern, String groupNamePattern) { + return redoService.isWatcherRegistered(serviceNamePattern, groupNamePattern); + } + + @Override + public void cancelFuzzyWatch(String serviceNamePattern, String groupNamePattern) throws NacosException { + if (NAMING_LOGGER.isDebugEnabled()) { + NAMING_LOGGER + .debug("[GRPC-CANCEL-WATCH] serviceNamePattern:{}, groupNamePattern:{}", serviceNamePattern, groupNamePattern); + } + redoService.watcherDeregister(serviceNamePattern, groupNamePattern); + doCancelFuzzyWatch(serviceNamePattern, groupNamePattern); + } + + /** + * Send cancel watch request to server. + * + * @param serviceNamePattern service name pattern + * @param groupNamePattern group name pattern + * @throws NacosException nacos exception + */ + public void doCancelFuzzyWatch(String serviceNamePattern, String groupNamePattern) throws NacosException { + WatchServiceRequest request = new WatchServiceRequest(namespaceId, serviceNamePattern, groupNamePattern, + NamingRemoteConstants.CANCEL_WATCH_SERVICE); + requestToServer(request, WatchServiceResponse.class); + redoService.removeWatcherForRedo(serviceNamePattern, groupNamePattern); + } + @Override public boolean serverHealthy() { return rpcClient.isRunning(); 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 bf6e1917aaa..9cca7c7c52f 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.AbstractWatchNotifyRequest; 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.NotifyWatcherResponse; 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.WatchServiceListHolder; import com.alibaba.nacos.common.remote.client.ServerRequestHandler; /** @@ -32,8 +35,11 @@ public class NamingPushRequestHandler implements ServerRequestHandler { private final ServiceInfoHolder serviceInfoHolder; - public NamingPushRequestHandler(ServiceInfoHolder serviceInfoHolder) { + private final WatchServiceListHolder watchServiceListHolder; + + public NamingPushRequestHandler(ServiceInfoHolder serviceInfoHolder, WatchServiceListHolder watchServiceListHolder) { this.serviceInfoHolder = serviceInfoHolder; + this.watchServiceListHolder = watchServiceListHolder; } @Override @@ -42,6 +48,10 @@ public Response requestReply(Request request) { NotifySubscriberRequest notifyRequest = (NotifySubscriberRequest) request; serviceInfoHolder.processServiceInfo(notifyRequest.getServiceInfo()); return new NotifySubscriberResponse(); + } else if (request instanceof AbstractWatchNotifyRequest) { + AbstractWatchNotifyRequest notifyRequest = (AbstractWatchNotifyRequest) request; + watchServiceListHolder.processServiceChange(notifyRequest); + return new NotifyWatcherResponse(); } 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 1b2caf9a940..f2aeeb1d097 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 @@ -23,6 +23,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.WatcherRedoData; import com.alibaba.nacos.client.utils.LogUtils; import com.alibaba.nacos.common.executor.NameThreadFactory; import com.alibaba.nacos.common.remote.client.ConnectionEventListener; @@ -35,6 +36,7 @@ import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; /** * Naming client gprc redo service. @@ -58,6 +60,8 @@ public class NamingGrpcRedoService implements ConnectionEventListener { private final ConcurrentMap subscribes = new ConcurrentHashMap<>(); + private final ConcurrentMap watcher = new ConcurrentHashMap<>(); + private final ScheduledExecutorService redoExecutor; private volatile boolean connected = false; @@ -88,6 +92,9 @@ public void onDisConnect() { synchronized (subscribes) { subscribes.values().forEach(subscriberRedoData -> subscriberRedoData.setRegistered(false)); } + synchronized (watcher) { + watcher.values().forEach(watcherRedoData -> watcherRedoData.setRegistered(false)); + } LogUtils.NAMING_LOGGER.warn("mark to redo completed"); } @@ -303,6 +310,95 @@ public Set findSubscriberRedoData() { return result; } + /** + * Cache watcher for redo. + * + * @param serviceNamePattern service name pattern + * @param groupNamePattern group name pattern + */ + public void cacheWatcherForRedo(String serviceNamePattern, String groupNamePattern) { + String key = NamingUtils.getGroupedName(serviceNamePattern, groupNamePattern); + WatcherRedoData redoData = WatcherRedoData.build(serviceNamePattern, groupNamePattern); + synchronized (watcher) { + watcher.put(key, redoData); + } + } + + /** + * Watcher register successfully, mark registered status as {@code true}. + * + * @param serviceNamePattern service name pattern + * @param groupNamePattern group name pattern + */ + public void watcherRegistered(String serviceNamePattern, String groupNamePattern) { + String key = NamingUtils.getGroupedName(serviceNamePattern, groupNamePattern); + synchronized (watcher) { + WatcherRedoData redoData = watcher.get(key); + if (null != redoData) { + redoData.setRegistered(true); + } + } + } + + /** + * Watcher deregister, mark unregistering status as {@code true}. + * + * @param serviceNamePattern service name pattern + * @param groupNamePattern group name pattern + */ + public void watcherDeregister(String serviceNamePattern, String groupNamePattern) { + String key = NamingUtils.getGroupedName(serviceNamePattern, groupNamePattern); + synchronized (watcher) { + WatcherRedoData redoData = watcher.get(key); + if (null != redoData) { + redoData.setUnregistering(true); + redoData.setExpectedRegistered(false); + } + } + } + + /** + * Remove watcher for redo. + * + * @param serviceNamePattern service name pattern + * @param groupNamePattern group name pattern + */ + public void removeWatcherForRedo(String serviceNamePattern, String groupNamePattern) { + String key = NamingUtils.getGroupedName(serviceNamePattern, groupNamePattern); + synchronized (watcher) { + WatcherRedoData redoData = watcher.get(key); + if (null != redoData && !redoData.isExpectedRegistered()) { + watcher.remove(key); + } + } + } + + /** + * Judge 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 isWatcherRegistered(String serviceNamePattern, String groupNamePattern) { + String key = NamingUtils.getGroupedName(serviceNamePattern, groupNamePattern); + synchronized (watcher) { + WatcherRedoData redoData = watcher.get(key); + return null != redoData && redoData.isRegistered(); + } + } + + /** + * Find all watcher redo data which need do redo. + * + * @return set of {@code WatcherRedoData} need to do redo. + */ + public Set findWatcherRedoData() { + return watcher.values().stream() + .filter(WatcherRedoData::isNeedRedo) + .collect(Collectors.toSet()); + } + /** * get Cache service. * @return 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..355bc09ec40 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.WatcherRedoData; import com.alibaba.nacos.client.utils.LogUtils; import com.alibaba.nacos.common.task.AbstractExecuteTask; @@ -50,6 +51,7 @@ public void run() { try { redoForInstances(); redoForSubscribes(); + redoForWatchers(); } catch (Exception e) { LogUtils.NAMING_LOGGER.warn("Redo task run with unexpected exception: ", e); } @@ -139,6 +141,42 @@ private void redoForSubscribe(SubscriberRedoData redoData) throws NacosException } } + private void redoForWatchers() { + for (WatcherRedoData each : redoService.findWatcherRedoData()) { + try { + redoForWatcher(each); + } catch (NacosException e) { + LogUtils.NAMING_LOGGER.error("Redo watcher operation {} for pattern {}@@{}, uuid {} failed. ", each.getRedoType(), + each.getGroupName(), each.getServiceName(), each.get(), e); + } + } + } + + private void redoForWatcher(WatcherRedoData redoData) throws NacosException { + RedoData.RedoType redoType = redoData.getRedoType(); + String serviceNamePattern = redoData.getServiceName(); + String groupNamePattern = redoData.getGroupName(); + LogUtils.NAMING_LOGGER.info("Redo watcher operation {} for pattern {}@@{}", redoType, groupNamePattern, serviceNamePattern); + 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.removeWatcherForRedo(redoData.getServiceName(), redoData.getGroupName()); + break; + default: + } + } + private boolean isClientDisabled() { return !clientProxy.isEnable(); } diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/redo/data/WatcherRedoData.java b/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/redo/data/WatcherRedoData.java new file mode 100644 index 00000000000..4a91d3dddc4 --- /dev/null +++ b/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/redo/data/WatcherRedoData.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 Watcher. + * + * @author tanyongquan + */ +public class WatcherRedoData extends RedoData { + + private WatcherRedoData(String serviceNamePattern, String groupNamePattern) { + super(serviceNamePattern, groupNamePattern); + } + + public static WatcherRedoData build(String serviceNamePattern, String groupNamePattern) { + return new WatcherRedoData(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 18e1557c990..605cc56fe23 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 @@ -342,6 +342,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 watch services by UDP, please use gRPC replaced."); + } + + @Override + public void cancelFuzzyWatch(String serviceNamePattern, String groupNamePattern) throws NacosException { + throw new UnsupportedOperationException("Do not support watch service by UDP, please use gRPC replaced."); + } + + @Override + public boolean isFuzzyWatched(String serviceNamePattern, String groupNamePattern) { + throw new UnsupportedOperationException("Do not support 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 9e01de67eb0..4e84e144ac7 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 @@ -347,6 +347,18 @@ {"name":"getServiceName","parameterTypes":[] } ] }, +{ + "name":"com.alibaba.nacos.api.naming.remote.request.AbstractWatchNotifyRequest", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "queryAllDeclaredConstructors":true, + "methods":[ + {"name":"","parameterTypes":[] }, + {"name":"getModule","parameterTypes":[] }, + {"name":"getServiceChangedType","parameterTypes":[] }, + {"name":"getPattern","parameterTypes":[] } + ] +}, { "name":"com.alibaba.nacos.api.naming.remote.request.InstanceRequest", "allDeclaredFields":true, 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 962e085a2b8..73c513d432d 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 @@ -173,6 +173,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 15ae5c1b9ae..2c5ad2565f1 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.NoneSelector; import com.alibaba.nacos.client.env.NacosClientProperties; import com.alibaba.nacos.client.naming.cache.ServiceInfoHolder; +import com.alibaba.nacos.client.naming.cache.WatchServiceListHolder; 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; @@ -49,11 +50,13 @@ public class NamingClientProxyDelegateTest { public void testRegisterServiceByGrpc() throws NacosException, NoSuchFieldException, IllegalAccessException { String ns = "ns1"; ServiceInfoHolder holder = Mockito.mock(ServiceInfoHolder.class); + WatchServiceListHolder watchServiceListHolder = Mockito.mock(WatchServiceListHolder.class); Properties props = new Properties(); props.setProperty("serverAddr", "localhost"); final NacosClientProperties nacosClientProperties = NacosClientProperties.PROTOTYPE.derive(props); InstancesChangeNotifier notifier = new InstancesChangeNotifier(); - NamingClientProxyDelegate delegate = new NamingClientProxyDelegate(ns, holder, nacosClientProperties, notifier); + NamingClientProxyDelegate delegate = new NamingClientProxyDelegate(ns, holder, watchServiceListHolder, + nacosClientProperties, notifier); NamingGrpcClientProxy mockGrpcClient = Mockito.mock(NamingGrpcClientProxy.class); Field grpcClientProxyField = NamingClientProxyDelegate.class.getDeclaredField("grpcClientProxy"); grpcClientProxyField.setAccessible(true); @@ -75,12 +78,13 @@ public void testRegisterServiceByGrpc() throws NacosException, NoSuchFieldExcept public void testBatchRegisterServiceByGrpc() throws NacosException, NoSuchFieldException, IllegalAccessException { String ns = "ns1"; ServiceInfoHolder holder = Mockito.mock(ServiceInfoHolder.class); + WatchServiceListHolder watchServiceListHolder = Mockito.mock(WatchServiceListHolder.class); Properties props = new Properties(); props.setProperty("serverAddr", "localhost"); InstancesChangeNotifier notifier = new InstancesChangeNotifier(); - final NacosClientProperties nacosClientProperties = NacosClientProperties.PROTOTYPE.derive(props); - NamingClientProxyDelegate delegate = new NamingClientProxyDelegate(ns, holder, nacosClientProperties, notifier); + NamingClientProxyDelegate delegate = new NamingClientProxyDelegate(ns, holder, watchServiceListHolder, + nacosClientProperties, notifier); NamingGrpcClientProxy mockGrpcClient = Mockito.mock(NamingGrpcClientProxy.class); Field grpcClientProxyField = NamingClientProxyDelegate.class.getDeclaredField("grpcClientProxy"); grpcClientProxyField.setAccessible(true); @@ -103,12 +107,14 @@ public void testBatchRegisterServiceByGrpc() throws NacosException, NoSuchFieldE public void testRegisterServiceByHttp() throws NacosException, NoSuchFieldException, IllegalAccessException { String ns = "ns1"; ServiceInfoHolder holder = Mockito.mock(ServiceInfoHolder.class); + WatchServiceListHolder watchServiceListHolder = Mockito.mock(WatchServiceListHolder.class); Properties props = new Properties(); props.setProperty("serverAddr", "localhost"); InstancesChangeNotifier notifier = new InstancesChangeNotifier(); - + final NacosClientProperties nacosClientProperties = NacosClientProperties.PROTOTYPE.derive(props); - NamingClientProxyDelegate delegate = new NamingClientProxyDelegate(ns, holder, nacosClientProperties, notifier); + NamingClientProxyDelegate delegate = new NamingClientProxyDelegate(ns, holder, watchServiceListHolder, + nacosClientProperties, notifier); NamingHttpClientProxy mockHttpClient = Mockito.mock(NamingHttpClientProxy.class); Field mockHttpClientField = NamingClientProxyDelegate.class.getDeclaredField("httpClientProxy"); mockHttpClientField.setAccessible(true); @@ -131,12 +137,14 @@ public void testRegisterServiceByHttp() throws NacosException, NoSuchFieldExcept public void testDeregisterServiceGrpc() throws NacosException, NoSuchFieldException, IllegalAccessException { String ns = "ns1"; ServiceInfoHolder holder = Mockito.mock(ServiceInfoHolder.class); + WatchServiceListHolder watchServiceListHolder = Mockito.mock(WatchServiceListHolder.class); Properties props = new Properties(); props.setProperty("serverAddr", "localhost"); InstancesChangeNotifier notifier = new InstancesChangeNotifier(); - + final NacosClientProperties nacosClientProperties = NacosClientProperties.PROTOTYPE.derive(props); - NamingClientProxyDelegate delegate = new NamingClientProxyDelegate(ns, holder, nacosClientProperties, notifier); + NamingClientProxyDelegate delegate = new NamingClientProxyDelegate(ns, holder, watchServiceListHolder, + nacosClientProperties, notifier); NamingGrpcClientProxy mockGrpcClient = Mockito.mock(NamingGrpcClientProxy.class); Field grpcClientProxyField = NamingClientProxyDelegate.class.getDeclaredField("grpcClientProxy"); grpcClientProxyField.setAccessible(true); @@ -159,12 +167,14 @@ public void testDeregisterServiceGrpc() throws NacosException, NoSuchFieldExcept public void testDeregisterServiceHttp() throws NacosException, NoSuchFieldException, IllegalAccessException { String ns = "ns1"; ServiceInfoHolder holder = Mockito.mock(ServiceInfoHolder.class); + WatchServiceListHolder watchServiceListHolder = Mockito.mock(WatchServiceListHolder.class); Properties props = new Properties(); props.setProperty("serverAddr", "localhost"); InstancesChangeNotifier notifier = new InstancesChangeNotifier(); - + final NacosClientProperties nacosClientProperties = NacosClientProperties.PROTOTYPE.derive(props); - NamingClientProxyDelegate delegate = new NamingClientProxyDelegate(ns, holder, nacosClientProperties, notifier); + NamingClientProxyDelegate delegate = new NamingClientProxyDelegate(ns, holder, watchServiceListHolder, + nacosClientProperties, notifier); NamingHttpClientProxy mockHttpClient = Mockito.mock(NamingHttpClientProxy.class); Field mockHttpClientField = NamingClientProxyDelegate.class.getDeclaredField("httpClientProxy"); mockHttpClientField.setAccessible(true); @@ -187,12 +197,14 @@ public void testDeregisterServiceHttp() throws NacosException, NoSuchFieldExcept public void testUpdateInstance() throws NacosException { String ns = "ns1"; ServiceInfoHolder holder = Mockito.mock(ServiceInfoHolder.class); + WatchServiceListHolder watchServiceListHolder = Mockito.mock(WatchServiceListHolder.class); Properties props = new Properties(); props.setProperty("serverAddr", "localhost"); InstancesChangeNotifier notifier = new InstancesChangeNotifier(); - + final NacosClientProperties nacosClientProperties = NacosClientProperties.PROTOTYPE.derive(props); - NamingClientProxyDelegate delegate = new NamingClientProxyDelegate(ns, holder, nacosClientProperties, notifier); + NamingClientProxyDelegate delegate = new NamingClientProxyDelegate(ns, holder, watchServiceListHolder, + nacosClientProperties, notifier); String serviceName = "service1"; String groupName = "group1"; Instance instance = new Instance(); @@ -207,12 +219,14 @@ public void testUpdateInstance() throws NacosException { public void testQueryInstancesOfService() throws NacosException, IllegalAccessException, NoSuchFieldException { String ns = "ns1"; ServiceInfoHolder holder = Mockito.mock(ServiceInfoHolder.class); + WatchServiceListHolder watchServiceListHolder = Mockito.mock(WatchServiceListHolder.class); Properties props = new Properties(); props.setProperty("serverAddr", "localhost"); InstancesChangeNotifier notifier = new InstancesChangeNotifier(); - + final NacosClientProperties nacosClientProperties = NacosClientProperties.PROTOTYPE.derive(props); - NamingClientProxyDelegate delegate = new NamingClientProxyDelegate(ns, holder, nacosClientProperties, notifier); + NamingClientProxyDelegate delegate = new NamingClientProxyDelegate(ns, holder, watchServiceListHolder, + nacosClientProperties, notifier); NamingGrpcClientProxy mockGrpcClient = Mockito.mock(NamingGrpcClientProxy.class); Field grpcClientProxyField = NamingClientProxyDelegate.class.getDeclaredField("grpcClientProxy"); grpcClientProxyField.setAccessible(true); @@ -229,12 +243,14 @@ public void testQueryInstancesOfService() throws NacosException, IllegalAccessEx public void testQueryService() throws NacosException { String ns = "ns1"; ServiceInfoHolder holder = Mockito.mock(ServiceInfoHolder.class); + WatchServiceListHolder watchServiceListHolder = Mockito.mock(WatchServiceListHolder.class); Properties props = new Properties(); props.setProperty("serverAddr", "localhost"); InstancesChangeNotifier notifier = new InstancesChangeNotifier(); - + final NacosClientProperties nacosClientProperties = NacosClientProperties.PROTOTYPE.derive(props); - NamingClientProxyDelegate delegate = new NamingClientProxyDelegate(ns, holder, nacosClientProperties, notifier); + NamingClientProxyDelegate delegate = new NamingClientProxyDelegate(ns, holder, watchServiceListHolder, + nacosClientProperties, notifier); Service service = delegate.queryService("a", "b"); Assert.assertNull(service); } @@ -243,12 +259,14 @@ public void testQueryService() throws NacosException { public void testCreateService() throws NacosException { String ns = "ns1"; ServiceInfoHolder holder = Mockito.mock(ServiceInfoHolder.class); + WatchServiceListHolder watchServiceListHolder = Mockito.mock(WatchServiceListHolder.class); Properties props = new Properties(); props.setProperty("serverAddr", "localhost"); InstancesChangeNotifier notifier = new InstancesChangeNotifier(); - + final NacosClientProperties nacosClientProperties = NacosClientProperties.PROTOTYPE.derive(props); - NamingClientProxyDelegate delegate = new NamingClientProxyDelegate(ns, holder, nacosClientProperties, notifier); + NamingClientProxyDelegate delegate = new NamingClientProxyDelegate(ns, holder, watchServiceListHolder, + nacosClientProperties, notifier); Service service = new Service(); try { delegate.createService(service, new NoneSelector()); @@ -261,12 +279,14 @@ public void testCreateService() throws NacosException { public void testDeleteService() throws NacosException { String ns = "ns1"; ServiceInfoHolder holder = Mockito.mock(ServiceInfoHolder.class); + WatchServiceListHolder watchServiceListHolder = Mockito.mock(WatchServiceListHolder.class); Properties props = new Properties(); props.setProperty("serverAddr", "localhost"); InstancesChangeNotifier notifier = new InstancesChangeNotifier(); - + final NacosClientProperties nacosClientProperties = NacosClientProperties.PROTOTYPE.derive(props); - NamingClientProxyDelegate delegate = new NamingClientProxyDelegate(ns, holder, nacosClientProperties, notifier); + NamingClientProxyDelegate delegate = new NamingClientProxyDelegate(ns, holder, watchServiceListHolder, + nacosClientProperties, notifier); Assert.assertFalse(delegate.deleteService("service", "group1")); } @@ -274,12 +294,14 @@ public void testDeleteService() throws NacosException { public void testUpdateService() throws NacosException { String ns = "ns1"; ServiceInfoHolder holder = Mockito.mock(ServiceInfoHolder.class); + WatchServiceListHolder watchServiceListHolder = Mockito.mock(WatchServiceListHolder.class); Properties props = new Properties(); props.setProperty("serverAddr", "localhost"); InstancesChangeNotifier notifier = new InstancesChangeNotifier(); - + final NacosClientProperties nacosClientProperties = NacosClientProperties.PROTOTYPE.derive(props); - NamingClientProxyDelegate delegate = new NamingClientProxyDelegate(ns, holder, nacosClientProperties, notifier); + NamingClientProxyDelegate delegate = new NamingClientProxyDelegate(ns, holder, watchServiceListHolder, + nacosClientProperties, notifier); Service service = new Service(); try { delegate.updateService(service, new ExpressionSelector()); @@ -292,12 +314,14 @@ public void testUpdateService() throws NacosException { public void testGetServiceList() throws NacosException, NoSuchFieldException, IllegalAccessException { String ns = "ns1"; ServiceInfoHolder holder = Mockito.mock(ServiceInfoHolder.class); + WatchServiceListHolder watchServiceListHolder = Mockito.mock(WatchServiceListHolder.class); Properties props = new Properties(); props.setProperty("serverAddr", "localhost"); InstancesChangeNotifier notifier = new InstancesChangeNotifier(); - + final NacosClientProperties nacosClientProperties = NacosClientProperties.PROTOTYPE.derive(props); - NamingClientProxyDelegate delegate = new NamingClientProxyDelegate(ns, holder, nacosClientProperties, notifier); + NamingClientProxyDelegate delegate = new NamingClientProxyDelegate(ns, holder, watchServiceListHolder, + nacosClientProperties, notifier); NamingGrpcClientProxy mockGrpcClient = Mockito.mock(NamingGrpcClientProxy.class); Field grpcClientProxyField = NamingClientProxyDelegate.class.getDeclaredField("grpcClientProxy"); grpcClientProxyField.setAccessible(true); @@ -316,12 +340,14 @@ public void testGetServiceList() throws NacosException, NoSuchFieldException, Il public void testSubscribe() throws NacosException, NoSuchFieldException, IllegalAccessException { String ns = "ns1"; ServiceInfoHolder holder = Mockito.mock(ServiceInfoHolder.class); + WatchServiceListHolder watchServiceListHolder = Mockito.mock(WatchServiceListHolder.class); Properties props = new Properties(); props.setProperty("serverAddr", "localhost"); InstancesChangeNotifier notifier = new InstancesChangeNotifier(); - + final NacosClientProperties nacosClientProperties = NacosClientProperties.PROTOTYPE.derive(props); - NamingClientProxyDelegate delegate = new NamingClientProxyDelegate(ns, holder, nacosClientProperties, notifier); + NamingClientProxyDelegate delegate = new NamingClientProxyDelegate(ns, holder, watchServiceListHolder, + nacosClientProperties, notifier); NamingGrpcClientProxy mockGrpcClient = Mockito.mock(NamingGrpcClientProxy.class); Field grpcClientProxyField = NamingClientProxyDelegate.class.getDeclaredField("grpcClientProxy"); grpcClientProxyField.setAccessible(true); @@ -347,12 +373,14 @@ public void testSubscribe() throws NacosException, NoSuchFieldException, Illegal public void testUnsubscribe() throws NacosException, IllegalAccessException, NoSuchFieldException { String ns = "ns1"; ServiceInfoHolder holder = Mockito.mock(ServiceInfoHolder.class); + WatchServiceListHolder watchServiceListHolder = Mockito.mock(WatchServiceListHolder.class); Properties props = new Properties(); props.setProperty("serverAddr", "localhost"); InstancesChangeNotifier notifier = new InstancesChangeNotifier(); - + final NacosClientProperties nacosClientProperties = NacosClientProperties.PROTOTYPE.derive(props); - NamingClientProxyDelegate delegate = new NamingClientProxyDelegate(ns, holder, nacosClientProperties, notifier); + NamingClientProxyDelegate delegate = new NamingClientProxyDelegate(ns, holder, watchServiceListHolder, + nacosClientProperties, notifier); NamingGrpcClientProxy mockGrpcClient = Mockito.mock(NamingGrpcClientProxy.class); Field grpcClientProxyField = NamingClientProxyDelegate.class.getDeclaredField("grpcClientProxy"); grpcClientProxyField.setAccessible(true); @@ -370,12 +398,14 @@ public void testUnsubscribe() throws NacosException, IllegalAccessException, NoS public void testServerHealthy() throws IllegalAccessException, NacosException, NoSuchFieldException { String ns = "ns1"; ServiceInfoHolder holder = Mockito.mock(ServiceInfoHolder.class); + WatchServiceListHolder watchServiceListHolder = Mockito.mock(WatchServiceListHolder.class); Properties props = new Properties(); props.setProperty("serverAddr", "localhost"); InstancesChangeNotifier notifier = new InstancesChangeNotifier(); - + final NacosClientProperties nacosClientProperties = NacosClientProperties.PROTOTYPE.derive(props); - NamingClientProxyDelegate delegate = new NamingClientProxyDelegate(ns, holder, nacosClientProperties, notifier); + NamingClientProxyDelegate delegate = new NamingClientProxyDelegate(ns, holder, watchServiceListHolder, + nacosClientProperties, notifier); NamingGrpcClientProxy mockGrpcClient = Mockito.mock(NamingGrpcClientProxy.class); Field grpcClientProxyField = NamingClientProxyDelegate.class.getDeclaredField("grpcClientProxy"); grpcClientProxyField.setAccessible(true); @@ -389,12 +419,14 @@ public void testServerHealthy() throws IllegalAccessException, NacosException, N public void testShutdown() throws NacosException, IllegalAccessException, NoSuchFieldException { String ns = "ns1"; ServiceInfoHolder holder = Mockito.mock(ServiceInfoHolder.class); + WatchServiceListHolder watchServiceListHolder = Mockito.mock(WatchServiceListHolder.class); Properties props = new Properties(); props.setProperty("serverAddr", "localhost"); InstancesChangeNotifier notifier = new InstancesChangeNotifier(); - + final NacosClientProperties nacosClientProperties = NacosClientProperties.PROTOTYPE.derive(props); - NamingClientProxyDelegate delegate = new NamingClientProxyDelegate(ns, holder, nacosClientProperties, notifier); + NamingClientProxyDelegate delegate = new NamingClientProxyDelegate(ns, holder, watchServiceListHolder, + nacosClientProperties, notifier); NamingGrpcClientProxy mockGrpcClient = Mockito.mock(NamingGrpcClientProxy.class); Field grpcClientProxyField = NamingClientProxyDelegate.class.getDeclaredField("grpcClientProxy"); grpcClientProxyField.setAccessible(true); 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 cc6a424dfb4..7890443dd22 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 @@ -43,6 +43,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.WatchServiceListHolder; import com.alibaba.nacos.client.naming.event.ServerListChangedEvent; import com.alibaba.nacos.client.naming.remote.gprc.redo.NamingGrpcRedoService; import com.alibaba.nacos.client.security.SecurityProxy; @@ -112,6 +113,9 @@ public class NamingGrpcClientProxyTest { @Mock private ServiceInfoHolder holder; + @Mock + private WatchServiceListHolder watchServiceListHolder; + @Mock private RpcClient rpcClient; @@ -138,7 +142,8 @@ public void setUp() throws NacosException, NoSuchFieldException, IllegalAccessEx 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, + watchServiceListHolder); Field uuidField = NamingGrpcClientProxy.class.getDeclaredField("uuid"); uuidField.setAccessible(true); @@ -501,7 +506,7 @@ public void close() { rpcClient.set(client, rpc); rpc.serverListFactory(factory); - rpc.registerServerRequestHandler(new NamingPushRequestHandler(holder)); + rpc.registerServerRequestHandler(new NamingPushRequestHandler(holder, watchServiceListHolder)); Field listenerField = NamingGrpcClientProxy.class.getDeclaredField("redoService"); listenerField.setAccessible(true); NamingGrpcRedoService listener = (NamingGrpcRedoService) listenerField.get(client); @@ -536,7 +541,8 @@ public void close() { @Test public 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, + watchServiceListHolder); 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 2e70dd24c38..c31da262ed7 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 @@ -24,6 +24,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.cache.WatchServiceListHolder; import org.junit.Assert; import org.junit.Test; @@ -37,7 +38,8 @@ public class NamingPushRequestHandlerTest { public void testRequestReply() { //given ServiceInfoHolder holder = mock(ServiceInfoHolder.class); - NamingPushRequestHandler handler = new NamingPushRequestHandler(holder); + WatchServiceListHolder watchServiceListHolder = mock(WatchServiceListHolder.class); + NamingPushRequestHandler handler = new NamingPushRequestHandler(holder, watchServiceListHolder); ServiceInfo info = new ServiceInfo("name", "cluster1"); Request req = NotifySubscriberRequest.buildNotifySubscriberRequest(info); //when From cafdb9184ed15dfa5b70f1949ab57f14b03a00b9 Mon Sep 17 00:00:00 2001 From: Chionanthus <33491015+Chionanthus@users.noreply.github.com> Date: Sun, 8 Oct 2023 22:29:43 -0500 Subject: [PATCH 2/4] add test prepare (#11188) --- .../alibaba/nacos/client/naming/NacosNamingServiceTest.java | 5 +++++ 1 file changed, 5 insertions(+) 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 e42323893b6..93182c83f49 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 @@ -830,6 +830,11 @@ public void testUnSubscribe4() throws NacosException { verify(proxy, times(1)).unsubscribe(serviceName, groupName, "cluster1,cluster2"); } + @Test + public void testFuzzyWatch() throws NacosException { + // TODO + } + @Test public void testGetServicesOfServer1() throws NacosException { //given From 4be6f3efbb3d2a4fc902719769cd29250d521ede Mon Sep 17 00:00:00 2001 From: Chionanthus <33491015+Chionanthus@users.noreply.github.com> Date: Thu, 19 Oct 2023 22:32:02 -0500 Subject: [PATCH 3/4] [ISSUE #10380] Support for fuzzy watch capability for Nacos registration center. (#11200) * add nacos fuzzy watch in client side * add fuzzy watch request handler, client operation and watch event * add fuzzy watch index manager support, add service change event in nacos naming module * add nacos server side notify fuzzy watch logic * add nacos naming fuzzy watch example * fix format * Optimizing Abstract Classes * alter class name and fix bug * remove redundant file import by repeat merge * fix bug --- .../alibaba/nacos/api/common/Constants.java | 10 +- .../nacos/api/naming/NamingService.java | 32 +-- ...a => AbstractFuzzyWatchEventListener.java} | 4 +- ...hListener.java => FuzzyWatchListener.java} | 6 +- ...yEvent.java => FuzzyWatchNotifyEvent.java} | 8 +- .../nacos/api/naming/pojo/Service.java | 6 + .../naming/remote/NamingRemoteConstants.java | 4 +- ...a => AbstractFuzzyWatchNotifyRequest.java} | 19 +- ...ava => FuzzyWatchNotifyChangeRequest.java} | 10 +- ....java => FuzzyWatchNotifyInitRequest.java} | 29 ++- ...iceRequest.java => FuzzyWatchRequest.java} | 8 +- ...eResponse.java => FuzzyWatchResponse.java} | 16 +- ...e.java => NotifyFuzzyWatcherResponse.java} | 4 +- .../nacos/api/naming/utils/NamingUtils.java | 18 +- .../com.alibaba.nacos.api.remote.Payload | 10 +- .../client/naming/NacosNamingService.java | 49 ++-- ....java => FuzzyWatchServiceListHolder.java} | 58 ++--- ...yEvent.java => FuzzyWatchNotifyEvent.java} | 14 +- ...ifier.java => ServicesChangeNotifier.java} | 64 +++--- .../naming/remote/NamingClientProxy.java | 6 +- .../remote/NamingClientProxyDelegate.java | 24 +- .../remote/gprc/NamingGrpcClientProxy.java | 45 ++-- .../remote/gprc/NamingPushRequestHandler.java | 20 +- .../gprc/redo/NamingGrpcRedoService.java | 71 +++--- .../remote/gprc/redo/RedoScheduledTask.java | 20 +- ...edoData.java => FuzzyWatcherRedoData.java} | 10 +- .../remote/http/NamingHttpClientProxy.java | 6 +- .../nacos-client/reflect-config.json | 27 ++- .../remote/NamingClientProxyDelegateTest.java | 66 +++--- .../gprc/NamingGrpcClientProxyTest.java | 10 +- .../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 | 27 +++ .../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 | 2 +- .../v2/NamingSubscriberServiceV2ImplTest.java | 3 +- .../push/v2/task/FixturePushExecutor.java | 17 ++ 58 files changed, 1665 insertions(+), 333 deletions(-) rename api/src/main/java/com/alibaba/nacos/api/naming/listener/{AbstractWatchEventListener.java => AbstractFuzzyWatchEventListener.java} (84%) rename api/src/main/java/com/alibaba/nacos/api/naming/listener/{WatchListener.java => FuzzyWatchListener.java} (87%) rename api/src/main/java/com/alibaba/nacos/api/naming/listener/{WatchNotifyEvent.java => FuzzyWatchNotifyEvent.java} (86%) rename api/src/main/java/com/alibaba/nacos/api/naming/remote/request/{AbstractWatchNotifyRequest.java => AbstractFuzzyWatchNotifyRequest.java} (74%) rename api/src/main/java/com/alibaba/nacos/api/naming/remote/request/{WatchNotifyChangeRequest.java => FuzzyWatchNotifyChangeRequest.java} (78%) rename api/src/main/java/com/alibaba/nacos/api/naming/remote/request/{WatchNotifyInitRequest.java => FuzzyWatchNotifyInitRequest.java} (50%) rename api/src/main/java/com/alibaba/nacos/api/naming/remote/request/{WatchServiceRequest.java => FuzzyWatchRequest.java} (79%) rename api/src/main/java/com/alibaba/nacos/api/naming/remote/response/{WatchServiceResponse.java => FuzzyWatchResponse.java} (74%) rename api/src/main/java/com/alibaba/nacos/api/naming/remote/response/{NotifyWatcherResponse.java => NotifyFuzzyWatcherResponse.java} (88%) rename client/src/main/java/com/alibaba/nacos/client/naming/cache/{WatchServiceListHolder.java => FuzzyWatchServiceListHolder.java} (63%) rename client/src/main/java/com/alibaba/nacos/client/naming/event/{WatchNotifyEvent.java => FuzzyWatchNotifyEvent.java} (72%) rename client/src/main/java/com/alibaba/nacos/client/naming/event/{WatchServiceChangeNotifier.java => ServicesChangeNotifier.java} (59%) rename client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/redo/data/{WatcherRedoData.java => FuzzyWatcherRedoData.java} (68%) 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 87e424ee9d6..017bbb7dccd 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 @@ -241,10 +241,10 @@ public static class Naming { } /** - * The constants in Watch Pattern Match Rule directory. + * The constants in fuzzy watch pattern match rule directory. */ - public static class WatchMatchRule { - + public static class FuzzyWatchMatchRule { + public static final String MATCH_ALL = "MATCH_ALL"; public static final String MATCH_PREFIX = "MATCH_PREFIX"; @@ -252,9 +252,9 @@ public static class WatchMatchRule { } /** - * The constants in Watch Notify Event directory. + * The constants in fuzzy watch event type directory. */ - public static class WatchEventType { + public static class ServiceChangedType { public static final String ADD_SERVICE = "ADD_SERVICE"; 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 fe1f6b266f2..fba95bb6e16 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.AbstractWatchEventListener; +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; @@ -534,44 +534,44 @@ void unsubscribe(String serviceName, String groupName, List clusters, Ev throws NacosException; /** - * Watch a range of services by match rule to receive notify events of matched services alteration. + * Watch a range of services by rule to receive notify events of matched services alteration. * - * @param fixedGroupName fixed group name for watch + * @param fixedGroupName fixed group name for fuzzy watch * @param listener event listener * @throws NacosException nacos exception */ - void fuzzyWatch(String fixedGroupName, AbstractWatchEventListener listener) throws NacosException; + void fuzzyWatch(String fixedGroupName, AbstractFuzzyWatchEventListener listener) throws NacosException; /** - * Watch a range of services by match rule to receive notify events of matched services alteration. + * Watch a range of services by rule to receive notify events of matched services alteration. * - * @param serviceNamePattern service name pattern for watch - * @param fixedGroupName fixed group name for watch + * @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, - AbstractWatchEventListener listener) throws NacosException; + AbstractFuzzyWatchEventListener listener) throws NacosException; /** - * Cancel watch event listener of a pattern. + * Cancel fuzzy watch, and remove event listener of a pattern. * - * @param fixedGroupName fixed group name for watch + * @param fixedGroupName fixed group name for fuzzy watch * @param listener event listener * @throws NacosException nacos exception */ - void cancelFuzzyWatch(String fixedGroupName, AbstractWatchEventListener listener) throws NacosException; + void cancelFuzzyWatch(String fixedGroupName, AbstractFuzzyWatchEventListener listener) throws NacosException; /** - * Cancel watch event listener of a pattern. + * Cancel fuzzy watch, and remove event listener of a pattern. * - * @param serviceNamePattern service name pattern for watch - * @param fixedGroupName fixed group name for watch + * @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, AbstractWatchEventListener listener) throws NacosException; - + void cancelFuzzyWatch(String serviceNamePattern, String fixedGroupName, AbstractFuzzyWatchEventListener listener) throws NacosException; + /** * Get all service names from server. * diff --git a/api/src/main/java/com/alibaba/nacos/api/naming/listener/AbstractWatchEventListener.java b/api/src/main/java/com/alibaba/nacos/api/naming/listener/AbstractFuzzyWatchEventListener.java similarity index 84% rename from api/src/main/java/com/alibaba/nacos/api/naming/listener/AbstractWatchEventListener.java rename to api/src/main/java/com/alibaba/nacos/api/naming/listener/AbstractFuzzyWatchEventListener.java index 1b36de6a2d0..e4c23dbe43b 100644 --- a/api/src/main/java/com/alibaba/nacos/api/naming/listener/AbstractWatchEventListener.java +++ b/api/src/main/java/com/alibaba/nacos/api/naming/listener/AbstractFuzzyWatchEventListener.java @@ -19,11 +19,11 @@ import java.util.concurrent.Executor; /** - * Abstract watch event listener, to support handle event by user custom executor. + * Abstract fuzzy watch event listener, to support handle event by user custom executor. * * @author tanyongquan */ -public abstract class AbstractWatchEventListener implements WatchListener { +public abstract class AbstractFuzzyWatchEventListener implements FuzzyWatchListener { String uuid; diff --git a/api/src/main/java/com/alibaba/nacos/api/naming/listener/WatchListener.java b/api/src/main/java/com/alibaba/nacos/api/naming/listener/FuzzyWatchListener.java similarity index 87% rename from api/src/main/java/com/alibaba/nacos/api/naming/listener/WatchListener.java rename to api/src/main/java/com/alibaba/nacos/api/naming/listener/FuzzyWatchListener.java index fcf384ddcca..ffd2090a799 100644 --- a/api/src/main/java/com/alibaba/nacos/api/naming/listener/WatchListener.java +++ b/api/src/main/java/com/alibaba/nacos/api/naming/listener/FuzzyWatchListener.java @@ -17,16 +17,16 @@ package com.alibaba.nacos.api.naming.listener; /** - * Watch Listener. + * Fuzzy Watch Listener. * * @author tanyongquan */ -public interface WatchListener { +public interface FuzzyWatchListener { /** * callback event. * * @param event event */ - void onEvent(WatchNotifyEvent event); + void onEvent(FuzzyWatchNotifyEvent event); } diff --git a/api/src/main/java/com/alibaba/nacos/api/naming/listener/WatchNotifyEvent.java b/api/src/main/java/com/alibaba/nacos/api/naming/listener/FuzzyWatchNotifyEvent.java similarity index 86% rename from api/src/main/java/com/alibaba/nacos/api/naming/listener/WatchNotifyEvent.java rename to api/src/main/java/com/alibaba/nacos/api/naming/listener/FuzzyWatchNotifyEvent.java index 65b9e79bd31..29f07309389 100644 --- a/api/src/main/java/com/alibaba/nacos/api/naming/listener/WatchNotifyEvent.java +++ b/api/src/main/java/com/alibaba/nacos/api/naming/listener/FuzzyWatchNotifyEvent.java @@ -19,20 +19,20 @@ import com.alibaba.nacos.api.naming.pojo.Service; /** - * Watch Notify Event. + * Fuzzy Watch Notify Event. * * @author tanyongquan */ -public class WatchNotifyEvent implements Event { +public class FuzzyWatchNotifyEvent implements Event { private Service service; private String changeType; - public WatchNotifyEvent() { + public FuzzyWatchNotifyEvent() { } - public WatchNotifyEvent(Service service, String changeType) { + public FuzzyWatchNotifyEvent(Service service, String changeType) { this.service = service; 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 0e9bfa166be..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,6 +16,8 @@ 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; @@ -101,6 +103,10 @@ public void setGroupName(String groupName) { this.groupName = groupName; } + public String getGroupedServiceName() { + return NamingUtils.getGroupedName(name, groupName); + } + public Map getMetadata() { return metadata; } 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 6d5e4b91b02..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,9 +30,9 @@ public class NamingRemoteConstants { public static final String DE_REGISTER_INSTANCE = "deregisterInstance"; - public static final String WATCH_SERVICE = "watchService"; + public static final String FUZZY_WATCH_SERVICE = "fuzzyWatchService"; - public static final String CANCEL_WATCH_SERVICE = "cancelWatchService"; + public static final String CANCEL_FUZZY_WATCH_SERVICE = "cancelFuzzyWatchService"; public static final String QUERY_SERVICE = "queryService"; diff --git a/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/AbstractWatchNotifyRequest.java b/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/AbstractFuzzyWatchNotifyRequest.java similarity index 74% rename from api/src/main/java/com/alibaba/nacos/api/naming/remote/request/AbstractWatchNotifyRequest.java rename to api/src/main/java/com/alibaba/nacos/api/naming/remote/request/AbstractFuzzyWatchNotifyRequest.java index edc21ca707c..b1678a8001d 100644 --- a/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/AbstractWatchNotifyRequest.java +++ b/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/AbstractFuzzyWatchNotifyRequest.java @@ -21,23 +21,20 @@ import static com.alibaba.nacos.api.common.Constants.Naming.NAMING_MODULE; /** - * Abstract watch notify request, including basic watch notify information. + * Abstract fuzzy watch notify request, including basic fuzzy watch notify information. * * @author tanyongquan */ -public abstract class AbstractWatchNotifyRequest extends ServerRequest { +public abstract class AbstractFuzzyWatchNotifyRequest extends ServerRequest { private String namespace; - private String pattern; - private String serviceChangedType; - public AbstractWatchNotifyRequest(){ + public AbstractFuzzyWatchNotifyRequest(){ } - public AbstractWatchNotifyRequest(String namespace, String pattern, String serviceChangedType) { + public AbstractFuzzyWatchNotifyRequest(String namespace, String serviceChangedType) { this.namespace = namespace; - this.pattern = pattern; this.serviceChangedType = serviceChangedType; } @@ -57,14 +54,6 @@ public void setServiceChangedType(String serviceChangedType) { this.serviceChangedType = serviceChangedType; } - public String getPattern() { - return pattern; - } - - public void setPattern(String pattern) { - this.pattern = pattern; - } - @Override public String getModule() { return NAMING_MODULE; diff --git a/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/WatchNotifyChangeRequest.java b/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/FuzzyWatchNotifyChangeRequest.java similarity index 78% rename from api/src/main/java/com/alibaba/nacos/api/naming/remote/request/WatchNotifyChangeRequest.java rename to api/src/main/java/com/alibaba/nacos/api/naming/remote/request/FuzzyWatchNotifyChangeRequest.java index 88730bccaac..bf31ecb58cd 100644 --- a/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/WatchNotifyChangeRequest.java +++ b/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/FuzzyWatchNotifyChangeRequest.java @@ -17,22 +17,22 @@ package com.alibaba.nacos.api.naming.remote.request; /** - * Nacos watch notify service change request, use it when one of the services changes. + * Nacos fuzzy watch notify service change request, use it when one of the services changes. * * @author tanyongquan */ -public class WatchNotifyChangeRequest extends AbstractWatchNotifyRequest { +public class FuzzyWatchNotifyChangeRequest extends AbstractFuzzyWatchNotifyRequest { String serviceName; String groupName; - public WatchNotifyChangeRequest() { + public FuzzyWatchNotifyChangeRequest() { } - public WatchNotifyChangeRequest(String namespace, String serviceName, + public FuzzyWatchNotifyChangeRequest(String namespace, String serviceName, String groupName, String serviceChangedType) { - super(namespace, "", serviceChangedType); + super(namespace, serviceChangedType); this.serviceName = serviceName; this.groupName = groupName; } diff --git a/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/WatchNotifyInitRequest.java b/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/FuzzyWatchNotifyInitRequest.java similarity index 50% rename from api/src/main/java/com/alibaba/nacos/api/naming/remote/request/WatchNotifyInitRequest.java rename to api/src/main/java/com/alibaba/nacos/api/naming/remote/request/FuzzyWatchNotifyInitRequest.java index 7889c2113e2..0983df802d7 100644 --- a/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/WatchNotifyInitRequest.java +++ b/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/FuzzyWatchNotifyInitRequest.java @@ -22,28 +22,39 @@ import java.util.HashSet; /** - * Nacos watch initial notify request, use it when init a watch request, push service by batch. + * Nacos fuzzy watch initial notify request, use it when init a watch request, push service by batch. * * @author tanyongquan */ -public class WatchNotifyInitRequest extends AbstractWatchNotifyRequest { +public class FuzzyWatchNotifyInitRequest extends AbstractFuzzyWatchNotifyRequest { + + private String pattern; private Collection servicesName; - public WatchNotifyInitRequest() { + public FuzzyWatchNotifyInitRequest() { } - private WatchNotifyInitRequest(String namespace, String pattern, String serviceChangedType, Collection servicesName) { - super(namespace, pattern, serviceChangedType); + 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 static WatchNotifyInitRequest buildInitRequest(String namespace, String pattern, Collection servicesName) { - return new WatchNotifyInitRequest(namespace, pattern, Constants.WatchEventType.WATCH_INITIAL_MATCH, servicesName); + public String getPattern() { + return pattern; } - public static WatchNotifyInitRequest buildInitFinishRequest(String namespace, String pattern) { - return new WatchNotifyInitRequest(namespace, pattern, Constants.WatchEventType.FINISH_WATCH_INIT, new HashSet<>(1)); + public void setPattern(String pattern) { + this.pattern = pattern; } public Collection getServicesName() { diff --git a/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/WatchServiceRequest.java b/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/FuzzyWatchRequest.java similarity index 79% rename from api/src/main/java/com/alibaba/nacos/api/naming/remote/request/WatchServiceRequest.java rename to api/src/main/java/com/alibaba/nacos/api/naming/remote/request/FuzzyWatchRequest.java index ac7b00018f1..dd5015abd8b 100644 --- a/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/WatchServiceRequest.java +++ b/api/src/main/java/com/alibaba/nacos/api/naming/remote/request/FuzzyWatchRequest.java @@ -17,18 +17,18 @@ package com.alibaba.nacos.api.naming.remote.request; /** - * Nacos naming watch service request. + * Nacos naming fuzzy watch service request. * * @author tanyongquan */ -public class WatchServiceRequest extends AbstractNamingRequest { +public class FuzzyWatchRequest extends AbstractNamingRequest { private String type; - public WatchServiceRequest() { + public FuzzyWatchRequest() { } - public WatchServiceRequest(String namespace, String serviceNamePattern, String groupNamePattern, String type) { + public FuzzyWatchRequest(String namespace, String serviceNamePattern, String groupNamePattern, String type) { super(namespace, serviceNamePattern, groupNamePattern); this.type = type; } diff --git a/api/src/main/java/com/alibaba/nacos/api/naming/remote/response/WatchServiceResponse.java b/api/src/main/java/com/alibaba/nacos/api/naming/remote/response/FuzzyWatchResponse.java similarity index 74% rename from api/src/main/java/com/alibaba/nacos/api/naming/remote/response/WatchServiceResponse.java rename to api/src/main/java/com/alibaba/nacos/api/naming/remote/response/FuzzyWatchResponse.java index ecf487e23fe..6abbad6ba54 100644 --- a/api/src/main/java/com/alibaba/nacos/api/naming/remote/response/WatchServiceResponse.java +++ b/api/src/main/java/com/alibaba/nacos/api/naming/remote/response/FuzzyWatchResponse.java @@ -20,23 +20,23 @@ import com.alibaba.nacos.api.remote.response.ResponseCode; /** - * Nacos naming watch service response. + * Nacos naming fuzzy watch service response. * * @author tanyongquan */ -public class WatchServiceResponse extends Response { +public class FuzzyWatchResponse extends Response { private String type; - public WatchServiceResponse(){ + public FuzzyWatchResponse(){ } - public WatchServiceResponse(String type) { + public FuzzyWatchResponse(String type) { this.type = type; } - public static WatchServiceResponse buildSuccessResponse(String type) { - return new WatchServiceResponse(type); + public static FuzzyWatchResponse buildSuccessResponse(String type) { + return new FuzzyWatchResponse(type); } /** @@ -45,8 +45,8 @@ public static WatchServiceResponse buildSuccessResponse(String type) { * @param message error message * @return fail response */ - public static WatchServiceResponse buildFailResponse(String message) { - WatchServiceResponse result = new WatchServiceResponse(); + public static FuzzyWatchResponse buildFailResponse(String message) { + FuzzyWatchResponse result = new FuzzyWatchResponse(); result.setErrorInfo(ResponseCode.FAIL.getCode(), message); return result; } diff --git a/api/src/main/java/com/alibaba/nacos/api/naming/remote/response/NotifyWatcherResponse.java b/api/src/main/java/com/alibaba/nacos/api/naming/remote/response/NotifyFuzzyWatcherResponse.java similarity index 88% rename from api/src/main/java/com/alibaba/nacos/api/naming/remote/response/NotifyWatcherResponse.java rename to api/src/main/java/com/alibaba/nacos/api/naming/remote/response/NotifyFuzzyWatcherResponse.java index 6ff93354da5..ac9d55298db 100644 --- a/api/src/main/java/com/alibaba/nacos/api/naming/remote/response/NotifyWatcherResponse.java +++ b/api/src/main/java/com/alibaba/nacos/api/naming/remote/response/NotifyFuzzyWatcherResponse.java @@ -19,10 +19,10 @@ import com.alibaba.nacos.api.remote.response.Response; /** - * Response for notify watcher. + * Response for notify fuzzy watcher. * * @author tanyongquan */ -public class NotifyWatcherResponse extends Response { +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 cb53846d22a..b29b9639918 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 @@ -208,7 +208,7 @@ public static String getPatternRemovedNamespace(String completedPattern) { } /** - * Get the Pattern subscribed to under this NamespaceId. + * Get the pattern watched under given namespace id. * @param namespaceId name space id * @param completedPattern a set of all watch pattern(with namespace id) * @return filtered pattern set @@ -228,16 +228,16 @@ public static Set filterPatternWithNamespace(String namespaceId, Set clust } @Override - public void fuzzyWatch(String fixedGroupName, AbstractWatchEventListener listener) throws NacosException { + public void fuzzyWatch(String fixedGroupName, AbstractFuzzyWatchEventListener listener) throws NacosException { // pattern e.g. DEFAULT_GROUP@@MATCH_ALL - doFuzzyWatch(Constants.WatchMatchRule.MATCH_ALL, fixedGroupName, listener); + doFuzzyWatch(Constants.FuzzyWatchMatchRule.MATCH_ALL, fixedGroupName, listener); } @Override public void fuzzyWatch(String serviceNamePattern, String fixedGroupName, - AbstractWatchEventListener listener) throws NacosException { + AbstractFuzzyWatchEventListener listener) throws NacosException { // only support prefix match right now // pattern e.g. DEFAULT_GROUP@@nacos.test##MATCH_PREFIX - String serviceNamePrefixPattern = NamingUtils.getGroupedPattern(serviceNamePattern, Constants.WatchMatchRule.MATCH_PREFIX); + String serviceNamePrefixPattern = NamingUtils.getGroupedPattern(serviceNamePattern, Constants.FuzzyWatchMatchRule.MATCH_PREFIX); doFuzzyWatch(serviceNamePrefixPattern, fixedGroupName, listener); } private void doFuzzyWatch(String serviceNamePattern, String groupNamePattern, - AbstractWatchEventListener listener) throws NacosException { + AbstractFuzzyWatchEventListener listener) throws NacosException { if (null == listener) { return; } String uuid = UUID.randomUUID().toString(); listener.setUuid(uuid); - watchServiceChangeNotifier.registerWatchListener(serviceNamePattern, groupNamePattern, listener); + servicesChangeNotifier.registerFuzzyWatchListener(serviceNamePattern, groupNamePattern, listener); clientProxy.fuzzyWatch(serviceNamePattern, groupNamePattern, uuid); } @Override - public void cancelFuzzyWatch(String fixedGroupName, AbstractWatchEventListener listener) throws NacosException { - doCancelFuzzyWatch(Constants.WatchMatchRule.MATCH_ALL, fixedGroupName, listener); + public void cancelFuzzyWatch(String fixedGroupName, AbstractFuzzyWatchEventListener listener) throws NacosException { + doCancelFuzzyWatch(Constants.FuzzyWatchMatchRule.MATCH_ALL, fixedGroupName, listener); } @Override - public void cancelFuzzyWatch(String serviceNamePattern, String fixedGroupName, AbstractWatchEventListener listener) throws NacosException { - String serviceNamePrefixPattern = NamingUtils.getGroupedPattern(serviceNamePattern, Constants.WatchMatchRule.MATCH_PREFIX); + public void cancelFuzzyWatch(String serviceNamePattern, String fixedGroupName, AbstractFuzzyWatchEventListener listener) throws NacosException { + String serviceNamePrefixPattern = NamingUtils.getGroupedPattern(serviceNamePattern, Constants.FuzzyWatchMatchRule.MATCH_PREFIX); doCancelFuzzyWatch(serviceNamePrefixPattern, fixedGroupName, listener); } - private void doCancelFuzzyWatch(String serviceNamePattern, String groupNamePattern, AbstractWatchEventListener listener) throws NacosException { + private void doCancelFuzzyWatch(String serviceNamePattern, String groupNamePattern, + AbstractFuzzyWatchEventListener listener) throws NacosException { if (null == listener) { return; } - watchServiceChangeNotifier.deregisterWatchListener(serviceNamePattern, groupNamePattern, listener); - if (!watchServiceChangeNotifier.isWatched(serviceNamePattern, groupNamePattern)) { + servicesChangeNotifier.deregisterFuzzyWatchListener(serviceNamePattern, groupNamePattern, listener); + if (!servicesChangeNotifier.isWatched(serviceNamePattern, groupNamePattern)) { clientProxy.cancelFuzzyWatch(serviceNamePattern, groupNamePattern); } } diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/cache/WatchServiceListHolder.java b/client/src/main/java/com/alibaba/nacos/client/naming/cache/FuzzyWatchServiceListHolder.java similarity index 63% rename from client/src/main/java/com/alibaba/nacos/client/naming/cache/WatchServiceListHolder.java rename to client/src/main/java/com/alibaba/nacos/client/naming/cache/FuzzyWatchServiceListHolder.java index 293849cabce..dfd0e2bd240 100644 --- a/client/src/main/java/com/alibaba/nacos/client/naming/cache/WatchServiceListHolder.java +++ b/client/src/main/java/com/alibaba/nacos/client/naming/cache/FuzzyWatchServiceListHolder.java @@ -18,12 +18,12 @@ import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.naming.pojo.Service; -import com.alibaba.nacos.api.naming.remote.request.AbstractWatchNotifyRequest; -import com.alibaba.nacos.api.naming.remote.request.WatchNotifyChangeRequest; -import com.alibaba.nacos.api.naming.remote.request.WatchNotifyInitRequest; +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.WatchNotifyEvent; +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; @@ -34,11 +34,11 @@ import java.util.concurrent.ConcurrentHashMap; /** - * Naming client watch service list holder. + * Naming client fuzzy watch service list holder. * * @author tanyongquan */ -public class WatchServiceListHolder { +public class FuzzyWatchServiceListHolder { private String notifierEventScope; @@ -47,54 +47,55 @@ public class WatchServiceListHolder { */ private Map> patternMatchMap = new ConcurrentHashMap<>(); - public WatchServiceListHolder(String notifierEventScope, NacosClientProperties properties) { + public FuzzyWatchServiceListHolder(String notifierEventScope, NacosClientProperties properties) { this.notifierEventScope = notifierEventScope; } /** - * Publish service change event notify by watch. + * Publish service change event notify from nacos server about fuzzy watch. * * @param request watch notify request from Nacos server */ - public void processServiceChange(AbstractWatchNotifyRequest request) { - if (request instanceof WatchNotifyInitRequest) { - WatchNotifyInitRequest watchNotifyInitRequest = (WatchNotifyInitRequest) request; - Set cacheService = patternMatchMap.computeIfAbsent(request.getPattern(), keyInner -> new ConcurrentHashSet<>()); + 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 (cacheService.add(service)) { - NotifyCenter.publishEvent(WatchNotifyEvent.buildNotifyPatternAllListenersEvent(notifierEventScope, - service, request.getPattern(), Constants.WatchEventType.ADD_SERVICE)); + if (matchedServiceSet.add(service)) { + NotifyCenter.publishEvent(FuzzyWatchNotifyEvent.buildNotifyPatternAllListenersEvent(notifierEventScope, + service, watchNotifyInitRequest.getPattern(), Constants.ServiceChangedType.ADD_SERVICE)); } } - } else if (request instanceof WatchNotifyChangeRequest) { - WatchNotifyChangeRequest notifyChangeRequest = (WatchNotifyChangeRequest) request; + } 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.WatchEventType.ADD_SERVICE: - case Constants.WatchEventType.INSTANCE_CHANGED: + 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( - WatchNotifyEvent.buildNotifyPatternAllListenersEvent(notifierEventScope, - service, pattern, serviceChangeType)); + FuzzyWatchNotifyEvent.buildNotifyPatternAllListenersEvent(notifierEventScope, + service, pattern, Constants.ServiceChangedType.ADD_SERVICE)); } } break; - case Constants.WatchEventType.DELETE_SERVICE: + case Constants.ServiceChangedType.DELETE_SERVICE: for (String pattern : matchedPattern) { Set matchedServiceSet = patternMatchMap.get(pattern); if (matchedServiceSet != null && matchedServiceSet.remove(service)) { NotifyCenter.publishEvent( - WatchNotifyEvent.buildNotifyPatternAllListenersEvent(notifierEventScope, - service, pattern, serviceChangeType)); + FuzzyWatchNotifyEvent.buildNotifyPatternAllListenersEvent(notifierEventScope, + service, pattern, Constants.ServiceChangedType.DELETE_SERVICE)); } } break; @@ -105,21 +106,22 @@ public void processServiceChange(AbstractWatchNotifyRequest request) { } /** - * For a duplicate watch of a certain pattern, initiate an initialization event to the corresponding Listener. + * 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 duplicateWatchInit(String serviceNamePattern, String groupNamePattern, String uuid) { + 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(WatchNotifyEvent.buildNotifyPatternSpecificListenerEvent(notifierEventScope, service, - pattern, uuid, Constants.WatchEventType.ADD_SERVICE)); + NotifyCenter.publishEvent( + FuzzyWatchNotifyEvent.buildNotifyPatternSpecificListenerEvent(notifierEventScope, service, + pattern, uuid, Constants.ServiceChangedType.ADD_SERVICE)); } } diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/event/WatchNotifyEvent.java b/client/src/main/java/com/alibaba/nacos/client/naming/event/FuzzyWatchNotifyEvent.java similarity index 72% rename from client/src/main/java/com/alibaba/nacos/client/naming/event/WatchNotifyEvent.java rename to client/src/main/java/com/alibaba/nacos/client/naming/event/FuzzyWatchNotifyEvent.java index 7d738369678..4f39dc30645 100644 --- a/client/src/main/java/com/alibaba/nacos/client/naming/event/WatchNotifyEvent.java +++ b/client/src/main/java/com/alibaba/nacos/client/naming/event/FuzzyWatchNotifyEvent.java @@ -24,7 +24,7 @@ * * @author tanyongquan */ -public class WatchNotifyEvent extends Event { +public class FuzzyWatchNotifyEvent extends Event { private final String eventScope; @@ -36,26 +36,26 @@ public class WatchNotifyEvent extends Event { private final String serviceChangedType; - private WatchNotifyEvent(String eventScope, Service changedService, String pattern, String uuid, String serviceChangedType) { + private FuzzyWatchNotifyEvent(String eventScope, Service changedService, String pattern, String uuid, String serviceChangedType) { this(eventScope, changedService, pattern, serviceChangedType); this.uuid = uuid; } - private WatchNotifyEvent(String eventScope, Service changedService, String pattern, String serviceChangedType) { + private FuzzyWatchNotifyEvent(String eventScope, Service changedService, String pattern, String serviceChangedType) { this.eventScope = eventScope; this.changedService = changedService; this.serviceChangedType = serviceChangedType; this.pattern = pattern; } - public static WatchNotifyEvent buildNotifyPatternSpecificListenerEvent(String eventScope, Service changedService, + public static FuzzyWatchNotifyEvent buildNotifyPatternSpecificListenerEvent(String eventScope, Service changedService, String pattern, String uuid, String serviceChangedType) { - return new WatchNotifyEvent(eventScope, changedService, pattern, uuid, serviceChangedType); + return new FuzzyWatchNotifyEvent(eventScope, changedService, pattern, uuid, serviceChangedType); } - public static WatchNotifyEvent buildNotifyPatternAllListenersEvent(String eventScope, Service changedService, + public static FuzzyWatchNotifyEvent buildNotifyPatternAllListenersEvent(String eventScope, Service changedService, String pattern, String serviceChangedType) { - return new WatchNotifyEvent(eventScope, changedService, pattern, serviceChangedType); + return new FuzzyWatchNotifyEvent(eventScope, changedService, pattern, serviceChangedType); } public Service getChangedService() { diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/event/WatchServiceChangeNotifier.java b/client/src/main/java/com/alibaba/nacos/client/naming/event/ServicesChangeNotifier.java similarity index 59% rename from client/src/main/java/com/alibaba/nacos/client/naming/event/WatchServiceChangeNotifier.java rename to client/src/main/java/com/alibaba/nacos/client/naming/event/ServicesChangeNotifier.java index e9e18415172..26bc5741387 100644 --- a/client/src/main/java/com/alibaba/nacos/client/naming/event/WatchServiceChangeNotifier.java +++ b/client/src/main/java/com/alibaba/nacos/client/naming/event/ServicesChangeNotifier.java @@ -16,7 +16,7 @@ package com.alibaba.nacos.client.naming.event; -import com.alibaba.nacos.api.naming.listener.AbstractWatchEventListener; +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; @@ -32,55 +32,55 @@ import java.util.concurrent.ConcurrentHashMap; /** - * A watcher to notify watch event Listener callback. + * A watcher to notify service change event Listener callback. * * @author tanyongquan */ -public class WatchServiceChangeNotifier extends Subscriber { +public class ServicesChangeNotifier extends Subscriber { private final String eventScope; @JustForTest - public WatchServiceChangeNotifier() { + public ServicesChangeNotifier() { this.eventScope = UUID.randomUUID().toString(); } - public WatchServiceChangeNotifier(String eventScope) { + public ServicesChangeNotifier(String eventScope) { this.eventScope = eventScope; } /** - * pattern -> Set[Listener]. + * The content of map is {pattern -> Set[Listener]}. */ - private final Map> watchListenerMap = new ConcurrentHashMap<>(); + private final Map> fuzzyWatchListenerMap = new ConcurrentHashMap<>(); - /** register watch listener. - * This listener responds to changes of the services (not the instance). + /** 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 registerWatchListener(String serviceNamePattern, String groupNamePattern, AbstractWatchEventListener listener) { + public void registerFuzzyWatchListener(String serviceNamePattern, String groupNamePattern, AbstractFuzzyWatchEventListener listener) { String key = NamingUtils.getGroupedName(serviceNamePattern, groupNamePattern); - Set eventListeners = watchListenerMap.computeIfAbsent(key, keyInner -> new ConcurrentHashSet<>()); + Set eventListeners = fuzzyWatchListenerMap.computeIfAbsent(key, keyInner -> new ConcurrentHashSet<>()); eventListeners.add(listener); } - /** remove watch listener. + /** remove fuzzy watch listener. * * @param serviceNamePattern service name pattern * @param groupNamePattern group name pattern */ - public void deregisterWatchListener(String serviceNamePattern, String groupNamePattern, AbstractWatchEventListener listener) { + public void deregisterFuzzyWatchListener(String serviceNamePattern, String groupNamePattern, AbstractFuzzyWatchEventListener listener) { String key = NamingUtils.getGroupedName(serviceNamePattern, groupNamePattern); - ConcurrentHashSet eventListeners = watchListenerMap.get(key); + ConcurrentHashSet eventListeners = fuzzyWatchListenerMap.get(key); if (eventListeners == null) { return; } eventListeners.remove(listener); if (CollectionUtils.isEmpty(eventListeners)) { - watchListenerMap.remove(key); + fuzzyWatchListenerMap.remove(key); } } @@ -93,55 +93,55 @@ public void deregisterWatchListener(String serviceNamePattern, String groupNameP */ public boolean isWatched(String serviceNamePattern, String groupNamePattern) { String key = NamingUtils.getGroupedName(serviceNamePattern, groupNamePattern); - ConcurrentHashSet eventListeners = watchListenerMap.get(key); + ConcurrentHashSet eventListeners = fuzzyWatchListenerMap.get(key); return CollectionUtils.isNotEmpty(eventListeners); } /** - * receive watch notify (watch init or service change) from nacos server, notify all listener watch this pattern. + * 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 + * multiple watches for a particular pattern. * * @param event watch notify event */ @Override - public void onEvent(WatchNotifyEvent event) { + public void onEvent(FuzzyWatchNotifyEvent event) { String uuid = event.getUuid(); - Collection listeners = watchListenerMap.get(event.getPattern()); - final com.alibaba.nacos.api.naming.listener.WatchNotifyEvent watchNotifyEvent = transferToWatchNotifyEvent(event); - for (AbstractWatchEventListener each : listeners) { + 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(watchNotifyEvent)); + each.getExecutor().execute(() -> each.onEvent(fuzzyWatchNotifyEvent)); } else { - each.onEvent(watchNotifyEvent); + 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(watchNotifyEvent)); + each.getExecutor().execute(() -> each.onEvent(fuzzyWatchNotifyEvent)); } else { - each.onEvent(watchNotifyEvent); + each.onEvent(fuzzyWatchNotifyEvent); } return; } } } - private com.alibaba.nacos.api.naming.listener.WatchNotifyEvent transferToWatchNotifyEvent( - WatchNotifyEvent watchNotifyEvent) { - return new com.alibaba.nacos.api.naming.listener.WatchNotifyEvent(watchNotifyEvent.getChangedService(), - watchNotifyEvent.getServiceChangedType()); + 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 WatchNotifyEvent.class; + return FuzzyWatchNotifyEvent.class; } @Override - public boolean scopeMatches(WatchNotifyEvent event) { + 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 e6965a7af5c..145bbf49cbb 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 @@ -183,11 +183,11 @@ ListView getServiceList(int pageNo, int pageSize, String groupName, Abst boolean isSubscribed(String serviceName, String groupName, String clusters) throws NacosException; /** - * Watch services change by pattern. + * 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 Watch + * @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; @@ -201,7 +201,7 @@ ListView getServiceList(int pageNo, int pageSize, String groupName, Abst */ boolean isFuzzyWatched(String serviceNamePattern, String groupNamePattern) throws NacosException; - /** Cancel watch pattern. + /** Cancel fuzzy watch pattern. * * @param serviceNamePattern service name pattern * @param groupNamePattern group name pattern 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 1ca9da81731..8d3ed891014 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,7 +25,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.WatchServiceListHolder; +import com.alibaba.nacos.client.naming.cache.FuzzyWatchServiceListHolder; import com.alibaba.nacos.client.naming.core.ServerListManager; import com.alibaba.nacos.client.naming.core.ServiceInfoUpdateService; import com.alibaba.nacos.client.naming.event.InstancesChangeNotifier; @@ -57,8 +57,8 @@ public class NamingClientProxyDelegate implements NamingClientProxy { private final ServiceInfoUpdateService serviceInfoUpdateService; private final ServiceInfoHolder serviceInfoHolder; - - private final WatchServiceListHolder watchServiceListHolder; + + private final FuzzyWatchServiceListHolder fuzzyWatchServiceListHolder; private final NamingHttpClientProxy httpClientProxy; @@ -68,19 +68,19 @@ public class NamingClientProxyDelegate implements NamingClientProxy { private ScheduledExecutorService executorService; - public NamingClientProxyDelegate(String namespace, ServiceInfoHolder serviceInfoHolder, WatchServiceListHolder watchServiceListHolder, + 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 ServerListManager(properties, namespace); this.serviceInfoHolder = serviceInfoHolder; - this.watchServiceListHolder = watchServiceListHolder; + this.fuzzyWatchServiceListHolder = fuzzyWatchServiceListHolder; this.securityProxy = new SecurityProxy(this.serverListManager.getServerList(), NamingHttpClientManager.getInstance().getNacosRestTemplate()); initSecurityProxy(properties); this.httpClientProxy = new NamingHttpClientProxy(namespace, securityProxy, serverListManager, properties); this.grpcClientProxy = new NamingGrpcClientProxy(namespace, securityProxy, serverListManager, properties, - serviceInfoHolder, watchServiceListHolder); + serviceInfoHolder, fuzzyWatchServiceListHolder); } private void initSecurityProxy(NacosClientProperties properties) { @@ -195,13 +195,13 @@ public boolean isSubscribed(String serviceName, String groupName, String cluster @Override public void fuzzyWatch(String serviceNamePattern, String groupNamePattern, String uuid) throws NacosException { - NAMING_LOGGER.info("[WATCH] serviceNamePattern:{}, groupNamePattern:{}", serviceNamePattern, groupNamePattern); - if (!watchServiceListHolder.containsPatternMatchCache(serviceNamePattern, groupNamePattern) + NAMING_LOGGER.info("[FUZZY-WATCH] serviceNamePattern:{}, groupNamePattern:{}", serviceNamePattern, groupNamePattern); + if (!fuzzyWatchServiceListHolder.containsPatternMatchCache(serviceNamePattern, groupNamePattern) || !isFuzzyWatched(serviceNamePattern, groupNamePattern)) { - watchServiceListHolder.addPatternMatchCache(serviceNamePattern, groupNamePattern); + fuzzyWatchServiceListHolder.addPatternMatchCache(serviceNamePattern, groupNamePattern); grpcClientProxy.fuzzyWatch(serviceNamePattern, groupNamePattern, ""); } else { - watchServiceListHolder.duplicateWatchInit(serviceNamePattern, groupNamePattern, uuid); + fuzzyWatchServiceListHolder.duplicateFuzzyWatchInit(serviceNamePattern, groupNamePattern, uuid); } } @@ -213,8 +213,8 @@ public boolean isFuzzyWatched(String serviceNamePattern, String groupNamePattern @Override public void cancelFuzzyWatch(String serviceNamePattern, String groupNamePattern) throws NacosException { NAMING_LOGGER - .debug("[CANCEL-WATCH] serviceNamePattern:{}, groupNamePattern:{} ", serviceNamePattern, groupNamePattern); - watchServiceListHolder.removePatternMatchCache(serviceNamePattern, groupNamePattern); + .debug("[CANCEL-FUZZY-WATCH] serviceNamePattern:{}, groupNamePattern:{} ", serviceNamePattern, groupNamePattern); + fuzzyWatchServiceListHolder.removePatternMatchCache(serviceNamePattern, groupNamePattern); grpcClientProxy.cancelFuzzyWatch(serviceNamePattern, groupNamePattern); } 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 a6443ce0192..e29be80f042 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 @@ -30,12 +30,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.WatchServiceRequest; +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.WatchServiceResponse; +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; @@ -44,7 +44,7 @@ import com.alibaba.nacos.api.selector.SelectorType; import com.alibaba.nacos.client.env.NacosClientProperties; import com.alibaba.nacos.client.naming.cache.ServiceInfoHolder; -import com.alibaba.nacos.client.naming.cache.WatchServiceListHolder; +import com.alibaba.nacos.client.naming.cache.FuzzyWatchServiceListHolder; import com.alibaba.nacos.client.naming.event.ServerListChangedEvent; import com.alibaba.nacos.client.naming.remote.AbstractNamingClientProxy; import com.alibaba.nacos.client.naming.remote.gprc.redo.NamingGrpcRedoService; @@ -91,7 +91,7 @@ public class NamingGrpcClientProxy extends AbstractNamingClientProxy { public NamingGrpcClientProxy(String namespaceId, SecurityProxy securityProxy, ServerListFactory serverListFactory, NacosClientProperties properties, ServiceInfoHolder serviceInfoHolder, - WatchServiceListHolder watchServiceListHolder) throws NacosException { + FuzzyWatchServiceListHolder fuzzyWatchServiceListHolder) throws NacosException { super(securityProxy); this.namespaceId = namespaceId; this.uuid = UUID.randomUUID().toString(); @@ -104,14 +104,15 @@ public NamingGrpcClientProxy(String namespaceId, SecurityProxy securityProxy, Se RpcClientTlsConfig.properties(properties.asProperties())); this.redoService = new NamingGrpcRedoService(this); NAMING_LOGGER.info("Create naming rpc client for uuid->{}", uuid); - start(serverListFactory, serviceInfoHolder, watchServiceListHolder); + start(serverListFactory, serviceInfoHolder, fuzzyWatchServiceListHolder); } private void start(ServerListFactory serverListFactory, ServiceInfoHolder serviceInfoHolder, - WatchServiceListHolder watchServiceListHolder) throws NacosException { + FuzzyWatchServiceListHolder fuzzyWatchServiceListHolder) throws NacosException { rpcClient.serverListFactory(serverListFactory); rpcClient.registerConnectionListener(redoService); - rpcClient.registerServerRequestHandler(new NamingPushRequestHandler(serviceInfoHolder, watchServiceListHolder)); + rpcClient.registerServerRequestHandler(new NamingPushRequestHandler(serviceInfoHolder, + fuzzyWatchServiceListHolder)); rpcClient.start(); NotifyCenter.registerSubscriber(this); } @@ -356,53 +357,53 @@ 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-WATCH] servicePattern:{}, groupPattern:{}", serviceNamePattern, groupNamePattern); + NAMING_LOGGER.debug("[GRPC-FUZZY-WATCH] servicePattern:{}, groupPattern:{}", serviceNamePattern, groupNamePattern); } - redoService.cacheWatcherForRedo(serviceNamePattern, groupNamePattern); + redoService.cacheFuzzyWatcherForRedo(serviceNamePattern, groupNamePattern); doFuzzyWatch(serviceNamePattern, groupNamePattern); } /** - * Execute watch operation. + * 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 { - WatchServiceRequest request = new WatchServiceRequest(namespaceId, serviceNamePattern, groupNamePattern, - NamingRemoteConstants.WATCH_SERVICE); - requestToServer(request, WatchServiceResponse.class); - redoService.watcherRegistered(serviceNamePattern, groupNamePattern); + 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.isWatcherRegistered(serviceNamePattern, 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-WATCH] serviceNamePattern:{}, groupNamePattern:{}", serviceNamePattern, groupNamePattern); + .debug("[GRPC-CANCEL-FUZZY-WATCH] serviceNamePattern:{}, groupNamePattern:{}", serviceNamePattern, groupNamePattern); } - redoService.watcherDeregister(serviceNamePattern, groupNamePattern); + redoService.fuzzyWatcherDeregister(serviceNamePattern, groupNamePattern); doCancelFuzzyWatch(serviceNamePattern, groupNamePattern); } /** - * Send cancel watch request to server. + * 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 { - WatchServiceRequest request = new WatchServiceRequest(namespaceId, serviceNamePattern, groupNamePattern, - NamingRemoteConstants.CANCEL_WATCH_SERVICE); - requestToServer(request, WatchServiceResponse.class); - redoService.removeWatcherForRedo(serviceNamePattern, groupNamePattern); + FuzzyWatchRequest request = new FuzzyWatchRequest(namespaceId, serviceNamePattern, groupNamePattern, + NamingRemoteConstants.CANCEL_FUZZY_WATCH_SERVICE); + requestToServer(request, FuzzyWatchResponse.class); + redoService.removeFuzzyWatcherForRedo(serviceNamePattern, groupNamePattern); } @Override 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 9cca7c7c52f..bb521228d65 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,14 @@ package com.alibaba.nacos.client.naming.remote.gprc; -import com.alibaba.nacos.api.naming.remote.request.AbstractWatchNotifyRequest; +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.NotifyWatcherResponse; +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.WatchServiceListHolder; +import com.alibaba.nacos.client.naming.cache.FuzzyWatchServiceListHolder; import com.alibaba.nacos.common.remote.client.ServerRequestHandler; /** @@ -35,11 +35,11 @@ public class NamingPushRequestHandler implements ServerRequestHandler { private final ServiceInfoHolder serviceInfoHolder; - private final WatchServiceListHolder watchServiceListHolder; + private final FuzzyWatchServiceListHolder fuzzyWatchServiceListHolder; - public NamingPushRequestHandler(ServiceInfoHolder serviceInfoHolder, WatchServiceListHolder watchServiceListHolder) { + public NamingPushRequestHandler(ServiceInfoHolder serviceInfoHolder, FuzzyWatchServiceListHolder fuzzyWatchServiceListHolder) { this.serviceInfoHolder = serviceInfoHolder; - this.watchServiceListHolder = watchServiceListHolder; + this.fuzzyWatchServiceListHolder = fuzzyWatchServiceListHolder; } @Override @@ -48,10 +48,10 @@ public Response requestReply(Request request) { NotifySubscriberRequest notifyRequest = (NotifySubscriberRequest) request; serviceInfoHolder.processServiceInfo(notifyRequest.getServiceInfo()); return new NotifySubscriberResponse(); - } else if (request instanceof AbstractWatchNotifyRequest) { - AbstractWatchNotifyRequest notifyRequest = (AbstractWatchNotifyRequest) request; - watchServiceListHolder.processServiceChange(notifyRequest); - return new NotifyWatcherResponse(); + } 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 f2aeeb1d097..8f78ab55584 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 @@ -23,7 +23,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.WatcherRedoData; +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.ConnectionEventListener; @@ -36,7 +36,6 @@ import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; -import java.util.stream.Collectors; /** * Naming client gprc redo service. @@ -60,7 +59,7 @@ public class NamingGrpcRedoService implements ConnectionEventListener { private final ConcurrentMap subscribes = new ConcurrentHashMap<>(); - private final ConcurrentMap watcher = new ConcurrentHashMap<>(); + private final ConcurrentMap fuzzyWatcher = new ConcurrentHashMap<>(); private final ScheduledExecutorService redoExecutor; @@ -92,8 +91,8 @@ public void onDisConnect() { synchronized (subscribes) { subscribes.values().forEach(subscriberRedoData -> subscriberRedoData.setRegistered(false)); } - synchronized (watcher) { - watcher.values().forEach(watcherRedoData -> watcherRedoData.setRegistered(false)); + synchronized (fuzzyWatcher) { + fuzzyWatcher.values().forEach(fuzzyWatcherRedoData -> fuzzyWatcherRedoData.setRegistered(false)); } LogUtils.NAMING_LOGGER.warn("mark to redo completed"); } @@ -311,29 +310,29 @@ public Set findSubscriberRedoData() { } /** - * Cache watcher for redo. + * Cache fuzzy watcher for redo. * * @param serviceNamePattern service name pattern * @param groupNamePattern group name pattern */ - public void cacheWatcherForRedo(String serviceNamePattern, String groupNamePattern) { + public void cacheFuzzyWatcherForRedo(String serviceNamePattern, String groupNamePattern) { String key = NamingUtils.getGroupedName(serviceNamePattern, groupNamePattern); - WatcherRedoData redoData = WatcherRedoData.build(serviceNamePattern, groupNamePattern); - synchronized (watcher) { - watcher.put(key, redoData); + FuzzyWatcherRedoData redoData = FuzzyWatcherRedoData.build(serviceNamePattern, groupNamePattern); + synchronized (fuzzyWatcher) { + fuzzyWatcher.put(key, redoData); } } /** - * Watcher register successfully, mark registered status as {@code true}. + * Fuzzy watcher register successfully, mark registered status as {@code true}. * * @param serviceNamePattern service name pattern * @param groupNamePattern group name pattern */ - public void watcherRegistered(String serviceNamePattern, String groupNamePattern) { + public void fuzzyWatcherRegistered(String serviceNamePattern, String groupNamePattern) { String key = NamingUtils.getGroupedName(serviceNamePattern, groupNamePattern); - synchronized (watcher) { - WatcherRedoData redoData = watcher.get(key); + synchronized (fuzzyWatcher) { + FuzzyWatcherRedoData redoData = fuzzyWatcher.get(key); if (null != redoData) { redoData.setRegistered(true); } @@ -341,15 +340,15 @@ public void watcherRegistered(String serviceNamePattern, String groupNamePattern } /** - * Watcher deregister, mark unregistering status as {@code true}. + * Fuzzy watcher deregister, mark unregistering status as {@code true}. * * @param serviceNamePattern service name pattern * @param groupNamePattern group name pattern */ - public void watcherDeregister(String serviceNamePattern, String groupNamePattern) { + public void fuzzyWatcherDeregister(String serviceNamePattern, String groupNamePattern) { String key = NamingUtils.getGroupedName(serviceNamePattern, groupNamePattern); - synchronized (watcher) { - WatcherRedoData redoData = watcher.get(key); + synchronized (fuzzyWatcher) { + FuzzyWatcherRedoData redoData = fuzzyWatcher.get(key); if (null != redoData) { redoData.setUnregistering(true); redoData.setExpectedRegistered(false); @@ -358,45 +357,51 @@ public void watcherDeregister(String serviceNamePattern, String groupNamePattern } /** - * Remove watcher for redo. + * Remove fuzzy watcher for redo. * * @param serviceNamePattern service name pattern * @param groupNamePattern group name pattern */ - public void removeWatcherForRedo(String serviceNamePattern, String groupNamePattern) { + public void removeFuzzyWatcherForRedo(String serviceNamePattern, String groupNamePattern) { String key = NamingUtils.getGroupedName(serviceNamePattern, groupNamePattern); - synchronized (watcher) { - WatcherRedoData redoData = watcher.get(key); + synchronized (fuzzyWatcher) { + FuzzyWatcherRedoData redoData = fuzzyWatcher.get(key); if (null != redoData && !redoData.isExpectedRegistered()) { - watcher.remove(key); + fuzzyWatcher.remove(key); } } } /** - * Judge watcher has registered to server. + * 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 isWatcherRegistered(String serviceNamePattern, String groupNamePattern) { + public boolean isFuzzyWatcherRegistered(String serviceNamePattern, String groupNamePattern) { String key = NamingUtils.getGroupedName(serviceNamePattern, groupNamePattern); - synchronized (watcher) { - WatcherRedoData redoData = watcher.get(key); + synchronized (fuzzyWatcher) { + FuzzyWatcherRedoData redoData = fuzzyWatcher.get(key); return null != redoData && redoData.isRegistered(); } } /** - * Find all watcher redo data which need do redo. + * Find all fuzzy watcher redo data which need to redo. * - * @return set of {@code WatcherRedoData} need to do redo. + * @return set of {@code WatcherRedoData} need to redo. */ - public Set findWatcherRedoData() { - return watcher.values().stream() - .filter(WatcherRedoData::isNeedRedo) - .collect(Collectors.toSet()); + public Set findFuzzyWatcherRedoData() { + Set result = new HashSet<>(); + synchronized (fuzzyWatcher) { + for (FuzzyWatcherRedoData each : fuzzyWatcher.values()) { + if (each.isNeedRedo()) { + result.add(each); + } + } + } + return result; } /** 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 355bc09ec40..f9166cc4529 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,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.WatcherRedoData; +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 +51,7 @@ public void run() { try { redoForInstances(); redoForSubscribes(); - redoForWatchers(); + redoForFuzzyWatchers(); } catch (Exception e) { LogUtils.NAMING_LOGGER.warn("Redo task run with unexpected exception: ", e); } @@ -141,22 +141,22 @@ private void redoForSubscribe(SubscriberRedoData redoData) throws NacosException } } - private void redoForWatchers() { - for (WatcherRedoData each : redoService.findWatcherRedoData()) { + private void redoForFuzzyWatchers() { + for (FuzzyWatcherRedoData each : redoService.findFuzzyWatcherRedoData()) { try { - redoForWatcher(each); + redoForFuzzyWatcher(each); } catch (NacosException e) { - LogUtils.NAMING_LOGGER.error("Redo watcher operation {} for pattern {}@@{}, uuid {} failed. ", each.getRedoType(), - each.getGroupName(), each.getServiceName(), each.get(), e); + LogUtils.NAMING_LOGGER.error("Redo fuzzy watcher operation {} for pattern {}@@{} failed. ", each.getRedoType(), + each.getGroupName(), each.getServiceName(), e); } } } - private void redoForWatcher(WatcherRedoData redoData) throws NacosException { + 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 watcher operation {} for pattern {}@@{}", redoType, groupNamePattern, serviceNamePattern); + LogUtils.NAMING_LOGGER.info("Redo fuzzy watcher operation {} for pattern {}@@{}", redoType, groupNamePattern, serviceNamePattern); switch (redoType) { case REGISTER: if (isClientDisabled()) { @@ -171,7 +171,7 @@ private void redoForWatcher(WatcherRedoData redoData) throws NacosException { clientProxy.doCancelFuzzyWatch(serviceNamePattern, groupNamePattern); break; case REMOVE: - redoService.removeWatcherForRedo(redoData.getServiceName(), redoData.getGroupName()); + redoService.removeFuzzyWatcherForRedo(serviceNamePattern, groupNamePattern); break; default: } diff --git a/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/redo/data/WatcherRedoData.java b/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/redo/data/FuzzyWatcherRedoData.java similarity index 68% rename from client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/redo/data/WatcherRedoData.java rename to client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/redo/data/FuzzyWatcherRedoData.java index 4a91d3dddc4..f0e820910dd 100644 --- a/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/redo/data/WatcherRedoData.java +++ b/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/redo/data/FuzzyWatcherRedoData.java @@ -17,17 +17,17 @@ package com.alibaba.nacos.client.naming.remote.gprc.redo.data; /** - * Redo data for Watcher. + * Redo data for fuzzy watcher. * * @author tanyongquan */ -public class WatcherRedoData extends RedoData { +public class FuzzyWatcherRedoData extends RedoData { - private WatcherRedoData(String serviceNamePattern, String groupNamePattern) { + private FuzzyWatcherRedoData(String serviceNamePattern, String groupNamePattern) { super(serviceNamePattern, groupNamePattern); } - public static WatcherRedoData build(String serviceNamePattern, String groupNamePattern) { - return new WatcherRedoData(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 605cc56fe23..23eb39e433f 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 @@ -344,17 +344,17 @@ public boolean isSubscribed(String serviceName, String groupName, String cluster @Override public void fuzzyWatch(String serviceNamePattern, String groupNamePattern, String uuid) throws NacosException { - throw new UnsupportedOperationException("Do not support watch services by UDP, please use gRPC replaced."); + 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 watch service by UDP, please use gRPC replaced."); + 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 watch service by UDP, please use gRPC replaced."); + 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 { 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 4e84e144ac7..c3d4eaaf0e1 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 @@ -348,15 +348,14 @@ ] }, { - "name":"com.alibaba.nacos.api.naming.remote.request.AbstractWatchNotifyRequest", + "name":"com.alibaba.nacos.api.naming.remote.request.AbstractFuzzyWatchNotifyRequest", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[ {"name":"","parameterTypes":[] }, {"name":"getModule","parameterTypes":[] }, - {"name":"getServiceChangedType","parameterTypes":[] }, - {"name":"getPattern","parameterTypes":[] } + {"name":"getServiceChangedType","parameterTypes":[] } ] }, { @@ -379,6 +378,28 @@ {"name":"setServiceInfo","parameterTypes":["com.alibaba.nacos.api.naming.pojo.ServiceInfo"] } ] }, +{ + "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/remote/NamingClientProxyDelegateTest.java b/client/src/test/java/com/alibaba/nacos/client/naming/remote/NamingClientProxyDelegateTest.java index 2c5ad2565f1..78b00d56e0d 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,7 +27,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.WatchServiceListHolder; +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; @@ -50,12 +50,12 @@ public class NamingClientProxyDelegateTest { public void testRegisterServiceByGrpc() throws NacosException, NoSuchFieldException, IllegalAccessException { String ns = "ns1"; ServiceInfoHolder holder = Mockito.mock(ServiceInfoHolder.class); - WatchServiceListHolder watchServiceListHolder = Mockito.mock(WatchServiceListHolder.class); + FuzzyWatchServiceListHolder fuzzyWatchServiceListHolder = Mockito.mock(FuzzyWatchServiceListHolder.class); Properties props = new Properties(); props.setProperty("serverAddr", "localhost"); final NacosClientProperties nacosClientProperties = NacosClientProperties.PROTOTYPE.derive(props); InstancesChangeNotifier notifier = new InstancesChangeNotifier(); - NamingClientProxyDelegate delegate = new NamingClientProxyDelegate(ns, holder, watchServiceListHolder, + NamingClientProxyDelegate delegate = new NamingClientProxyDelegate(ns, holder, fuzzyWatchServiceListHolder, nacosClientProperties, notifier); NamingGrpcClientProxy mockGrpcClient = Mockito.mock(NamingGrpcClientProxy.class); Field grpcClientProxyField = NamingClientProxyDelegate.class.getDeclaredField("grpcClientProxy"); @@ -78,12 +78,12 @@ public void testRegisterServiceByGrpc() throws NacosException, NoSuchFieldExcept public void testBatchRegisterServiceByGrpc() throws NacosException, NoSuchFieldException, IllegalAccessException { String ns = "ns1"; ServiceInfoHolder holder = Mockito.mock(ServiceInfoHolder.class); - WatchServiceListHolder watchServiceListHolder = Mockito.mock(WatchServiceListHolder.class); + FuzzyWatchServiceListHolder fuzzyWatchServiceListHolder = Mockito.mock(FuzzyWatchServiceListHolder.class); Properties props = new Properties(); props.setProperty("serverAddr", "localhost"); InstancesChangeNotifier notifier = new InstancesChangeNotifier(); final NacosClientProperties nacosClientProperties = NacosClientProperties.PROTOTYPE.derive(props); - NamingClientProxyDelegate delegate = new NamingClientProxyDelegate(ns, holder, watchServiceListHolder, + NamingClientProxyDelegate delegate = new NamingClientProxyDelegate(ns, holder, fuzzyWatchServiceListHolder, nacosClientProperties, notifier); NamingGrpcClientProxy mockGrpcClient = Mockito.mock(NamingGrpcClientProxy.class); Field grpcClientProxyField = NamingClientProxyDelegate.class.getDeclaredField("grpcClientProxy"); @@ -107,13 +107,13 @@ public void testBatchRegisterServiceByGrpc() throws NacosException, NoSuchFieldE public void testRegisterServiceByHttp() throws NacosException, NoSuchFieldException, IllegalAccessException { String ns = "ns1"; ServiceInfoHolder holder = Mockito.mock(ServiceInfoHolder.class); - WatchServiceListHolder watchServiceListHolder = Mockito.mock(WatchServiceListHolder.class); + FuzzyWatchServiceListHolder fuzzyWatchServiceListHolder = Mockito.mock(FuzzyWatchServiceListHolder.class); Properties props = new Properties(); props.setProperty("serverAddr", "localhost"); InstancesChangeNotifier notifier = new InstancesChangeNotifier(); final NacosClientProperties nacosClientProperties = NacosClientProperties.PROTOTYPE.derive(props); - NamingClientProxyDelegate delegate = new NamingClientProxyDelegate(ns, holder, watchServiceListHolder, + NamingClientProxyDelegate delegate = new NamingClientProxyDelegate(ns, holder, fuzzyWatchServiceListHolder, nacosClientProperties, notifier); NamingHttpClientProxy mockHttpClient = Mockito.mock(NamingHttpClientProxy.class); Field mockHttpClientField = NamingClientProxyDelegate.class.getDeclaredField("httpClientProxy"); @@ -137,13 +137,13 @@ public void testRegisterServiceByHttp() throws NacosException, NoSuchFieldExcept public void testDeregisterServiceGrpc() throws NacosException, NoSuchFieldException, IllegalAccessException { String ns = "ns1"; ServiceInfoHolder holder = Mockito.mock(ServiceInfoHolder.class); - WatchServiceListHolder watchServiceListHolder = Mockito.mock(WatchServiceListHolder.class); + FuzzyWatchServiceListHolder fuzzyWatchServiceListHolder = Mockito.mock(FuzzyWatchServiceListHolder.class); Properties props = new Properties(); props.setProperty("serverAddr", "localhost"); InstancesChangeNotifier notifier = new InstancesChangeNotifier(); final NacosClientProperties nacosClientProperties = NacosClientProperties.PROTOTYPE.derive(props); - NamingClientProxyDelegate delegate = new NamingClientProxyDelegate(ns, holder, watchServiceListHolder, + NamingClientProxyDelegate delegate = new NamingClientProxyDelegate(ns, holder, fuzzyWatchServiceListHolder, nacosClientProperties, notifier); NamingGrpcClientProxy mockGrpcClient = Mockito.mock(NamingGrpcClientProxy.class); Field grpcClientProxyField = NamingClientProxyDelegate.class.getDeclaredField("grpcClientProxy"); @@ -167,13 +167,13 @@ public void testDeregisterServiceGrpc() throws NacosException, NoSuchFieldExcept public void testDeregisterServiceHttp() throws NacosException, NoSuchFieldException, IllegalAccessException { String ns = "ns1"; ServiceInfoHolder holder = Mockito.mock(ServiceInfoHolder.class); - WatchServiceListHolder watchServiceListHolder = Mockito.mock(WatchServiceListHolder.class); + FuzzyWatchServiceListHolder fuzzyWatchServiceListHolder = Mockito.mock(FuzzyWatchServiceListHolder.class); Properties props = new Properties(); props.setProperty("serverAddr", "localhost"); InstancesChangeNotifier notifier = new InstancesChangeNotifier(); final NacosClientProperties nacosClientProperties = NacosClientProperties.PROTOTYPE.derive(props); - NamingClientProxyDelegate delegate = new NamingClientProxyDelegate(ns, holder, watchServiceListHolder, + NamingClientProxyDelegate delegate = new NamingClientProxyDelegate(ns, holder, fuzzyWatchServiceListHolder, nacosClientProperties, notifier); NamingHttpClientProxy mockHttpClient = Mockito.mock(NamingHttpClientProxy.class); Field mockHttpClientField = NamingClientProxyDelegate.class.getDeclaredField("httpClientProxy"); @@ -197,13 +197,13 @@ public void testDeregisterServiceHttp() throws NacosException, NoSuchFieldExcept public void testUpdateInstance() throws NacosException { String ns = "ns1"; ServiceInfoHolder holder = Mockito.mock(ServiceInfoHolder.class); - WatchServiceListHolder watchServiceListHolder = Mockito.mock(WatchServiceListHolder.class); + FuzzyWatchServiceListHolder fuzzyWatchServiceListHolder = Mockito.mock(FuzzyWatchServiceListHolder.class); Properties props = new Properties(); props.setProperty("serverAddr", "localhost"); InstancesChangeNotifier notifier = new InstancesChangeNotifier(); final NacosClientProperties nacosClientProperties = NacosClientProperties.PROTOTYPE.derive(props); - NamingClientProxyDelegate delegate = new NamingClientProxyDelegate(ns, holder, watchServiceListHolder, + NamingClientProxyDelegate delegate = new NamingClientProxyDelegate(ns, holder, fuzzyWatchServiceListHolder, nacosClientProperties, notifier); String serviceName = "service1"; String groupName = "group1"; @@ -219,13 +219,13 @@ public void testUpdateInstance() throws NacosException { public void testQueryInstancesOfService() throws NacosException, IllegalAccessException, NoSuchFieldException { String ns = "ns1"; ServiceInfoHolder holder = Mockito.mock(ServiceInfoHolder.class); - WatchServiceListHolder watchServiceListHolder = Mockito.mock(WatchServiceListHolder.class); + FuzzyWatchServiceListHolder fuzzyWatchServiceListHolder = Mockito.mock(FuzzyWatchServiceListHolder.class); Properties props = new Properties(); props.setProperty("serverAddr", "localhost"); InstancesChangeNotifier notifier = new InstancesChangeNotifier(); final NacosClientProperties nacosClientProperties = NacosClientProperties.PROTOTYPE.derive(props); - NamingClientProxyDelegate delegate = new NamingClientProxyDelegate(ns, holder, watchServiceListHolder, + NamingClientProxyDelegate delegate = new NamingClientProxyDelegate(ns, holder, fuzzyWatchServiceListHolder, nacosClientProperties, notifier); NamingGrpcClientProxy mockGrpcClient = Mockito.mock(NamingGrpcClientProxy.class); Field grpcClientProxyField = NamingClientProxyDelegate.class.getDeclaredField("grpcClientProxy"); @@ -243,13 +243,13 @@ public void testQueryInstancesOfService() throws NacosException, IllegalAccessEx public void testQueryService() throws NacosException { String ns = "ns1"; ServiceInfoHolder holder = Mockito.mock(ServiceInfoHolder.class); - WatchServiceListHolder watchServiceListHolder = Mockito.mock(WatchServiceListHolder.class); + FuzzyWatchServiceListHolder fuzzyWatchServiceListHolder = Mockito.mock(FuzzyWatchServiceListHolder.class); Properties props = new Properties(); props.setProperty("serverAddr", "localhost"); InstancesChangeNotifier notifier = new InstancesChangeNotifier(); final NacosClientProperties nacosClientProperties = NacosClientProperties.PROTOTYPE.derive(props); - NamingClientProxyDelegate delegate = new NamingClientProxyDelegate(ns, holder, watchServiceListHolder, + NamingClientProxyDelegate delegate = new NamingClientProxyDelegate(ns, holder, fuzzyWatchServiceListHolder, nacosClientProperties, notifier); Service service = delegate.queryService("a", "b"); Assert.assertNull(service); @@ -259,13 +259,13 @@ public void testQueryService() throws NacosException { public void testCreateService() throws NacosException { String ns = "ns1"; ServiceInfoHolder holder = Mockito.mock(ServiceInfoHolder.class); - WatchServiceListHolder watchServiceListHolder = Mockito.mock(WatchServiceListHolder.class); + FuzzyWatchServiceListHolder fuzzyWatchServiceListHolder = Mockito.mock(FuzzyWatchServiceListHolder.class); Properties props = new Properties(); props.setProperty("serverAddr", "localhost"); InstancesChangeNotifier notifier = new InstancesChangeNotifier(); final NacosClientProperties nacosClientProperties = NacosClientProperties.PROTOTYPE.derive(props); - NamingClientProxyDelegate delegate = new NamingClientProxyDelegate(ns, holder, watchServiceListHolder, + NamingClientProxyDelegate delegate = new NamingClientProxyDelegate(ns, holder, fuzzyWatchServiceListHolder, nacosClientProperties, notifier); Service service = new Service(); try { @@ -279,13 +279,13 @@ public void testCreateService() throws NacosException { public void testDeleteService() throws NacosException { String ns = "ns1"; ServiceInfoHolder holder = Mockito.mock(ServiceInfoHolder.class); - WatchServiceListHolder watchServiceListHolder = Mockito.mock(WatchServiceListHolder.class); + FuzzyWatchServiceListHolder fuzzyWatchServiceListHolder = Mockito.mock(FuzzyWatchServiceListHolder.class); Properties props = new Properties(); props.setProperty("serverAddr", "localhost"); InstancesChangeNotifier notifier = new InstancesChangeNotifier(); final NacosClientProperties nacosClientProperties = NacosClientProperties.PROTOTYPE.derive(props); - NamingClientProxyDelegate delegate = new NamingClientProxyDelegate(ns, holder, watchServiceListHolder, + NamingClientProxyDelegate delegate = new NamingClientProxyDelegate(ns, holder, fuzzyWatchServiceListHolder, nacosClientProperties, notifier); Assert.assertFalse(delegate.deleteService("service", "group1")); } @@ -294,13 +294,13 @@ public void testDeleteService() throws NacosException { public void testUpdateService() throws NacosException { String ns = "ns1"; ServiceInfoHolder holder = Mockito.mock(ServiceInfoHolder.class); - WatchServiceListHolder watchServiceListHolder = Mockito.mock(WatchServiceListHolder.class); + FuzzyWatchServiceListHolder fuzzyWatchServiceListHolder = Mockito.mock(FuzzyWatchServiceListHolder.class); Properties props = new Properties(); props.setProperty("serverAddr", "localhost"); InstancesChangeNotifier notifier = new InstancesChangeNotifier(); final NacosClientProperties nacosClientProperties = NacosClientProperties.PROTOTYPE.derive(props); - NamingClientProxyDelegate delegate = new NamingClientProxyDelegate(ns, holder, watchServiceListHolder, + NamingClientProxyDelegate delegate = new NamingClientProxyDelegate(ns, holder, fuzzyWatchServiceListHolder, nacosClientProperties, notifier); Service service = new Service(); try { @@ -314,13 +314,13 @@ public void testUpdateService() throws NacosException { public void testGetServiceList() throws NacosException, NoSuchFieldException, IllegalAccessException { String ns = "ns1"; ServiceInfoHolder holder = Mockito.mock(ServiceInfoHolder.class); - WatchServiceListHolder watchServiceListHolder = Mockito.mock(WatchServiceListHolder.class); + FuzzyWatchServiceListHolder fuzzyWatchServiceListHolder = Mockito.mock(FuzzyWatchServiceListHolder.class); Properties props = new Properties(); props.setProperty("serverAddr", "localhost"); InstancesChangeNotifier notifier = new InstancesChangeNotifier(); final NacosClientProperties nacosClientProperties = NacosClientProperties.PROTOTYPE.derive(props); - NamingClientProxyDelegate delegate = new NamingClientProxyDelegate(ns, holder, watchServiceListHolder, + NamingClientProxyDelegate delegate = new NamingClientProxyDelegate(ns, holder, fuzzyWatchServiceListHolder, nacosClientProperties, notifier); NamingGrpcClientProxy mockGrpcClient = Mockito.mock(NamingGrpcClientProxy.class); Field grpcClientProxyField = NamingClientProxyDelegate.class.getDeclaredField("grpcClientProxy"); @@ -340,13 +340,13 @@ public void testGetServiceList() throws NacosException, NoSuchFieldException, Il public void testSubscribe() throws NacosException, NoSuchFieldException, IllegalAccessException { String ns = "ns1"; ServiceInfoHolder holder = Mockito.mock(ServiceInfoHolder.class); - WatchServiceListHolder watchServiceListHolder = Mockito.mock(WatchServiceListHolder.class); + FuzzyWatchServiceListHolder fuzzyWatchServiceListHolder = Mockito.mock(FuzzyWatchServiceListHolder.class); Properties props = new Properties(); props.setProperty("serverAddr", "localhost"); InstancesChangeNotifier notifier = new InstancesChangeNotifier(); final NacosClientProperties nacosClientProperties = NacosClientProperties.PROTOTYPE.derive(props); - NamingClientProxyDelegate delegate = new NamingClientProxyDelegate(ns, holder, watchServiceListHolder, + NamingClientProxyDelegate delegate = new NamingClientProxyDelegate(ns, holder, fuzzyWatchServiceListHolder, nacosClientProperties, notifier); NamingGrpcClientProxy mockGrpcClient = Mockito.mock(NamingGrpcClientProxy.class); Field grpcClientProxyField = NamingClientProxyDelegate.class.getDeclaredField("grpcClientProxy"); @@ -373,13 +373,13 @@ public void testSubscribe() throws NacosException, NoSuchFieldException, Illegal public void testUnsubscribe() throws NacosException, IllegalAccessException, NoSuchFieldException { String ns = "ns1"; ServiceInfoHolder holder = Mockito.mock(ServiceInfoHolder.class); - WatchServiceListHolder watchServiceListHolder = Mockito.mock(WatchServiceListHolder.class); + FuzzyWatchServiceListHolder fuzzyWatchServiceListHolder = Mockito.mock(FuzzyWatchServiceListHolder.class); Properties props = new Properties(); props.setProperty("serverAddr", "localhost"); InstancesChangeNotifier notifier = new InstancesChangeNotifier(); final NacosClientProperties nacosClientProperties = NacosClientProperties.PROTOTYPE.derive(props); - NamingClientProxyDelegate delegate = new NamingClientProxyDelegate(ns, holder, watchServiceListHolder, + NamingClientProxyDelegate delegate = new NamingClientProxyDelegate(ns, holder, fuzzyWatchServiceListHolder, nacosClientProperties, notifier); NamingGrpcClientProxy mockGrpcClient = Mockito.mock(NamingGrpcClientProxy.class); Field grpcClientProxyField = NamingClientProxyDelegate.class.getDeclaredField("grpcClientProxy"); @@ -398,13 +398,13 @@ public void testUnsubscribe() throws NacosException, IllegalAccessException, NoS public void testServerHealthy() throws IllegalAccessException, NacosException, NoSuchFieldException { String ns = "ns1"; ServiceInfoHolder holder = Mockito.mock(ServiceInfoHolder.class); - WatchServiceListHolder watchServiceListHolder = Mockito.mock(WatchServiceListHolder.class); + FuzzyWatchServiceListHolder fuzzyWatchServiceListHolder = Mockito.mock(FuzzyWatchServiceListHolder.class); Properties props = new Properties(); props.setProperty("serverAddr", "localhost"); InstancesChangeNotifier notifier = new InstancesChangeNotifier(); final NacosClientProperties nacosClientProperties = NacosClientProperties.PROTOTYPE.derive(props); - NamingClientProxyDelegate delegate = new NamingClientProxyDelegate(ns, holder, watchServiceListHolder, + NamingClientProxyDelegate delegate = new NamingClientProxyDelegate(ns, holder, fuzzyWatchServiceListHolder, nacosClientProperties, notifier); NamingGrpcClientProxy mockGrpcClient = Mockito.mock(NamingGrpcClientProxy.class); Field grpcClientProxyField = NamingClientProxyDelegate.class.getDeclaredField("grpcClientProxy"); @@ -419,13 +419,13 @@ public void testServerHealthy() throws IllegalAccessException, NacosException, N public void testShutdown() throws NacosException, IllegalAccessException, NoSuchFieldException { String ns = "ns1"; ServiceInfoHolder holder = Mockito.mock(ServiceInfoHolder.class); - WatchServiceListHolder watchServiceListHolder = Mockito.mock(WatchServiceListHolder.class); + FuzzyWatchServiceListHolder fuzzyWatchServiceListHolder = Mockito.mock(FuzzyWatchServiceListHolder.class); Properties props = new Properties(); props.setProperty("serverAddr", "localhost"); InstancesChangeNotifier notifier = new InstancesChangeNotifier(); final NacosClientProperties nacosClientProperties = NacosClientProperties.PROTOTYPE.derive(props); - NamingClientProxyDelegate delegate = new NamingClientProxyDelegate(ns, holder, watchServiceListHolder, + NamingClientProxyDelegate delegate = new NamingClientProxyDelegate(ns, holder, fuzzyWatchServiceListHolder, nacosClientProperties, notifier); NamingGrpcClientProxy mockGrpcClient = Mockito.mock(NamingGrpcClientProxy.class); Field grpcClientProxyField = NamingClientProxyDelegate.class.getDeclaredField("grpcClientProxy"); 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 7890443dd22..9bd64b1b6d1 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 @@ -43,7 +43,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.WatchServiceListHolder; +import com.alibaba.nacos.client.naming.cache.FuzzyWatchServiceListHolder; import com.alibaba.nacos.client.naming.event.ServerListChangedEvent; import com.alibaba.nacos.client.naming.remote.gprc.redo.NamingGrpcRedoService; import com.alibaba.nacos.client.security.SecurityProxy; @@ -114,7 +114,7 @@ public class NamingGrpcClientProxyTest { private ServiceInfoHolder holder; @Mock - private WatchServiceListHolder watchServiceListHolder; + private FuzzyWatchServiceListHolder fuzzyWatchServiceListHolder; @Mock private RpcClient rpcClient; @@ -143,7 +143,7 @@ public void setUp() throws NacosException, NoSuchFieldException, IllegalAccessEx final NacosClientProperties nacosClientProperties = NacosClientProperties.PROTOTYPE.derive(prop); client = new NamingGrpcClientProxy(NAMESPACE_ID, proxy, factory, nacosClientProperties, holder, - watchServiceListHolder); + fuzzyWatchServiceListHolder); Field uuidField = NamingGrpcClientProxy.class.getDeclaredField("uuid"); uuidField.setAccessible(true); @@ -506,7 +506,7 @@ public void close() { rpcClient.set(client, rpc); rpc.serverListFactory(factory); - rpc.registerServerRequestHandler(new NamingPushRequestHandler(holder, watchServiceListHolder)); + rpc.registerServerRequestHandler(new NamingPushRequestHandler(holder, fuzzyWatchServiceListHolder)); Field listenerField = NamingGrpcClientProxy.class.getDeclaredField("redoService"); listenerField.setAccessible(true); NamingGrpcRedoService listener = (NamingGrpcRedoService) listenerField.get(client); @@ -542,7 +542,7 @@ public void close() { public void testConfigAppNameLabels() throws Exception { final NacosClientProperties nacosClientProperties = NacosClientProperties.PROTOTYPE.derive(prop); client = new NamingGrpcClientProxy(NAMESPACE_ID, proxy, factory, nacosClientProperties, holder, - watchServiceListHolder); + 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 c31da262ed7..0f7075202a4 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 @@ -24,7 +24,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.cache.WatchServiceListHolder; +import com.alibaba.nacos.client.naming.cache.FuzzyWatchServiceListHolder; import org.junit.Assert; import org.junit.Test; @@ -38,8 +38,8 @@ public class NamingPushRequestHandlerTest { public void testRequestReply() { //given ServiceInfoHolder holder = mock(ServiceInfoHolder.class); - WatchServiceListHolder watchServiceListHolder = mock(WatchServiceListHolder.class); - NamingPushRequestHandler handler = new NamingPushRequestHandler(holder, watchServiceListHolder); + 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..b4a8e5b207d --- /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 23c7086a6f6..097e8c13e7c 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; @@ -133,6 +136,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 d80824f59e6..69865fad06c 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,19 +155,33 @@ 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; + } publisherIndexes.computeIfAbsent(service, key -> new ConcurrentHashSet<>()); publisherIndexes.get(service).add(clientId); - NotifyCenter.publishEvent(new ServiceEvent.ServiceChangedEvent(service, true)); + NotifyCenter.publishEvent(new ServiceEvent.ServiceChangedEvent(service, serviceChangedType, 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 f2223a49f1e..bc7606bf92d 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 @@ -146,6 +146,33 @@ public void unsubscribeService(Service service, Subscriber subscriber, String cl 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) { if (client == null) { Loggers.SRV_LOG.warn("Client connection {} already disconnect", clientId); 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 e7df05b3ee9..bdfb5180eeb 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 @@ -181,6 +181,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 6933779d0a5..2383fb430e2 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.getNamespace(), service.getGroup(), service.getName()); } 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 0aa980ba64f..e7c56d9528d 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 @@ public void testSubscribeTypes() { List> classes = clientServiceIndexesManager.subscribeTypes(); Assert.assertNotNull(classes); - Assert.assertEquals(classes.size(), 5); + Assert.assertEquals(classes.size(), 7); } @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 cb5a7dfb73a..6cc5121fd2a 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; @@ -117,7 +118,7 @@ public void testGetFuzzySubscribersByService() { @Test public void onEvent() { - subscriberService.onEvent(new ServiceEvent.ServiceChangedEvent(service)); + 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 021dc619518..dbbebaf9b63 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; @@ -40,6 +42,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 6f5e20e5b57a84142442a4e54aa23184bff64781 Mon Sep 17 00:00:00 2001 From: Chionanthus <33491015+Chionanthus@users.noreply.github.com> Date: Sun, 22 Oct 2023 22:14:36 -0500 Subject: [PATCH 4/4] [ISSUE #10380] Change Fuzzy Watch Pattern Rule (#11276) * change fuzzy watch service name pattern construct rule * add namespaceId in fuzzy watch redo log --- .../alibaba/nacos/api/common/Constants.java | 13 +-- .../nacos/api/naming/NamingService.java | 9 ++- .../nacos/api/naming/utils/NamingUtils.java | 79 ++++--------------- .../client/naming/NacosNamingService.java | 22 ++++-- .../remote/gprc/NamingGrpcClientProxy.java | 4 + .../remote/gprc/redo/RedoScheduledTask.java | 7 +- .../nacos/example/FuzzyWatchExample.java | 2 +- 7 files changed, 48 insertions(+), 88 deletions(-) diff --git a/api/src/main/java/com/alibaba/nacos/api/common/Constants.java b/api/src/main/java/com/alibaba/nacos/api/common/Constants.java index 017bbb7dccd..c670d9b00fa 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 @@ -204,6 +204,8 @@ 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"; @@ -240,17 +242,6 @@ public static class Naming { public static final String CMDB_CONTEXT_TYPE = "CMDB"; } - /** - * The constants in fuzzy watch pattern match rule directory. - */ - public static class FuzzyWatchMatchRule { - - public static final String MATCH_ALL = "MATCH_ALL"; - - public static final String MATCH_PREFIX = "MATCH_PREFIX"; - - } - /** * The constants in fuzzy watch event type 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 fba95bb6e16..75d7d9264f3 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 @@ -534,7 +534,9 @@ void unsubscribe(String serviceName, String groupName, List clusters, Ev throws NacosException; /** - * Watch a range of services by rule to receive notify events of matched services alteration. + * 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 @@ -543,7 +545,10 @@ void unsubscribe(String serviceName, String groupName, List clusters, Ev void fuzzyWatch(String fixedGroupName, AbstractFuzzyWatchEventListener listener) throws NacosException; /** - * Watch a range of services by rule to receive notify events of matched services alteration. + * 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 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 b29b9639918..2207e97d3bc 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 @@ -30,6 +30,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.FUZZY_WATCH_PATTERN_WILDCARD; import static com.alibaba.nacos.api.common.Constants.NUMBER_PATTERN_STRING; /** @@ -209,68 +210,20 @@ public static String getPatternRemovedNamespace(String completedPattern) { /** * Get the pattern watched under given namespace id. + * * @param namespaceId name space id - * @param completedPattern a set of all watch pattern(with namespace id) + * @param completedPatterns a set of all watched pattern(with namespace id) * @return filtered pattern set */ - public static Set filterPatternWithNamespace(String namespaceId, Set completedPattern) { - Set patterns = new HashSet<>(); - for (String each : completedPattern) { - String eachId = getNamespaceFromPattern(each); - if (namespaceId.equals(eachId)) { - patterns.add(getPatternRemovedNamespace(each)); + 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 patterns; - } - - /** - * Returns a combined string with matchPattern and matchType. - * @param matchPattern a match pattern. Such as a 'serviceNamePrefix' - * @param matchType The match type want to use - * @return grouped pattern like 'matchPattern##matchType' - */ - public static String getGroupedPattern(final String matchPattern, final String matchType) { - if (StringUtils.isBlank(matchPattern) && !matchType.equals(Constants.FuzzyWatchMatchRule.MATCH_ALL)) { - throw new IllegalArgumentException("Param 'matchPattern' is illegal, matchPattern is blank"); - } - if (StringUtils.isBlank(matchType)) { - throw new IllegalArgumentException("Param 'matchType' is illegal, matchType is blank"); - } else if (matchType.equals(Constants.FuzzyWatchMatchRule.MATCH_ALL)) { - return Constants.FuzzyWatchMatchRule.MATCH_ALL; - } - final String resultGroupedName = matchPattern + Constants.MATCH_PATTERN_SPLITER + matchType; - return resultGroupedName.intern(); - } - - /** - * Given a Pattern, return the string to be used for the match. - * @param groupedPattern a grouped pattern, format: [match string ## match type] - * @return the string to be used for the match. - */ - public static String getMatchName(String groupedPattern) { - if (StringUtils.isBlank(groupedPattern)) { - return StringUtils.EMPTY; - } - if (!groupedPattern.contains(Constants.MATCH_PATTERN_SPLITER)) { - return groupedPattern; - } - return groupedPattern.split(Constants.MATCH_PATTERN_SPLITER)[0]; - } - - /** - * Given a Pattern, return the matching rule type. - * @param groupedPattern a grouped pattern (match string ## match type) - * @return the matching rule type. - */ - public static String getMatchRule(String groupedPattern) { - if (StringUtils.isBlank(groupedPattern)) { - return StringUtils.EMPTY; - } - if (!groupedPattern.contains(Constants.MATCH_PATTERN_SPLITER)) { - return Constants.FuzzyWatchMatchRule.MATCH_ALL; - } - return groupedPattern.split(Constants.MATCH_PATTERN_SPLITER)[1]; + return patternsOfGivenNamespace; } /** @@ -318,6 +271,7 @@ public static Set getPatternMatchedServices(Collection servicesL /** * 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 @@ -326,14 +280,13 @@ public static Set getPatternMatchedServices(Collection servicesL * @return matching result */ public static boolean isMatchPattern(String serviceName, String groupName, String serviceNamePattern, String groupNamePattern) { - String serviceMatchName = getMatchName(serviceNamePattern); - String serviceMatchType = getMatchRule(serviceNamePattern); - // Only support prefix match or all match right now + // Only support prefix match or all match service name right now // Only support fixed group name right now - if (serviceMatchType.equals(Constants.FuzzyWatchMatchRule.MATCH_ALL)) { + if (serviceNamePattern.equals(FUZZY_WATCH_PATTERN_WILDCARD)) { return groupName.equals(groupNamePattern); - } else if (serviceMatchType.equals(Constants.FuzzyWatchMatchRule.MATCH_PREFIX)) { - return prefixMatchWithFixedGroupName(serviceName, serviceMatchName, groupName, getMatchName(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; } 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 eb7e90beb42..d7afbd0c99b 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 @@ -50,6 +50,8 @@ import java.util.Properties; import java.util.UUID; +import static com.alibaba.nacos.api.common.Constants.FUZZY_WATCH_PATTERN_WILDCARD; + /** * Nacos Naming Service. * @@ -446,17 +448,22 @@ public void unsubscribe(String serviceName, String groupName, List clust @Override public void fuzzyWatch(String fixedGroupName, AbstractFuzzyWatchEventListener listener) throws NacosException { - // pattern e.g. DEFAULT_GROUP@@MATCH_ALL - doFuzzyWatch(Constants.FuzzyWatchMatchRule.MATCH_ALL, fixedGroupName, listener); + 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 - // pattern e.g. DEFAULT_GROUP@@nacos.test##MATCH_PREFIX - String serviceNamePrefixPattern = NamingUtils.getGroupedPattern(serviceNamePattern, Constants.FuzzyWatchMatchRule.MATCH_PREFIX); - doFuzzyWatch(serviceNamePrefixPattern, fixedGroupName, listener); + 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, @@ -472,13 +479,12 @@ private void doFuzzyWatch(String serviceNamePattern, String groupNamePattern, @Override public void cancelFuzzyWatch(String fixedGroupName, AbstractFuzzyWatchEventListener listener) throws NacosException { - doCancelFuzzyWatch(Constants.FuzzyWatchMatchRule.MATCH_ALL, fixedGroupName, listener); + doCancelFuzzyWatch(FUZZY_WATCH_PATTERN_WILDCARD, fixedGroupName, listener); } @Override public void cancelFuzzyWatch(String serviceNamePattern, String fixedGroupName, AbstractFuzzyWatchEventListener listener) throws NacosException { - String serviceNamePrefixPattern = NamingUtils.getGroupedPattern(serviceNamePattern, Constants.FuzzyWatchMatchRule.MATCH_PREFIX); - doCancelFuzzyWatch(serviceNamePrefixPattern, fixedGroupName, listener); + doCancelFuzzyWatch(serviceNamePattern, fixedGroupName, listener); } private void doCancelFuzzyWatch(String serviceNamePattern, String groupNamePattern, 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 e29be80f042..588cc3e08db 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 @@ -456,4 +456,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/RedoScheduledTask.java b/client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/redo/RedoScheduledTask.java index f9166cc4529..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 @@ -146,8 +146,8 @@ private void redoForFuzzyWatchers() { try { redoForFuzzyWatcher(each); } catch (NacosException e) { - LogUtils.NAMING_LOGGER.error("Redo fuzzy watcher operation {} for pattern {}@@{} failed. ", each.getRedoType(), - each.getGroupName(), each.getServiceName(), e); + LogUtils.NAMING_LOGGER.error("Redo fuzzy watcher operation {} for pattern {}@@{} in namespace {} failed. ", + each.getRedoType(), each.getGroupName(), each.getServiceName(), clientProxy.getNamespaceId(), e); } } } @@ -156,7 +156,8 @@ private void redoForFuzzyWatcher(FuzzyWatcherRedoData redoData) throws NacosExce RedoData.RedoType redoType = redoData.getRedoType(); String serviceNamePattern = redoData.getServiceName(); String groupNamePattern = redoData.getGroupName(); - LogUtils.NAMING_LOGGER.info("Redo fuzzy watcher operation {} for pattern {}@@{}", redoType, groupNamePattern, serviceNamePattern); + LogUtils.NAMING_LOGGER.info("Redo fuzzy watcher operation {} for pattern {}@@{} in namespace {}", redoType, + groupNamePattern, serviceNamePattern, clientProxy.getNamespaceId()); switch (redoType) { case REGISTER: if (isClientDisabled()) { 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 b4a8e5b207d..20f0829eb94 100644 --- a/example/src/main/java/com/alibaba/nacos/example/FuzzyWatchExample.java +++ b/example/src/main/java/com/alibaba/nacos/example/FuzzyWatchExample.java @@ -78,7 +78,7 @@ public void onEvent(FuzzyWatchNotifyEvent event) { } }); - naming.fuzzyWatch("nacos.test", DEFAULT_GROUP, new AbstractFuzzyWatchEventListener() { + naming.fuzzyWatch("nacos.test.*", DEFAULT_GROUP, new AbstractFuzzyWatchEventListener() { @Override public Executor getExecutor() {