This is an automated email from the ASF dual-hosted git repository.

liujun pushed a commit to branch 3.0
in repository https://gitbox.apache.org/repos/asf/dubbo.git


The following commit(s) were added to refs/heads/3.0 by this push:
     new eea2237  metadata read & write
eea2237 is described below

commit eea22371fec47ce948956c541304ac5bd0b72dfa
Author: ken.lj <ken.lj...@gmail.com>
AuthorDate: Mon Jun 15 10:21:28 2020 +0800

    metadata read & write
---
 .../dubbo/config/AbstractInterfaceConfig.java      |   2 +
 .../apache/dubbo/config/context/ConfigManager.java |   8 +
 .../org/apache/dubbo/config/ReferenceConfig.java   |  17 +-
 .../org/apache/dubbo/config/ServiceConfig.java     |  14 +-
 .../dubbo/config/bootstrap/DubboBootstrap.java     |  35 +++-
 .../metadata/DefaultMetadataParamsFilter.java      |  47 +++++
 .../org/apache/dubbo/metadata/MetadataInfo.java    | 100 +++++++++
 .../dubbo/metadata/MetadataParamsFilter.java}      |  20 +-
 .../org/apache/dubbo/metadata/MetadataService.java |   2 +
 .../org/apache/dubbo/metadata/MetadataUtil.java    |  49 -----
 .../apache/dubbo/metadata/RevisionResolver.java    |  57 +++++
 .../dubbo/metadata/WritableMetadataService.java    |  14 --
 .../dubbo/metadata/report/MetadataReport.java      |  22 +-
 .../metadata/report/MetadataReportInstance.java    |  24 ++-
 .../store/BaseWritableMetadataService.java         |  73 -------
 .../store/RemoteWritableMetadataService.java       | 233 ---------------------
 .../RemoteWritableMetadataServiceDelegate.java     | 100 ---------
 ...g.apache.dubbo.metadata.WritableMetadataService |   2 -
 .../InMemoryWritableMetadataServiceTest.java       | 149 -------------
 .../RemoteWritableMetadataServiceDelegateTest.java |   2 +-
 .../registry/client/ServiceDiscoveryRegistry.java  |   7 +-
 .../registry/client/ServiceInstanceCustomizer.java |   3 -
 .../CustomizableServiceInstanceListener.java       |  42 ----
 .../listener/ServiceInstancesChangedListener.java  |  26 ++-
 ...ExportedServicesRevisionMetadataCustomizer.java |  56 -----
 .../registry/client/metadata/MetadataUtils.java    | 112 ++++++++++
 .../metadata/ServiceInstanceMetadataUtils.java     |   7 +
 ...bscribedServicesRevisionMetadataCustomizer.java |  57 -----
 .../client/metadata/URLRevisionResolver.java       | 111 ----------
 .../proxy/BaseMetadataServiceProxyFactory.java     |  38 ----
 .../proxy/DefaultMetadataServiceProxyFactory.java  |  85 --------
 .../proxy/MetadataServiceProxyFactory.java         |  57 -----
 .../metadata/proxy/RemoteMetadataServiceProxy.java |  95 ---------
 .../proxy/RemoteMetadataServiceProxyFactory.java   |  34 ---
 .../store/InMemoryWritableMetadataService.java     |  18 +-
 .../metadata/store/RemoteMetadataServiceImpl.java  | 126 +++++++++++
 .../registry/integration/RegistryDirectory.java    |   1 +
 .../internal/org.apache.dubbo.event.EventListener  |   1 -
 .../event/listener/LoggingEventListenerTest.java   |   5 +-
 .../ServiceInstancesChangedListenerTest.java       |  27 ---
 40 files changed, 574 insertions(+), 1304 deletions(-)

diff --git 
a/dubbo-common/src/main/java/org/apache/dubbo/config/AbstractInterfaceConfig.java
 
b/dubbo-common/src/main/java/org/apache/dubbo/config/AbstractInterfaceConfig.java
index 82b5636..970a6ae 100644
--- 
a/dubbo-common/src/main/java/org/apache/dubbo/config/AbstractInterfaceConfig.java
+++ 
b/dubbo-common/src/main/java/org/apache/dubbo/config/AbstractInterfaceConfig.java
@@ -280,6 +280,8 @@ public abstract class AbstractInterfaceConfig extends 
AbstractMethodConfig {
                     RegistryConfig registryConfig = new RegistryConfig();
                     registryConfig.refresh();
                     registryConfigs.add(registryConfig);
+                } else {
+                    registryConfigs = new ArrayList<>(registryConfigs);
                 }
                 setRegistries(registryConfigs);
             }
diff --git 
a/dubbo-common/src/main/java/org/apache/dubbo/config/context/ConfigManager.java 
b/dubbo-common/src/main/java/org/apache/dubbo/config/context/ConfigManager.java
index ed0188a..c0e4b8c 100644
--- 
a/dubbo-common/src/main/java/org/apache/dubbo/config/context/ConfigManager.java
+++ 
b/dubbo-common/src/main/java/org/apache/dubbo/config/context/ConfigManager.java
@@ -164,6 +164,14 @@ public class ConfigManager extends LifecycleAdapter 
implements FrameworkExt {
         return getConfigs(getTagName(MetadataReportConfig.class));
     }
 
+    public Collection<MetadataReportConfig> getDefaultMetadataConfigs() {
+        Collection<MetadataReportConfig> defaults = 
getDefaultConfigs(getConfigsMap(getTagName(MetadataReportConfig.class)));
+        if (CollectionUtils.isEmpty(defaults)) {
+            return getMetadataConfigs();
+        }
+        return defaults;
+    }
+
     // MetadataReportConfig correlative methods
 
     public void addProvider(ProviderConfig providerConfig) {
diff --git 
a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ReferenceConfig.java
 
b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ReferenceConfig.java
index 138a68e..4203a8a 100644
--- 
a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ReferenceConfig.java
+++ 
b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ReferenceConfig.java
@@ -34,7 +34,7 @@ import 
org.apache.dubbo.config.event.ReferenceConfigInitializedEvent;
 import org.apache.dubbo.config.utils.ConfigValidationUtils;
 import org.apache.dubbo.event.Event;
 import org.apache.dubbo.event.EventDispatcher;
-import org.apache.dubbo.metadata.WritableMetadataService;
+import org.apache.dubbo.registry.client.metadata.MetadataUtils;
 import org.apache.dubbo.rpc.Invoker;
 import org.apache.dubbo.rpc.Protocol;
 import org.apache.dubbo.rpc.ProxyFactory;
@@ -62,7 +62,6 @@ import static 
org.apache.dubbo.common.constants.CommonConstants.ANY_VALUE;
 import static org.apache.dubbo.common.constants.CommonConstants.CLUSTER_KEY;
 import static 
org.apache.dubbo.common.constants.CommonConstants.COMMA_SEPARATOR;
 import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER_SIDE;
-import static 
org.apache.dubbo.common.constants.CommonConstants.DEFAULT_METADATA_STORAGE_TYPE;
 import static org.apache.dubbo.common.constants.CommonConstants.INTERFACE_KEY;
 import static 
org.apache.dubbo.common.constants.CommonConstants.LOCALHOST_VALUE;
 import static org.apache.dubbo.common.constants.CommonConstants.METADATA_KEY;
@@ -352,16 +351,10 @@ public class ReferenceConfig<T> extends 
ReferenceConfigBase<T> {
         if (logger.isInfoEnabled()) {
             logger.info("Refer dubbo service " + interfaceClass.getName() + " 
from url " + invoker.getUrl());
         }
-        /**
-         * @since 2.7.0
-         * ServiceData Store
-         */
-        String metadata = map.get(METADATA_KEY);
-        WritableMetadataService metadataService = 
WritableMetadataService.getExtension(metadata == null ? 
DEFAULT_METADATA_STORAGE_TYPE : metadata);
-        if (metadataService != null) {
-            URL consumerURL = new URL(CONSUMER_PROTOCOL, 
map.remove(REGISTER_IP_KEY), 0, map.get(INTERFACE_KEY), map);
-            metadataService.publishServiceDefinition(consumerURL);
-        }
+
+        URL consumerURL = new URL(CONSUMER_PROTOCOL, 
map.remove(REGISTER_IP_KEY), 0, map.get(INTERFACE_KEY), map);
+        MetadataUtils.publishServiceDefinition(consumerURL);
+
         // create service proxy
         return (T) PROXY_FACTORY.getProxy(invoker, 
ProtocolUtils.isGeneric(generic));
     }
diff --git 
a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ServiceConfig.java
 
b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ServiceConfig.java
index 3ad4545..68dc97a 100644
--- 
a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ServiceConfig.java
+++ 
b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ServiceConfig.java
@@ -37,7 +37,7 @@ import org.apache.dubbo.config.support.Parameter;
 import org.apache.dubbo.config.utils.ConfigValidationUtils;
 import org.apache.dubbo.event.Event;
 import org.apache.dubbo.event.EventDispatcher;
-import org.apache.dubbo.metadata.WritableMetadataService;
+import org.apache.dubbo.registry.client.metadata.MetadataUtils;
 import org.apache.dubbo.rpc.Exporter;
 import org.apache.dubbo.rpc.Invoker;
 import org.apache.dubbo.rpc.Protocol;
@@ -68,7 +68,6 @@ import java.util.concurrent.TimeUnit;
 
 import static org.apache.dubbo.common.constants.CommonConstants.ANYHOST_KEY;
 import static org.apache.dubbo.common.constants.CommonConstants.ANY_VALUE;
-import static 
org.apache.dubbo.common.constants.CommonConstants.DEFAULT_METADATA_STORAGE_TYPE;
 import static org.apache.dubbo.common.constants.CommonConstants.DUBBO;
 import static 
org.apache.dubbo.common.constants.CommonConstants.DUBBO_IP_TO_BIND;
 import static 
org.apache.dubbo.common.constants.CommonConstants.LOCALHOST_VALUE;
@@ -210,6 +209,7 @@ public class ServiceConfig<T> extends ServiceConfigBase<T> {
     }
 
     public void exported() {
+
         // dispatch a ServiceConfigExportedEvent since 2.7.4
         dispatch(new ServiceConfigExportedEvent(this));
     }
@@ -502,14 +502,8 @@ public class ServiceConfig<T> extends ServiceConfigBase<T> 
{
                     Exporter<?> exporter = PROTOCOL.export(wrapperInvoker);
                     exporters.add(exporter);
                 }
-                /**
-                 * @since 2.7.0
-                 * ServiceData Store
-                 */
-                WritableMetadataService metadataService = 
WritableMetadataService.getExtension(url.getParameter(METADATA_KEY, 
DEFAULT_METADATA_STORAGE_TYPE));
-                if (metadataService != null) {
-                    metadataService.publishServiceDefinition(url);
-                }
+
+                MetadataUtils.publishServiceDefinition(url);
             }
         }
         this.urls.add(url);
diff --git 
a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/DubboBootstrap.java
 
b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/DubboBootstrap.java
index eee642b..3bdffa9 100644
--- 
a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/DubboBootstrap.java
+++ 
b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/DubboBootstrap.java
@@ -66,6 +66,9 @@ import 
org.apache.dubbo.registry.client.DefaultServiceInstance;
 import org.apache.dubbo.registry.client.ServiceDiscovery;
 import org.apache.dubbo.registry.client.ServiceDiscoveryRegistry;
 import org.apache.dubbo.registry.client.ServiceInstance;
+import org.apache.dubbo.registry.client.ServiceInstanceCustomizer;
+import org.apache.dubbo.registry.client.metadata.MetadataUtils;
+import 
org.apache.dubbo.registry.client.metadata.store.RemoteMetadataServiceImpl;
 import org.apache.dubbo.registry.support.AbstractRegistryFactory;
 import org.apache.dubbo.rpc.model.ApplicationModel;
 
@@ -617,17 +620,18 @@ public class DubboBootstrap extends GenericEventListener {
         Collection<MetadataReportConfig> metadataReportConfigs = 
configManager.getMetadataConfigs();
         if (CollectionUtils.isEmpty(metadataReportConfigs)) {
             if (REMOTE_METADATA_STORAGE_TYPE.equals(metadataType)) {
-                throw new IllegalStateException("No MetadataConfig found, you 
must specify the remote Metadata Center address when 'metadata=remote' is 
enabled.");
+                throw new IllegalStateException("No MetadataConfig found, 
Metadata Center address is required when 'metadata=remote' is enabled.");
             }
             return;
         }
-        MetadataReportConfig metadataReportConfig = 
metadataReportConfigs.iterator().next();
-        ConfigValidationUtils.validateMetadataConfig(metadataReportConfig);
-        if (!metadataReportConfig.isValid()) {
-            return;
-        }
 
-        MetadataReportInstance.init(metadataReportConfig.toUrl());
+        for (MetadataReportConfig metadataReportConfig : 
metadataReportConfigs) {
+            ConfigValidationUtils.validateMetadataConfig(metadataReportConfig);
+            if (!metadataReportConfig.isValid()) {
+                return;
+            }
+            MetadataReportInstance.init(metadataReportConfig);
+        }
     }
 
     /**
@@ -1016,9 +1020,17 @@ public class DubboBootstrap extends GenericEventListener 
{
 
         ServiceInstance serviceInstance = createServiceInstance(serviceName, 
host, port);
 
+        // register metadata
+        publishMetadataToRemote(serviceInstance);
+
         getServiceDiscoveries().forEach(serviceDiscovery -> 
serviceDiscovery.register(serviceInstance));
     }
 
+    private void publishMetadataToRemote(ServiceInstance serviceInstance) {
+        RemoteMetadataServiceImpl remoteMetadataService = 
MetadataUtils.getRemoteMetadataService();
+        remoteMetadataService.publishMetadata(serviceInstance);
+    }
+
     private URL selectMetadataServiceExportedURL() {
 
         URL selectedURL = null;
@@ -1056,6 +1068,15 @@ public class DubboBootstrap extends GenericEventListener 
{
     private ServiceInstance createServiceInstance(String serviceName, String 
host, int port) {
         this.serviceInstance = new DefaultServiceInstance(serviceName, host, 
port);
         setMetadataStorageType(serviceInstance, getMetadataType());
+
+        ExtensionLoader<ServiceInstanceCustomizer> loader =
+                
ExtensionLoader.getExtensionLoader(ServiceInstanceCustomizer.class);
+        // FIXME, sort customizer before apply
+        loader.getSupportedExtensionInstances().forEach(customizer -> {
+            // customizes
+            customizer.customize(this.serviceInstance);
+        });
+
         return this.serviceInstance;
     }
 
diff --git 
a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/DefaultMetadataParamsFilter.java
 
b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/DefaultMetadataParamsFilter.java
new file mode 100644
index 0000000..7f4b85c
--- /dev/null
+++ 
b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/DefaultMetadataParamsFilter.java
@@ -0,0 +1,47 @@
+/*
+ * 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 static org.apache.dubbo.common.constants.CommonConstants.CLUSTER_KEY;
+import static 
org.apache.dubbo.common.constants.CommonConstants.DUBBO_VERSION_KEY;
+import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY;
+import static 
org.apache.dubbo.common.constants.CommonConstants.LOADBALANCE_KEY;
+import static org.apache.dubbo.common.constants.CommonConstants.PATH_KEY;
+import static org.apache.dubbo.common.constants.CommonConstants.RELEASE_KEY;
+import static org.apache.dubbo.common.constants.CommonConstants.TIMEOUT_KEY;
+import static org.apache.dubbo.common.constants.CommonConstants.TIMESTAMP_KEY;
+import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY;
+import static org.apache.dubbo.remoting.Constants.CODEC_KEY;
+import static org.apache.dubbo.remoting.Constants.CONNECTIONS_KEY;
+import static org.apache.dubbo.remoting.Constants.EXCHANGER_KEY;
+import static org.apache.dubbo.remoting.Constants.SERIALIZATION_KEY;
+import static org.apache.dubbo.rpc.Constants.DEPRECATED_KEY;
+import static org.apache.dubbo.rpc.Constants.MOCK_KEY;
+import static org.apache.dubbo.rpc.Constants.TOKEN_KEY;
+import static org.apache.dubbo.rpc.cluster.Constants.WARMUP_KEY;
+import static org.apache.dubbo.rpc.cluster.Constants.WEIGHT_KEY;
+
+public class DefaultMetadataParamsFilter implements MetadataParamsFilter {
+    @Override
+    public String[] include() {
+        return new String[]{
+                CODEC_KEY, EXCHANGER_KEY, SERIALIZATION_KEY, CLUSTER_KEY, 
CONNECTIONS_KEY, DEPRECATED_KEY,
+                GROUP_KEY, LOADBALANCE_KEY, MOCK_KEY, PATH_KEY, TIMEOUT_KEY, 
TOKEN_KEY, VERSION_KEY, WARMUP_KEY,
+                WEIGHT_KEY, TIMESTAMP_KEY, DUBBO_VERSION_KEY, RELEASE_KEY
+        };
+    }
+}
diff --git 
a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataInfo.java
 
b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataInfo.java
index 20933fb..bf9aa68 100644
--- 
a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataInfo.java
+++ 
b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataInfo.java
@@ -17,12 +17,22 @@
 package org.apache.dubbo.metadata;
 
 import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.extension.ExtensionLoader;
+import org.apache.dubbo.common.utils.ArrayUtils;
+import org.apache.dubbo.common.utils.StringUtils;
 
 import java.io.Serializable;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 
+import static org.apache.dubbo.common.constants.CommonConstants.DOT_SEPARATOR;
+import static 
org.apache.dubbo.common.constants.CommonConstants.GROUP_CHAR_SEPERATOR;
+import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY;
+import static org.apache.dubbo.common.constants.CommonConstants.METHODS_KEY;
+import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY;
+
 public class MetadataInfo implements Serializable {
     private String app;
     private String revision;
@@ -37,6 +47,36 @@ public class MetadataInfo implements Serializable {
         this.services = services == null ? new HashMap<>() : services;
     }
 
+    public void addService(ServiceInfo serviceInfo) {
+        if (serviceInfo == null) {
+            return;
+        }
+        this.services.put(serviceInfo.getMatchKey(), serviceInfo);
+    }
+
+    public void removeService(ServiceInfo serviceInfo) {
+        if (serviceInfo == null) {
+            return;
+        }
+        this.services.remove(serviceInfo.getMatchKey());
+    }
+
+    public void removeService(String key) {
+        if (key == null) {
+            return;
+        }
+        this.services.remove(key);
+    }
+
+    public String revision() {
+        StringBuilder sb = new StringBuilder();
+        sb.append(app);
+        for (Map.Entry<String, ServiceInfo> entry : services.entrySet()) {
+            sb.append(entry.getValue().toString());
+        }
+        return RevisionResolver.calRevision(sb.toString());
+    }
+
     public String getApp() {
         return app;
     }
@@ -82,6 +122,7 @@ public class MetadataInfo implements Serializable {
     }
 
     public static class ServiceInfo implements Serializable {
+        private static ExtensionLoader<MetadataParamsFilter> loader = 
ExtensionLoader.getExtensionLoader(MetadataParamsFilter.class);
         private String name;
         private String group;
         private String version;
@@ -91,10 +132,45 @@ public class MetadataInfo implements Serializable {
 
         private transient Map<String, Map<String, String>> methodParams;
         private transient String serviceKey;
+        private transient String matchKey;
 
         public ServiceInfo() {
         }
 
+        public ServiceInfo(URL url) {
+            // FIXME, how to match registry.
+            this(
+                    url.getServiceInterface(),
+                    url.getParameter(GROUP_KEY),
+                    url.getParameter(VERSION_KEY),
+                    url.getProtocol(),
+                    "",
+                    null
+            );
+
+            Map<String, String> params = new HashMap<>();
+            List<MetadataParamsFilter> filters = 
loader.getActivateExtension(url, "params-filter");
+            for (MetadataParamsFilter filter : filters) {
+                String[] paramsIncluded = filter.include();
+                if (ArrayUtils.isNotEmpty(paramsIncluded)) {
+                    for (String p : paramsIncluded) {
+                        String value = url.getParameter(p);
+                        if (StringUtils.isNotEmpty(value) && params.get(p) == 
null) {
+                            params.put(p, value);
+                        }
+                        String[] methods = url.getParameter(METHODS_KEY, 
(String[]) null);
+                        for (String method : methods) {
+                            String mValue = url.getMethodParameter(method, p);
+                            if (StringUtils.isNotEmpty(mValue)) {
+                                params.put(method + DOT_SEPARATOR + p, mValue);
+                            }
+                        }
+                    }
+                }
+            }
+            this.params = params;
+        }
+
         public ServiceInfo(String name, String group, String version, String 
protocol, String registry, Map<String, String> params) {
             this.name = name;
             this.group = group;
@@ -104,9 +180,33 @@ public class MetadataInfo implements Serializable {
             this.params = params == null ? new HashMap<>() : params;
 
             this.serviceKey = URL.buildKey(name, group, version);
+            this.matchKey = buildMatchKey();
+        }
+
+        public String getMatchKey() {
+            if (matchKey != null) {
+                return matchKey;
+            }
+            buildMatchKey();
+            return matchKey;
+        }
+
+        private String buildMatchKey() {
+            matchKey = getServiceKey();
+            if (StringUtils.isNotEmpty(protocol)) {
+                matchKey = getServiceKey() + GROUP_CHAR_SEPERATOR + protocol;
+            }
+            if (StringUtils.isNotEmpty(registry)) {
+                matchKey = getServiceKey() + GROUP_CHAR_SEPERATOR + registry;
+            }
+            return matchKey;
         }
 
         public String getServiceKey() {
+            if (serviceKey != null) {
+                return serviceKey;
+            }
+            this.serviceKey = URL.buildKey(name, group, version);
             return serviceKey;
         }
 
diff --git 
a/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/LocalMetadataServiceTest.java
 
b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataParamsFilter.java
similarity index 64%
rename from 
dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/LocalMetadataServiceTest.java
rename to 
dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataParamsFilter.java
index 45fc8e0..5ccb9aa 100644
--- 
a/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/LocalMetadataServiceTest.java
+++ 
b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataParamsFilter.java
@@ -16,21 +16,9 @@
  */
 package org.apache.dubbo.metadata;
 
-import org.apache.dubbo.metadata.store.InMemoryWritableMetadataService;
+import org.apache.dubbo.common.extension.SPI;
 
-import org.junit.jupiter.api.Test;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-
-/**
- * {@link WritableMetadataService} Test
- *
- * @since 2.7.5
- */
-public class LocalMetadataServiceTest {
-
-    @Test
-    public void testDefaultExtension() {
-        assertEquals(InMemoryWritableMetadataService.class, 
WritableMetadataService.getDefaultExtension().getClass());
-    }
+@SPI
+public interface MetadataParamsFilter {
+   String[] include();
 }
diff --git 
a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataService.java
 
b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataService.java
index 0780149..71d338f 100644
--- 
a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataService.java
+++ 
b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataService.java
@@ -174,6 +174,8 @@ public interface MetadataService {
      */
     String getServiceDefinition(String serviceKey);
 
+    MetadataInfo getMetadataInfo();
+
     /**
      * Is the {@link URL} for the {@link MetadataService} or not?
      *
diff --git 
a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataUtil.java
 
b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataUtil.java
deleted file mode 100644
index 85ce30e..0000000
--- 
a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataUtil.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * 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;
-
-/**
- * FIXME
- * 2019-07-31
- */
-public class MetadataUtil {
-
-//    public static String getMetadataKey(URL url, KeyTypeEnum keyType){
-//
-//    }
-//
-//    private String getIdentifierKey() {
-//        return serviceInterface
-//                + KEY_SEPARATOR + (version == null ? "" : version)
-//                + KEY_SEPARATOR + (group == null ? "" : group)
-//                + KEY_SEPARATOR + (side == null ? "" : side)
-//                + KEY_SEPARATOR + application;
-//    }
-//
-//    private String getFilePathKey() {
-//        return getFilePathKey(DEFAULT_PATH_TAG);
-//    }
-//
-//    private String getFilePathKey(String pathTag) {
-//        return pathTag
-//                + (StringUtils.isEmpty(toServicePath()) ? "" : 
(PATH_SEPARATOR + toServicePath()))
-//                + (version == null ? "" : (PATH_SEPARATOR + version))
-//                + (group == null ? "" : (PATH_SEPARATOR + group))
-//                + (side == null ? "" : (PATH_SEPARATOR + side))
-//                + (getApplication() == null ? "" : (PATH_SEPARATOR + 
getApplication()));
-//    }
-}
diff --git 
a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/RevisionResolver.java
 
b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/RevisionResolver.java
new file mode 100644
index 0000000..76d8cfb
--- /dev/null
+++ 
b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/RevisionResolver.java
@@ -0,0 +1,57 @@
+/*
+ * 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.logger.Logger;
+import org.apache.dubbo.common.logger.LoggerFactory;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+public class RevisionResolver {
+    private static final Logger logger = 
LoggerFactory.getLogger(RevisionResolver.class);
+    private static char hexDigits[] = {
+            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 
'D', 'E', 'F'
+    };
+
+    private static MessageDigest mdInst;
+
+    static {
+        try {
+            mdInst = MessageDigest.getInstance("MD5");
+        } catch (NoSuchAlgorithmException e) {
+            logger.error("Failed to calculate metadata revision", e);
+        }
+    }
+
+    public static String calRevision(String metadata) {
+        mdInst.update(metadata.getBytes(UTF_8));
+        byte[] md5 = mdInst.digest();
+
+        int j = md5.length;
+        char str[] = new char[j * 2];
+        int k = 0;
+        for (int i = 0; i < j; i++) {
+            byte byte0 = md5[i];
+            str[k++] = hexDigits[byte0 >>> 4 & 0xf];
+            str[k++] = hexDigits[byte0 & 0xf];
+        }
+        return new String(str);
+    }
+}
diff --git 
a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/WritableMetadataService.java
 
b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/WritableMetadataService.java
index 3a17395..b453842 100644
--- 
a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/WritableMetadataService.java
+++ 
b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/WritableMetadataService.java
@@ -18,11 +18,8 @@ package org.apache.dubbo.metadata;
 
 import org.apache.dubbo.common.URL;
 import org.apache.dubbo.common.extension.ExtensionLoader;
-import org.apache.dubbo.common.extension.SPI;
-import org.apache.dubbo.metadata.store.InMemoryWritableMetadataService;
 import org.apache.dubbo.rpc.model.ApplicationModel;
 
-import static 
org.apache.dubbo.common.constants.CommonConstants.DEFAULT_METADATA_STORAGE_TYPE;
 import static 
org.apache.dubbo.common.extension.ExtensionLoader.getExtensionLoader;
 
 /**
@@ -31,7 +28,6 @@ import static 
org.apache.dubbo.common.extension.ExtensionLoader.getExtensionLoad
  *
  * @since 2.7.5
  */
-@SPI(DEFAULT_METADATA_STORAGE_TYPE)
 public interface WritableMetadataService extends MetadataService {
     /**
      * Gets the current Dubbo Service name
@@ -60,15 +56,6 @@ public interface WritableMetadataService extends 
MetadataService {
     boolean unexportURL(URL url);
 
     /**
-     * fresh Exports
-     *
-     * @return If success , return <code>true</code>
-     */
-    default boolean refreshMetadata(String exportedRevision, String 
subscribedRevision) {
-        return true;
-    }
-
-    /**
      * Subscribes a {@link URL}
      *
      * @param url a {@link URL}
@@ -90,7 +77,6 @@ public interface WritableMetadataService extends 
MetadataService {
      * Get {@link ExtensionLoader#getDefaultExtension() the defautl extension} 
of {@link WritableMetadataService}
      *
      * @return non-null
-     * @see InMemoryWritableMetadataService
      */
     static WritableMetadataService getDefaultExtension() {
         return 
getExtensionLoader(WritableMetadataService.class).getDefaultExtension();
diff --git 
a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/report/MetadataReport.java
 
b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/report/MetadataReport.java
index 90ca45a..4bc133d 100644
--- 
a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/report/MetadataReport.java
+++ 
b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/report/MetadataReport.java
@@ -18,6 +18,7 @@ package org.apache.dubbo.metadata.report;
 
 
 import org.apache.dubbo.common.URL;
+import org.apache.dubbo.metadata.MetadataInfo;
 import org.apache.dubbo.metadata.definition.model.ServiceDefinition;
 import org.apache.dubbo.metadata.report.identifier.MetadataIdentifier;
 import org.apache.dubbo.metadata.report.identifier.ServiceMetadataIdentifier;
@@ -27,24 +28,33 @@ import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
-/**
- *
- */
 public interface MetadataReport {
 
     void storeProviderMetadata(MetadataIdentifier providerMetadataIdentifier, 
ServiceDefinition serviceDefinition);
 
     void storeConsumerMetadata(MetadataIdentifier consumerMetadataIdentifier, 
Map<String, String> serviceParameterMap);
 
+    List<String> getExportedURLs(ServiceMetadataIdentifier metadataIdentifier);
+
+    String getServiceDefinition(MetadataIdentifier metadataIdentifier);
+
+    default void publishAppMetadata(SubscriberMetadataIdentifier identifier, 
MetadataInfo metadataInfo) {
+    }
+
+    default MetadataInfo getAppMetadata(SubscriberMetadataIdentifier 
identifier, Map<String, String> instanceMetadata) {
+        return null;
+    }
+
+    /**
+     * deprecated
+     **/
+
     void saveServiceMetadata(ServiceMetadataIdentifier metadataIdentifier, URL 
url);
 
     void removeServiceMetadata(ServiceMetadataIdentifier metadataIdentifier);
 
-    List<String> getExportedURLs(ServiceMetadataIdentifier metadataIdentifier);
-
     void saveSubscribedData(SubscriberMetadataIdentifier 
subscriberMetadataIdentifier, Set<String> urls);
 
     List<String> getSubscribedURLs(SubscriberMetadataIdentifier 
subscriberMetadataIdentifier);
 
-    String getServiceDefinition(MetadataIdentifier metadataIdentifier);
 }
diff --git 
a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/report/MetadataReportInstance.java
 
b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/report/MetadataReportInstance.java
index 4f84e0c..4b66fe4 100644
--- 
a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/report/MetadataReportInstance.java
+++ 
b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/report/MetadataReportInstance.java
@@ -19,7 +19,10 @@ package org.apache.dubbo.metadata.report;
 import org.apache.dubbo.common.URL;
 import org.apache.dubbo.common.URLBuilder;
 import org.apache.dubbo.common.extension.ExtensionLoader;
+import org.apache.dubbo.config.MetadataReportConfig;
 
+import java.util.ArrayList;
+import java.util.List;
 import java.util.concurrent.atomic.AtomicBoolean;
 
 import static 
org.apache.dubbo.common.constants.CommonConstants.DEFAULT_DIRECTORY;
@@ -32,33 +35,34 @@ public class MetadataReportInstance {
 
     private static AtomicBoolean init = new AtomicBoolean(false);
 
-    private static MetadataReport metadataReport;
+    private static final List<MetadataReport> metadataReports = new 
ArrayList<>();
 
-    public static void init(URL metadataReportURL) {
+    public static void init(MetadataReportConfig config) {
         if (init.get()) {
             return;
         }
         MetadataReportFactory metadataReportFactory = 
ExtensionLoader.getExtensionLoader(MetadataReportFactory.class).getAdaptiveExtension();
-        if (METADATA_REPORT_KEY.equals(metadataReportURL.getProtocol())) {
-            String protocol = 
metadataReportURL.getParameter(METADATA_REPORT_KEY, DEFAULT_DIRECTORY);
-            metadataReportURL = URLBuilder.from(metadataReportURL)
+        URL url = config.toUrl();
+        if (METADATA_REPORT_KEY.equals(url.getProtocol())) {
+            String protocol = url.getParameter(METADATA_REPORT_KEY, 
DEFAULT_DIRECTORY);
+            url = URLBuilder.from(url)
                     .setProtocol(protocol)
                     .removeParameter(METADATA_REPORT_KEY)
                     .build();
         }
-        metadataReport = 
metadataReportFactory.getMetadataReport(metadataReportURL);
+        metadataReports.add(metadataReportFactory.getMetadataReport(url));
         init.set(true);
     }
 
-    public static MetadataReport getMetadataReport() {
-        return getMetadataReport(false);
+    public static List<MetadataReport> getMetadataReports() {
+        return getMetadataReports(false);
     }
 
-    public static MetadataReport getMetadataReport(boolean checked) {
+    public static List<MetadataReport> getMetadataReports(boolean checked) {
         if (checked) {
             checkInit();
         }
-        return metadataReport;
+        return metadataReports;
     }
 
     private static void checkInit() {
diff --git 
a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/store/BaseWritableMetadataService.java
 
b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/store/BaseWritableMetadataService.java
deleted file mode 100644
index 84fcd15..0000000
--- 
a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/store/BaseWritableMetadataService.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * 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.store;
-
-import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.logger.Logger;
-import org.apache.dubbo.common.logger.LoggerFactory;
-import org.apache.dubbo.metadata.MetadataService;
-
-import java.util.Collection;
-import java.util.Map;
-import java.util.SortedSet;
-import java.util.concurrent.ConcurrentNavigableMap;
-import java.util.concurrent.ConcurrentSkipListMap;
-import java.util.function.Consumer;
-
-public class BaseWritableMetadataService {
-    final Logger logger = LoggerFactory.getLogger(getClass());
-
-    // =================================== Registration 
=================================== //
-
-    /**
-     * All exported {@link URL urls} {@link Map} whose key is the return value 
of {@link URL#getServiceKey()} method
-     * and value is the {@link SortedSet sorted set} of the {@link URL URLs}
-     */
-    static ConcurrentNavigableMap<String, SortedSet<URL>> exportedServiceURLs 
= new ConcurrentSkipListMap<>();
-
-    // 
====================================================================================
 //
-
-    // =================================== Subscription 
=================================== //
-
-    /**
-     * The subscribed {@link URL urls} {@link Map} of {@link MetadataService},
-     * whose key is the return value of {@link URL#getServiceKey()} method and 
value is
-     * the {@link SortedSet sorted set} of the {@link URL URLs}
-     */
-    final static ConcurrentNavigableMap<String, SortedSet<URL>> 
SUBSCRIBED_SERVICE_URLS = new ConcurrentSkipListMap<>();
-
-    final static ConcurrentNavigableMap<String, String> SERVICE_DEFINITIONS = 
new ConcurrentSkipListMap<>();
-
-
-    boolean throwableAction(Consumer<URL> consumer, URL url) {
-        try {
-            consumer.accept(url);
-        } catch (Exception e) {
-            logger.error("Failed to remove url metadata to remote center, url 
is: " + url);
-            return false;
-        }
-        return true;
-    }
-
-    public SortedSet<String> getSubscribedURLs() {
-        return getAllUnmodifiableServiceURLs(SUBSCRIBED_SERVICE_URLS);
-    }
-
-    static SortedSet<String> getAllUnmodifiableServiceURLs(Map<String, 
SortedSet<URL>> serviceURLs) {
-        return 
MetadataService.toSortedStrings(serviceURLs.values().stream().flatMap(Collection::stream));
-    }
-}
diff --git 
a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/store/RemoteWritableMetadataService.java
 
b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/store/RemoteWritableMetadataService.java
deleted file mode 100644
index 9891478..0000000
--- 
a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/store/RemoteWritableMetadataService.java
+++ /dev/null
@@ -1,233 +0,0 @@
-/*
- * 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.store;
-
-import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.logger.Logger;
-import org.apache.dubbo.common.logger.LoggerFactory;
-import org.apache.dubbo.common.utils.CollectionUtils;
-import org.apache.dubbo.common.utils.StringUtils;
-import org.apache.dubbo.metadata.WritableMetadataService;
-import org.apache.dubbo.metadata.definition.ServiceDefinitionBuilder;
-import org.apache.dubbo.metadata.definition.model.FullServiceDefinition;
-import org.apache.dubbo.metadata.report.MetadataReport;
-import org.apache.dubbo.metadata.report.MetadataReportInstance;
-import org.apache.dubbo.metadata.report.identifier.MetadataIdentifier;
-import org.apache.dubbo.metadata.report.identifier.ServiceMetadataIdentifier;
-import 
org.apache.dubbo.metadata.report.identifier.SubscriberMetadataIdentifier;
-import org.apache.dubbo.remoting.Constants;
-import org.apache.dubbo.rpc.RpcException;
-
-import java.util.Iterator;
-import java.util.Set;
-import java.util.SortedSet;
-import java.util.function.BiConsumer;
-import java.util.function.Consumer;
-
-import static 
org.apache.dubbo.common.constants.CommonConstants.APPLICATION_KEY;
-import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER_SIDE;
-import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY;
-import static org.apache.dubbo.common.constants.CommonConstants.INTERFACE_KEY;
-import static org.apache.dubbo.common.constants.CommonConstants.PID_KEY;
-import static org.apache.dubbo.common.constants.CommonConstants.PROVIDER_SIDE;
-import static org.apache.dubbo.common.constants.CommonConstants.SIDE_KEY;
-import static org.apache.dubbo.common.constants.CommonConstants.TIMESTAMP_KEY;
-import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY;
-
-/**
- * The {@link WritableMetadataService} implementation stores the metadata of 
Dubbo services in metadata center when they
- * exported.
- * It is used by server (provider).
- *
- * @since 2.7.5
- */
-public class RemoteWritableMetadataService implements WritableMetadataService {
-
-    protected final Logger logger = LoggerFactory.getLogger(getClass());
-    private volatile String exportedRevision;
-    private volatile String subscribedRevision;
-    private InMemoryWritableMetadataService writableMetadataService;
-
-    public RemoteWritableMetadataService(InMemoryWritableMetadataService 
writableMetadataService) {
-        this.writableMetadataService = writableMetadataService;
-    }
-
-    public MetadataReport getMetadataReport() {
-        return MetadataReportInstance.getMetadataReport(true);
-    }
-
-    @Override
-    public void publishServiceDefinition(URL url) {
-        String side = url.getParameter(SIDE_KEY);
-        if (PROVIDER_SIDE.equalsIgnoreCase(side)) {
-            //TODO, the params part is duplicate with that stored by 
exportURL(url), can be further optimized in the future.
-            publishProvider(url);
-        } else {
-            //TODO, only useful for ops showing the url parameters, this is 
duplicate with subscribeURL(url), can be removed in the future.
-            publishConsumer(url);
-        }
-    }
-
-    private void publishProvider(URL providerUrl) throws RpcException {
-        //first add into the list
-        // remove the individual param
-        providerUrl = providerUrl.removeParameters(PID_KEY, TIMESTAMP_KEY, 
Constants.BIND_IP_KEY,
-                Constants.BIND_PORT_KEY, TIMESTAMP_KEY);
-
-        try {
-            String interfaceName = providerUrl.getParameter(INTERFACE_KEY);
-            if (StringUtils.isNotEmpty(interfaceName)) {
-                Class interfaceClass = Class.forName(interfaceName);
-                FullServiceDefinition fullServiceDefinition = 
ServiceDefinitionBuilder.buildFullDefinition(interfaceClass,
-                        providerUrl.getParameters());
-                getMetadataReport().storeProviderMetadata(new 
MetadataIdentifier(providerUrl.getServiceInterface(),
-                        providerUrl.getParameter(VERSION_KEY), 
providerUrl.getParameter(GROUP_KEY),
-                        PROVIDER_SIDE, 
providerUrl.getParameter(APPLICATION_KEY)), fullServiceDefinition);
-                return;
-            }
-            logger.error("publishProvider interfaceName is empty . 
providerUrl: " + providerUrl.toFullString());
-        } catch (ClassNotFoundException e) {
-            //ignore error
-            logger.error("publishProvider getServiceDescriptor error. 
providerUrl: " + providerUrl.toFullString(), e);
-        }
-    }
-
-    private void publishConsumer(URL consumerURL) throws RpcException {
-        consumerURL = consumerURL.removeParameters(PID_KEY, TIMESTAMP_KEY, 
Constants.BIND_IP_KEY,
-                Constants.BIND_PORT_KEY, TIMESTAMP_KEY);
-        getMetadataReport().storeConsumerMetadata(new 
MetadataIdentifier(consumerURL.getServiceInterface(),
-                consumerURL.getParameter(VERSION_KEY), 
consumerURL.getParameter(GROUP_KEY), CONSUMER_SIDE,
-                consumerURL.getParameter(APPLICATION_KEY)), 
consumerURL.getParameters());
-    }
-
-    @Override
-    public boolean exportURL(URL url) {
-        // do nothing for one single url export, the actual report will be 
done in callback (refreshMetadata) after all urls are exported.
-        return true;
-    }
-
-    @Override
-    public boolean unexportURL(URL url) {
-        ServiceMetadataIdentifier metadataIdentifier = new 
ServiceMetadataIdentifier(url);
-        metadataIdentifier.setRevision(exportedRevision);
-        metadataIdentifier.setProtocol(url.getProtocol());
-        return throwableAction(getMetadataReport()::removeServiceMetadata, 
metadataIdentifier);
-    }
-
-    @Override
-    public boolean subscribeURL(URL url) {
-        // do nothing for one single url export, the actual report will be 
done in callback (refreshMetadata) after all urls are exported.
-        return true;
-    }
-
-    @Override
-    public boolean unsubscribeURL(URL url) {
-        // do nothing for one single url export, the actual report will be 
done in callback (refreshMetadata) after all urls are exported.
-        return true;
-    }
-
-    @Override
-    public boolean refreshMetadata(String exportedRevision, String 
subscribedRevision) {
-        boolean result = true;
-        if (!StringUtils.isEmpty(exportedRevision) && 
!exportedRevision.equals(this.exportedRevision)) {
-            this.exportedRevision = exportedRevision;
-            boolean executeResult = saveServiceMetadata();
-            if (!executeResult) {
-                result = false;
-            }
-        }
-        if (!StringUtils.isEmpty(subscribedRevision) && 
!subscribedRevision.equals(this.subscribedRevision)
-                && 
CollectionUtils.isNotEmpty(writableMetadataService.getSubscribedURLs())) {
-            this.subscribedRevision = subscribedRevision;
-            SubscriberMetadataIdentifier metadataIdentifier = new 
SubscriberMetadataIdentifier();
-            metadataIdentifier.setApplication(serviceName());
-            metadataIdentifier.setRevision(subscribedRevision);
-            boolean executeResult = 
throwableAction(getMetadataReport()::saveSubscribedData, metadataIdentifier,
-                    writableMetadataService.getSubscribedURLs());
-            if (!executeResult) {
-                result = false;
-            }
-        }
-        return result;
-    }
-
-    private boolean saveServiceMetadata() {
-        boolean result = true;
-        for (SortedSet<URL> urls : 
writableMetadataService.exportedServiceURLs.values()) {
-            Iterator<URL> iterator = urls.iterator();
-            while (iterator.hasNext()) {
-                URL url = iterator.next();
-                // refresh revision in urls
-                ServiceMetadataIdentifier metadataIdentifier = new 
ServiceMetadataIdentifier(url);
-                metadataIdentifier.setRevision(exportedRevision);
-                metadataIdentifier.setProtocol(url.getProtocol());
-
-                boolean tmpResult = 
throwableAction(getMetadataReport()::saveServiceMetadata, metadataIdentifier, 
url);
-                if (!tmpResult) result = tmpResult;
-            }
-        }
-        return result;
-    }
-
-
-    @Override
-    public SortedSet<String> getExportedURLs(String serviceInterface, String 
group, String version, String protocol) {
-        return null;
-    }
-
-    @Override
-    public String getServiceDefinition(String interfaceName, String version, 
String group) {
-        return null;
-    }
-
-    @Override
-    public String getServiceDefinition(String serviceKey) {
-        return null;
-    }
-
-    boolean throwableAction(BiConsumer<ServiceMetadataIdentifier, URL> 
consumer,
-                            ServiceMetadataIdentifier metadataIdentifier, URL 
url) {
-        try {
-            consumer.accept(metadataIdentifier, url);
-        } catch (Exception e) {
-            logger.error("Failed to execute consumer, url is: " + url);
-            return false;
-        }
-        return true;
-    }
-
-    boolean throwableAction(BiConsumer<SubscriberMetadataIdentifier, 
Set<String>> consumer,
-                            SubscriberMetadataIdentifier metadataIdentifier, 
Set<String> urls) {
-        try {
-            consumer.accept(metadataIdentifier, urls);
-        } catch (Exception e) {
-            logger.error("Failed to execute consumer, url is: " + urls);
-            return false;
-        }
-        return true;
-    }
-
-    boolean throwableAction(Consumer<ServiceMetadataIdentifier> consumer, 
ServiceMetadataIdentifier metadataIdentifier) {
-        try {
-            consumer.accept(metadataIdentifier);
-        } catch (Exception e) {
-            logger.error("Failed to remove url metadata to remote center, 
metadataIdentifier is: " + metadataIdentifier);
-            return false;
-        }
-        return true;
-    }
-}
diff --git 
a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/store/RemoteWritableMetadataServiceDelegate.java
 
b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/store/RemoteWritableMetadataServiceDelegate.java
deleted file mode 100644
index 132a0cb..0000000
--- 
a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/store/RemoteWritableMetadataServiceDelegate.java
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * 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.store;
-
-import org.apache.dubbo.common.URL;
-import org.apache.dubbo.metadata.WritableMetadataService;
-
-import java.util.SortedSet;
-import java.util.function.BiFunction;
-
-/**
- * 2019-08-14
- *
- * @since 2.7.5
- */
-public class RemoteWritableMetadataServiceDelegate implements 
WritableMetadataService {
-    InMemoryWritableMetadataService defaultWritableMetadataService;
-    RemoteWritableMetadataService remoteWritableMetadataService;
-
-    public RemoteWritableMetadataServiceDelegate() {
-        defaultWritableMetadataService = (InMemoryWritableMetadataService) 
WritableMetadataService.getExtension("local");
-        remoteWritableMetadataService = new 
RemoteWritableMetadataService(defaultWritableMetadataService);
-    }
-
-    private WritableMetadataService getDefaultWritableMetadataService() {
-        return defaultWritableMetadataService;
-    }
-
-    @Override
-    public boolean exportURL(URL url) {
-        return doFunction(WritableMetadataService::exportURL, url);
-    }
-
-    @Override
-    public boolean unexportURL(URL url) {
-        return doFunction(WritableMetadataService::unexportURL, url);
-    }
-
-    @Override
-    public boolean subscribeURL(URL url) {
-        return doFunction(WritableMetadataService::subscribeURL, url);
-    }
-
-    @Override
-    public boolean unsubscribeURL(URL url) {
-        return doFunction(WritableMetadataService::unsubscribeURL, url);
-    }
-
-    @Override
-    public boolean refreshMetadata(String exportedRevision, String 
subscribedRevision) {
-        boolean result = true;
-        result &= 
defaultWritableMetadataService.refreshMetadata(exportedRevision, 
subscribedRevision);
-        result &= 
remoteWritableMetadataService.refreshMetadata(exportedRevision, 
subscribedRevision);
-        return result;
-    }
-
-    @Override
-    public void publishServiceDefinition(URL providerUrl) {
-        defaultWritableMetadataService.publishServiceDefinition(providerUrl);
-        remoteWritableMetadataService.publishServiceDefinition(providerUrl);
-    }
-
-    @Override
-    public SortedSet<String> getExportedURLs(String serviceInterface, String 
group, String version, String protocol) {
-        return 
getDefaultWritableMetadataService().getExportedURLs(serviceInterface, group, 
version, protocol);
-    }
-
-    @Override
-    public SortedSet<String> getSubscribedURLs() {
-        return getDefaultWritableMetadataService().getSubscribedURLs();
-    }
-
-    @Override
-    public String getServiceDefinition(String interfaceName, String version, 
String group) {
-        return 
getDefaultWritableMetadataService().getServiceDefinition(interfaceName, 
version, group);
-    }
-
-    @Override
-    public String getServiceDefinition(String serviceKey) {
-        return 
getDefaultWritableMetadataService().getServiceDefinition(serviceKey);
-    }
-
-    private boolean doFunction(BiFunction<WritableMetadataService, URL, 
Boolean> func, URL url) {
-        return func.apply(defaultWritableMetadataService, url) && 
func.apply(remoteWritableMetadataService, url);
-    }
-}
diff --git 
a/dubbo-metadata/dubbo-metadata-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metadata.WritableMetadataService
 
b/dubbo-metadata/dubbo-metadata-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metadata.WritableMetadataService
deleted file mode 100644
index bfe794b..0000000
--- 
a/dubbo-metadata/dubbo-metadata-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metadata.WritableMetadataService
+++ /dev/null
@@ -1,2 +0,0 @@
-local=org.apache.dubbo.metadata.store.InMemoryWritableMetadataService
-remote=org.apache.dubbo.metadata.store.RemoteWritableMetadataServiceDelegate
\ No newline at end of file
diff --git 
a/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/InMemoryWritableMetadataServiceTest.java
 
b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/InMemoryWritableMetadataServiceTest.java
deleted file mode 100644
index 461cf28..0000000
--- 
a/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/InMemoryWritableMetadataServiceTest.java
+++ /dev/null
@@ -1,149 +0,0 @@
-/*
- * 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 org.apache.dubbo.metadata.store.InMemoryWritableMetadataService;
-import org.apache.dubbo.rpc.model.ApplicationModel;
-
-import org.junit.jupiter.api.BeforeAll;
-import org.junit.jupiter.api.Test;
-
-import java.util.Set;
-import java.util.SortedSet;
-import java.util.TreeSet;
-
-import static java.util.Arrays.asList;
-import static java.util.Collections.unmodifiableSortedSet;
-import static org.apache.dubbo.common.URL.valueOf;
-import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY;
-import static org.apache.dubbo.common.constants.CommonConstants.PROTOCOL_KEY;
-import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-
-/**
- * {@link InMemoryWritableMetadataService} Test
- *
- * @since 2.7.5
- */
-public class InMemoryWritableMetadataServiceTest {
-
-    private WritableMetadataService metadataService = new 
InMemoryWritableMetadataService();
-
-    private static final String TEST_SERVICE = 
"org.apache.dubbo.test.TestService";
-
-    private static final URL BASE_URL = valueOf("dubbo://127.0.0.1:20880/" + 
TEST_SERVICE);
-    private static final URL REST_BASE_URL = valueOf("rest://127.0.0.1:20880/" 
+ TEST_SERVICE);
-    private static final URL BASE_URL_GROUP = BASE_URL.addParameter(GROUP_KEY, 
"test");
-    private static final URL BASE_URL_GROUP_AND_VERSION = 
BASE_URL_GROUP.addParameter(VERSION_KEY, "1.0.0");
-    private static final URL BASE_URL_GROUP_AND_VERSION_AND_PROTOCOL = 
BASE_URL_GROUP_AND_VERSION.addParameter(PROTOCOL_KEY, "rest");
-
-    @BeforeAll
-    public static void init() {
-        ApplicationModel.setApplication("test");
-    }
-
-    @Test
-    public void testServiceName() {
-        assertEquals("test", metadataService.serviceName());
-    }
-
-    @Test
-    public void testVersion() {
-        assertEquals("1.0.0", MetadataService.VERSION);
-        assertEquals("1.0.0", metadataService.version());
-    }
-
-    @Test
-    public void testGetExportedURLs() {
-
-        assertTrue(metadataService.exportURL(BASE_URL));
-        Set<String> exportedURLs = 
metadataService.getExportedURLs(TEST_SERVICE);
-        assertEquals(1, exportedURLs.size());
-        assertEquals(asSortedSet(BASE_URL.toFullString()), exportedURLs);
-        assertTrue(metadataService.unexportURL(BASE_URL));
-
-        assertTrue(metadataService.exportURL(BASE_URL));
-        assertFalse(metadataService.exportURL(BASE_URL));
-
-        assertTrue(metadataService.exportURL(BASE_URL_GROUP));
-        assertTrue(metadataService.exportURL(BASE_URL_GROUP_AND_VERSION));
-
-        exportedURLs = metadataService.getExportedURLs(TEST_SERVICE);
-        assertEquals(asSortedSet(BASE_URL.toFullString()), exportedURLs);
-        assertEquals(asSortedSet(
-                BASE_URL.toFullString(),
-                BASE_URL_GROUP.toFullString(),
-                BASE_URL_GROUP_AND_VERSION.toFullString()), 
metadataService.getExportedURLs());
-
-        assertTrue(metadataService.exportURL(REST_BASE_URL));
-        exportedURLs = metadataService.getExportedURLs(TEST_SERVICE);
-        assertEquals(asSortedSet(BASE_URL.toFullString(), 
REST_BASE_URL.toFullString()), exportedURLs);
-
-        metadataService.exportURL(BASE_URL_GROUP_AND_VERSION_AND_PROTOCOL);
-
-        exportedURLs = metadataService.getExportedURLs(TEST_SERVICE, "test", 
"1.0.0", "rest");
-
-        
assertEquals(asSortedSet(BASE_URL_GROUP_AND_VERSION_AND_PROTOCOL.toFullString()),
 exportedURLs);
-    }
-
-    @Test
-    public void testGetSubscribedURLs() {
-        assertTrue(metadataService.subscribeURL(BASE_URL));
-        assertFalse(metadataService.subscribeURL(BASE_URL));
-
-        assertTrue(metadataService.subscribeURL(BASE_URL_GROUP));
-        assertTrue(metadataService.subscribeURL(BASE_URL_GROUP_AND_VERSION));
-        assertTrue(metadataService.subscribeURL(REST_BASE_URL));
-
-        Set<String> subscribedURLs = metadataService.getSubscribedURLs();
-        assertEquals(4, subscribedURLs.size());
-        assertEquals(asSortedSet(
-                BASE_URL.toFullString(),
-                REST_BASE_URL.toFullString(),
-                BASE_URL_GROUP.toFullString(),
-                BASE_URL_GROUP_AND_VERSION.toFullString()), subscribedURLs);
-
-        assertTrue(metadataService.unsubscribeURL(REST_BASE_URL));
-        subscribedURLs = metadataService.getSubscribedURLs();
-        assertEquals(3, subscribedURLs.size());
-        assertEquals(asSortedSet(
-                BASE_URL.toFullString(),
-                BASE_URL_GROUP.toFullString(),
-                BASE_URL_GROUP_AND_VERSION.toFullString()), subscribedURLs);
-
-        assertTrue(metadataService.unsubscribeURL(BASE_URL_GROUP));
-        subscribedURLs = metadataService.getSubscribedURLs();
-        assertEquals(2, subscribedURLs.size());
-        assertEquals(asSortedSet(
-                BASE_URL.toFullString(),
-                BASE_URL_GROUP_AND_VERSION.toFullString()), subscribedURLs);
-
-        assertTrue(metadataService.unsubscribeURL(BASE_URL_GROUP_AND_VERSION));
-        subscribedURLs = metadataService.getSubscribedURLs();
-        assertEquals(1, subscribedURLs.size());
-        assertEquals(asSortedSet(
-                BASE_URL.toFullString()), subscribedURLs);
-    }
-
-    private static <T extends Comparable<T>> SortedSet<T> asSortedSet(T... 
values) {
-        return unmodifiableSortedSet(new TreeSet<>(asList(values)));
-    }
-
-}
\ No newline at end of file
diff --git 
a/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/store/RemoteWritableMetadataServiceDelegateTest.java
 
b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/store/RemoteWritableMetadataServiceDelegateTest.java
index 8db9534..7c95e31 100644
--- 
a/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/store/RemoteWritableMetadataServiceDelegateTest.java
+++ 
b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/store/RemoteWritableMetadataServiceDelegateTest.java
@@ -150,7 +150,7 @@ public class RemoteWritableMetadataServiceDelegateTest {
         metadataReportService.exportURL(publishUrl);
         metadataReportService.exportURL(publishUrl2);
         String exportedRevision = "9999";
-        JTestMetadataReport4Test jTestMetadataReport4Test = 
(JTestMetadataReport4Test) MetadataReportInstance.getMetadataReport(true);
+        JTestMetadataReport4Test jTestMetadataReport4Test = 
(JTestMetadataReport4Test) MetadataReportInstance.getMetadataReports(true);
         int origSize = jTestMetadataReport4Test.store.size();
         int num = countNum();
         
Assertions.assertTrue(metadataReportService.refreshMetadata(exportedRevision, 
"1109"));
diff --git 
a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistry.java
 
b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistry.java
index 6232850..2607054 100644
--- 
a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistry.java
+++ 
b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistry.java
@@ -29,6 +29,7 @@ import org.apache.dubbo.metadata.WritableMetadataService;
 import org.apache.dubbo.registry.NotifyListener;
 import org.apache.dubbo.registry.Registry;
 import 
org.apache.dubbo.registry.client.event.listener.ServiceInstancesChangedListener;
+import org.apache.dubbo.registry.client.metadata.MetadataUtils;
 import org.apache.dubbo.registry.client.metadata.SubscribedURLsSynthesizer;
 import org.apache.dubbo.registry.support.FailbackRegistry;
 
@@ -64,7 +65,6 @@ import static 
org.apache.dubbo.common.function.ThrowableAction.execute;
 import static org.apache.dubbo.common.utils.CollectionUtils.isEmpty;
 import static org.apache.dubbo.common.utils.StringUtils.isBlank;
 import static 
org.apache.dubbo.registry.client.ServiceDiscoveryFactory.getExtension;
-import static 
org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.getMetadataStorageType;
 
 /**
  * Being different to the traditional registry, {@link 
ServiceDiscoveryRegistry} that is a new service-oriented
@@ -132,8 +132,7 @@ public class ServiceDiscoveryRegistry extends 
FailbackRegistry {
         this.serviceDiscovery = createServiceDiscovery(registryURL);
         this.subscribedServices = 
parseServices(registryURL.getParameter(SUBSCRIBED_SERVICE_NAMES_KEY));
         this.serviceNameMapping = ServiceNameMapping.getDefaultExtension();
-        String metadataStorageType = getMetadataStorageType(registryURL);
-        this.writableMetadataService = 
WritableMetadataService.getExtension(metadataStorageType);
+        this.writableMetadataService = MetadataUtils.getLocalMetadataService();
     }
 
     public ServiceDiscovery getServiceDiscovery() {
@@ -299,7 +298,7 @@ public class ServiceDiscoveryRegistry extends 
FailbackRegistry {
     protected void subscribeURLs(URL url, NotifyListener listener, String 
serviceName) {
         // register ServiceInstancesChangedListener
         ServiceInstancesChangedListener serviceListener = 
serviceListeners.computeIfAbsent(serviceName,
-                k -> new ServiceInstancesChangedListener(serviceName) {
+                k -> new ServiceInstancesChangedListener(serviceName, 
serviceDiscovery, url) {
                     @Override
                     protected void notifyAddresses() {
                         listener.notifyServiceInstances();
diff --git 
a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceInstanceCustomizer.java
 
b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceInstanceCustomizer.java
index 10674fc..11f2c28 100644
--- 
a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceInstanceCustomizer.java
+++ 
b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceInstanceCustomizer.java
@@ -19,13 +19,10 @@ package org.apache.dubbo.registry.client;
 import org.apache.dubbo.common.extension.SPI;
 import org.apache.dubbo.common.lang.Prioritized;
 import 
org.apache.dubbo.registry.client.event.ServiceInstancePreRegisteredEvent;
-import 
org.apache.dubbo.registry.client.event.listener.CustomizableServiceInstanceListener;
 
 /**
  * The interface to customize {@link ServiceInstance the service instance} on 
{@link ServiceInstancePreRegisteredEvent}
  *
- * @see CustomizableServiceInstanceListener
- * @see ServiceInstancePreRegisteredEvent
  * @see ServiceInstance#getMetadata()
  * @since 2.7.5
  */
diff --git 
a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/event/listener/CustomizableServiceInstanceListener.java
 
b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/event/listener/CustomizableServiceInstanceListener.java
deleted file mode 100644
index d2ee50f..0000000
--- 
a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/event/listener/CustomizableServiceInstanceListener.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * 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.listener;
-
-import org.apache.dubbo.common.extension.ExtensionLoader;
-import org.apache.dubbo.event.EventListener;
-import org.apache.dubbo.registry.client.ServiceInstance;
-import org.apache.dubbo.registry.client.ServiceInstanceCustomizer;
-import 
org.apache.dubbo.registry.client.event.ServiceInstancePreRegisteredEvent;
-
-/**
- * Customize the {@link ServiceInstance} before registering to Registry.
- *
- * @since 2.7.5
- */
-public class CustomizableServiceInstanceListener implements 
EventListener<ServiceInstancePreRegisteredEvent> {
-
-    @Override
-    public void onEvent(ServiceInstancePreRegisteredEvent event) {
-        ExtensionLoader<ServiceInstanceCustomizer> loader =
-                
ExtensionLoader.getExtensionLoader(ServiceInstanceCustomizer.class);
-        // FIXME, sort customizer before apply
-        loader.getSupportedExtensionInstances().forEach(customizer -> {
-            // customizes
-            customizer.customize(event.getServiceInstance());
-        });
-    }
-}
diff --git 
a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/event/listener/ServiceInstancesChangedListener.java
 
b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/event/listener/ServiceInstancesChangedListener.java
index 33f0bcc..5f40b2a 100644
--- 
a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/event/listener/ServiceInstancesChangedListener.java
+++ 
b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/event/listener/ServiceInstancesChangedListener.java
@@ -16,17 +16,19 @@
  */
 package org.apache.dubbo.registry.client.event.listener;
 
+import org.apache.dubbo.common.URL;
 import org.apache.dubbo.common.utils.CollectionUtils;
 import org.apache.dubbo.event.ConditionalEventListener;
 import org.apache.dubbo.event.EventListener;
 import org.apache.dubbo.metadata.MetadataInfo;
 import org.apache.dubbo.metadata.MetadataInfo.ServiceInfo;
 import org.apache.dubbo.metadata.MetadataService;
-import org.apache.dubbo.metadata.MetadataUtils;
-import org.apache.dubbo.metadata.store.RemoteMetadataServiceImpl;
+import org.apache.dubbo.registry.client.ServiceDiscovery;
 import org.apache.dubbo.registry.client.ServiceInstance;
 import org.apache.dubbo.registry.client.event.ServiceInstancesChangedEvent;
+import org.apache.dubbo.registry.client.metadata.MetadataUtils;
 import org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils;
+import 
org.apache.dubbo.registry.client.metadata.store.RemoteMetadataServiceImpl;
 
 import java.util.ArrayList;
 import java.util.Collection;
@@ -50,6 +52,8 @@ import static 
org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataU
 public abstract class ServiceInstancesChangedListener implements 
ConditionalEventListener<ServiceInstancesChangedEvent> {
 
     private final String serviceName;
+    private final ServiceDiscovery serviceDiscovery;
+    private final URL url;
 
     private List<ServiceInstance> instances;
 
@@ -61,8 +65,10 @@ public abstract class ServiceInstancesChangedListener 
implements ConditionalEven
 
     private Map<String, List<ServiceInstance>> serviceToInstances;
 
-    protected ServiceInstancesChangedListener(String serviceName) {
+    protected ServiceInstancesChangedListener(String serviceName, 
ServiceDiscovery serviceDiscovery, URL url) {
         this.serviceName = serviceName;
+        this.serviceDiscovery = serviceDiscovery;
+        this.url = url;
     }
 
     /**
@@ -131,16 +137,15 @@ public abstract class ServiceInstancesChangedListener 
implements ConditionalEven
 
     private MetadataInfo getMetadataInfo(ServiceInstance instance) {
         String metadataType = 
ServiceInstanceMetadataUtils.getMetadataStorageType(instance);
-        String cluster = 
ServiceInstanceMetadataUtils.getRemoteCluster(instance);
 
         MetadataInfo metadataInfo;
         try {
             if (REMOTE_METADATA_STORAGE_TYPE.equals(metadataType)) {
-                RemoteMetadataServiceImpl remoteMetadataService = 
MetadataUtils.getConsumerRemoteMetadataService(consumerUrl.getServiceKey());
-                metadataInfo = remoteMetadataService.getMetadata(serviceName, 
revision);
+                RemoteMetadataServiceImpl remoteMetadataService = 
MetadataUtils.getRemoteMetadataService();
+                metadataInfo = remoteMetadataService.getMetadata(instance);
             } else {
-                MetadataService localMetadataService = 
MetadataUtils.getLocalMetadataService();
-                metadataInfo = localMetadataService.getMetadataInfo();
+                MetadataService metadataServiceProxy = 
MetadataUtils.getMetadataServiceProxy(instance);
+                metadataInfo = metadataServiceProxy.getMetadataInfo();
             }
         } catch (Exception e) {
             // TODO, load metadata backup
@@ -149,7 +154,6 @@ public abstract class ServiceInstancesChangedListener 
implements ConditionalEven
         return metadataInfo;
     }
 
-
     protected abstract void notifyAddresses();
 
     /**
@@ -161,6 +165,10 @@ public abstract class ServiceInstancesChangedListener 
implements ConditionalEven
         return serviceName;
     }
 
+    public URL getUrl() {
+        return url;
+    }
+
     /**
      * @param event {@link ServiceInstancesChangedEvent event}
      * @return If service name matches, return <code>true</code>, or 
<code>false</code>
diff --git 
a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/ExportedServicesRevisionMetadataCustomizer.java
 
b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/ExportedServicesRevisionMetadataCustomizer.java
deleted file mode 100644
index a356068..0000000
--- 
a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/ExportedServicesRevisionMetadataCustomizer.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * 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.metadata;
-
-import org.apache.dubbo.metadata.WritableMetadataService;
-import org.apache.dubbo.registry.client.ServiceInstance;
-import org.apache.dubbo.registry.client.ServiceInstanceMetadataCustomizer;
-
-import java.util.SortedSet;
-
-import static org.apache.dubbo.metadata.WritableMetadataService.getExtension;
-import static 
org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.EXPORTED_SERVICES_REVISION_PROPERTY_NAME;
-import static 
org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.getMetadataStorageType;
-
-/**
- * The customizer to a add the metadata that the reversion of Dubbo exported 
services calculates.
- * <p>
- * The reversion is calculated on the methods that all Dubbo exported 
interfaces declare
- *
- * @since 2.7.5
- */
-public class ExportedServicesRevisionMetadataCustomizer extends 
ServiceInstanceMetadataCustomizer {
-
-    @Override
-    protected String resolveMetadataPropertyName(ServiceInstance 
serviceInstance) {
-        return EXPORTED_SERVICES_REVISION_PROPERTY_NAME;
-    }
-
-    @Override
-    protected String resolveMetadataPropertyValue(ServiceInstance 
serviceInstance) {
-
-        String metadataStorageType = getMetadataStorageType(serviceInstance);
-
-        WritableMetadataService writableMetadataService = 
getExtension(metadataStorageType);
-
-        SortedSet<String> exportedURLs = 
writableMetadataService.getExportedURLs();
-
-        URLRevisionResolver resolver = new URLRevisionResolver();
-
-        return resolver.resolve(exportedURLs);
-    }
-}
diff --git 
a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/MetadataUtils.java
 
b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/MetadataUtils.java
new file mode 100644
index 0000000..0926f7d
--- /dev/null
+++ 
b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/MetadataUtils.java
@@ -0,0 +1,112 @@
+/*
+ * 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.metadata;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.extension.ExtensionLoader;
+import org.apache.dubbo.common.utils.CollectionUtils;
+import org.apache.dubbo.common.utils.StringUtils;
+import org.apache.dubbo.metadata.MetadataService;
+import org.apache.dubbo.metadata.WritableMetadataService;
+import org.apache.dubbo.registry.client.ServiceInstance;
+import 
org.apache.dubbo.registry.client.metadata.store.InMemoryWritableMetadataService;
+import 
org.apache.dubbo.registry.client.metadata.store.RemoteMetadataServiceImpl;
+import org.apache.dubbo.rpc.Invoker;
+import org.apache.dubbo.rpc.Protocol;
+import org.apache.dubbo.rpc.ProxyFactory;
+
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import static 
org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.METADATA_SERVICE_URLS_PROPERTY_NAME;
+
+public class MetadataUtils {
+
+    private static final Object LOCK = new Object();
+    private static final Object REMOTE_LOCK = new Object();
+
+    public static ConcurrentMap<String, MetadataService> 
metadataServiceProxies = new ConcurrentHashMap<>();
+
+    private static final ProxyFactory proxyFactory = 
ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
+
+    private static final Protocol protocol = 
ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
+
+    public static RemoteMetadataServiceImpl remoteMetadataService;
+
+    public static WritableMetadataService localMetadataService;
+
+    public static RemoteMetadataServiceImpl getRemoteMetadataService() {
+        if (remoteMetadataService == null) {
+            synchronized (REMOTE_LOCK) {
+                if (remoteMetadataService == null) {
+                    remoteMetadataService = new 
RemoteMetadataServiceImpl(getLocalMetadataService());
+                }
+            }
+        }
+        return remoteMetadataService;
+    }
+
+    public static WritableMetadataService getLocalMetadataService() {
+        if (localMetadataService == null) {
+            synchronized (LOCK) {
+                if (localMetadataService == null) {
+                    localMetadataService = new 
InMemoryWritableMetadataService();
+                }
+            }
+        }
+        return localMetadataService;
+    }
+
+    public static void publishServiceDefinition(URL url) {
+        // store in local
+        getLocalMetadataService().publishServiceDefinition(url);
+        // send to remote
+        getRemoteMetadataService().publishServiceDefinition(url);
+    }
+
+    public static MetadataService getMetadataServiceProxy(ServiceInstance 
instance) {
+        String key = instance.getServiceName() + "##" +
+                
ServiceInstanceMetadataUtils.getExportedServicesRevision(instance);
+        return metadataServiceProxies.computeIfAbsent(key, k -> {
+            MetadataServiceURLBuilder builder = null;
+            ExtensionLoader<MetadataServiceURLBuilder> loader
+                    = 
ExtensionLoader.getExtensionLoader(MetadataServiceURLBuilder.class);
+
+            Map<String, String> metadata = instance.getMetadata();
+            // METADATA_SERVICE_URLS_PROPERTY_NAME is a unique key exists only 
on instances of spring-cloud-alibaba.
+            String dubboURLsJSON = 
metadata.get(METADATA_SERVICE_URLS_PROPERTY_NAME);
+            if (StringUtils.isNotEmpty(dubboURLsJSON)) {
+                builder = 
loader.getExtension(SpringCloudMetadataServiceURLBuilder.NAME);
+            } else {
+                builder = 
loader.getExtension(StandardMetadataServiceURLBuilder.NAME);
+            }
+
+            List<URL> urls = builder.build(instance);
+            if (CollectionUtils.isEmpty(urls)) {
+                throw new IllegalStateException("You have enabled 
introspection service discovery mode for instance "
+                        + instance + ", but no metadata service can build from 
it.");
+            }
+
+            // Simply rely on the first metadata url, as stated in 
MetadataServiceURLBuilder.
+            Invoker<MetadataService> invoker = 
protocol.refer(MetadataService.class, urls.get(0));
+
+            return proxyFactory.getProxy(invoker);
+        });
+    }
+}
diff --git 
a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/ServiceInstanceMetadataUtils.java
 
b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/ServiceInstanceMetadataUtils.java
index b66ae79..75903ee 100644
--- 
a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/ServiceInstanceMetadataUtils.java
+++ 
b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/ServiceInstanceMetadataUtils.java
@@ -85,6 +85,8 @@ public class ServiceInstanceMetadataUtils {
      */
     public static String METADATA_STORAGE_TYPE_PROPERTY_NAME = 
"dubbo.metadata.storage-type";
 
+    public static String METADATA_CLUSTER_PROPERTY_NAME = 
"dubbo.metadata.cluster";
+
     /**
      * Get the multiple {@link URL urls'} parameters of {@link MetadataService 
MetadataService's} Metadata
      *
@@ -195,6 +197,11 @@ public class ServiceInstanceMetadataUtils {
         metadata.put(METADATA_STORAGE_TYPE_PROPERTY_NAME, metadataType);
     }
 
+    public static String getRemoteCluster(ServiceInstance serviceInstance) {
+        Map<String, String> metadata = serviceInstance.getMetadata();
+        return metadata.get(METADATA_CLUSTER_PROPERTY_NAME);
+    }
+
     /**
      * Is Dubbo Service instance or not
      *
diff --git 
a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/SubscribedServicesRevisionMetadataCustomizer.java
 
b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/SubscribedServicesRevisionMetadataCustomizer.java
deleted file mode 100644
index fd5646b..0000000
--- 
a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/SubscribedServicesRevisionMetadataCustomizer.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * 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.metadata;
-
-import org.apache.dubbo.metadata.WritableMetadataService;
-import org.apache.dubbo.registry.client.ServiceInstance;
-import org.apache.dubbo.registry.client.ServiceInstanceMetadataCustomizer;
-
-import java.util.SortedSet;
-
-import static org.apache.dubbo.metadata.WritableMetadataService.getExtension;
-import static 
org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.SUBSCRIBER_SERVICES_REVISION_PROPERTY_NAME;
-import static 
org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.getMetadataStorageType;
-
-/**
- * The customizer to a add the metadata that the reversion of Dubbo subscribed 
services calculates.
- * <p>
- * The reversion is calculated on the methods that all Dubbo subscribed 
interfaces declare
- *
- * @since 2.7.5
- */
-public class SubscribedServicesRevisionMetadataCustomizer extends 
ServiceInstanceMetadataCustomizer {
-
-    @Override
-    protected String resolveMetadataPropertyName(ServiceInstance 
serviceInstance) {
-        return SUBSCRIBER_SERVICES_REVISION_PROPERTY_NAME;
-    }
-
-    @Override
-    protected String resolveMetadataPropertyValue(ServiceInstance 
serviceInstance) {
-
-        String metadataStorageType = getMetadataStorageType(serviceInstance);
-
-        WritableMetadataService writableMetadataService = 
getExtension(metadataStorageType);
-
-        SortedSet<String> subscribedURLs = 
writableMetadataService.getSubscribedURLs();
-
-        URLRevisionResolver resolver = new URLRevisionResolver();
-
-        return resolver.resolve(subscribedURLs);
-    }
-
-}
diff --git 
a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/URLRevisionResolver.java
 
b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/URLRevisionResolver.java
deleted file mode 100644
index 66f4dbf..0000000
--- 
a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/URLRevisionResolver.java
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * 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.metadata;
-
-import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.compiler.support.ClassUtils;
-import org.apache.dubbo.metadata.MetadataService;
-
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.List;
-import java.util.Set;
-import java.util.SortedSet;
-import java.util.TreeSet;
-import java.util.stream.Collectors;
-
-import static org.apache.dubbo.common.constants.CommonConstants.PID_KEY;
-import static org.apache.dubbo.common.constants.CommonConstants.TIMESTAMP_KEY;
-import static org.apache.dubbo.common.utils.CollectionUtils.isEmpty;
-
-/**
- * A class to resolve the version from {@link URL URLs}
- *
- * @since 2.7.5
- */
-public class URLRevisionResolver {
-
-    public static final String NO_REVISION = "N/A";
-
-    /**
-     * Resolve revision as {@link String}
-     *
-     * @param urls {@link URL#toFullString() strings} presenting the {@link 
URL URLs}
-     * @return non-null
-     */
-    public String resolve(Collection<String> urls) {
-
-        if (isEmpty(urls)) {
-            return NO_REVISION;
-        }
-
-        List<URL> urlsList = toURLsList(urls);
-
-        SortedSet<String> methodSignatures = resolveMethodSignatures(urlsList);
-
-        SortedSet<String> urlParameters = resolveURLParameters(urlsList);
-
-        SortedSet<String> values = new TreeSet<>(methodSignatures);
-
-        values.addAll(urlParameters);
-
-        return values.stream()
-                .map(this::hashCode)                     // generate Long 
hashCode
-                .reduce(Long::sum)                       // sum hashCode
-                .map(String::valueOf)                    // Long to String
-                .orElse(NO_REVISION);                    // NO_REVISION as 
default
-    }
-
-    private List<URL> toURLsList(Collection<String> urls) {
-        return urls.stream()
-                .map(URL::valueOf)                             // String to URL
-                .filter(url -> 
isNotMetadataService(url.getServiceInterface())) // filter not MetadataService 
interface
-                .collect(Collectors.toList());
-    }
-
-    private SortedSet<String> resolveMethodSignatures(List<URL> urls) {
-        return urls.stream()
-                .map(URL::getServiceInterface)                 // get the 
service interface
-                .map(ClassUtils::forName)                      // load 
business interface class
-                .map(Class::getMethods)                        // get all 
public methods from business interface
-                .map(Arrays::asList)                           // Array to List
-                .flatMap(Collection::stream)                   // flat 
Stream<Stream> to be Stream
-                .map(Object::toString)                         // Method to 
String
-                .collect(TreeSet::new, Set::add, Set::addAll); // sort and 
remove the duplicate
-    }
-
-    private SortedSet<String> resolveURLParameters(Collection<URL> urls) {
-        return urls.stream()
-                .map(url -> url.removeParameter(PID_KEY))
-                .map(url -> url.removeParameter(TIMESTAMP_KEY))
-                .map(URL::toParameterString)
-                .collect(TreeSet::new, Set::add, Set::addAll); // sort and 
remove the duplicate
-    }
-
-    private long hashCode(String value) {
-        long h = 0;
-        char[] chars = value.toCharArray();
-        for (int i = 0; i < chars.length; i++) {
-            h = 31L * h + chars[i];
-        }
-        return h;
-    }
-
-    private boolean isNotMetadataService(String serviceInterface) {
-        return !MetadataService.class.getName().equals(serviceInterface);
-    }
-}
diff --git 
a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/proxy/BaseMetadataServiceProxyFactory.java
 
b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/proxy/BaseMetadataServiceProxyFactory.java
deleted file mode 100644
index 1e8d9fa..0000000
--- 
a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/proxy/BaseMetadataServiceProxyFactory.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * 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.metadata.proxy;
-
-import org.apache.dubbo.metadata.MetadataService;
-import org.apache.dubbo.registry.client.ServiceInstance;
-import org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils;
-
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * base class for remote and local implementations.
- */
-abstract class BaseMetadataServiceProxyFactory implements 
MetadataServiceProxyFactory {
-    private final Map<String, MetadataService> proxies = new HashMap<>();
-
-    public final MetadataService getProxy(ServiceInstance serviceInstance) {
-        return proxies.computeIfAbsent(serviceInstance.getServiceName() + "##" 
+
-                
ServiceInstanceMetadataUtils.getExportedServicesRevision(serviceInstance), id 
-> createProxy(serviceInstance));
-    }
-
-    protected abstract MetadataService createProxy(ServiceInstance 
serviceInstance);
-}
diff --git 
a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/proxy/DefaultMetadataServiceProxyFactory.java
 
b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/proxy/DefaultMetadataServiceProxyFactory.java
deleted file mode 100644
index 90b3719..0000000
--- 
a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/proxy/DefaultMetadataServiceProxyFactory.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * 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.metadata.proxy;
-
-import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.extension.ExtensionLoader;
-import org.apache.dubbo.common.utils.CollectionUtils;
-import org.apache.dubbo.common.utils.StringUtils;
-import org.apache.dubbo.metadata.MetadataService;
-import org.apache.dubbo.registry.client.ServiceInstance;
-import org.apache.dubbo.registry.client.metadata.MetadataServiceURLBuilder;
-import 
org.apache.dubbo.registry.client.metadata.SpringCloudMetadataServiceURLBuilder;
-import 
org.apache.dubbo.registry.client.metadata.StandardMetadataServiceURLBuilder;
-import org.apache.dubbo.rpc.Invoker;
-import org.apache.dubbo.rpc.Protocol;
-import org.apache.dubbo.rpc.ProxyFactory;
-
-import java.util.List;
-import java.util.Map;
-
-import static 
org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.METADATA_SERVICE_URLS_PROPERTY_NAME;
-
-/**
- * Works on Consumer side, useful when using local metadata mode.
- *
- * Use this implementation to generate the proxy on Consumer side representing 
the remote MetadataService
- * exposed on the Provider side. Also see {@link 
RemoteMetadataServiceProxyFactory}
- *
- * @since 2.7.5
- */
-public class DefaultMetadataServiceProxyFactory extends 
BaseMetadataServiceProxyFactory implements MetadataServiceProxyFactory {
-
-    private ProxyFactory proxyFactory;
-
-    private Protocol protocol;
-
-    public void setProtocol(Protocol protocol) {
-        this.protocol = protocol;
-    }
-
-    public void setProxyFactory(ProxyFactory proxyFactory) {
-        this.proxyFactory = proxyFactory;
-    }
-
-
-    protected MetadataService createProxy(ServiceInstance serviceInstance) {
-        MetadataServiceURLBuilder builder = null;
-        ExtensionLoader<MetadataServiceURLBuilder> loader
-                = 
ExtensionLoader.getExtensionLoader(MetadataServiceURLBuilder.class);
-
-        Map<String, String> metadata = serviceInstance.getMetadata();
-        // METADATA_SERVICE_URLS_PROPERTY_NAME is a unique key exists only on 
instances of spring-cloud-alibaba.
-        String dubboURLsJSON = 
metadata.get(METADATA_SERVICE_URLS_PROPERTY_NAME);
-        if (StringUtils.isNotEmpty(dubboURLsJSON)) {
-            builder = 
loader.getExtension(SpringCloudMetadataServiceURLBuilder.NAME);
-        } else {
-            builder = 
loader.getExtension(StandardMetadataServiceURLBuilder.NAME);
-        }
-
-        List<URL> urls = builder.build(serviceInstance);
-        if (CollectionUtils.isEmpty(urls)) {
-            throw new IllegalStateException("You have enabled introspection 
service discovery mode for instance "
-                    + serviceInstance + ", but no metadata service can build 
from it.");
-        }
-
-        // Simply rely on the first metadata url, as stated in 
MetadataServiceURLBuilder.
-        Invoker<MetadataService> invoker = 
protocol.refer(MetadataService.class, urls.get(0));
-
-        return proxyFactory.getProxy(invoker);
-    }
-}
diff --git 
a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/proxy/MetadataServiceProxyFactory.java
 
b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/proxy/MetadataServiceProxyFactory.java
deleted file mode 100644
index 14523ac..0000000
--- 
a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/proxy/MetadataServiceProxyFactory.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * 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.metadata.proxy;
-
-import org.apache.dubbo.common.extension.SPI;
-import org.apache.dubbo.metadata.MetadataService;
-import org.apache.dubbo.registry.client.ServiceInstance;
-
-import static 
org.apache.dubbo.common.constants.CommonConstants.DEFAULT_METADATA_STORAGE_TYPE;
-import static 
org.apache.dubbo.common.extension.ExtensionLoader.getExtensionLoader;
-
-/**
- * A factory to create a {@link MetadataService} proxy
- *
- * @see ServiceInstance
- * @see MetadataService
- * @since 2.7.5
- */
-@SPI(DEFAULT_METADATA_STORAGE_TYPE)
-
-public interface MetadataServiceProxyFactory {
-
-    /**
-     * Create a {@link MetadataService} proxy via the specified {@link 
ServiceInstance}
-     *WritableMetadataService
-     * @param serviceInstance the instance of {@link ServiceInstance}
-     * @return non-null
-     */
-    MetadataService getProxy(ServiceInstance serviceInstance);
-
-    /**
-     * Get the default extension of {@link MetadataServiceProxyFactory}
-     *
-     * @return non-null
-     */
-    static MetadataServiceProxyFactory getDefaultExtension() {
-        return 
getExtensionLoader(MetadataServiceProxyFactory.class).getDefaultExtension();
-    }
-
-    static MetadataServiceProxyFactory getExtension(String name) {
-        return 
getExtensionLoader(MetadataServiceProxyFactory.class).getExtension(name);
-    }
-}
diff --git 
a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/proxy/RemoteMetadataServiceProxy.java
 
b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/proxy/RemoteMetadataServiceProxy.java
deleted file mode 100644
index e6bc86d..0000000
--- 
a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/proxy/RemoteMetadataServiceProxy.java
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * 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.metadata.proxy;
-
-import org.apache.dubbo.common.logger.Logger;
-import org.apache.dubbo.common.logger.LoggerFactory;
-import org.apache.dubbo.common.utils.UrlUtils;
-import org.apache.dubbo.metadata.MetadataService;
-import org.apache.dubbo.metadata.report.MetadataReport;
-import org.apache.dubbo.metadata.report.MetadataReportInstance;
-import org.apache.dubbo.metadata.report.identifier.MetadataIdentifier;
-import org.apache.dubbo.metadata.report.identifier.ServiceMetadataIdentifier;
-import org.apache.dubbo.registry.client.ServiceInstance;
-import org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils;
-
-import java.util.Collection;
-import java.util.Collections;
-import java.util.SortedSet;
-import java.util.TreeSet;
-
-import static org.apache.dubbo.common.constants.CommonConstants.PROVIDER_SIDE;
-import static 
org.apache.dubbo.registry.client.metadata.URLRevisionResolver.NO_REVISION;
-
-public class RemoteMetadataServiceProxy implements MetadataService {
-    protected final Logger logger = LoggerFactory.getLogger(getClass());
-
-    private String serviceName;
-    private String revision;
-
-
-    public RemoteMetadataServiceProxy(ServiceInstance serviceInstance) {
-        this.serviceName = serviceInstance.getServiceName();
-        // this is ServiceInstance of registry(Provider)
-        this.revision = serviceInstance.getMetadata()
-                
.getOrDefault(ServiceInstanceMetadataUtils.EXPORTED_SERVICES_REVISION_PROPERTY_NAME,
 NO_REVISION);
-    }
-
-    @Override
-    public String serviceName() {
-        return serviceName;
-    }
-
-    @Override
-    public SortedSet<String> getExportedURLs(String serviceInterface, String 
group, String version, String protocol) {
-        return toSortedStrings(getMetadataReport().getExportedURLs(
-                new ServiceMetadataIdentifier(serviceInterface, group, 
version, PROVIDER_SIDE, revision, protocol)));
-    }
-
-    private static SortedSet<String> toSortedStrings(Collection<String> 
values) {
-        return Collections.unmodifiableSortedSet(new TreeSet<>(values));
-    }
-
-    @Override
-    public String getServiceDefinition(String interfaceName, String version, 
String group) {
-        return getMetadataReport().getServiceDefinition(new 
MetadataIdentifier(interfaceName,
-                version, group, PROVIDER_SIDE, serviceName));
-    }
-
-    @Override
-    public String getServiceDefinition(String serviceKey) {
-        String[] services = UrlUtils.parseServiceKey(serviceKey);
-        String serviceInterface = services[0];
-        // if version or group is not exist
-        String version = null;
-        if (services.length > 1) {
-            version = services[1];
-        }
-        String group = null;
-        if (services.length > 2) {
-            group = services[2];
-        }
-        return getMetadataReport().getServiceDefinition(new 
MetadataIdentifier(serviceInterface,
-                version, group, PROVIDER_SIDE, serviceName));
-    }
-
-    MetadataReport getMetadataReport() {
-        return MetadataReportInstance.getMetadataReport(true);
-    }
-
-
-}
diff --git 
a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/proxy/RemoteMetadataServiceProxyFactory.java
 
b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/proxy/RemoteMetadataServiceProxyFactory.java
deleted file mode 100644
index e7188a8..0000000
--- 
a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/proxy/RemoteMetadataServiceProxyFactory.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * 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.metadata.proxy;
-
-import org.apache.dubbo.metadata.MetadataService;
-import org.apache.dubbo.registry.client.ServiceInstance;
-
-/**
- * Works on Consumer side, useful when using remote metadata mode. Also see 
{@link DefaultMetadataServiceProxyFactory}
- *
- * Provider will not expose the embedded {@link MetadataService}, instead, 
each provider register its metadata info to a
- * remote Metadata Center. So the Consumer side need to connect to the same 
Metadata Center to get one instance's metadata.
- */
-public class RemoteMetadataServiceProxyFactory extends 
BaseMetadataServiceProxyFactory implements MetadataServiceProxyFactory {
-
-    @Override
-    public MetadataService createProxy(ServiceInstance serviceInstance) {
-        return new RemoteMetadataServiceProxy(serviceInstance);
-    }
-}
diff --git 
a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/store/InMemoryWritableMetadataService.java
 
b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/store/InMemoryWritableMetadataService.java
similarity index 93%
rename from 
dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/store/InMemoryWritableMetadataService.java
rename to 
dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/store/InMemoryWritableMetadataService.java
index 8d62ae1..b9e7877 100644
--- 
a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/store/InMemoryWritableMetadataService.java
+++ 
b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/store/InMemoryWritableMetadataService.java
@@ -14,12 +14,14 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.metadata.store;
+package org.apache.dubbo.registry.client.metadata.store;
 
 import org.apache.dubbo.common.URL;
 import org.apache.dubbo.common.logger.Logger;
 import org.apache.dubbo.common.logger.LoggerFactory;
 import org.apache.dubbo.common.utils.StringUtils;
+import org.apache.dubbo.metadata.MetadataInfo;
+import org.apache.dubbo.metadata.MetadataInfo.ServiceInfo;
 import org.apache.dubbo.metadata.MetadataService;
 import org.apache.dubbo.metadata.WritableMetadataService;
 import org.apache.dubbo.metadata.definition.ServiceDefinitionBuilder;
@@ -67,6 +69,7 @@ public class InMemoryWritableMetadataService implements 
WritableMetadataService
      * and value is the {@link SortedSet sorted set} of the {@link URL URLs}
      */
     ConcurrentNavigableMap<String, SortedSet<URL>> exportedServiceURLs = new 
ConcurrentSkipListMap<>();
+    MetadataInfo metadataInfo;
 
     // 
====================================================================================
 //
 
@@ -81,6 +84,10 @@ public class InMemoryWritableMetadataService implements 
WritableMetadataService
 
     ConcurrentNavigableMap<String, String> serviceDefinitions = new 
ConcurrentSkipListMap<>();
 
+    public InMemoryWritableMetadataService() {
+        this.metadataInfo = new MetadataInfo();
+    }
+
     @Override
     public SortedSet<String> getSubscribedURLs() {
         return getAllUnmodifiableServiceURLs(subscribedServiceURLs);
@@ -112,11 +119,15 @@ public class InMemoryWritableMetadataService implements 
WritableMetadataService
 
     @Override
     public boolean exportURL(URL url) {
+        ServiceInfo serviceInfo = new ServiceInfo(url);
+        metadataInfo.addService(serviceInfo);
         return addURL(exportedServiceURLs, url);
     }
 
     @Override
     public boolean unexportURL(URL url) {
+        ServiceInfo serviceInfo = new ServiceInfo(url);
+        metadataInfo.removeService(serviceInfo);
         return removeURL(exportedServiceURLs, url);
     }
 
@@ -160,6 +171,11 @@ public class InMemoryWritableMetadataService implements 
WritableMetadataService
         return serviceDefinitions.get(serviceKey);
     }
 
+    @Override
+    public MetadataInfo getMetadataInfo() {
+        return metadataInfo;
+    }
+
     boolean addURL(Map<String, SortedSet<URL>> serviceURLs, URL url) {
         return executeMutually(() -> {
             SortedSet<URL> urls = 
serviceURLs.computeIfAbsent(url.getServiceKey(), this::newSortedURLs);
diff --git 
a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/store/RemoteMetadataServiceImpl.java
 
b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/store/RemoteMetadataServiceImpl.java
new file mode 100644
index 0000000..ac0cbc2
--- /dev/null
+++ 
b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/store/RemoteMetadataServiceImpl.java
@@ -0,0 +1,126 @@
+/*
+ * 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.metadata.store;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.common.utils.StringUtils;
+import org.apache.dubbo.metadata.MetadataInfo;
+import org.apache.dubbo.metadata.WritableMetadataService;
+import org.apache.dubbo.metadata.definition.ServiceDefinitionBuilder;
+import org.apache.dubbo.metadata.definition.model.FullServiceDefinition;
+import org.apache.dubbo.metadata.report.MetadataReport;
+import org.apache.dubbo.metadata.report.MetadataReportInstance;
+import org.apache.dubbo.metadata.report.identifier.MetadataIdentifier;
+import 
org.apache.dubbo.metadata.report.identifier.SubscriberMetadataIdentifier;
+import org.apache.dubbo.registry.client.ServiceInstance;
+import org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils;
+import org.apache.dubbo.remoting.Constants;
+import org.apache.dubbo.rpc.RpcException;
+
+import java.util.List;
+
+import static 
org.apache.dubbo.common.constants.CommonConstants.APPLICATION_KEY;
+import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER_SIDE;
+import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY;
+import static org.apache.dubbo.common.constants.CommonConstants.INTERFACE_KEY;
+import static org.apache.dubbo.common.constants.CommonConstants.PID_KEY;
+import static org.apache.dubbo.common.constants.CommonConstants.PROVIDER_SIDE;
+import static org.apache.dubbo.common.constants.CommonConstants.SIDE_KEY;
+import static org.apache.dubbo.common.constants.CommonConstants.TIMESTAMP_KEY;
+import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY;
+
+public class RemoteMetadataServiceImpl {
+
+    protected final Logger logger = LoggerFactory.getLogger(getClass());
+    private WritableMetadataService localMetadataService;
+
+    public RemoteMetadataServiceImpl(WritableMetadataService 
writableMetadataService) {
+        this.localMetadataService = writableMetadataService;
+    }
+
+    public List<MetadataReport> getMetadataReports() {
+        return MetadataReportInstance.getMetadataReports(true);
+    }
+
+    public void publishMetadata(ServiceInstance instance) {
+        SubscriberMetadataIdentifier identifier = new 
SubscriberMetadataIdentifier(instance.getServiceName(), 
ServiceInstanceMetadataUtils.getExportedServicesRevision(instance));
+        getMetadataReports().forEach(metadataReport -> {
+            metadataReport.publishAppMetadata(identifier, 
localMetadataService.getMetadataInfo());
+        });
+    }
+
+    public MetadataInfo getMetadata(ServiceInstance instance) {
+        SubscriberMetadataIdentifier identifier = new 
SubscriberMetadataIdentifier(instance.getServiceName(), 
ServiceInstanceMetadataUtils.getExportedServicesRevision(instance));
+        for (MetadataReport reporter : getMetadataReports()) {
+            MetadataInfo metadataInfo = reporter.getAppMetadata(identifier, 
instance.getMetadata());
+            if (metadataInfo != null) {
+                return metadataInfo;
+            }
+        }
+        return null;
+    }
+
+    public void publishServiceDefinition(URL url) {
+        String side = url.getParameter(SIDE_KEY);
+        if (PROVIDER_SIDE.equalsIgnoreCase(side)) {
+            //TODO, the params part is duplicate with that stored by 
exportURL(url), can be further optimized in the future.
+            publishProvider(url);
+        } else {
+            //TODO, only useful for ops showing the url parameters, this is 
duplicate with subscribeURL(url), can be removed in the future.
+            publishConsumer(url);
+        }
+    }
+
+    private void publishProvider(URL providerUrl) throws RpcException {
+        //first add into the list
+        // remove the individual param
+        providerUrl = providerUrl.removeParameters(PID_KEY, TIMESTAMP_KEY, 
Constants.BIND_IP_KEY,
+                Constants.BIND_PORT_KEY, TIMESTAMP_KEY);
+
+        try {
+            String interfaceName = providerUrl.getParameter(INTERFACE_KEY);
+            if (StringUtils.isNotEmpty(interfaceName)) {
+                Class interfaceClass = Class.forName(interfaceName);
+                FullServiceDefinition fullServiceDefinition = 
ServiceDefinitionBuilder.buildFullDefinition(interfaceClass,
+                        providerUrl.getParameters());
+                for (MetadataReport metadataReport : getMetadataReports()) {
+                    metadataReport.storeProviderMetadata(new 
MetadataIdentifier(providerUrl.getServiceInterface(),
+                            providerUrl.getParameter(VERSION_KEY), 
providerUrl.getParameter(GROUP_KEY),
+                            PROVIDER_SIDE, 
providerUrl.getParameter(APPLICATION_KEY)), fullServiceDefinition);
+                }
+                return;
+            }
+            logger.error("publishProvider interfaceName is empty . 
providerUrl: " + providerUrl.toFullString());
+        } catch (ClassNotFoundException e) {
+            //ignore error
+            logger.error("publishProvider getServiceDescriptor error. 
providerUrl: " + providerUrl.toFullString(), e);
+        }
+    }
+
+    private void publishConsumer(URL consumerURL) throws RpcException {
+        final URL url = consumerURL.removeParameters(PID_KEY, TIMESTAMP_KEY, 
Constants.BIND_IP_KEY,
+                Constants.BIND_PORT_KEY, TIMESTAMP_KEY);
+        getMetadataReports().forEach(config -> {
+            config.storeConsumerMetadata(new 
MetadataIdentifier(url.getServiceInterface(),
+                    url.getParameter(VERSION_KEY), 
url.getParameter(GROUP_KEY), CONSUMER_SIDE,
+                    url.getParameter(APPLICATION_KEY)), url.getParameters());
+        });
+    }
+
+}
diff --git 
a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryDirectory.java
 
b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryDirectory.java
index 1db7eb0..1372556 100644
--- 
a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryDirectory.java
+++ 
b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryDirectory.java
@@ -183,6 +183,7 @@ public class RegistryDirectory<T> extends 
AbstractDirectory<T> implements Notify
                 );
             }
         }
+        // FIXME, filter out unmatched urls before notify: version, group, 
protocol, registry
         notify(urls);
     }
 
diff --git 
a/dubbo-registry/dubbo-registry-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.event.EventListener
 
b/dubbo-registry/dubbo-registry-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.event.EventListener
index 729e8a5..d856dd2 100644
--- 
a/dubbo-registry/dubbo-registry-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.event.EventListener
+++ 
b/dubbo-registry/dubbo-registry-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.event.EventListener
@@ -1,2 +1 @@
-service-instance=org.apache.dubbo.registry.client.event.listener.CustomizableServiceInstanceListener
 
registry-logging=org.apache.dubbo.registry.client.event.listener.LoggingEventListener
\ No newline at end of file
diff --git 
a/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/event/listener/LoggingEventListenerTest.java
 
b/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/event/listener/LoggingEventListenerTest.java
index 0933092..7eda41a 100644
--- 
a/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/event/listener/LoggingEventListenerTest.java
+++ 
b/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/event/listener/LoggingEventListenerTest.java
@@ -32,7 +32,8 @@ import 
org.apache.dubbo.registry.client.event.ServiceInstancesChangedEvent;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
-import static java.util.Collections.singleton;
+import java.util.Arrays;
+
 import static 
org.apache.dubbo.registry.client.DefaultServiceInstanceTest.createInstance;
 
 /**
@@ -71,7 +72,7 @@ public class LoggingEventListenerTest {
         listener.onEvent(new ServiceInstanceRegisteredEvent(serviceDiscovery, 
createInstance()));
 
         // ServiceInstancesChangedEvent
-        listener.onEvent(new ServiceInstancesChangedEvent("test", 
singleton(createInstance())));
+        listener.onEvent(new ServiceInstancesChangedEvent("test", 
Arrays.asList(createInstance())));
 
         // ServiceInstancePreUnregisteredEvent
         listener.onEvent(new 
ServiceInstancePreUnregisteredEvent(serviceDiscovery, createInstance()));
diff --git 
a/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/event/listener/ServiceInstancesChangedListenerTest.java
 
b/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/event/listener/ServiceInstancesChangedListenerTest.java
index 8e76e44..5095f11 100644
--- 
a/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/event/listener/ServiceInstancesChangedListenerTest.java
+++ 
b/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/event/listener/ServiceInstancesChangedListenerTest.java
@@ -16,18 +16,8 @@
  */
 package org.apache.dubbo.registry.client.event.listener;
 
-import org.apache.dubbo.event.Event;
-import org.apache.dubbo.event.EventDispatcher;
-import org.apache.dubbo.registry.client.event.ServiceInstancesChangedEvent;
-
 import org.junit.jupiter.api.Test;
 
-import java.util.concurrent.atomic.AtomicReference;
-
-import static java.util.Collections.emptyList;
-import static org.apache.dubbo.event.EventDispatcher.getDefaultExtension;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-
 /**
  * {@link ServiceInstancesChangedListener} Test
  *
@@ -38,22 +28,5 @@ public class ServiceInstancesChangedListenerTest {
     @Test
     public void testOnEvent() {
 
-        EventDispatcher eventDispatcher = getDefaultExtension();
-
-        Event event = new ServiceInstancesChangedEvent("test", emptyList());
-
-        AtomicReference<Event> eventRef = new AtomicReference<>();
-
-        eventDispatcher.addEventListener(new 
ServiceInstancesChangedListener("test") {
-            @Override
-            public void onEvent(ServiceInstancesChangedEvent event) {
-                eventRef.set(event);
-            }
-        });
-
-        // Dispatch a ServiceInstancesChangedEvent
-        eventDispatcher.dispatch(event);
-
-        assertEquals(eventRef.get(), event);
     }
 }

Reply via email to