+ * Typically, do not worry about their ready status, because they are initialized before
+ * any {@link ServiceConfig} exports, or The Dubbo export will be failed.
+ *
+ * @see MetadataServiceExporter
+ * @see ServiceConfig
+ * @see ConfigManager
+ * @since 2.7.2
+ */
+public class ConfigurableMetadataServiceExporter implements MetadataServiceExporter {
+
+ private final Logger logger = LoggerFactory.getLogger(getClass());
+
+ /**
+ * {@link ConfigManager} stores {@link AbstractConfig the Dubbo *Config instances}
+ */
+ private final ConfigManager configManager = ConfigManager.getInstance();
+
+ private volatile ServiceConfig
executor is null
+ */
+ protected AbstractEventDispatcher(Executor executor) {
+ if (executor == null) {
+ throw new NullPointerException("executor must not be null");
+ }
+ this.executor = executor;
+ this.loadEventListenerInstances();
+ }
+
+ @Override
+ public void addEventListener(EventListener> listener) throws NullPointerException, IllegalArgumentException {
+ Listenable.assertListener(listener);
+ doInListener(listener, listeners -> {
+ addIfAbsent(listeners, listener);
+ });
+ }
+
+ @Override
+ public void removeEventListener(EventListener> listener) throws NullPointerException, IllegalArgumentException {
+ Listenable.assertListener(listener);
+ doInListener(listener, listeners -> listeners.remove(listener));
+ }
+
+ @Override
+ public List
+ * It could be override by the sub-class
+ *
+ * @see EventListener
+ * @see ServiceLoader#load(Class)
+ */
+ protected void loadEventListenerInstances() {
+ ServiceLoader
+ * The {@link #onEvent(Event) handle method} will be notified when the matched-type {@link Event Dubbo Event} is
+ * published, whose priority could be changed by {@link #getPriority()} method.
+ *
+ * @param null, the behavior is same as default.
+ * @see #DIRECT_EXECUTOR
+ */
+ default Executor getExecutor() {
+ return DIRECT_EXECUTOR;
+ }
+
+ /**
+ * The default extension of {@link EventDispatcher} is loaded by {@link ExtensionLoader}
+ *
+ * @return the default extension of {@link EventDispatcher}
+ */
+ static EventDispatcher getDefaultExtension() {
+ return ExtensionLoader.getExtensionLoader(EventDispatcher.class).getDefaultExtension();
+ }
+}
diff --git a/dubbo-event/src/main/java/org/apache/dubbo/event/EventListener.java b/dubbo-event/src/main/java/org/apache/dubbo/event/EventListener.java
new file mode 100644
index 000000000000..92c35b9c8824
--- /dev/null
+++ b/dubbo-event/src/main/java/org/apache/dubbo/event/EventListener.java
@@ -0,0 +1,124 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.event;
+
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.Objects;
+
+import static java.lang.Integer.compare;
+import static org.apache.dubbo.common.utils.ReflectUtils.findParameterizedTypes;
+
+/**
+ * The {@link Event Dubbo Event} Listener that is based on Java standard {@link java.util.EventListener} interface supports
+ * the generic {@link Event}.
+ * null if not found
+ */
+ static Class extends Event> findEventType(EventListener> listener) {
+ return findEventType(listener.getClass());
+ }
+
+ /**
+ * Find the {@link Class type} {@link Event Dubbo event} from the specified {@link EventListener Dubbo event listener}
+ *
+ * @param listenerClass the {@link Class class} of {@link EventListener Dubbo event listener}
+ * @return null if not found
+ */
+ static Class extends Event> findEventType(Class> listenerClass) {
+ Class extends Event> eventType = null;
+
+ if (listenerClass != null && EventListener.class.isAssignableFrom(listenerClass)) {
+ eventType = findParameterizedTypes(listenerClass)
+ .stream()
+ .map(EventListener::findEventType)
+ .filter(Objects::nonNull)
+ .findAny()
+ .orElse((Class) findEventType(listenerClass.getSuperclass()));
+ }
+
+ return eventType;
+ }
+
+ /**
+ * Find the type {@link Event Dubbo event} from the specified {@link ParameterizedType} presents
+ * a class of {@link EventListener Dubbo event listener}
+ *
+ * @param parameterizedType the {@link ParameterizedType} presents a class of {@link EventListener Dubbo event listener}
+ * @return null if not found
+ */
+ static Class extends Event> findEventType(ParameterizedType parameterizedType) {
+ Class extends Event> eventType = null;
+
+ Type rawType = parameterizedType.getRawType();
+ if ((rawType instanceof Class) && EventListener.class.isAssignableFrom((Class) rawType)) {
+ Type[] typeArguments = parameterizedType.getActualTypeArguments();
+ for (Type typeArgument : typeArguments) {
+ if (typeArgument instanceof Class) {
+ Class argumentClass = (Class) typeArgument;
+ if (Event.class.isAssignableFrom(argumentClass)) {
+ eventType = argumentClass;
+ break;
+ }
+ }
+ }
+ }
+
+ return eventType;
+ }
+}
\ No newline at end of file
diff --git a/dubbo-event/src/main/java/org/apache/dubbo/event/GenericEvent.java b/dubbo-event/src/main/java/org/apache/dubbo/event/GenericEvent.java
new file mode 100644
index 000000000000..591bcebb086a
--- /dev/null
+++ b/dubbo-event/src/main/java/org/apache/dubbo/event/GenericEvent.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.event;
+
+/**
+ * Generic {@link Event Dubbo event}
+ *
+ * @param the type of event source
+ * @since 2.7.2
+ */
+public class GenericEvent extends Event {
+
+ public GenericEvent(S source) {
+ super(source);
+ }
+
+ public S getSource() {
+ return (S) super.getSource();
+ }
+}
diff --git a/dubbo-event/src/main/java/org/apache/dubbo/event/Listenable.java b/dubbo-event/src/main/java/org/apache/dubbo/event/Listenable.java
new file mode 100644
index 000000000000..1857ac85f3f3
--- /dev/null
+++ b/dubbo-event/src/main/java/org/apache/dubbo/event/Listenable.java
@@ -0,0 +1,131 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.event;
+
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import static java.util.stream.StreamSupport.stream;
+
+/**
+ * Dubbo Event Listenable
+ *
+ * @see EventDispatcher
+ * @since 2.7.2
+ */
+public interface Listenablefalse
+ * @throws NullPointerException if listener argument is null
+ * @throws IllegalArgumentException if listener argument is not concrete instance
+ */
+ void addEventListener(E listener) throws NullPointerException, IllegalArgumentException;
+
+ /**
+ * Add one or more {@link EventListener Dubbo event listeners}
+ *
+ * @param listener a {@link EventListener Dubbo event listener}
+ * @param others an optional {@link EventListener Dubbo event listeners}
+ * @throws NullPointerException if one of arguments is null
+ * @throws IllegalArgumentException if one of arguments argument is not concrete instance
+ */
+ default void addEventListeners(E listener, E... others) throws NullPointerException,
+ IllegalArgumentException {
+ Listlisteners argument is null
+ * @throws IllegalArgumentException if any element of listeners is not concrete instance
+ */
+ default void addEventListeners(Iterabletrue.
+ * If current {@link EventListener} is existed, return false
+ * @throws NullPointerException if listener argument is null
+ */
+ void removeEventListener(E listener) throws NullPointerException, IllegalArgumentException;
+
+ /**
+ * Remove a {@link EventListener Dubbo event listener}
+ *
+ * @param listeners the {@link EventListener Dubbo event listeners}
+ * @return If remove successfully, return true.
+ * If current {@link EventListener} is existed, return false
+ * @throws NullPointerException if listener argument is null
+ * @throws IllegalArgumentException if any element of listeners is not concrete instance
+ */
+ default void removeEventListeners(Iterabletrue
+ */
+ boolean exportURL(URL url);
+
+ /**
+ * Unexports a {@link URL}
+ *
+ * @param url a {@link URL}
+ * @return If success , return true
+ */
+ boolean unexportURL(URL url);
+
+ /**
+ * Subscribes a {@link URL}
+ *
+ * @param url a {@link URL}
+ * @return If success , return true
+ */
+ boolean subscribeURL(URL url);
+
+ /**
+ * Unsubscribes a {@link URL}
+ *
+ * @param url a {@link URL}
+ * @return If success , return true
+ */
+ boolean unsubscribeURL(URL url);
+
+
+ /**
+ * Get {@link ExtensionLoader#getDefaultExtension() the defautl extension} of {@link LocalMetadataService}
+ *
+ * @return non-null
+ * @see InMemoryLocalMetadataService
+ */
+ public static LocalMetadataService getDefaultExtension() {
+ return getExtensionLoader(LocalMetadataService.class).getDefaultExtension();
+ }
+
+}
diff --git a/dubbo-metadata/src/main/java/org/apache/dubbo/metadata/MetadataService.java b/dubbo-metadata/src/main/java/org/apache/dubbo/metadata/MetadataService.java
new file mode 100644
index 000000000000..4cbbae578351
--- /dev/null
+++ b/dubbo-metadata/src/main/java/org/apache/dubbo/metadata/MetadataService.java
@@ -0,0 +1,151 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.metadata;
+
+import org.apache.dubbo.common.URL;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+import static java.util.stream.StreamSupport.stream;
+
+/**
+ * A framework interface of Dubbo Metadata Service defines the contract of Dubbo Services registartion and subscription
+ * between Dubbo service providers and its consumers. The implementationwill be exported as a normal Dubbo service that
+ * the clients would subscribe, whose version comes from the {@link #version()} method and group gets from
+ * {@link #serviceName()}, that means, The different Dubbo service(application) will export the different
+ * {@link MetadataService} that persists all the exported and subscribed metadata, they are present by
+ * {@link #getExportedURLs()} and {@link #getSubscribedURLs()} respectively. What's more, {@link MetadataService}
+ * also providers the fine-grain methods for the precise queries.
+ *
+ * @see InMemoryLocalMetadataService
+ * @since 2.7.2
+ */
+public interface MetadataService {
+
+ /**
+ * The value of all service names
+ */
+ String ALL_SERVICE_NAMES = "*";
+
+ /**
+ * The value of All service instances
+ */
+ String ALL_SERVICE_INTERFACES = "*";
+
+ /**
+ * The contract version of {@link MetadataService}, the future update must make sure compatible.
+ */
+ String VERSION = "1.0.0";
+
+ /**
+ * Gets the current Dubbo Service name
+ *
+ * @return non-null
+ */
+ String serviceName();
+
+ /**
+ * Gets the version of {@link MetadataService} that always equals {@link #VERSION}
+ *
+ * @return non-null
+ * @see #VERSION
+ */
+ default String version() {
+ return VERSION;
+ }
+
+ /**
+ * the list of String that presents all Dubbo subscribed {@link URL urls}
+ *
+ * @return non-null read-only {@link List}
+ */
+ ListserviceInterface
+ *
+ * @param serviceInterface The class name of Dubbo service interface
+ * @return non-null read-only {@link List}
+ * @see URL
+ */
+ default ListserviceInterface and group
+ *
+ * @param serviceInterface The class name of Dubbo service interface
+ * @param group the Dubbo Service Group (optional)
+ * @return non-null read-only {@link List}
+ * @see URL
+ */
+ default ListserviceInterface, group and version
+ *
+ * @param serviceInterface The class name of Dubbo service interface
+ * @param group the Dubbo Service Group (optional)
+ * @param version the Dubbo Service Version (optional)
+ * @return non-null read-only {@link List}
+ * @see URL
+ */
+ default ListserviceInterface, group, version and protocol
+ *
+ * @param serviceInterface The class name of Dubbo service interface
+ * @param group the Dubbo Service Group (optional)
+ * @param version the Dubbo Service Version (optional)
+ * @param protocol the Dubbo Service Protocol (optional)
+ * @return non-null read-only {@link List}
+ * @see URL
+ */
+ List
serviceName is null is null
+ */
+ ListserviceName is null is null
+ */
+ default int getTotalSizeInstances(String serviceName) throws NullPointerException {
+ return getInstances(serviceName).size();
+ }
+
+ /**
+ * Gets the {@link Page pagination} of {@link ServiceInstance service instances} by the specified service name.
+ * It's equal to {@link #getInstances(String, int, int, boolean)} with healthyOnly == true
+ *
+ * @param serviceName the service name
+ * @param offset the offset of request , the number "0" indicates first page
+ * @param requestSize the number of request, the {@link Integer#MAX_VALUE max value} indicates the range is unlimited
+ * @return non-null {@link Page} object
+ * @throws NullPointerException if serviceName is null is null
+ * @throws IllegalArgumentException if offset or requestSize is negative number
+ */
+ default PagehealthyOnly == true, filter healthy instances only.
+ *
+ * @param serviceName the service name
+ * @param offset the offset of request , the number "0" indicates first page
+ * @param requestSize the number of request, the {@link Integer#MAX_VALUE max value} indicates the range is unlimited
+ * @param healthyOnly if true , filter healthy instances only
+ * @return non-null {@link Page} object
+ * @throws NullPointerException if serviceName is null is null
+ * @throws IllegalArgumentException if offset or requestSize is negative number
+ */
+ default PageserviceName is null is null
+ * @throws IllegalArgumentException if offset or requestSize is negative number
+ */
+ default Map
+ *
+ * @since 2.7.2
+ */
+public interface ServiceInstance {
+
+ /**
+ * The id of the registered service instance.
+ *
+ * @return nullable
+ */
+ String getId();
+
+ /**
+ * The name of service that current instance belongs to.
+ *
+ * @return non-null
+ */
+ String getServiceName();
+
+ /**
+ * The hostname of the registered service instance.
+ *
+ * @return non-null
+ */
+ String getHost();
+
+ /**
+ * The port of the registered service instance.
+ *
+ * @return the positive integer
+ */
+ int getPort();
+
+ /**
+ * The enable status of the registered service instance.
+ *
+ * @return if true, indicates current instance is enabled, or disable, the client should remove this one.
+ * The default value is true
+ */
+ default boolean isEnabled() {
+ return true;
+ }
+
+ /**
+ * The registered service instance is health or not.
+ *
+ * @return if true, indicates current instance is enabled, or disable, the client may ignore this one.
+ * The default value is true
+ */
+ default boolean isHealthy() {
+ return true;
+ }
+
+ /**
+ * The key / value pair metadata associated with the service instance.
+ *
+ * @return non-null, mutable and unsorted {@link Map}
+ */
+ Maptrue, or false
+ */
+ boolean equals(Object another);
+}
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceRegistry.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceRegistry.java
new file mode 100644
index 000000000000..07b1bfceba68
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceRegistry.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.registry.client;
+
+/**
+ * The common interface to register and unregister for a service registry
+ *
+ * @since 2.7.2
+ */
+public interface ServiceRegistry {
+
+ /**
+ * A human-readable description of the implementation
+ *
+ * @return The description.
+ */
+ String toString();
+
+ /**
+ * Registers an instance of {@link ServiceInstance}.
+ *
+ * @param serviceInstance an instance of {@link ServiceInstance} to be registered
+ * @throws RuntimeException if failed
+ */
+ void register(ServiceInstance serviceInstance) throws RuntimeException;
+
+ /**
+ * Updates the registered {@link ServiceInstance}.
+ *
+ * @param serviceInstance the registered {@link ServiceInstance}
+ * @throws RuntimeException if failed
+ */
+ void update(ServiceInstance serviceInstance) throws RuntimeException;
+
+ /**
+ * Unregisters an instance of {@link ServiceInstance}.
+ *
+ * @param serviceInstance an instance of {@link ServiceInstance} to be deregistered
+ * @throws RuntimeException if failed
+ */
+ void unregister(ServiceInstance serviceInstance) throws RuntimeException;
+
+ /**
+ * Starts the ServiceRegistry. This is a lifecycle method.
+ */
+ void start();
+
+ /**
+ * Stops the ServiceRegistry. This is a lifecycle method.
+ */
+ void stop();
+}
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/event/ServiceDiscoveryChangeEvent.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/event/ServiceDiscoveryChangeEvent.java
new file mode 100644
index 000000000000..1bbf768081c1
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/event/ServiceDiscoveryChangeEvent.java
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.registry.client.event;
+
+import org.apache.dubbo.event.Event;
+import org.apache.dubbo.registry.client.ServiceInstance;
+
+import java.util.Collection;
+import java.util.EventObject;
+
+import static java.util.Collections.unmodifiableCollection;
+
+/**
+ * The Service Discovery Change {@link EventObject Event}
+ *
+ * @see ServiceDiscoveryChangeListener
+ * @since 2.7.2
+ */
+public class ServiceDiscoveryChangeEvent extends Event {
+
+ private final String serviceName;
+
+ private final Collection
+ * It's compatible with Spring Cloud
+ *
+ * @since 2.7.2
+ */
+public class ZookeeperInstance {
+
+ private String id;
+
+ private String name;
+
+ private Mapnull
+ */
+ public