You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@dubbo.apache.org by li...@apache.org on 2020/08/04 07:09:37 UTC

[dubbo] branch 3.0 updated (99c4166 -> 0e4bbb8)

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

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


    from 99c4166  fix address notification issue
     add 47ed754  fix issue 6504, and polish some code (#6505)
     add 418e056  Update README.md
     add 4df7e2f  [Source] Upgrade the version to be 2.7.9-SNAPSHOT (#6526)
     add 043da68  update all version to 2.7.9-SNAPSHOT (#6531)
     new adba6d0  Merge branch 'master' of https://github.com/apache/dubbo
     new 20cf141  service instance subscription
     new b33aef3  metadata read & write
     new c682389  metadata read & write
     new 3810536  redefine MetadataReport
     new 1934655  service discovery demo
     new adffd92  skip when metadata is null
     new c572c2c  Service Discovery Enhancement
     new 8a414bf  metadata report status
     new ca62d83  service discovery
     new 71189cb  can basically work with InstanceAddressURL
     new a001f98  set metadata proxy timeout
     new 52b146f  fix uts
     new 730602c  fix uts
     new e7fcaed  update version to 3.0.0-SNAPSHOT
     new 4cacf33  fix metadata conflicts after master merged
     new 596ba5b  refactor param filter to support both service and instance customization
     new cd44607  unify registry_cluster key
     new 69202d8  customize instance metadata.
     new 540f495  unify registry-cluster key
     new 4b1b563  fix compilation error
     new bf34c16  add demo
     new 0f53367  add side key to MetadataService
     new 9f13240  enhance url notification
     new 5db015c  migrating from interface address pool to instance address pool
     new 7acf2db  fix address notification issue
     new 0e4bbb8  Merge branch '3.0' of https://github.com/apache/dubbo into 3.0

The 27 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 README.md                                          |  2 +-
 .../src/main/java/org/apache/dubbo/common/URL.java | 70 +++++++++++-----------
 .../org/apache/dubbo/config/AbstractConfig.java    |  8 ++-
 .../apache/dubbo/config/MetadataReportConfig.java  | 26 ++++----
 .../consul/ConsulDynamicConfiguration.java         | 24 +++++---
 5 files changed, 76 insertions(+), 54 deletions(-)


[dubbo] 22/27: add demo

Posted by li...@apache.org.
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

commit bf34c16992a13a243fb36892e711921f654a083a
Author: ken.lj <ke...@gmail.com>
AuthorDate: Mon Jul 27 15:35:49 2020 +0800

    add demo
---
 .../apache/dubbo/demo/consumer/Application.java    | 19 ++++++++++++++
 .../src/main/resources/spring/dubbo-consumer.xml   |  3 +++
 .../dubbo/demo/provider/GreetingServiceImpl.java   | 29 ++++++++++++++++++++++
 .../src/main/resources/spring/dubbo-provider.xml   |  7 ++++--
 4 files changed, 56 insertions(+), 2 deletions(-)

diff --git a/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-consumer/src/main/java/org/apache/dubbo/demo/consumer/Application.java b/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-consumer/src/main/java/org/apache/dubbo/demo/consumer/Application.java
index f637eb2..fba18bc 100644
--- a/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-consumer/src/main/java/org/apache/dubbo/demo/consumer/Application.java
+++ b/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-consumer/src/main/java/org/apache/dubbo/demo/consumer/Application.java
@@ -17,6 +17,7 @@
 package org.apache.dubbo.demo.consumer;
 
 import org.apache.dubbo.demo.DemoService;
+import org.apache.dubbo.demo.GreetingService;
 
 import org.springframework.context.support.ClassPathXmlApplicationContext;
 
@@ -31,9 +32,27 @@ public class Application {
         ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring/dubbo-consumer.xml");
         context.start();
         DemoService demoService = context.getBean("demoService", DemoService.class);
+        GreetingService greetingService = context.getBean("greetingService", GreetingService.class);
+
+        new Thread(() -> {
+            while (true) {
+                String greetings = greetingService.hello();
+                System.out.println(greetings + " from separated thread.");
+                try {
+                    Thread.sleep(100);
+                } catch (InterruptedException e) {
+                    e.printStackTrace();
+                }
+            }
+        }).start();
+
         while (true) {
             CompletableFuture<String> hello = demoService.sayHelloAsync("world");
             System.out.println("result: " + hello.get());
+
+            String greetings = greetingService.hello();
+            System.out.println("result: " + greetings);
+
             Thread.sleep(500);
         }
     }
diff --git a/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-consumer/src/main/resources/spring/dubbo-consumer.xml b/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-consumer/src/main/resources/spring/dubbo-consumer.xml
index bec81fe..44e8712 100644
--- a/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-consumer/src/main/resources/spring/dubbo-consumer.xml
+++ b/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-consumer/src/main/resources/spring/dubbo-consumer.xml
@@ -33,4 +33,7 @@
     <dubbo:reference provided-by="demo-provider" id="demoService" check="false"
                      interface="org.apache.dubbo.demo.DemoService"/>
 
+    <dubbo:reference provided-by="demo-provider" version="1.0.0" group="greeting" id="greetingService" check="false"
+                     interface="org.apache.dubbo.demo.GreetingService"/>
+
 </beans>
diff --git a/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-provider/src/main/java/org/apache/dubbo/demo/provider/GreetingServiceImpl.java b/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-provider/src/main/java/org/apache/dubbo/demo/provider/GreetingServiceImpl.java
new file mode 100644
index 0000000..cc1b5de
--- /dev/null
+++ b/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-provider/src/main/java/org/apache/dubbo/demo/provider/GreetingServiceImpl.java
@@ -0,0 +1,29 @@
+/*
+ * 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.demo.provider;
+
+import org.apache.dubbo.demo.GreetingService;
+
+/**
+ *
+ */
+public class GreetingServiceImpl implements GreetingService {
+    @Override
+    public String hello() {
+        return "Greetings!";
+    }
+}
diff --git a/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-provider/src/main/resources/spring/dubbo-provider.xml b/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-provider/src/main/resources/spring/dubbo-provider.xml
index 90a6ae3..856ff73 100644
--- a/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-provider/src/main/resources/spring/dubbo-provider.xml
+++ b/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-provider/src/main/resources/spring/dubbo-provider.xml
@@ -29,10 +29,13 @@
     <dubbo:metadata-report address="zookeeper://127.0.0.1:2181"/>
     <dubbo:registry id="registry1" address="zookeeper://127.0.0.1:2181?registry-type=service"/>
 
-    <dubbo:protocol name="dubbo"/>
+    <dubbo:protocol name="dubbo" port="-1"/>
 
     <bean id="demoService" class="org.apache.dubbo.demo.provider.DemoServiceImpl"/>
+    <bean id="greetingService" class="org.apache.dubbo.demo.provider.GreetingServiceImpl"/>
 
-    <dubbo:service interface="org.apache.dubbo.demo.DemoService" ref="demoService" registry="registry1"/>
+    <dubbo:service interface="org.apache.dubbo.demo.DemoService" timeout="3000" ref="demoService" registry="registry1"/>
+    <dubbo:service version="1.0.0" group="greeting" timeout="5000" interface="org.apache.dubbo.demo.GreetingService"
+                   ref="greetingService"/>
 
 </beans>


[dubbo] 21/27: fix compilation error

Posted by li...@apache.org.
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

commit 4b1b56312afafb4c32cdf1e65fa02af4af5a09d1
Author: ken.lj <ke...@gmail.com>
AuthorDate: Mon Jul 27 15:35:38 2020 +0800

    fix compilation error
---
 .../client/metadata/store/InMemoryWritableMetadataService.java        | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/store/InMemoryWritableMetadataService.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/store/InMemoryWritableMetadataService.java
index f0281da..44c6bbc 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/store/InMemoryWritableMetadataService.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/store/InMemoryWritableMetadataService.java
@@ -125,7 +125,7 @@ public class InMemoryWritableMetadataService implements WritableMetadataService
 
     @Override
     public boolean exportURL(URL url) {
-        String registryCluster = RegistryClusterIdentifier.getExtension().providerKey(url);
+        String registryCluster = RegistryClusterIdentifier.getExtension(url).providerKey(url);
         String[] clusters = registryCluster.split(",");
         for (String cluster : clusters) {
             MetadataInfo metadataInfo = metadataInfos.computeIfAbsent(cluster, k -> {
@@ -139,7 +139,7 @@ public class InMemoryWritableMetadataService implements WritableMetadataService
 
     @Override
     public boolean unexportURL(URL url) {
-        String registryCluster = RegistryClusterIdentifier.getExtension().providerKey(url);
+        String registryCluster = RegistryClusterIdentifier.getExtension(url).providerKey(url);
         String[] clusters = registryCluster.split(",");
         for (String cluster : clusters) {
             MetadataInfo metadataInfo = metadataInfos.get(cluster);


[dubbo] 09/27: metadata report status

Posted by li...@apache.org.
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

commit 8a414bfa925084e1875ebb1b0d180f0cdebfaa64
Author: ken.lj <ke...@gmail.com>
AuthorDate: Tue Jul 7 11:43:17 2020 +0800

    metadata report status
---
 .../org/apache/dubbo/metadata/MetadataInfo.java    | 35 ++++++++++++++++------
 .../metadata/store/RemoteMetadataServiceImpl.java  | 16 +++++-----
 2 files changed, 35 insertions(+), 16 deletions(-)

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 932517d..73e736f 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
@@ -30,6 +30,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.SortedSet;
 import java.util.TreeSet;
+import java.util.concurrent.atomic.AtomicBoolean;
 
 import static org.apache.dubbo.common.constants.CommonConstants.DOT_SEPARATOR;
 import static org.apache.dubbo.common.constants.CommonConstants.GROUP_CHAR_SEPERATOR;
@@ -43,6 +44,7 @@ public class MetadataInfo implements Serializable {
     private Map<String, ServiceInfo> services;
 
     private transient Map<String, String> extendParams;
+    private transient AtomicBoolean reported = new AtomicBoolean(false);
 
     public MetadataInfo(String app) {
         this(app, null, null);
@@ -60,6 +62,7 @@ public class MetadataInfo implements Serializable {
             return;
         }
         this.services.put(serviceInfo.getMatchKey(), serviceInfo);
+        markChanged();
     }
 
     public void removeService(ServiceInfo serviceInfo) {
@@ -67,6 +70,7 @@ public class MetadataInfo implements Serializable {
             return;
         }
         this.services.remove(serviceInfo.getMatchKey());
+        markChanged();
     }
 
     public void removeService(String key) {
@@ -74,18 +78,11 @@ public class MetadataInfo implements Serializable {
             return;
         }
         this.services.remove(key);
-    }
-
-    public String getApp() {
-        return app;
-    }
-
-    public void setApp(String app) {
-        this.app = app;
+        markChanged();
     }
 
     public String getRevision() {
-        if (revision != null) {
+        if (revision != null && hasReported()) {
             return revision;
         }
         StringBuilder sb = new StringBuilder();
@@ -101,6 +98,26 @@ public class MetadataInfo implements Serializable {
         this.revision = revision;
     }
 
+    public boolean hasReported() {
+        return reported.get();
+    }
+
+    public void markReported() {
+        reported.compareAndSet(false, true);
+    }
+
+    public void markChanged() {
+        reported.compareAndSet(true, false);
+    }
+
+    public String getApp() {
+        return app;
+    }
+
+    public void setApp(String app) {
+        this.app = app;
+    }
+
     public Map<String, ServiceInfo> getServices() {
         return services;
     }
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
index 85d6855..ee41050 100644
--- 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
@@ -62,14 +62,16 @@ public class RemoteMetadataServiceImpl {
     public void publishMetadata(ServiceInstance instance) {
         Map<String, MetadataInfo> metadataInfos = localMetadataService.getMetadataInfos();
         metadataInfos.forEach((registryKey, metadataInfo) -> {
-            SubscriberMetadataIdentifier identifier = new SubscriberMetadataIdentifier(instance.getServiceName(), metadataInfo.getRevision());
-            metadataInfo.getRevision();
-            metadataInfo.getExtendParams().put(REGISTRY_KEY, registryKey);
-            MetadataReport metadataReport = getMetadataReports().get(registryKey);
-            if (metadataReport == null) {
-                metadataReport = getMetadataReports().entrySet().iterator().next().getValue();
+            if (!metadataInfo.hasReported()) {
+                SubscriberMetadataIdentifier identifier = new SubscriberMetadataIdentifier(instance.getServiceName(), metadataInfo.getRevision());
+                metadataInfo.getRevision();
+                metadataInfo.getExtendParams().put(REGISTRY_KEY, registryKey);
+                MetadataReport metadataReport = getMetadataReports().get(registryKey);
+                if (metadataReport == null) {
+                    metadataReport = getMetadataReports().entrySet().iterator().next().getValue();
+                }
+                metadataReport.publishAppMetadata(identifier, metadataInfo);
             }
-            metadataReport.publishAppMetadata(identifier, metadataInfo);
         });
     }
 


[dubbo] 24/27: enhance url notification

Posted by li...@apache.org.
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

commit 9f13240835eb71b2ec2355821273921d49ab223b
Author: ken.lj <ke...@gmail.com>
AuthorDate: Mon Jul 27 21:39:09 2020 +0800

    enhance url notification
---
 .../dubbo/registry/client/InstanceAddressURL.java       |  6 ++++++
 .../event/listener/ServiceInstancesChangedListener.java | 17 ++++++++++-------
 .../metadata/ServiceInstanceMetadataCustomizer.java     |  4 +++-
 .../client/metadata/ServiceInstanceMetadataUtils.java   |  3 +++
 4 files changed, 22 insertions(+), 8 deletions(-)

diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/InstanceAddressURL.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/InstanceAddressURL.java
index f4ec3fe..5159cfc 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/InstanceAddressURL.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/InstanceAddressURL.java
@@ -365,6 +365,12 @@ public class InstanceAddressURL extends URL {
 
     @Override
     public String toString() {
+        if (instance == null) {
+            return "{}";
+        }
+        if (metadataInfo == null) {
+            return instance.toString();
+        }
         return instance.toString() + metadataInfo.toString();
     }
 }
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 6ff7c2a..cb43cee 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
@@ -178,15 +178,18 @@ public class ServiceInstancesChangedListener implements ConditionalEventListener
     private void notifyAddressChanged() {
         listeners.forEach((key, notifyListener) -> {
             //FIXME, group wildcard match
-            List<URL> urls = serviceUrls.get(key);
-            if (CollectionUtils.isEmpty(urls)) {
-                urls = new ArrayList<>();
-                urls.add(new InstanceAddressURL());
-            }
-            notifyListener.notify(urls);
+            notifyListener.notify(toUrlsWithEmpty(serviceUrls.get(key)));
         });
     }
 
+    private List<URL> toUrlsWithEmpty(List<URL> urls) {
+        if (CollectionUtils.isEmpty(urls)) {
+            urls = new ArrayList<>();
+            urls.add(new InstanceAddressURL());
+        }
+        return urls;
+    }
+
     public void addListener(String serviceKey, NotifyListener listener) {
         this.listeners.put(serviceKey, listener);
     }
@@ -196,7 +199,7 @@ public class ServiceInstancesChangedListener implements ConditionalEventListener
     }
 
     public List<URL> getUrls(String serviceKey) {
-        return serviceUrls.get(serviceKey);
+        return toUrlsWithEmpty(serviceUrls.get(serviceKey));
     }
 
     /**
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/ServiceInstanceMetadataCustomizer.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/ServiceInstanceMetadataCustomizer.java
index 8d9824a..76fb765 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/ServiceInstanceMetadataCustomizer.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/ServiceInstanceMetadataCustomizer.java
@@ -78,7 +78,9 @@ public class ServiceInstanceMetadataCustomizer implements ServiceInstanceCustomi
                 serviceInstance.getMetadata().putAll(allParams);
             } else {
                 for (String p : included) {
-                    serviceInstance.getMetadata().put(p, allParams.get(p));
+                    if (allParams.get(p) != null) {
+                        serviceInstance.getMetadata().put(p, allParams.get(p));
+                    }
                 }
             }
         });
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 0f0cf3c..0881e0c 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
@@ -241,6 +241,9 @@ public class ServiceInstanceMetadataUtils {
 
     public static void calInstanceRevision(ServiceDiscovery serviceDiscovery, ServiceInstance instance) {
         String registryCluster = serviceDiscovery.getUrl().getParameter(ID_KEY);
+        if (registryCluster == null) {
+            return;
+        }
         MetadataInfo metadataInfo = WritableMetadataService.getDefaultExtension().getMetadataInfos().get(registryCluster);
         if (metadataInfo != null) {
             String existingInstanceRevision = instance.getMetadata().get(EXPORTED_SERVICES_REVISION_PROPERTY_NAME);


[dubbo] 16/27: fix metadata conflicts after master merged

Posted by li...@apache.org.
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

commit 4cacf33f785af73eb673f9f762537e176b518117
Author: ken.lj <ke...@gmail.com>
AuthorDate: Thu Jul 23 10:46:22 2020 +0800

    fix metadata conflicts after master merged
---
 .../dubbo/config/bootstrap/DubboBootstrap.java     | 223 ++++++++++++++---
 .../metadata/AbstractMetadataServiceExporter.java  | 150 ------------
 .../ConfigurableMetadataServiceExporter.java       |  69 +++---
 .../metadata/RemoteMetadataServiceExporter.java    |  79 ------
 .../PublishingServiceDefinitionListenerTest.java   |   2 +-
 .../RemoteMetadataServiceExporterTest.java         | 106 --------
 dubbo-dependencies-bom/pom.xml                     |   8 +-
 .../metadata/CompositeServiceNameMapping.java      |  96 --------
 .../org/apache/dubbo/metadata/MetadataInfo.java    |   4 +-
 .../metadata/ParameterizedServiceNameMapping.java  |  47 ----
 .../metadata/PropertiesFileServiceNameMapping.java | 148 -----------
 .../dubbo/metadata/ReadOnlyServiceNameMapping.java |  45 ----
 .../report/support/AbstractMetadataReport.java     | 133 +++-------
 .../support/ConfigCenterBasedMetadataReport.java   | 162 -------------
 .../ConfigCenterBasedMetadataReportFactory.java    |  90 -------
 .../file/FileSystemMetadataReportFactory.java      |  33 ---
 .../org.apache.dubbo.metadata.MetadataParamsFilter |   1 +
 .../metadata/CompositeServiceNameMappingTest.java  | 107 --------
 .../dubbo/metadata/MetadataConstantsTest.java      |  35 ---
 .../ParameterizedServiceNameMappingTest.java       |  67 -----
 .../PropertiesFileServiceNameMappingTest.java      |  60 -----
 .../dubbo/metadata/ServiceNameMappingTest.java     |   8 +-
 .../support/AbstractMetadataReportFactoryTest.java | 270 ++++++++++-----------
 .../report/support/AbstractMetadataReportTest.java |  58 +++--
 .../ConfigCenterBasedMetadataReportTest.java       | 155 ------------
 .../store/zookeeper/ZookeeperMetadataReport.java   |  29 ---
 .../zookeeper/ZookeeperMetadataReportFactory.java  |  22 +-
 dubbo-metadata/pom.xml                             |  12 +-
 .../registry/client/ServiceDiscoveryRegistry.java  |   4 +-
 .../CompositeMetadataServiceProxyFactory.java      | 133 ----------
 .../proxy/BaseMetadataServiceProxyFactoryTest.java |  78 ------
 .../CompositeMetadataServiceProxyFactoryTest.java  |  96 --------
 .../proxy/MetadataServiceProxyFactoryTest.java     |  49 ----
 .../proxy/MyMetadataServiceProxyFactory.java       |  29 ---
 34 files changed, 471 insertions(+), 2137 deletions(-)

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 a9f2bcf..34977b3 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
@@ -20,6 +20,7 @@ import org.apache.dubbo.common.URL;
 import org.apache.dubbo.common.config.ConfigurationUtils;
 import org.apache.dubbo.common.config.Environment;
 import org.apache.dubbo.common.config.configcenter.DynamicConfiguration;
+import org.apache.dubbo.common.config.configcenter.DynamicConfigurationFactory;
 import org.apache.dubbo.common.config.configcenter.wrapper.CompositeDynamicConfiguration;
 import org.apache.dubbo.common.extension.ExtensionLoader;
 import org.apache.dubbo.common.lang.ShutdownHookCallback;
@@ -28,6 +29,7 @@ import org.apache.dubbo.common.logger.Logger;
 import org.apache.dubbo.common.logger.LoggerFactory;
 import org.apache.dubbo.common.threadpool.concurrent.ScheduledCompletableFuture;
 import org.apache.dubbo.common.threadpool.manager.ExecutorRepository;
+import org.apache.dubbo.common.utils.ArrayUtils;
 import org.apache.dubbo.common.utils.CollectionUtils;
 import org.apache.dubbo.common.utils.StringUtils;
 import org.apache.dubbo.config.ApplicationConfig;
@@ -62,6 +64,7 @@ import org.apache.dubbo.event.GenericEventListener;
 import org.apache.dubbo.metadata.MetadataService;
 import org.apache.dubbo.metadata.MetadataServiceExporter;
 import org.apache.dubbo.metadata.WritableMetadataService;
+import org.apache.dubbo.metadata.report.MetadataReportFactory;
 import org.apache.dubbo.metadata.report.MetadataReportInstance;
 import org.apache.dubbo.registry.client.DefaultServiceInstance;
 import org.apache.dubbo.registry.client.ServiceInstance;
@@ -89,14 +92,19 @@ import java.util.concurrent.locks.Condition;
 import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReentrantLock;
 import java.util.function.Consumer;
+import java.util.function.Supplier;
 
+import static java.lang.String.format;
 import static java.util.Arrays.asList;
 import static java.util.concurrent.Executors.newSingleThreadExecutor;
 import static org.apache.dubbo.common.config.ConfigurationUtils.parseProperties;
 import static org.apache.dubbo.common.config.configcenter.DynamicConfiguration.getDynamicConfiguration;
 import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_METADATA_STORAGE_TYPE;
+import static org.apache.dubbo.common.constants.CommonConstants.REGISTRY_SPLIT_PATTERN;
 import static org.apache.dubbo.common.constants.CommonConstants.REMOTE_METADATA_STORAGE_TYPE;
+import static org.apache.dubbo.common.extension.ExtensionLoader.getExtensionLoader;
 import static org.apache.dubbo.common.function.ThrowableAction.execute;
+import static org.apache.dubbo.common.utils.StringUtils.isEmpty;
 import static org.apache.dubbo.common.utils.StringUtils.isNotEmpty;
 import static org.apache.dubbo.metadata.MetadataConstants.DEFAULT_METADATA_PUBLISH_DELAY;
 import static org.apache.dubbo.metadata.MetadataConstants.METADATA_PUBLISH_DELAY_KEY;
@@ -108,9 +116,9 @@ import static org.apache.dubbo.remoting.Constants.CLIENT_KEY;
 
 /**
  * See {@link ApplicationModel} and {@link ExtensionLoader} for why this class is designed to be singleton.
- *
+ * <p>
  * The bootstrap class of Dubbo
- *
+ * <p>
  * Get singleton instance by calling static method {@link #getInstance()}.
  * Designed as singleton because some classes inside Dubbo, such as ExtensionLoader, are designed only for one instance per process.
  *
@@ -134,7 +142,7 @@ public class DubboBootstrap extends GenericEventListener {
 
     private final Logger logger = LoggerFactory.getLogger(getClass());
 
-    private static DubboBootstrap instance;
+    private static volatile DubboBootstrap instance;
 
     private final AtomicBoolean awaited = new AtomicBoolean(false);
 
@@ -148,7 +156,7 @@ public class DubboBootstrap extends GenericEventListener {
 
     private final EventDispatcher eventDispatcher = EventDispatcher.getDefaultExtension();
 
-    private final ExecutorRepository executorRepository = ExtensionLoader.getExtensionLoader(ExecutorRepository.class).getDefaultExtension();
+    private final ExecutorRepository executorRepository = getExtensionLoader(ExecutorRepository.class).getDefaultExtension();
 
     private final ConfigManager configManager;
 
@@ -183,9 +191,13 @@ public class DubboBootstrap extends GenericEventListener {
     /**
      * See {@link ApplicationModel} and {@link ExtensionLoader} for why DubboBootstrap is designed to be singleton.
      */
-    public static synchronized DubboBootstrap getInstance() {
+    public static DubboBootstrap getInstance() {
         if (instance == null) {
-            instance = new DubboBootstrap();
+            synchronized (DubboBootstrap.class) {
+                if (instance == null) {
+                    instance = new DubboBootstrap();
+                }
+            }
         }
         return instance;
     }
@@ -507,7 +519,7 @@ public class DubboBootstrap extends GenericEventListener {
     /**
      * Initialize
      */
-    private void initialize() {
+    public void initialize() {
         if (!initialized.compareAndSet(false, true)) {
             return;
         }
@@ -516,12 +528,13 @@ public class DubboBootstrap extends GenericEventListener {
 
         startConfigCenter();
 
-        useRegistryAsConfigCenterIfNecessary();
-
         loadRemoteConfigs();
 
         checkGlobalConfigs();
 
+        // @since 2.7.8
+        startMetadataCenter();
+
         initMetadataService();
 
         initEventListener();
@@ -590,6 +603,9 @@ public class DubboBootstrap extends GenericEventListener {
     }
 
     private void startConfigCenter() {
+
+        useRegistryAsConfigCenterIfNecessary();
+
         Collection<ConfigCenterConfig> configCenters = configManager.getConfigCenters();
 
         // check Config Center
@@ -617,7 +633,10 @@ public class DubboBootstrap extends GenericEventListener {
         configManager.refreshAll();
     }
 
-    private void startMetadataReport() {
+    private void startMetadataCenter() {
+
+        useRegistryAsMetadataCenterIfNecessary();
+
         ApplicationConfig applicationConfig = getApplication();
 
         String metadataType = applicationConfig.getMetadataType();
@@ -654,33 +673,159 @@ public class DubboBootstrap extends GenericEventListener {
             return;
         }
 
-        configManager.getDefaultRegistries().stream()
-                .filter(registryConfig -> registryConfig.getUseAsConfigCenter() == null || registryConfig.getUseAsConfigCenter())
-                .forEach(registryConfig -> {
-                    String protocol = registryConfig.getProtocol();
-                    String id = "config-center-" + protocol + "-" + registryConfig.getPort();
-                    ConfigCenterConfig cc = new ConfigCenterConfig();
-                    cc.setId(id);
-                    if (cc.getParameters() == null) {
-                        cc.setParameters(new HashMap<>());
-                    }
-                    if (registryConfig.getParameters() != null) {
-                        cc.getParameters().putAll(registryConfig.getParameters());
-                    }
-                    cc.getParameters().put(CLIENT_KEY, registryConfig.getClient());
-                    cc.setProtocol(registryConfig.getProtocol());
-                    cc.setPort(registryConfig.getPort());
-                    cc.setAddress(registryConfig.getAddress());
-                    cc.setNamespace(registryConfig.getGroup());
-                    cc.setUsername(registryConfig.getUsername());
-                    cc.setPassword(registryConfig.getPassword());
-                    if (registryConfig.getTimeout() != null) {
-                        cc.setTimeout(registryConfig.getTimeout().longValue());
-                    }
-                    cc.setHighestPriority(false);
-                    configManager.addConfigCenter(cc);
-                });
-        startConfigCenter();
+        configManager
+                .getDefaultRegistries()
+                .stream()
+                .filter(this::isUsedRegistryAsConfigCenter)
+                .map(this::registryAsConfigCenter)
+                .forEach(configManager::addConfigCenter);
+    }
+
+    private boolean isUsedRegistryAsConfigCenter(RegistryConfig registryConfig) {
+        return isUsedRegistryAsCenter(registryConfig, registryConfig::getUseAsConfigCenter, "config",
+                DynamicConfigurationFactory.class);
+    }
+
+    private ConfigCenterConfig registryAsConfigCenter(RegistryConfig registryConfig) {
+        String protocol = registryConfig.getProtocol();
+        Integer port = registryConfig.getPort();
+        String id = "config-center-" + protocol + "-" + port;
+        ConfigCenterConfig cc = new ConfigCenterConfig();
+        cc.setId(id);
+        if (cc.getParameters() == null) {
+            cc.setParameters(new HashMap<>());
+        }
+        if (registryConfig.getParameters() != null) {
+            cc.getParameters().putAll(registryConfig.getParameters()); // copy the parameters
+        }
+        cc.getParameters().put(CLIENT_KEY, registryConfig.getClient());
+        cc.setProtocol(protocol);
+        cc.setPort(port);
+        cc.setGroup(registryConfig.getGroup());
+        cc.setAddress(getRegistryCompatibleAddress(registryConfig));
+        cc.setNamespace(registryConfig.getGroup());
+        cc.setUsername(registryConfig.getUsername());
+        cc.setPassword(registryConfig.getPassword());
+        if (registryConfig.getTimeout() != null) {
+            cc.setTimeout(registryConfig.getTimeout().longValue());
+        }
+        cc.setHighestPriority(false);
+        return cc;
+    }
+
+    private void useRegistryAsMetadataCenterIfNecessary() {
+
+        Collection<MetadataReportConfig> metadataConfigs = configManager.getMetadataConfigs();
+
+        if (CollectionUtils.isNotEmpty(metadataConfigs)) {
+            return;
+        }
+
+        configManager
+                .getDefaultRegistries()
+                .stream()
+                .filter(this::isUsedRegistryAsMetadataCenter)
+                .map(this::registryAsMetadataCenter)
+                .forEach(configManager::addMetadataReport);
+
+    }
+
+    private boolean isUsedRegistryAsMetadataCenter(RegistryConfig registryConfig) {
+        return isUsedRegistryAsCenter(registryConfig, registryConfig::getUseAsMetadataCenter, "metadata",
+                MetadataReportFactory.class);
+    }
+
+    /**
+     * Is used the specified registry as a center infrastructure
+     *
+     * @param registryConfig       the {@link RegistryConfig}
+     * @param usedRegistryAsCenter the configured value on
+     * @param centerType           the type name of center
+     * @param extensionClass       an extension class of a center infrastructure
+     * @return
+     * @since 2.7.8
+     */
+    private boolean isUsedRegistryAsCenter(RegistryConfig registryConfig, Supplier<Boolean> usedRegistryAsCenter,
+                                           String centerType,
+                                           Class<?> extensionClass) {
+        final boolean supported;
+
+        Boolean configuredValue = usedRegistryAsCenter.get();
+        if (configuredValue != null) { // If configured, take its value.
+            supported = configuredValue.booleanValue();
+        } else {                       // Or check the extension existence
+            String protocol = registryConfig.getProtocol();
+            supported = supportsExtension(extensionClass, protocol);
+            if (logger.isInfoEnabled()) {
+                logger.info(format("No value is configured in the registry, the %s extension[name : %s] %s as the %s center"
+                        , extensionClass.getSimpleName(), protocol, supported ? "supports" : "does not support", centerType));
+            }
+        }
+
+        if (logger.isInfoEnabled()) {
+            logger.info(format("The registry[%s] will be %s as the %s center", registryConfig,
+                    supported ? "used" : "not used", centerType));
+        }
+        return supported;
+    }
+
+    /**
+     * Supports the extension with the specified class and name
+     *
+     * @param extensionClass the {@link Class} of extension
+     * @param name           the name of extension
+     * @return if supports, return <code>true</code>, or <code>false</code>
+     * @since 2.7.8
+     */
+    private boolean supportsExtension(Class<?> extensionClass, String name) {
+        if (isNotEmpty(name)) {
+            ExtensionLoader extensionLoader = getExtensionLoader(extensionClass);
+            return extensionLoader.hasExtension(name);
+        }
+        return false;
+    }
+
+    private MetadataReportConfig registryAsMetadataCenter(RegistryConfig registryConfig) {
+        String protocol = registryConfig.getProtocol();
+        Integer port = registryConfig.getPort();
+        String id = "metadata-center-" + protocol + "-" + port;
+        MetadataReportConfig metadataReportConfig = new MetadataReportConfig();
+        metadataReportConfig.setId(id);
+        if (metadataReportConfig.getParameters() == null) {
+            metadataReportConfig.setParameters(new HashMap<>());
+        }
+        if (registryConfig.getParameters() != null) {
+            metadataReportConfig.getParameters().putAll(registryConfig.getParameters()); // copy the parameters
+        }
+        metadataReportConfig.getParameters().put(CLIENT_KEY, registryConfig.getClient());
+        metadataReportConfig.setGroup(registryConfig.getGroup());
+        metadataReportConfig.setAddress(getRegistryCompatibleAddress(registryConfig));
+        metadataReportConfig.setUsername(registryConfig.getUsername());
+        metadataReportConfig.setPassword(registryConfig.getPassword());
+        metadataReportConfig.setTimeout(registryConfig.getTimeout());
+        return metadataReportConfig;
+    }
+
+    private String getRegistryCompatibleAddress(RegistryConfig registryConfig) {
+        String registryAddress = registryConfig.getAddress();
+        String[] addresses = REGISTRY_SPLIT_PATTERN.split(registryAddress);
+        if (ArrayUtils.isEmpty(addresses)) {
+            throw new IllegalStateException("Invalid registry address found.");
+        }
+        String address = addresses[0];
+        // since 2.7.8
+        // Issue : https://github.com/apache/dubbo/issues/6476
+        StringBuilder metadataAddressBuilder = new StringBuilder();
+        URL url = URL.valueOf(address);
+        String protocolFromAddress = url.getProtocol();
+        if (isEmpty(protocolFromAddress)) {
+            // If the protocol from address is missing, is like :
+            // "dubbo.registry.address = 127.0.0.1:2181"
+            String protocolFromConfig = registryConfig.getProtocol();
+            metadataAddressBuilder.append(protocolFromConfig).append("://");
+        }
+        metadataAddressBuilder.append(address);
+        return metadataAddressBuilder.toString();
     }
 
     private void loadRemoteConfigs() {
@@ -722,7 +867,7 @@ public class DubboBootstrap extends GenericEventListener {
      * Initialize {@link MetadataService} from {@link WritableMetadataService}'s extension
      */
     private void initMetadataService() {
-        startMetadataReport();
+        startMetadataCenter();
         this.metadataService = getDefaultExtension();
         this.metadataServiceExporter = new ConfigurableMetadataServiceExporter(metadataService);
     }
@@ -1144,7 +1289,7 @@ public class DubboBootstrap extends GenericEventListener {
     }
 
     private void clearConfigs() {
-        configManager.clear();
+        configManager.destroy();
         if (logger.isDebugEnabled()) {
             logger.debug(NAME + "'s configs have been clear.");
         }
diff --git a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/metadata/AbstractMetadataServiceExporter.java b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/metadata/AbstractMetadataServiceExporter.java
deleted file mode 100644
index ce2b389..0000000
--- a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/metadata/AbstractMetadataServiceExporter.java
+++ /dev/null
@@ -1,150 +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.config.metadata;
-
-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 org.apache.dubbo.metadata.MetadataServiceExporter;
-import org.apache.dubbo.metadata.MetadataServiceType;
-import org.apache.dubbo.metadata.WritableMetadataService;
-
-import java.util.List;
-import java.util.Set;
-import java.util.stream.Collectors;
-
-import static java.util.EnumSet.of;
-import static org.apache.dubbo.metadata.MetadataServiceType.getOrDefault;
-
-/**
- * The abstract implementation of {@link MetadataServiceExporter} to provider the commons features for sub-types
- *
- * @see MetadataServiceExporter
- * @see MetadataService
- * @since 2.7.8
- */
-public abstract class AbstractMetadataServiceExporter implements MetadataServiceExporter {
-
-    protected final Logger logger = LoggerFactory.getLogger(getClass());
-
-    protected final WritableMetadataService metadataService;
-
-    private final int priority;
-
-    private final Set<MetadataServiceType> supportedMetadataServiceTypes;
-
-    private volatile boolean exported = false;
-
-    public AbstractMetadataServiceExporter(String metadataType,
-                                           int priority,
-                                           MetadataServiceType supportMetadataServiceType,
-                                           MetadataServiceType... otherSupportMetadataServiceTypes) {
-        this(metadataType, priority, of(supportMetadataServiceType, otherSupportMetadataServiceTypes));
-    }
-
-    public AbstractMetadataServiceExporter(String metadataType,
-                                           int priority,
-                                           Set<MetadataServiceType> supportedMetadataServiceTypes) {
-        this.metadataService = WritableMetadataService.getExtension(metadataType);
-        this.priority = priority;
-        this.supportedMetadataServiceTypes = supportedMetadataServiceTypes;
-    }
-
-    @Override
-    public final MetadataServiceExporter export() {
-        if (!isExported()) {
-            try {
-                doExport();
-                exported = true;
-            } catch (Exception e) {
-                if (logger.isErrorEnabled()) {
-                    logger.error("Exporting the MetadataService fails", e);
-                }
-                exported = false;
-            }
-        } else {
-            if (logger.isWarnEnabled()) {
-                logger.warn("The MetadataService has been exported : " + getExportedURLs());
-            }
-        }
-        return this;
-    }
-
-    @Override
-    public final MetadataServiceExporter unexport() {
-        if (isExported()) {
-            try {
-                doUnexport();
-                exported = false;
-            } catch (Exception e) {
-                if (logger.isErrorEnabled()) {
-                    logger.error("UnExporting the MetadataService fails", e);
-                }
-            }
-        }
-        return this;
-    }
-
-    @Override
-    public List<URL> getExportedURLs() {
-        return metadataService
-                .getExportedURLs()
-                .stream()
-                .map(URL::valueOf)
-                .collect(Collectors.toList());
-    }
-
-    @Override
-    public boolean isExported() {
-        return exported;
-    }
-
-    @Override
-    public final boolean supports(String metadataType) {
-        MetadataServiceType metadataServiceType = getOrDefault(metadataType);
-        return supportedMetadataServiceTypes.contains(metadataServiceType);
-    }
-
-    @Override
-    public int getPriority() {
-        return priority;
-    }
-
-    /**
-     * Exports the {@link MetadataService}
-     *
-     * @throws Exception If some exception occurs
-     */
-    protected abstract void doExport() throws Exception;
-
-    /**
-     * Unexports the {@link MetadataService}
-     *
-     * @throws Exception If some exception occurs
-     */
-    protected abstract void doUnexport() throws Exception;
-
-    /**
-     * Get the underlying of {@link MetadataService}
-     *
-     * @return non-null
-     */
-    public WritableMetadataService getMetadataService() {
-        return metadataService;
-    }
-}
diff --git a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/metadata/ConfigurableMetadataServiceExporter.java b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/metadata/ConfigurableMetadataServiceExporter.java
index d89fb77..fdb011f 100644
--- a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/metadata/ConfigurableMetadataServiceExporter.java
+++ b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/metadata/ConfigurableMetadataServiceExporter.java
@@ -17,6 +17,8 @@
 package org.apache.dubbo.config.metadata;
 
 import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.LoggerFactory;
 import org.apache.dubbo.config.ApplicationConfig;
 import org.apache.dubbo.config.ProtocolConfig;
 import org.apache.dubbo.config.RegistryConfig;
@@ -24,15 +26,12 @@ import org.apache.dubbo.config.ServiceConfig;
 import org.apache.dubbo.config.context.ConfigManager;
 import org.apache.dubbo.metadata.MetadataService;
 import org.apache.dubbo.metadata.MetadataServiceExporter;
-import org.apache.dubbo.metadata.MetadataServiceType;
 import org.apache.dubbo.rpc.model.ApplicationModel;
 
 import java.util.ArrayList;
 import java.util.List;
 
 import static java.util.Collections.emptyList;
-import static java.util.EnumSet.allOf;
-import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_METADATA_STORAGE_TYPE;
 import static org.apache.dubbo.common.constants.CommonConstants.DUBBO;
 
 /**
@@ -50,41 +49,56 @@ import static org.apache.dubbo.common.constants.CommonConstants.DUBBO;
  * @see ConfigManager
  * @since 2.7.5
  */
-public class ConfigurableMetadataServiceExporter extends AbstractMetadataServiceExporter {
+public class ConfigurableMetadataServiceExporter implements MetadataServiceExporter {
+
+    private final Logger logger = LoggerFactory.getLogger(getClass());
+
+    private final MetadataService metadataService;
 
     private volatile ServiceConfig<MetadataService> serviceConfig;
 
-    public ConfigurableMetadataServiceExporter() {
-        super(DEFAULT_METADATA_STORAGE_TYPE, MAX_PRIORITY, allOf(MetadataServiceType.class));
+    public ConfigurableMetadataServiceExporter(MetadataService metadataService) {
+        this.metadataService = metadataService;
     }
 
     @Override
-    protected void doExport() throws Exception {
-
-        ServiceConfig<MetadataService> serviceConfig = new ServiceConfig<>();
-        serviceConfig.setApplication(getApplicationConfig());
-        serviceConfig.setRegistries(getRegistries());
-        serviceConfig.setProtocol(generateMetadataProtocol());
-        serviceConfig.setInterface(MetadataService.class);
-        serviceConfig.setRef(metadataService);
-        serviceConfig.setGroup(getApplicationConfig().getName());
-        serviceConfig.setVersion(metadataService.version());
-
-        // export
-        serviceConfig.export();
-
-        if (logger.isInfoEnabled()) {
-            logger.info("The MetadataService exports urls : " + serviceConfig.getExportedUrls());
+    public ConfigurableMetadataServiceExporter export() {
+
+        if (!isExported()) {
+
+            ServiceConfig<MetadataService> serviceConfig = new ServiceConfig<>();
+            serviceConfig.setApplication(getApplicationConfig());
+            serviceConfig.setRegistries(getRegistries());
+            serviceConfig.setProtocol(generateMetadataProtocol());
+            serviceConfig.setInterface(MetadataService.class);
+            serviceConfig.setRef(metadataService);
+            serviceConfig.setGroup(getApplicationConfig().getName());
+            serviceConfig.setVersion(metadataService.version());
+
+            // export
+            serviceConfig.export();
+
+            if (logger.isInfoEnabled()) {
+                logger.info("The MetadataService exports urls : " + serviceConfig.getExportedUrls());
+            }
+
+            this.serviceConfig = serviceConfig;
+
+        } else {
+            if (logger.isWarnEnabled()) {
+                logger.warn("The MetadataService has been exported : " + serviceConfig.getExportedUrls());
+            }
         }
 
-        this.serviceConfig = serviceConfig;
+        return this;
     }
 
     @Override
-    protected void doUnexport() throws Exception {
-        if (serviceConfig != null) {
+    public ConfigurableMetadataServiceExporter unexport() {
+        if (isExported()) {
             serviceConfig.unexport();
         }
+        return this;
     }
 
     @Override
@@ -96,11 +110,6 @@ public class ConfigurableMetadataServiceExporter extends AbstractMetadataService
         return serviceConfig != null && serviceConfig.isExported();
     }
 
-    @Override
-    public int getPriority() {
-        return MAX_PRIORITY;
-    }
-
     private ApplicationConfig getApplicationConfig() {
         return ApplicationModel.getConfigManager().getApplication().get();
     }
diff --git a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/metadata/RemoteMetadataServiceExporter.java b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/metadata/RemoteMetadataServiceExporter.java
deleted file mode 100644
index 6d48921..0000000
--- a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/metadata/RemoteMetadataServiceExporter.java
+++ /dev/null
@@ -1,79 +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.config.metadata;
-
-import org.apache.dubbo.common.constants.CommonConstants;
-import org.apache.dubbo.metadata.MetadataServiceExporter;
-import org.apache.dubbo.metadata.MetadataServiceType;
-import org.apache.dubbo.metadata.URLRevisionResolver;
-import org.apache.dubbo.metadata.WritableMetadataService;
-import org.apache.dubbo.metadata.report.MetadataReport;
-import org.apache.dubbo.metadata.report.MetadataReportInstance;
-import org.apache.dubbo.metadata.report.identifier.SubscriberMetadataIdentifier;
-
-import java.util.SortedSet;
-
-import static org.apache.dubbo.common.constants.CommonConstants.REMOTE_METADATA_STORAGE_TYPE;
-
-/**
- * The implementation of {@link MetadataServiceExporter} for
- * {@link CommonConstants#REMOTE_METADATA_STORAGE_TYPE "remote" metadata storage type}
- *
- * @see MetadataServiceExporter
- * @since 2.7.8
- */
-public class RemoteMetadataServiceExporter extends AbstractMetadataServiceExporter {
-
-    private final URLRevisionResolver urlRevisionResolver;
-
-    public RemoteMetadataServiceExporter() {
-        super(REMOTE_METADATA_STORAGE_TYPE, MIN_PRIORITY, MetadataServiceType.REMOTE, MetadataServiceType.COMPOSITE);
-        this.urlRevisionResolver = URLRevisionResolver.INSTANCE;
-    }
-
-    @Override
-    protected void doExport() throws Exception {
-        WritableMetadataService metadataServiceDelegate = WritableMetadataService.getDefaultExtension();
-        if (publishServiceMetadata(metadataServiceDelegate)) {
-            publicConsumerMetadata(metadataServiceDelegate);
-        }
-    }
-
-    private boolean publishServiceMetadata(WritableMetadataService metadataServiceDelegate) {
-        String serviceName = metadataServiceDelegate.serviceName();
-        SortedSet<String> exportedURLs = metadataServiceDelegate.getExportedURLs();
-        String revision = urlRevisionResolver.resolve(exportedURLs);
-        return getMetadataReport().saveExportedURLs(serviceName, revision, exportedURLs);
-    }
-
-    private boolean publicConsumerMetadata(WritableMetadataService metadataServiceDelegate) {
-        String serviceName = metadataServiceDelegate.serviceName();
-        SortedSet<String> subscribedURLs = metadataServiceDelegate.getSubscribedURLs();
-        String revision = urlRevisionResolver.resolve(subscribedURLs);
-        getMetadataReport().saveSubscribedData(new SubscriberMetadataIdentifier(serviceName, revision), subscribedURLs);
-        return true;
-    }
-
-    private MetadataReport getMetadataReport() {
-        return MetadataReportInstance.getMetadataReport(true);
-    }
-
-    @Override
-    protected void doUnexport() throws Exception {
-        // DOES NOTHING
-    }
-}
diff --git a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/event/listener/PublishingServiceDefinitionListenerTest.java b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/event/listener/PublishingServiceDefinitionListenerTest.java
index 67a562d..e7e5376 100644
--- a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/event/listener/PublishingServiceDefinitionListenerTest.java
+++ b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/event/listener/PublishingServiceDefinitionListenerTest.java
@@ -59,7 +59,7 @@ public class PublishingServiceDefinitionListenerTest {
         ApplicationConfig applicationConfig = new ApplicationConfig("dubbo-demo-provider");
         applicationConfig.setMetadataType(metadataType);
         configManager.setApplication(applicationConfig);
-        this.writableMetadataService = WritableMetadataService.getExtension(metadataType);
+        this.writableMetadataService = WritableMetadataService.getDefaultExtension();
     }
 
     @AfterEach
diff --git a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/metadata/RemoteMetadataServiceExporterTest.java b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/metadata/RemoteMetadataServiceExporterTest.java
deleted file mode 100644
index e4d13a1..0000000
--- a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/metadata/RemoteMetadataServiceExporterTest.java
+++ /dev/null
@@ -1,106 +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.config.metadata;
-
-import org.apache.dubbo.common.URL;
-import org.apache.dubbo.config.ApplicationConfig;
-import org.apache.dubbo.metadata.MetadataServiceExporter;
-import org.apache.dubbo.metadata.WritableMetadataService;
-import org.apache.dubbo.metadata.report.MetadataReportInstance;
-import org.apache.dubbo.rpc.model.ApplicationModel;
-import org.apache.dubbo.rpc.service.EchoService;
-
-import org.junit.jupiter.api.AfterEach;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-
-import static java.util.Arrays.asList;
-import static org.apache.dubbo.common.constants.CommonConstants.APPLICATION_KEY;
-import static org.apache.dubbo.common.constants.CommonConstants.COMPOSITE_METADATA_STORAGE_TYPE;
-import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_METADATA_STORAGE_TYPE;
-import static org.apache.dubbo.common.constants.CommonConstants.REMOTE_METADATA_STORAGE_TYPE;
-import static org.apache.dubbo.common.constants.CommonConstants.SIDE_KEY;
-import static org.apache.dubbo.metadata.MetadataServiceExporter.getExtension;
-import static org.apache.dubbo.metadata.report.support.Constants.SYNC_REPORT_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 RemoteMetadataServiceExporter} Test-Cases
- *
- * @since 2.7.8
- */
-public class RemoteMetadataServiceExporterTest {
-
-    private static final URL METADATA_REPORT_URL = URL.valueOf("file://")
-            .addParameter(APPLICATION_KEY, "test")
-            .addParameter(SYNC_REPORT_KEY, "true");
-
-    private static final Class<EchoService> INTERFACE_CLASS = EchoService.class;
-
-    private static final String INTERFACE_NAME = INTERFACE_CLASS.getName();
-
-    private static final String APP_NAME = "test-service";
-
-    private static final URL BASE_URL = URL
-            .valueOf("dubbo://127.0.0.1:20880")
-            .setPath(INTERFACE_NAME)
-            .addParameter(APPLICATION_KEY, APP_NAME)
-            .addParameter(SIDE_KEY, "provider");
-
-    private final MetadataServiceExporter exporter = getExtension(REMOTE_METADATA_STORAGE_TYPE);
-
-    private WritableMetadataService writableMetadataService;
-
-    @BeforeEach
-    public void init() {
-        ApplicationModel.getConfigManager().setApplication(new ApplicationConfig(APP_NAME));
-        MetadataReportInstance.init(METADATA_REPORT_URL);
-        writableMetadataService = WritableMetadataService.getDefaultExtension();
-        writableMetadataService.exportURL(BASE_URL);
-    }
-
-    @AfterEach
-    public void reset() {
-        ApplicationModel.reset();
-    }
-
-    @Test
-    public void testType() {
-        assertEquals(RemoteMetadataServiceExporter.class, exporter.getClass());
-    }
-
-    @Test
-    public void testSupports() {
-        assertTrue(exporter.supports(REMOTE_METADATA_STORAGE_TYPE));
-        assertTrue(exporter.supports(COMPOSITE_METADATA_STORAGE_TYPE));
-        assertFalse(exporter.supports(DEFAULT_METADATA_STORAGE_TYPE));
-    }
-
-    @Test
-    public void testExportAndUnexport() {
-        assertFalse(exporter.isExported());
-        assertEquals(exporter, exporter.export());
-        assertTrue(exporter.isExported());
-
-        assertEquals(asList(BASE_URL), exporter.getExportedURLs());
-
-        assertEquals(exporter, exporter.unexport());
-        assertFalse(exporter.isExported());
-    }
-}
diff --git a/dubbo-dependencies-bom/pom.xml b/dubbo-dependencies-bom/pom.xml
index 92f2a90..9c40c96 100644
--- a/dubbo-dependencies-bom/pom.xml
+++ b/dubbo-dependencies-bom/pom.xml
@@ -97,7 +97,7 @@
         <grizzly_version>2.1.4</grizzly_version>
         <httpclient_version>4.5.3</httpclient_version>
         <httpcore_version>4.4.6</httpcore_version>
-        <fastjson_version>1.2.68</fastjson_version>
+        <fastjson_version>1.2.70</fastjson_version>
         <zookeeper_version>3.4.13</zookeeper_version>
         <curator_version>4.0.1</curator_version>
         <curator_test_version>2.12.0</curator_test_version>
@@ -130,7 +130,7 @@
         <resteasy_version>3.0.19.Final</resteasy_version>
         <tomcat_embed_version>8.5.31</tomcat_embed_version>
         <jetcd_version>0.4.1</jetcd_version>
-        <nacos_version>1.1.1</nacos_version>
+        <nacos_version>1.3.1</nacos_version>
         <grpc.version>1.22.1</grpc.version>
         <!-- Log libs -->
         <slf4j_version>1.7.25</slf4j_version>
@@ -146,13 +146,13 @@
         <eureka.version>1.9.12</eureka.version>
 
         <!-- Alibaba -->
-        <alibaba_spring_context_support_version>1.0.6</alibaba_spring_context_support_version>
+        <alibaba_spring_context_support_version>1.0.8</alibaba_spring_context_support_version>
 
         <jaxb_version>2.2.7</jaxb_version>
         <activation_version>1.2.0</activation_version>
         <test_container_version>1.11.2</test_container_version>
         <etcd_launcher_version>0.3.0</etcd_launcher_version>
-        <hessian_lite_version>3.2.7</hessian_lite_version>
+        <hessian_lite_version>3.2.8</hessian_lite_version>
         <swagger_version>1.5.19</swagger_version>
         <spring_test_version>4.3.16.RELEASE</spring_test_version>
 
diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/CompositeServiceNameMapping.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/CompositeServiceNameMapping.java
deleted file mode 100644
index 9ad130b..0000000
--- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/CompositeServiceNameMapping.java
+++ /dev/null
@@ -1,96 +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 java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Set;
-
-import static java.util.Collections.emptySet;
-import static java.util.Collections.unmodifiableSet;
-import static org.apache.dubbo.common.extension.ExtensionLoader.getExtensionLoader;
-import static org.apache.dubbo.common.utils.CollectionUtils.isNotEmpty;
-
-/**
- * The composite implementation of {@link ServiceNameMapping}
- *
- * @see ParameterizedServiceNameMapping
- * @see PropertiesFileServiceNameMapping
- * @see DynamicConfigurationServiceNameMapping
- * @since 2.7.8
- */
-public class CompositeServiceNameMapping implements ServiceNameMapping {
-
-    private volatile List<ServiceNameMapping> serviceNameMappings;
-
-    private List<ServiceNameMapping> getServiceNameMappings() {
-        if (this.serviceNameMappings == null) {
-            synchronized (this) {
-                if (this.serviceNameMappings == null) {
-                    Set<ServiceNameMapping> serviceNameMappings = loadAllServiceNameMappings();
-
-                    removeSelf(serviceNameMappings);
-
-                    this.serviceNameMappings = new LinkedList<>(serviceNameMappings);
-                }
-            }
-        }
-        return this.serviceNameMappings;
-    }
-
-    private Set<ServiceNameMapping> loadAllServiceNameMappings() {
-        return getExtensionLoader(ServiceNameMapping.class).getSupportedExtensionInstances();
-    }
-
-    private void removeSelf(Set<ServiceNameMapping> serviceNameMappings) {
-        Iterator<ServiceNameMapping> iterator = serviceNameMappings.iterator();
-        while (iterator.hasNext()) {
-            ServiceNameMapping serviceNameMapping = iterator.next();
-            if (this.getClass().equals(serviceNameMapping.getClass())) {
-                iterator.remove(); // Remove self
-            }
-        }
-    }
-
-    @Override
-    public void map(URL exportedURL) {
-        List<ServiceNameMapping> serviceNameMappings = getServiceNameMappings();
-        serviceNameMappings.forEach(serviceNameMapping -> serviceNameMapping.map(exportedURL));
-    }
-
-    @Override
-    public Set<String> get(URL subscribedURL) {
-        List<ServiceNameMapping> serviceNameMappings = getServiceNameMappings();
-        Set<String> serviceNames = null;
-        for (ServiceNameMapping serviceNameMapping : serviceNameMappings) {
-            serviceNames = serviceNameMapping.get(subscribedURL);
-            if (isNotEmpty(serviceNames)) {
-                break;
-            }
-        }
-        return serviceNames == null ? emptySet() : unmodifiableSet(serviceNames);
-    }
-
-    @Override
-    public int getPriority() {
-        return MIN_PRIORITY;
-    }
-}
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 e3f6881..f479830 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
@@ -36,7 +36,7 @@ import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.atomic.AtomicBoolean;
 
 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_CHAR_SEPARATOR;
 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;
@@ -236,7 +236,7 @@ public class MetadataInfo implements Serializable {
         private String buildMatchKey() {
             matchKey = getServiceKey();
             if (StringUtils.isNotEmpty(protocol)) {
-                matchKey = getServiceKey() + GROUP_CHAR_SEPERATOR + protocol;
+                matchKey = getServiceKey() + GROUP_CHAR_SEPARATOR + protocol;
             }
             return matchKey;
         }
diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/ParameterizedServiceNameMapping.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/ParameterizedServiceNameMapping.java
deleted file mode 100644
index 893a6f4..0000000
--- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/ParameterizedServiceNameMapping.java
+++ /dev/null
@@ -1,47 +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 java.util.Set;
-
-import static org.apache.dubbo.common.constants.RegistryConstants.SUBSCRIBED_SERVICE_NAMES_KEY;
-
-/**
- * The parameterized implementation of {@link ServiceNameMapping}
- *
- * @see ReadOnlyServiceNameMapping
- * @since 2.7.8
- */
-public class ParameterizedServiceNameMapping extends ReadOnlyServiceNameMapping {
-
-    /**
-     * The priority of {@link PropertiesFileServiceNameMapping}
-     */
-    static final int PRIORITY = MAX_PRIORITY + 99;
-
-    @Override
-    public Set<String> get(URL subscribedURL) {
-        return getValue(subscribedURL.getParameter(SUBSCRIBED_SERVICE_NAMES_KEY));
-    }
-
-    @Override
-    public int getPriority() {
-        return PRIORITY;
-    }
-}
diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/PropertiesFileServiceNameMapping.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/PropertiesFileServiceNameMapping.java
deleted file mode 100644
index 7870895..0000000
--- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/PropertiesFileServiceNameMapping.java
+++ /dev/null
@@ -1,148 +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.common.config.Configuration;
-import org.apache.dubbo.common.constants.CommonConstants;
-import org.apache.dubbo.common.utils.ClassUtils;
-import org.apache.dubbo.common.utils.PathUtils;
-import org.apache.dubbo.common.utils.StringUtils;
-import org.apache.dubbo.rpc.model.ApplicationModel;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.util.Enumeration;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Properties;
-import java.util.Set;
-
-import static java.lang.String.format;
-import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_SERVICE_NAME_MAPPING_PROPERTIES_PATH;
-import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY;
-import static org.apache.dubbo.common.constants.CommonConstants.SERVICE_NAME_MAPPING_PROPERTIES_FILE_KEY;
-import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY;
-import static org.apache.dubbo.common.utils.StringUtils.SLASH;
-import static org.apache.dubbo.metadata.MetadataConstants.KEY_SEPARATOR;
-
-/**
- * The externalized {@link Properties} file implementation of {@link ServiceNameMapping},
- * the default properties class path is
- * {@link CommonConstants#DEFAULT_SERVICE_NAME_MAPPING_PROPERTIES_PATH "/META-INF/dubbo/service-name-mapping.properties"},
- * whose format as following:
- * <pre>
- * dubbo\:com.acme.Interface1\:default = Service1
- * thirft\:com.acme.InterfaceX = Service1,Service2
- * rest\:com.acme.interfaceN = Service3
- * </pre>
- * <p>
- * THe search path could be configured by the externalized property {@link CommonConstants#SERVICE_NAME_MAPPING_PROPERTIES_FILE_KEY}
- *
- * @see ReadOnlyServiceNameMapping
- * @see ParameterizedServiceNameMapping
- * @since 2.7.8
- */
-public class PropertiesFileServiceNameMapping extends ReadOnlyServiceNameMapping {
-
-    /**
-     * The priority of {@link PropertiesFileServiceNameMapping} is
-     * lower than {@link ParameterizedServiceNameMapping}
-     */
-    static final int PRIORITY = ParameterizedServiceNameMapping.PRIORITY + 1;
-
-
-    private final List<Properties> propertiesList;
-
-    public PropertiesFileServiceNameMapping() {
-        this.propertiesList = loadPropertiesList();
-    }
-
-    @Override
-    public Set<String> get(URL subscribedURL) {
-        String propertyKey = getPropertyKey(subscribedURL);
-        String propertyValue = null;
-
-        for (Properties properties : propertiesList) {
-            propertyValue = properties.getProperty(propertyKey);
-            if (propertyValue != null) {
-                break;
-            }
-        }
-
-        return getValue(propertyValue);
-    }
-
-    private String getPropertyKey(URL url) {
-        String protocol = url.getProtocol();
-        String serviceInterface = url.getServiceInterface();
-        // Optional
-        String group = url.getParameter(GROUP_KEY);
-        String version = url.getParameter(VERSION_KEY);
-
-        StringBuilder propertyKeyBuilder = new StringBuilder(protocol)
-                .append(KEY_SEPARATOR)
-                .append(serviceInterface);
-
-        appendIfPresent(propertyKeyBuilder, group);
-        appendIfPresent(propertyKeyBuilder, version);
-
-        return propertyKeyBuilder.toString();
-    }
-
-    private void appendIfPresent(StringBuilder builder, String value) {
-        if (!StringUtils.isBlank(value)) {
-            builder.append(KEY_SEPARATOR).append(value);
-        }
-    }
-
-    private List<Properties> loadPropertiesList() {
-        List<Properties> propertiesList = new LinkedList<>();
-        String propertiesPath = getPropertiesPath();
-        try {
-            Enumeration<java.net.URL> resources = ClassUtils.getClassLoader().getResources(propertiesPath);
-            while (resources.hasMoreElements()) {
-                java.net.URL resource = resources.nextElement();
-                InputStream inputStream = resource.openStream();
-                Properties properties = new Properties();
-                properties.load(new InputStreamReader(inputStream, "UTF-8"));
-                propertiesList.add(properties);
-            }
-        } catch (IOException e) {
-            if (logger.isErrorEnabled()) {
-                logger.error(format("The path of ServiceNameMapping's Properties file[path : %s] can't be loaded", propertiesPath), e);
-            }
-        }
-        return propertiesList;
-    }
-
-    private String getPropertiesPath() {
-        Configuration configuration = ApplicationModel.getEnvironment().getConfiguration();
-        String propertyPath = configuration.getString(SERVICE_NAME_MAPPING_PROPERTIES_FILE_KEY, DEFAULT_SERVICE_NAME_MAPPING_PROPERTIES_PATH);
-        propertyPath = PathUtils.normalize(propertyPath);
-        if (propertyPath.startsWith(SLASH)) {
-            propertyPath = propertyPath.substring(SLASH.length());
-        }
-        return propertyPath;
-    }
-
-    @Override
-    public int getPriority() {
-        return PRIORITY;
-    }
-}
diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/ReadOnlyServiceNameMapping.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/ReadOnlyServiceNameMapping.java
deleted file mode 100644
index 58035d3..0000000
--- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/ReadOnlyServiceNameMapping.java
+++ /dev/null
@@ -1,45 +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.common.logger.Logger;
-import org.apache.dubbo.common.logger.LoggerFactory;
-
-import java.util.Set;
-
-import static org.apache.dubbo.common.constants.CommonConstants.COMMA_SEPARATOR_CHAR;
-import static org.apache.dubbo.common.utils.StringUtils.splitToSet;
-
-/**
- * Read-Only implementation of {@link ServiceNameMapping}
- *
- * @since 2.7.8
- */
-public abstract class ReadOnlyServiceNameMapping implements ServiceNameMapping {
-
-    protected final Logger logger = LoggerFactory.getLogger(getClass());
-
-    @Override
-    public void map(URL exportedURL) {
-        // DO NOTING for mapping
-    }
-
-    protected Set<String> getValue(String rawValue) {
-        return splitToSet(rawValue, COMMA_SEPARATOR_CHAR, true);
-    }
-}
diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/report/support/AbstractMetadataReport.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/report/support/AbstractMetadataReport.java
index f3b90db..1d39401 100644
--- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/report/support/AbstractMetadataReport.java
+++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/report/support/AbstractMetadataReport.java
@@ -44,7 +44,6 @@ import java.nio.channels.FileChannel;
 import java.nio.channels.FileLock;
 import java.util.ArrayList;
 import java.util.Calendar;
-import java.util.Collection;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
@@ -53,6 +52,7 @@ import java.util.Set;
 import java.util.SortedSet;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
 import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.ScheduledFuture;
 import java.util.concurrent.ThreadLocalRandom;
@@ -61,15 +61,11 @@ import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.atomic.AtomicLong;
 
-import static java.util.concurrent.Executors.newScheduledThreadPool;
-import static java.util.concurrent.Executors.newSingleThreadExecutor;
-import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
 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.FILE_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.utils.StringUtils.replace;
 import static org.apache.dubbo.metadata.report.support.Constants.CYCLE_REPORT_KEY;
 import static org.apache.dubbo.metadata.report.support.Constants.DEFAULT_METADATA_REPORT_CYCLE_REPORT;
 import static org.apache.dubbo.metadata.report.support.Constants.DEFAULT_METADATA_REPORT_RETRY_PERIOD;
@@ -90,52 +86,24 @@ public abstract class AbstractMetadataReport implements MetadataReport {
     // Log output
     protected final Logger logger = LoggerFactory.getLogger(getClass());
 
-    private final AtomicBoolean initialized = new AtomicBoolean(false);
-
+    // Local disk cache, where the special key value.registries records the list of metadata centers, and the others are the list of notified service providers
+    final Properties properties = new Properties();
+    private final ExecutorService reportCacheExecutor = Executors.newFixedThreadPool(1, new NamedThreadFactory("DubboSaveMetadataReport", true));
     final Map<MetadataIdentifier, Object> allMetadataReports = new ConcurrentHashMap<>(4);
 
+    private final AtomicLong lastCacheChanged = new AtomicLong();
     final Map<MetadataIdentifier, Object> failedReports = new ConcurrentHashMap<>(4);
-
     private URL reportURL;
     boolean syncReport;
-
     // Local disk cache file
-    File localCacheFile;
-    // Local disk cache, where the special key value.registries records the list of metadata centers, and the others are the list of notified service providers
-    final Properties properties = new Properties();
-
-    private final AtomicLong lastCacheChanged = new AtomicLong();
-
-    // ThreadPoolExecutors
-    private final ExecutorService reportCacheExecutor;
-
-    public final MetadataReportRetry metadataReportRetry;
-
-    private final ScheduledExecutorService cycleReportExecutor;
+    File file;
+    private AtomicBoolean initialized = new AtomicBoolean(false);
+    public MetadataReportRetry metadataReportRetry;
 
     public AbstractMetadataReport(URL reportServerURL) {
         setUrl(reportServerURL);
-
-        this.localCacheFile = initializeLocalCacheFile(reportServerURL);
-        loadProperties();
-        syncReport = reportServerURL.getParameter(SYNC_REPORT_KEY, false);
-        metadataReportRetry = new MetadataReportRetry(reportServerURL.getParameter(RETRY_TIMES_KEY, DEFAULT_METADATA_REPORT_RETRY_TIMES),
-                reportServerURL.getParameter(RETRY_PERIOD_KEY, DEFAULT_METADATA_REPORT_RETRY_PERIOD));
-        this.reportCacheExecutor = newSingleThreadExecutor(new NamedThreadFactory("DubboSaveMetadataReport", true));
-        this.cycleReportExecutor = newSingleThreadScheduledExecutor(new NamedThreadFactory("DubboMetadataReportTimer", true));
-        // cycle report the data switch
-        if (reportServerURL.getParameter(CYCLE_REPORT_KEY, DEFAULT_METADATA_REPORT_CYCLE_REPORT)) {
-            cycleReportExecutor.scheduleAtFixedRate(this::publishAll, calculateStartTime(), ONE_DAY_IN_MILLISECONDS, TimeUnit.MILLISECONDS);
-        }
-    }
-
-    private File initializeLocalCacheFile(URL reportServerURL) {
         // Start file save timer
-        String defaultFilename = System.getProperty("user.home") +
-                "/.dubbo/dubbo-metadata-" +
-                reportServerURL.getParameter(APPLICATION_KEY) + "-" +
-                replace(reportServerURL.getAddress(), ":", "-") +
-                ".cache";
+        String defaultFilename = System.getProperty("user.home") + "/.dubbo/dubbo-metadata-" + reportServerURL.getParameter(APPLICATION_KEY) + "-" + reportServerURL.getAddress().replaceAll(":", "-") + ".cache";
         String filename = reportServerURL.getParameter(FILE_KEY, defaultFilename);
         File file = null;
         if (ConfigUtils.isNotEmpty(filename)) {
@@ -150,7 +118,16 @@ public abstract class AbstractMetadataReport implements MetadataReport {
                 file.delete();
             }
         }
-        return file;
+        this.file = file;
+        loadProperties();
+        syncReport = reportServerURL.getParameter(SYNC_REPORT_KEY, false);
+        metadataReportRetry = new MetadataReportRetry(reportServerURL.getParameter(RETRY_TIMES_KEY, DEFAULT_METADATA_REPORT_RETRY_TIMES),
+                reportServerURL.getParameter(RETRY_PERIOD_KEY, DEFAULT_METADATA_REPORT_RETRY_PERIOD));
+        // cycle report the data switch
+        if (reportServerURL.getParameter(CYCLE_REPORT_KEY, DEFAULT_METADATA_REPORT_CYCLE_REPORT)) {
+            ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(new NamedThreadFactory("DubboMetadataReportTimer", true));
+            scheduler.scheduleAtFixedRate(this::publishAll, calculateStartTime(), ONE_DAY_IN_MILLISECONDS, TimeUnit.MILLISECONDS);
+        }
     }
 
     public URL getUrl() {
@@ -168,12 +145,12 @@ public abstract class AbstractMetadataReport implements MetadataReport {
         if (version < lastCacheChanged.get()) {
             return;
         }
-        if (localCacheFile == null) {
+        if (file == null) {
             return;
         }
         // Save
         try {
-            File lockfile = new File(localCacheFile.getAbsolutePath() + ".lock");
+            File lockfile = new File(file.getAbsolutePath() + ".lock");
             if (!lockfile.exists()) {
                 lockfile.createNewFile();
             }
@@ -181,14 +158,14 @@ public abstract class AbstractMetadataReport implements MetadataReport {
                  FileChannel channel = raf.getChannel()) {
                 FileLock lock = channel.tryLock();
                 if (lock == null) {
-                    throw new IOException("Can not lock the metadataReport cache file " + localCacheFile.getAbsolutePath() + ", ignore and retry later, maybe multi java process use the file, please config: dubbo.metadata.file=xxx.properties");
+                    throw new IOException("Can not lock the metadataReport cache file " + file.getAbsolutePath() + ", ignore and retry later, maybe multi java process use the file, please config: dubbo.metadata.file=xxx.properties");
                 }
                 // Save
                 try {
-                    if (!localCacheFile.exists()) {
-                        localCacheFile.createNewFile();
+                    if (!file.exists()) {
+                        file.createNewFile();
                     }
-                    try (FileOutputStream outputFile = new FileOutputStream(localCacheFile)) {
+                    try (FileOutputStream outputFile = new FileOutputStream(file)) {
                         properties.store(outputFile, "Dubbo metadataReport Cache");
                     }
                 } finally {
@@ -206,20 +183,20 @@ public abstract class AbstractMetadataReport implements MetadataReport {
     }
 
     void loadProperties() {
-        if (localCacheFile != null && localCacheFile.exists()) {
-            try (InputStream in = new FileInputStream(localCacheFile)) {
+        if (file != null && file.exists()) {
+            try (InputStream in = new FileInputStream(file)) {
                 properties.load(in);
                 if (logger.isInfoEnabled()) {
-                    logger.info("Load service store file " + localCacheFile + ", data: " + properties);
+                    logger.info("Load service store file " + file + ", data: " + properties);
                 }
             } catch (Throwable e) {
-                logger.warn("Failed to load service store file " + localCacheFile, e);
+                logger.warn("Failed to load service store file " + file, e);
             }
         }
     }
 
     private void saveProperties(MetadataIdentifier metadataIdentifier, String value, boolean add, boolean sync) {
-        if (localCacheFile == null) {
+        if (file == null) {
             return;
         }
 
@@ -341,7 +318,7 @@ public abstract class AbstractMetadataReport implements MetadataReport {
     }
 
     @Override
-    public void saveSubscribedData(SubscriberMetadataIdentifier subscriberMetadataIdentifier, Collection<String> urls) {
+    public void saveSubscribedData(SubscriberMetadataIdentifier subscriberMetadataIdentifier, Set<String> urls) {
         if (syncReport) {
             doSaveSubscriberData(subscriberMetadataIdentifier, new Gson().toJson(urls));
         } else {
@@ -351,7 +328,7 @@ public abstract class AbstractMetadataReport implements MetadataReport {
 
 
     @Override
-    public Set<String> getSubscribedURLs(SubscriberMetadataIdentifier subscriberMetadataIdentifier) {
+    public List<String> getSubscribedURLs(SubscriberMetadataIdentifier subscriberMetadataIdentifier) {
         String content = doGetSubscribedURLs(subscriberMetadataIdentifier);
         Type setType = new TypeToken<SortedSet<String>>() {
         }.getType();
@@ -415,7 +392,7 @@ public abstract class AbstractMetadataReport implements MetadataReport {
     class MetadataReportRetry {
         protected final Logger logger = LoggerFactory.getLogger(getClass());
 
-        final ScheduledExecutorService retryExecutor = newScheduledThreadPool(0, new NamedThreadFactory("DubboMetadataReportRetryTimer", true));
+        final ScheduledExecutorService retryExecutor = Executors.newScheduledThreadPool(0, new NamedThreadFactory("DubboMetadataReportRetryTimer", true));
         volatile ScheduledFuture retryScheduledFuture;
         final AtomicInteger retryCounter = new AtomicInteger(0);
         // retry task schedule period
@@ -458,10 +435,8 @@ public abstract class AbstractMetadataReport implements MetadataReport {
         }
 
         void cancelRetryTask() {
-            if (retryScheduledFuture != null) {
-                retryScheduledFuture.cancel(false);
-            }
-            shutdown(retryExecutor);
+            retryScheduledFuture.cancel(false);
+            retryExecutor.shutdown();
         }
     }
 
@@ -476,13 +451,6 @@ public abstract class AbstractMetadataReport implements MetadataReport {
         doSaveSubscriberData(subscriberMetadataIdentifier, encodedUrlList);
     }
 
-    @Override
-    public final void close() throws Exception {
-        this.shutdownThreadPoolExecutors();
-        this.clearCache();
-        doClose();
-    }
-
     protected abstract void doStoreProviderMetadata(MetadataIdentifier providerMetadataIdentifier, String serviceDefinitions);
 
     protected abstract void doStoreConsumerMetadata(MetadataIdentifier consumerMetadataIdentifier, String serviceParameterString);
@@ -497,35 +465,4 @@ public abstract class AbstractMetadataReport implements MetadataReport {
 
     protected abstract String doGetSubscribedURLs(SubscriberMetadataIdentifier subscriberMetadataIdentifier);
 
-    /**
-     * Close other resources
-     *
-     * @since 2.7.8
-     */
-    protected void doClose() throws Exception {
-
-    }
-
-    private void clearCache() {
-        this.properties.clear();
-        this.allMetadataReports.clear();
-        this.failedReports.clear();
-        this.localCacheFile.delete();
-    }
-
-    private void shutdownThreadPoolExecutors() {
-        this.metadataReportRetry.cancelRetryTask();
-        shutdown(this.reportCacheExecutor);
-        shutdown(cycleReportExecutor);
-    }
-
-    private static void shutdown(ExecutorService executorService) {
-        if (executorService == null) {
-            return;
-        }
-        if (!executorService.isShutdown()) {
-            executorService.shutdown();
-        }
-    }
-
 }
diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/report/support/ConfigCenterBasedMetadataReport.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/report/support/ConfigCenterBasedMetadataReport.java
deleted file mode 100644
index d962ca5..0000000
--- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/report/support/ConfigCenterBasedMetadataReport.java
+++ /dev/null
@@ -1,162 +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.report.support;
-
-import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.config.configcenter.DynamicConfiguration;
-import org.apache.dubbo.common.config.configcenter.DynamicConfigurationFactory;
-import org.apache.dubbo.metadata.report.MetadataReport;
-import org.apache.dubbo.metadata.report.identifier.BaseMetadataIdentifier;
-import org.apache.dubbo.metadata.report.identifier.KeyTypeEnum;
-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 java.util.List;
-
-import static org.apache.dubbo.common.config.configcenter.DynamicConfigurationFactory.getDynamicConfigurationFactory;
-import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY;
-import static org.apache.dubbo.metadata.MetadataConstants.EXPORTED_URLS_TAG;
-
-/**
- * The generic implementation of {@link MetadataReport} based on {@link DynamicConfiguration
- * the config-center infrastructure}
- *
- * @see AbstractMetadataReport
- * @since 2.7.8
- */
-public class ConfigCenterBasedMetadataReport extends AbstractMetadataReport {
-
-    private final KeyTypeEnum keyType;
-
-    private final String group;
-
-    private final DynamicConfiguration dynamicConfiguration;
-
-    public ConfigCenterBasedMetadataReport(URL reportServerURL, KeyTypeEnum keyTypeEnum) {
-        super(reportServerURL);
-        this.keyType = keyTypeEnum;
-        this.group = reportServerURL.getParameter(GROUP_KEY, DEFAULT_ROOT);
-        String extensionName = reportServerURL.getProtocol();
-        DynamicConfigurationFactory dynamicConfigurationFactory = getDynamicConfigurationFactory(extensionName);
-        dynamicConfiguration = dynamicConfigurationFactory.getDynamicConfiguration(reportServerURL);
-    }
-
-
-    @Override
-    protected void doStoreProviderMetadata(MetadataIdentifier providerMetadataIdentifier, String serviceDefinitions) {
-        saveMetadata(providerMetadataIdentifier, serviceDefinitions);
-    }
-
-    @Override
-    protected void doStoreConsumerMetadata(MetadataIdentifier consumerMetadataIdentifier, String serviceParameterString) {
-        saveMetadata(consumerMetadataIdentifier, serviceParameterString);
-    }
-
-    @Override
-    protected void doSaveMetadata(ServiceMetadataIdentifier metadataIdentifier, URL url) {
-        saveMetadata(metadataIdentifier, URL.encode(url.toFullString()));
-    }
-
-    @Override
-    protected void doRemoveMetadata(ServiceMetadataIdentifier metadataIdentifier) {
-        removeMetadata(metadataIdentifier);
-    }
-
-    @Override
-    protected List<String> doGetExportedURLs(ServiceMetadataIdentifier metadataIdentifier) {
-        throw new UnsupportedOperationException("doGetExportedURLs method will not be supported!");
-    }
-
-    @Override
-    protected void doSaveSubscriberData(SubscriberMetadataIdentifier subscriberMetadataIdentifier, String urlListStr) {
-        saveMetadata(subscriberMetadataIdentifier, urlListStr);
-    }
-
-    @Override
-    protected String doGetSubscribedURLs(SubscriberMetadataIdentifier subscriberMetadataIdentifier) {
-        return getMetadata(subscriberMetadataIdentifier);
-    }
-
-    @Override
-    public String getServiceDefinition(MetadataIdentifier metadataIdentifier) {
-        return getMetadata(metadataIdentifier);
-    }
-
-    @Override
-    public boolean saveExportedURLs(String serviceName, String exportedServicesRevision, String exportedURLsContent) {
-        String key = buildExportedURLsMetadataKey(serviceName, exportedServicesRevision);
-        return dynamicConfiguration.publishConfig(key, group, exportedURLsContent);
-    }
-
-    @Override
-    public String getExportedURLsContent(String serviceName, String exportedServicesRevision) {
-        String key = buildExportedURLsMetadataKey(serviceName, exportedServicesRevision);
-        return dynamicConfiguration.getConfig(key, group);
-    }
-
-    private String buildExportedURLsMetadataKey(String serviceName, String exportedServicesRevision) {
-        return keyType.build(EXPORTED_URLS_TAG, serviceName, exportedServicesRevision);
-    }
-
-    protected void saveMetadata(BaseMetadataIdentifier metadataIdentifier, String value) {
-        String key = getKey(metadataIdentifier);
-        dynamicConfiguration.publishConfig(key, group, value);
-    }
-
-    protected void saveMetadata(MetadataIdentifier metadataIdentifier, String value) {
-        String key = getKey(metadataIdentifier);
-        dynamicConfiguration.publishConfig(key, group, value);
-    }
-
-    protected String getMetadata(ServiceMetadataIdentifier metadataIdentifier) {
-        String key = getKey(metadataIdentifier);
-        return dynamicConfiguration.getConfig(key, group);
-    }
-
-    protected String getMetadata(MetadataIdentifier metadataIdentifier) {
-        String key = getKey(metadataIdentifier);
-        return dynamicConfiguration.getConfig(key, group);
-    }
-
-    protected String getMetadata(SubscriberMetadataIdentifier metadataIdentifier) {
-        String key = getKey(metadataIdentifier);
-        return dynamicConfiguration.getConfig(key, group);
-    }
-
-    protected void removeMetadata(MetadataIdentifier metadataIdentifier) {
-        String key = getKey(metadataIdentifier);
-        dynamicConfiguration.removeConfig(key, group);
-    }
-
-    protected void removeMetadata(ServiceMetadataIdentifier metadataIdentifier) {
-        String key = getKey(metadataIdentifier);
-        dynamicConfiguration.removeConfig(key, group);
-    }
-
-    protected String getKey(BaseMetadataIdentifier metadataIdentifier) {
-        return metadataIdentifier.getUniqueKey(keyType);
-    }
-
-    protected String getKey(MetadataIdentifier metadataIdentifier) {
-        return metadataIdentifier.getUniqueKey(keyType);
-    }
-
-    protected void doClose() throws Exception {
-        this.dynamicConfiguration.close();
-    }
-}
diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/report/support/ConfigCenterBasedMetadataReportFactory.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/report/support/ConfigCenterBasedMetadataReportFactory.java
deleted file mode 100644
index b445feb..0000000
--- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/report/support/ConfigCenterBasedMetadataReportFactory.java
+++ /dev/null
@@ -1,90 +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.report.support;
-
-import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.config.configcenter.DynamicConfiguration;
-import org.apache.dubbo.metadata.report.MetadataReport;
-import org.apache.dubbo.metadata.report.MetadataReportFactory;
-import org.apache.dubbo.metadata.report.identifier.KeyTypeEnum;
-
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-
-import static org.apache.dubbo.common.config.configcenter.TreePathDynamicConfiguration.CONFIG_ROOT_PATH_PARAM_NAME;
-import static org.apache.dubbo.common.utils.StringUtils.SLASH;
-import static org.apache.dubbo.common.utils.StringUtils.isBlank;
-import static org.apache.dubbo.metadata.report.identifier.KeyTypeEnum.PATH;
-import static org.apache.dubbo.rpc.cluster.Constants.EXPORT_KEY;
-import static org.apache.dubbo.rpc.cluster.Constants.REFER_KEY;
-
-/**
- * The abstract implementation of {@link MetadataReportFactory} based on
- * {@link DynamicConfiguration the config-center infrastructure}
- *
- * @see MetadataReportFactory
- * @see MetadataReport
- * @see DynamicConfiguration
- * @since 2.7.8
- */
-public abstract class ConfigCenterBasedMetadataReportFactory implements MetadataReportFactory {
-
-    /**
-     * org.apache.dubbo.metadata.report.MetadataReport
-     */
-    private static final String URL_PATH = MetadataReport.class.getName();
-
-    // Registry Collection Map<metadataAddress, MetadataReport>
-    private static final Map<String, ConfigCenterBasedMetadataReport> metadataReportCache = new ConcurrentHashMap();
-
-    private final KeyTypeEnum keyType;
-
-    public ConfigCenterBasedMetadataReportFactory(KeyTypeEnum keyType) {
-        if (keyType == null) {
-            throw new NullPointerException("The keyType argument must not be null!");
-        }
-        this.keyType = keyType;
-    }
-
-    @Override
-    public ConfigCenterBasedMetadataReport getMetadataReport(URL url) {
-        url = url.setPath(URL_PATH);
-        final URL actualURL = resolveURLParameters(url);
-        String key = actualURL.toServiceString();
-        // Lock the metadata access process to ensure a single instance of the metadata instance
-        return metadataReportCache.computeIfAbsent(key, k -> new ConfigCenterBasedMetadataReport(actualURL, keyType));
-    }
-
-    private URL resolveURLParameters(URL url) {
-        URL resolvedURL = url.removeParameters(EXPORT_KEY, REFER_KEY);
-        if (PATH.equals(getKeyType())) { // Only handles for "PATH" type
-            if (isBlank(resolvedURL.getParameter(CONFIG_ROOT_PATH_PARAM_NAME))) {
-                resolvedURL = resolvedURL.addParameter(CONFIG_ROOT_PATH_PARAM_NAME, SLASH);
-            }
-        }
-        return resolvedURL;
-    }
-
-    /**
-     * Get {@link KeyTypeEnum the key type}
-     *
-     * @return non-null
-     */
-    protected KeyTypeEnum getKeyType() {
-        return keyType;
-    }
-}
diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/report/support/file/FileSystemMetadataReportFactory.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/report/support/file/FileSystemMetadataReportFactory.java
deleted file mode 100644
index c4cc487..0000000
--- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/report/support/file/FileSystemMetadataReportFactory.java
+++ /dev/null
@@ -1,33 +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.report.support.file;
-
-import org.apache.dubbo.metadata.report.identifier.KeyTypeEnum;
-import org.apache.dubbo.metadata.report.support.ConfigCenterBasedMetadataReportFactory;
-
-/**
- * The implementation of {@link ConfigCenterBasedMetadataReportFactory} based on File System
- *
- * @see ConfigCenterBasedMetadataReportFactory
- * @since 2.7.8
- */
-public class FileSystemMetadataReportFactory extends ConfigCenterBasedMetadataReportFactory {
-
-    public FileSystemMetadataReportFactory() {
-        super(KeyTypeEnum.PATH);
-    }
-}
diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metadata.MetadataParamsFilter b/dubbo-metadata/dubbo-metadata-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metadata.MetadataParamsFilter
new file mode 100644
index 0000000..6754126
--- /dev/null
+++ b/dubbo-metadata/dubbo-metadata-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metadata.MetadataParamsFilter
@@ -0,0 +1 @@
+default=org.apache.dubbo.metadata.DefaultMetadataParamsFilter
\ No newline at end of file
diff --git a/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/CompositeServiceNameMappingTest.java b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/CompositeServiceNameMappingTest.java
deleted file mode 100644
index 48243f8..0000000
--- a/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/CompositeServiceNameMappingTest.java
+++ /dev/null
@@ -1,107 +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.common.config.configcenter.file.FileSystemDynamicConfiguration;
-import org.apache.dubbo.config.ApplicationConfig;
-import org.apache.dubbo.rpc.model.ApplicationModel;
-import org.apache.dubbo.rpc.service.EchoService;
-
-import org.apache.commons.io.FileUtils;
-import org.junit.jupiter.api.AfterEach;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-
-import java.util.Set;
-
-import static java.util.Collections.singleton;
-import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY;
-import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY;
-import static org.apache.dubbo.common.constants.RegistryConstants.SUBSCRIBED_SERVICE_NAMES_KEY;
-import static org.apache.dubbo.common.utils.CollectionUtils.ofSet;
-import static org.apache.dubbo.metadata.DynamicConfigurationServiceNameMapping.buildGroup;
-import static org.apache.dubbo.rpc.model.ApplicationModel.getApplicationConfig;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertNotNull;
-
-/**
- * {@link CompositeServiceNameMapping} Test
- *
- * @since 2.7.8
- */
-public class CompositeServiceNameMappingTest {
-
-    private static final URL BASE_URL = URL.valueOf("dubbo://127.0.0.1:20880")
-            .setPath(EchoService.class.getName())
-            .addParameter(GROUP_KEY, "default")
-            .addParameter(VERSION_KEY, "1.0.0");
-
-    private static final String APP_NAME = "test-service";
-
-    private ServiceNameMapping serviceNameMapping;
-
-    private FileSystemDynamicConfiguration dynamicConfiguration;
-
-    @BeforeEach
-    public void init() {
-        serviceNameMapping = ServiceNameMapping.getDefaultExtension();
-        dynamicConfiguration = new FileSystemDynamicConfiguration();
-        ApplicationModel.getConfigManager().setApplication(new ApplicationConfig(APP_NAME));
-        ApplicationModel.getEnvironment().setDynamicConfiguration(dynamicConfiguration);
-    }
-
-    @AfterEach
-    public void reset() {
-        FileUtils.deleteQuietly(dynamicConfiguration.getRootDirectory());
-        ApplicationModel.reset();
-    }
-
-    @Test
-    public void testType() {
-        assertEquals(CompositeServiceNameMapping.class, serviceNameMapping.getClass());
-    }
-
-    @Test
-    public void testMap() {
-        serviceNameMapping.map(BASE_URL);
-        assertNotNull(dynamicConfiguration.getConfig(APP_NAME,
-                buildGroup(BASE_URL.getServiceInterface(), null, null, null)));
-    }
-
-    @Test
-    public void testGet() {
-        serviceNameMapping.map(BASE_URL);
-        Set<String> serviceNames = serviceNameMapping.get(BASE_URL);
-        assertEquals(singleton(APP_NAME), serviceNames);
-
-        getApplicationConfig().setName("service1");
-        serviceNameMapping.map(BASE_URL);
-        serviceNames = serviceNameMapping.get(BASE_URL);
-        assertEquals(ofSet(APP_NAME, "service1"), serviceNames);
-
-        serviceNames = serviceNameMapping.get(BASE_URL
-                .setPath("com.acme.Interface1")
-                .removeParameter(VERSION_KEY)
-        );
-        assertEquals(singleton("Service1"), serviceNames);
-
-        serviceNames = serviceNameMapping.get(BASE_URL.addParameter(SUBSCRIBED_SERVICE_NAMES_KEY, "s1 , s2 , s3 "));
-        assertEquals(ofSet("s1", "s2", "s3"), serviceNames);
-    }
-}
-
diff --git a/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/MetadataConstantsTest.java b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/MetadataConstantsTest.java
deleted file mode 100644
index d0c201b..0000000
--- a/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/MetadataConstantsTest.java
+++ /dev/null
@@ -1,35 +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.junit.jupiter.api.Test;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-
-/**
- * {@link MetadataConstants} Test-Cases
- *
- * @since 2.7.8
- */
-public class MetadataConstantsTest {
-
-    @Test
-    public void testConstants() {
-        assertEquals("exported-urls", MetadataConstants.EXPORTED_URLS_TAG);
-        assertEquals("subscribed-urls", MetadataConstants.SUBSCRIBED_URLS_TAG);
-    }
-}
diff --git a/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/ParameterizedServiceNameMappingTest.java b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/ParameterizedServiceNameMappingTest.java
deleted file mode 100644
index dbe5454..0000000
--- a/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/ParameterizedServiceNameMappingTest.java
+++ /dev/null
@@ -1,67 +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.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-
-import java.util.Arrays;
-import java.util.LinkedHashSet;
-import java.util.Set;
-
-import static java.util.Collections.emptySet;
-import static java.util.Collections.singleton;
-import static org.apache.dubbo.common.constants.RegistryConstants.SUBSCRIBED_SERVICE_NAMES_KEY;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-
-/**
- * {@link ParameterizedServiceNameMapping} Test
- *
- * @see ParameterizedServiceNameMapping
- * @since 2.7.8
- */
-public class ParameterizedServiceNameMappingTest {
-
-    private static final URL BASE_URL = URL.valueOf("dubbo://127.0.0.1:20880");
-
-    private ParameterizedServiceNameMapping serviceNameMapping;
-
-    @BeforeEach
-    public void init() {
-        serviceNameMapping = new ParameterizedServiceNameMapping();
-    }
-
-    @Test
-    public void testMap() {
-        // NOTHING to happen
-        serviceNameMapping.map(BASE_URL);
-    }
-
-    @Test
-    public void testGet() {
-        Set<String> serviceNames = serviceNameMapping.get(BASE_URL);
-        assertEquals(emptySet(), serviceNames);
-
-        serviceNames = serviceNameMapping.get(BASE_URL.addParameter(SUBSCRIBED_SERVICE_NAMES_KEY, "    Service1     "));
-        assertEquals(singleton("Service1"), serviceNames);
-
-        serviceNames = serviceNameMapping.get(BASE_URL.addParameter(SUBSCRIBED_SERVICE_NAMES_KEY, "Service1 ,  Service2   "));
-        assertEquals(new LinkedHashSet(Arrays.asList("Service1", "Service2")), serviceNames);
-    }
-}
diff --git a/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/PropertiesFileServiceNameMappingTest.java b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/PropertiesFileServiceNameMappingTest.java
deleted file mode 100644
index 68a6ebf..0000000
--- a/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/PropertiesFileServiceNameMappingTest.java
+++ /dev/null
@@ -1,60 +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.junit.jupiter.api.Test;
-
-import java.util.Arrays;
-import java.util.LinkedHashSet;
-
-import static java.util.Collections.singleton;
-import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY;
-import static org.apache.dubbo.common.constants.CommonConstants.SERVICE_NAME_MAPPING_PROPERTIES_FILE_KEY;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-
-/**
- * {@link PropertiesFileServiceNameMapping} Test
- *
- * @since 2.7.8
- */
-public class PropertiesFileServiceNameMappingTest {
-
-    private static final URL BASE_URL = URL.valueOf("dubbo://127.0.0.1:20880");
-
-
-    @Test
-    public void testMap() {
-        PropertiesFileServiceNameMapping serviceNameMapping = new PropertiesFileServiceNameMapping();
-        serviceNameMapping.map(BASE_URL);
-    }
-
-    @Test
-    public void testGet() {
-
-        PropertiesFileServiceNameMapping serviceNameMapping = new PropertiesFileServiceNameMapping();
-        URL url = BASE_URL.setServiceInterface("com.acme.Interface1").addParameter(GROUP_KEY, "default");
-        assertEquals(singleton("Service1"), serviceNameMapping.get(url));
-
-        System.setProperty(SERVICE_NAME_MAPPING_PROPERTIES_FILE_KEY, "///META-INF//dubbo/service-name-mapping.properties");
-        serviceNameMapping = new PropertiesFileServiceNameMapping();
-
-        url = BASE_URL.setProtocol("thirft").setServiceInterface("com.acme.InterfaceX");
-        assertEquals(new LinkedHashSet<>(Arrays.asList("Service1", "Service2")), serviceNameMapping.get(url));
-    }
-}
diff --git a/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/ServiceNameMappingTest.java b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/ServiceNameMappingTest.java
index 8c37998..e4b4c1e 100644
--- a/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/ServiceNameMappingTest.java
+++ b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/ServiceNameMappingTest.java
@@ -13,7 +13,8 @@
  * 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;
@@ -39,11 +40,13 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.junit.jupiter.api.Assertions.assertThrows;
 
+*/
 /**
  * {@link ServiceNameMapping} Test
  *
  * @since 2.7.8
- */
+ *//*
+
 public class ServiceNameMappingTest {
 
     private static final URL BASE_URL = URL.valueOf("dubbo://127.0.0.1:20880");
@@ -119,3 +122,4 @@ public class ServiceNameMappingTest {
 
     }
 }
+*/
diff --git a/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/report/support/AbstractMetadataReportFactoryTest.java b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/report/support/AbstractMetadataReportFactoryTest.java
index 3aa5c02..5b0ffd7 100644
--- a/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/report/support/AbstractMetadataReportFactoryTest.java
+++ b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/report/support/AbstractMetadataReportFactoryTest.java
@@ -1,135 +1,135 @@
-/*
- * 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.report.support;
-
-import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.utils.NetUtils;
-import org.apache.dubbo.metadata.definition.model.ServiceDefinition;
-import org.apache.dubbo.metadata.report.MetadataReport;
-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 com.alibaba.fastjson.JSON;
-import org.junit.jupiter.api.Assertions;
-import org.junit.jupiter.api.Test;
-
-import java.util.Collection;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-
-/**
- * 2018/9/14
- */
-public class AbstractMetadataReportFactoryTest {
-
-    private AbstractMetadataReportFactory metadataReportFactory = new AbstractMetadataReportFactory() {
-        @Override
-        protected MetadataReport createMetadataReport(URL url) {
-            return new MetadataReport() {
-
-                @Override
-                public void storeProviderMetadata(MetadataIdentifier providerMetadataIdentifier, ServiceDefinition serviceDefinition) {
-                    store.put(providerMetadataIdentifier.getIdentifierKey(), JSON.toJSONString(serviceDefinition));
-                }
-
-                @Override
-                public void saveServiceMetadata(ServiceMetadataIdentifier metadataIdentifier, URL url) {
-
-                }
-
-                @Override
-                public void removeServiceMetadata(ServiceMetadataIdentifier metadataIdentifier) {
-
-                }
-
-                @Override
-                public List<String> getExportedURLs(ServiceMetadataIdentifier metadataIdentifier) {
-                    return null;
-                }
-
-                @Override
-                public void saveSubscribedData(SubscriberMetadataIdentifier subscriberMetadataIdentifier,
-                                               Collection<String> urls) {
-
-                }
-
-                @Override
-                public List<String> getSubscribedURLs(SubscriberMetadataIdentifier subscriberMetadataIdentifier) {
-                    return null;
-                }
-
-                @Override
-                public String getServiceDefinition(MetadataIdentifier consumerMetadataIdentifier) {
-                    return null;
-                }
-
-                @Override
-                public void storeConsumerMetadata(MetadataIdentifier consumerMetadataIdentifier, Map serviceParameterMap) {
-                    store.put(consumerMetadataIdentifier.getIdentifierKey(), JSON.toJSONString(serviceParameterMap));
-                }
-
-                @Override
-                public void close() throws Exception {
-
-                }
-
-                Map<String, String> store = new ConcurrentHashMap<>();
-
-
-            };
-        }
-    };
-
-    @Test
-    public void testGetOneMetadataReport() {
-        URL url = URL.valueOf("zookeeper://" + NetUtils.getLocalAddress().getHostName() + ":4444/org.apache.dubbo.TestService?version=1.0.0&application=vic");
-        MetadataReport metadataReport1 = metadataReportFactory.getMetadataReport(url);
-        MetadataReport metadataReport2 = metadataReportFactory.getMetadataReport(url);
-        Assertions.assertEquals(metadataReport1, metadataReport2);
-    }
-
-    @Test
-    public void testGetOneMetadataReportForIpFormat() {
-        String hostName = NetUtils.getLocalAddress().getHostName();
-        String ip = NetUtils.getIpByHost(hostName);
-        URL url1 = URL.valueOf("zookeeper://" + hostName + ":4444/org.apache.dubbo.TestService?version=1.0.0&application=vic");
-        URL url2 = URL.valueOf("zookeeper://" + ip + ":4444/org.apache.dubbo.TestService?version=1.0.0&application=vic");
-        MetadataReport metadataReport1 = metadataReportFactory.getMetadataReport(url1);
-        MetadataReport metadataReport2 = metadataReportFactory.getMetadataReport(url2);
-        Assertions.assertEquals(metadataReport1, metadataReport2);
-    }
-
-    @Test
-    public void testGetForDiffService() {
-        URL url1 = URL.valueOf("zookeeper://" + NetUtils.getLocalAddress().getHostName() + ":4444/org.apache.dubbo.TestService1?version=1.0.0&application=vic");
-        URL url2 = URL.valueOf("zookeeper://" + NetUtils.getLocalAddress().getHostName() + ":4444/org.apache.dubbo.TestService2?version=1.0.0&application=vic");
-        MetadataReport metadataReport1 = metadataReportFactory.getMetadataReport(url1);
-        MetadataReport metadataReport2 = metadataReportFactory.getMetadataReport(url2);
-        Assertions.assertEquals(metadataReport1, metadataReport2);
-    }
-
-    @Test
-    public void testGetForDiffGroup() {
-        URL url1 = URL.valueOf("zookeeper://" + NetUtils.getLocalAddress().getHostName() + ":4444/org.apache.dubbo.TestService?version=1.0.0&application=vic&group=aaa");
-        URL url2 = URL.valueOf("zookeeper://" + NetUtils.getLocalAddress().getHostName() + ":4444/org.apache.dubbo.TestService?version=1.0.0&application=vic&group=bbb");
-        MetadataReport metadataReport1 = metadataReportFactory.getMetadataReport(url1);
-        MetadataReport metadataReport2 = metadataReportFactory.getMetadataReport(url2);
-        Assertions.assertNotEquals(metadataReport1, metadataReport2);
-    }
-}
+///*
+// * 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.report.support;
+//
+//import org.apache.dubbo.common.URL;
+//import org.apache.dubbo.common.utils.NetUtils;
+//import org.apache.dubbo.metadata.definition.model.ServiceDefinition;
+//import org.apache.dubbo.metadata.report.MetadataReport;
+//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 com.alibaba.fastjson.JSON;
+//import org.junit.jupiter.api.Assertions;
+//import org.junit.jupiter.api.Test;
+//
+//import java.util.Collection;
+//import java.util.List;
+//import java.util.Map;
+//import java.util.concurrent.ConcurrentHashMap;
+//
+///**
+// * 2018/9/14
+// */
+//public class AbstractMetadataReportFactoryTest {
+//
+//    private AbstractMetadataReportFactory metadataReportFactory = new AbstractMetadataReportFactory() {
+//        @Override
+//        protected MetadataReport createMetadataReport(URL url) {
+//            return new MetadataReport() {
+//
+//                @Override
+//                public void storeProviderMetadata(MetadataIdentifier providerMetadataIdentifier, ServiceDefinition serviceDefinition) {
+//                    store.put(providerMetadataIdentifier.getIdentifierKey(), JSON.toJSONString(serviceDefinition));
+//                }
+//
+//                @Override
+//                public void saveServiceMetadata(ServiceMetadataIdentifier metadataIdentifier, URL url) {
+//
+//                }
+//
+//                @Override
+//                public void removeServiceMetadata(ServiceMetadataIdentifier metadataIdentifier) {
+//
+//                }
+//
+//                @Override
+//                public List<String> getExportedURLs(ServiceMetadataIdentifier metadataIdentifier) {
+//                    return null;
+//                }
+//
+//                @Override
+//                public void saveSubscribedData(SubscriberMetadataIdentifier subscriberMetadataIdentifier,
+//                                               Collection<String> urls) {
+//
+//                }
+//
+//                @Override
+//                public List<String> getSubscribedURLs(SubscriberMetadataIdentifier subscriberMetadataIdentifier) {
+//                    return null;
+//                }
+//
+//                @Override
+//                public String getServiceDefinition(MetadataIdentifier consumerMetadataIdentifier) {
+//                    return null;
+//                }
+//
+//                @Override
+//                public void storeConsumerMetadata(MetadataIdentifier consumerMetadataIdentifier, Map serviceParameterMap) {
+//                    store.put(consumerMetadataIdentifier.getIdentifierKey(), JSON.toJSONString(serviceParameterMap));
+//                }
+//
+//                @Override
+//                public void close() throws Exception {
+//
+//                }
+//
+//                Map<String, String> store = new ConcurrentHashMap<>();
+//
+//
+//            };
+//        }
+//    };
+//
+//    @Test
+//    public void testGetOneMetadataReport() {
+//        URL url = URL.valueOf("zookeeper://" + NetUtils.getLocalAddress().getHostName() + ":4444/org.apache.dubbo.TestService?version=1.0.0&application=vic");
+//        MetadataReport metadataReport1 = metadataReportFactory.getMetadataReport(url);
+//        MetadataReport metadataReport2 = metadataReportFactory.getMetadataReport(url);
+//        Assertions.assertEquals(metadataReport1, metadataReport2);
+//    }
+//
+//    @Test
+//    public void testGetOneMetadataReportForIpFormat() {
+//        String hostName = NetUtils.getLocalAddress().getHostName();
+//        String ip = NetUtils.getIpByHost(hostName);
+//        URL url1 = URL.valueOf("zookeeper://" + hostName + ":4444/org.apache.dubbo.TestService?version=1.0.0&application=vic");
+//        URL url2 = URL.valueOf("zookeeper://" + ip + ":4444/org.apache.dubbo.TestService?version=1.0.0&application=vic");
+//        MetadataReport metadataReport1 = metadataReportFactory.getMetadataReport(url1);
+//        MetadataReport metadataReport2 = metadataReportFactory.getMetadataReport(url2);
+//        Assertions.assertEquals(metadataReport1, metadataReport2);
+//    }
+//
+//    @Test
+//    public void testGetForDiffService() {
+//        URL url1 = URL.valueOf("zookeeper://" + NetUtils.getLocalAddress().getHostName() + ":4444/org.apache.dubbo.TestService1?version=1.0.0&application=vic");
+//        URL url2 = URL.valueOf("zookeeper://" + NetUtils.getLocalAddress().getHostName() + ":4444/org.apache.dubbo.TestService2?version=1.0.0&application=vic");
+//        MetadataReport metadataReport1 = metadataReportFactory.getMetadataReport(url1);
+//        MetadataReport metadataReport2 = metadataReportFactory.getMetadataReport(url2);
+//        Assertions.assertEquals(metadataReport1, metadataReport2);
+//    }
+//
+//    @Test
+//    public void testGetForDiffGroup() {
+//        URL url1 = URL.valueOf("zookeeper://" + NetUtils.getLocalAddress().getHostName() + ":4444/org.apache.dubbo.TestService?version=1.0.0&application=vic&group=aaa");
+//        URL url2 = URL.valueOf("zookeeper://" + NetUtils.getLocalAddress().getHostName() + ":4444/org.apache.dubbo.TestService?version=1.0.0&application=vic&group=bbb");
+//        MetadataReport metadataReport1 = metadataReportFactory.getMetadataReport(url1);
+//        MetadataReport metadataReport2 = metadataReportFactory.getMetadataReport(url2);
+//        Assertions.assertNotEquals(metadataReport1, metadataReport2);
+//    }
+//}
diff --git a/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/report/support/AbstractMetadataReportTest.java b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/report/support/AbstractMetadataReportTest.java
index c5325a1..37b0d3c 100644
--- a/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/report/support/AbstractMetadataReportTest.java
+++ b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/report/support/AbstractMetadataReportTest.java
@@ -13,7 +13,8 @@
  * 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.report.support;
 
 import org.apache.dubbo.common.URL;
@@ -49,9 +50,19 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNull;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
+*/
 /**
+ * Test {@link MetadataReport#saveExportedURLs(String, String, String)} method
  *
- */
+ * @since 2.7.8
+ * <p>
+ * Test {@link MetadataReport#getExportedURLs(String, String)} method
+ * @since 2.7.8
+ * <p>
+ * Test {@link MetadataReport#getExportedURLsContent(String, String)} method
+ * @since 2.7.8
+ *//*
+
 public class AbstractMetadataReportTest {
 
     private NewMetadataReport abstractMetadataReport;
@@ -266,11 +277,19 @@ public class AbstractMetadataReportTest {
         }
     }
 
-    /**
-     * Test {@link MetadataReport#saveExportedURLs(String, String, String)} method
-     *
-     * @since 2.7.8
-     */
+    */
+/**
+ * Test {@link MetadataReport#saveExportedURLs(String, String, String)} method
+ *
+ * @since 2.7.8
+ * <p>
+ * Test {@link MetadataReport#getExportedURLs(String, String)} method
+ * @since 2.7.8
+ * <p>
+ * Test {@link MetadataReport#getExportedURLsContent(String, String)} method
+ * @since 2.7.8
+ *//*
+
     @Test
     public void testSaveExportedURLs() {
         String serviceName = null;
@@ -284,11 +303,13 @@ public class AbstractMetadataReportTest {
         assertTrue(abstractMetadataReport.saveExportedURLs(serviceName, exportedServiceRevision, exportedURLsContent));
     }
 
-    /**
-     * Test {@link MetadataReport#getExportedURLs(String, String)} method
-     *
-     * @since 2.7.8
-     */
+    */
+/**
+ * Test {@link MetadataReport#getExportedURLs(String, String)} method
+ *
+ * @since 2.7.8
+ *//*
+
     @Test
     public void testGetExportedURLs() {
         String serviceName = null;
@@ -296,11 +317,13 @@ public class AbstractMetadataReportTest {
         assertEquals(emptySet(), abstractMetadataReport.getExportedURLs(serviceName, exportedServiceRevision));
     }
 
-    /**
-     * Test {@link MetadataReport#getExportedURLsContent(String, String)} method
-     *
-     * @since 2.7.8
-     */
+    */
+/**
+ * Test {@link MetadataReport#getExportedURLsContent(String, String)} method
+ *
+ * @since 2.7.8
+ *//*
+
     @Test
     public void testGetExportedURLsContent() {
         String serviceName = null;
@@ -446,3 +469,4 @@ public class AbstractMetadataReportTest {
 
 
 }
+*/
diff --git a/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/report/support/ConfigCenterBasedMetadataReportTest.java b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/report/support/ConfigCenterBasedMetadataReportTest.java
deleted file mode 100644
index 32205b9..0000000
--- a/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/report/support/ConfigCenterBasedMetadataReportTest.java
+++ /dev/null
@@ -1,155 +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.report.support;
-
-import org.apache.dubbo.common.URL;
-import org.apache.dubbo.config.ApplicationConfig;
-import org.apache.dubbo.metadata.URLRevisionResolver;
-import org.apache.dubbo.metadata.definition.ServiceDefinitionBuilder;
-import org.apache.dubbo.metadata.definition.model.ServiceDefinition;
-import org.apache.dubbo.metadata.report.MetadataReport;
-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.metadata.report.support.file.FileSystemMetadataReportFactory;
-import org.apache.dubbo.rpc.model.ApplicationModel;
-import org.apache.dubbo.rpc.service.EchoService;
-
-import com.google.gson.Gson;
-import org.junit.jupiter.api.AfterEach;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-
-import java.util.Collection;
-import java.util.Map;
-import java.util.Set;
-import java.util.SortedSet;
-import java.util.TreeSet;
-
-import static java.util.Collections.singleton;
-import static java.util.stream.Collectors.toSet;
-import static org.apache.dubbo.common.constants.CommonConstants.APPLICATION_KEY;
-import static org.apache.dubbo.common.constants.CommonConstants.SIDE_KEY;
-import static org.apache.dubbo.metadata.report.support.Constants.SYNC_REPORT_KEY;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertNull;
-
-/**
- * {@link ConfigCenterBasedMetadataReport} Test-Cases
- *
- * @since 2.7.8
- */
-public class ConfigCenterBasedMetadataReportTest {
-
-    private static final URL REPORT_SERVER_URL = URL.valueOf("file://")
-            .addParameter(APPLICATION_KEY, "test")
-            .addParameter(SYNC_REPORT_KEY, "true");
-
-    private static final Class<EchoService> INTERFACE_CLASS = EchoService.class;
-
-    private static final String INTERFACE_NAME = INTERFACE_CLASS.getName();
-
-    private static final String APP_NAME = "test-service";
-
-    private static final URL BASE_URL = URL
-            .valueOf("dubbo://127.0.0.1:20880")
-            .setPath(INTERFACE_NAME)
-            .addParameter(APPLICATION_KEY, APP_NAME)
-            .addParameter(SIDE_KEY, "provider");
-
-    private ConfigCenterBasedMetadataReport metadataReport;
-
-    @BeforeEach
-    public void init() {
-        ApplicationModel.getConfigManager().setApplication(new ApplicationConfig("test-service"));
-        this.metadataReport = new FileSystemMetadataReportFactory().getMetadataReport(REPORT_SERVER_URL);
-    }
-
-    @AfterEach
-    public void reset() throws Exception {
-        ApplicationModel.reset();
-        this.metadataReport.close();
-    }
-
-    /**
-     * Test {@link MetadataReport#storeProviderMetadata(MetadataIdentifier, ServiceDefinition)} and
-     * {@link MetadataReport#getServiceDefinition(MetadataIdentifier)}
-     */
-    @Test
-    public void testStoreProviderMetadataAndGetServiceDefinition() {
-        MetadataIdentifier metadataIdentifier = new MetadataIdentifier(BASE_URL);
-        ServiceDefinition serviceDefinition = ServiceDefinitionBuilder.buildFullDefinition(INTERFACE_CLASS, BASE_URL.getParameters());
-        metadataReport.storeProviderMetadata(metadataIdentifier, serviceDefinition);
-        String serviceDefinitionJSON = metadataReport.getServiceDefinition(metadataIdentifier);
-        assertEquals(serviceDefinitionJSON, new Gson().toJson(serviceDefinition));
-    }
-
-    /**
-     * Test {@link MetadataReport#storeConsumerMetadata(MetadataIdentifier, Map)} and
-     * {@link MetadataReport#getServiceDefinition(MetadataIdentifier)}
-     */
-    @Test
-    public void testStoreConsumerMetadata() {
-        MetadataIdentifier metadataIdentifier = new MetadataIdentifier(BASE_URL);
-        metadataReport.storeConsumerMetadata(metadataIdentifier, BASE_URL.getParameters());
-        String parametersJSON = metadataReport.getServiceDefinition(metadataIdentifier);
-        assertEquals(parametersJSON, new Gson().toJson(BASE_URL.getParameters()));
-    }
-
-    /**
-     * Test {@link MetadataReport#saveServiceMetadata(ServiceMetadataIdentifier, URL)} and
-     * {@link MetadataReport#removeServiceMetadata(ServiceMetadataIdentifier)}
-     */
-    @Test
-    public void testSaveServiceMetadataAndRemoveServiceMetadata() {
-        ServiceMetadataIdentifier metadataIdentifier = new ServiceMetadataIdentifier(BASE_URL);
-        metadataReport.saveServiceMetadata(metadataIdentifier, BASE_URL);
-        String metadata = metadataReport.getMetadata(metadataIdentifier);
-        assertEquals(URL.encode(BASE_URL.toFullString()), metadata);
-        metadataReport.removeServiceMetadata(metadataIdentifier);
-        assertNull(metadataReport.getMetadata(metadataIdentifier));
-    }
-
-    /**
-     * Test {@link MetadataReport#saveSubscribedData(SubscriberMetadataIdentifier, Collection)} and
-     * {@link MetadataReport#getSubscribedURLs(SubscriberMetadataIdentifier)}
-     */
-    @Test
-    public void testSaveSubscribedDataAndGetSubscribedURLs() {
-        SubscriberMetadataIdentifier metadataIdentifier = new SubscriberMetadataIdentifier(BASE_URL);
-        Set<String> urls = singleton(BASE_URL).stream().map(URL::toIdentityString).collect(toSet());
-        metadataReport.saveSubscribedData(metadataIdentifier, urls);
-        Collection<String> subscribedURLs = metadataReport.getSubscribedURLs(metadataIdentifier);
-        assertEquals(1, subscribedURLs.size());
-        assertEquals(urls, subscribedURLs);
-    }
-
-    /**
-     * Test {@link MetadataReport#saveExportedURLs(SortedSet)},
-     * {@link MetadataReport#getExportedURLsContent(String, String)} and
-     * {@link MetadataReport#getExportedURLs(String, String)}
-     */
-    @Test
-    public void testSaveExportedURLsAndGetExportedURLs() {
-        SortedSet<String> urls = singleton(BASE_URL).stream().map(URL::toIdentityString).collect(TreeSet::new, Set::add, Set::addAll);
-        metadataReport.saveExportedURLs(urls);
-
-        URLRevisionResolver urlRevisionResolver = URLRevisionResolver.INSTANCE;
-        String revision = urlRevisionResolver.resolve(urls);
-        assertEquals(urls, metadataReport.getExportedURLs(APP_NAME, revision));
-    }
-}
diff --git a/dubbo-metadata/dubbo-metadata-report-zookeeper/src/main/java/org/apache/dubbo/metadata/store/zookeeper/ZookeeperMetadataReport.java b/dubbo-metadata/dubbo-metadata-report-zookeeper/src/main/java/org/apache/dubbo/metadata/store/zookeeper/ZookeeperMetadataReport.java
index 3a74f8d..9ca2e42 100644
--- a/dubbo-metadata/dubbo-metadata-report-zookeeper/src/main/java/org/apache/dubbo/metadata/store/zookeeper/ZookeeperMetadataReport.java
+++ b/dubbo-metadata/dubbo-metadata-report-zookeeper/src/main/java/org/apache/dubbo/metadata/store/zookeeper/ZookeeperMetadataReport.java
@@ -19,7 +19,6 @@ package org.apache.dubbo.metadata.store.zookeeper;
 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.PathUtils;
 import org.apache.dubbo.common.utils.StringUtils;
 import org.apache.dubbo.metadata.report.identifier.BaseMetadataIdentifier;
 import org.apache.dubbo.metadata.report.identifier.KeyTypeEnum;
@@ -27,7 +26,6 @@ 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.metadata.report.support.AbstractMetadataReport;
-import org.apache.dubbo.metadata.report.support.ConfigCenterBasedMetadataReport;
 import org.apache.dubbo.remoting.zookeeper.ZookeeperClient;
 import org.apache.dubbo.remoting.zookeeper.ZookeeperTransporter;
 
@@ -38,15 +36,10 @@ import java.util.List;
 
 import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY;
 import static org.apache.dubbo.common.constants.CommonConstants.PATH_SEPARATOR;
-import static org.apache.dubbo.metadata.MetadataConstants.DEFAULT_PATH_TAG;
-import static org.apache.dubbo.metadata.MetadataConstants.EXPORTED_URLS_TAG;
 
 /**
  * ZookeeperMetadataReport
- *
- * @deprecated 2.7.8 This class will be removed in the future, {@link ConfigCenterBasedMetadataReport} as a substitute.
  */
-@Deprecated
 public class ZookeeperMetadataReport extends AbstractMetadataReport {
 
     private final static Logger logger = LoggerFactory.getLogger(ZookeeperMetadataReport.class);
@@ -127,26 +120,4 @@ public class ZookeeperMetadataReport extends AbstractMetadataReport {
         return toRootDir() + metadataIdentifier.getUniqueKey(KeyTypeEnum.PATH);
     }
 
-    @Override
-    public boolean saveExportedURLs(String serviceName, String exportedServicesRevision, String exportedURLsContent) {
-        String path = buildExportedURLsMetadataPath(serviceName, exportedServicesRevision);
-        zkClient.create(path, exportedURLsContent, false);
-        return true;
-    }
-
-    @Override
-    public String getExportedURLsContent(String serviceName, String exportedServicesRevision) {
-        String path = buildExportedURLsMetadataPath(serviceName, exportedServicesRevision);
-        String content = zkClient.getContent(path);
-        return content;
-    }
-
-    private String buildExportedURLsMetadataPath(String serviceName, String exportedServicesRevision) {
-        return buildPath(DEFAULT_PATH_TAG, EXPORTED_URLS_TAG, serviceName, exportedServicesRevision);
-    }
-
-    private String buildPath(String... paths) {
-        return PathUtils.buildPath(toRootDir(), paths);
-    }
-
 }
diff --git a/dubbo-metadata/dubbo-metadata-report-zookeeper/src/main/java/org/apache/dubbo/metadata/store/zookeeper/ZookeeperMetadataReportFactory.java b/dubbo-metadata/dubbo-metadata-report-zookeeper/src/main/java/org/apache/dubbo/metadata/store/zookeeper/ZookeeperMetadataReportFactory.java
index 4773e1e..0ffed8d 100644
--- a/dubbo-metadata/dubbo-metadata-report-zookeeper/src/main/java/org/apache/dubbo/metadata/store/zookeeper/ZookeeperMetadataReportFactory.java
+++ b/dubbo-metadata/dubbo-metadata-report-zookeeper/src/main/java/org/apache/dubbo/metadata/store/zookeeper/ZookeeperMetadataReportFactory.java
@@ -16,17 +16,25 @@
  */
 package org.apache.dubbo.metadata.store.zookeeper;
 
-import org.apache.dubbo.metadata.report.identifier.KeyTypeEnum;
-import org.apache.dubbo.metadata.report.support.ConfigCenterBasedMetadataReportFactory;
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.metadata.report.MetadataReport;
+import org.apache.dubbo.metadata.report.support.AbstractMetadataReportFactory;
+import org.apache.dubbo.remoting.zookeeper.ZookeeperTransporter;
 
 /**
  * ZookeeperRegistryFactory.
- *
- * @revised 2.7.8 {@link ConfigCenterBasedMetadataReportFactory}
  */
-public class ZookeeperMetadataReportFactory extends ConfigCenterBasedMetadataReportFactory {
+public class ZookeeperMetadataReportFactory extends AbstractMetadataReportFactory {
+
+    private ZookeeperTransporter zookeeperTransporter;
 
-    public ZookeeperMetadataReportFactory() {
-        super(KeyTypeEnum.PATH);
+    public void setZookeeperTransporter(ZookeeperTransporter zookeeperTransporter) {
+        this.zookeeperTransporter = zookeeperTransporter;
     }
+
+    @Override
+    public MetadataReport createMetadataReport(URL url) {
+        return new ZookeeperMetadataReport(url, zookeeperTransporter);
+    }
+
 }
diff --git a/dubbo-metadata/pom.xml b/dubbo-metadata/pom.xml
index 17ceada..a112779 100644
--- a/dubbo-metadata/pom.xml
+++ b/dubbo-metadata/pom.xml
@@ -29,13 +29,13 @@
     <packaging>pom</packaging>
     <modules>
         <module>dubbo-metadata-api</module>
-        <module>dubbo-metadata-definition-protobuf</module>
+        <!--        <module>dubbo-metadata-definition-protobuf</module>-->
         <module>dubbo-metadata-report-zookeeper</module>
-        <module>dubbo-metadata-report-redis</module>
-        <module>dubbo-metadata-report-consul</module>
-        <module>dubbo-metadata-report-etcd</module>
-        <module>dubbo-metadata-report-nacos</module>
-        <module>dubbo-metadata-processor</module>
+        <!--        <module>dubbo-metadata-report-redis</module>-->
+        <!--        <module>dubbo-metadata-report-consul</module>-->
+        <!--        <module>dubbo-metadata-report-etcd</module>-->
+        <!--        <module>dubbo-metadata-report-nacos</module>-->
+        <!--        <module>dubbo-metadata-processor</module>-->
     </modules>
 
 </project>
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 7d4d48e..8e5a800 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
@@ -53,7 +53,7 @@ import static java.util.Collections.unmodifiableSet;
 import static java.util.stream.Collectors.toSet;
 import static java.util.stream.Stream.of;
 import static org.apache.dubbo.common.constants.CommonConstants.DUBBO;
-import static org.apache.dubbo.common.constants.CommonConstants.GROUP_CHAR_SEPERATOR;
+import static org.apache.dubbo.common.constants.CommonConstants.GROUP_CHAR_SEPARATOR;
 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.MAPPING_KEY;
@@ -318,7 +318,7 @@ public class ServiceDiscoveryRegistry implements Registry {
             List<ServiceInstance> serviceInstances = serviceDiscovery.getInstances(serviceName);
             serviceListener.onEvent(new ServiceInstancesChangedEvent(serviceName, serviceInstances));
         });
-        listener.notify(serviceListener.getUrls(url.getServiceKey() + GROUP_CHAR_SEPERATOR + url.getParameter(PROTOCOL_KEY, DUBBO)));
+        listener.notify(serviceListener.getUrls(url.getServiceKey() + GROUP_CHAR_SEPARATOR + url.getParameter(PROTOCOL_KEY, DUBBO)));
 
         serviceListener.addListener(url.getProtocolServiceKey(), listener);
         registerServiceInstancesChangedListener(url, serviceListener);
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/proxy/CompositeMetadataServiceProxyFactory.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/proxy/CompositeMetadataServiceProxyFactory.java
deleted file mode 100644
index f8c95d6..0000000
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/proxy/CompositeMetadataServiceProxyFactory.java
+++ /dev/null
@@ -1,133 +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.metadata.MetadataService;
-import org.apache.dubbo.registry.client.ServiceInstance;
-
-import java.lang.reflect.InvocationHandler;
-import java.lang.reflect.Method;
-import java.util.List;
-import java.util.Objects;
-import java.util.stream.Collectors;
-
-import static java.lang.String.format;
-import static java.lang.reflect.Proxy.newProxyInstance;
-import static org.apache.dubbo.common.extension.ExtensionLoader.getExtensionLoader;
-
-/**
- * The composite implementation of {@link MetadataServiceProxyFactory}
- *
- * @since 2.7.8
- */
-public class CompositeMetadataServiceProxyFactory extends BaseMetadataServiceProxyFactory {
-
-    private static final Logger logger = LoggerFactory.getLogger(CompositeMetadataServiceProxyFactory.class);
-
-    @Override
-    public MetadataService createProxy(ServiceInstance serviceInstance) {
-        MetadataService metadataService = (MetadataService) newProxyInstance(
-                getClass().getClassLoader(),
-                new Class[]{MetadataService.class},
-                new MetadataServiceInvocationHandler(serviceInstance, this)
-        );
-        return metadataService;
-    }
-
-    static class MetadataServiceInvocationHandler implements InvocationHandler {
-
-        private final ServiceInstance serviceInstance;
-
-        private final MetadataServiceProxyFactory excluded;
-
-        private volatile List<MetadataService> metadataServices;
-
-        MetadataServiceInvocationHandler(ServiceInstance serviceInstance,
-                                         MetadataServiceProxyFactory excluded) {
-            this.serviceInstance = serviceInstance;
-            this.excluded = excluded;
-        }
-
-        private List<MetadataService> loadMetadataServices() {
-            return getExtensionLoader(MetadataServiceProxyFactory.class)
-                    .getSupportedExtensionInstances()
-                    .stream()
-                    .filter(this::isRequiredFactory)
-                    .map(this::getProxy)
-                    .filter(Objects::nonNull)
-                    .collect(Collectors.toList());
-        }
-
-        private List<MetadataService> getMetadataServices() {
-            if (metadataServices == null) {
-                metadataServices = loadMetadataServices();
-                if (metadataServices.isEmpty()) {
-                    throw new IllegalStateException(format("No valid proxy of %s can't be loaded.",
-                            MetadataService.class.getName()));
-                }
-            }
-            return metadataServices;
-        }
-
-        private boolean isRequiredFactory(MetadataServiceProxyFactory factory) {
-            return !factory.equals(excluded);
-        }
-
-        private MetadataService getProxy(MetadataServiceProxyFactory factory) {
-            MetadataService metadataService = null;
-            try {
-                metadataService = factory.getProxy(serviceInstance);
-            } catch (Exception e) {
-                if (logger.isErrorEnabled()) {
-                    logger.error(format("The proxy of %s can't be gotten by %s [from : %s].",
-                            MetadataService.class.getName(),
-                            factory.getClass().getName(),
-                            serviceInstance.toString()));
-                }
-            }
-            return metadataService;
-        }
-
-        @Override
-        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
-
-            if (Object.class.equals(method.getDeclaringClass())) {
-                return method.invoke(proxy, args);
-            }
-
-            Object result = null;
-
-            for (MetadataService metadataService : getMetadataServices()) {
-                try {
-                    result = method.invoke(metadataService, args);
-                    if (result != null) {
-                        break;
-                    }
-                } catch (Exception e) {
-                    if (logger.isErrorEnabled()) {
-                        logger.error(format("MetadataService[type : %s] executes failed.", metadataService.getClass().getName()), e);
-                    }
-                }
-            }
-
-            return result;
-        }
-    }
-
-}
diff --git a/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/metadata/proxy/BaseMetadataServiceProxyFactoryTest.java b/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/metadata/proxy/BaseMetadataServiceProxyFactoryTest.java
deleted file mode 100644
index 6aca4c6..0000000
--- a/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/metadata/proxy/BaseMetadataServiceProxyFactoryTest.java
+++ /dev/null
@@ -1,78 +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.DefaultServiceInstance;
-
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-
-import java.util.HashMap;
-import java.util.Map;
-
-import static java.lang.String.valueOf;
-import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.EXPORTED_SERVICES_REVISION_PROPERTY_NAME;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertNotSame;
-import static org.junit.jupiter.api.Assertions.assertSame;
-
-/**
- * {@link BaseMetadataServiceProxyFactory} Test-Cases
- *
- * @since 2.7.8
- */
-public class BaseMetadataServiceProxyFactoryTest {
-
-    private MyMetadataServiceProxyFactory factory;
-
-    private DefaultServiceInstance instance;
-
-    @BeforeEach
-    public void init() {
-        factory = new MyMetadataServiceProxyFactory();
-        instance = createServiceInstance();
-    }
-
-    private DefaultServiceInstance createServiceInstance() {
-        DefaultServiceInstance serviceInstance = new DefaultServiceInstance(valueOf(System.nanoTime()), "A", "127.0.0.1", 8080);
-        Map<String, String> metadata = new HashMap<>();
-        metadata.put(EXPORTED_SERVICES_REVISION_PROPERTY_NAME, "X");
-        serviceInstance.setMetadata(metadata);
-        return serviceInstance;
-    }
-
-    @Test
-    public void testCreateProxyCacheKey() {
-        assertEquals("A#X", factory.createProxyCacheKey(instance));
-    }
-
-    @Test
-    public void testCreateProxy() {
-        MetadataService metadataService = factory.createProxy(instance);
-        MetadataService metadataService2 = factory.createProxy(instance);
-        assertNotSame(metadataService, metadataService2);
-    }
-
-    @Test
-    public void testGetProxy() {
-        MetadataService metadataService = factory.getProxy(instance);
-        MetadataService metadataService2 = factory.getProxy(instance);
-        assertSame(metadataService, metadataService2);
-    }
-
-}
diff --git a/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/metadata/proxy/CompositeMetadataServiceProxyFactoryTest.java b/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/metadata/proxy/CompositeMetadataServiceProxyFactoryTest.java
deleted file mode 100644
index ddd78e0..0000000
--- a/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/metadata/proxy/CompositeMetadataServiceProxyFactoryTest.java
+++ /dev/null
@@ -1,96 +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.config.ApplicationConfig;
-import org.apache.dubbo.metadata.MetadataService;
-import org.apache.dubbo.metadata.report.MetadataReportInstance;
-import org.apache.dubbo.registry.client.DefaultServiceInstance;
-import org.apache.dubbo.rpc.model.ApplicationModel;
-
-import org.junit.jupiter.api.AfterEach;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-
-import java.util.HashMap;
-import java.util.Map;
-import java.util.SortedSet;
-
-import static java.lang.String.valueOf;
-import static org.apache.dubbo.common.constants.CommonConstants.APPLICATION_KEY;
-import static org.apache.dubbo.common.constants.CommonConstants.COMPOSITE_METADATA_STORAGE_TYPE;
-import static org.apache.dubbo.metadata.report.support.Constants.SYNC_REPORT_KEY;
-import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.EXPORTED_SERVICES_REVISION_PROPERTY_NAME;
-import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.METADATA_SERVICE_URL_PARAMS_PROPERTY_NAME;
-import static org.junit.jupiter.api.Assertions.assertSame;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-
-/**
- * {@link CompositeMetadataServiceProxyFactory} Test-Cases
- *
- * @since 2.7.8
- */
-public class CompositeMetadataServiceProxyFactoryTest {
-
-    private static final URL METADATA_REPORT_URL = URL.valueOf("file://")
-            .addParameter(APPLICATION_KEY, "test")
-            .addParameter(SYNC_REPORT_KEY, "true");
-
-    private static final String APP_NAME = "test-service";
-
-    private MetadataServiceProxyFactory factory;
-
-    private DefaultServiceInstance instance;
-
-    @BeforeEach
-    public void init() {
-        ApplicationModel.getConfigManager().setApplication(new ApplicationConfig(APP_NAME));
-        MetadataReportInstance.init(METADATA_REPORT_URL);
-        factory = MetadataServiceProxyFactory.getExtension(COMPOSITE_METADATA_STORAGE_TYPE);
-        instance = createServiceInstance();
-    }
-
-    @AfterEach
-    public void reset() throws Exception {
-        ApplicationModel.reset();
-        MetadataReportInstance.getMetadataReport().close();
-    }
-
-    private DefaultServiceInstance createServiceInstance() {
-        DefaultServiceInstance serviceInstance = new DefaultServiceInstance(valueOf(System.nanoTime()), "A", "127.0.0.1", 8080);
-        Map<String, String> metadata = new HashMap<>();
-        metadata.put(EXPORTED_SERVICES_REVISION_PROPERTY_NAME, "X");
-        metadata.put(METADATA_SERVICE_URL_PARAMS_PROPERTY_NAME, "{\"dubbo\":{\"application\":\"dubbo-provider-demo\",\"deprecated\":\"false\",\"group\":\"dubbo-provider-demo\",\"version\":\"1.0.0\",\"timestamp\":\"1564845042651\",\"dubbo\":\"2.0.2\",\"host\":\"192.168.0.102\",\"port\":\"20880\"}}");
-        serviceInstance.setMetadata(metadata);
-        return serviceInstance;
-    }
-
-    @Test
-    public void testGetProxy() {
-        MetadataService metadataService = factory.getProxy(instance);
-        MetadataService metadataService2 = factory.getProxy(instance);
-        assertSame(metadataService, metadataService2);
-    }
-
-    @Test
-    public void testGetExportedURLs() {
-        MetadataService metadataService = factory.getProxy(instance);
-        SortedSet<String> exportedURLs = metadataService.getExportedURLs();
-        assertTrue(exportedURLs.isEmpty());
-    }
-}
diff --git a/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/metadata/proxy/MetadataServiceProxyFactoryTest.java b/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/metadata/proxy/MetadataServiceProxyFactoryTest.java
deleted file mode 100644
index 2c31c22..0000000
--- a/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/metadata/proxy/MetadataServiceProxyFactoryTest.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.registry.client.metadata.proxy;
-
-import org.junit.jupiter.api.Test;
-
-import static org.apache.dubbo.common.constants.CommonConstants.COMPOSITE_METADATA_STORAGE_TYPE;
-import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_METADATA_STORAGE_TYPE;
-import static org.apache.dubbo.common.constants.CommonConstants.REMOTE_METADATA_STORAGE_TYPE;
-import static org.apache.dubbo.registry.client.metadata.proxy.MetadataServiceProxyFactory.getDefaultExtension;
-import static org.apache.dubbo.registry.client.metadata.proxy.MetadataServiceProxyFactory.getExtension;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-
-/**
- * {@link MetadataServiceProxyFactory} Test-Cases
- *
- * @since 2.7.8
- */
-public class MetadataServiceProxyFactoryTest {
-
-    @Test
-    public void testExtension() {
-        MetadataServiceProxyFactory defaultFactory = getDefaultExtension();
-        MetadataServiceProxyFactory factory = getExtension(DEFAULT_METADATA_STORAGE_TYPE);
-        assertEquals(defaultFactory, factory);
-
-        assertEquals(MyMetadataServiceProxyFactory.class, factory.getClass());
-
-        factory = getExtension(REMOTE_METADATA_STORAGE_TYPE);
-        assertEquals(RemoteMetadataServiceProxyFactory.class, factory.getClass());
-
-        factory = getExtension(COMPOSITE_METADATA_STORAGE_TYPE);
-        assertEquals(CompositeMetadataServiceProxyFactory.class, factory.getClass());
-    }
-}
diff --git a/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/metadata/proxy/MyMetadataServiceProxyFactory.java b/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/metadata/proxy/MyMetadataServiceProxyFactory.java
deleted file mode 100644
index 86247e1..0000000
--- a/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/metadata/proxy/MyMetadataServiceProxyFactory.java
+++ /dev/null
@@ -1,29 +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.metadata.store.InMemoryWritableMetadataService;
-import org.apache.dubbo.registry.client.ServiceInstance;
-
-public class MyMetadataServiceProxyFactory extends BaseMetadataServiceProxyFactory {
-
-    @Override
-    protected MetadataService createProxy(ServiceInstance serviceInstance) {
-        return new InMemoryWritableMetadataService();
-    }
-}


[dubbo] 05/27: redefine MetadataReport

Posted by li...@apache.org.
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

commit 3810536e0fd6ae39452d435e1851039b2f96e599
Author: ken.lj <ke...@gmail.com>
AuthorDate: Tue Jun 16 00:38:33 2020 +0800

    redefine MetadataReport
---
 .../src/main/java/org/apache/dubbo/common/URL.java |  11 ++-
 .../dubbo/common/constants/CommonConstants.java    |   6 ++
 .../org/apache/dubbo/config/ServiceConfig.java     |   7 +-
 .../event/listener/ServiceNameMappingListener.java |  49 ---------
 .../metadata/DefaultMetadataParamsFilter.java      |   3 +-
 .../DynamicConfigurationServiceNameMapping.java    |  60 ++++--------
 .../org/apache/dubbo/metadata/MetadataInfo.java    |   5 +-
 .../dubbo/metadata/MetadataServiceNameMapping.java |  71 ++++++++++++++
 .../apache/dubbo/metadata/ServiceNameMapping.java  |  61 +++++-------
 .../dubbo/metadata/report/MetadataReport.java      |  27 ++++-
 .../org.apache.dubbo.metadata.ServiceNameMapping   |  11 +--
 ...DynamicConfigurationServiceNameMappingTest.java | 109 ---------------------
 .../apache/dubbo/metadata/MetadataInfoTest.java    |   2 +-
 .../registry/client/DefaultServiceInstance.java    |   2 +-
 .../registry/client/ServiceDiscoveryRegistry.java  |  27 +----
 .../listener/ServiceInstancesChangedListener.java  |   6 +-
 .../registry/client/metadata/MetadataUtils.java    |   1 -
 .../store/InMemoryWritableMetadataService.java     |   3 +-
 18 files changed, 173 insertions(+), 288 deletions(-)

diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/URL.java b/dubbo-common/src/main/java/org/apache/dubbo/common/URL.java
index 6ab3a89..a396cc9 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/URL.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/URL.java
@@ -850,6 +850,15 @@ class URL implements Serializable {
         return URL.decode(getMethodParameter(method, key, defaultValue));
     }
 
+    public String getMethodParameterStrict(String method, String key) {
+        Map<String, String> keyMap = methodParameters.get(method);
+        String value = null;
+        if (keyMap != null) {
+            value = keyMap.get(key);
+        }
+        return value;
+    }
+
     public String getMethodParameter(String method, String key) {
         Map<String, String> keyMap = methodParameters.get(method);
         String value = null;
@@ -1041,7 +1050,7 @@ class URL implements Serializable {
             }
             return false;
         }
-        String value = getMethodParameter(method, key);
+        String value = getMethodParameterStrict(method, key);
         return StringUtils.isNotEmpty(value);
     }
 
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/constants/CommonConstants.java b/dubbo-common/src/main/java/org/apache/dubbo/common/constants/CommonConstants.java
index e2cc73d..a9ac119 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/constants/CommonConstants.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/constants/CommonConstants.java
@@ -189,6 +189,12 @@ public interface CommonConstants {
 
     String METADATA_KEY = "metadata-type";
 
+    String MAPPING_KEY = "mapping-type";
+
+    String CONFIG_MAPPING_TYPE = "config";
+
+    String METADATA_MAPPING_TYPE = "metadata";
+
     String DEFAULT_METADATA_STORAGE_TYPE = "local";
 
     String REMOTE_METADATA_STORAGE_TYPE = "remote";
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 3a7702b..37972c1 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,6 +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.ServiceNameMapping;
 import org.apache.dubbo.registry.client.metadata.MetadataUtils;
 import org.apache.dubbo.rpc.Exporter;
 import org.apache.dubbo.rpc.Invoker;
@@ -71,6 +72,7 @@ import static org.apache.dubbo.common.constants.CommonConstants.ANY_VALUE;
 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;
+import static org.apache.dubbo.common.constants.CommonConstants.MAPPING_KEY;
 import static org.apache.dubbo.common.constants.CommonConstants.METADATA_KEY;
 import static org.apache.dubbo.common.constants.CommonConstants.METHODS_KEY;
 import static org.apache.dubbo.common.constants.CommonConstants.MONITOR_KEY;
@@ -209,7 +211,10 @@ public class ServiceConfig<T> extends ServiceConfigBase<T> {
     }
 
     public void exported() {
-
+        List<URL> exportedURLs = this.getExportedUrls();
+        exportedURLs.forEach(url -> {
+            ServiceNameMapping.getExtension(getApplication().getParameters().get(MAPPING_KEY)).map(url);
+        });
         // dispatch a ServiceConfigExportedEvent since 2.7.4
         dispatch(new ServiceConfigExportedEvent(this));
     }
diff --git a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/event/listener/ServiceNameMappingListener.java b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/event/listener/ServiceNameMappingListener.java
deleted file mode 100644
index 8607b51..0000000
--- a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/event/listener/ServiceNameMappingListener.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.config.event.listener;
-
-import org.apache.dubbo.common.URL;
-import org.apache.dubbo.config.ServiceConfig;
-import org.apache.dubbo.config.event.ServiceConfigExportedEvent;
-import org.apache.dubbo.event.EventListener;
-import org.apache.dubbo.metadata.ServiceNameMapping;
-
-import java.util.List;
-
-import static org.apache.dubbo.metadata.ServiceNameMapping.getDefaultExtension;
-
-/**
- * An {@link EventListener event listener} for mapping {@link ServiceConfig#getExportedUrls() the exported Dubbo
- * service inerface} to its service name
- *
- * @see ServiceNameMapping
- * @see ServiceConfig#getExportedUrls()
- * @since 2.7.5
- */
-public class ServiceNameMappingListener implements EventListener<ServiceConfigExportedEvent> {
-
-    private final ServiceNameMapping serviceNameMapping = getDefaultExtension();
-
-    @Override
-    public void onEvent(ServiceConfigExportedEvent event) {
-        ServiceConfig serviceConfig = event.getServiceConfig();
-        List<URL> exportedURLs = serviceConfig.getExportedUrls();
-        exportedURLs.forEach(url -> {
-            serviceNameMapping.map(url);
-        });
-    }
-}
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
index 0563e43..7a11096 100644
--- 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
@@ -25,7 +25,6 @@ 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;
@@ -44,7 +43,7 @@ public class DefaultMetadataParamsFilter implements MetadataParamsFilter {
         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
+                WEIGHT_KEY, DUBBO_VERSION_KEY, RELEASE_KEY
         };
     }
 }
diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/DynamicConfigurationServiceNameMapping.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/DynamicConfigurationServiceNameMapping.java
index 2a04cb7..a25d07a 100644
--- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/DynamicConfigurationServiceNameMapping.java
+++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/DynamicConfigurationServiceNameMapping.java
@@ -28,17 +28,12 @@ import java.util.Set;
 
 import static java.lang.String.valueOf;
 import static java.util.Arrays.asList;
-import static org.apache.dubbo.common.config.configcenter.DynamicConfiguration.getDynamicConfiguration;
 import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY;
 import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY;
-import static org.apache.dubbo.common.utils.CollectionUtils.isNotEmpty;
-import static org.apache.dubbo.common.utils.StringUtils.SLASH;
 import static org.apache.dubbo.rpc.model.ApplicationModel.getName;
 
 /**
  * The {@link ServiceNameMapping} implementation based on {@link DynamicConfiguration}
- *
- * @since 2.7.5
  */
 public class DynamicConfigurationServiceNameMapping implements ServiceNameMapping {
 
@@ -48,32 +43,27 @@ public class DynamicConfigurationServiceNameMapping implements ServiceNameMappin
 
     private final Logger logger = LoggerFactory.getLogger(getClass());
 
-    /**
-     * The priority of {@link DynamicConfigurationServiceNameMapping} is
-     * lower than {@link ParameterizedServiceNameMapping}
-     */
-    static final int PRIORITY = PropertiesFileServiceNameMapping.PRIORITY + 1;
-
     @Override
-    public void map(URL exportedURL) {
-
-        String serviceInterface = exportedURL.getServiceInterface();
+    public void map(URL url) {
+        String serviceInterface = url.getServiceInterface();
+        String group = url.getParameter(GROUP_KEY);
+        String version = url.getParameter(VERSION_KEY);
+        String protocol = url.getProtocol();
 
         if (IGNORED_SERVICE_INTERFACES.contains(serviceInterface)) {
             return;
         }
 
-        String group = exportedURL.getParameter(GROUP_KEY);
-        String version = exportedURL.getParameter(VERSION_KEY);
-        String protocol = exportedURL.getProtocol();
+        DynamicConfiguration dynamicConfiguration = DynamicConfiguration.getDynamicConfiguration();
 
         // the Dubbo Service Key as group
         // the service(application) name as key
         // It does matter whatever the content is, we just need a record
         String key = getName();
         String content = valueOf(System.currentTimeMillis());
+
         execute(() -> {
-            getDynamicConfiguration().publishConfig(key, buildGroup(serviceInterface, group, version, protocol), content);
+            dynamicConfiguration.publishConfig(key, ServiceNameMapping.buildGroup(serviceInterface, group, version, protocol), content);
             if (logger.isInfoEnabled()) {
                 logger.info(String.format("Dubbo service[%s] mapped to interface name[%s].",
                         group, serviceInterface, group));
@@ -82,33 +72,22 @@ public class DynamicConfigurationServiceNameMapping implements ServiceNameMappin
     }
 
     @Override
-    public Set<String> get(URL subscribedURL) {
-
-        String serviceInterface = subscribedURL.getServiceInterface();
-        String group = subscribedURL.getParameter(GROUP_KEY);
-        String version = subscribedURL.getParameter(VERSION_KEY);
-        String protocol = subscribedURL.getProtocol();
+    public Set<String> get(URL url) {
+        String serviceInterface = url.getServiceInterface();
+        String group = url.getParameter(GROUP_KEY);
+        String version = url.getParameter(VERSION_KEY);
+        String protocol = url.getProtocol();
+        DynamicConfiguration dynamicConfiguration = DynamicConfiguration.getDynamicConfiguration();
 
         Set<String> serviceNames = new LinkedHashSet<>();
         execute(() -> {
-            Set<String> keys = getDynamicConfiguration().getConfigKeys(buildGroup(serviceInterface, group, version, protocol));
-            if (isNotEmpty(keys)) {
-                serviceNames.addAll(keys);
-            }
+            Set<String> keys = dynamicConfiguration
+                    .getConfigKeys(ServiceNameMapping.buildGroup(serviceInterface, group, version, protocol));
+            serviceNames.addAll(keys);
         });
         return Collections.unmodifiableSet(serviceNames);
     }
 
-    protected static String buildGroup(String serviceInterface, String group, String version, String protocol) {
-        //        the issue : https://github.com/apache/dubbo/issues/4671
-        //        StringBuilder groupBuilder = new StringBuilder(serviceInterface)
-        //                .append(KEY_SEPARATOR).append(defaultString(group))
-        //                .append(KEY_SEPARATOR).append(defaultString(version))
-        //                .append(KEY_SEPARATOR).append(defaultString(protocol));
-        //        return groupBuilder.toString();
-        return DEFAULT_MAPPING_GROUP + SLASH + serviceInterface;
-    }
-
     private void execute(Runnable runnable) {
         try {
             runnable.run();
@@ -118,9 +97,4 @@ public class DynamicConfigurationServiceNameMapping implements ServiceNameMappin
             }
         }
     }
-
-    @Override
-    public int getPriority() {
-        return PRIORITY;
-    }
 }
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 01e3c09..f235655 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
@@ -42,7 +42,8 @@ public class MetadataInfo implements Serializable {
     private String revision;
     private Map<String, ServiceInfo> services;
 
-    public MetadataInfo() {
+    public MetadataInfo(String app) {
+        this.app = app;
         this.services = new HashMap<>();
     }
 
@@ -166,7 +167,7 @@ public class MetadataInfo implements Serializable {
                         String[] methods = url.getParameter(METHODS_KEY, (String[]) null);
                         if (methods != null) {
                             for (String method : methods) {
-                                String mValue = url.getMethodParameter(method, p);
+                                String mValue = url.getMethodParameterStrict(method, p);
                                 if (StringUtils.isNotEmpty(mValue)) {
                                     params.put(method + DOT_SEPARATOR + p, mValue);
                                 }
diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataServiceNameMapping.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataServiceNameMapping.java
new file mode 100644
index 0000000..9c0c600
--- /dev/null
+++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataServiceNameMapping.java
@@ -0,0 +1,71 @@
+/*
+ * 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.common.utils.CollectionUtils;
+import org.apache.dubbo.metadata.report.MetadataReport;
+import org.apache.dubbo.metadata.report.MetadataReportInstance;
+
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+
+import static java.util.Arrays.asList;
+import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY;
+import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY;
+import static org.apache.dubbo.rpc.model.ApplicationModel.getName;
+
+public class MetadataServiceNameMapping implements ServiceNameMapping {
+    private static final List<String> IGNORED_SERVICE_INTERFACES = asList(MetadataService.class.getName());
+
+    @Override
+    public void map(URL url) {
+        String serviceInterface = url.getServiceInterface();
+        String group = url.getParameter(GROUP_KEY);
+        String version = url.getParameter(VERSION_KEY);
+        String protocol = url.getProtocol();
+
+        if (IGNORED_SERVICE_INTERFACES.contains(serviceInterface)) {
+            return;
+        }
+
+        List<MetadataReport> metadataReports = MetadataReportInstance.getMetadataReports(true);
+        metadataReports.forEach(reporter -> {
+            reporter.registerServiceAppMapping(ServiceNameMapping.buildGroup(serviceInterface, group, version, protocol), getName(), url);
+        });
+    }
+
+    @Override
+    public Set<String> get(URL url) {
+        String serviceInterface = url.getServiceInterface();
+        String group = url.getParameter(GROUP_KEY);
+        String version = url.getParameter(VERSION_KEY);
+        String protocol = url.getProtocol();
+
+        List<MetadataReport> metadataReports = MetadataReportInstance.getMetadataReports(true);
+        Set<String> serviceNames = new LinkedHashSet<>();
+        for (MetadataReport reporter : metadataReports) {
+            Set<String> apps = reporter.getServiceAppMapping(ServiceNameMapping.buildGroup(serviceInterface, group, version, protocol), url);
+            if (CollectionUtils.isNotEmpty(apps)) {
+                serviceNames.addAll(apps);
+                break;
+            }
+        }
+        return serviceNames;
+    }
+}
diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/ServiceNameMapping.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/ServiceNameMapping.java
index 46f657d..44d55c5 100644
--- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/ServiceNameMapping.java
+++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/ServiceNameMapping.java
@@ -18,65 +18,34 @@ package org.apache.dubbo.metadata;
 
 import org.apache.dubbo.common.URL;
 import org.apache.dubbo.common.extension.SPI;
-import org.apache.dubbo.common.lang.Prioritized;
 
 import java.util.Set;
 
+import static org.apache.dubbo.common.constants.CommonConstants.CONFIG_MAPPING_TYPE;
 import static org.apache.dubbo.common.extension.ExtensionLoader.getExtensionLoader;
+import static org.apache.dubbo.common.utils.StringUtils.SLASH;
+import static org.apache.dubbo.metadata.DynamicConfigurationServiceNameMapping.DEFAULT_MAPPING_GROUP;
 
 /**
  * The interface for Dubbo service name Mapping
  *
  * @since 2.7.5
  */
-@SPI("default")
-public interface ServiceNameMapping extends Prioritized {
+@SPI("config")
+public interface ServiceNameMapping {
 
     /**
      * Map the specified Dubbo service interface, group, version and protocol to current Dubbo service name
-     *
-     * @param serviceInterface the class name of Dubbo service interface
-     * @param group            the group of Dubbo service interface (optional)
-     * @param version          the version of Dubbo service interface version (optional)
-     * @param protocol         the protocol of Dubbo service interface exported (optional)
-     * @deprecated 2.7.8 This method will be removed since 3.0
      */
-    @Deprecated
-    default void map(String serviceInterface, String group, String version, String protocol) {
-        throw new UnsupportedOperationException("This method has been deprecated and should not be invoked!");
-    }
-
-    /**
-     * Map the specified Dubbo service {@link URL} to current Dubbo service name
-     *
-     * @param exportedURL the {@link URL} that the Dubbo Provider exported
-     * @since 2.7.8
-     */
-    void map(URL exportedURL);
+    void map(URL url);
 
     /**
      * Get the service names from the specified Dubbo service interface, group, version and protocol
      *
-     * @param serviceInterface the class name of Dubbo service interface
-     * @param group            the group of Dubbo service interface (optional)
-     * @param version          the version of Dubbo service interface version (optional)
-     * @param protocol         the protocol of Dubbo service interface exported (optional)
-     * @return non-null {@link Set}
-     * @deprecated 2.7.8 This method will be removed since 3.0
+     * @return
      */
-    @Deprecated
-    default Set<String> get(String serviceInterface, String group, String version, String protocol) {
-        throw new UnsupportedOperationException("This method has been deprecated and should not be invoked!");
-    }
+    Set<String> get(URL url);
 
-    /**
-     * Get the service names from the subscribed Dubbo service {@link URL}
-     *
-     * @param subscribedURL the {@link URL} that the Dubbo consumer subscribed
-     * @return non-null {@link Set}
-     * @since 2.7.8
-     */
-    Set<String> get(URL subscribedURL);
 
     /**
      * Get the default extension of {@link ServiceNameMapping}
@@ -87,4 +56,18 @@ public interface ServiceNameMapping extends Prioritized {
     static ServiceNameMapping getDefaultExtension() {
         return getExtensionLoader(ServiceNameMapping.class).getDefaultExtension();
     }
+
+    static ServiceNameMapping getExtension(String name) {
+        return getExtensionLoader(ServiceNameMapping.class).getExtension(name == null ? CONFIG_MAPPING_TYPE : name);
+    }
+
+    static String buildGroup(String serviceInterface, String group, String version, String protocol) {
+        //        the issue : https://github.com/apache/dubbo/issues/4671
+        //        StringBuilder groupBuilder = new StringBuilder(serviceInterface)
+        //                .append(KEY_SEPARATOR).append(defaultString(group))
+        //                .append(KEY_SEPARATOR).append(defaultString(version))
+        //                .append(KEY_SEPARATOR).append(defaultString(protocol));
+        //        return groupBuilder.toString();
+        return DEFAULT_MAPPING_GROUP + SLASH + serviceInterface;
+    }
 }
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 4bc133d..256ae6e 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
@@ -24,20 +24,23 @@ 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 java.util.Collections;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
 public interface MetadataReport {
 
+    /**
+     * Service Definition -- START
+     **/
     void storeProviderMetadata(MetadataIdentifier providerMetadataIdentifier, ServiceDefinition serviceDefinition);
 
-    void storeConsumerMetadata(MetadataIdentifier consumerMetadataIdentifier, Map<String, String> serviceParameterMap);
-
-    List<String> getExportedURLs(ServiceMetadataIdentifier metadataIdentifier);
-
     String getServiceDefinition(MetadataIdentifier metadataIdentifier);
 
+    /**
+     * Application Metadata -- START
+     **/
     default void publishAppMetadata(SubscriberMetadataIdentifier identifier, MetadataInfo metadataInfo) {
     }
 
@@ -46,8 +49,22 @@ public interface MetadataReport {
     }
 
     /**
-     * deprecated
+     * Service<-->Application Mapping -- START
      **/
+    default Set<String> getServiceAppMapping(String serviceKey, URL url) {
+        return Collections.emptySet();
+    }
+
+    default void registerServiceAppMapping(String serviceKey, String application, URL url) {
+        return;
+    }
+
+    /**
+     * deprecated or need triage
+     **/
+    void storeConsumerMetadata(MetadataIdentifier consumerMetadataIdentifier, Map<String, String> serviceParameterMap);
+
+    List<String> getExportedURLs(ServiceMetadataIdentifier metadataIdentifier);
 
     void saveServiceMetadata(ServiceMetadataIdentifier metadataIdentifier, URL url);
 
diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metadata.ServiceNameMapping b/dubbo-metadata/dubbo-metadata-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metadata.ServiceNameMapping
index beadd2e..d43b342 100644
--- a/dubbo-metadata/dubbo-metadata-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metadata.ServiceNameMapping
+++ b/dubbo-metadata/dubbo-metadata-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metadata.ServiceNameMapping
@@ -1,9 +1,2 @@
-# "default" implementation has been changed since 2.7.8
-default=org.apache.dubbo.metadata.CompositeServiceNameMapping
-
-# "properties-file" and "parameterized" are introduced since 2.7.8
-properties-file = org.apache.dubbo.metadata.PropertiesFileServiceNameMapping
-parameterized  = org.apache.dubbo.metadata.ParameterizedServiceNameMapping
-
-# "dynamic-configuration" has been revised since 2.7.8
-dynamic-configuration = org.apache.dubbo.metadata.DynamicConfigurationServiceNameMapping
\ No newline at end of file
+config=org.apache.dubbo.metadata.DynamicConfigurationServiceNameMapping
+metadata=org.apache.dubbo.metadata.MetadataServiceNameMapping
\ No newline at end of file
diff --git a/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/DynamicConfigurationServiceNameMappingTest.java b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/DynamicConfigurationServiceNameMappingTest.java
deleted file mode 100644
index d4d25ee..0000000
--- a/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/DynamicConfigurationServiceNameMappingTest.java
+++ /dev/null
@@ -1,109 +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.common.config.configcenter.DynamicConfiguration;
-import org.apache.dubbo.common.config.configcenter.DynamicConfigurationFactory;
-import org.apache.dubbo.config.ApplicationConfig;
-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.TreeSet;
-
-import static java.util.Arrays.asList;
-import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY;
-import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY;
-import static org.apache.dubbo.common.extension.ExtensionLoader.getExtensionLoader;
-import static org.apache.dubbo.metadata.DynamicConfigurationServiceNameMapping.buildGroup;
-import static org.apache.dubbo.metadata.ServiceNameMapping.getDefaultExtension;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertThrows;
-
-/**
- * {@link DynamicConfigurationServiceNameMapping} Test
- *
- * @since 2.7.5
- */
-public class DynamicConfigurationServiceNameMappingTest {
-
-
-    private final ServiceNameMapping serviceNameMapping = getDefaultExtension();
-
-    @BeforeAll
-    public static void setUp() throws Exception {
-
-        DynamicConfiguration configuration = getExtensionLoader(DynamicConfigurationFactory.class)
-                .getExtension("file")
-                .getDynamicConfiguration(null);
-
-        ApplicationModel.getEnvironment().setDynamicConfiguration(configuration);
-    }
-
-    @Test
-    public void testBuildGroup() {
-        assertEquals("mapping/test", buildGroup("test", null, null, null));
-        assertEquals("mapping/test", buildGroup("test", "default", null, null));
-        assertEquals("mapping/test", buildGroup("test", "default", "1.0.0", null));
-        assertEquals("mapping/test", buildGroup("test", "default", "1.0.0", "dubbo"));
-    }
-
-    @Test
-    public void testAndGetOnFailed() {
-        assertThrows(UnsupportedOperationException.class, () -> {
-            serviceNameMapping.map(null, null, null, null);
-        });
-
-        assertThrows(UnsupportedOperationException.class, () -> {
-            serviceNameMapping.get(null, null, null, null);
-        });
-    }
-
-    @Test
-    public void testMapAndGet() {
-
-        String serviceName = "test";
-        String serviceName2 = "test2";
-
-        ApplicationModel.getConfigManager().setApplication(new ApplicationConfig(serviceName));
-
-        String serviceInterface = "org.apache.dubbo.service.UserService";
-        String group = null;
-        String version = null;
-        String protocol = null;
-
-        URL url = URL.valueOf("dubbo://127.0.0.1:20880").setServiceInterface(serviceInterface)
-                .addParameter(GROUP_KEY, group)
-                .addParameter(VERSION_KEY, version);
-
-        serviceNameMapping.map(url);
-
-        ApplicationModel.getConfigManager().removeConfig(new ApplicationConfig(serviceName));
-        ApplicationModel.getConfigManager().setApplication(new ApplicationConfig(serviceName2));
-
-        serviceNameMapping.map(url);
-
-        Set<String> serviceNames = serviceNameMapping.get(url);
-
-        assertEquals(new TreeSet(asList(serviceName, serviceName2)), serviceNames);
-
-        ApplicationModel.getConfigManager().removeConfig(new ApplicationConfig(serviceName2));
-    }
-}
diff --git a/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/MetadataInfoTest.java b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/MetadataInfoTest.java
index 1d2f42e..be8c3fe 100644
--- a/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/MetadataInfoTest.java
+++ b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/MetadataInfoTest.java
@@ -23,7 +23,7 @@ import org.junit.jupiter.api.Test;
 public class MetadataInfoTest {
     @Test
     public void revisionTest() {
-        MetadataInfo metadataInfo = new MetadataInfo();
+        MetadataInfo metadataInfo = new MetadataInfo("demo");
         metadataInfo.setApp("demo");
 
         URL url = URL.valueOf("dubbo://10.230.11.211:20880/org.apache.dubbo.metadata.DemoService?timeout=1000&testKey=aaa");
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/DefaultServiceInstance.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/DefaultServiceInstance.java
index 6c88e8c..3d80c22 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/DefaultServiceInstance.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/DefaultServiceInstance.java
@@ -46,7 +46,7 @@ public class DefaultServiceInstance implements ServiceInstance {
 
     private Map<String, String> metadata = new HashMap<>();
 
-    private MetadataInfo serviceMetadata;
+    private transient MetadataInfo serviceMetadata;
 
     public DefaultServiceInstance() {
     }
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 48ceffd..92db129 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
@@ -23,7 +23,6 @@ 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.MetadataService;
 import org.apache.dubbo.metadata.ServiceNameMapping;
 import org.apache.dubbo.metadata.WritableMetadataService;
 import org.apache.dubbo.registry.NotifyListener;
@@ -33,7 +32,6 @@ import org.apache.dubbo.registry.client.metadata.SubscribedURLsSynthesizer;
 import org.apache.dubbo.registry.support.FailbackRegistry;
 
 import java.util.ArrayList;
-import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.LinkedHashMap;
@@ -49,9 +47,9 @@ import static java.util.Collections.emptySet;
 import static java.util.Collections.unmodifiableSet;
 import static java.util.stream.Collectors.toSet;
 import static java.util.stream.Stream.of;
-import static org.apache.dubbo.common.constants.CommonConstants.DUBBO_PROTOCOL;
 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.MAPPING_KEY;
 import static org.apache.dubbo.common.constants.CommonConstants.PROTOCOL_KEY;
 import static org.apache.dubbo.common.constants.CommonConstants.PROVIDER_SIDE;
 import static org.apache.dubbo.common.constants.CommonConstants.SIDE_KEY;
@@ -79,20 +77,6 @@ import static org.apache.dubbo.registry.client.ServiceDiscoveryFactory.getExtens
  * protocol associates with a kind of {@link ServiceDiscovery}'s implementation if present, or the
  * {@link FileSystemServiceDiscovery} will be the default one. Obviously, it's also allowed to extend
  * {@link ServiceDiscovery} using {@link SPI the Dubbo SPI}.
- * <p>
- * In the {@link #subscribe(URL, NotifyListener) subscription phase}, the {@link ServiceDiscovery} instance will be used
- * to discovery the {@link ServiceInstance service instances} via the {@link ServiceDiscovery#getInstances(String)}.
- * However, the argument of this method requires the service name that the subscribed {@link URL} can't find, thus,
- * {@link ServiceNameMapping} will help to figure out one or more services that exported correlative Dubbo services. If
- * the service names can be found, the exported {@link URL URLs} will be get from the remote {@link MetadataService}
- * being deployed on all {@link ServiceInstance instances} of services. The whole process runs under the
- * {@link #subscribeURLs(URL, NotifyListener, String, Collection)} method. It's very expensive to invoke
- * {@link MetadataService} for each {@link ServiceInstance service instance}, thus {@link ServiceDiscoveryRegistry}
- * introduces a cache to optimize the calculation with "revisions". If the revisions of N
- * {@link ServiceInstance service instances} are same, {@link MetadataService} is invoked just only once, and then it
- * does return the exported {@link URL URLs} as a template by which others are
- * {@link #cloneExportedURLs(URL, Collection) cloned}.
- * <p>
  * In contrast, current {@link ServiceInstance service instance} will not be registered to the registry whether any
  * Dubbo service is exported or not.
  * <p>
@@ -130,7 +114,7 @@ public class ServiceDiscoveryRegistry extends FailbackRegistry {
         super(registryURL);
         this.serviceDiscovery = createServiceDiscovery(registryURL);
         this.subscribedServices = parseServices(registryURL.getParameter(SUBSCRIBED_SERVICE_NAMES_KEY));
-        this.serviceNameMapping = ServiceNameMapping.getDefaultExtension();
+        this.serviceNameMapping = ServiceNameMapping.getExtension(registryURL.getParameter(MAPPING_KEY));
         this.writableMetadataService = WritableMetadataService.getDefaultExtension();
     }
 
@@ -303,6 +287,7 @@ public class ServiceDiscoveryRegistry extends FailbackRegistry {
                         listener.notifyServiceInstances();
                     }
                 });
+        serviceListener.setUrl(url);
         listener.addServiceListener(serviceListener);
         registerServiceInstancesChangedListener(url, serviceListener);
     }
@@ -392,11 +377,7 @@ public class ServiceDiscoveryRegistry extends FailbackRegistry {
      * @return
      */
     protected Set<String> findMappedServices(URL subscribedURL) {
-        String serviceInterface = subscribedURL.getServiceInterface();
-        String group = subscribedURL.getParameter(GROUP_KEY);
-        String version = subscribedURL.getParameter(VERSION_KEY);
-        String protocol = subscribedURL.getParameter(PROTOCOL_KEY, DUBBO_PROTOCOL);
-        return serviceNameMapping.get(serviceInterface, group, version, protocol);
+        return serviceNameMapping.get(subscribedURL);
     }
 
     /**
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 5f40b2a..3bd4042 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
@@ -53,7 +53,7 @@ public abstract class ServiceInstancesChangedListener implements ConditionalEven
 
     private final String serviceName;
     private final ServiceDiscovery serviceDiscovery;
-    private final URL url;
+    private URL url;
 
     private List<ServiceInstance> instances;
 
@@ -165,6 +165,10 @@ public abstract class ServiceInstancesChangedListener implements ConditionalEven
         return serviceName;
     }
 
+    public void setUrl(URL url) {
+        this.url = url;
+    }
+
     public URL getUrl() {
         return url;
     }
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
index 9cbe577..2685a4a 100644
--- 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
@@ -37,7 +37,6 @@ import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataU
 
 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<>();
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/store/InMemoryWritableMetadataService.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/store/InMemoryWritableMetadataService.java
index b9e7877..588c7ae 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/store/InMemoryWritableMetadataService.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/store/InMemoryWritableMetadataService.java
@@ -26,6 +26,7 @@ import org.apache.dubbo.metadata.MetadataService;
 import org.apache.dubbo.metadata.WritableMetadataService;
 import org.apache.dubbo.metadata.definition.ServiceDefinitionBuilder;
 import org.apache.dubbo.metadata.definition.model.ServiceDefinition;
+import org.apache.dubbo.rpc.model.ApplicationModel;
 import org.apache.dubbo.rpc.support.ProtocolUtils;
 
 import com.google.gson.Gson;
@@ -85,7 +86,7 @@ public class InMemoryWritableMetadataService implements WritableMetadataService
     ConcurrentNavigableMap<String, String> serviceDefinitions = new ConcurrentSkipListMap<>();
 
     public InMemoryWritableMetadataService() {
-        this.metadataInfo = new MetadataInfo();
+        this.metadataInfo = new MetadataInfo(ApplicationModel.getName());
     }
 
     @Override


[dubbo] 07/27: skip when metadata is null

Posted by li...@apache.org.
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

commit adffd9213448bd810b3d23e57f6f42a7ee29bffd
Author: ken.lj <ke...@gmail.com>
AuthorDate: Tue Jun 16 01:29:42 2020 +0800

    skip when metadata is null
---
 .../registry/client/DefaultServiceInstance.java    |  4 +++-
 .../dubbo/registry/client/InstanceAddressURL.java  |  4 ++--
 .../registry/client/ServiceDiscoveryRegistry.java  |  7 ++++++
 .../listener/ServiceInstancesChangedListener.java  | 27 +++++++++++++++++-----
 .../registry/integration/RegistryDirectory.java    |  2 +-
 5 files changed, 34 insertions(+), 10 deletions(-)

diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/DefaultServiceInstance.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/DefaultServiceInstance.java
index 3d80c22..b394024 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/DefaultServiceInstance.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/DefaultServiceInstance.java
@@ -140,7 +140,9 @@ public class DefaultServiceInstance implements ServiceInstance {
 
     @Override
     public URL toURL(String protocol, String path, String interfaceName, String group, String version, String serviceKey) {
-        return new InstanceAddressURL(protocol, host, port, path, interfaceName, group, version, serviceKey);
+        InstanceAddressURL url = new InstanceAddressURL(protocol, host, port, path, interfaceName, group, version, serviceKey);
+        url.setMetadata(this.getServiceMetadata());
+        return url;
     }
 
     @Override
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/InstanceAddressURL.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/InstanceAddressURL.java
index a4ece68..6ec4df2 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/InstanceAddressURL.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/InstanceAddressURL.java
@@ -72,7 +72,7 @@ public class InstanceAddressURL extends URL {
         }
 
         String value = super.getParameter(key);
-        if (StringUtils.isEmpty(value)) {
+        if (StringUtils.isEmpty(value) && metadataInfo != null) {
             value = metadataInfo.getParameter(key, this.getServiceKey());
         }
         return value;
@@ -112,7 +112,7 @@ public class InstanceAddressURL extends URL {
     @Override
     public Map<String, String> getParameters() {
         Map<String, String> instanceParams = super.getParameters();
-        Map<String, String> metadataParams = metadataInfo.getParameters(getServiceKey());
+        Map<String, String> metadataParams = (metadataInfo == null ? new HashMap<>() : metadataInfo.getParameters(getServiceKey()));
         int i = instanceParams == null ? 0 : instanceParams.size();
         int j = metadataParams == null ? 0 : metadataParams.size();
         Map<String, String> params = new HashMap<>((int) ((i + j) / 0.75) + 1);
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 92db129..878b9e6 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
@@ -27,6 +27,7 @@ import org.apache.dubbo.metadata.ServiceNameMapping;
 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.ServiceInstancesChangedEvent;
 import org.apache.dubbo.registry.client.event.listener.ServiceInstancesChangedListener;
 import org.apache.dubbo.registry.client.metadata.SubscribedURLsSynthesizer;
 import org.apache.dubbo.registry.support.FailbackRegistry;
@@ -289,6 +290,12 @@ public class ServiceDiscoveryRegistry extends FailbackRegistry {
                 });
         serviceListener.setUrl(url);
         listener.addServiceListener(serviceListener);
+
+        // FIXME, sync notification
+        List<ServiceInstance> serviceInstances = serviceDiscovery.getInstances(serviceName);
+        serviceListener.onEvent(new ServiceInstancesChangedEvent(serviceName, serviceInstances));
+        listener.notifyServiceInstances();
+
         registerServiceInstancesChangedListener(url, serviceListener);
     }
 
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 3bd4042..36365c1 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
@@ -17,12 +17,15 @@
 package org.apache.dubbo.registry.client.event.listener;
 
 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.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.registry.client.DefaultServiceInstance;
 import org.apache.dubbo.registry.client.ServiceDiscovery;
 import org.apache.dubbo.registry.client.ServiceInstance;
 import org.apache.dubbo.registry.client.event.ServiceInstancesChangedEvent;
@@ -51,6 +54,8 @@ import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataU
  */
 public abstract class ServiceInstancesChangedListener implements ConditionalEventListener<ServiceInstancesChangedEvent> {
 
+    private static final Logger logger = LoggerFactory.getLogger(ServiceInstancesChangedListener.class);
+
     private final String serviceName;
     private final ServiceDiscovery serviceDiscovery;
     private URL url;
@@ -87,14 +92,24 @@ public abstract class ServiceInstancesChangedListener implements ConditionalEven
             Collection<ServiceInstance> rInstances = localRevisionToInstances.computeIfAbsent(revision, r -> new ArrayList<>());
             rInstances.add(instance);
 
-            MetadataInfo metadata = revisionToMetadata.get(revision);
-            if (metadata != null) {
+            MetadataInfo metadata = null;
+            if (revisionToMetadata != null && ((metadata = revisionToMetadata.get(revision)) != null)) {
                 localRevisionToMetadata.put(revision, metadata);
             } else {
                 metadata = getMetadataInfo(instance);
-                localRevisionToMetadata.put(revision, getMetadataInfo(instance));
+                if (metadata != null) {
+                    localRevisionToMetadata.put(revision, getMetadataInfo(instance));
+                }
+            }
+
+            if (metadata != null) {
+                parse(revision, metadata, localServiceToRevisions);
+                ((DefaultServiceInstance) instance).setServiceMetadata(metadata);
+            } else {
+                logger.error("Failed to load service metadata for instance " + instance);
+                Set<String> set = localServiceToRevisions.computeIfAbsent(url.getServiceKey(), k -> new HashSet<>());
+                set.add(revision);
             }
-            parse(revision, metadata, localServiceToRevisions);
         }
 
         this.revisionToInstances = localRevisionToInstances;
@@ -126,8 +141,8 @@ public abstract class ServiceInstancesChangedListener implements ConditionalEven
 
     private Map<String, Set<String>> parse(String revision, MetadataInfo metadata, Map<String, Set<String>> localServiceToRevisions) {
         Map<String, ServiceInfo> serviceInfos = metadata.getServices();
-        for (Map.Entry<String, ServiceInfo> serviceInfo : serviceInfos.entrySet()) {
-            String serviceKey = serviceInfo.getValue().getServiceKey();
+        for (Map.Entry<String, ServiceInfo> entry : serviceInfos.entrySet()) {
+            String serviceKey = entry.getValue().getServiceKey();
             Set<String> set = localServiceToRevisions.computeIfAbsent(serviceKey, k -> new HashSet<>());
             set.add(revision);
         }
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 9953a28..54104e9 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
@@ -151,7 +151,7 @@ public class RegistryDirectory<T> extends AbstractDirectory<T> implements Notify
             throw new IllegalArgumentException("registry serviceKey is null.");
         }
         this.serviceType = serviceType;
-        this.serviceKey = url.getServiceKey();
+        this.serviceKey = super.getConsumerUrl().getServiceKey();
         this.queryMap = StringUtils.parseQueryString(url.getParameterAndDecoded(REFER_KEY));
         this.overrideDirectoryUrl = this.directoryUrl = turnRegistryUrlToConsumerUrl(url);
         String group = directoryUrl.getParameter(GROUP_KEY, "");


[dubbo] 25/27: migrating from interface address pool to instance address pool

Posted by li...@apache.org.
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

commit 5db015ccde960708decf19e07f40a1a6b4d95454
Author: ken.lj <ke...@gmail.com>
AuthorDate: Mon Aug 3 16:41:37 2020 +0800

    migrating from interface address pool to instance address pool
---
 .../apache/dubbo/rpc/cluster/ClusterInvoker.java   |    2 +
 .../org/apache/dubbo/rpc/cluster/Directory.java    |    2 +
 .../rpc/cluster/directory/AbstractDirectory.java   |    1 +
 .../cluster/support/AbstractClusterInvoker.java    |    5 +
 .../support/wrapper/MockClusterInvoker.java        |    5 +
 .../dubbo/common/constants/RegistryConstants.java  |    6 +
 .../org/apache/dubbo/config/ApplicationConfig.java |    8 +-
 .../org/apache/dubbo/rpc/model/ConsumerModel.java  |    6 +
 .../dubbo/config/utils/ConfigValidationUtils.java  |   30 +-
 .../src/main/resources/spring/dubbo-consumer.xml   |    4 +-
 .../DynamicConfigurationServiceNameMapping.java    |    2 +-
 .../apache/dubbo/metadata/MappingChangedEvent.java |    9 +
 .../org/apache/dubbo/metadata/MetadataInfo.java    |    7 +
 .../apache/dubbo/metadata/ServiceNameMapping.java  |    3 +-
 .../metadata/report/MetadataReportInstance.java    |   10 +
 .../org.apache.dubbo.metadata.ServiceNameMapping   |    3 +-
 ...che.dubbo.metadata.report.MetadataReportFactory |    1 -
 .../{integration => client}/RegistryProtocol.java  | 1655 ++++++++++----------
 .../registry/client/ServiceDiscoveryRegistry.java  |    2 +-
 .../client/ServiceDiscoveryRegistryProtocol.java   |   51 -
 .../ServiceDiscoveryRegistryProtocolListener.java  |    1 -
 .../listener/ServiceInstancesChangedListener.java  |    4 -
 .../metadata/MetadataServiceNameMapping.java       |   51 +-
 .../registry/client/metadata/MetadataUtils.java    |    2 +
 .../metadata/ServiceInstanceMetadataUtils.java     |    2 +-
 .../metadata/store/RemoteMetadataServiceImpl.java  |    2 +-
 .../registry/integration/DynamicDirectory.java     |    2 +-
 .../InterfaceCompatibleRegistryProtocol.java       |  177 +++
 .../registry/integration/RegistryDirectory.java    |    2 +-
 .../integration/RegistryInvokerWrapper.java        |   79 -
 .../integration/RegistryProtocolListener.java      |    1 +
 .../org.apache.dubbo.metadata.ServiceNameMapping   |    1 +
 .../dubbo/internal/org.apache.dubbo.rpc.Protocol   |    4 +-
 33 files changed, 1139 insertions(+), 1001 deletions(-)

diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/ClusterInvoker.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/ClusterInvoker.java
index 4b9199e..a89bdf4 100644
--- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/ClusterInvoker.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/ClusterInvoker.java
@@ -35,4 +35,6 @@ public interface ClusterInvoker<T> extends Invoker<T> {
     URL getRegistryUrl();
 
     Directory<T> getDirectory();
+
+    boolean isDestroyed();
 }
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/Directory.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/Directory.java
index 2940bf6..5d48264 100644
--- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/Directory.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/Directory.java
@@ -51,4 +51,6 @@ public interface Directory<T> extends Node {
 
     URL getConsumerUrl();
 
+    boolean isDestroyed();
+
 }
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/directory/AbstractDirectory.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/directory/AbstractDirectory.java
index 6b4773f..c663552 100644
--- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/directory/AbstractDirectory.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/directory/AbstractDirectory.java
@@ -113,6 +113,7 @@ public abstract class AbstractDirectory<T> implements Directory<T> {
         this.consumerUrl = consumerUrl;
     }
 
+    @Override
     public boolean isDestroyed() {
         return destroyed;
     }
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/AbstractClusterInvoker.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/AbstractClusterInvoker.java
index b1e8a95..4464763 100644
--- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/AbstractClusterInvoker.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/AbstractClusterInvoker.java
@@ -113,6 +113,11 @@ public abstract class AbstractClusterInvoker<T> implements ClusterInvoker<T> {
         }
     }
 
+    @Override
+    public boolean isDestroyed() {
+        return destroyed.get();
+    }
+
     /**
      * Select a invoker using loadbalance policy.</br>
      * a) Firstly, select an invoker using loadbalance. If this invoker is in previously selected list, or,
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/wrapper/MockClusterInvoker.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/wrapper/MockClusterInvoker.java
index 90c43bd..a682297 100644
--- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/wrapper/MockClusterInvoker.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/wrapper/MockClusterInvoker.java
@@ -64,6 +64,11 @@ public class MockClusterInvoker<T> implements ClusterInvoker<T> {
     }
 
     @Override
+    public boolean isDestroyed() {
+        return directory.isDestroyed();
+    }
+
+    @Override
     public boolean isAvailable() {
         return directory.isAvailable();
     }
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/constants/RegistryConstants.java b/dubbo-common/src/main/java/org/apache/dubbo/common/constants/RegistryConstants.java
index d25e9fc..3feeeb2 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/constants/RegistryConstants.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/constants/RegistryConstants.java
@@ -23,6 +23,8 @@ public interface RegistryConstants {
 
     String REGISTRY_CLUSTER_KEY = "REGISTRY_CLUSTER";
 
+    String REGISTRY_CLUSTER = "REGISTRY_CLUSTER";
+
     String REGISTRY_CLUSTER_TYPE_KEY = "registry-cluster-type";
 
     String REGISTRY_PROTOCOL = "registry";
@@ -57,6 +59,10 @@ public interface RegistryConstants {
 
     String COMPATIBLE_CONFIG_KEY = "compatible_config";
 
+    String REGISTRY_DUPLICATE_KEY = "duplicate";
+
+    String ENABLE_REGISTRY_DIRECTORY_AUTO_MIGRATION = "enable-auto-migration";
+
     /**
      * The parameter key of Dubbo Registry type
      *
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/ApplicationConfig.java b/dubbo-common/src/main/java/org/apache/dubbo/config/ApplicationConfig.java
index 460dbc6..7e54e42 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/config/ApplicationConfig.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/config/ApplicationConfig.java
@@ -464,7 +464,13 @@ public class ApplicationConfig extends AbstractConfig {
             for (InfraAdapter adapter : adapters) {
                 Map<String, String> extraParameters = adapter.getExtraAttributes(inputParameters);
                 if (CollectionUtils.isNotEmptyMap(extraParameters)) {
-                    parameters.putAll(extraParameters);
+                    extraParameters.forEach((key, value) -> {
+                        String prefix = this.getPrefix() + ".";
+                        if (key.startsWith(prefix)) {
+                            key = key.substring(prefix.length());
+                        }
+                        parameters.put(key, value);
+                    });
                 }
             }
         }
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/rpc/model/ConsumerModel.java b/dubbo-common/src/main/java/org/apache/dubbo/rpc/model/ConsumerModel.java
index e49888d..34fe276 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/rpc/model/ConsumerModel.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/rpc/model/ConsumerModel.java
@@ -28,6 +28,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.Optional;
 import java.util.Set;
+import java.util.TreeSet;
 
 /**
  * This model is bound to your reference's configuration, for example, group, version or method level configuration.
@@ -36,6 +37,7 @@ public class ConsumerModel {
     private String serviceKey;
     private final ServiceDescriptor serviceModel;
     private final ReferenceConfigBase<?> referenceConfig;
+    private final Set<String> apps = new TreeSet<>();
 
     private Object proxyObject;
 
@@ -110,6 +112,10 @@ public class ConsumerModel {
         return referenceConfig;
     }
 
+    public Set<String> getApps() {
+        return apps;
+    }
+
     public AsyncMethodInfo getAsyncInfo(String methodName) {
         return methodConfigs.get(methodName);
     }
diff --git a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/utils/ConfigValidationUtils.java b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/utils/ConfigValidationUtils.java
index 8a91d1c..13e5fb1 100644
--- a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/utils/ConfigValidationUtils.java
+++ b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/utils/ConfigValidationUtils.java
@@ -86,8 +86,10 @@ import static org.apache.dubbo.common.constants.CommonConstants.SHUTDOWN_WAIT_SE
 import static org.apache.dubbo.common.constants.CommonConstants.THREADPOOL_KEY;
 import static org.apache.dubbo.common.constants.CommonConstants.USERNAME_KEY;
 import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY;
+import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_DUPLICATE_KEY;
 import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_KEY;
 import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_PROTOCOL;
+import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_TYPE_KEY;
 import static org.apache.dubbo.common.constants.RegistryConstants.SERVICE_REGISTRY_PROTOCOL;
 import static org.apache.dubbo.common.constants.RemotingConstants.BACKUP_KEY;
 import static org.apache.dubbo.common.extension.ExtensionLoader.getExtensionLoader;
@@ -202,7 +204,33 @@ public class ConfigValidationUtils {
                 }
             }
         }
-        return registryList;
+        return genCompatibleRegistries(registryList, provider);
+    }
+
+    private static List<URL> genCompatibleRegistries(List<URL> registryList, boolean provider) {
+        List<URL> result = new ArrayList<>(registryList.size());
+        registryList.forEach(registryURL -> {
+            result.add(registryURL);
+            if (provider) {
+                // for registries enabled service discovery, automatically register interface compatible addresses.
+                if (SERVICE_REGISTRY_PROTOCOL.equals(registryURL.getProtocol())
+                        && registryURL.getParameter(REGISTRY_DUPLICATE_KEY, true)
+                        && registryNotExists(registryURL, registryList, REGISTRY_PROTOCOL)) {
+                    URL interfaceCompatibleRegistryURL = URLBuilder.from(registryURL)
+                            .setProtocol(REGISTRY_PROTOCOL)
+                            .removeParameter(REGISTRY_TYPE_KEY)
+                            .build();
+                    result.add(interfaceCompatibleRegistryURL);
+                }
+            }
+        });
+        return result;
+    }
+
+    private static boolean registryNotExists(URL registryURL, List<URL> registryList, String registryType) {
+        return registryList.stream().noneMatch(
+                url -> registryType.equals(url.getProtocol()) && registryURL.getBackupAddress().equals(url.getBackupAddress())
+        );
     }
 
     public static URL loadMonitor(AbstractInterfaceConfig interfaceConfig, URL registryURL) {
diff --git a/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-consumer/src/main/resources/spring/dubbo-consumer.xml b/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-consumer/src/main/resources/spring/dubbo-consumer.xml
index 44e8712..9225959 100644
--- a/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-consumer/src/main/resources/spring/dubbo-consumer.xml
+++ b/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-consumer/src/main/resources/spring/dubbo-consumer.xml
@@ -23,12 +23,12 @@
 
     <dubbo:application name="demo-consumer">
         <dubbo:parameter key="mapping-type" value="metadata"/>
+        <dubbo:parameter key="enable-auto-migration" value="true"/>
     </dubbo:application>
 
     <!--    <dubbo:metadata-report address="zookeeper://127.0.0.1:2181"/>-->
 
-    <dubbo:registry address="zookeeper://127.0.0.1:2181?registry-type=service"/>
-
+    <dubbo:registry address="zookeeper://127.0.0.1:2181"/>
 
     <dubbo:reference provided-by="demo-provider" id="demoService" check="false"
                      interface="org.apache.dubbo.demo.DemoService"/>
diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/DynamicConfigurationServiceNameMapping.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/DynamicConfigurationServiceNameMapping.java
index 21df199..12d6665 100644
--- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/DynamicConfigurationServiceNameMapping.java
+++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/DynamicConfigurationServiceNameMapping.java
@@ -72,7 +72,7 @@ public class DynamicConfigurationServiceNameMapping implements ServiceNameMappin
     }
 
     @Override
-    public Set<String> get(URL url, MappingListener mappingListener) {
+    public Set<String> getAndListen(URL url, MappingListener mappingListener) {
         String serviceInterface = url.getServiceInterface();
         String group = url.getParameter(GROUP_KEY);
         String version = url.getParameter(VERSION_KEY);
diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MappingChangedEvent.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MappingChangedEvent.java
index 27a7a3f..36b6275 100644
--- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MappingChangedEvent.java
+++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MappingChangedEvent.java
@@ -21,6 +21,7 @@ import java.util.Set;
 public class MappingChangedEvent {
     private String serviceKey;
     private Set<String> apps;
+    private Set<String> oldApps;
 
     public String getServiceKey() {
         return serviceKey;
@@ -37,4 +38,12 @@ public class MappingChangedEvent {
     public void setApps(Set<String> apps) {
         this.apps = apps;
     }
+
+    public Set<String> getOldApps() {
+        return oldApps;
+    }
+
+    public void setOldApps(Set<String> oldApps) {
+        this.oldApps = oldApps;
+    }
 }
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 47e8705..7ce4cfb 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
@@ -191,12 +191,15 @@ public class MetadataInfo implements Serializable {
         // service + group + version + protocol
         private transient String matchKey;
 
+        private URL url;
+
         public ServiceInfo() {
         }
 
         public ServiceInfo(URL url) {
             this(url.getServiceInterface(), url.getParameter(GROUP_KEY), url.getParameter(VERSION_KEY), url.getProtocol(), url.getPath(), null);
 
+            this.url = url;
             Map<String, String> params = new HashMap<>();
             List<MetadataParamsFilter> filters = loader.getActivateExtension(url, "params-filter");
             for (MetadataParamsFilter filter : filters) {
@@ -409,6 +412,10 @@ public class MetadataInfo implements Serializable {
             return methodNumbers;
         }
 
+        public URL getUrl() {
+            return url;
+        }
+
         @Override
         public boolean equals(Object obj) {
             if (obj == null) {
diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/ServiceNameMapping.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/ServiceNameMapping.java
index b4ad31e..f08a748 100644
--- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/ServiceNameMapping.java
+++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/ServiceNameMapping.java
@@ -44,8 +44,7 @@ public interface ServiceNameMapping {
      *
      * @return
      */
-    Set<String> get(URL url, MappingListener mappingListener);
-
+    Set<String> getAndListen(URL url, MappingListener mappingListener);
 
     /**
      * Get the default extension of {@link ServiceNameMapping}
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 c3afdb0..8d11784 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
@@ -65,6 +65,16 @@ public class MetadataReportInstance {
         return metadataReports;
     }
 
+    public static MetadataReport getMetadataReport(String registryKey) {
+        checkInit();
+        MetadataReport metadataReport = metadataReports.get(registryKey);
+        if (metadataReport == null) {
+            metadataReport = metadataReports.values().iterator().next();
+        }
+        return metadataReport;
+    }
+
+
     private static void checkInit() {
         if (!init.get()) {
             throw new IllegalStateException("the metadata report was not inited.");
diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metadata.ServiceNameMapping b/dubbo-metadata/dubbo-metadata-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metadata.ServiceNameMapping
index d43b342..b991bc3 100644
--- a/dubbo-metadata/dubbo-metadata-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metadata.ServiceNameMapping
+++ b/dubbo-metadata/dubbo-metadata-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metadata.ServiceNameMapping
@@ -1,2 +1 @@
-config=org.apache.dubbo.metadata.DynamicConfigurationServiceNameMapping
-metadata=org.apache.dubbo.metadata.MetadataServiceNameMapping
\ No newline at end of file
+config=org.apache.dubbo.metadata.DynamicConfigurationServiceNameMapping
\ No newline at end of file
diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metadata.report.MetadataReportFactory b/dubbo-metadata/dubbo-metadata-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metadata.report.MetadataReportFactory
deleted file mode 100644
index b686fce..0000000
--- a/dubbo-metadata/dubbo-metadata-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metadata.report.MetadataReportFactory
+++ /dev/null
@@ -1 +0,0 @@
-file = org.apache.dubbo.metadata.report.support.file.FileSystemMetadataReportFactory
\ No newline at end of file
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocol.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/RegistryProtocol.java
similarity index 92%
rename from dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocol.java
rename to dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/RegistryProtocol.java
index 976d0da..700062a 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocol.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/RegistryProtocol.java
@@ -1,830 +1,825 @@
-/*
- * 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.integration;
-
-import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.URLBuilder;
-import org.apache.dubbo.common.config.ConfigurationUtils;
-import org.apache.dubbo.common.config.configcenter.DynamicConfiguration;
-import org.apache.dubbo.common.extension.ExtensionLoader;
-import org.apache.dubbo.common.logger.Logger;
-import org.apache.dubbo.common.logger.LoggerFactory;
-import org.apache.dubbo.common.timer.HashedWheelTimer;
-import org.apache.dubbo.common.utils.CollectionUtils;
-import org.apache.dubbo.common.utils.NamedThreadFactory;
-import org.apache.dubbo.common.utils.StringUtils;
-import org.apache.dubbo.common.utils.UrlUtils;
-import org.apache.dubbo.registry.NotifyListener;
-import org.apache.dubbo.registry.Registry;
-import org.apache.dubbo.registry.RegistryFactory;
-import org.apache.dubbo.registry.RegistryService;
-import org.apache.dubbo.registry.retry.ReExportTask;
-import org.apache.dubbo.registry.support.SkipFailbackWrapperException;
-import org.apache.dubbo.rpc.Exporter;
-import org.apache.dubbo.rpc.Invoker;
-import org.apache.dubbo.rpc.Protocol;
-import org.apache.dubbo.rpc.ProtocolServer;
-import org.apache.dubbo.rpc.ProxyFactory;
-import org.apache.dubbo.rpc.RpcException;
-import org.apache.dubbo.rpc.cluster.Cluster;
-import org.apache.dubbo.rpc.cluster.Configurator;
-import org.apache.dubbo.rpc.cluster.governance.GovernanceRuleRepository;
-import org.apache.dubbo.rpc.cluster.support.MergeableCluster;
-import org.apache.dubbo.rpc.model.ApplicationModel;
-import org.apache.dubbo.rpc.model.ProviderModel;
-import org.apache.dubbo.rpc.protocol.InvokerWrapper;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.TimeUnit;
-
-import static java.util.concurrent.Executors.newSingleThreadExecutor;
-import static org.apache.dubbo.common.constants.CommonConstants.APPLICATION_KEY;
-import static org.apache.dubbo.common.constants.CommonConstants.CLUSTER_KEY;
-import static org.apache.dubbo.common.constants.CommonConstants.COMMA_SPLIT_PATTERN;
-import static org.apache.dubbo.common.constants.CommonConstants.DUBBO_VERSION_KEY;
-import static org.apache.dubbo.common.constants.CommonConstants.EXTRA_KEYS_KEY;
-import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY;
-import static org.apache.dubbo.common.constants.CommonConstants.HIDE_KEY_PREFIX;
-import static org.apache.dubbo.common.constants.CommonConstants.INTERFACE_KEY;
-import static org.apache.dubbo.common.constants.CommonConstants.LOADBALANCE_KEY;
-import static org.apache.dubbo.common.constants.CommonConstants.METHODS_KEY;
-import static org.apache.dubbo.common.constants.CommonConstants.MONITOR_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.common.constants.FilterConstants.VALIDATION_KEY;
-import static org.apache.dubbo.common.constants.QosConstants.ACCEPT_FOREIGN_IP;
-import static org.apache.dubbo.common.constants.QosConstants.QOS_ENABLE;
-import static org.apache.dubbo.common.constants.QosConstants.QOS_HOST;
-import static org.apache.dubbo.common.constants.QosConstants.QOS_PORT;
-import static org.apache.dubbo.common.constants.RegistryConstants.CATEGORY_KEY;
-import static org.apache.dubbo.common.constants.RegistryConstants.CONFIGURATORS_CATEGORY;
-import static org.apache.dubbo.common.constants.RegistryConstants.OVERRIDE_PROTOCOL;
-import static org.apache.dubbo.common.constants.RegistryConstants.PROVIDERS_CATEGORY;
-import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_KEY;
-import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_PROTOCOL;
-import static org.apache.dubbo.common.constants.RegistryConstants.ROUTERS_CATEGORY;
-import static org.apache.dubbo.common.utils.UrlUtils.classifyUrls;
-import static org.apache.dubbo.registry.Constants.CONFIGURATORS_SUFFIX;
-import static org.apache.dubbo.registry.Constants.CONSUMER_PROTOCOL;
-import static org.apache.dubbo.registry.Constants.DEFAULT_REGISTRY;
-import static org.apache.dubbo.registry.Constants.DEFAULT_REGISTRY_RETRY_PERIOD;
-import static org.apache.dubbo.registry.Constants.PROVIDER_PROTOCOL;
-import static org.apache.dubbo.registry.Constants.REGISTER_IP_KEY;
-import static org.apache.dubbo.registry.Constants.REGISTER_KEY;
-import static org.apache.dubbo.registry.Constants.REGISTRY_RETRY_PERIOD_KEY;
-import static org.apache.dubbo.registry.Constants.SIMPLIFIED_KEY;
-import static org.apache.dubbo.remoting.Constants.BIND_IP_KEY;
-import static org.apache.dubbo.remoting.Constants.BIND_PORT_KEY;
-import static org.apache.dubbo.remoting.Constants.CHECK_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.INTERFACES;
-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.EXPORT_KEY;
-import static org.apache.dubbo.rpc.cluster.Constants.REFER_KEY;
-import static org.apache.dubbo.rpc.cluster.Constants.WARMUP_KEY;
-import static org.apache.dubbo.rpc.cluster.Constants.WEIGHT_KEY;
-
-/**
- * RegistryProtocol
- */
-public class RegistryProtocol implements Protocol {
-    public static final String[] DEFAULT_REGISTER_PROVIDER_KEYS = {
-            APPLICATION_KEY, 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
-    };
-
-    public static final String[] DEFAULT_REGISTER_CONSUMER_KEYS = {
-            APPLICATION_KEY, VERSION_KEY, GROUP_KEY, DUBBO_VERSION_KEY, RELEASE_KEY
-    };
-
-    private final static Logger logger = LoggerFactory.getLogger(RegistryProtocol.class);
-    private final Map<URL, NotifyListener> overrideListeners = new ConcurrentHashMap<>();
-    private final Map<String, ServiceConfigurationListener> serviceConfigurationListeners = new ConcurrentHashMap<>();
-    private final ProviderConfigurationListener providerConfigurationListener = new ProviderConfigurationListener();
-    //To solve the problem of RMI repeated exposure port conflicts, the services that have been exposed are no longer exposed.
-    //providerurl <--> exporter
-    private final ConcurrentMap<String, ExporterChangeableWrapper<?>> bounds = new ConcurrentHashMap<>();
-    private Protocol protocol;
-    private RegistryFactory registryFactory;
-    private ProxyFactory proxyFactory;
-
-    private ConcurrentMap<URL, ReExportTask> reExportFailedTasks = new ConcurrentHashMap<>();
-    private HashedWheelTimer retryTimer = new HashedWheelTimer(new NamedThreadFactory("DubboReexportTimer", true), DEFAULT_REGISTRY_RETRY_PERIOD, TimeUnit.MILLISECONDS, 128);
-
-    //Filter the parameters that do not need to be output in url(Starting with .)
-    private static String[] getFilteredKeys(URL url) {
-        Map<String, String> params = url.getParameters();
-        if (CollectionUtils.isNotEmptyMap(params)) {
-            return params.keySet().stream()
-                    .filter(k -> k.startsWith(HIDE_KEY_PREFIX))
-                    .toArray(String[]::new);
-        } else {
-            return new String[0];
-        }
-    }
-
-    public void setProtocol(Protocol protocol) {
-        this.protocol = protocol;
-    }
-
-    public void setRegistryFactory(RegistryFactory registryFactory) {
-        this.registryFactory = registryFactory;
-    }
-
-    public void setProxyFactory(ProxyFactory proxyFactory) {
-        this.proxyFactory = proxyFactory;
-    }
-
-    @Override
-    public int getDefaultPort() {
-        return 9090;
-    }
-
-    public Map<URL, NotifyListener> getOverrideListeners() {
-        return overrideListeners;
-    }
-
-    private void register(URL registryUrl, URL registeredProviderUrl) {
-        Registry registry = registryFactory.getRegistry(registryUrl);
-        registry.register(registeredProviderUrl);
-    }
-
-    private void registerStatedUrl(URL registryUrl, URL registeredProviderUrl, boolean registered) {
-        ProviderModel model = ApplicationModel.getProviderModel(registeredProviderUrl.getServiceKey());
-        model.addStatedUrl(new ProviderModel.RegisterStatedURL(
-                registeredProviderUrl,
-                registryUrl,
-                registered));
-    }
-
-    @Override
-    public <T> Exporter<T> export(final Invoker<T> originInvoker) throws RpcException {
-        URL registryUrl = getRegistryUrl(originInvoker);
-        // url to export locally
-        URL providerUrl = getProviderUrl(originInvoker);
-
-        // Subscribe the override data
-        // FIXME When the provider subscribes, it will affect the scene : a certain JVM exposes the service and call
-        //  the same service. Because the subscribed is cached key with the name of the service, it causes the
-        //  subscription information to cover.
-        final URL overrideSubscribeUrl = getSubscribedOverrideUrl(providerUrl);
-        final OverrideListener overrideSubscribeListener = new OverrideListener(overrideSubscribeUrl, originInvoker);
-        overrideListeners.put(overrideSubscribeUrl, overrideSubscribeListener);
-
-        providerUrl = overrideUrlWithConfig(providerUrl, overrideSubscribeListener);
-        //export invoker
-        final ExporterChangeableWrapper<T> exporter = doLocalExport(originInvoker, providerUrl);
-
-        // url to registry
-        final Registry registry = getRegistry(originInvoker);
-        final URL registeredProviderUrl = getUrlToRegistry(providerUrl, registryUrl);
-
-        // decide if we need to delay publish
-        boolean register = providerUrl.getParameter(REGISTER_KEY, true);
-        if (register) {
-            register(registryUrl, registeredProviderUrl);
-        }
-
-        // register stated url on provider model
-        registerStatedUrl(registryUrl, registeredProviderUrl, register);
-
-
-        exporter.setRegisterUrl(registeredProviderUrl);
-        exporter.setSubscribeUrl(overrideSubscribeUrl);
-
-        // Deprecated! Subscribe to override rules in 2.6.x or before.
-        registry.subscribe(overrideSubscribeUrl, overrideSubscribeListener);
-
-        notifyExport(exporter);
-        //Ensure that a new exporter instance is returned every time export
-        return new DestroyableExporter<>(exporter);
-    }
-
-    private <T> void notifyExport(ExporterChangeableWrapper<T> exporter) {
-        List<RegistryProtocolListener> listeners = ExtensionLoader.getExtensionLoader(RegistryProtocolListener.class)
-                .getActivateExtension(exporter.getOriginInvoker().getUrl(), "registry.protocol.listener");
-        if (CollectionUtils.isNotEmpty(listeners)) {
-            for (RegistryProtocolListener listener : listeners) {
-                listener.onExport(this, exporter);
-            }
-        }
-    }
-
-    private URL overrideUrlWithConfig(URL providerUrl, OverrideListener listener) {
-        providerUrl = providerConfigurationListener.overrideUrl(providerUrl);
-        ServiceConfigurationListener serviceConfigurationListener = new ServiceConfigurationListener(providerUrl, listener);
-        serviceConfigurationListeners.put(providerUrl.getServiceKey(), serviceConfigurationListener);
-        return serviceConfigurationListener.overrideUrl(providerUrl);
-    }
-
-    @SuppressWarnings("unchecked")
-    private <T> ExporterChangeableWrapper<T> doLocalExport(final Invoker<T> originInvoker, URL providerUrl) {
-        String key = getCacheKey(originInvoker);
-
-        return (ExporterChangeableWrapper<T>) bounds.computeIfAbsent(key, s -> {
-            Invoker<?> invokerDelegate = new InvokerDelegate<>(originInvoker, providerUrl);
-            return new ExporterChangeableWrapper<>((Exporter<T>) protocol.export(invokerDelegate), originInvoker);
-        });
-    }
-
-    public <T> void reExport(Exporter<T> exporter, URL newInvokerUrl) {
-        if (exporter instanceof ExporterChangeableWrapper) {
-            ExporterChangeableWrapper<T> exporterWrapper = (ExporterChangeableWrapper<T>) exporter;
-            Invoker<T> originInvoker = exporterWrapper.getOriginInvoker();
-            reExport(originInvoker, newInvokerUrl);
-        }
-    }
-
-    /**
-     * Reexport the invoker of the modified url
-     *
-     * @param originInvoker
-     * @param newInvokerUrl
-     * @param <T>
-     */
-    @SuppressWarnings("unchecked")
-    public <T> void reExport(final Invoker<T> originInvoker, URL newInvokerUrl) {
-        String key = getCacheKey(originInvoker);
-        ExporterChangeableWrapper<T> exporter = (ExporterChangeableWrapper<T>) bounds.get(key);
-        URL registeredUrl = exporter.getRegisterUrl();
-
-        URL registryUrl = getRegistryUrl(originInvoker);
-        URL newProviderUrl = getUrlToRegistry(newInvokerUrl, registryUrl);
-
-        // update local exporter
-        Invoker<T> invokerDelegate = new InvokerDelegate<T>(originInvoker, newInvokerUrl);
-        exporter.setExporter(protocol.export(invokerDelegate));
-
-        // update registry
-        if (!newProviderUrl.equals(registeredUrl)) {
-            try {
-                doReExport(originInvoker, exporter, registryUrl, registeredUrl, newProviderUrl);
-            } catch (Exception e) {
-                ReExportTask oldTask = reExportFailedTasks.get(registeredUrl);
-                if (oldTask != null) {
-                    return;
-                }
-                ReExportTask task = new ReExportTask(
-                        () -> doReExport(originInvoker, exporter, registryUrl, registeredUrl, newProviderUrl),
-                        registeredUrl,
-                        null
-                );
-                oldTask = reExportFailedTasks.putIfAbsent(registeredUrl, task);
-                if (oldTask == null) {
-                    // never has a retry task. then start a new task for retry.
-                    retryTimer.newTimeout(task, registryUrl.getParameter(REGISTRY_RETRY_PERIOD_KEY, DEFAULT_REGISTRY_RETRY_PERIOD), TimeUnit.MILLISECONDS);
-                }
-            }
-        }
-    }
-
-    private <T> void doReExport(final Invoker<T> originInvoker, ExporterChangeableWrapper<T> exporter,
-                                URL registryUrl, URL oldProviderUrl, URL newProviderUrl) {
-        if (getProviderUrl(originInvoker).getParameter(REGISTER_KEY, true)) {
-            Registry registry = null;
-            try {
-                registry = getRegistry(originInvoker);
-            } catch (Exception e) {
-                throw new SkipFailbackWrapperException(e);
-            }
-
-            logger.info("Try to unregister old url: " + oldProviderUrl);
-            registry.reExportUnregister(oldProviderUrl);
-
-            logger.info("Try to register new url: " + newProviderUrl);
-            registry.reExportRegister(newProviderUrl);
-        }
-        try {
-            ProviderModel.RegisterStatedURL statedUrl = getStatedUrl(registryUrl, newProviderUrl);
-            statedUrl.setProviderUrl(newProviderUrl);
-            exporter.setRegisterUrl(newProviderUrl);
-        } catch (Exception e) {
-            throw new SkipFailbackWrapperException(e);
-        }
-    }
-
-    private ProviderModel.RegisterStatedURL getStatedUrl(URL registryUrl, URL providerUrl) {
-        ProviderModel providerModel = ApplicationModel.getServiceRepository()
-                .lookupExportedService(providerUrl.getServiceKey());
-
-        List<ProviderModel.RegisterStatedURL> statedUrls = providerModel.getStatedUrl();
-        return statedUrls.stream()
-                .filter(u -> u.getRegistryUrl().equals(registryUrl)
-                        && u.getProviderUrl().getProtocol().equals(providerUrl.getProtocol()))
-                .findFirst().orElseThrow(() -> new IllegalStateException("There should have at least one registered url."));
-    }
-
-    /**
-     * Get an instance of registry based on the address of invoker
-     *
-     * @param originInvoker
-     * @return
-     */
-    protected Registry getRegistry(final Invoker<?> originInvoker) {
-        URL registryUrl = getRegistryUrl(originInvoker);
-        return registryFactory.getRegistry(registryUrl);
-    }
-
-    protected URL getRegistryUrl(Invoker<?> originInvoker) {
-        URL registryUrl = originInvoker.getUrl();
-        if (REGISTRY_PROTOCOL.equals(registryUrl.getProtocol())) {
-            String protocol = registryUrl.getParameter(REGISTRY_KEY, DEFAULT_REGISTRY);
-            registryUrl = registryUrl.setProtocol(protocol).removeParameter(REGISTRY_KEY);
-        }
-        return registryUrl;
-    }
-
-    protected URL getRegistryUrl(URL url) {
-        return URLBuilder.from(url)
-                .setProtocol(url.getParameter(REGISTRY_KEY, DEFAULT_REGISTRY))
-                .removeParameter(REGISTRY_KEY)
-                .build();
-    }
-
-
-    /**
-     * Return the url that is registered to the registry and filter the url parameter once
-     *
-     * @param providerUrl
-     * @return url to registry.
-     */
-    private URL getUrlToRegistry(final URL providerUrl, final URL registryUrl) {
-        //The address you see at the registry
-        if (!registryUrl.getParameter(SIMPLIFIED_KEY, false)) {
-            return providerUrl.removeParameters(getFilteredKeys(providerUrl)).removeParameters(
-                    MONITOR_KEY, BIND_IP_KEY, BIND_PORT_KEY, QOS_ENABLE, QOS_HOST, QOS_PORT, ACCEPT_FOREIGN_IP, VALIDATION_KEY,
-                    INTERFACES);
-        } else {
-            String extraKeys = registryUrl.getParameter(EXTRA_KEYS_KEY, "");
-            // if path is not the same as interface name then we should keep INTERFACE_KEY,
-            // otherwise, the registry structure of zookeeper would be '/dubbo/path/providers',
-            // but what we expect is '/dubbo/interface/providers'
-            if (!providerUrl.getPath().equals(providerUrl.getParameter(INTERFACE_KEY))) {
-                if (StringUtils.isNotEmpty(extraKeys)) {
-                    extraKeys += ",";
-                }
-                extraKeys += INTERFACE_KEY;
-            }
-            String[] paramsToRegistry = getParamsToRegistry(DEFAULT_REGISTER_PROVIDER_KEYS
-                    , COMMA_SPLIT_PATTERN.split(extraKeys));
-            return URL.valueOf(providerUrl, paramsToRegistry, providerUrl.getParameter(METHODS_KEY, (String[]) null));
-        }
-
-    }
-
-    private URL getSubscribedOverrideUrl(URL registeredProviderUrl) {
-        return registeredProviderUrl.setProtocol(PROVIDER_PROTOCOL)
-                .addParameters(CATEGORY_KEY, CONFIGURATORS_CATEGORY, CHECK_KEY, String.valueOf(false));
-    }
-
-    /**
-     * Get the address of the providerUrl through the url of the invoker
-     *
-     * @param originInvoker
-     * @return
-     */
-    private URL getProviderUrl(final Invoker<?> originInvoker) {
-        String export = originInvoker.getUrl().getParameterAndDecoded(EXPORT_KEY);
-        if (export == null || export.length() == 0) {
-            throw new IllegalArgumentException("The registry export url is null! registry: " + originInvoker.getUrl());
-        }
-        return URL.valueOf(export);
-    }
-
-    /**
-     * Get the key cached in bounds by invoker
-     *
-     * @param originInvoker
-     * @return
-     */
-    private String getCacheKey(final Invoker<?> originInvoker) {
-        URL providerUrl = getProviderUrl(originInvoker);
-        String key = providerUrl.removeParameters("dynamic", "enabled").toFullString();
-        return key;
-    }
-
-    @Override
-    @SuppressWarnings("unchecked")
-    public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
-        url = getRegistryUrl(url);
-        Registry registry = registryFactory.getRegistry(url);
-        if (RegistryService.class.equals(type)) {
-            return proxyFactory.getInvoker((T) registry, type, url);
-        }
-
-        // group="a,b" or group="*"
-        Map<String, String> qs = StringUtils.parseQueryString(url.getParameterAndDecoded(REFER_KEY));
-        String group = qs.get(GROUP_KEY);
-        if (group != null && group.length() > 0) {
-            if ((COMMA_SPLIT_PATTERN.split(group)).length > 1 || "*".equals(group)) {
-                return doRefer(Cluster.getCluster(MergeableCluster.NAME), registry, type, url);
-            }
-        }
-
-        Cluster cluster = Cluster.getCluster(qs.get(CLUSTER_KEY));
-        return doRefer(cluster, registry, type, url);
-    }
-
-    protected <T> Invoker<T> doRefer(Cluster cluster, Registry registry, Class<T> type, URL url) {
-        // FIXME, SPI extension, support prototype instance
-        DynamicDirectory<T> directory = createDirectory(type, url);
-        directory.setRegistry(registry);
-        directory.setProtocol(protocol);
-        // all attributes of REFER_KEY
-        Map<String, String> parameters = new HashMap<String, String>(directory.getConsumerUrl().getParameters());
-        URL subscribeUrl = new URL(CONSUMER_PROTOCOL, parameters.remove(REGISTER_IP_KEY), 0, type.getName(), parameters);
-        if (directory.isShouldRegister()) {
-            directory.setRegisteredConsumerUrl(subscribeUrl);
-            registry.register(directory.getRegisteredConsumerUrl());
-        }
-        directory.buildRouterChain(subscribeUrl);
-        directory.subscribe(toSubscribeUrl(subscribeUrl));
-
-        Invoker<T> invoker = cluster.join(directory);
-        List<RegistryProtocolListener> listeners = findRegistryProtocolListeners(url);
-        if (CollectionUtils.isEmpty(listeners)) {
-            return invoker;
-        }
-
-        RegistryInvokerWrapper<T> registryInvokerWrapper = new RegistryInvokerWrapper<>(directory, cluster, invoker);
-        for (RegistryProtocolListener listener : listeners) {
-            listener.onRefer(this, registryInvokerWrapper);
-        }
-        return registryInvokerWrapper;
-    }
-
-    protected <T> DynamicDirectory<T> createDirectory(Class<T> type, URL url) {
-        return new RegistryDirectory<T>(type, url);
-    }
-
-    public <T> void reRefer(Invoker<T> invoker, URL newSubscribeUrl) {
-        if (!(invoker instanceof RegistryInvokerWrapper)) {
-            return;
-        }
-
-        RegistryInvokerWrapper<T> invokerWrapper = (RegistryInvokerWrapper<T>) invoker;
-        URL oldSubscribeUrl = invokerWrapper.getUrl();
-        DynamicDirectory<T> directory = invokerWrapper.getDirectory();
-        Registry registry = directory.getRegistry();
-        registry.unregister(directory.getRegisteredConsumerUrl());
-        directory.unSubscribe(toSubscribeUrl(oldSubscribeUrl));
-
-        directory.setRegisteredConsumerUrl(newSubscribeUrl);
-        registry.register(directory.getRegisteredConsumerUrl());
-        directory.buildRouterChain(newSubscribeUrl);
-        directory.subscribe(toSubscribeUrl(newSubscribeUrl));
-
-        invokerWrapper.setInvoker(invokerWrapper.getCluster().join(directory));
-    }
-
-    private static URL toSubscribeUrl(URL url) {
-        return url.addParameter(CATEGORY_KEY, PROVIDERS_CATEGORY + "," + CONFIGURATORS_CATEGORY + "," + ROUTERS_CATEGORY);
-    }
-
-    private List<RegistryProtocolListener> findRegistryProtocolListeners(URL url) {
-        return ExtensionLoader.getExtensionLoader(RegistryProtocolListener.class)
-                .getActivateExtension(url, "registry.protocol.listener");
-    }
-
-    // available to test
-    public String[] getParamsToRegistry(String[] defaultKeys, String[] additionalParameterKeys) {
-        int additionalLen = additionalParameterKeys.length;
-        String[] registryParams = new String[defaultKeys.length + additionalLen];
-        System.arraycopy(defaultKeys, 0, registryParams, 0, defaultKeys.length);
-        System.arraycopy(additionalParameterKeys, 0, registryParams, defaultKeys.length, additionalLen);
-        return registryParams;
-    }
-
-    @Override
-    public void destroy() {
-        List<RegistryProtocolListener> listeners = ExtensionLoader.getExtensionLoader(RegistryProtocolListener.class)
-                .getLoadedExtensionInstances();
-        if (CollectionUtils.isNotEmpty(listeners)) {
-            for (RegistryProtocolListener listener : listeners) {
-                listener.onDestroy();
-            }
-        }
-
-        List<Exporter<?>> exporters = new ArrayList<Exporter<?>>(bounds.values());
-        for (Exporter<?> exporter : exporters) {
-            exporter.unexport();
-        }
-        bounds.clear();
-
-        ExtensionLoader.getExtensionLoader(GovernanceRuleRepository.class).getDefaultExtension()
-                .removeListener(ApplicationModel.getApplication() + CONFIGURATORS_SUFFIX, providerConfigurationListener);
-    }
-
-    @Override
-    public List<ProtocolServer> getServers() {
-        return protocol.getServers();
-    }
-
-    //Merge the urls of configurators
-    private static URL getConfigedInvokerUrl(List<Configurator> configurators, URL url) {
-        if (configurators != null && configurators.size() > 0) {
-            for (Configurator configurator : configurators) {
-                url = configurator.configure(url);
-            }
-        }
-        return url;
-    }
-
-    public static class InvokerDelegate<T> extends InvokerWrapper<T> {
-        private final Invoker<T> invoker;
-
-        /**
-         * @param invoker
-         * @param url     invoker.getUrl return this value
-         */
-        public InvokerDelegate(Invoker<T> invoker, URL url) {
-            super(invoker, url);
-            this.invoker = invoker;
-        }
-
-        public Invoker<T> getInvoker() {
-            if (invoker instanceof InvokerDelegate) {
-                return ((InvokerDelegate<T>) invoker).getInvoker();
-            } else {
-                return invoker;
-            }
-        }
-    }
-
-    private static class DestroyableExporter<T> implements Exporter<T> {
-
-        private Exporter<T> exporter;
-
-        public DestroyableExporter(Exporter<T> exporter) {
-            this.exporter = exporter;
-        }
-
-        @Override
-        public Invoker<T> getInvoker() {
-            return exporter.getInvoker();
-        }
-
-        @Override
-        public void unexport() {
-            exporter.unexport();
-        }
-    }
-
-    /**
-     * Reexport: the exporter destroy problem in protocol
-     * 1.Ensure that the exporter returned by registryprotocol can be normal destroyed
-     * 2.No need to re-register to the registry after notify
-     * 3.The invoker passed by the export method , would better to be the invoker of exporter
-     */
-    private class OverrideListener implements NotifyListener {
-        private final URL subscribeUrl;
-        private final Invoker originInvoker;
-
-
-        private List<Configurator> configurators;
-
-        public OverrideListener(URL subscribeUrl, Invoker originalInvoker) {
-            this.subscribeUrl = subscribeUrl;
-            this.originInvoker = originalInvoker;
-        }
-
-        /**
-         * @param urls The list of registered information, is always not empty, The meaning is the same as the
-         *             return value of {@link org.apache.dubbo.registry.RegistryService#lookup(URL)}.
-         */
-        @Override
-        public synchronized void notify(List<URL> urls) {
-            logger.debug("original override urls: " + urls);
-
-            List<URL> matchedUrls = getMatchedUrls(urls, subscribeUrl.addParameter(CATEGORY_KEY,
-                    CONFIGURATORS_CATEGORY));
-            logger.debug("subscribe url: " + subscribeUrl + ", override urls: " + matchedUrls);
-
-            // No matching results
-            if (matchedUrls.isEmpty()) {
-                return;
-            }
-
-            this.configurators = Configurator.toConfigurators(classifyUrls(matchedUrls, UrlUtils::isConfigurator))
-                    .orElse(configurators);
-
-            doOverrideIfNecessary();
-        }
-
-        public synchronized void doOverrideIfNecessary() {
-            final Invoker<?> invoker;
-            if (originInvoker instanceof InvokerDelegate) {
-                invoker = ((InvokerDelegate<?>) originInvoker).getInvoker();
-            } else {
-                invoker = originInvoker;
-            }
-            //The origin invoker
-            URL originUrl = RegistryProtocol.this.getProviderUrl(invoker);
-            String key = getCacheKey(originInvoker);
-            ExporterChangeableWrapper<?> exporter = bounds.get(key);
-            if (exporter == null) {
-                logger.warn(new IllegalStateException("error state, exporter should not be null"));
-                return;
-            }
-            //The current, may have been merged many times
-            URL currentUrl = exporter.getInvoker().getUrl();
-            //Merged with this configuration
-            URL newUrl = getConfigedInvokerUrl(configurators, currentUrl);
-            newUrl = getConfigedInvokerUrl(providerConfigurationListener.getConfigurators(), newUrl);
-            newUrl = getConfigedInvokerUrl(serviceConfigurationListeners.get(originUrl.getServiceKey())
-                    .getConfigurators(), newUrl);
-            if (!currentUrl.equals(newUrl)) {
-                RegistryProtocol.this.reExport(originInvoker, newUrl);
-                logger.info("exported provider url changed, origin url: " + originUrl +
-                        ", old export url: " + currentUrl + ", new export url: " + newUrl);
-            }
-        }
-
-        private List<URL> getMatchedUrls(List<URL> configuratorUrls, URL currentSubscribe) {
-            List<URL> result = new ArrayList<URL>();
-            for (URL url : configuratorUrls) {
-                URL overrideUrl = url;
-                // Compatible with the old version
-                if (url.getParameter(CATEGORY_KEY) == null && OVERRIDE_PROTOCOL.equals(url.getProtocol())) {
-                    overrideUrl = url.addParameter(CATEGORY_KEY, CONFIGURATORS_CATEGORY);
-                }
-
-                // Check whether url is to be applied to the current service
-                if (UrlUtils.isMatch(currentSubscribe, overrideUrl)) {
-                    result.add(url);
-                }
-            }
-            return result;
-        }
-    }
-
-    private class ServiceConfigurationListener extends AbstractConfiguratorListener {
-        private URL providerUrl;
-        private OverrideListener notifyListener;
-
-        public ServiceConfigurationListener(URL providerUrl, OverrideListener notifyListener) {
-            this.providerUrl = providerUrl;
-            this.notifyListener = notifyListener;
-            this.initWith(DynamicConfiguration.getRuleKey(providerUrl) + CONFIGURATORS_SUFFIX);
-        }
-
-        private <T> URL overrideUrl(URL providerUrl) {
-            return RegistryProtocol.getConfigedInvokerUrl(configurators, providerUrl);
-        }
-
-        @Override
-        protected void notifyOverrides() {
-            notifyListener.doOverrideIfNecessary();
-        }
-    }
-
-    private class ProviderConfigurationListener extends AbstractConfiguratorListener {
-
-        public ProviderConfigurationListener() {
-            this.initWith(ApplicationModel.getApplication() + CONFIGURATORS_SUFFIX);
-        }
-
-        /**
-         * Get existing configuration rule and override provider url before exporting.
-         *
-         * @param providerUrl
-         * @param <T>
-         * @return
-         */
-        private <T> URL overrideUrl(URL providerUrl) {
-            return RegistryProtocol.getConfigedInvokerUrl(configurators, providerUrl);
-        }
-
-        @Override
-        protected void notifyOverrides() {
-            overrideListeners.values().forEach(listener -> ((OverrideListener) listener).doOverrideIfNecessary());
-        }
-    }
-
-    /**
-     * exporter proxy, establish the corresponding relationship between the returned exporter and the exporter
-     * exported by the protocol, and can modify the relationship at the time of override.
-     *
-     * @param <T>
-     */
-    private class ExporterChangeableWrapper<T> implements Exporter<T> {
-
-        private final ExecutorService executor = newSingleThreadExecutor(new NamedThreadFactory("Exporter-Unexport", true));
-
-        private final Invoker<T> originInvoker;
-        private Exporter<T> exporter;
-        private URL subscribeUrl;
-        private URL registerUrl;
-
-        public ExporterChangeableWrapper(Exporter<T> exporter, Invoker<T> originInvoker) {
-            this.exporter = exporter;
-            this.originInvoker = originInvoker;
-        }
-
-        public Invoker<T> getOriginInvoker() {
-            return originInvoker;
-        }
-
-        @Override
-        public Invoker<T> getInvoker() {
-            return exporter.getInvoker();
-        }
-
-        public void setExporter(Exporter<T> exporter) {
-            this.exporter = exporter;
-        }
-
-        @Override
-        public void unexport() {
-            String key = getCacheKey(this.originInvoker);
-            bounds.remove(key);
-
-            Registry registry = RegistryProtocol.this.getRegistry(originInvoker);
-            try {
-                registry.unregister(registerUrl);
-            } catch (Throwable t) {
-                logger.warn(t.getMessage(), t);
-            }
-            try {
-                NotifyListener listener = RegistryProtocol.this.overrideListeners.remove(subscribeUrl);
-                registry.unsubscribe(subscribeUrl, listener);
-                ExtensionLoader.getExtensionLoader(GovernanceRuleRepository.class).getDefaultExtension()
-                        .removeListener(subscribeUrl.getServiceKey() + CONFIGURATORS_SUFFIX,
-                                serviceConfigurationListeners.get(subscribeUrl.getServiceKey()));
-            } catch (Throwable t) {
-                logger.warn(t.getMessage(), t);
-            }
-
-            executor.submit(() -> {
-                try {
-                    int timeout = ConfigurationUtils.getServerShutdownTimeout();
-                    if (timeout > 0) {
-                        logger.info("Waiting " + timeout + "ms for registry to notify all consumers before unexport. " +
-                                "Usually, this is called when you use dubbo API");
-                        Thread.sleep(timeout);
-                    }
-                    exporter.unexport();
-                } catch (Throwable t) {
-                    logger.warn(t.getMessage(), t);
-                }
-            });
-        }
-
-        public void setSubscribeUrl(URL subscribeUrl) {
-            this.subscribeUrl = subscribeUrl;
-        }
-
-        public void setRegisterUrl(URL registerUrl) {
-            this.registerUrl = registerUrl;
-        }
-
-        public URL getRegisterUrl() {
-            return registerUrl;
-        }
-    }
-
-    // for unit test
-    private static RegistryProtocol INSTANCE;
-
-    // for unit test
-    public RegistryProtocol() {
-        INSTANCE = this;
-    }
-
-    // for unit test
-    public static RegistryProtocol getRegistryProtocol() {
-        if (INSTANCE == null) {
-            ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(REGISTRY_PROTOCOL); // load
-        }
-        return INSTANCE;
-    }
-}
+/*
+ * 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;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.config.ConfigurationUtils;
+import org.apache.dubbo.common.config.configcenter.DynamicConfiguration;
+import org.apache.dubbo.common.extension.ExtensionLoader;
+import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.common.timer.HashedWheelTimer;
+import org.apache.dubbo.common.utils.CollectionUtils;
+import org.apache.dubbo.common.utils.NamedThreadFactory;
+import org.apache.dubbo.common.utils.StringUtils;
+import org.apache.dubbo.common.utils.UrlUtils;
+import org.apache.dubbo.registry.NotifyListener;
+import org.apache.dubbo.registry.Registry;
+import org.apache.dubbo.registry.RegistryFactory;
+import org.apache.dubbo.registry.RegistryService;
+import org.apache.dubbo.registry.integration.AbstractConfiguratorListener;
+import org.apache.dubbo.registry.integration.DynamicDirectory;
+import org.apache.dubbo.registry.integration.InterfaceCompatibleRegistryProtocol;
+import org.apache.dubbo.registry.integration.RegistryProtocolListener;
+import org.apache.dubbo.registry.retry.ReExportTask;
+import org.apache.dubbo.registry.support.SkipFailbackWrapperException;
+import org.apache.dubbo.rpc.Exporter;
+import org.apache.dubbo.rpc.Invoker;
+import org.apache.dubbo.rpc.Protocol;
+import org.apache.dubbo.rpc.ProtocolServer;
+import org.apache.dubbo.rpc.ProxyFactory;
+import org.apache.dubbo.rpc.RpcException;
+import org.apache.dubbo.rpc.cluster.Cluster;
+import org.apache.dubbo.rpc.cluster.ClusterInvoker;
+import org.apache.dubbo.rpc.cluster.Configurator;
+import org.apache.dubbo.rpc.cluster.governance.GovernanceRuleRepository;
+import org.apache.dubbo.rpc.cluster.support.MergeableCluster;
+import org.apache.dubbo.rpc.model.ApplicationModel;
+import org.apache.dubbo.rpc.model.ProviderModel;
+import org.apache.dubbo.rpc.protocol.InvokerWrapper;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.TimeUnit;
+
+import static java.util.concurrent.Executors.newSingleThreadExecutor;
+import static org.apache.dubbo.common.constants.CommonConstants.APPLICATION_KEY;
+import static org.apache.dubbo.common.constants.CommonConstants.CLUSTER_KEY;
+import static org.apache.dubbo.common.constants.CommonConstants.COMMA_SPLIT_PATTERN;
+import static org.apache.dubbo.common.constants.CommonConstants.DUBBO_VERSION_KEY;
+import static org.apache.dubbo.common.constants.CommonConstants.EXTRA_KEYS_KEY;
+import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY;
+import static org.apache.dubbo.common.constants.CommonConstants.HIDE_KEY_PREFIX;
+import static org.apache.dubbo.common.constants.CommonConstants.INTERFACE_KEY;
+import static org.apache.dubbo.common.constants.CommonConstants.LOADBALANCE_KEY;
+import static org.apache.dubbo.common.constants.CommonConstants.METHODS_KEY;
+import static org.apache.dubbo.common.constants.CommonConstants.MONITOR_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.common.constants.FilterConstants.VALIDATION_KEY;
+import static org.apache.dubbo.common.constants.QosConstants.ACCEPT_FOREIGN_IP;
+import static org.apache.dubbo.common.constants.QosConstants.QOS_ENABLE;
+import static org.apache.dubbo.common.constants.QosConstants.QOS_HOST;
+import static org.apache.dubbo.common.constants.QosConstants.QOS_PORT;
+import static org.apache.dubbo.common.constants.RegistryConstants.CATEGORY_KEY;
+import static org.apache.dubbo.common.constants.RegistryConstants.CONFIGURATORS_CATEGORY;
+import static org.apache.dubbo.common.constants.RegistryConstants.OVERRIDE_PROTOCOL;
+import static org.apache.dubbo.common.constants.RegistryConstants.PROVIDERS_CATEGORY;
+import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_KEY;
+import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_PROTOCOL;
+import static org.apache.dubbo.common.constants.RegistryConstants.ROUTERS_CATEGORY;
+import static org.apache.dubbo.common.constants.RegistryConstants.SERVICE_REGISTRY_PROTOCOL;
+import static org.apache.dubbo.common.utils.UrlUtils.classifyUrls;
+import static org.apache.dubbo.registry.Constants.CONFIGURATORS_SUFFIX;
+import static org.apache.dubbo.registry.Constants.CONSUMER_PROTOCOL;
+import static org.apache.dubbo.registry.Constants.DEFAULT_REGISTRY_RETRY_PERIOD;
+import static org.apache.dubbo.registry.Constants.PROVIDER_PROTOCOL;
+import static org.apache.dubbo.registry.Constants.REGISTER_IP_KEY;
+import static org.apache.dubbo.registry.Constants.REGISTER_KEY;
+import static org.apache.dubbo.registry.Constants.REGISTRY_RETRY_PERIOD_KEY;
+import static org.apache.dubbo.registry.Constants.SIMPLIFIED_KEY;
+import static org.apache.dubbo.remoting.Constants.BIND_IP_KEY;
+import static org.apache.dubbo.remoting.Constants.BIND_PORT_KEY;
+import static org.apache.dubbo.remoting.Constants.CHECK_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.INTERFACES;
+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.EXPORT_KEY;
+import static org.apache.dubbo.rpc.cluster.Constants.REFER_KEY;
+import static org.apache.dubbo.rpc.cluster.Constants.WARMUP_KEY;
+import static org.apache.dubbo.rpc.cluster.Constants.WEIGHT_KEY;
+
+/**
+ * TODO, replace RegistryProtocol completely in the future.
+ */
+public class RegistryProtocol implements Protocol {
+    public static final String[] DEFAULT_REGISTER_PROVIDER_KEYS = {
+            APPLICATION_KEY, 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
+    };
+
+    public static final String[] DEFAULT_REGISTER_CONSUMER_KEYS = {
+            APPLICATION_KEY, VERSION_KEY, GROUP_KEY, DUBBO_VERSION_KEY, RELEASE_KEY
+    };
+
+    private final static Logger logger = LoggerFactory.getLogger(InterfaceCompatibleRegistryProtocol.class);
+    private final Map<URL, NotifyListener> overrideListeners = new ConcurrentHashMap<>();
+    private final Map<String, ServiceConfigurationListener> serviceConfigurationListeners = new ConcurrentHashMap<>();
+    private final ProviderConfigurationListener providerConfigurationListener = new ProviderConfigurationListener();
+    //To solve the problem of RMI repeated exposure port conflicts, the services that have been exposed are no longer exposed.
+    //providerurl <--> exporter
+    private final ConcurrentMap<String, ExporterChangeableWrapper<?>> bounds = new ConcurrentHashMap<>();
+    protected Protocol protocol;
+    protected RegistryFactory registryFactory;
+    protected ProxyFactory proxyFactory;
+
+    private ConcurrentMap<URL, ReExportTask> reExportFailedTasks = new ConcurrentHashMap<>();
+    private HashedWheelTimer retryTimer = new HashedWheelTimer(new NamedThreadFactory("DubboReexportTimer", true), DEFAULT_REGISTRY_RETRY_PERIOD, TimeUnit.MILLISECONDS, 128);
+
+    //Filter the parameters that do not need to be output in url(Starting with .)
+    private static String[] getFilteredKeys(URL url) {
+        Map<String, String> params = url.getParameters();
+        if (CollectionUtils.isNotEmptyMap(params)) {
+            return params.keySet().stream()
+                    .filter(k -> k.startsWith(HIDE_KEY_PREFIX))
+                    .toArray(String[]::new);
+        } else {
+            return new String[0];
+        }
+    }
+
+    public void setProtocol(Protocol protocol) {
+        this.protocol = protocol;
+    }
+
+    public void setRegistryFactory(RegistryFactory registryFactory) {
+        this.registryFactory = registryFactory;
+    }
+
+    public void setProxyFactory(ProxyFactory proxyFactory) {
+        this.proxyFactory = proxyFactory;
+    }
+
+    @Override
+    public int getDefaultPort() {
+        return 9090;
+    }
+
+    public Map<URL, NotifyListener> getOverrideListeners() {
+        return overrideListeners;
+    }
+
+    private void register(URL registryUrl, URL registeredProviderUrl) {
+        Registry registry = registryFactory.getRegistry(registryUrl);
+        registry.register(registeredProviderUrl);
+    }
+
+    private void registerStatedUrl(URL registryUrl, URL registeredProviderUrl, boolean registered) {
+        ProviderModel model = ApplicationModel.getProviderModel(registeredProviderUrl.getServiceKey());
+        model.addStatedUrl(new ProviderModel.RegisterStatedURL(
+                registeredProviderUrl,
+                registryUrl,
+                registered));
+    }
+
+    @Override
+    public <T> Exporter<T> export(final Invoker<T> originInvoker) throws RpcException {
+        URL registryUrl = getRegistryUrl(originInvoker);
+        // url to export locally
+        URL providerUrl = getProviderUrl(originInvoker);
+
+        // Subscribe the override data
+        // FIXME When the provider subscribes, it will affect the scene : a certain JVM exposes the service and call
+        //  the same service. Because the subscribed is cached key with the name of the service, it causes the
+        //  subscription information to cover.
+        final URL overrideSubscribeUrl = getSubscribedOverrideUrl(providerUrl);
+        final OverrideListener overrideSubscribeListener = new OverrideListener(overrideSubscribeUrl, originInvoker);
+        overrideListeners.put(overrideSubscribeUrl, overrideSubscribeListener);
+
+        providerUrl = overrideUrlWithConfig(providerUrl, overrideSubscribeListener);
+        //export invoker
+        final ExporterChangeableWrapper<T> exporter = doLocalExport(originInvoker, providerUrl);
+
+        // url to registry
+        final Registry registry = getRegistry(originInvoker);
+        final URL registeredProviderUrl = getUrlToRegistry(providerUrl, registryUrl);
+
+        // decide if we need to delay publish
+        boolean register = providerUrl.getParameter(REGISTER_KEY, true);
+        if (register) {
+            register(registryUrl, registeredProviderUrl);
+        }
+
+        // register stated url on provider model
+        registerStatedUrl(registryUrl, registeredProviderUrl, register);
+
+
+        exporter.setRegisterUrl(registeredProviderUrl);
+        exporter.setSubscribeUrl(overrideSubscribeUrl);
+
+        // Deprecated! Subscribe to override rules in 2.6.x or before.
+        registry.subscribe(overrideSubscribeUrl, overrideSubscribeListener);
+
+        notifyExport(exporter);
+        //Ensure that a new exporter instance is returned every time export
+        return new DestroyableExporter<>(exporter);
+    }
+
+    private <T> void notifyExport(ExporterChangeableWrapper<T> exporter) {
+        List<RegistryProtocolListener> listeners = ExtensionLoader.getExtensionLoader(RegistryProtocolListener.class)
+                .getActivateExtension(exporter.getOriginInvoker().getUrl(), "registry.protocol.listener");
+        if (CollectionUtils.isNotEmpty(listeners)) {
+            for (RegistryProtocolListener listener : listeners) {
+                listener.onExport(this, exporter);
+            }
+        }
+    }
+
+    private URL overrideUrlWithConfig(URL providerUrl, OverrideListener listener) {
+        providerUrl = providerConfigurationListener.overrideUrl(providerUrl);
+        ServiceConfigurationListener serviceConfigurationListener = new ServiceConfigurationListener(providerUrl, listener);
+        serviceConfigurationListeners.put(providerUrl.getServiceKey(), serviceConfigurationListener);
+        return serviceConfigurationListener.overrideUrl(providerUrl);
+    }
+
+    @SuppressWarnings("unchecked")
+    private <T> ExporterChangeableWrapper<T> doLocalExport(final Invoker<T> originInvoker, URL providerUrl) {
+        String key = getCacheKey(originInvoker);
+
+        return (ExporterChangeableWrapper<T>) bounds.computeIfAbsent(key, s -> {
+            Invoker<?> invokerDelegate = new InvokerDelegate<>(originInvoker, providerUrl);
+            return new ExporterChangeableWrapper<>((Exporter<T>) protocol.export(invokerDelegate), originInvoker);
+        });
+    }
+
+    public <T> void reExport(Exporter<T> exporter, URL newInvokerUrl) {
+        if (exporter instanceof ExporterChangeableWrapper) {
+            ExporterChangeableWrapper<T> exporterWrapper = (ExporterChangeableWrapper<T>) exporter;
+            Invoker<T> originInvoker = exporterWrapper.getOriginInvoker();
+            reExport(originInvoker, newInvokerUrl);
+        }
+    }
+
+    /**
+     * Reexport the invoker of the modified url
+     *
+     * @param originInvoker
+     * @param newInvokerUrl
+     * @param <T>
+     */
+    @SuppressWarnings("unchecked")
+    public <T> void reExport(final Invoker<T> originInvoker, URL newInvokerUrl) {
+        String key = getCacheKey(originInvoker);
+        ExporterChangeableWrapper<T> exporter = (ExporterChangeableWrapper<T>) bounds.get(key);
+        URL registeredUrl = exporter.getRegisterUrl();
+
+        URL registryUrl = getRegistryUrl(originInvoker);
+        URL newProviderUrl = getUrlToRegistry(newInvokerUrl, registryUrl);
+
+        // update local exporter
+        Invoker<T> invokerDelegate = new InvokerDelegate<T>(originInvoker, newInvokerUrl);
+        exporter.setExporter(protocol.export(invokerDelegate));
+
+        // update registry
+        if (!newProviderUrl.equals(registeredUrl)) {
+            try {
+                doReExport(originInvoker, exporter, registryUrl, registeredUrl, newProviderUrl);
+            } catch (Exception e) {
+                ReExportTask oldTask = reExportFailedTasks.get(registeredUrl);
+                if (oldTask != null) {
+                    return;
+                }
+                ReExportTask task = new ReExportTask(
+                        () -> doReExport(originInvoker, exporter, registryUrl, registeredUrl, newProviderUrl),
+                        registeredUrl,
+                        null
+                );
+                oldTask = reExportFailedTasks.putIfAbsent(registeredUrl, task);
+                if (oldTask == null) {
+                    // never has a retry task. then start a new task for retry.
+                    retryTimer.newTimeout(task, registryUrl.getParameter(REGISTRY_RETRY_PERIOD_KEY, DEFAULT_REGISTRY_RETRY_PERIOD), TimeUnit.MILLISECONDS);
+                }
+            }
+        }
+    }
+
+    private <T> void doReExport(final Invoker<T> originInvoker, ExporterChangeableWrapper<T> exporter,
+                                URL registryUrl, URL oldProviderUrl, URL newProviderUrl) {
+        if (getProviderUrl(originInvoker).getParameter(REGISTER_KEY, true)) {
+            Registry registry = null;
+            try {
+                registry = getRegistry(originInvoker);
+            } catch (Exception e) {
+                throw new SkipFailbackWrapperException(e);
+            }
+
+            logger.info("Try to unregister old url: " + oldProviderUrl);
+            registry.reExportUnregister(oldProviderUrl);
+
+            logger.info("Try to register new url: " + newProviderUrl);
+            registry.reExportRegister(newProviderUrl);
+        }
+        try {
+            ProviderModel.RegisterStatedURL statedUrl = getStatedUrl(registryUrl, newProviderUrl);
+            statedUrl.setProviderUrl(newProviderUrl);
+            exporter.setRegisterUrl(newProviderUrl);
+        } catch (Exception e) {
+            throw new SkipFailbackWrapperException(e);
+        }
+    }
+
+    private ProviderModel.RegisterStatedURL getStatedUrl(URL registryUrl, URL providerUrl) {
+        ProviderModel providerModel = ApplicationModel.getServiceRepository()
+                .lookupExportedService(providerUrl.getServiceKey());
+
+        List<ProviderModel.RegisterStatedURL> statedUrls = providerModel.getStatedUrl();
+        return statedUrls.stream()
+                .filter(u -> u.getRegistryUrl().equals(registryUrl)
+                        && u.getProviderUrl().getProtocol().equals(providerUrl.getProtocol()))
+                .findFirst().orElseThrow(() -> new IllegalStateException("There should have at least one registered url."));
+    }
+
+    /**
+     * Get an instance of registry based on the address of invoker
+     *
+     * @param originInvoker
+     * @return
+     */
+    protected Registry getRegistry(final Invoker<?> originInvoker) {
+        URL registryUrl = getRegistryUrl(originInvoker);
+        return registryFactory.getRegistry(registryUrl);
+    }
+
+    protected URL getRegistryUrl(Invoker<?> originInvoker) {
+        return originInvoker.getUrl();
+    }
+
+    protected URL getRegistryUrl(URL url) {
+        if (SERVICE_REGISTRY_PROTOCOL.equals(url.getProtocol())) {
+            return url;
+        }
+        return url.addParameter(REGISTRY_KEY, url.getProtocol()).setProtocol(SERVICE_REGISTRY_PROTOCOL);
+    }
+
+    /**
+     * Return the url that is registered to the registry and filter the url parameter once
+     *
+     * @param providerUrl
+     * @return url to registry.
+     */
+    private URL getUrlToRegistry(final URL providerUrl, final URL registryUrl) {
+        //The address you see at the registry
+        if (!registryUrl.getParameter(SIMPLIFIED_KEY, false)) {
+            return providerUrl.removeParameters(getFilteredKeys(providerUrl)).removeParameters(
+                    MONITOR_KEY, BIND_IP_KEY, BIND_PORT_KEY, QOS_ENABLE, QOS_HOST, QOS_PORT, ACCEPT_FOREIGN_IP, VALIDATION_KEY,
+                    INTERFACES);
+        } else {
+            String extraKeys = registryUrl.getParameter(EXTRA_KEYS_KEY, "");
+            // if path is not the same as interface name then we should keep INTERFACE_KEY,
+            // otherwise, the registry structure of zookeeper would be '/dubbo/path/providers',
+            // but what we expect is '/dubbo/interface/providers'
+            if (!providerUrl.getPath().equals(providerUrl.getParameter(INTERFACE_KEY))) {
+                if (StringUtils.isNotEmpty(extraKeys)) {
+                    extraKeys += ",";
+                }
+                extraKeys += INTERFACE_KEY;
+            }
+            String[] paramsToRegistry = getParamsToRegistry(DEFAULT_REGISTER_PROVIDER_KEYS
+                    , COMMA_SPLIT_PATTERN.split(extraKeys));
+            return URL.valueOf(providerUrl, paramsToRegistry, providerUrl.getParameter(METHODS_KEY, (String[]) null));
+        }
+
+    }
+
+    private URL getSubscribedOverrideUrl(URL registeredProviderUrl) {
+        return registeredProviderUrl.setProtocol(PROVIDER_PROTOCOL)
+                .addParameters(CATEGORY_KEY, CONFIGURATORS_CATEGORY, CHECK_KEY, String.valueOf(false));
+    }
+
+    /**
+     * Get the address of the providerUrl through the url of the invoker
+     *
+     * @param originInvoker
+     * @return
+     */
+    private URL getProviderUrl(final Invoker<?> originInvoker) {
+        String export = originInvoker.getUrl().getParameterAndDecoded(EXPORT_KEY);
+        if (export == null || export.length() == 0) {
+            throw new IllegalArgumentException("The registry export url is null! registry: " + originInvoker.getUrl());
+        }
+        return URL.valueOf(export);
+    }
+
+    /**
+     * Get the key cached in bounds by invoker
+     *
+     * @param originInvoker
+     * @return
+     */
+    private String getCacheKey(final Invoker<?> originInvoker) {
+        URL providerUrl = getProviderUrl(originInvoker);
+        String key = providerUrl.removeParameters("dynamic", "enabled").toFullString();
+        return key;
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
+        url = getRegistryUrl(url);
+        Registry registry = registryFactory.getRegistry(url);
+        if (RegistryService.class.equals(type)) {
+            return proxyFactory.getInvoker((T) registry, type, url);
+        }
+
+        // group="a,b" or group="*"
+        Map<String, String> qs = StringUtils.parseQueryString(url.getParameterAndDecoded(REFER_KEY));
+        String group = qs.get(GROUP_KEY);
+        if (group != null && group.length() > 0) {
+            if ((COMMA_SPLIT_PATTERN.split(group)).length > 1 || "*".equals(group)) {
+                return doRefer(Cluster.getCluster(MergeableCluster.NAME), registry, type, url);
+            }
+        }
+
+        Cluster cluster = Cluster.getCluster(qs.get(CLUSTER_KEY));
+        return doRefer(cluster, registry, type, url);
+    }
+
+    protected <T> Invoker<T> doRefer(Cluster cluster, Registry registry, Class<T> type, URL url) {
+        return interceptInvoker(getInvoker(cluster, registry, type, url), url);
+    }
+
+    protected <T> Invoker<T> interceptInvoker(ClusterInvoker<T> invoker, URL url) {
+        List<RegistryProtocolListener> listeners = findRegistryProtocolListeners(url);
+        if (CollectionUtils.isEmpty(listeners)) {
+            return invoker;
+        }
+
+        for (RegistryProtocolListener listener : listeners) {
+            listener.onRefer(this, invoker);
+        }
+        return invoker;
+    }
+
+    protected <T> ClusterInvoker<T> getInvoker(Cluster cluster, Registry registry, Class<T> type, URL url) {
+        DynamicDirectory<T> directory = createDirectory(type, url);
+        directory.setRegistry(registry);
+        directory.setProtocol(protocol);
+        // all attributes of REFER_KEY
+        Map<String, String> parameters = new HashMap<String, String>(directory.getConsumerUrl().getParameters());
+        URL urlToRegistry = new URL(CONSUMER_PROTOCOL, parameters.remove(REGISTER_IP_KEY), 0, type.getName(), parameters);
+        if (directory.isShouldRegister()) {
+            directory.setRegisteredConsumerUrl(urlToRegistry);
+            registry.register(directory.getRegisteredConsumerUrl());
+        }
+        directory.buildRouterChain(urlToRegistry);
+        directory.subscribe(toSubscribeUrl(urlToRegistry));
+
+        return (ClusterInvoker<T>) cluster.join(directory);
+    }
+
+    protected <T> DynamicDirectory<T> createDirectory(Class<T> type, URL url) {
+        return new ServiceDiscoveryRegistryDirectory<>(type, url);
+    }
+
+    public <T> void reRefer(DynamicDirectory<T> directory, URL newSubscribeUrl) {
+        URL oldSubscribeUrl = directory.getRegisteredConsumerUrl();
+        Registry registry = directory.getRegistry();
+        registry.unregister(directory.getRegisteredConsumerUrl());
+        directory.unSubscribe(toSubscribeUrl(oldSubscribeUrl));
+        registry.register(directory.getRegisteredConsumerUrl());
+
+        directory.setRegisteredConsumerUrl(newSubscribeUrl);
+        directory.buildRouterChain(newSubscribeUrl);
+        directory.subscribe(toSubscribeUrl(newSubscribeUrl));
+    }
+
+    protected static URL toSubscribeUrl(URL url) {
+        return url.addParameter(CATEGORY_KEY, PROVIDERS_CATEGORY + "," + CONFIGURATORS_CATEGORY + "," + ROUTERS_CATEGORY);
+    }
+
+    protected List<RegistryProtocolListener> findRegistryProtocolListeners(URL url) {
+        return ExtensionLoader.getExtensionLoader(RegistryProtocolListener.class)
+                .getActivateExtension(url, "registry.protocol.listener");
+    }
+
+    // available to test
+    public String[] getParamsToRegistry(String[] defaultKeys, String[] additionalParameterKeys) {
+        int additionalLen = additionalParameterKeys.length;
+        String[] registryParams = new String[defaultKeys.length + additionalLen];
+        System.arraycopy(defaultKeys, 0, registryParams, 0, defaultKeys.length);
+        System.arraycopy(additionalParameterKeys, 0, registryParams, defaultKeys.length, additionalLen);
+        return registryParams;
+    }
+
+    @Override
+    public void destroy() {
+        List<RegistryProtocolListener> listeners = ExtensionLoader.getExtensionLoader(RegistryProtocolListener.class)
+                .getLoadedExtensionInstances();
+        if (CollectionUtils.isNotEmpty(listeners)) {
+            for (RegistryProtocolListener listener : listeners) {
+                listener.onDestroy();
+            }
+        }
+
+        List<Exporter<?>> exporters = new ArrayList<Exporter<?>>(bounds.values());
+        for (Exporter<?> exporter : exporters) {
+            exporter.unexport();
+        }
+        bounds.clear();
+
+        ExtensionLoader.getExtensionLoader(GovernanceRuleRepository.class).getDefaultExtension()
+                .removeListener(ApplicationModel.getApplication() + CONFIGURATORS_SUFFIX, providerConfigurationListener);
+    }
+
+    @Override
+    public List<ProtocolServer> getServers() {
+        return protocol.getServers();
+    }
+
+    //Merge the urls of configurators
+    private static URL getConfigedInvokerUrl(List<Configurator> configurators, URL url) {
+        if (configurators != null && configurators.size() > 0) {
+            for (Configurator configurator : configurators) {
+                url = configurator.configure(url);
+            }
+        }
+        return url;
+    }
+
+    public static class InvokerDelegate<T> extends InvokerWrapper<T> {
+        private final Invoker<T> invoker;
+
+        /**
+         * @param invoker
+         * @param url     invoker.getUrl return this value
+         */
+        public InvokerDelegate(Invoker<T> invoker, URL url) {
+            super(invoker, url);
+            this.invoker = invoker;
+        }
+
+        public Invoker<T> getInvoker() {
+            if (invoker instanceof InvokerDelegate) {
+                return ((InvokerDelegate<T>) invoker).getInvoker();
+            } else {
+                return invoker;
+            }
+        }
+    }
+
+    private static class DestroyableExporter<T> implements Exporter<T> {
+
+        private Exporter<T> exporter;
+
+        public DestroyableExporter(Exporter<T> exporter) {
+            this.exporter = exporter;
+        }
+
+        @Override
+        public Invoker<T> getInvoker() {
+            return exporter.getInvoker();
+        }
+
+        @Override
+        public void unexport() {
+            exporter.unexport();
+        }
+    }
+
+    /**
+     * Reexport: the exporter destroy problem in protocol
+     * 1.Ensure that the exporter returned by registryprotocol can be normal destroyed
+     * 2.No need to re-register to the registry after notify
+     * 3.The invoker passed by the export method , would better to be the invoker of exporter
+     */
+    private class OverrideListener implements NotifyListener {
+        private final URL subscribeUrl;
+        private final Invoker originInvoker;
+
+
+        private List<Configurator> configurators;
+
+        public OverrideListener(URL subscribeUrl, Invoker originalInvoker) {
+            this.subscribeUrl = subscribeUrl;
+            this.originInvoker = originalInvoker;
+        }
+
+        /**
+         * @param urls The list of registered information, is always not empty, The meaning is the same as the
+         *             return value of {@link org.apache.dubbo.registry.RegistryService#lookup(URL)}.
+         */
+        @Override
+        public synchronized void notify(List<URL> urls) {
+            logger.debug("original override urls: " + urls);
+
+            List<URL> matchedUrls = getMatchedUrls(urls, subscribeUrl.addParameter(CATEGORY_KEY,
+                    CONFIGURATORS_CATEGORY));
+            logger.debug("subscribe url: " + subscribeUrl + ", override urls: " + matchedUrls);
+
+            // No matching results
+            if (matchedUrls.isEmpty()) {
+                return;
+            }
+
+            this.configurators = Configurator.toConfigurators(classifyUrls(matchedUrls, UrlUtils::isConfigurator))
+                    .orElse(configurators);
+
+            doOverrideIfNecessary();
+        }
+
+        public synchronized void doOverrideIfNecessary() {
+            final Invoker<?> invoker;
+            if (originInvoker instanceof InvokerDelegate) {
+                invoker = ((InvokerDelegate<?>) originInvoker).getInvoker();
+            } else {
+                invoker = originInvoker;
+            }
+            //The origin invoker
+            URL originUrl = RegistryProtocol.this.getProviderUrl(invoker);
+            String key = getCacheKey(originInvoker);
+            ExporterChangeableWrapper<?> exporter = bounds.get(key);
+            if (exporter == null) {
+                logger.warn(new IllegalStateException("error state, exporter should not be null"));
+                return;
+            }
+            //The current, may have been merged many times
+            URL currentUrl = exporter.getInvoker().getUrl();
+            //Merged with this configuration
+            URL newUrl = getConfigedInvokerUrl(configurators, currentUrl);
+            newUrl = getConfigedInvokerUrl(providerConfigurationListener.getConfigurators(), newUrl);
+            newUrl = getConfigedInvokerUrl(serviceConfigurationListeners.get(originUrl.getServiceKey())
+                    .getConfigurators(), newUrl);
+            if (!currentUrl.equals(newUrl)) {
+                RegistryProtocol.this.reExport(originInvoker, newUrl);
+                logger.info("exported provider url changed, origin url: " + originUrl +
+                        ", old export url: " + currentUrl + ", new export url: " + newUrl);
+            }
+        }
+
+        private List<URL> getMatchedUrls(List<URL> configuratorUrls, URL currentSubscribe) {
+            List<URL> result = new ArrayList<URL>();
+            for (URL url : configuratorUrls) {
+                URL overrideUrl = url;
+                // Compatible with the old version
+                if (url.getParameter(CATEGORY_KEY) == null && OVERRIDE_PROTOCOL.equals(url.getProtocol())) {
+                    overrideUrl = url.addParameter(CATEGORY_KEY, CONFIGURATORS_CATEGORY);
+                }
+
+                // Check whether url is to be applied to the current service
+                if (UrlUtils.isMatch(currentSubscribe, overrideUrl)) {
+                    result.add(url);
+                }
+            }
+            return result;
+        }
+    }
+
+    private class ServiceConfigurationListener extends AbstractConfiguratorListener {
+        private URL providerUrl;
+        private OverrideListener notifyListener;
+
+        public ServiceConfigurationListener(URL providerUrl, OverrideListener notifyListener) {
+            this.providerUrl = providerUrl;
+            this.notifyListener = notifyListener;
+            this.initWith(DynamicConfiguration.getRuleKey(providerUrl) + CONFIGURATORS_SUFFIX);
+        }
+
+        private <T> URL overrideUrl(URL providerUrl) {
+            return RegistryProtocol.getConfigedInvokerUrl(configurators, providerUrl);
+        }
+
+        @Override
+        protected void notifyOverrides() {
+            notifyListener.doOverrideIfNecessary();
+        }
+    }
+
+    private class ProviderConfigurationListener extends AbstractConfiguratorListener {
+
+        public ProviderConfigurationListener() {
+            this.initWith(ApplicationModel.getApplication() + CONFIGURATORS_SUFFIX);
+        }
+
+        /**
+         * Get existing configuration rule and override provider url before exporting.
+         *
+         * @param providerUrl
+         * @param <T>
+         * @return
+         */
+        private <T> URL overrideUrl(URL providerUrl) {
+            return RegistryProtocol.getConfigedInvokerUrl(configurators, providerUrl);
+        }
+
+        @Override
+        protected void notifyOverrides() {
+            overrideListeners.values().forEach(listener -> ((OverrideListener) listener).doOverrideIfNecessary());
+        }
+    }
+
+    /**
+     * exporter proxy, establish the corresponding relationship between the returned exporter and the exporter
+     * exported by the protocol, and can modify the relationship at the time of override.
+     *
+     * @param <T>
+     */
+    private class ExporterChangeableWrapper<T> implements Exporter<T> {
+
+        private final ExecutorService executor = newSingleThreadExecutor(new NamedThreadFactory("Exporter-Unexport", true));
+
+        private final Invoker<T> originInvoker;
+        private Exporter<T> exporter;
+        private URL subscribeUrl;
+        private URL registerUrl;
+
+        public ExporterChangeableWrapper(Exporter<T> exporter, Invoker<T> originInvoker) {
+            this.exporter = exporter;
+            this.originInvoker = originInvoker;
+        }
+
+        public Invoker<T> getOriginInvoker() {
+            return originInvoker;
+        }
+
+        @Override
+        public Invoker<T> getInvoker() {
+            return exporter.getInvoker();
+        }
+
+        public void setExporter(Exporter<T> exporter) {
+            this.exporter = exporter;
+        }
+
+        @Override
+        public void unexport() {
+            String key = getCacheKey(this.originInvoker);
+            bounds.remove(key);
+
+            Registry registry = RegistryProtocol.this.getRegistry(originInvoker);
+            try {
+                registry.unregister(registerUrl);
+            } catch (Throwable t) {
+                logger.warn(t.getMessage(), t);
+            }
+            try {
+                NotifyListener listener = RegistryProtocol.this.overrideListeners.remove(subscribeUrl);
+                registry.unsubscribe(subscribeUrl, listener);
+                ExtensionLoader.getExtensionLoader(GovernanceRuleRepository.class).getDefaultExtension()
+                        .removeListener(subscribeUrl.getServiceKey() + CONFIGURATORS_SUFFIX,
+                                serviceConfigurationListeners.get(subscribeUrl.getServiceKey()));
+            } catch (Throwable t) {
+                logger.warn(t.getMessage(), t);
+            }
+
+            executor.submit(() -> {
+                try {
+                    int timeout = ConfigurationUtils.getServerShutdownTimeout();
+                    if (timeout > 0) {
+                        logger.info("Waiting " + timeout + "ms for registry to notify all consumers before unexport. " +
+                                "Usually, this is called when you use dubbo API");
+                        Thread.sleep(timeout);
+                    }
+                    exporter.unexport();
+                } catch (Throwable t) {
+                    logger.warn(t.getMessage(), t);
+                }
+            });
+        }
+
+        public void setSubscribeUrl(URL subscribeUrl) {
+            this.subscribeUrl = subscribeUrl;
+        }
+
+        public void setRegisterUrl(URL registerUrl) {
+            this.registerUrl = registerUrl;
+        }
+
+        public URL getRegisterUrl() {
+            return registerUrl;
+        }
+    }
+
+    // for unit test
+    private static RegistryProtocol INSTANCE;
+
+    // for unit test
+    public RegistryProtocol() {
+        INSTANCE = this;
+    }
+
+    // for unit test
+    public static RegistryProtocol getRegistryProtocol() {
+        if (INSTANCE == null) {
+            ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(REGISTRY_PROTOCOL); // load
+        }
+        return INSTANCE;
+    }
+}
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 efd3c57..1315c54 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
@@ -392,7 +392,7 @@ public class ServiceDiscoveryRegistry implements Registry {
      * @return
      */
     protected Set<String> findMappedServices(URL subscribedURL, MappingListener listener) {
-        return serviceNameMapping.get(subscribedURL, listener);
+        return serviceNameMapping.getAndListen(subscribedURL, listener);
     }
 
     /**
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistryProtocol.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistryProtocol.java
deleted file mode 100644
index f4f872f..0000000
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistryProtocol.java
+++ /dev/null
@@ -1,51 +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;
-
-import org.apache.dubbo.common.URL;
-import org.apache.dubbo.registry.integration.DynamicDirectory;
-import org.apache.dubbo.registry.integration.RegistryProtocol;
-import org.apache.dubbo.rpc.Invoker;
-
-import static org.apache.dubbo.common.constants.RegistryConstants.SERVICE_REGISTRY_PROTOCOL;
-
-/**
- * TODO, replace RegistryProtocol completely in the future.
- */
-public class ServiceDiscoveryRegistryProtocol extends RegistryProtocol {
-    @Override
-    protected URL getRegistryUrl(Invoker<?> originInvoker) {
-        URL registryUrl = originInvoker.getUrl();
-        if (SERVICE_REGISTRY_PROTOCOL.equals(registryUrl.getProtocol())) {
-            return registryUrl;
-        }
-        return super.getRegistryUrl(originInvoker);
-    }
-
-    @Override
-    protected URL getRegistryUrl(URL url) {
-        if (SERVICE_REGISTRY_PROTOCOL.equals(url.getProtocol())) {
-            return url;
-        }
-        return super.getRegistryUrl(url);
-    }
-
-    @Override
-    protected <T> DynamicDirectory<T> createDirectory(Class<T> type, URL url) {
-        return new ServiceDiscoveryRegistryDirectory<>(type, url);
-    }
-}
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistryProtocolListener.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistryProtocolListener.java
index d213171..bc9748c 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistryProtocolListener.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistryProtocolListener.java
@@ -16,7 +16,6 @@
  */
 package org.apache.dubbo.registry.client;
 
-import org.apache.dubbo.registry.integration.RegistryProtocol;
 import org.apache.dubbo.registry.integration.RegistryProtocolListener;
 import org.apache.dubbo.rpc.Exporter;
 import org.apache.dubbo.rpc.Invoker;
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 cb43cee..b915cb3 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
@@ -194,10 +194,6 @@ public class ServiceInstancesChangedListener implements ConditionalEventListener
         this.listeners.put(serviceKey, listener);
     }
 
-    public void removeListener(String serviceKey) {
-        this.listeners.remove(serviceKey);
-    }
-
     public List<URL> getUrls(String serviceKey) {
         return toUrlsWithEmpty(serviceUrls.get(serviceKey));
     }
diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataServiceNameMapping.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/MetadataServiceNameMapping.java
similarity index 57%
rename from dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataServiceNameMapping.java
rename to dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/MetadataServiceNameMapping.java
index 5389e63..b6d7a9c 100644
--- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataServiceNameMapping.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/MetadataServiceNameMapping.java
@@ -14,19 +14,23 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.metadata;
+package org.apache.dubbo.registry.client.metadata;
 
 import org.apache.dubbo.common.URL;
 import org.apache.dubbo.common.utils.CollectionUtils;
+import org.apache.dubbo.metadata.MappingListener;
+import org.apache.dubbo.metadata.MetadataService;
+import org.apache.dubbo.metadata.ServiceNameMapping;
 import org.apache.dubbo.metadata.report.MetadataReport;
 import org.apache.dubbo.metadata.report.MetadataReportInstance;
+import org.apache.dubbo.registry.client.RegistryClusterIdentifier;
 
 import java.util.LinkedHashSet;
 import java.util.List;
-import java.util.Map;
 import java.util.Set;
 
 import static java.util.Arrays.asList;
+import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_KEY;
 import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY;
 import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY;
 import static org.apache.dubbo.rpc.model.ApplicationModel.getName;
@@ -44,33 +48,42 @@ public class MetadataServiceNameMapping implements ServiceNameMapping {
         if (IGNORED_SERVICE_INTERFACES.contains(serviceInterface)) {
             return;
         }
-
-        Map<String, MetadataReport> metadataReports = MetadataReportInstance.getMetadataReports(true);
-        metadataReports.forEach((key, reporter) -> {
-            reporter.registerServiceAppMapping(ServiceNameMapping.buildGroup(serviceInterface, group, version, protocol), getName(), url);
-        });
+        String registryCluster = getRegistryCluster(url);
+        MetadataReport metadataReport = MetadataReportInstance.getMetadataReport(registryCluster);
+        metadataReport.registerServiceAppMapping(ServiceNameMapping.buildGroup(serviceInterface, group, version, protocol), getName(), url);
     }
 
     @Override
-    public Set<String> get(URL url, MappingListener mappingListener) {
+    public Set<String> getAndListen(URL url, MappingListener mappingListener) {
         String serviceInterface = url.getServiceInterface();
         String group = url.getParameter(GROUP_KEY);
         String version = url.getParameter(VERSION_KEY);
         String protocol = url.getProtocol();
 
-        Map<String, MetadataReport> metadataReports = MetadataReportInstance.getMetadataReports(true);
+        String mappingKey = ServiceNameMapping.buildGroup(serviceInterface, group, version, protocol);
         Set<String> serviceNames = new LinkedHashSet<>();
-        for (Map.Entry<String, MetadataReport> entry : metadataReports.entrySet()) {
-            MetadataReport reporter = entry.getValue();
-            Set<String> apps = reporter.getServiceAppMapping(
-                    ServiceNameMapping.buildGroup(serviceInterface, group, version, protocol),
-                    mappingListener,
-                    url);
-            if (CollectionUtils.isNotEmpty(apps)) {
-                serviceNames.addAll(apps);
-                break;
-            }
+        String registryCluster = getRegistryCluster(url);
+        MetadataReport metadataReport = MetadataReportInstance.getMetadataReport(registryCluster);
+        Set<String> apps = metadataReport.getServiceAppMapping(
+                mappingKey,
+                mappingListener,
+                url);
+        if (CollectionUtils.isNotEmpty(apps)) {
+            serviceNames.addAll(apps);
         }
+
         return serviceNames;
     }
+
+    protected String getRegistryCluster(URL url) {
+        String registryCluster = RegistryClusterIdentifier.getExtension(url).providerKey(url);
+        if (registryCluster == null) {
+            registryCluster = DEFAULT_KEY;
+        }
+        int i = registryCluster.indexOf(",");
+        if (i > 0) {
+            registryCluster = registryCluster.substring(0, i);
+        }
+        return registryCluster;
+    }
 }
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
index b65dba1..0a6a106 100644
--- 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
@@ -65,7 +65,9 @@ public class MetadataUtils {
         // store in local
         WritableMetadataService.getDefaultExtension().publishServiceDefinition(url);
         // send to remote
+//        if (REMOTE_METADATA_STORAGE_TYPE.equals(url.getParameter(METADATA_KEY))) {
         getRemoteMetadataService().publishServiceDefinition(url);
+//        }
     }
 
     public static MetadataService getMetadataServiceProxy(ServiceInstance instance, ServiceDiscovery serviceDiscovery) {
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 0881e0c..95e5349 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
@@ -42,7 +42,7 @@ import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY;
 import static org.apache.dubbo.common.constants.CommonConstants.PORT_KEY;
 import static org.apache.dubbo.common.constants.CommonConstants.TIMESTAMP_KEY;
 import static org.apache.dubbo.common.utils.StringUtils.isBlank;
-import static org.apache.dubbo.registry.integration.RegistryProtocol.DEFAULT_REGISTER_PROVIDER_KEYS;
+import static org.apache.dubbo.registry.integration.InterfaceCompatibleRegistryProtocol.DEFAULT_REGISTER_PROVIDER_KEYS;
 import static org.apache.dubbo.rpc.Constants.DEPRECATED_KEY;
 import static org.apache.dubbo.rpc.Constants.ID_KEY;
 
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
index c4235f4..93c02fe 100644
--- 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
@@ -55,7 +55,7 @@ public class RemoteMetadataServiceImpl {
     }
 
     public Map<String, MetadataReport> getMetadataReports() {
-        return MetadataReportInstance.getMetadataReports(true);
+        return MetadataReportInstance.getMetadataReports(false);
     }
 
     public void publishMetadata(String serviceName) {
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/DynamicDirectory.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/DynamicDirectory.java
index 80aadc8..a53378c 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/DynamicDirectory.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/DynamicDirectory.java
@@ -53,7 +53,7 @@ import static org.apache.dubbo.common.constants.RegistryConstants.CONSUMERS_CATE
 import static org.apache.dubbo.registry.Constants.REGISTER_IP_KEY;
 import static org.apache.dubbo.registry.Constants.REGISTER_KEY;
 import static org.apache.dubbo.registry.Constants.SIMPLIFIED_KEY;
-import static org.apache.dubbo.registry.integration.RegistryProtocol.DEFAULT_REGISTER_CONSUMER_KEYS;
+import static org.apache.dubbo.registry.integration.InterfaceCompatibleRegistryProtocol.DEFAULT_REGISTER_CONSUMER_KEYS;
 import static org.apache.dubbo.remoting.Constants.CHECK_KEY;
 
 
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/InterfaceCompatibleRegistryProtocol.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/InterfaceCompatibleRegistryProtocol.java
new file mode 100644
index 0000000..d591e02
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/InterfaceCompatibleRegistryProtocol.java
@@ -0,0 +1,177 @@
+/*
+ * 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.integration;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.URLBuilder;
+import org.apache.dubbo.registry.Registry;
+import org.apache.dubbo.registry.client.RegistryProtocol;
+import org.apache.dubbo.rpc.Invocation;
+import org.apache.dubbo.rpc.Invoker;
+import org.apache.dubbo.rpc.Result;
+import org.apache.dubbo.rpc.RpcException;
+import org.apache.dubbo.rpc.cluster.Cluster;
+import org.apache.dubbo.rpc.cluster.ClusterInvoker;
+import org.apache.dubbo.rpc.cluster.Directory;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.apache.dubbo.common.constants.RegistryConstants.ENABLE_REGISTRY_DIRECTORY_AUTO_MIGRATION;
+import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_KEY;
+import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_PROTOCOL;
+import static org.apache.dubbo.registry.Constants.CONSUMER_PROTOCOL;
+import static org.apache.dubbo.registry.Constants.DEFAULT_REGISTRY;
+import static org.apache.dubbo.registry.Constants.REGISTER_IP_KEY;
+
+/**
+ * RegistryProtocol
+ */
+public class InterfaceCompatibleRegistryProtocol extends RegistryProtocol {
+
+    @Override
+    protected URL getRegistryUrl(Invoker<?> originInvoker) {
+        URL registryUrl = originInvoker.getUrl();
+        if (REGISTRY_PROTOCOL.equals(registryUrl.getProtocol())) {
+            String protocol = registryUrl.getParameter(REGISTRY_KEY, DEFAULT_REGISTRY);
+            registryUrl = registryUrl.setProtocol(protocol).removeParameter(REGISTRY_KEY);
+        }
+        return registryUrl;
+    }
+
+    @Override
+    protected URL getRegistryUrl(URL url) {
+        return URLBuilder.from(url)
+                .setProtocol(url.getParameter(REGISTRY_KEY, DEFAULT_REGISTRY))
+                .removeParameter(REGISTRY_KEY)
+                .build();
+    }
+
+    @Override
+    protected <T> DynamicDirectory<T> createDirectory(Class<T> type, URL url) {
+        return new RegistryDirectory<>(type, url);
+    }
+
+    protected <T> Invoker<T> doRefer(Cluster cluster, Registry registry, Class<T> type, URL url) {
+        ClusterInvoker<T> invoker = getInvoker(cluster, registry, type, url);
+        ClusterInvoker<T> serviceDiscoveryInvoker = getServiceDiscoveryInvoker(cluster, type, url);
+        ClusterInvoker<T> migrationInvoker = new MigrationInvoker<>(invoker, serviceDiscoveryInvoker);
+
+        return interceptInvoker(migrationInvoker, url);
+    }
+
+    protected <T> ClusterInvoker<T> getServiceDiscoveryInvoker(Cluster cluster, Class<T> type, URL url) {
+        Registry registry = registryFactory.getRegistry(super.getRegistryUrl(url));
+        ClusterInvoker<T> serviceDiscoveryInvoker = null;
+        // enable auto migration from interface address pool to instance address pool
+        boolean autoMigration = url.getParameter(ENABLE_REGISTRY_DIRECTORY_AUTO_MIGRATION, false);
+        if (autoMigration) {
+            DynamicDirectory<T> serviceDiscoveryDirectory = super.createDirectory(type, url);
+            serviceDiscoveryDirectory.setRegistry(registry);
+            serviceDiscoveryDirectory.setProtocol(protocol);
+            Map<String, String> parameters = new HashMap<String, String>(serviceDiscoveryDirectory.getConsumerUrl().getParameters());
+            URL urlToRegistry = new URL(CONSUMER_PROTOCOL, parameters.remove(REGISTER_IP_KEY), 0, type.getName(), parameters);
+            if (serviceDiscoveryDirectory.isShouldRegister()) {
+                serviceDiscoveryDirectory.setRegisteredConsumerUrl(urlToRegistry);
+                registry.register(serviceDiscoveryDirectory.getRegisteredConsumerUrl());
+            }
+            serviceDiscoveryDirectory.buildRouterChain(urlToRegistry);
+            serviceDiscoveryDirectory.subscribe(toSubscribeUrl(urlToRegistry));
+            serviceDiscoveryInvoker = (ClusterInvoker<T>) cluster.join(serviceDiscoveryDirectory);
+        }
+        return serviceDiscoveryInvoker;
+    }
+
+    private static class MigrationInvoker<T> implements ClusterInvoker<T> {
+        private ClusterInvoker<T> invoker;
+        private ClusterInvoker<T> serviceDiscoveryInvoker;
+
+        public MigrationInvoker(ClusterInvoker<T> invoker, ClusterInvoker<T> serviceDiscoveryInvoker) {
+            this.invoker = invoker;
+            this.serviceDiscoveryInvoker = serviceDiscoveryInvoker;
+        }
+
+        public ClusterInvoker<T> getInvoker() {
+            return invoker;
+        }
+
+        public void setInvoker(ClusterInvoker<T> invoker) {
+            this.invoker = invoker;
+        }
+
+        public ClusterInvoker<T> getServiceDiscoveryInvoker() {
+            return serviceDiscoveryInvoker;
+        }
+
+        public void setServiceDiscoveryInvoker(ClusterInvoker<T> serviceDiscoveryInvoker) {
+            this.serviceDiscoveryInvoker = serviceDiscoveryInvoker;
+        }
+
+        @Override
+        public Class<T> getInterface() {
+            return invoker.getInterface();
+        }
+
+        @Override
+        public Result invoke(Invocation invocation) throws RpcException {
+            if (serviceDiscoveryInvoker == null) {
+                return invoker.invoke(invocation);
+            }
+
+            if (invoker.isDestroyed()) {
+                return serviceDiscoveryInvoker.invoke(invocation);
+            }
+            if (serviceDiscoveryInvoker.isAvailable()) {
+                invoker.destroy(); // can be destroyed asynchronously
+                return serviceDiscoveryInvoker.invoke(invocation);
+            }
+            return invoker.invoke(invocation);
+        }
+
+        @Override
+        public URL getUrl() {
+            return invoker.getUrl();
+        }
+
+        @Override
+        public boolean isAvailable() {
+            return invoker.isAvailable() || serviceDiscoveryInvoker.isAvailable();
+        }
+
+        @Override
+        public void destroy() {
+            invoker.destroy();
+            serviceDiscoveryInvoker.destroy();
+        }
+
+        @Override
+        public URL getRegistryUrl() {
+            return invoker.getRegistryUrl();
+        }
+
+        @Override
+        public Directory<T> getDirectory() {
+            return invoker.getDirectory();
+        }
+
+        @Override
+        public boolean isDestroyed() {
+            return invoker.isDestroyed() && serviceDiscoveryInvoker.isDestroyed();
+        }
+    }
+
+}
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 f0d7938..e94f79b 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
@@ -73,7 +73,7 @@ import static org.apache.dubbo.common.constants.RegistryConstants.PROVIDERS_CATE
 import static org.apache.dubbo.common.constants.RegistryConstants.ROUTERS_CATEGORY;
 import static org.apache.dubbo.common.constants.RegistryConstants.ROUTE_PROTOCOL;
 import static org.apache.dubbo.registry.Constants.CONFIGURATORS_SUFFIX;
-import static org.apache.dubbo.registry.integration.RegistryProtocol.DEFAULT_REGISTER_CONSUMER_KEYS;
+import static org.apache.dubbo.registry.integration.InterfaceCompatibleRegistryProtocol.DEFAULT_REGISTER_CONSUMER_KEYS;
 import static org.apache.dubbo.remoting.Constants.CHECK_KEY;
 import static org.apache.dubbo.rpc.cluster.Constants.ROUTER_KEY;
 
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryInvokerWrapper.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryInvokerWrapper.java
deleted file mode 100644
index c6ce46f..0000000
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryInvokerWrapper.java
+++ /dev/null
@@ -1,79 +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.integration;
-
-import org.apache.dubbo.common.URL;
-import org.apache.dubbo.rpc.Invocation;
-import org.apache.dubbo.rpc.Invoker;
-import org.apache.dubbo.rpc.Result;
-import org.apache.dubbo.rpc.RpcException;
-import org.apache.dubbo.rpc.cluster.Cluster;
-
-class RegistryInvokerWrapper<T> implements Invoker<T> {
-    private DynamicDirectory<T> directory;
-    private Cluster cluster;
-    private Invoker<T> invoker;
-    private URL url;
-
-    public RegistryInvokerWrapper(DynamicDirectory<T> directory, Cluster cluster, Invoker<T> invoker, URL url) {
-        this.directory = directory;
-        this.cluster = cluster;
-        this.invoker = invoker;
-        this.url = url;
-    }
-
-    @Override
-    public Class<T> getInterface() {
-        return invoker.getInterface();
-    }
-
-    @Override
-    public Result invoke(Invocation invocation) throws RpcException {
-        return invoker.invoke(invocation);
-    }
-
-    @Override
-    public URL getUrl() {
-        return url;
-    }
-
-    public void setUrl(URL url) {
-        this.url = url;
-    }
-
-    public void setInvoker(Invoker<T> invoker) {
-        this.invoker = invoker;
-    }
-
-    public DynamicDirectory<T> getDirectory() {
-        return directory;
-    }
-
-    public Cluster getCluster() {
-        return cluster;
-    }
-
-    @Override
-    public boolean isAvailable() {
-        return invoker.isAvailable();
-    }
-
-    @Override
-    public void destroy() {
-        invoker.destroy();
-    }
-}
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocolListener.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocolListener.java
index 2a4f3c3..5bf47ca 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocolListener.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocolListener.java
@@ -18,6 +18,7 @@ package org.apache.dubbo.registry.integration;
 
 import org.apache.dubbo.common.URL;
 import org.apache.dubbo.common.extension.SPI;
+import org.apache.dubbo.registry.client.RegistryProtocol;
 import org.apache.dubbo.rpc.Exporter;
 import org.apache.dubbo.rpc.Invoker;
 
diff --git a/dubbo-registry/dubbo-registry-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metadata.ServiceNameMapping b/dubbo-registry/dubbo-registry-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metadata.ServiceNameMapping
new file mode 100644
index 0000000..562d8fa
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metadata.ServiceNameMapping
@@ -0,0 +1 @@
+metadata=org.apache.dubbo.registry.client.metadata.MetadataServiceNameMapping
\ No newline at end of file
diff --git a/dubbo-registry/dubbo-registry-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.Protocol b/dubbo-registry/dubbo-registry-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.Protocol
index c8903b0..5dda00e 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.Protocol
+++ b/dubbo-registry/dubbo-registry-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.Protocol
@@ -1,2 +1,2 @@
-registry=org.apache.dubbo.registry.integration.RegistryProtocol
-service-discovery-registry=org.apache.dubbo.registry.client.ServiceDiscoveryRegistryProtocol
\ No newline at end of file
+registry=org.apache.dubbo.registry.integration.InterfaceCompatibleRegistryProtocol
+service-discovery-registry=org.apache.dubbo.registry.client.RegistryProtocol
\ No newline at end of file


[dubbo] 17/27: refactor param filter to support both service and instance customization

Posted by li...@apache.org.
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

commit 596ba5b07e0c6f9f7989c6c207c36bd59b5a71e3
Author: ken.lj <ke...@gmail.com>
AuthorDate: Thu Jul 23 17:08:42 2020 +0800

    refactor param filter to support both service and instance customization
---
 .../dubbo/metadata/DefaultMetadataParamsFilter.java       |  8 +++++++-
 .../main/java/org/apache/dubbo/metadata/MetadataInfo.java |  2 +-
 .../org/apache/dubbo/metadata/MetadataParamsFilter.java   | 15 ++++++++++++++-
 3 files changed, 22 insertions(+), 3 deletions(-)

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
index 7a11096..667c7cd 100644
--- 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
@@ -39,11 +39,17 @@ import static org.apache.dubbo.rpc.cluster.Constants.WEIGHT_KEY;
 @Activate
 public class DefaultMetadataParamsFilter implements MetadataParamsFilter {
     @Override
-    public String[] include() {
+    public String[] serviceParamsIncluded() {
         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, DUBBO_VERSION_KEY, RELEASE_KEY
         };
     }
+
+
+    @Override
+    public String[] instanceParamsIncluded() {
+        return new String[0];
+    }
 }
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 f479830..315e56e 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
@@ -191,7 +191,7 @@ public class MetadataInfo implements Serializable {
             Map<String, String> params = new HashMap<>();
             List<MetadataParamsFilter> filters = loader.getActivateExtension(url, "params-filter");
             for (MetadataParamsFilter filter : filters) {
-                String[] paramsIncluded = filter.include();
+                String[] paramsIncluded = filter.serviceParamsIncluded();
                 if (ArrayUtils.isNotEmpty(paramsIncluded)) {
                     for (String p : paramsIncluded) {
                         String value = url.getParameter(p);
diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataParamsFilter.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataParamsFilter.java
index 5ccb9aa..542acb1 100644
--- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataParamsFilter.java
+++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataParamsFilter.java
@@ -20,5 +20,18 @@ import org.apache.dubbo.common.extension.SPI;
 
 @SPI
 public interface MetadataParamsFilter {
-   String[] include();
+
+   /**
+    * params that need to be sent to metadata center
+    *
+    * @return arrays of keys
+    */
+   String[] serviceParamsIncluded();
+
+   /**
+    * params that need to be sent to registry center
+    *
+    * @return arrays of keys
+    */
+   String[] instanceParamsIncluded();
 }


[dubbo] 15/27: update version to 3.0.0-SNAPSHOT

Posted by li...@apache.org.
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

commit e7fcaedc9524af27bba762f28b368f37729b2832
Author: ken.lj <ke...@gmail.com>
AuthorDate: Wed Jul 22 11:36:48 2020 +0800

    update version to 3.0.0-SNAPSHOT
---
 dubbo-dependencies-bom/pom.xml                          | 10 +++++-----
 dubbo-dependencies/dubbo-dependencies-zookeeper/pom.xml |  2 +-
 pom.xml                                                 |  2 +-
 3 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/dubbo-dependencies-bom/pom.xml b/dubbo-dependencies-bom/pom.xml
index 7a63e3c..92f2a90 100644
--- a/dubbo-dependencies-bom/pom.xml
+++ b/dubbo-dependencies-bom/pom.xml
@@ -97,7 +97,7 @@
         <grizzly_version>2.1.4</grizzly_version>
         <httpclient_version>4.5.3</httpclient_version>
         <httpcore_version>4.4.6</httpcore_version>
-        <fastjson_version>1.2.70</fastjson_version>
+        <fastjson_version>1.2.68</fastjson_version>
         <zookeeper_version>3.4.13</zookeeper_version>
         <curator_version>4.0.1</curator_version>
         <curator_test_version>2.12.0</curator_test_version>
@@ -130,7 +130,7 @@
         <resteasy_version>3.0.19.Final</resteasy_version>
         <tomcat_embed_version>8.5.31</tomcat_embed_version>
         <jetcd_version>0.4.1</jetcd_version>
-        <nacos_version>1.3.1</nacos_version>
+        <nacos_version>1.1.1</nacos_version>
         <grpc.version>1.22.1</grpc.version>
         <!-- Log libs -->
         <slf4j_version>1.7.25</slf4j_version>
@@ -146,13 +146,13 @@
         <eureka.version>1.9.12</eureka.version>
 
         <!-- Alibaba -->
-        <alibaba_spring_context_support_version>1.0.8</alibaba_spring_context_support_version>
+        <alibaba_spring_context_support_version>1.0.6</alibaba_spring_context_support_version>
 
         <jaxb_version>2.2.7</jaxb_version>
         <activation_version>1.2.0</activation_version>
         <test_container_version>1.11.2</test_container_version>
         <etcd_launcher_version>0.3.0</etcd_launcher_version>
-        <hessian_lite_version>3.2.8</hessian_lite_version>
+        <hessian_lite_version>3.2.7</hessian_lite_version>
         <swagger_version>1.5.19</swagger_version>
         <spring_test_version>4.3.16.RELEASE</spring_test_version>
 
@@ -163,7 +163,7 @@
         <mortbay_jetty_version>6.1.26</mortbay_jetty_version>
         <portlet_version>2.0</portlet_version>
         <maven_flatten_version>1.1.0</maven_flatten_version>
-        <revision>2.7.9-SNAPSHOT</revision>
+        <revision>3.0.0-SNAPSHOT</revision>
     </properties>
 
     <dependencyManagement>
diff --git a/dubbo-dependencies/dubbo-dependencies-zookeeper/pom.xml b/dubbo-dependencies/dubbo-dependencies-zookeeper/pom.xml
index e5c54c8..1ff9f7f 100644
--- a/dubbo-dependencies/dubbo-dependencies-zookeeper/pom.xml
+++ b/dubbo-dependencies/dubbo-dependencies-zookeeper/pom.xml
@@ -32,7 +32,7 @@
     <packaging>pom</packaging>
 
     <properties>
-        <revision>2.7.9-SNAPSHOT</revision>
+        <revision>3.0.0-SNAPSHOT</revision>
         <maven_flatten_version>1.1.0</maven_flatten_version>
     </properties>
 
diff --git a/pom.xml b/pom.xml
index 51b48a0..e49eed2 100644
--- a/pom.xml
+++ b/pom.xml
@@ -126,7 +126,7 @@
         <arguments />
         <checkstyle.skip>true</checkstyle.skip>
         <rat.skip>true</rat.skip>
-        <revision>2.7.9-SNAPSHOT</revision>
+        <revision>3.0.0-SNAPSHOT</revision>
     </properties>
 
     <modules>


[dubbo] 03/27: metadata read & write

Posted by li...@apache.org.
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

commit b33aef310dc2f5d3293cf2c3b358f5af6e382692
Author: ken.lj <ke...@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     | 313 +++++----------------
 .../metadata/DefaultMetadataParamsFilter.java      |  47 ++++
 .../org/apache/dubbo/metadata/MetadataInfo.java    | 100 +++++++
 .../dubbo/metadata/MetadataParamsFilter.java}      |  20 +-
 .../org/apache/dubbo/metadata/MetadataService.java |   2 +
 .../apache/dubbo/metadata/RevisionResolver.java    |  57 ++++
 .../apache/dubbo/metadata/URLRevisionResolver.java | 152 ----------
 .../dubbo/metadata/WritableMetadataService.java    |  22 +-
 .../dubbo/metadata/report/MetadataReport.java      | 130 +--------
 .../metadata/report/MetadataReportInstance.java    |  24 +-
 .../store/RemoteWritableMetadataService.java       | 122 --------
 ...g.apache.dubbo.metadata.WritableMetadataService |   2 -
 .../InMemoryWritableMetadataServiceTest.java       | 157 -----------
 .../RemoteWritableMetadataServiceDelegateTest.java | 216 ++++++++++++++
 .../registry/client/ServiceDiscoveryRegistry.java  |   7 +-
 .../registry/client/ServiceInstanceCustomizer.java |   3 -
 .../CustomizableServiceInstanceListener.java       |  44 ---
 .../listener/ServiceInstancesChangedListener.java  |  26 +-
 ...ExportedServicesRevisionMetadataCustomizer.java |  57 ----
 .../registry/client/metadata/MetadataUtils.java    | 112 ++++++++
 .../metadata/ServiceInstanceMetadataUtils.java     |   7 +
 ...bscribedServicesRevisionMetadataCustomizer.java |  58 ----
 .../proxy/BaseMetadataServiceProxyFactory.java     |  58 ----
 .../proxy/DefaultMetadataServiceProxyFactory.java  |  85 ------
 .../proxy/MetadataServiceProxyFactory.java         |  56 ----
 .../metadata/proxy/RemoteMetadataServiceProxy.java | 111 --------
 .../proxy/RemoteMetadataServiceProxyFactory.java   |  34 ---
 .../store/InMemoryWritableMetadataService.java     |  76 +++--
 .../metadata/store/RemoteMetadataServiceImpl.java  | 126 +++++++++
 .../registry/integration/RegistryDirectory.java    |   1 +
 .../internal/org.apache.dubbo.event.EventListener  |   5 +-
 .../event/listener/LoggingEventListenerTest.java   |   5 +-
 .../ServiceInstancesChangedListenerTest.java       |  27 --
 37 files changed, 873 insertions(+), 1430 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 868fadc..218fef0 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 da90597..23f7bf6 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
@@ -36,7 +36,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.Invoker;
 import org.apache.dubbo.rpc.Protocol;
 import org.apache.dubbo.rpc.ProxyFactory;
@@ -66,7 +66,6 @@ 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.COMMA_SEPARATOR_CHAR;
 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;
@@ -391,16 +390,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 cf5af44..3a7702b 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 1ef096b..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
@@ -19,7 +19,6 @@ package org.apache.dubbo.config.bootstrap;
 import org.apache.dubbo.common.URL;
 import org.apache.dubbo.common.config.Environment;
 import org.apache.dubbo.common.config.configcenter.DynamicConfiguration;
-import org.apache.dubbo.common.config.configcenter.DynamicConfigurationFactory;
 import org.apache.dubbo.common.config.configcenter.wrapper.CompositeDynamicConfiguration;
 import org.apache.dubbo.common.extension.ExtensionLoader;
 import org.apache.dubbo.common.lang.ShutdownHookCallback;
@@ -28,7 +27,6 @@ import org.apache.dubbo.common.logger.Logger;
 import org.apache.dubbo.common.logger.LoggerFactory;
 import org.apache.dubbo.common.threadpool.concurrent.ScheduledCompletableFuture;
 import org.apache.dubbo.common.threadpool.manager.ExecutorRepository;
-import org.apache.dubbo.common.utils.ArrayUtils;
 import org.apache.dubbo.common.utils.CollectionUtils;
 import org.apache.dubbo.common.utils.StringUtils;
 import org.apache.dubbo.config.ApplicationConfig;
@@ -54,6 +52,7 @@ import org.apache.dubbo.config.bootstrap.builders.ReferenceBuilder;
 import org.apache.dubbo.config.bootstrap.builders.RegistryBuilder;
 import org.apache.dubbo.config.bootstrap.builders.ServiceBuilder;
 import org.apache.dubbo.config.context.ConfigManager;
+import org.apache.dubbo.config.metadata.ConfigurableMetadataServiceExporter;
 import org.apache.dubbo.config.utils.ConfigValidationUtils;
 import org.apache.dubbo.config.utils.ReferenceConfigCache;
 import org.apache.dubbo.event.EventDispatcher;
@@ -62,13 +61,14 @@ import org.apache.dubbo.event.GenericEventListener;
 import org.apache.dubbo.metadata.MetadataService;
 import org.apache.dubbo.metadata.MetadataServiceExporter;
 import org.apache.dubbo.metadata.WritableMetadataService;
-import org.apache.dubbo.metadata.report.MetadataReportFactory;
 import org.apache.dubbo.metadata.report.MetadataReportInstance;
 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;
 
@@ -77,7 +77,6 @@ import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.List;
-import java.util.Optional;
 import java.util.Set;
 import java.util.SortedSet;
 import java.util.concurrent.CompletableFuture;
@@ -88,29 +87,25 @@ import java.util.concurrent.locks.Condition;
 import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReentrantLock;
 import java.util.function.Consumer;
-import java.util.function.Supplier;
 import java.util.stream.Collectors;
 
-import static java.lang.String.format;
 import static java.util.Arrays.asList;
 import static java.util.concurrent.Executors.newSingleThreadExecutor;
 import static org.apache.dubbo.common.config.ConfigurationUtils.parseProperties;
 import static org.apache.dubbo.common.config.configcenter.DynamicConfiguration.getDynamicConfiguration;
 import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_METADATA_STORAGE_TYPE;
-import static org.apache.dubbo.common.constants.CommonConstants.REGISTRY_SPLIT_PATTERN;
 import static org.apache.dubbo.common.constants.CommonConstants.REMOTE_METADATA_STORAGE_TYPE;
-import static org.apache.dubbo.common.extension.ExtensionLoader.getExtensionLoader;
 import static org.apache.dubbo.common.function.ThrowableAction.execute;
-import static org.apache.dubbo.common.utils.StringUtils.isEmpty;
 import static org.apache.dubbo.common.utils.StringUtils.isNotEmpty;
+import static org.apache.dubbo.metadata.WritableMetadataService.getExtension;
 import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.setMetadataStorageType;
 import static org.apache.dubbo.remoting.Constants.CLIENT_KEY;
 
 /**
  * See {@link ApplicationModel} and {@link ExtensionLoader} for why this class is designed to be singleton.
- * <p>
+ *
  * The bootstrap class of Dubbo
- * <p>
+ *
  * Get singleton instance by calling static method {@link #getInstance()}.
  * Designed as singleton because some classes inside Dubbo, such as ExtensionLoader, are designed only for one instance per process.
  *
@@ -134,7 +129,7 @@ public class DubboBootstrap extends GenericEventListener {
 
     private final Logger logger = LoggerFactory.getLogger(getClass());
 
-    private static volatile DubboBootstrap instance;
+    private static DubboBootstrap instance;
 
     private final AtomicBoolean awaited = new AtomicBoolean(false);
 
@@ -148,7 +143,7 @@ public class DubboBootstrap extends GenericEventListener {
 
     private final EventDispatcher eventDispatcher = EventDispatcher.getDefaultExtension();
 
-    private final ExecutorRepository executorRepository = getExtensionLoader(ExecutorRepository.class).getDefaultExtension();
+    private final ExecutorRepository executorRepository = ExtensionLoader.getExtensionLoader(ExecutorRepository.class).getDefaultExtension();
 
     private final ConfigManager configManager;
 
@@ -172,7 +167,7 @@ public class DubboBootstrap extends GenericEventListener {
 
     private volatile MetadataService metadataService;
 
-    private volatile Set<MetadataServiceExporter> metadataServiceExporters;
+    private volatile MetadataServiceExporter metadataServiceExporter;
 
     private List<ServiceConfigBase<?>> exportedServices = new ArrayList<>();
 
@@ -183,13 +178,9 @@ public class DubboBootstrap extends GenericEventListener {
     /**
      * See {@link ApplicationModel} and {@link ExtensionLoader} for why DubboBootstrap is designed to be singleton.
      */
-    public static DubboBootstrap getInstance() {
+    public static synchronized DubboBootstrap getInstance() {
         if (instance == null) {
-            synchronized (DubboBootstrap.class) {
-                if (instance == null) {
-                    instance = new DubboBootstrap();
-                }
-            }
+            instance = new DubboBootstrap();
         }
         return instance;
     }
@@ -511,7 +502,7 @@ public class DubboBootstrap extends GenericEventListener {
     /**
      * Initialize
      */
-    public void initialize() {
+    private void initialize() {
         if (!initialized.compareAndSet(false, true)) {
             return;
         }
@@ -520,17 +511,14 @@ public class DubboBootstrap extends GenericEventListener {
 
         startConfigCenter();
 
+        useRegistryAsConfigCenterIfNecessary();
+
         loadRemoteConfigs();
 
         checkGlobalConfigs();
 
-        // @since 2.7.8
-        startMetadataCenter();
-
         initMetadataService();
 
-        initMetadataServiceExports();
-
         initEventListener();
 
         if (logger.isInfoEnabled()) {
@@ -597,9 +585,6 @@ public class DubboBootstrap extends GenericEventListener {
     }
 
     private void startConfigCenter() {
-
-        useRegistryAsConfigCenterIfNecessary();
-
         Collection<ConfigCenterConfig> configCenters = configManager.getConfigCenters();
 
         // check Config Center
@@ -627,10 +612,7 @@ public class DubboBootstrap extends GenericEventListener {
         configManager.refreshAll();
     }
 
-    private void startMetadataCenter() {
-
-        useRegistryAsMetadataCenterIfNecessary();
-
+    private void startMetadataReport() {
         ApplicationConfig applicationConfig = getApplication();
 
         String metadataType = applicationConfig.getMetadataType();
@@ -638,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);
+        }
     }
 
     /**
@@ -666,159 +649,33 @@ public class DubboBootstrap extends GenericEventListener {
             return;
         }
 
-        configManager
-                .getDefaultRegistries()
-                .stream()
-                .filter(this::isUsedRegistryAsConfigCenter)
-                .map(this::registryAsConfigCenter)
-                .forEach(configManager::addConfigCenter);
-    }
-
-    private boolean isUsedRegistryAsConfigCenter(RegistryConfig registryConfig) {
-        return isUsedRegistryAsCenter(registryConfig, registryConfig::getUseAsConfigCenter, "config",
-                DynamicConfigurationFactory.class);
-    }
-
-    private ConfigCenterConfig registryAsConfigCenter(RegistryConfig registryConfig) {
-        String protocol = registryConfig.getProtocol();
-        Integer port = registryConfig.getPort();
-        String id = "config-center-" + protocol + "-" + port;
-        ConfigCenterConfig cc = new ConfigCenterConfig();
-        cc.setId(id);
-        if (cc.getParameters() == null) {
-            cc.setParameters(new HashMap<>());
-        }
-        if (registryConfig.getParameters() != null) {
-            cc.getParameters().putAll(registryConfig.getParameters()); // copy the parameters
-        }
-        cc.getParameters().put(CLIENT_KEY, registryConfig.getClient());
-        cc.setProtocol(protocol);
-        cc.setPort(port);
-        cc.setGroup(registryConfig.getGroup());
-        cc.setAddress(getRegistryCompatibleAddress(registryConfig));
-        cc.setNamespace(registryConfig.getGroup());
-        cc.setUsername(registryConfig.getUsername());
-        cc.setPassword(registryConfig.getPassword());
-        if (registryConfig.getTimeout() != null) {
-            cc.setTimeout(registryConfig.getTimeout().longValue());
-        }
-        cc.setHighestPriority(false);
-        return cc;
-    }
-
-    private void useRegistryAsMetadataCenterIfNecessary() {
-
-        Collection<MetadataReportConfig> metadataConfigs = configManager.getMetadataConfigs();
-
-        if (CollectionUtils.isNotEmpty(metadataConfigs)) {
-            return;
-        }
-
-        configManager
-                .getDefaultRegistries()
-                .stream()
-                .filter(this::isUsedRegistryAsMetadataCenter)
-                .map(this::registryAsMetadataCenter)
-                .forEach(configManager::addMetadataReport);
-
-    }
-
-    private boolean isUsedRegistryAsMetadataCenter(RegistryConfig registryConfig) {
-        return isUsedRegistryAsCenter(registryConfig, registryConfig::getUseAsMetadataCenter, "metadata",
-                MetadataReportFactory.class);
-    }
-
-    /**
-     * Is used the specified registry as a center infrastructure
-     *
-     * @param registryConfig       the {@link RegistryConfig}
-     * @param usedRegistryAsCenter the configured value on
-     * @param centerType           the type name of center
-     * @param extensionClass       an extension class of a center infrastructure
-     * @return
-     * @since 2.7.8
-     */
-    private boolean isUsedRegistryAsCenter(RegistryConfig registryConfig, Supplier<Boolean> usedRegistryAsCenter,
-                                           String centerType,
-                                           Class<?> extensionClass) {
-        final boolean supported;
-
-        Boolean configuredValue = usedRegistryAsCenter.get();
-        if (configuredValue != null) { // If configured, take its value.
-            supported = configuredValue.booleanValue();
-        } else {                       // Or check the extension existence
-            String protocol = registryConfig.getProtocol();
-            supported = supportsExtension(extensionClass, protocol);
-            if (logger.isInfoEnabled()) {
-                logger.info(format("No value is configured in the registry, the %s extension[name : %s] %s as the %s center"
-                        , extensionClass.getSimpleName(), protocol, supported ? "supports" : "does not support", centerType));
-            }
-        }
-
-        if (logger.isInfoEnabled()) {
-            logger.info(format("The registry[%s] will be %s as the %s center", registryConfig,
-                    supported ? "used" : "not used", centerType));
-        }
-        return supported;
-    }
-
-    /**
-     * Supports the extension with the specified class and name
-     *
-     * @param extensionClass the {@link Class} of extension
-     * @param name           the name of extension
-     * @return if supports, return <code>true</code>, or <code>false</code>
-     * @since 2.7.8
-     */
-    private boolean supportsExtension(Class<?> extensionClass, String name) {
-        if (isNotEmpty(name)) {
-            ExtensionLoader extensionLoader = getExtensionLoader(extensionClass);
-            return extensionLoader.hasExtension(name);
-        }
-        return false;
-    }
-
-    private MetadataReportConfig registryAsMetadataCenter(RegistryConfig registryConfig) {
-        String protocol = registryConfig.getProtocol();
-        Integer port = registryConfig.getPort();
-        String id = "metadata-center-" + protocol + "-" + port;
-        MetadataReportConfig metadataReportConfig = new MetadataReportConfig();
-        metadataReportConfig.setId(id);
-        if (metadataReportConfig.getParameters() == null) {
-            metadataReportConfig.setParameters(new HashMap<>());
-        }
-        if (registryConfig.getParameters() != null) {
-            metadataReportConfig.getParameters().putAll(registryConfig.getParameters()); // copy the parameters
-        }
-        metadataReportConfig.getParameters().put(CLIENT_KEY, registryConfig.getClient());
-        metadataReportConfig.setGroup(registryConfig.getGroup());
-        metadataReportConfig.setAddress(getRegistryCompatibleAddress(registryConfig));
-        metadataReportConfig.setUsername(registryConfig.getUsername());
-        metadataReportConfig.setPassword(registryConfig.getPassword());
-        metadataReportConfig.setTimeout(registryConfig.getTimeout());
-        return metadataReportConfig;
-    }
-
-    private String getRegistryCompatibleAddress(RegistryConfig registryConfig) {
-        String registryAddress = registryConfig.getAddress();
-        String[] addresses = REGISTRY_SPLIT_PATTERN.split(registryAddress);
-        if (ArrayUtils.isEmpty(addresses)) {
-            throw new IllegalStateException("Invalid registry address found.");
-        }
-        String address = addresses[0];
-        // since 2.7.8
-        // Issue : https://github.com/apache/dubbo/issues/6476
-        StringBuilder metadataAddressBuilder = new StringBuilder();
-        URL url = URL.valueOf(address);
-        String protocolFromAddress = url.getProtocol();
-        if (isEmpty(protocolFromAddress)) {
-            // If the protocol from address is missing, is like :
-            // "dubbo.registry.address = 127.0.0.1:2181"
-            String protocolFromConfig = registryConfig.getProtocol();
-            metadataAddressBuilder.append(protocolFromConfig).append("://");
-        }
-        metadataAddressBuilder.append(address);
-        return metadataAddressBuilder.toString();
+        configManager.getDefaultRegistries().stream()
+                .filter(registryConfig -> registryConfig.getUseAsConfigCenter() == null || registryConfig.getUseAsConfigCenter())
+                .forEach(registryConfig -> {
+                    String protocol = registryConfig.getProtocol();
+                    String id = "config-center-" + protocol + "-" + registryConfig.getPort();
+                    ConfigCenterConfig cc = new ConfigCenterConfig();
+                    cc.setId(id);
+                    if (cc.getParameters() == null) {
+                        cc.setParameters(new HashMap<>());
+                    }
+                    if (registryConfig.getParameters() != null) {
+                        cc.getParameters().putAll(registryConfig.getParameters());
+                    }
+                    cc.getParameters().put(CLIENT_KEY, registryConfig.getClient());
+                    cc.setProtocol(registryConfig.getProtocol());
+                    cc.setPort(registryConfig.getPort());
+                    cc.setAddress(registryConfig.getAddress());
+                    cc.setNamespace(registryConfig.getGroup());
+                    cc.setUsername(registryConfig.getUsername());
+                    cc.setPassword(registryConfig.getPassword());
+                    if (registryConfig.getTimeout() != null) {
+                        cc.setTimeout(registryConfig.getTimeout().longValue());
+                    }
+                    cc.setHighestPriority(false);
+                    configManager.addConfigCenter(cc);
+                });
+        startConfigCenter();
     }
 
     private void loadRemoteConfigs() {
@@ -857,17 +714,12 @@ public class DubboBootstrap extends GenericEventListener {
 
 
     /**
-     * Initialize {@link #metadataService WritableMetadataService} from {@link WritableMetadataService}'s extension
+     * Initialize {@link MetadataService} from {@link WritableMetadataService}'s extension
      */
     private void initMetadataService() {
-        this.metadataService = WritableMetadataService.getExtension(getMetadataType());
-    }
-
-    /**
-     * Initialize {@link #metadataServiceExporters MetadataServiceExporter}
-     */
-    private void initMetadataServiceExports() {
-        this.metadataServiceExporters = getExtensionLoader(MetadataServiceExporter.class).getSupportedExtensionInstances();
+        startMetadataReport();
+        this.metadataService = getExtension(getMetadataType());
+        this.metadataServiceExporter = new ConfigurableMetadataServiceExporter(metadataService);
     }
 
     /**
@@ -1069,21 +921,13 @@ public class DubboBootstrap extends GenericEventListener {
      * export {@link MetadataService}
      */
     private void exportMetadataService() {
-        metadataServiceExporters
-                .stream()
-                .filter(this::supports)
-                .forEach(MetadataServiceExporter::export);
+        metadataServiceExporter.export();
     }
 
     private void unexportMetadataService() {
-        Optional.ofNullable(metadataServiceExporters)
-                .ifPresent(set -> set.stream()
-                        .filter(this::supports)
-                        .forEach(MetadataServiceExporter::unexport));
-    }
-
-    private boolean supports(MetadataServiceExporter exporter) {
-        return exporter.supports(getMetadataType());
+        if (metadataServiceExporter != null && metadataServiceExporter.isExported()) {
+            metadataServiceExporter.unexport();
+        }
     }
 
     private void exportServices() {
@@ -1176,35 +1020,15 @@ public class DubboBootstrap extends GenericEventListener {
 
         ServiceInstance serviceInstance = createServiceInstance(serviceName, host, port);
 
-        preRegisterServiceInstance(serviceInstance);
+        // register metadata
+        publishMetadataToRemote(serviceInstance);
 
         getServiceDiscoveries().forEach(serviceDiscovery -> serviceDiscovery.register(serviceInstance));
     }
 
-    /**
-     * Pre-register {@link ServiceInstance the service instance}
-     *
-     * @param serviceInstance {@link ServiceInstance the service instance}
-     * @since 2.7.8
-     */
-    private void preRegisterServiceInstance(ServiceInstance serviceInstance) {
-        customizeServiceInstance(serviceInstance);
-    }
-
-    /**
-     * Customize {@link ServiceInstance the service instance}
-     *
-     * @param serviceInstance {@link ServiceInstance the service instance}
-     * @since 2.7.8
-     */
-    private void customizeServiceInstance(ServiceInstance serviceInstance) {
-        ExtensionLoader<ServiceInstanceCustomizer> loader =
-                getExtensionLoader(ServiceInstanceCustomizer.class);
-        // FIXME, sort customizer before apply
-        loader.getSupportedExtensionInstances().forEach(customizer -> {
-            // customizes
-            customizer.customize(serviceInstance);
-        });
+    private void publishMetadataToRemote(ServiceInstance serviceInstance) {
+        RemoteMetadataServiceImpl remoteMetadataService = MetadataUtils.getRemoteMetadataService();
+        remoteMetadataService.publishMetadata(serviceInstance);
     }
 
     private URL selectMetadataServiceExportedURL() {
@@ -1244,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;
     }
 
@@ -1297,7 +1130,7 @@ public class DubboBootstrap extends GenericEventListener {
     }
 
     private void clearConfigs() {
-        configManager.destroy();
+        configManager.clear();
         if (logger.isDebugEnabled()) {
             logger.debug(NAME + "'s configs have been clear.");
         }
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 660ac42..ba9e50b 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
@@ -177,6 +177,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/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/URLRevisionResolver.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/URLRevisionResolver.java
deleted file mode 100644
index f3fe79c..0000000
--- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/URLRevisionResolver.java
+++ /dev/null
@@ -1,152 +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.common.compiler.support.ClassUtils;
-
-import java.util.ArrayList;
-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 java.util.stream.StreamSupport;
-
-import static java.util.Collections.emptyList;
-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}
- *
- * @revised 2.7.8 repackage and refactor
- * @since 2.7.5
- */
-public class URLRevisionResolver {
-
-    /**
-     * @since 2.7.8
-     */
-    public static final String UNKNOWN_REVISION = "X";
-
-    /**
-     * @since 2.7.8
-     */
-    public static final URLRevisionResolver INSTANCE = new URLRevisionResolver();
-
-    /**
-     * Resolve revision as {@link String} from the specified the {@link URL#toFullString() strings} presenting the {@link URL URLs}.
-     *
-     * @param url    one {@link URL}
-     * @param others the others {@link URL}
-     * @return non-null
-     * @since 2.7.8
-     */
-    public String resolve(String url, String... others) {
-        List<String> urls = new ArrayList<>(others.length + 1);
-        urls.add(url);
-        urls.addAll(Arrays.asList(others));
-        return resolve(urls);
-    }
-
-    /**
-     * Resolve revision as {@link String}
-     *
-     * @param urls {@link URL#toFullString() strings} presenting the {@link URL URLs}
-     * @return non-null
-     * @revised 2.7.8 refactor the parameter as the super interface (from Collection to Iterable)
-     */
-    public String resolve(Iterable<String> urls) {
-        List<URL> urlsList = toURLsList(urls);
-        return resolve(urlsList);
-    }
-
-    /**
-     * Resolve revision as {@link String} from the specified the {@link URL URLs}.
-     *
-     * @param urls the {@link URL URLs}
-     * @return non-null
-     * @since 2.7.8
-     */
-    public String resolve(Collection<URL> urls) {
-
-        if (isEmpty(urls)) {
-            return UNKNOWN_REVISION;
-        }
-
-        SortedSet<String> methodSignatures = resolveMethodSignatures(urls);
-
-        SortedSet<String> urlParameters = resolveURLParameters(urls);
-
-        SortedSet<String> values = new TreeSet<>(methodSignatures);
-
-        values.addAll(urlParameters);
-
-        return values.stream()
-                .map(this::hashCode)                     // generate Long hashCode
-                .reduce(Long::sum)                       // sum hashCode
-                .map(Long::toHexString)                  // Using Hex for the shorten content
-                .orElse(UNKNOWN_REVISION);               // NO_REVISION as default
-    }
-
-    private List<URL> toURLsList(Iterable<String> urls) {
-        if (urls == null) {
-            return emptyList();
-        }
-        return StreamSupport.
-                stream(urls.spliterator(), false)
-                .map(URL::valueOf)                             // String to URL
-                .filter(url -> isNotMetadataService(url.getServiceInterface())) // filter not MetadataService interface
-                .collect(Collectors.toList());
-    }
-
-    private SortedSet<String> resolveMethodSignatures(Collection<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-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 6cde356..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,12 +18,9 @@ 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;
-import static org.apache.dubbo.rpc.model.ApplicationModel.getName;
 
 /**
  * Local {@link MetadataService} that extends {@link MetadataService} and provides the modification, which is used for
@@ -31,7 +28,6 @@ import static org.apache.dubbo.rpc.model.ApplicationModel.getName;
  *
  * @since 2.7.5
  */
-@SPI(DEFAULT_METADATA_STORAGE_TYPE)
 public interface WritableMetadataService extends MetadataService {
     /**
      * Gets the current Dubbo Service name
@@ -40,7 +36,7 @@ public interface WritableMetadataService extends MetadataService {
      */
     @Override
     default String serviceName() {
-        return getName();
+        return ApplicationModel.getApplication();
     }
 
     /**
@@ -60,17 +56,6 @@ public interface WritableMetadataService extends MetadataService {
     boolean unexportURL(URL url);
 
     /**
-     * fresh Exports
-     *
-     * @return If success , return <code>true</code>
-     * @deprecated Recommend to use {@link MetadataServiceExporter} since 2.7.8
-     */
-    @Deprecated
-    default boolean refreshMetadata(String exportedRevision, String subscribedRevision) {
-        return true;
-    }
-
-    /**
      * Subscribes a {@link URL}
      *
      * @param url a {@link URL}
@@ -86,13 +71,12 @@ public interface WritableMetadataService extends MetadataService {
      */
     boolean unsubscribeURL(URL url);
 
-    void publishServiceDefinition(URL url);
+    void publishServiceDefinition(URL providerUrl);
 
     /**
      * 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 068d3a0..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,143 +18,43 @@ package org.apache.dubbo.metadata.report;
 
 
 import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.utils.StringUtils;
-import org.apache.dubbo.metadata.URLRevisionResolver;
+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;
 import org.apache.dubbo.metadata.report.identifier.SubscriberMetadataIdentifier;
 
-import com.google.gson.Gson;
-
-import java.util.Collection;
 import java.util.List;
 import java.util.Map;
-import java.util.SortedSet;
-import java.util.TreeSet;
-
-import static java.util.Collections.emptyList;
-import static java.util.Collections.emptySortedSet;
-import static org.apache.dubbo.rpc.model.ApplicationModel.getName;
+import java.util.Set;
 
-/**
- * The interface to report the metadata
- *
- * @see AutoCloseable since 2.7.8
- */
-public interface MetadataReport extends AutoCloseable {
+public interface MetadataReport {
 
     void storeProviderMetadata(MetadataIdentifier providerMetadataIdentifier, ServiceDefinition serviceDefinition);
 
     void storeConsumerMetadata(MetadataIdentifier consumerMetadataIdentifier, Map<String, String> serviceParameterMap);
 
-    /**
-     * @deprecated 2.7.8
-     */
-    @Deprecated
-    default void saveServiceMetadata(ServiceMetadataIdentifier metadataIdentifier, URL url) {
-    }
-
-    /**
-     * @deprecated 2.7.8
-     */
-    @Deprecated
-    default void removeServiceMetadata(ServiceMetadataIdentifier metadataIdentifier) {
-
-    }
-
-    /**
-     * @deprecated 2.7.8
-     */
-    @Deprecated
-    default List<String> getExportedURLs(ServiceMetadataIdentifier metadataIdentifier) {
-        return emptyList();
-    }
-
-    void saveSubscribedData(SubscriberMetadataIdentifier subscriberMetadataIdentifier, Collection<String> urls);
-
-    Collection<String> getSubscribedURLs(SubscriberMetadataIdentifier subscriberMetadataIdentifier);
+    List<String> getExportedURLs(ServiceMetadataIdentifier metadataIdentifier);
 
     String getServiceDefinition(MetadataIdentifier metadataIdentifier);
 
-    /**
-     * Save the exported {@link URL#toFullString() strings} presenting the {@link URL URLs} in bulk.
-     *
-     * @param exportedURLs the exported {@link URL urls}
-     * @return If successful, return <code>true</code>, or <code>false</code>
-     * @since 2.7.8
-     */
-    default boolean saveExportedURLs(SortedSet<String> exportedURLs) {
-        return saveExportedURLs(getName(), exportedURLs);
+    default void publishAppMetadata(SubscriberMetadataIdentifier identifier, MetadataInfo metadataInfo) {
     }
 
-    /**
-     * Save the exported {@link URL#toFullString() strings} presenting the {@link URL URLs} in bulk.
-     *
-     * @param serviceName  the specified Dubbo service name
-     * @param exportedURLs the exported {@link URL urls}
-     * @return If successful, return <code>true</code>, or <code>false</code>
-     * @since 2.7.8
-     */
-    default boolean saveExportedURLs(String serviceName, SortedSet<String> exportedURLs) {
-        return saveExportedURLs(serviceName, new URLRevisionResolver().resolve(exportedURLs), exportedURLs);
+    default MetadataInfo getAppMetadata(SubscriberMetadataIdentifier identifier, Map<String, String> instanceMetadata) {
+        return null;
     }
 
     /**
-     * Save the exported {@link URL#toFullString() strings} presenting the {@link URL URLs} in bulk.
-     *
-     * @param serviceName              the specified Dubbo service name
-     * @param exportedServicesRevision the revision of the exported Services
-     * @param exportedURLs             the exported {@link URL urls}
-     * @return If successful, return <code>true</code>, or <code>false</code>
-     * @since 2.7.8
-     */
-    default boolean saveExportedURLs(String serviceName, String exportedServicesRevision, SortedSet<String> exportedURLs) {
-        Gson gson = new Gson();
-        String content = gson.toJson(exportedURLs);
-        return saveExportedURLs(serviceName, exportedServicesRevision, content);
-    }
+     * deprecated
+     **/
 
-    /**
-     * Save the exported {@link URL#toFullString() strings} presenting the {@link URL URLs} in bulk.
-     *
-     * @param serviceName              the specified Dubbo service name
-     * @param exportedServicesRevision the revision of the exported Services
-     * @param exportedURLsContent      the content of the exported {@link URL urls}
-     * @return If successful, return <code>true</code>, or <code>false</code>
-     * @since 2.7.8
-     */
-    default boolean saveExportedURLs(String serviceName, String exportedServicesRevision, String exportedURLsContent) {
-        return true;
-    }
+    void saveServiceMetadata(ServiceMetadataIdentifier metadataIdentifier, URL url);
 
-    /**
-     * Get the {@link URL#toFullString() strings} presenting the {@link URL URLs} that were exported by the provider
-     *
-     * @param serviceName              the specified Dubbo service name
-     * @param exportedServicesRevision the revision of the exported Services
-     * @return non-null
-     * @since 2.7.8
-     */
-    default SortedSet<String> getExportedURLs(String serviceName, String exportedServicesRevision) {
-        String exportedURLsContent = getExportedURLsContent(serviceName, exportedServicesRevision);
-        if (StringUtils.isBlank(exportedURLsContent)) {
-            return emptySortedSet();
-        }
-        Gson gson = new Gson();
-        return gson.fromJson(exportedURLsContent, TreeSet.class);
-    }
+    void removeServiceMetadata(ServiceMetadataIdentifier metadataIdentifier);
 
-    /**
-     * Get the {@link URL#toFullString() strings} presenting the {@link URL URLs} that were exported by the provider
-     *
-     * @param serviceName              the specified Dubbo service name
-     * @param exportedServicesRevision the revision of the exported Services
-     * @return the content of the exported {@link URL urls} if found, or <code>null</code>
-     * @since 2.7.8
-     */
-    default String getExportedURLsContent(String serviceName, String exportedServicesRevision) {
-        return null;
-    }
+    void saveSubscribedData(SubscriberMetadataIdentifier subscriberMetadataIdentifier, Set<String> urls);
+
+    List<String> getSubscribedURLs(SubscriberMetadataIdentifier subscriberMetadataIdentifier);
 
-}
\ No newline at end of file
+}
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/RemoteWritableMetadataService.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/store/RemoteWritableMetadataService.java
deleted file mode 100644
index c2ae3e5..0000000
--- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/store/RemoteWritableMetadataService.java
+++ /dev/null
@@ -1,122 +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.utils.StringUtils;
-import org.apache.dubbo.metadata.URLRevisionResolver;
-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 java.util.SortedSet;
-
-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.PROVIDER_SIDE;
-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 extends AbstractAbstractWritableMetadataService {
-
-    private final InMemoryWritableMetadataService writableMetadataServiceDelegate;
-
-    private final URLRevisionResolver urlRevisionResolver;
-
-    public RemoteWritableMetadataService() {
-        this.writableMetadataServiceDelegate = (InMemoryWritableMetadataService) WritableMetadataService.getDefaultExtension();
-        urlRevisionResolver = URLRevisionResolver.INSTANCE;
-    }
-
-    public MetadataReport getMetadataReport() {
-        return MetadataReportInstance.getMetadataReport(true);
-    }
-
-    @Override
-    protected void publishConsumerParameters(URL consumerURL) {
-        getMetadataReport().storeConsumerMetadata(new MetadataIdentifier(consumerURL.getServiceInterface(),
-                consumerURL.getParameter(VERSION_KEY), consumerURL.getParameter(GROUP_KEY), CONSUMER_SIDE,
-                consumerURL.getParameter(APPLICATION_KEY)), consumerURL.getParameters());
-    }
-
-    @Override
-    protected void publishProviderServiceDefinition(URL providerURL) {
-        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 . url: " + providerURL.toFullString());
-        } catch (ClassNotFoundException e) {
-            //ignore error
-            logger.error("publishProvider getServiceDescriptor error. url: " + providerURL.toFullString(), e);
-        }
-    }
-
-    @Override
-    public boolean exportURL(URL url) {
-        return writableMetadataServiceDelegate.exportURL(url);
-    }
-
-    @Override
-    public boolean unexportURL(URL url) {
-        return writableMetadataServiceDelegate.unexportURL(url);
-    }
-
-    @Override
-    public boolean subscribeURL(URL url) {
-        return writableMetadataServiceDelegate.subscribeURL(url);
-    }
-
-    @Override
-    public boolean unsubscribeURL(URL url) {
-        return writableMetadataServiceDelegate.unsubscribeURL(url);
-    }
-
-    @Override
-    public SortedSet<String> getExportedURLs(String serviceInterface, String group, String version, String protocol) {
-        return writableMetadataServiceDelegate.getExportedURLs(serviceInterface, group, version, protocol);
-    }
-
-    @Override
-    public String getServiceDefinition(String serviceKey) {
-        return writableMetadataServiceDelegate.getServiceDefinition(serviceKey);
-    }
-
-    @Override
-    public SortedSet<String> getSubscribedURLs() {
-        return writableMetadataServiceDelegate.getSubscribedURLs();
-    }
-
-}
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 26bc942..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.RemoteWritableMetadataService
\ 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 187f126..0000000
--- a/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/InMemoryWritableMetadataServiceTest.java
+++ /dev/null
@@ -1,157 +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.config.ApplicationConfig;
-import org.apache.dubbo.metadata.store.InMemoryWritableMetadataService;
-import org.apache.dubbo.rpc.model.ApplicationModel;
-
-import org.junit.jupiter.api.AfterEach;
-import org.junit.jupiter.api.BeforeEach;
-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");
-
-    @BeforeEach
-    public void init() {
-        ApplicationConfig applicationConfig = new ApplicationConfig("test");
-        ApplicationModel.getConfigManager().setApplication(applicationConfig);
-    }
-
-    @AfterEach
-    public void reset() {
-        ApplicationModel.reset();
-    }
-
-    @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
new file mode 100644
index 0000000..7c95e31
--- /dev/null
+++ b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/store/RemoteWritableMetadataServiceDelegateTest.java
@@ -0,0 +1,216 @@
+/*
+ * 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.
+ */
+
+/*
+ * 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.utils.NetUtils;
+import org.apache.dubbo.metadata.WritableMetadataService;
+import org.apache.dubbo.metadata.report.MetadataReportInstance;
+import org.apache.dubbo.metadata.report.identifier.KeyTypeEnum;
+import org.apache.dubbo.metadata.report.identifier.ServiceMetadataIdentifier;
+import org.apache.dubbo.metadata.report.identifier.SubscriberMetadataIdentifier;
+import org.apache.dubbo.metadata.test.JTestMetadataReport4Test;
+import org.apache.dubbo.rpc.model.ApplicationModel;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.util.SortedSet;
+
+
+/**
+ * 2019-08-27
+ */
+public class RemoteWritableMetadataServiceDelegateTest {
+    static URL metadataURL = URL.valueOf("JTest://" + NetUtils.getLocalAddress().getHostName() + ":4444/org.apache.dubbo.Tes33tService?version=1.0.0&application=vic");
+
+    RemoteWritableMetadataServiceDelegate metadataReportService;
+
+    String interfaceName = "org.apache.dubbo.metadata.store.InterfaceNameTestService80", version = "0.6.9", group = null;
+    URL url = URL.valueOf("dubbo://" + NetUtils.getLocalAddress().getHostName() + ":4444/?interface=" + interfaceName + "&version="
+            + version + "&application=vicpubprovder&side=provider");
+
+    @BeforeAll
+    public static void beforeAll() {
+        MetadataReportInstance.init(metadataURL);
+    }
+
+    @BeforeEach
+    public void before() {
+        metadataReportService = new RemoteWritableMetadataServiceDelegate();
+    }
+
+
+    @Test
+    public void testInstance() {
+        WritableMetadataService metadataReportService1 = WritableMetadataService.getExtension("remote");
+        WritableMetadataService metadataReportService2 = WritableMetadataService.getExtension("remote");
+        Assertions.assertSame(metadataReportService1, metadataReportService2);
+        Assertions.assertTrue(metadataReportService1 instanceof RemoteWritableMetadataServiceDelegate);
+    }
+
+    @Test
+    public void testPublishServiceDefinition() throws InterruptedException {
+        String interfaceName = "org.apache.dubbo.metadata.store.InterfaceNameTestService2", version = "0.9.9", group = null;
+        URL tmpUrl = URL.valueOf("dubbo://" + NetUtils.getLocalAddress().getHostName() + ":4444/?interface=" + interfaceName + "&version="
+                + version + "&application=vicpubprovder&side=provider");
+        metadataReportService.publishServiceDefinition(tmpUrl);
+        Thread.sleep(150);
+        String v = metadataReportService.getServiceDefinition(interfaceName, version, group);
+        Assertions.assertNotNull(v);
+    }
+
+    @Test
+    public void testExportURL() throws InterruptedException {
+        URL url = URL.valueOf("dubbo://" + NetUtils.getLocalAddress().getHostName() + ":4444/org.apache.dubbo.Test567Service?version=1.0.44&application=vicpubprovder&side=provider");
+        metadataReportService.exportURL(url);
+        Thread.sleep(100);
+        Assertions.assertTrue(getInMemoryWriableMetadataService().exportedServiceURLs.size() == 1);
+        Assertions.assertEquals(getInMemoryWriableMetadataService().exportedServiceURLs.get(url.getServiceKey()).first(), url);
+    }
+
+    @Test
+    public void testSubscribeURL() throws InterruptedException {
+        URL url = URL.valueOf("subscriber://" + NetUtils.getLocalAddress().getHostName() + ":4444/org.apache.dubbo.Test0678Service?version=1.3.144&application=vicpubprovder&side=provider");
+        int origSize = getInMemoryWriableMetadataService().subscribedServiceURLs.size();
+        metadataReportService.subscribeURL(url);
+        Thread.sleep(100);
+        int size = getInMemoryWriableMetadataService().subscribedServiceURLs.size();
+        Assertions.assertTrue(size - origSize == 1);
+        Assertions.assertEquals(getInMemoryWriableMetadataService().subscribedServiceURLs.get(url.getServiceKey()).first(), url);
+    }
+
+    @Test
+    public void testUnExportURL() throws InterruptedException {
+        URL url = URL.valueOf("dubbo://" + NetUtils.getLocalAddress().getHostName() + ":4444/org.apache.dubbo.Test0567Service?version=1.2.44&application=vicpubprovder&side=provider");
+        int origSize = getInMemoryWriableMetadataService().exportedServiceURLs.size();
+        metadataReportService.exportURL(url);
+        Thread.sleep(100);
+        int size = getInMemoryWriableMetadataService().exportedServiceURLs.size();
+        Assertions.assertTrue(size - origSize == 1);
+        Assertions.assertEquals(getInMemoryWriableMetadataService().exportedServiceURLs.get(url.getServiceKey()).first(), url);
+
+        metadataReportService.unexportURL(url);
+        int unexportSize = getInMemoryWriableMetadataService().exportedServiceURLs.size();
+        Assertions.assertTrue(size - unexportSize == 1);
+    }
+
+    @Test
+    public void testUnSubscribeURL() throws InterruptedException {
+        URL url = URL.valueOf("subscriber://" + NetUtils.getLocalAddress().getHostName() + ":4444/org.apache.dubbo.Test0678Service?version=1.5.477&application=vicpubprovder&side=provider");
+        int origSize = getInMemoryWriableMetadataService().subscribedServiceURLs.size();
+        metadataReportService.subscribeURL(url);
+        Thread.sleep(100);
+        int size = getInMemoryWriableMetadataService().subscribedServiceURLs.size();
+        Assertions.assertTrue(size - origSize == 1);
+        Assertions.assertEquals(getInMemoryWriableMetadataService().subscribedServiceURLs.get(url.getServiceKey()).first(), url);
+
+        metadataReportService.unsubscribeURL(url);
+        Thread.sleep(100);
+        Assertions.assertTrue(getInMemoryWriableMetadataService().subscribedServiceURLs.size() == 0);
+    }
+
+    @Test
+    public void testRefreshMetadataService() throws InterruptedException {
+        URL publishUrl = URL.valueOf("dubbo://" + NetUtils.getLocalAddress().getHostName() + ":4444/org.apache.dubbo.TestRefreshMetadataService?version=1.6.8&application=vicpubprovder&side=provider");
+        URL publishUrl2 = URL.valueOf("dubbo://" + NetUtils.getLocalAddress().getHostName() + ":4444/org.apache.dubbo.TestRefreshMetadata2Service?version=1.6.5&application=vicpubprovder&side=provider");
+        metadataReportService.exportURL(publishUrl);
+        metadataReportService.exportURL(publishUrl2);
+        String exportedRevision = "9999";
+        JTestMetadataReport4Test jTestMetadataReport4Test = (JTestMetadataReport4Test) MetadataReportInstance.getMetadataReports(true);
+        int origSize = jTestMetadataReport4Test.store.size();
+        int num = countNum();
+        Assertions.assertTrue(metadataReportService.refreshMetadata(exportedRevision, "1109"));
+        Thread.sleep(200);
+        int size = jTestMetadataReport4Test.store.size();
+        Assertions.assertTrue(size - origSize == num);
+        Assertions.assertEquals(jTestMetadataReport4Test.store.get(getServiceMetadataIdentifier(publishUrl, exportedRevision).getUniqueKey(KeyTypeEnum.UNIQUE_KEY)), publishUrl.toFullString());
+        Assertions.assertEquals(jTestMetadataReport4Test.store.get(getServiceMetadataIdentifier(publishUrl2, exportedRevision).getUniqueKey(KeyTypeEnum.UNIQUE_KEY)), publishUrl2.toFullString());
+    }
+
+
+    // unstable test
+//    @Test
+//    public void testRefreshMetadataSubscription() throws InterruptedException {
+//        URL subscriberUrl1 = URL.valueOf("subscriber://" + NetUtils.getLocalAddress().getHostName() + ":4444/org.apache.dubbo.TestRefreshMetadata00Service?version=2.0.8&application=vicpubprovder&side=provider");
+//        URL subscriberUrl2 = URL.valueOf("subscriber://" + NetUtils.getLocalAddress().getHostName() + ":4444/org.apache.dubbo.TestRefreshMetadata09Service?version=2.0.5&application=vicpubprovder&side=provider");
+//        metadataReportService.subscribeURL(subscriberUrl1);
+//        metadataReportService.subscribeURL(subscriberUrl2);
+//        String exportedRevision = "9999";
+//        String subscriberRevision = "2099";
+//        String applicationName = "wriableMetadataService";
+//        JTestMetadataReport4Test jTestMetadataReport4Test = (JTestMetadataReport4Test) MetadataReportInstance.getMetadataReport(true);
+//        int origSize = jTestMetadataReport4Test.store.size();
+//        ApplicationModel.setApplication(applicationName);
+//        Assertions.assertTrue(metadataReportService.refreshMetadata(exportedRevision, subscriberRevision));
+//        Thread.sleep(200);
+//        int size = jTestMetadataReport4Test.store.size();
+//        Assertions.assertTrue(size - origSize == 1);
+//        String r = jTestMetadataReport4Test.store.get(getSubscriberMetadataIdentifier(
+//                subscriberRevision).getUniqueKey(KeyTypeEnum.UNIQUE_KEY));
+//        Assertions.assertNotNull(r);
+//    }
+
+
+    private ServiceMetadataIdentifier getServiceMetadataIdentifier(URL publishUrl, String exportedRevision) {
+        ServiceMetadataIdentifier serviceMetadataIdentifier = new ServiceMetadataIdentifier(publishUrl);
+        serviceMetadataIdentifier.setRevision(exportedRevision);
+        serviceMetadataIdentifier.setProtocol(publishUrl.getProtocol());
+        return serviceMetadataIdentifier;
+    }
+
+    private SubscriberMetadataIdentifier getSubscriberMetadataIdentifier(String subscriberRevision) {
+        SubscriberMetadataIdentifier subscriberMetadataIdentifier = new SubscriberMetadataIdentifier();
+        subscriberMetadataIdentifier.setRevision(subscriberRevision);
+        subscriberMetadataIdentifier.setApplication(ApplicationModel.getApplication());
+        return subscriberMetadataIdentifier;
+    }
+
+    private InMemoryWritableMetadataService getInMemoryWriableMetadataService() {
+        return (InMemoryWritableMetadataService) metadataReportService.defaultWritableMetadataService;
+    }
+
+    private int countNum() {
+        int num = 0;
+        for (SortedSet<URL> tmp : getInMemoryWriableMetadataService().exportedServiceURLs.values()) {
+            num += tmp.size();
+        }
+        if (!getInMemoryWriableMetadataService().subscribedServiceURLs.values().isEmpty()) {
+            num++;
+        }
+        return num;
+    }
+}
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 f739ca8..0000000
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/event/listener/CustomizableServiceInstanceListener.java
+++ /dev/null
@@ -1,44 +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
- * @deprecated 2.7.8 Current class will be removed since 3.0.0
- */
-@Deprecated
-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 951f957..0000000
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/ExportedServicesRevisionMetadataCustomizer.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.URLRevisionResolver;
-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 14c7988..e55f6f2 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 c9e6b04..0000000
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/SubscribedServicesRevisionMetadataCustomizer.java
+++ /dev/null
@@ -1,58 +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.URLRevisionResolver;
-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/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 d599891..0000000
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/proxy/BaseMetadataServiceProxyFactory.java
+++ /dev/null
@@ -1,58 +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 java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
-
-import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.getExportedServicesRevision;
-
-/**
- * base class for remote and local implementations.
- *
- * @since 2.7.5
- */
-abstract class BaseMetadataServiceProxyFactory implements MetadataServiceProxyFactory {
-
-    private final ConcurrentMap<String, MetadataService> proxiesCache = new ConcurrentHashMap<>();
-
-    public final MetadataService getProxy(ServiceInstance serviceInstance) {
-        return proxiesCache.computeIfAbsent(createProxyCacheKey(serviceInstance), id -> createProxy(serviceInstance));
-    }
-
-    /**
-     * Create the cache key of the proxy of {@link MetadataService}
-     *
-     * @param serviceInstance {@link ServiceInstance}
-     * @return non-null
-     * @since 2.7.8
-     */
-    protected String createProxyCacheKey(ServiceInstance serviceInstance) {
-        return serviceInstance.getServiceName() + "#" + getExportedServicesRevision(serviceInstance);
-    }
-
-    /**
-     * Create the instance proxy of {@link MetadataService}
-     *
-     * @param serviceInstance {@link ServiceInstance}
-     * @return non-null
-     */
-    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 e49003f..0000000
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/proxy/MetadataServiceProxyFactory.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.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 a92cafb..0000000
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/proxy/RemoteMetadataServiceProxy.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.proxy;
-
-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.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.registry.client.ServiceInstance;
-
-import java.util.Set;
-import java.util.SortedSet;
-import java.util.TreeSet;
-
-import static java.util.Collections.unmodifiableSortedSet;
-import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY;
-import static org.apache.dubbo.common.constants.CommonConstants.PROVIDER_SIDE;
-import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY;
-import static org.apache.dubbo.metadata.URLRevisionResolver.UNKNOWN_REVISION;
-import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.EXPORTED_SERVICES_REVISION_PROPERTY_NAME;
-
-/**
- * The proxy of {@link MetadataService} is based on {@link MetadataReport}
- *
- * @since 2.7.5
- */
-public class RemoteMetadataServiceProxy implements MetadataService {
-
-    protected final Logger logger = LoggerFactory.getLogger(getClass());
-
-    private final String serviceName;
-
-    private final String revision;
-
-    public RemoteMetadataServiceProxy(ServiceInstance serviceInstance) {
-        this.serviceName = serviceInstance.getServiceName();
-        // this is ServiceInstance of registry(Provider)
-        this.revision = serviceInstance.getMetadata(EXPORTED_SERVICES_REVISION_PROPERTY_NAME, UNKNOWN_REVISION);
-    }
-
-    @Override
-    public String serviceName() {
-        return serviceName;
-    }
-
-    @Override
-    public SortedSet<String> getExportedURLs(String serviceInterface, String group, String version, String protocol) {
-
-        SortedSet<String> exportedURLs = getMetadataReport().getExportedURLs(serviceName, revision);
-        if (ALL_SERVICE_INTERFACES.equals(serviceInterface)) {
-            return exportedURLs;
-        }
-
-        return unmodifiableSortedSet(
-                exportedURLs
-                        .stream()
-                        .map(URL::valueOf)
-                        .filter(url -> serviceInterface == null || serviceInterface.equals(url.getServiceInterface()))
-                        .filter(url -> group == null || group.equals(url.getParameter(GROUP_KEY)))
-                        .filter(url -> version == null || version.equals(url.getParameter(VERSION_KEY)))
-                        .filter(url -> protocol == null || protocol.equals(url.getProtocol()))
-                        .map(URL::toFullString)
-                        .collect(TreeSet::new, Set::add, Set::addAll)
-        );
-    }
-
-    @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 serviceDefinitionKey) {
-        String[] services = UrlUtils.parseServiceKey(serviceDefinitionKey);
-        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 71%
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 a897d76..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,13 +14,21 @@
  * 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.BaseServiceMetadata;
 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;
 import org.apache.dubbo.metadata.definition.model.ServiceDefinition;
+import org.apache.dubbo.rpc.support.ProtocolUtils;
+
+import com.google.gson.Gson;
 
 import java.util.Comparator;
 import java.util.Map;
@@ -33,11 +41,12 @@ import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReentrantLock;
 
 import static java.util.Collections.emptySortedSet;
-import static java.util.Collections.unmodifiableSortedMap;
 import static java.util.Collections.unmodifiableSortedSet;
 import static org.apache.dubbo.common.URL.buildKey;
+import static org.apache.dubbo.common.constants.CommonConstants.INTERFACE_KEY;
 import static org.apache.dubbo.common.constants.CommonConstants.PROTOCOL_KEY;
 import static org.apache.dubbo.common.utils.CollectionUtils.isEmpty;
+import static org.apache.dubbo.rpc.Constants.GENERIC_KEY;
 
 /**
  * The {@link WritableMetadataService} implementation stores the metadata of Dubbo services in memory locally when they
@@ -47,7 +56,9 @@ import static org.apache.dubbo.common.utils.CollectionUtils.isEmpty;
  * @see WritableMetadataService
  * @since 2.7.5
  */
-public class InMemoryWritableMetadataService extends AbstractAbstractWritableMetadataService {
+public class InMemoryWritableMetadataService implements WritableMetadataService {
+
+    final Logger logger = LoggerFactory.getLogger(getClass());
 
     private final Lock lock = new ReentrantLock();
 
@@ -57,7 +68,8 @@ public class InMemoryWritableMetadataService extends AbstractAbstractWritableMet
      * 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}
      */
-    private final ConcurrentNavigableMap<String, SortedSet<URL>> exportedServiceURLs = new ConcurrentSkipListMap<>();
+    ConcurrentNavigableMap<String, SortedSet<URL>> exportedServiceURLs = new ConcurrentSkipListMap<>();
+    MetadataInfo metadataInfo;
 
     // ==================================================================================== //
 
@@ -68,13 +80,13 @@ public class InMemoryWritableMetadataService extends AbstractAbstractWritableMet
      * whose key is the return value of {@link URL#getServiceKey()} method and value is
      * the {@link SortedSet sorted set} of the {@link URL URLs}
      */
-    private final ConcurrentNavigableMap<String, SortedSet<URL>> subscribedServiceURLs = new ConcurrentSkipListMap<>();
+    ConcurrentNavigableMap<String, SortedSet<URL>> subscribedServiceURLs = new ConcurrentSkipListMap<>();
 
-    /**
-     * The {@link Map} caches the json of {@link ServiceDefinition} with
-     * {@link BaseServiceMetadata#buildServiceKey(String, String, String) the service key}
-     */
-    private final ConcurrentNavigableMap<String, String> serviceDefinitions = new ConcurrentSkipListMap<>();
+    ConcurrentNavigableMap<String, String> serviceDefinitions = new ConcurrentSkipListMap<>();
+
+    public InMemoryWritableMetadataService() {
+        this.metadataInfo = new MetadataInfo();
+    }
 
     @Override
     public SortedSet<String> getSubscribedURLs() {
@@ -107,11 +119,15 @@ public class InMemoryWritableMetadataService extends AbstractAbstractWritableMet
 
     @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);
     }
 
@@ -126,25 +142,38 @@ public class InMemoryWritableMetadataService extends AbstractAbstractWritableMet
     }
 
     @Override
-    protected void publishServiceDefinition(String key, String json) {
-        serviceDefinitions.put(key, json);
+    public void publishServiceDefinition(URL providerUrl) {
+        try {
+            String interfaceName = providerUrl.getParameter(INTERFACE_KEY);
+            if (StringUtils.isNotEmpty(interfaceName)
+                    && !ProtocolUtils.isGeneric(providerUrl.getParameter(GENERIC_KEY))) {
+                Class interfaceClass = Class.forName(interfaceName);
+                ServiceDefinition serviceDefinition = ServiceDefinitionBuilder.build(interfaceClass);
+                Gson gson = new Gson();
+                String data = gson.toJson(serviceDefinition);
+                serviceDefinitions.put(providerUrl.getServiceKey(), data);
+                return;
+            }
+            logger.error("publishProvider interfaceName is empty . providerUrl: " + providerUrl.toFullString());
+        } catch (ClassNotFoundException e) {
+            //ignore error
+            logger.error("publishProvider getServiceDescriptor error. providerUrl: " + providerUrl.toFullString(), e);
+        }
     }
 
     @Override
-    public String getServiceDefinition(String serviceDefinitionKey) {
-        return serviceDefinitions.get(serviceDefinitionKey);
-    }
-
-    public Map<String, SortedSet<URL>> getExportedServiceURLs() {
-        return unmodifiableSortedMap(exportedServiceURLs);
+    public String getServiceDefinition(String interfaceName, String version, String group) {
+        return serviceDefinitions.get(URL.buildKey(interfaceName, group, version));
     }
 
-    public Map<String, SortedSet<URL>> getSubscribedServiceURLs() {
-        return unmodifiableSortedMap(subscribedServiceURLs);
+    @Override
+    public String getServiceDefinition(String serviceKey) {
+        return serviceDefinitions.get(serviceKey);
     }
 
-    public Map<String, String> getServiceDefinitions() {
-        return unmodifiableSortedMap(serviceDefinitions);
+    @Override
+    public MetadataInfo getMetadataInfo() {
+        return metadataInfo;
     }
 
     boolean addURL(Map<String, SortedSet<URL>> serviceURLs, URL url) {
@@ -210,6 +239,7 @@ public class InMemoryWritableMetadataService extends AbstractAbstractWritableMet
                 || protocol.equals(url.getProtocol());
     }
 
+
     static class URLComparator implements Comparator<URL> {
 
         public static final URLComparator INSTANCE = new URLComparator();
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 16f5446..9953a28 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 a7fb58d..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,4 +1 @@
-# "service-instance" has been deprecated since 2.7.8
-# service-instance=org.apache.dubbo.registry.client.event.listener.CustomizableServiceInstanceListener
-
-registry-logging=org.apache.dubbo.registry.client.event.listener.LoggingEventListener
+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);
     }
 }


[dubbo] 12/27: set metadata proxy timeout

Posted by li...@apache.org.
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

commit a001f983b31b7174f01d9781b7921dddd6115c77
Author: ken.lj <ke...@gmail.com>
AuthorDate: Fri Jul 17 17:12:09 2020 +0800

    set metadata proxy timeout
---
 .../rpc/cluster/directory/MockDirInvocation.java   |   5 +
 .../router/condition/ConditionRouterTest.java      |   2 +-
 .../support/AbstractClusterInvokerTest.java        |   6 +-
 .../org/apache/dubbo/common/ConfigurationURL.java  |  12 +-
 .../src/main/java/org/apache/dubbo/common/URL.java | 361 ++++++++++++++++++---
 .../org/apache/dubbo/config/cache/CacheTest.java   |   2 +-
 .../apache/dubbo/metadata/MetadataConstants.java   |   2 +
 .../org/apache/dubbo/metadata/MetadataInfo.java    |  18 +
 .../dubbo/monitor/support/MonitorFilterTest.java   |   8 +-
 .../registry/client/DefaultServiceInstance.java    |   8 +
 .../dubbo/registry/client/InstanceAddressURL.java  | 187 +++++++++--
 .../client/ServiceDiscoveryRegistryDirectory.java  |  72 ++--
 .../dubbo/registry/client/ServiceInstance.java     |   2 +
 .../listener/ServiceInstancesChangedListener.java  |  14 +-
 .../registry/client/metadata/MetadataUtils.java    |   3 +-
 .../StandardMetadataServiceURLBuilder.java         |   7 +-
 .../main/java/org/apache/dubbo/rpc/Invocation.java |   2 +
 .../main/java/org/apache/dubbo/rpc/RpcContext.java |  66 ++--
 .../java/org/apache/dubbo/rpc/RpcInvocation.java   |  32 +-
 .../org/apache/dubbo/rpc/filter/GenericFilter.java |   2 +-
 .../apache/dubbo/rpc/protocol/AbstractInvoker.java |   2 +-
 .../dubbo/rpc/proxy/AbstractProxyInvoker.java      |   2 +-
 .../dubbo/rpc/proxy/InvokerInvocationHandler.java  |   9 +-
 .../dubbo/rpc/filter/ExceptionFilterTest.java      |   8 +-
 .../apache/dubbo/rpc/filter/GenericFilterTest.java |   8 +-
 .../dubbo/rpc/filter/GenericImplFilterTest.java    |   8 +-
 .../apache/dubbo/rpc/proxy/AbstractProxyTest.java  |   4 +-
 .../apache/dubbo/rpc/support/MockInvocation.java   |   5 +
 .../org/apache/dubbo/rpc/support/RpcUtilsTest.java |  48 +--
 .../rpc/protocol/dubbo/CallbackServiceCodec.java   |  10 +-
 .../dubbo/rpc/protocol/dubbo/DubboProtocol.java    |   7 +-
 .../dubbo/ReferenceCountExchangeClient.java        |   9 +-
 32 files changed, 674 insertions(+), 257 deletions(-)

diff --git a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/directory/MockDirInvocation.java b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/directory/MockDirInvocation.java
index 7a4ffb9..bc237b9 100644
--- a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/directory/MockDirInvocation.java
+++ b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/directory/MockDirInvocation.java
@@ -52,6 +52,11 @@ public class MockDirInvocation implements Invocation {
         return null;
     }
 
+    @Override
+    public String getProtocolServiceKey() {
+        return null;
+    }
+
     public String getMethodName() {
         return "echo";
     }
diff --git a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/condition/ConditionRouterTest.java b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/condition/ConditionRouterTest.java
index 00fdf53..204ce9a 100644
--- a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/condition/ConditionRouterTest.java
+++ b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/router/condition/ConditionRouterTest.java
@@ -137,7 +137,7 @@ public class ConditionRouterTest {
 
     @Test
     public void testRoute_methodRoute() {
-        Invocation invocation = new RpcInvocation("getFoo", "com.foo.BarService", new Class<?>[0], new Object[0]);
+        Invocation invocation = new RpcInvocation("getFoo", "com.foo.BarService", "", new Class<?>[0], new Object[0]);
         // More than one methods, mismatch
         Router router = new ConditionRouterFactory().getRouter(getRouteUrl("methods=getFoo => host = 1.2.3.4"));
         boolean matchWhen = ((ConditionRouter) router).matchWhen(
diff --git a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/AbstractClusterInvokerTest.java b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/AbstractClusterInvokerTest.java
index a6ec35b..5300058 100644
--- a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/AbstractClusterInvokerTest.java
+++ b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/AbstractClusterInvokerTest.java
@@ -494,21 +494,21 @@ public class AbstractClusterInvokerTest {
         Directory<DemoService> directory = new StaticDirectory<DemoService>(invokers);
         FailoverClusterInvoker<DemoService> failoverClusterInvoker = new FailoverClusterInvoker<DemoService>(directory);
         try {
-            failoverClusterInvoker.invoke(new RpcInvocation("sayHello", DemoService.class.getName(), new Class<?>[0], new Object[0]));
+            failoverClusterInvoker.invoke(new RpcInvocation("sayHello", DemoService.class.getName(), "", new Class<?>[0], new Object[0]));
             Assertions.fail();
         } catch (RpcException e) {
             Assertions.assertEquals(RpcException.TIMEOUT_EXCEPTION, e.getCode());
         }
         ForkingClusterInvoker<DemoService> forkingClusterInvoker = new ForkingClusterInvoker<DemoService>(directory);
         try {
-            forkingClusterInvoker.invoke(new RpcInvocation("sayHello", DemoService.class.getName(), new Class<?>[0], new Object[0]));
+            forkingClusterInvoker.invoke(new RpcInvocation("sayHello", DemoService.class.getName(), "", new Class<?>[0], new Object[0]));
             Assertions.fail();
         } catch (RpcException e) {
             Assertions.assertEquals(RpcException.TIMEOUT_EXCEPTION, e.getCode());
         }
         FailfastClusterInvoker<DemoService> failfastClusterInvoker = new FailfastClusterInvoker<DemoService>(directory);
         try {
-            failfastClusterInvoker.invoke(new RpcInvocation("sayHello", DemoService.class.getName(), new Class<?>[0], new Object[0]));
+            failfastClusterInvoker.invoke(new RpcInvocation("sayHello", DemoService.class.getName(), "", new Class<?>[0], new Object[0]));
             Assertions.fail();
         } catch (RpcException e) {
             Assertions.assertEquals(RpcException.TIMEOUT_EXCEPTION, e.getCode());
diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataConstants.java b/dubbo-common/src/main/java/org/apache/dubbo/common/ConfigurationURL.java
similarity index 56%
copy from dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataConstants.java
copy to dubbo-common/src/main/java/org/apache/dubbo/common/ConfigurationURL.java
index 7ba0a43..2042277 100644
--- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataConstants.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/ConfigurationURL.java
@@ -14,15 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.metadata;
+package org.apache.dubbo.common;
 
-public class MetadataConstants {
-    public static final String KEY_SEPARATOR = ":";
-    public static final String DEFAULT_PATH_TAG = "metadata";
-    public static final String KEY_REVISON_PREFIX = "revision";
-    public static final String META_DATA_STORE_TAG = ".metaData";
-    public static final String SERVICE_META_DATA_STORE_TAG = ".smd";
-    public static final String CONSUMER_META_DATA_STORE_TAG = ".cmd";
-    public static final String METADATA_PUBLISH_DELAY_KEY = "dubbo.application.metadata.delay";
-    public static final int DEFAULT_METADATA_PUBLISH_DELAY = 5000;
+public class ConfigurationURL extends URL {
 }
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/URL.java b/dubbo-common/src/main/java/org/apache/dubbo/common/URL.java
index e645822..f227353 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/URL.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/URL.java
@@ -589,41 +589,6 @@ class URL implements Serializable {
         return Collections.unmodifiableMap(selectedParameters);
     }
 
-    public Map<String, Map<String, String>> getMethodParameters() {
-        return methodParameters;
-    }
-
-    public String getParameterAndDecoded(String key) {
-        return getParameterAndDecoded(key, null);
-    }
-
-    public String getParameterAndDecoded(String key, String defaultValue) {
-        return decode(getParameter(key, defaultValue));
-    }
-
-    public String getParameter(String key) {
-        return parameters.get(key);
-    }
-
-    public String getParameter(String key, String defaultValue) {
-        String value = getParameter(key);
-        return StringUtils.isEmpty(value) ? defaultValue : value;
-    }
-
-    public String[] getParameter(String key, String[] defaultValue) {
-        String value = getParameter(key);
-        return StringUtils.isEmpty(value) ? defaultValue : COMMA_SPLIT_PATTERN.split(value);
-    }
-
-    public List<String> getParameter(String key, List<String> defaultValue) {
-        String value = getParameter(key);
-        if (StringUtils.isEmpty(value)) {
-            return defaultValue;
-        }
-        String[] strArray = COMMA_SPLIT_PATTERN.split(value);
-        return Arrays.asList(strArray);
-    }
-
     /**
      * Get parameter
      *
@@ -659,7 +624,43 @@ class URL implements Serializable {
         return result;
     }
 
-    private Map<String, Number> getNumbers() {
+
+    public Map<String, Map<String, String>> getMethodParameters() {
+        return methodParameters;
+    }
+
+    public String getParameterAndDecoded(String key) {
+        return getParameterAndDecoded(key, null);
+    }
+
+    public String getParameterAndDecoded(String key, String defaultValue) {
+        return decode(getParameter(key, defaultValue));
+    }
+
+    public String getParameter(String key) {
+        return parameters.get(key);
+    }
+
+    public String getParameter(String key, String defaultValue) {
+        String value = getParameter(key);
+        return StringUtils.isEmpty(value) ? defaultValue : value;
+    }
+
+    public String[] getParameter(String key, String[] defaultValue) {
+        String value = getParameter(key);
+        return StringUtils.isEmpty(value) ? defaultValue : COMMA_SPLIT_PATTERN.split(value);
+    }
+
+    public List<String> getParameter(String key, List<String> defaultValue) {
+        String value = getParameter(key);
+        if (StringUtils.isEmpty(value)) {
+            return defaultValue;
+        }
+        String[] strArray = COMMA_SPLIT_PATTERN.split(value);
+        return Arrays.asList(strArray);
+    }
+
+    protected Map<String, Number> getNumbers() {
         // concurrent initialization is tolerant
         if (numbers == null) {
             numbers = new ConcurrentHashMap<>();
@@ -667,7 +668,7 @@ class URL implements Serializable {
         return numbers;
     }
 
-    private Map<String, Map<String, Number>> getMethodNumbers() {
+    protected Map<String, Map<String, Number>> getMethodNumbers() {
         if (methodNumbers == null) { // concurrent initialization is tolerant
             methodNumbers = new ConcurrentHashMap<>();
         }
@@ -852,7 +853,7 @@ class URL implements Serializable {
     }
 
     public String getMethodParameterStrict(String method, String key) {
-        Map<String, String> keyMap = methodParameters.get(method);
+        Map<String, String> keyMap = getMethodParameters().get(method);
         String value = null;
         if (keyMap != null) {
             value = keyMap.get(key);
@@ -861,7 +862,7 @@ class URL implements Serializable {
     }
 
     public String getMethodParameter(String method, String key) {
-        Map<String, String> keyMap = methodParameters.get(method);
+        Map<String, String> keyMap = getMethodParameters().get(method);
         String value = null;
         if (keyMap != null) {
             value = keyMap.get(key);
@@ -1710,16 +1711,276 @@ class URL implements Serializable {
         subParameter.put(key, value);
     }
 
-//    public String getServiceParameter(String service, String key) {
-//        return getParameter(key);
-//    }
-//
-//    public String getServiceMethodParameter(String service, String key) {
-//        return getParameter(key);
-//    }
-//
-//    public String getServiceParameter(String service, String key) {
-//        return getParameter(key);
-//    }
+    /* add service scope operations, see InstanceAddressURL */
+    public Map<String, String> getServiceParameters(String service) {
+        return getParameters();
+    }
+
+    public String getServiceParameter(String service, String key) {
+        return getParameter(key);
+    }
+
+    public String getServiceParameter(String service, String key, String defaultValue) {
+        String value = getServiceParameter(service, key);
+        return StringUtils.isEmpty(value) ? defaultValue : value;
+    }
+
+    public int getServiceParameter(String service, String key, int defaultValue) {
+        return getParameter(key, defaultValue);
+    }
+
+    public double getServiceParameter(String service, String key, double defaultValue) {
+        Number n = getServiceNumbers(service).get(key);
+        if (n != null) {
+            return n.doubleValue();
+        }
+        String value = getServiceParameter(service, key);
+        if (StringUtils.isEmpty(value)) {
+            return defaultValue;
+        }
+        double d = Double.parseDouble(value);
+        getNumbers().put(key, d);
+        return d;
+    }
+
+    public float getServiceParameter(String service, String key, float defaultValue) {
+        Number n = getNumbers().get(key);
+        if (n != null) {
+            return n.floatValue();
+        }
+        String value = getServiceParameter(service, key);
+        if (StringUtils.isEmpty(value)) {
+            return defaultValue;
+        }
+        float f = Float.parseFloat(value);
+        getNumbers().put(key, f);
+        return f;
+    }
+
+    public long getServiceParameter(String service, String key, long defaultValue) {
+        Number n = getNumbers().get(key);
+        if (n != null) {
+            return n.longValue();
+        }
+        String value = getServiceParameter(service, key);
+        if (StringUtils.isEmpty(value)) {
+            return defaultValue;
+        }
+        long l = Long.parseLong(value);
+        getNumbers().put(key, l);
+        return l;
+    }
+
+    public short getServiceParameter(String service, String key, short defaultValue) {
+        Number n = getNumbers().get(key);
+        if (n != null) {
+            return n.shortValue();
+        }
+        String value = getServiceParameter(service, key);
+        if (StringUtils.isEmpty(value)) {
+            return defaultValue;
+        }
+        short s = Short.parseShort(value);
+        getNumbers().put(key, s);
+        return s;
+    }
+
+    public byte getServiceParameter(String service, String key, byte defaultValue) {
+        Number n = getNumbers().get(key);
+        if (n != null) {
+            return n.byteValue();
+        }
+        String value = getServiceParameter(service, key);
+        if (StringUtils.isEmpty(value)) {
+            return defaultValue;
+        }
+        byte b = Byte.parseByte(value);
+        getNumbers().put(key, b);
+        return b;
+    }
+
+    public char getServiceParameter(String service, String key, char defaultValue) {
+        String value = getServiceParameter(service, key);
+        return StringUtils.isEmpty(value) ? defaultValue : value.charAt(0);
+    }
+
+    public boolean getServiceParameter(String service, String key, boolean defaultValue) {
+        String value = getServiceParameter(service, key);
+        return StringUtils.isEmpty(value) ? defaultValue : Boolean.parseBoolean(value);
+    }
+
+    public boolean hasServiceParameter(String service, String key) {
+        String value = getServiceParameter(service, key);
+        return value != null && value.length() > 0;
+    }
+
+    public float getPositiveServiceParameter(String service, String key, float defaultValue) {
+        if (defaultValue <= 0) {
+            throw new IllegalArgumentException("defaultValue <= 0");
+        }
+        float value = getServiceParameter(service, key, defaultValue);
+        return value <= 0 ? defaultValue : value;
+    }
+
+    public double getPositiveServiceParameter(String service, String key, double defaultValue) {
+        if (defaultValue <= 0) {
+            throw new IllegalArgumentException("defaultValue <= 0");
+        }
+        double value = getServiceParameter(service, key, defaultValue);
+        return value <= 0 ? defaultValue : value;
+    }
+
+    public long getPositiveServiceParameter(String service, String key, long defaultValue) {
+        if (defaultValue <= 0) {
+            throw new IllegalArgumentException("defaultValue <= 0");
+        }
+        long value = getServiceParameter(service, key, defaultValue);
+        return value <= 0 ? defaultValue : value;
+    }
+
+    public int getPositiveServiceParameter(String service, String key, int defaultValue) {
+        if (defaultValue <= 0) {
+            throw new IllegalArgumentException("defaultValue <= 0");
+        }
+        int value = getServiceParameter(service, key, defaultValue);
+        return value <= 0 ? defaultValue : value;
+    }
+
+    public short getPositiveServiceParameter(String service, String key, short defaultValue) {
+        if (defaultValue <= 0) {
+            throw new IllegalArgumentException("defaultValue <= 0");
+        }
+        short value = getServiceParameter(service, key, defaultValue);
+        return value <= 0 ? defaultValue : value;
+    }
+
+    public byte getPositiveServiceParameter(String service, String key, byte defaultValue) {
+        if (defaultValue <= 0) {
+            throw new IllegalArgumentException("defaultValue <= 0");
+        }
+        byte value = getServiceParameter(service, key, defaultValue);
+        return value <= 0 ? defaultValue : value;
+    }
+
+    public String getServiceMethodParameterAndDecoded(String service, String method, String key) {
+        return URL.decode(getServiceMethodParameter(service, method, key));
+    }
+
+    public String getServiceMethodParameterAndDecoded(String service, String method, String key, String defaultValue) {
+        return URL.decode(getServiceMethodParameter(service, method, key, defaultValue));
+    }
+
+    public String getServiceMethodParameterStrict(String service, String method, String key) {
+        return getMethodParameterStrict(method, key);
+    }
+
+    public String getServiceMethodParameter(String service, String method, String key) {
+        return getMethodParameter(method, key);
+    }
+
+    public String getServiceMethodParameter(String service, String method, String key, String defaultValue) {
+        String value = getServiceMethodParameter(service, method, key);
+        return StringUtils.isEmpty(value) ? defaultValue : value;
+    }
+
+    public double getServiceMethodParameter(String service, String method, String key, double defaultValue) {
+        Number n = getCachedNumber(method, key);
+        if (n != null) {
+            return n.doubleValue();
+        }
+        String value = getServiceMethodParameter(service, method, key);
+        if (StringUtils.isEmpty(value)) {
+            return defaultValue;
+        }
+        double d = Double.parseDouble(value);
+        updateCachedNumber(method, key, d);
+        return d;
+    }
+
+    public float getServiceMethodParameter(String service, String method, String key, float defaultValue) {
+        Number n = getCachedNumber(method, key);
+        if (n != null) {
+            return n.floatValue();
+        }
+        String value = getServiceMethodParameter(service, method, key);
+        if (StringUtils.isEmpty(value)) {
+            return defaultValue;
+        }
+        float f = Float.parseFloat(value);
+        updateCachedNumber(method, key, f);
+        return f;
+    }
+
+    public long getServiceMethodParameter(String service, String method, String key, long defaultValue) {
+        Number n = getCachedNumber(method, key);
+        if (n != null) {
+            return n.longValue();
+        }
+        String value = getServiceMethodParameter(service, method, key);
+        if (StringUtils.isEmpty(value)) {
+            return defaultValue;
+        }
+        long l = Long.parseLong(value);
+        updateCachedNumber(method, key, l);
+        return l;
+    }
+
+    public int getServiceMethodParameter(String service, String method, String key, int defaultValue) {
+        Number n = getCachedNumber(method, key);
+        if (n != null) {
+            return n.intValue();
+        }
+        String value = getServiceMethodParameter(service, method, key);
+        if (StringUtils.isEmpty(value)) {
+            return defaultValue;
+        }
+        int i = Integer.parseInt(value);
+        updateCachedNumber(method, key, i);
+        return i;
+    }
+
+    public short getMethodParameter(String service, String method, String key, short defaultValue) {
+        Number n = getCachedNumber(method, key);
+        if (n != null) {
+            return n.shortValue();
+        }
+        String value = getServiceMethodParameter(service, method, key);
+        if (StringUtils.isEmpty(value)) {
+            return defaultValue;
+        }
+        short s = Short.parseShort(value);
+        updateCachedNumber(method, key, s);
+        return s;
+    }
+
+    public byte getServiceMethodParameter(String service, String method, String key, byte defaultValue) {
+        Number n = getCachedNumber(method, key);
+        if (n != null) {
+            return n.byteValue();
+        }
+        String value = getServiceMethodParameter(service, method, key);
+        if (StringUtils.isEmpty(value)) {
+            return defaultValue;
+        }
+        byte b = Byte.parseByte(value);
+        updateCachedNumber(method, key, b);
+        return b;
+    }
+
+    public boolean hasServiceMethodParameter(String service, String method, String key) {
+        return hasMethodParameter(method, key);
+    }
+
+    public boolean hasServiceMethodParameter(String service, String method) {
+        return hasMethodParameter(method);
+    }
+
+    protected Map<String, Number> getServiceNumbers(String service) {
+        return getNumbers();
+    }
+
+    protected Map<String, Map<String, Number>> getServiceMethodNumbers(String service) {
+        return getMethodNumbers();
+    }
 
 }
diff --git a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/cache/CacheTest.java b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/cache/CacheTest.java
index 5cb18c0..ed7cb5e 100644
--- a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/cache/CacheTest.java
+++ b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/cache/CacheTest.java
@@ -131,7 +131,7 @@ public class CacheTest {
         parameters.put("findCache.cache", "threadlocal");
         URL url = new URL("dubbo", "127.0.0.1", 29582, "org.apache.dubbo.config.cache.CacheService", parameters);
 
-        Invocation invocation = new RpcInvocation("findCache", CacheService.class.getName(), new Class[]{String.class}, new String[]{"0"}, null, null, null);
+        Invocation invocation = new RpcInvocation("findCache", CacheService.class.getName(), "", new Class[]{String.class}, new String[]{"0"}, null, null, null);
 
         Cache cache = cacheFactory.getCache(url, invocation);
         assertTrue(cache instanceof ThreadLocalCache);
diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataConstants.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataConstants.java
index 7ba0a43..6bf38c9 100644
--- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataConstants.java
+++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataConstants.java
@@ -25,4 +25,6 @@ public class MetadataConstants {
     public static final String CONSUMER_META_DATA_STORE_TAG = ".cmd";
     public static final String METADATA_PUBLISH_DELAY_KEY = "dubbo.application.metadata.delay";
     public static final int DEFAULT_METADATA_PUBLISH_DELAY = 5000;
+    public static final String METADATA_PROXY_TIMEOUT_KEY = "dubbo.application.metadata.delay";
+    public static final int DEFAULT_METADATA_TIMEOUT_VALUE = 5000;
 }
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 931a331..e3f6881 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
@@ -32,6 +32,7 @@ import java.util.Map;
 import java.util.Objects;
 import java.util.SortedSet;
 import java.util.TreeSet;
+import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.atomic.AtomicBoolean;
 
 import static org.apache.dubbo.common.constants.CommonConstants.DOT_SEPARATOR;
@@ -176,6 +177,8 @@ public class MetadataInfo implements Serializable {
         private transient Map<String, String> consumerParams;
         private transient Map<String, Map<String, String>> methodParams;
         private transient Map<String, Map<String, String>> consumerMethodParams;
+        private volatile transient Map<String, Number> numbers;
+        private volatile transient Map<String, Map<String, Number>> methodNumbers;
         private transient String serviceKey;
         private transient String matchKey;
 
@@ -382,6 +385,21 @@ public class MetadataInfo implements Serializable {
             }
         }
 
+        public Map<String, Number> getNumbers() {
+            // concurrent initialization is tolerant
+            if (numbers == null) {
+                numbers = new ConcurrentHashMap<>();
+            }
+            return numbers;
+        }
+
+        public Map<String, Map<String, Number>> getMethodNumbers() {
+            if (methodNumbers == null) { // concurrent initialization is tolerant
+                methodNumbers = new ConcurrentHashMap<>();
+            }
+            return methodNumbers;
+        }
+
         @Override
         public boolean equals(Object obj) {
             if (obj == null) {
diff --git a/dubbo-monitor/dubbo-monitor-api/src/test/java/org/apache/dubbo/monitor/support/MonitorFilterTest.java b/dubbo-monitor/dubbo-monitor-api/src/test/java/org/apache/dubbo/monitor/support/MonitorFilterTest.java
index 2180b27..dce7e51 100644
--- a/dubbo-monitor/dubbo-monitor-api/src/test/java/org/apache/dubbo/monitor/support/MonitorFilterTest.java
+++ b/dubbo-monitor/dubbo-monitor-api/src/test/java/org/apache/dubbo/monitor/support/MonitorFilterTest.java
@@ -119,7 +119,7 @@ public class MonitorFilterTest {
     public void testFilter() throws Exception {
         MonitorFilter monitorFilter = new MonitorFilter();
         monitorFilter.setMonitorFactory(monitorFactory);
-        Invocation invocation = new RpcInvocation("aaa", MonitorService.class.getName(), new Class<?>[0], new Object[0]);
+        Invocation invocation = new RpcInvocation("aaa", MonitorService.class.getName(), "", new Class<?>[0], new Object[0]);
         RpcContext.getContext().setRemoteAddress(NetUtils.getLocalHost(), 20880).setLocalAddress(NetUtils.getLocalHost(), 2345);
         Result result = monitorFilter.invoke(serviceInvoker, invocation);
         result.whenCompleteWithContext((r, t) -> {
@@ -149,7 +149,7 @@ public class MonitorFilterTest {
         MonitorFilter monitorFilter = new MonitorFilter();
         MonitorFactory mockMonitorFactory = mock(MonitorFactory.class);
         monitorFilter.setMonitorFactory(mockMonitorFactory);
-        Invocation invocation = new RpcInvocation("aaa", MonitorService.class.getName(), new Class<?>[0], new Object[0]);
+        Invocation invocation = new RpcInvocation("aaa", MonitorService.class.getName(), "", new Class<?>[0], new Object[0]);
         Invoker invoker = mock(Invoker.class);
         given(invoker.getUrl()).willReturn(URL.valueOf("dubbo://" + NetUtils.getLocalHost() + ":20880?" + APPLICATION_KEY + "=abc&" + SIDE_KEY + "=" + CONSUMER_SIDE));
 
@@ -162,7 +162,7 @@ public class MonitorFilterTest {
     public void testGenericFilter() throws Exception {
         MonitorFilter monitorFilter = new MonitorFilter();
         monitorFilter.setMonitorFactory(monitorFactory);
-        Invocation invocation = new RpcInvocation("$invoke", MonitorService.class.getName(), new Class<?>[]{String.class, String[].class, Object[].class}, new Object[]{"xxx", new String[]{}, new Object[]{}});
+        Invocation invocation = new RpcInvocation("$invoke", MonitorService.class.getName(), "", new Class<?>[]{String.class, String[].class, Object[].class}, new Object[]{"xxx", new String[]{}, new Object[]{}});
         RpcContext.getContext().setRemoteAddress(NetUtils.getLocalHost(), 20880).setLocalAddress(NetUtils.getLocalHost(), 2345);
         Result result = monitorFilter.invoke(serviceInvoker, invocation);
         result.whenCompleteWithContext((r, t) -> {
@@ -196,7 +196,7 @@ public class MonitorFilterTest {
 
         monitorFilter.setMonitorFactory(mockMonitorFactory);
         given(mockMonitorFactory.getMonitor(any(URL.class))).willReturn(mockMonitor);
-        Invocation invocation = new RpcInvocation("aaa", MonitorService.class.getName(), new Class<?>[0], new Object[0]);
+        Invocation invocation = new RpcInvocation("aaa", MonitorService.class.getName(), "", new Class<?>[0], new Object[0]);
 
         monitorFilter.invoke(serviceInvoker, invocation);
     }
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/DefaultServiceInstance.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/DefaultServiceInstance.java
index 3ee5dd2..3c7204d 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/DefaultServiceInstance.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/DefaultServiceInstance.java
@@ -146,6 +146,14 @@ public class DefaultServiceInstance implements ServiceInstance {
         return extendParams;
     }
 
+    @Override
+    public Map<String, String> getAllParams() {
+        Map<String, String> allParams = new HashMap<>((int) ((metadata.size() + extendParams.size()) / 0.75f + 1));
+        allParams.putAll(metadata);
+        allParams.putAll(extendParams);
+        return allParams;
+    }
+
     public void setMetadata(Map<String, String> metadata) {
         this.metadata = metadata;
     }
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/InstanceAddressURL.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/InstanceAddressURL.java
index 149c6e2..ca8f9be 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/InstanceAddressURL.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/InstanceAddressURL.java
@@ -23,30 +23,31 @@ import org.apache.dubbo.rpc.RpcContext;
 
 import java.util.HashMap;
 import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
 
 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.VERSION_KEY;
 
-/**
- * FIXME, replace RpcContext operations with explicitly defined APIs
- */
 public class InstanceAddressURL extends URL {
     private ServiceInstance instance;
     private MetadataInfo metadataInfo;
+    private volatile transient Map<String, Number> numbers;
+    private volatile transient Map<String, Map<String, Number>> methodNumbers;
+
+    public InstanceAddressURL() {
+    }
 
     public InstanceAddressURL(
             ServiceInstance instance,
             MetadataInfo metadataInfo
     ) {
-//        super()
         this.instance = instance;
         this.metadataInfo = metadataInfo;
         this.host = instance.getHost();
         this.port = instance.getPort();
     }
 
-
     public ServiceInstance getInstance() {
         return instance;
     }
@@ -74,6 +75,11 @@ public class InstanceAddressURL extends URL {
     }
 
     @Override
+    public String getProtocolServiceKey() {
+        return RpcContext.getContext().getProtocolServiceKey();
+    }
+
+    @Override
     public String getServiceKey() {
         return RpcContext.getContext().getServiceKey();
     }
@@ -99,33 +105,32 @@ public class InstanceAddressURL extends URL {
             return getServiceInterface();
         }
 
-        String value = getInstanceMetadata().get(key);
-        if (StringUtils.isEmpty(value) && metadataInfo != null) {
-            value = metadataInfo.getParameter(key, RpcContext.getContext().getProtocolServiceKey());
+        String protocolServiceKey = getProtocolServiceKey();
+        if (protocolServiceKey == null) {
+            return getInstanceParameter(key);
         }
-        return value;
+        return getServiceParameter(protocolServiceKey, key);
     }
 
     @Override
-    public String getParameter(String key, String defaultValue) {
-        if (VERSION_KEY.equals(key)) {
-            return getVersion();
-        } else if (GROUP_KEY.equals(key)) {
-            return getGroup();
-        } else if (INTERFACE_KEY.equals(key)) {
-            return getServiceInterface();
-        }
-
-        String value = getParameter(key);
-        if (StringUtils.isEmpty(value)) {
-            return defaultValue;
+    public String getServiceParameter(String service, String key) {
+        String value = getInstanceParameter(key);
+        if (StringUtils.isEmpty(value) && metadataInfo != null) {
+            value = metadataInfo.getParameter(key, service);
         }
         return value;
     }
 
+    /**
+     * method parameter only exists in ServiceInfo
+     *
+     * @param method
+     * @param key
+     * @return
+     */
     @Override
-    public String getMethodParameter(String method, String key) {
-        MetadataInfo.ServiceInfo serviceInfo = metadataInfo.getServiceInfo(getProtocolServiceKey());
+    public String getServiceMethodParameter(String protocolServiceKey, String method, String key) {
+        MetadataInfo.ServiceInfo serviceInfo = metadataInfo.getServiceInfo(protocolServiceKey);
         String value = serviceInfo.getMethodParameter(method, key, null);
         if (StringUtils.isNotEmpty(value)) {
             return value;
@@ -134,8 +139,24 @@ public class InstanceAddressURL extends URL {
     }
 
     @Override
-    public boolean hasMethodParameter(String method, String key) {
-        MetadataInfo.ServiceInfo serviceInfo = metadataInfo.getServiceInfo(getProtocolServiceKey());
+    public String getMethodParameter(String method, String key) {
+        String protocolServiceKey = getProtocolServiceKey();
+        if (protocolServiceKey == null) {
+            return null;
+        }
+        return getServiceMethodParameter(protocolServiceKey, method, key);
+    }
+
+    /**
+     * method parameter only exists in ServiceInfo
+     *
+     * @param method
+     * @param key
+     * @return
+     */
+    @Override
+    public boolean hasServiceMethodParameter(String protocolServiceKey, String method, String key) {
+        MetadataInfo.ServiceInfo serviceInfo = metadataInfo.getServiceInfo(protocolServiceKey);
 
         if (method == null) {
             String suffix = "." + key;
@@ -160,20 +181,44 @@ public class InstanceAddressURL extends URL {
     }
 
     @Override
-    public boolean hasMethodParameter(String method) {
-        MetadataInfo.ServiceInfo serviceInfo = metadataInfo.getServiceInfo(getProtocolServiceKey());
+    public boolean hasMethodParameter(String method, String key) {
+        String protocolServiceKey = getProtocolServiceKey();
+        if (protocolServiceKey == null) {
+            return false;
+        }
+        return hasServiceMethodParameter(protocolServiceKey, method, key);
+    }
+
+    /**
+     * method parameter only exists in ServiceInfo
+     *
+     * @param method
+     * @return
+     */
+    @Override
+    public boolean hasServiceMethodParameter(String protocolServiceKey, String method) {
+        MetadataInfo.ServiceInfo serviceInfo = metadataInfo.getServiceInfo(protocolServiceKey);
         return serviceInfo.hasMethodParameter(method);
     }
 
+    @Override
+    public boolean hasMethodParameter(String method) {
+        String protocolServiceKey = getProtocolServiceKey();
+        if (protocolServiceKey == null) {
+            return false;
+        }
+        return hasServiceMethodParameter(protocolServiceKey, method);
+    }
+
     /**
      * Avoid calling this method in RPC call.
      *
      * @return
      */
     @Override
-    public Map<String, String> getParameters() {
+    public Map<String, String> getServiceParameters(String protocolServiceKey) {
         Map<String, String> instanceParams = getInstanceMetadata();
-        Map<String, String> metadataParams = (metadataInfo == null ? new HashMap<>() : metadataInfo.getParameters(RpcContext.getContext().getProtocolServiceKey()));
+        Map<String, String> metadataParams = (metadataInfo == null ? new HashMap<>() : metadataInfo.getParameters(protocolServiceKey));
         int i = instanceParams == null ? 0 : instanceParams.size();
         int j = metadataParams == null ? 0 : metadataParams.size();
         Map<String, String> params = new HashMap<>((int) ((i + j) / 0.75) + 1);
@@ -186,8 +231,13 @@ public class InstanceAddressURL extends URL {
         return params;
     }
 
-    private Map<String, String> getInstanceMetadata() {
-        return this.instance.getMetadata();
+    @Override
+    public Map<String, String> getParameters() {
+        String protocolServiceKey = getProtocolServiceKey();
+        if (protocolServiceKey == null) {
+            return getInstance().getAllParams();
+        }
+        return getServiceParameters(protocolServiceKey);
     }
 
     @Override
@@ -196,8 +246,7 @@ public class InstanceAddressURL extends URL {
             return this;
         }
 
-        String protocolServiceKey = RpcContext.getContext().getProtocolServiceKey();
-        getMetadataInfo().getServiceInfo(protocolServiceKey).addParameter(key, value);
+        getInstance().getExtendParams().put(key, value);
         return this;
     }
 
@@ -207,18 +256,84 @@ public class InstanceAddressURL extends URL {
             return this;
         }
 
-        String protocolServiceKey = RpcContext.getContext().getProtocolServiceKey();
+        getInstance().getExtendParams().putIfAbsent(key, value);
+        return this;
+    }
+
+    public URL addServiceParameter(String protocolServiceKey, String key, String value) {
+        if (StringUtils.isEmpty(key) || StringUtils.isEmpty(value)) {
+            return this;
+        }
+
+        getMetadataInfo().getServiceInfo(protocolServiceKey).addParameter(key, value);
+        return this;
+    }
+
+    public URL addServiceParameterIfAbsent(String protocolServiceKey, String key, String value) {
+        if (StringUtils.isEmpty(key) || StringUtils.isEmpty(value)) {
+            return this;
+        }
+
         getMetadataInfo().getServiceInfo(protocolServiceKey).addParameterIfAbsent(key, value);
         return this;
     }
 
-    public URL addConsumerParams(Map<String, String> params) {
-        String protocolServiceKey = RpcContext.getContext().getProtocolServiceKey();
+    public URL addConsumerParams(String protocolServiceKey, Map<String, String> params) {
         getMetadataInfo().getServiceInfo(protocolServiceKey).addConsumerParams(params);
         return this;
     }
 
     @Override
+    protected Map<String, Number> getServiceNumbers(String protocolServiceKey) {
+        return getServiceInfo(protocolServiceKey).getNumbers();
+    }
+
+    @Override
+    protected Map<String, Number> getNumbers() {
+        String protocolServiceKey = getProtocolServiceKey();
+        if (protocolServiceKey == null) {
+            if (numbers == null) { // concurrent initialization is tolerant
+                numbers = new ConcurrentHashMap<>();
+            }
+            return numbers;
+        }
+        return getServiceNumbers(protocolServiceKey);
+    }
+
+    @Override
+    protected Map<String, Map<String, Number>> getServiceMethodNumbers(String protocolServiceKey) {
+        return getServiceInfo(protocolServiceKey).getMethodNumbers();
+    }
+
+    @Override
+    protected Map<String, Map<String, Number>> getMethodNumbers() {
+        String protocolServiceKey = getProtocolServiceKey();
+        if (protocolServiceKey == null) {
+            if (methodNumbers == null) { // concurrent initialization is tolerant
+                methodNumbers = new ConcurrentHashMap<>();
+            }
+            return methodNumbers;
+        }
+        return getServiceMethodNumbers(protocolServiceKey);
+    }
+
+    private MetadataInfo.ServiceInfo getServiceInfo(String protocolServiceKey) {
+        return metadataInfo.getServiceInfo(protocolServiceKey);
+    }
+
+    private String getInstanceParameter(String key) {
+        String value = this.instance.getMetadata().get(key);
+        if (StringUtils.isNotEmpty(value)) {
+            return value;
+        }
+        return this.instance.getExtendParams().get(key);
+    }
+
+    private Map<String, String> getInstanceMetadata() {
+        return this.instance.getMetadata();
+    }
+
+    @Override
     public boolean equals(Object obj) {
         // instance metadata equals
         if (obj == null) {
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistryDirectory.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistryDirectory.java
index e01a36e..9c725ac 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistryDirectory.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistryDirectory.java
@@ -57,49 +57,53 @@ public class ServiceDiscoveryRegistryDirectory<T> extends DynamicDirectory<T> im
     public synchronized void notify(List<URL> instanceUrls) {
         // Set the context of the address notification thread.
         RpcContext.setRpcContext(getConsumerUrl());
-        if (CollectionUtils.isEmpty(instanceUrls)) {
-            // FIXME, empty protocol
-        }
         refreshInvoker(instanceUrls);
     }
 
     private void refreshInvoker(List<URL> invokerUrls) {
-        Assert.notNull(invokerUrls, "invokerUrls should not be null, use empty:// to clear address.");
+        Assert.notNull(invokerUrls, "invokerUrls should not be null, use empty InstanceAddressURL to clear address.");
 
-        if (invokerUrls.size() == 1
-                && invokerUrls.get(0) != null
-                && EMPTY_PROTOCOL.equals(invokerUrls.get(0).getProtocol())) {
-            this.forbidden = true; // Forbid to access
-            this.invokers = Collections.emptyList();
-            routerChain.setInvokers(this.invokers);
-            destroyAllInvokers(); // Close all invokers
-        } else {
-            this.forbidden = false; // Allow to access
-            Map<String, Invoker<T>> oldUrlInvokerMap = this.urlInvokerMap; // local reference
-            if (CollectionUtils.isEmpty(invokerUrls)) {
-                return;
+        if (invokerUrls.size() == 1) {
+            URL url = invokerUrls.get(0);
+            if (!(url instanceof InstanceAddressURL)) {
+                throw new IllegalStateException("use empty InstanceAddressURL to clear address");
+            } else {
+                InstanceAddressURL instanceAddressURL = (InstanceAddressURL) url;
+                if (instanceAddressURL.getInstance() == null) {
+                    this.forbidden = true; // Forbid to access
+                    this.invokers = Collections.emptyList();
+                    routerChain.setInvokers(this.invokers);
+                    destroyAllInvokers(); // Close all invokers
+                    return;
+                }
             }
+        }
 
-            Map<String, Invoker<T>> newUrlInvokerMap = toInvokers(invokerUrls);// Translate url list to Invoker map
+        this.forbidden = false; // Allow to access
+        Map<String, Invoker<T>> oldUrlInvokerMap = this.urlInvokerMap; // local reference
+        if (CollectionUtils.isEmpty(invokerUrls)) {
+            return;
+        }
 
-            if (CollectionUtils.isEmptyMap(newUrlInvokerMap)) {
-                logger.error(new IllegalStateException("Cannot create invokers from url address list (total " + invokerUrls.size() + ")"));
-                return;
-            }
+        Map<String, Invoker<T>> newUrlInvokerMap = toInvokers(invokerUrls);// Translate url list to Invoker map
 
-            List<Invoker<T>> newInvokers = Collections.unmodifiableList(new ArrayList<>(newUrlInvokerMap.values()));
-            // pre-route and build cache, notice that route cache should build on original Invoker list.
-            // toMergeMethodInvokerMap() will wrap some invokers having different groups, those wrapped invokers not should be routed.
-            routerChain.setInvokers(newInvokers);
-            this.invokers = multiGroup ? toMergeInvokerList(newInvokers) : newInvokers;
-            this.urlInvokerMap = newUrlInvokerMap;
+        if (CollectionUtils.isEmptyMap(newUrlInvokerMap)) {
+            logger.error(new IllegalStateException("Cannot create invokers from url address list (total " + invokerUrls.size() + ")"));
+            return;
+        }
 
-            if (oldUrlInvokerMap != null) {
-                try {
-                    destroyUnusedInvokers(oldUrlInvokerMap, newUrlInvokerMap); // Close the unused Invoker
-                } catch (Exception e) {
-                    logger.warn("destroyUnusedInvokers error. ", e);
-                }
+        List<Invoker<T>> newInvokers = Collections.unmodifiableList(new ArrayList<>(newUrlInvokerMap.values()));
+        // pre-route and build cache, notice that route cache should build on original Invoker list.
+        // toMergeMethodInvokerMap() will wrap some invokers having different groups, those wrapped invokers not should be routed.
+        routerChain.setInvokers(newInvokers);
+        this.invokers = multiGroup ? toMergeInvokerList(newInvokers) : newInvokers;
+        this.urlInvokerMap = newUrlInvokerMap;
+
+        if (oldUrlInvokerMap != null) {
+            try {
+                destroyUnusedInvokers(oldUrlInvokerMap, newUrlInvokerMap); // Close the unused Invoker
+            } catch (Exception e) {
+                logger.warn("destroyUnusedInvokers error. ", e);
             }
         }
     }
@@ -129,7 +133,7 @@ public class ServiceDiscoveryRegistryDirectory<T> extends DynamicDirectory<T> im
             }
 
             // FIXME, some keys may need to be removed.
-            instanceAddressURL.addConsumerParams(queryMap);
+            instanceAddressURL.addConsumerParams(getConsumerUrl().getProtocolServiceKey(), queryMap);
 
             Invoker<T> invoker = urlInvokerMap == null ? null : urlInvokerMap.get(instanceAddressURL.getAddress());
             if (invoker == null || urlChanged(invoker, instanceAddressURL)) { // Not in the cache, refer again
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceInstance.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceInstance.java
index ad950c1..d019a99 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceInstance.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceInstance.java
@@ -86,6 +86,8 @@ public interface ServiceInstance extends Serializable {
 
     Map<String, String> getExtendParams();
 
+    Map<String, String> getAllParams();
+
     /**
      * Get the value of metadata by the specified name
      *
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 8af48a3..37b6bf0 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
@@ -19,6 +19,7 @@ package org.apache.dubbo.registry.client.event.listener;
 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.event.ConditionalEventListener;
 import org.apache.dubbo.event.EventListener;
 import org.apache.dubbo.metadata.MetadataInfo;
@@ -26,6 +27,7 @@ import org.apache.dubbo.metadata.MetadataInfo.ServiceInfo;
 import org.apache.dubbo.metadata.MetadataService;
 import org.apache.dubbo.registry.NotifyListener;
 import org.apache.dubbo.registry.client.DefaultServiceInstance;
+import org.apache.dubbo.registry.client.InstanceAddressURL;
 import org.apache.dubbo.registry.client.RegistryClusterIdentifier;
 import org.apache.dubbo.registry.client.ServiceDiscovery;
 import org.apache.dubbo.registry.client.ServiceInstance;
@@ -161,12 +163,13 @@ public class ServiceInstancesChangedListener implements ConditionalEventListener
                 RemoteMetadataServiceImpl remoteMetadataService = MetadataUtils.getRemoteMetadataService();
                 metadataInfo = remoteMetadataService.getMetadata(instance);
             } else {
-                MetadataService metadataServiceProxy = MetadataUtils.getMetadataServiceProxy(instance);
+                MetadataService metadataServiceProxy = MetadataUtils.getMetadataServiceProxy(instance, serviceDiscovery);
                 metadataInfo = metadataServiceProxy.getMetadataInfo(ServiceInstanceMetadataUtils.getExportedServicesRevision(instance));
             }
         } catch (Exception e) {
-            // TODO, load metadata backup
+            logger.error("Failed to load service metadata, metadta type is " + metadataType, e);
             metadataInfo = null;
+            // TODO, load metadata backup. Stop getting metadata after x times of failure for one revision?
         }
         return metadataInfo;
     }
@@ -174,7 +177,12 @@ public class ServiceInstancesChangedListener implements ConditionalEventListener
     private void notifyAddressChanged() {
         listeners.forEach((key, notifyListener) -> {
             //FIXME, group wildcard match
-            notifyListener.notify(serviceUrls.get(key));
+            List<URL> urls = serviceUrls.get(key);
+            if (CollectionUtils.isEmpty(urls)) {
+                urls = new ArrayList<>();
+                urls.add(new InstanceAddressURL());
+            }
+            notifyListener.notify(urls);
         });
     }
 
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
index 2685a4a..b65dba1 100644
--- 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
@@ -22,6 +22,7 @@ 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.ServiceDiscovery;
 import org.apache.dubbo.registry.client.ServiceInstance;
 import org.apache.dubbo.registry.client.metadata.store.RemoteMetadataServiceImpl;
 import org.apache.dubbo.rpc.Invoker;
@@ -67,7 +68,7 @@ public class MetadataUtils {
         getRemoteMetadataService().publishServiceDefinition(url);
     }
 
-    public static MetadataService getMetadataServiceProxy(ServiceInstance instance) {
+    public static MetadataService getMetadataServiceProxy(ServiceInstance instance, ServiceDiscovery serviceDiscovery) {
         String key = instance.getServiceName() + "##" +
                 ServiceInstanceMetadataUtils.getExportedServicesRevision(instance);
         return metadataServiceProxies.computeIfAbsent(key, k -> {
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/StandardMetadataServiceURLBuilder.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/StandardMetadataServiceURLBuilder.java
index 216e832..a542012 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/StandardMetadataServiceURLBuilder.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/StandardMetadataServiceURLBuilder.java
@@ -18,6 +18,7 @@ package org.apache.dubbo.registry.client.metadata;
 
 import org.apache.dubbo.common.URL;
 import org.apache.dubbo.common.URLBuilder;
+import org.apache.dubbo.common.config.ConfigurationUtils;
 import org.apache.dubbo.metadata.MetadataService;
 import org.apache.dubbo.registry.client.ServiceInstance;
 
@@ -28,6 +29,9 @@ import java.util.Map;
 import static java.lang.String.valueOf;
 import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY;
 import static org.apache.dubbo.common.constants.CommonConstants.PORT_KEY;
+import static org.apache.dubbo.common.constants.CommonConstants.TIMEOUT_KEY;
+import static org.apache.dubbo.metadata.MetadataConstants.DEFAULT_METADATA_TIMEOUT_VALUE;
+import static org.apache.dubbo.metadata.MetadataConstants.METADATA_PROXY_TIMEOUT_KEY;
 import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.getMetadataServiceURLsParams;
 
 /**
@@ -65,7 +69,8 @@ public class StandardMetadataServiceURLBuilder implements MetadataServiceURLBuil
                     .setHost(host)
                     .setPort(port)
                     .setProtocol(protocol)
-                    .setPath(MetadataService.class.getName());
+                    .setPath(MetadataService.class.getName())
+                    .addParameter(TIMEOUT_KEY, ConfigurationUtils.get(METADATA_PROXY_TIMEOUT_KEY, DEFAULT_METADATA_TIMEOUT_VALUE));
 
             // add parameters
             params.forEach((name, value) -> urlBuilder.addParameter(name, valueOf(value)));
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/Invocation.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/Invocation.java
index ac02fee..8422baf 100644
--- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/Invocation.java
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/Invocation.java
@@ -32,6 +32,8 @@ public interface Invocation {
 
     String getTargetServiceUniqueName();
 
+    String getProtocolServiceKey();
+
     /**
      * get method name.
      *
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/RpcContext.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/RpcContext.java
index 2c3d2b8..8077067 100644
--- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/RpcContext.java
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/RpcContext.java
@@ -800,60 +800,48 @@ public class RpcContext {
     }
 
     // RPC service context updated before each service call.
-    private String group;
-    private String version;
-    private String interfaceName;
-    private String protocol;
-    private String serviceKey;
-    private String protocolServiceKey;
     private URL consumerUrl;
 
     public String getGroup() {
-        return group;
-    }
-
-    public void setGroup(String group) {
-        this.group = group;
+        if (consumerUrl == null) {
+            return null;
+        }
+        return consumerUrl.getParameter(GROUP_KEY);
     }
 
     public String getVersion() {
-        return version;
-    }
-
-    public void setVersion(String version) {
-        this.version = version;
+        if (consumerUrl == null) {
+            return null;
+        }
+        return consumerUrl.getParameter(VERSION_KEY);
     }
 
     public String getInterfaceName() {
-        return interfaceName;
-    }
-
-    public void setInterfaceName(String interfaceName) {
-        this.interfaceName = interfaceName;
+        if (consumerUrl == null) {
+            return null;
+        }
+        return consumerUrl.getServiceInterface();
     }
 
     public String getProtocol() {
-        return protocol;
-    }
-
-    public void setProtocol(String protocol) {
-        this.protocol = protocol;
+        if (consumerUrl == null) {
+            return null;
+        }
+        return consumerUrl.getParameter(PROTOCOL_KEY, DUBBO);
     }
 
     public String getServiceKey() {
-        return serviceKey;
-    }
-
-    public void setServiceKey(String serviceKey) {
-        this.serviceKey = serviceKey;
+        if (consumerUrl == null) {
+            return null;
+        }
+        return consumerUrl.getServiceKey();
     }
 
     public String getProtocolServiceKey() {
-        return protocolServiceKey;
-    }
-
-    public void setProtocolServiceKey(String protocolServiceKey) {
-        this.protocolServiceKey = protocolServiceKey;
+        if (consumerUrl == null) {
+            return null;
+        }
+        return consumerUrl.getProtocolServiceKey();
     }
 
     public URL getConsumerUrl() {
@@ -867,11 +855,5 @@ public class RpcContext {
     public static void setRpcContext(URL url) {
         RpcContext rpcContext = RpcContext.getContext();
         rpcContext.setConsumerUrl(url);
-        rpcContext.setInterfaceName(url.getServiceInterface());
-        rpcContext.setVersion(url.getParameter(VERSION_KEY));
-        rpcContext.setGroup(url.getParameter(GROUP_KEY));
-        rpcContext.setProtocol(url.getParameter(PROTOCOL_KEY, DUBBO));
-        rpcContext.setServiceKey(url.getServiceKey());
-        rpcContext.setProtocolServiceKey(url.getProtocolServiceKey());
     }
 }
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/RpcInvocation.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/RpcInvocation.java
index 87baa42..4a78cd6 100644
--- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/RpcInvocation.java
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/RpcInvocation.java
@@ -51,6 +51,7 @@ public class RpcInvocation implements Invocation, Serializable {
     private static final long serialVersionUID = -4355285085441097045L;
 
     private String targetServiceUniqueName;
+    private String protocolServiceKey;
 
     private String methodName;
     private String serviceName;
@@ -83,8 +84,8 @@ public class RpcInvocation implements Invocation, Serializable {
     }
 
     public RpcInvocation(Invocation invocation, Invoker<?> invoker) {
-        this(invocation.getMethodName(), invocation.getServiceName(), invocation.getParameterTypes(),
-                invocation.getArguments(), new HashMap<>(invocation.getObjectAttachments()),
+        this(invocation.getMethodName(), invocation.getServiceName(), invocation.getProtocolServiceKey(),
+                invocation.getParameterTypes(), invocation.getArguments(), new HashMap<>(invocation.getObjectAttachments()),
                 invocation.getInvoker(), invocation.getAttributes());
         if (invoker != null) {
             URL url = invoker.getUrl();
@@ -109,35 +110,37 @@ public class RpcInvocation implements Invocation, Serializable {
             }
         }
         this.targetServiceUniqueName = invocation.getTargetServiceUniqueName();
+        this.protocolServiceKey = invocation.getProtocolServiceKey();
     }
 
     public RpcInvocation(Invocation invocation) {
-        this(invocation.getMethodName(), invocation.getServiceName(), invocation.getParameterTypes(),
+        this(invocation.getMethodName(), invocation.getServiceName(), invocation.getProtocolServiceKey(), invocation.getParameterTypes(),
                 invocation.getArguments(), invocation.getObjectAttachments(), invocation.getInvoker(), invocation.getAttributes());
         this.targetServiceUniqueName = invocation.getTargetServiceUniqueName();
     }
 
-    public RpcInvocation(Method method, String serviceName, Object[] arguments) {
-        this(method, serviceName, arguments, null, null);
+    public RpcInvocation(Method method, String serviceName, String protocolServiceKey, Object[] arguments) {
+        this(method, serviceName, protocolServiceKey, arguments, null, null);
     }
 
-    public RpcInvocation(Method method, String serviceName, Object[] arguments, Map<String, Object> attachment, Map<Object, Object> attributes) {
-        this(method.getName(), serviceName, method.getParameterTypes(), arguments, attachment, null, attributes);
+    public RpcInvocation(Method method, String serviceName, String protocolServiceKey, Object[] arguments, Map<String, Object> attachment, Map<Object, Object> attributes) {
+        this(method.getName(), serviceName, protocolServiceKey, method.getParameterTypes(), arguments, attachment, null, attributes);
         this.returnType = method.getReturnType();
     }
 
-    public RpcInvocation(String methodName, String serviceName, Class<?>[] parameterTypes, Object[] arguments) {
-        this(methodName, serviceName, parameterTypes, arguments, null, null, null);
+    public RpcInvocation(String methodName, String serviceName, String protocolServiceKey, Class<?>[] parameterTypes, Object[] arguments) {
+        this(methodName, serviceName, protocolServiceKey, parameterTypes, arguments, null, null, null);
     }
 
-    public RpcInvocation(String methodName, String serviceName, Class<?>[] parameterTypes, Object[] arguments, Map<String, Object> attachments) {
-        this(methodName, serviceName, parameterTypes, arguments, attachments, null, null);
+    public RpcInvocation(String methodName, String serviceName, String protocolServiceKey, Class<?>[] parameterTypes, Object[] arguments, Map<String, Object> attachments) {
+        this(methodName, serviceName, protocolServiceKey, parameterTypes, arguments, attachments, null, null);
     }
 
-    public RpcInvocation(String methodName, String serviceName, Class<?>[] parameterTypes, Object[] arguments,
+    public RpcInvocation(String methodName, String serviceName, String protocolServiceKey, Class<?>[] parameterTypes, Object[] arguments,
                          Map<String, Object> attachments, Invoker<?> invoker, Map<Object, Object> attributes) {
         this.methodName = methodName;
         this.serviceName = serviceName;
+        this.protocolServiceKey = protocolServiceKey;
         this.parameterTypes = parameterTypes == null ? new Class<?>[0] : parameterTypes;
         this.arguments = arguments == null ? new Object[0] : arguments;
         this.attachments = attachments == null ? new HashMap<>() : attachments;
@@ -194,6 +197,11 @@ public class RpcInvocation implements Invocation, Serializable {
         return targetServiceUniqueName;
     }
 
+    @Override
+    public String getProtocolServiceKey() {
+        return protocolServiceKey;
+    }
+
     public void setTargetServiceUniqueName(String targetServiceUniqueName) {
         this.targetServiceUniqueName = targetServiceUniqueName;
     }
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/GenericFilter.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/GenericFilter.java
index 13c29b6..0234139 100644
--- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/GenericFilter.java
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/GenericFilter.java
@@ -140,7 +140,7 @@ public class GenericFilter implements Filter, Filter.Listener {
                     }
                 }
 
-                RpcInvocation rpcInvocation = new RpcInvocation(method, invoker.getInterface().getName(), args, inv.getObjectAttachments(), inv.getAttributes());
+                RpcInvocation rpcInvocation = new RpcInvocation(method, invoker.getInterface().getName(), invoker.getUrl().getProtocolServiceKey(), args, inv.getObjectAttachments(), inv.getAttributes());
                 rpcInvocation.setInvoker(inv.getInvoker());
                 rpcInvocation.setTargetServiceUniqueName(inv.getTargetServiceUniqueName());
 
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/AbstractInvoker.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/AbstractInvoker.java
index 90e101d..0865f08 100644
--- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/AbstractInvoker.java
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/AbstractInvoker.java
@@ -45,7 +45,7 @@ import java.util.concurrent.ExecutorService;
 import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
- * AbstractInvoker.
+ * This Invoker works on Consumer side.
  */
 public abstract class AbstractInvoker<T> implements Invoker<T> {
 
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/proxy/AbstractProxyInvoker.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/proxy/AbstractProxyInvoker.java
index 6d74825..1b1d592 100644
--- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/proxy/AbstractProxyInvoker.java
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/proxy/AbstractProxyInvoker.java
@@ -33,7 +33,7 @@ import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.CompletionException;
 
 /**
- * InvokerWrapper
+ * This Invoker works on provider side, delegates RPC to interface implementation.
  */
 public abstract class AbstractProxyInvoker<T> implements Invoker<T> {
     Logger logger = LoggerFactory.getLogger(AbstractProxyInvoker.class);
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/proxy/InvokerInvocationHandler.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/proxy/InvokerInvocationHandler.java
index 0eae664..69f1d06 100644
--- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/proxy/InvokerInvocationHandler.java
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/proxy/InvokerInvocationHandler.java
@@ -16,6 +16,7 @@
  */
 package org.apache.dubbo.rpc.proxy;
 
+import org.apache.dubbo.common.URL;
 import org.apache.dubbo.common.logger.Logger;
 import org.apache.dubbo.common.logger.LoggerFactory;
 import org.apache.dubbo.rpc.Constants;
@@ -35,10 +36,14 @@ public class InvokerInvocationHandler implements InvocationHandler {
     private static final Logger logger = LoggerFactory.getLogger(InvokerInvocationHandler.class);
     private final Invoker<?> invoker;
     private ConsumerModel consumerModel;
+    private URL url;
+    private String protocolServiceKey;
 
     public InvokerInvocationHandler(Invoker<?> handler) {
         this.invoker = handler;
-        String serviceKey = invoker.getUrl().getServiceKey();
+        this.url = invoker.getUrl();
+        String serviceKey = this.url.getServiceKey();
+        this.protocolServiceKey = this.url.getProtocolServiceKey();
         if (serviceKey != null) {
             this.consumerModel = ApplicationModel.getConsumerModel(serviceKey);
         }
@@ -63,7 +68,7 @@ public class InvokerInvocationHandler implements InvocationHandler {
         } else if (parameterTypes.length == 1 && "equals".equals(methodName)) {
             return invoker.equals(args[0]);
         }
-        RpcInvocation rpcInvocation = new RpcInvocation(method, invoker.getInterface().getName(), args);
+        RpcInvocation rpcInvocation = new RpcInvocation(method, invoker.getInterface().getName(), protocolServiceKey, args);
         String serviceKey = invoker.getUrl().getServiceKey();
         rpcInvocation.setTargetServiceUniqueName(serviceKey);
 
diff --git a/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/ExceptionFilterTest.java b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/ExceptionFilterTest.java
index 53f8921..2f32ff6 100644
--- a/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/ExceptionFilterTest.java
+++ b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/ExceptionFilterTest.java
@@ -51,7 +51,7 @@ public class ExceptionFilterTest {
         RpcException exception = new RpcException("TestRpcException");
 
         ExceptionFilter exceptionFilter = new ExceptionFilter();
-        RpcInvocation invocation = new RpcInvocation("sayHello", DemoService.class.getName(), new Class<?>[]{String.class}, new Object[]{"world"});
+        RpcInvocation invocation = new RpcInvocation("sayHello", DemoService.class.getName(), "", new Class<?>[]{String.class}, new Object[]{"world"});
         Invoker<DemoService> invoker = mock(Invoker.class);
         given(invoker.getInterface()).willReturn(DemoService.class);
         given(invoker.invoke(eq(invocation))).willThrow(exception);
@@ -75,7 +75,7 @@ public class ExceptionFilterTest {
     public void testJavaException() {
 
         ExceptionFilter exceptionFilter = new ExceptionFilter();
-        RpcInvocation invocation = new RpcInvocation("sayHello", DemoService.class.getName(), new Class<?>[]{String.class}, new Object[]{"world"});
+        RpcInvocation invocation = new RpcInvocation("sayHello", DemoService.class.getName(), "", new Class<?>[]{String.class}, new Object[]{"world"});
 
         AppResponse appResponse = new AppResponse();
         appResponse.setException(new IllegalArgumentException("java"));
@@ -95,7 +95,7 @@ public class ExceptionFilterTest {
     public void testRuntimeException() {
 
         ExceptionFilter exceptionFilter = new ExceptionFilter();
-        RpcInvocation invocation = new RpcInvocation("sayHello", DemoService.class.getName(), new Class<?>[]{String.class}, new Object[]{"world"});
+        RpcInvocation invocation = new RpcInvocation("sayHello", DemoService.class.getName(), "", new Class<?>[]{String.class}, new Object[]{"world"});
 
         AppResponse appResponse = new AppResponse();
         appResponse.setException(new LocalException("localException"));
@@ -115,7 +115,7 @@ public class ExceptionFilterTest {
     public void testConvertToRunTimeException() throws Exception {
 
         ExceptionFilter exceptionFilter = new ExceptionFilter();
-        RpcInvocation invocation = new RpcInvocation("sayHello", DemoService.class.getName(), new Class<?>[]{String.class}, new Object[]{"world"});
+        RpcInvocation invocation = new RpcInvocation("sayHello", DemoService.class.getName(), "", new Class<?>[]{String.class}, new Object[]{"world"});
 
         AppResponse mockRpcResult = new AppResponse();
         mockRpcResult.setException(new HessianException("hessian"));
diff --git a/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/GenericFilterTest.java b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/GenericFilterTest.java
index e06d2b7..c49aa92 100644
--- a/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/GenericFilterTest.java
+++ b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/GenericFilterTest.java
@@ -54,7 +54,7 @@ public class GenericFilterTest {
         person.put("name", "dubbo");
         person.put("age", 10);
 
-        RpcInvocation invocation = new RpcInvocation($INVOKE, GenericService.class.getName(), genericInvoke.getParameterTypes(),
+        RpcInvocation invocation = new RpcInvocation($INVOKE, GenericService.class.getName(), "", genericInvoke.getParameterTypes(),
                 new Object[]{"getPerson", new String[]{Person.class.getCanonicalName()}, new Object[]{person}});
 
         URL url = URL.valueOf("test://test:11/org.apache.dubbo.rpc.support.DemoService?" +
@@ -82,7 +82,7 @@ public class GenericFilterTest {
             person.put("name", "dubbo");
             person.put("age", 10);
 
-            RpcInvocation invocation = new RpcInvocation($INVOKE, GenericService.class.getName(), genericInvoke.getParameterTypes(),
+            RpcInvocation invocation = new RpcInvocation($INVOKE, GenericService.class.getName(), "", genericInvoke.getParameterTypes(),
                     new Object[]{"getPerson", new String[]{Person.class.getCanonicalName()}, new Object[]{person}});
             invocation.setAttachment(GENERIC_KEY, GENERIC_SERIALIZATION_NATIVE_JAVA);
 
@@ -106,7 +106,7 @@ public class GenericFilterTest {
         person.put("name", "dubbo");
         person.put("age", 10);
 
-        RpcInvocation invocation = new RpcInvocation("sayHi", GenericService.class.getName(), genericInvoke.getParameterTypes()
+        RpcInvocation invocation = new RpcInvocation("sayHi", GenericService.class.getName(), "", genericInvoke.getParameterTypes()
                 , new Object[]{"getPerson", new String[]{Person.class.getCanonicalName()}, new Object[]{person}});
 
         URL url = URL.valueOf("test://test:11/org.apache.dubbo.rpc.support.DemoService?" +
@@ -130,7 +130,7 @@ public class GenericFilterTest {
         person.put("name", "dubbo");
         person.put("age", 10);
 
-        RpcInvocation invocation = new RpcInvocation($INVOKE, GenericService.class.getName(), genericInvoke.getParameterTypes()
+        RpcInvocation invocation = new RpcInvocation($INVOKE, GenericService.class.getName(), "", genericInvoke.getParameterTypes()
                 , new Object[]{"getPerson", new String[]{Person.class.getCanonicalName()}});
 
         URL url = URL.valueOf("test://test:11/org.apache.dubbo.rpc.support.DemoService?" +
diff --git a/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/GenericImplFilterTest.java b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/GenericImplFilterTest.java
index a1908fb..b7828aa 100644
--- a/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/GenericImplFilterTest.java
+++ b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/filter/GenericImplFilterTest.java
@@ -49,7 +49,7 @@ public class GenericImplFilterTest {
     public void testInvoke() throws Exception {
 
         RpcInvocation invocation = new RpcInvocation("getPerson", "org.apache.dubbo.rpc.support.DemoService",
-                new Class[]{Person.class}, new Object[]{new Person("dubbo", 10)});
+                "org.apache.dubbo.rpc.support.DemoService:dubbo", new Class[]{Person.class}, new Object[]{new Person("dubbo", 10)});
 
 
         URL url = URL.valueOf("test://test:11/org.apache.dubbo.rpc.support.DemoService?" +
@@ -77,7 +77,7 @@ public class GenericImplFilterTest {
     public void testInvokeWithException() throws Exception {
 
         RpcInvocation invocation = new RpcInvocation("getPerson", "org.apache.dubbo.rpc.support.DemoService",
-                new Class[]{Person.class}, new Object[]{new Person("dubbo", 10)});
+                "org.apache.dubbo.rpc.support.DemoService:dubbo", new Class[]{Person.class}, new Object[]{new Person("dubbo", 10)});
 
         URL url = URL.valueOf("test://test:11/org.apache.dubbo.rpc.support.DemoService?" +
                 "accesslog=true&group=dubbo&version=1.1&generic=true");
@@ -104,8 +104,8 @@ public class GenericImplFilterTest {
         person.put("name", "dubbo");
         person.put("age", 10);
 
-        RpcInvocation invocation = new RpcInvocation($INVOKE, GenericService.class.getName(), genericInvoke.getParameterTypes(),
-                new Object[]{"getPerson", new String[]{Person.class.getCanonicalName()}, new Object[]{person}});
+        RpcInvocation invocation = new RpcInvocation($INVOKE, GenericService.class.getName(), "org.apache.dubbo.rpc.support.DemoService:dubbo",
+                genericInvoke.getParameterTypes(), new Object[]{"getPerson", new String[]{Person.class.getCanonicalName()}, new Object[]{person}});
 
         URL url = URL.valueOf("test://test:11/org.apache.dubbo.rpc.support.DemoService?" +
                 "accesslog=true&group=dubbo&version=1.1&generic=true");
diff --git a/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/proxy/AbstractProxyTest.java b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/proxy/AbstractProxyTest.java
index 1abaf0e..b505ee8 100644
--- a/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/proxy/AbstractProxyTest.java
+++ b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/proxy/AbstractProxyTest.java
@@ -51,7 +51,7 @@ public abstract class AbstractProxyTest {
         //Assertions.assertEquals(proxy.toString(), invoker.toString());
         //Assertions.assertEquals(proxy.hashCode(), invoker.hashCode());
 
-        Assertions.assertEquals(invoker.invoke(new RpcInvocation("echo", DemoService.class.getName(), new Class[]{String.class}, new Object[]{"aa"})).getValue()
+        Assertions.assertEquals(invoker.invoke(new RpcInvocation("echo", DemoService.class.getName(), DemoService.class.getName() + ":dubbo", new Class[]{String.class}, new Object[]{"aa"})).getValue()
                 , proxy.echo("aa"));
     }
 
@@ -65,7 +65,7 @@ public abstract class AbstractProxyTest {
 
         Assertions.assertEquals(invoker.getInterface(), DemoService.class);
 
-        Assertions.assertEquals(invoker.invoke(new RpcInvocation("echo", DemoService.class.getName(), new Class[]{String.class}, new Object[]{"aa"})).getValue(),
+        Assertions.assertEquals(invoker.invoke(new RpcInvocation("echo", DemoService.class.getName(), DemoService.class.getName() + ":dubbo", new Class[]{String.class}, new Object[]{"aa"})).getValue(),
                 origin.echo("aa"));
 
     }
diff --git a/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/support/MockInvocation.java b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/support/MockInvocation.java
index bdf77b3..586c990 100644
--- a/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/support/MockInvocation.java
+++ b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/support/MockInvocation.java
@@ -52,6 +52,11 @@ public class MockInvocation implements Invocation {
         return null;
     }
 
+    @Override
+    public String getProtocolServiceKey() {
+        return null;
+    }
+
     public String getMethodName() {
         return "echo";
     }
diff --git a/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/support/RpcUtilsTest.java b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/support/RpcUtilsTest.java
index f70d8b6..00deb91 100644
--- a/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/support/RpcUtilsTest.java
+++ b/dubbo-rpc/dubbo-rpc-api/src/test/java/org/apache/dubbo/rpc/support/RpcUtilsTest.java
@@ -51,7 +51,7 @@ public class RpcUtilsTest {
         URL url = URL.valueOf("dubbo://localhost/?test.async=true");
         Map<String, Object> attachments = new HashMap<>();
         attachments.put("aa", "bb");
-        Invocation inv = new RpcInvocation("test", "DemoService", new Class[]{}, new String[]{}, attachments);
+        Invocation inv = new RpcInvocation("test", "DemoService", "", new Class[]{}, new String[]{}, attachments);
         RpcUtils.attachInvocationIdIfAsync(url, inv);
         long id1 = RpcUtils.getInvocationId(inv);
         RpcUtils.attachInvocationIdIfAsync(url, inv);
@@ -68,7 +68,7 @@ public class RpcUtilsTest {
     @Test
     public void testAttachInvocationIdIfAsync_sync() {
         URL url = URL.valueOf("dubbo://localhost/");
-        Invocation inv = new RpcInvocation("test", "DemoService", new Class[]{}, new String[]{});
+        Invocation inv = new RpcInvocation("test", "DemoService", "", new Class[]{}, new String[]{});
         RpcUtils.attachInvocationIdIfAsync(url, inv);
         assertNull(RpcUtils.getInvocationId(inv));
     }
@@ -80,7 +80,7 @@ public class RpcUtilsTest {
     @Test
     public void testAttachInvocationIdIfAsync_nullAttachments() {
         URL url = URL.valueOf("dubbo://localhost/?test.async=true");
-        Invocation inv = new RpcInvocation("test", "DemoService", new Class[]{}, new String[]{});
+        Invocation inv = new RpcInvocation("test", "DemoService", "", new Class[]{}, new String[]{});
         RpcUtils.attachInvocationIdIfAsync(url, inv);
         assertTrue(RpcUtils.getInvocationId(inv) >= 0L);
     }
@@ -92,7 +92,7 @@ public class RpcUtilsTest {
     @Test
     public void testAttachInvocationIdIfAsync_forceNotAttache() {
         URL url = URL.valueOf("dubbo://localhost/?test.async=true&" + AUTO_ATTACH_INVOCATIONID_KEY + "=false");
-        Invocation inv = new RpcInvocation("test", "DemoService", new Class[]{}, new String[]{});
+        Invocation inv = new RpcInvocation("test", "DemoService", "", new Class[]{}, new String[]{});
         RpcUtils.attachInvocationIdIfAsync(url, inv);
         assertNull(RpcUtils.getInvocationId(inv));
     }
@@ -104,7 +104,7 @@ public class RpcUtilsTest {
     @Test
     public void testAttachInvocationIdIfAsync_forceAttache() {
         URL url = URL.valueOf("dubbo://localhost/?" + AUTO_ATTACH_INVOCATIONID_KEY + "=true");
-        Invocation inv = new RpcInvocation("test", "DemoService", new Class[]{}, new String[]{});
+        Invocation inv = new RpcInvocation("test", "DemoService", "", new Class[]{}, new String[]{});
         RpcUtils.attachInvocationIdIfAsync(url, inv);
         assertNotNull(RpcUtils.getInvocationId(inv));
     }
@@ -117,30 +117,30 @@ public class RpcUtilsTest {
         given(invoker.getUrl()).willReturn(URL.valueOf("test://127.0.0.1:1/org.apache.dubbo.rpc.support.DemoService?interface=org.apache.dubbo.rpc.support.DemoService"));
 
         // void sayHello(String name);
-        RpcInvocation inv = new RpcInvocation("sayHello", serviceName, new Class<?>[]{String.class}, null, null, invoker, null);
+        RpcInvocation inv = new RpcInvocation("sayHello", serviceName, "", new Class<?>[]{String.class}, null, null, invoker, null);
         Class<?> returnType = RpcUtils.getReturnType(inv);
         Assertions.assertNull(returnType);
 
         //String echo(String text);
-        RpcInvocation inv1 = new RpcInvocation("echo", serviceName, new Class<?>[]{String.class}, null, null, invoker, null);
+        RpcInvocation inv1 = new RpcInvocation("echo", serviceName, "", new Class<?>[]{String.class}, null, null, invoker, null);
         Class<?> returnType1 = RpcUtils.getReturnType(inv1);
         Assertions.assertNotNull(returnType1);
         Assertions.assertEquals(String.class, returnType1);
 
         //int getSize(String[] strs);
-        RpcInvocation inv2 = new RpcInvocation("getSize", serviceName, new Class<?>[]{String[].class}, null, null, invoker, null);
+        RpcInvocation inv2 = new RpcInvocation("getSize", serviceName, "", new Class<?>[]{String[].class}, null, null, invoker, null);
         Class<?> returnType2 = RpcUtils.getReturnType(inv2);
         Assertions.assertNotNull(returnType2);
         Assertions.assertEquals(int.class, returnType2);
 
         //Person getPerson(Person person);
-        RpcInvocation inv3 = new RpcInvocation("getPerson", serviceName, new Class<?>[]{Person.class}, null, null, invoker, null);
+        RpcInvocation inv3 = new RpcInvocation("getPerson", serviceName, "", new Class<?>[]{Person.class}, null, null, invoker, null);
         Class<?> returnType3 = RpcUtils.getReturnType(inv3);
         Assertions.assertNotNull(returnType3);
         Assertions.assertEquals(Person.class, returnType3);
 
         //List<String> testReturnType1(String str);
-        RpcInvocation inv4 = new RpcInvocation("testReturnType1", serviceName, new Class<?>[]{String.class}, null, null, invoker, null);
+        RpcInvocation inv4 = new RpcInvocation("testReturnType1", serviceName, "", new Class<?>[]{String.class}, null, null, invoker, null);
         Class<?> returnType4 = RpcUtils.getReturnType(inv4);
         Assertions.assertNotNull(returnType4);
         Assertions.assertEquals(List.class, returnType4);
@@ -154,7 +154,7 @@ public class RpcUtilsTest {
         Invoker invoker = mock(Invoker.class);
         given(invoker.getUrl()).willReturn(URL.valueOf("test://127.0.0.1:1/org.apache.dubbo.rpc.support.DemoService?interface=org.apache.dubbo.rpc.support.DemoService"));
 
-        RpcInvocation inv = new RpcInvocation("testReturnType", serviceName, new Class<?>[]{String.class}, null, null, invoker, null);
+        RpcInvocation inv = new RpcInvocation("testReturnType", serviceName, "", new Class<?>[]{String.class}, null, null, invoker, null);
         Type[] types = RpcUtils.getReturnTypes(inv);
         Assertions.assertNotNull(types);
         Assertions.assertEquals(2, types.length);
@@ -162,7 +162,7 @@ public class RpcUtilsTest {
         Assertions.assertEquals(String.class, types[1]);
         Assertions.assertArrayEquals(types, inv.getReturnTypes());
 
-        RpcInvocation inv1 = new RpcInvocation("testReturnType1", serviceName, new Class<?>[]{String.class}, null, null, invoker, null);
+        RpcInvocation inv1 = new RpcInvocation("testReturnType1", serviceName, "", new Class<?>[]{String.class}, null, null, invoker, null);
         java.lang.reflect.Type[] types1 = RpcUtils.getReturnTypes(inv1);
         Assertions.assertNotNull(types1);
         Assertions.assertEquals(2, types1.length);
@@ -170,7 +170,7 @@ public class RpcUtilsTest {
         Assertions.assertEquals(demoServiceClass.getMethod("testReturnType1", String.class).getGenericReturnType(), types1[1]);
         Assertions.assertArrayEquals(types1, inv1.getReturnTypes());
 
-        RpcInvocation inv2 = new RpcInvocation("testReturnType2", serviceName, new Class<?>[]{String.class}, null, null, invoker, null);
+        RpcInvocation inv2 = new RpcInvocation("testReturnType2", serviceName, "", new Class<?>[]{String.class}, null, null, invoker, null);
         java.lang.reflect.Type[] types2 = RpcUtils.getReturnTypes(inv2);
         Assertions.assertNotNull(types2);
         Assertions.assertEquals(2, types2.length);
@@ -178,7 +178,7 @@ public class RpcUtilsTest {
         Assertions.assertEquals(String.class, types2[1]);
         Assertions.assertArrayEquals(types2, inv2.getReturnTypes());
 
-        RpcInvocation inv3 = new RpcInvocation("testReturnType3", serviceName, new Class<?>[]{String.class}, null, null, invoker, null);
+        RpcInvocation inv3 = new RpcInvocation("testReturnType3", serviceName, "", new Class<?>[]{String.class}, null, null, invoker, null);
         java.lang.reflect.Type[] types3 = RpcUtils.getReturnTypes(inv3);
         Assertions.assertNotNull(types3);
         Assertions.assertEquals(2, types3.length);
@@ -187,7 +187,7 @@ public class RpcUtilsTest {
         Assertions.assertEquals(((ParameterizedType) genericReturnType3).getActualTypeArguments()[0], types3[1]);
         Assertions.assertArrayEquals(types3, inv3.getReturnTypes());
 
-        RpcInvocation inv4 = new RpcInvocation("testReturnType4", serviceName, new Class<?>[]{String.class}, null, null, invoker, null);
+        RpcInvocation inv4 = new RpcInvocation("testReturnType4", serviceName, "", new Class<?>[]{String.class}, null, null, invoker, null);
         java.lang.reflect.Type[] types4 = RpcUtils.getReturnTypes(inv4);
         Assertions.assertNotNull(types4);
         Assertions.assertEquals(2, types4.length);
@@ -195,7 +195,7 @@ public class RpcUtilsTest {
         Assertions.assertNull(types4[1]);
         Assertions.assertArrayEquals(types4, inv4.getReturnTypes());
 
-        RpcInvocation inv5 = new RpcInvocation("testReturnType5", serviceName, new Class<?>[]{String.class}, null, null, invoker, null);
+        RpcInvocation inv5 = new RpcInvocation("testReturnType5", serviceName, "", new Class<?>[]{String.class}, null, null, invoker, null);
         java.lang.reflect.Type[] types5 = RpcUtils.getReturnTypes(inv5);
         Assertions.assertNotNull(types5);
         Assertions.assertEquals(2, types5.length);
@@ -212,7 +212,7 @@ public class RpcUtilsTest {
         Invoker invoker = mock(Invoker.class);
 
         // void sayHello(String name);
-        RpcInvocation inv1 = new RpcInvocation("sayHello", serviceName,
+        RpcInvocation inv1 = new RpcInvocation("sayHello", serviceName, "",
                 new Class<?>[]{String.class}, null, null, invoker, null);
         Class<?>[] parameterTypes1 = RpcUtils.getParameterTypes(inv1);
         Assertions.assertNotNull(parameterTypes1);
@@ -220,12 +220,12 @@ public class RpcUtilsTest {
         Assertions.assertEquals(String.class, parameterTypes1[0]);
 
         //long timestamp();
-        RpcInvocation inv2 = new RpcInvocation("timestamp", serviceName, null, null, null, invoker, null);
+        RpcInvocation inv2 = new RpcInvocation("timestamp", serviceName, "", null, null, null, invoker, null);
         Class<?>[] parameterTypes2 = RpcUtils.getParameterTypes(inv2);
         Assertions.assertEquals(0, parameterTypes2.length);
 
         //Type enumlength(Type... types);
-        RpcInvocation inv3 = new RpcInvocation("enumlength", serviceName,
+        RpcInvocation inv3 = new RpcInvocation("enumlength", serviceName, "",
                 new Class<?>[]{Type.class, Type.class}, null, null, invoker, null);
         Class<?>[] parameterTypes3 = RpcUtils.getParameterTypes(inv3);
         Assertions.assertNotNull(parameterTypes3);
@@ -234,7 +234,7 @@ public class RpcUtilsTest {
         Assertions.assertEquals(Type.class, parameterTypes3[1]);
 
         //byte getbyte(byte arg);
-        RpcInvocation inv4 = new RpcInvocation("getbyte", serviceName,
+        RpcInvocation inv4 = new RpcInvocation("getbyte", serviceName, "",
                 new Class<?>[]{byte.class}, null, null, invoker, null);
         Class<?>[] parameterTypes4 = RpcUtils.getParameterTypes(inv4);
         Assertions.assertNotNull(parameterTypes4);
@@ -242,7 +242,7 @@ public class RpcUtilsTest {
         Assertions.assertEquals(byte.class, parameterTypes4[0]);
 
         //void $invoke(String s1, String s2);
-        RpcInvocation inv5 = new RpcInvocation("$invoke", serviceName,
+        RpcInvocation inv5 = new RpcInvocation("$invoke", serviceName, "",
                 new Class<?>[]{String.class, String[].class},
                 new Object[]{"method", new String[]{"java.lang.String", "void", "java.lang.Object"}},
                 null, invoker, null);
@@ -265,7 +265,7 @@ public class RpcUtilsTest {
         String serviceName = demoServiceClass.getName();
         Invoker invoker = mock(Invoker.class);
 
-        RpcInvocation inv1 = new RpcInvocation(methodName, serviceName,
+        RpcInvocation inv1 = new RpcInvocation(methodName, serviceName, "",
                 new Class<?>[]{String.class}, null, null, invoker, null);
         String actual = RpcUtils.getMethodName(inv1);
         Assertions.assertNotNull(actual);
@@ -283,7 +283,7 @@ public class RpcUtilsTest {
         String serviceName = demoServiceClass.getName();
         Invoker invoker = mock(Invoker.class);
 
-        RpcInvocation inv = new RpcInvocation("$invoke", serviceName,
+        RpcInvocation inv = new RpcInvocation("$invoke", serviceName, "",
                 new Class<?>[]{String.class, String[].class},
                 new Object[]{method, new String[]{"java.lang.String", "void", "java.lang.Object"}},
                 null, invoker, null);
@@ -300,7 +300,7 @@ public class RpcUtilsTest {
         String serviceName = demoServiceClass.getName();
         Invoker invoker = mock(Invoker.class);
 
-        RpcInvocation inv = new RpcInvocation("$invoke", serviceName,
+        RpcInvocation inv = new RpcInvocation("$invoke", serviceName, "",
                 new Class<?>[]{String.class, String[].class, Object[].class},
                 new Object[]{"method", new String[]{}, args},
                 null, invoker, null);
diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/CallbackServiceCodec.java b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/CallbackServiceCodec.java
index eead37c..3b87068 100644
--- a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/CallbackServiceCodec.java
+++ b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/CallbackServiceCodec.java
@@ -64,11 +64,11 @@ class CallbackServiceCodec {
     private static final byte CALLBACK_DESTROY = 0x2;
     private static final String INV_ATT_CALLBACK_KEY = "sys_callback_arg-";
 
-    private static byte isCallBack(URL url, String methodName, int argIndex) {
+    private static byte isCallBack(URL url, String protocolServiceKey, String methodName, int argIndex) {
         // parameter callback rule: method-name.parameter-index(starting from 0).callback
         byte isCallback = CALLBACK_NONE;
-        if (url != null && url.hasMethodParameter(methodName)) {
-            String callback = url.getParameter(methodName + "." + argIndex + ".callback");
+        if (url != null && url.hasServiceMethodParameter(protocolServiceKey, methodName)) {
+            String callback = url.getServiceParameter(protocolServiceKey, methodName + "." + argIndex + ".callback");
             if (callback != null) {
                 if ("true".equalsIgnoreCase(callback)) {
                     isCallback = CALLBACK_CREATE;
@@ -269,7 +269,7 @@ class CallbackServiceCodec {
     public static Object encodeInvocationArgument(Channel channel, RpcInvocation inv, int paraIndex) throws IOException {
         // get URL directly
         URL url = inv.getInvoker() == null ? null : inv.getInvoker().getUrl();
-        byte callbackStatus = isCallBack(url, inv.getMethodName(), paraIndex);
+        byte callbackStatus = isCallBack(url, inv.getProtocolServiceKey(), inv.getMethodName(), paraIndex);
         Object[] args = inv.getArguments();
         Class<?>[] pts = inv.getParameterTypes();
         switch (callbackStatus) {
@@ -296,7 +296,7 @@ class CallbackServiceCodec {
             }
             return inObject;
         }
-        byte callbackstatus = isCallBack(url, inv.getMethodName(), paraIndex);
+        byte callbackstatus = isCallBack(url, inv.getProtocolServiceKey(), inv.getMethodName(), paraIndex);
         switch (callbackstatus) {
             case CallbackServiceCodec.CALLBACK_CREATE:
                 try {
diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboProtocol.java b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboProtocol.java
index e095523..791df67 100644
--- a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboProtocol.java
+++ b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboProtocol.java
@@ -195,7 +195,7 @@ public class DubboProtocol extends AbstractProtocol {
                 return null;
             }
 
-            RpcInvocation invocation = new RpcInvocation(method, url.getParameter(INTERFACE_KEY), new Class<?>[0], new Object[0]);
+            RpcInvocation invocation = new RpcInvocation(method, url.getParameter(INTERFACE_KEY), "", new Class<?>[0], new Object[0]);
             invocation.setAttachment(PATH_KEY, url.getPath());
             invocation.setAttachment(GROUP_KEY, url.getParameter(GROUP_KEY));
             invocation.setAttachment(INTERFACE_KEY, url.getParameter(INTERFACE_KEY));
@@ -572,10 +572,9 @@ public class DubboProtocol extends AbstractProtocol {
         // client type setting.
         String str = url.getParameter(CLIENT_KEY, url.getParameter(SERVER_KEY, DEFAULT_REMOTING_CLIENT));
 
-//        url = url.addParameter(CODEC_KEY, DubboCodec.NAME);
+        url = url.addParameter(CODEC_KEY, DubboCodec.NAME);
         // enable heartbeat by default
-        // FIXME,
-//        url = url.addParameterIfAbsent(HEARTBEAT_KEY, String.valueOf(DEFAULT_HEARTBEAT));
+        url = url.addParameterIfAbsent(HEARTBEAT_KEY, String.valueOf(DEFAULT_HEARTBEAT));
 
         // BIO is not allowed since it has severe performance issue.
         if (str != null && str.length() > 0 && !ExtensionLoader.getExtensionLoader(Transporter.class).hasExtension(str)) {
diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/ReferenceCountExchangeClient.java b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/ReferenceCountExchangeClient.java
index 9229363..8c4d07b 100644
--- a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/ReferenceCountExchangeClient.java
+++ b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/ReferenceCountExchangeClient.java
@@ -19,7 +19,6 @@ package org.apache.dubbo.rpc.protocol.dubbo;
 
 import org.apache.dubbo.common.Parameters;
 import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.URLBuilder;
 import org.apache.dubbo.remoting.ChannelHandler;
 import org.apache.dubbo.remoting.RemotingException;
 import org.apache.dubbo.remoting.exchange.ExchangeClient;
@@ -181,14 +180,10 @@ final class ReferenceCountExchangeClient implements ExchangeClient {
      */
     private void replaceWithLazyClient() {
         // this is a defensive operation to avoid client is closed by accident, the initial state of the client is false
-        URL lazyUrl = URLBuilder.from(url)
-                .addParameter(LAZY_CONNECT_INITIAL_STATE_KEY, Boolean.TRUE)
+        URL lazyUrl = url.addParameter(LAZY_CONNECT_INITIAL_STATE_KEY, Boolean.TRUE)
                 .addParameter(RECONNECT_KEY, Boolean.FALSE)
                 .addParameter(SEND_RECONNECT_KEY, Boolean.TRUE.toString())
-                .addParameter("warning", Boolean.TRUE.toString())
-                .addParameter(LazyConnectExchangeClient.REQUEST_WITH_WARNING_KEY, true)
-                .addParameter("_client_memo", "referencecounthandler.replacewithlazyclient")
-                .build();
+                .addParameter(LazyConnectExchangeClient.REQUEST_WITH_WARNING_KEY, true);
 
         /**
          * the order of judgment in the if statement cannot be changed.


[dubbo] 02/27: service instance subscription

Posted by li...@apache.org.
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

commit 20cf141f2e8588e1695c37c14b4d89a268b96452
Author: ken.lj <ke...@gmail.com>
AuthorDate: Thu Jun 4 11:52:08 2020 +0800

    service instance subscription
---
 .../org/apache/dubbo/metadata/MetadataInfo.java    | 181 +++++++
 .../org/apache/dubbo/registry/NotifyListener.java  |   6 +
 .../registry/client/DefaultServiceInstance.java    |  18 +
 .../dubbo/registry/client/InstanceAddressURL.java  | 132 +++++
 .../dubbo/registry/client/ServiceDiscovery.java    |   3 +-
 .../registry/client/ServiceDiscoveryRegistry.java  | 592 ++-------------------
 .../dubbo/registry/client/ServiceInstance.java     |   4 +
 .../client/event/ServiceInstancesChangedEvent.java |  12 +-
 .../listener/ServiceInstancesChangedListener.java  | 113 +++-
 .../registry/integration/RegistryDirectory.java    |  33 ++
 10 files changed, 544 insertions(+), 550 deletions(-)

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
new file mode 100644
index 0000000..20933fb
--- /dev/null
+++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataInfo.java
@@ -0,0 +1,181 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.metadata;
+
+import org.apache.dubbo.common.URL;
+
+import java.io.Serializable;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+public class MetadataInfo implements Serializable {
+    private String app;
+    private String revision;
+    private Map<String, ServiceInfo> services;
+
+    public MetadataInfo() {
+    }
+
+    public MetadataInfo(String app, String revision, Map<String, ServiceInfo> services) {
+        this.app = app;
+        this.revision = revision;
+        this.services = services == null ? new HashMap<>() : services;
+    }
+
+    public String getApp() {
+        return app;
+    }
+
+    public void setApp(String app) {
+        this.app = app;
+    }
+
+    public String getRevision() {
+        return revision;
+    }
+
+    public void setRevision(String revision) {
+        this.revision = revision;
+    }
+
+    public Map<String, ServiceInfo> getServices() {
+        return services;
+    }
+
+    public void setServices(Map<String, ServiceInfo> services) {
+        this.services = services;
+    }
+
+    public ServiceInfo getServiceInfo(String serviceKey) {
+        return services.get(serviceKey);
+    }
+
+    public String getParameter(String key, String serviceKey) {
+        ServiceInfo serviceInfo = services.get(serviceKey);
+        if (serviceInfo == null) {
+            return null;
+        }
+        return serviceInfo.getParameter(key);
+    }
+
+    public Map<String, String> getParameters(String serviceKey) {
+        ServiceInfo serviceInfo = services.get(serviceKey);
+        if (serviceInfo == null) {
+            return Collections.emptyMap();
+        }
+        return serviceInfo.getParams();
+    }
+
+    public static class ServiceInfo implements Serializable {
+        private String name;
+        private String group;
+        private String version;
+        private String protocol;
+        private String registry;
+        private Map<String, String> params;
+
+        private transient Map<String, Map<String, String>> methodParams;
+        private transient String serviceKey;
+
+        public ServiceInfo() {
+        }
+
+        public ServiceInfo(String name, String group, String version, String protocol, String registry, Map<String, String> params) {
+            this.name = name;
+            this.group = group;
+            this.version = version;
+            this.protocol = protocol;
+            this.registry = registry;
+            this.params = params == null ? new HashMap<>() : params;
+
+            this.serviceKey = URL.buildKey(name, group, version);
+        }
+
+        public String getServiceKey() {
+            return serviceKey;
+        }
+
+        public String getName() {
+            return name;
+        }
+
+        public void setName(String name) {
+            this.name = name;
+        }
+
+        public String getGroup() {
+            return group;
+        }
+
+        public void setGroup(String group) {
+            this.group = group;
+        }
+
+        public String getVersion() {
+            return version;
+        }
+
+        public void setVersion(String version) {
+            this.version = version;
+        }
+
+        public String getProtocol() {
+            return protocol;
+        }
+
+        public void setProtocol(String protocol) {
+            this.protocol = protocol;
+        }
+
+        public String getRegistry() {
+            return registry;
+        }
+
+        public void setRegistry(String registry) {
+            this.registry = registry;
+        }
+
+        public Map<String, String> getParams() {
+            if (params == null) {
+                return Collections.emptyMap();
+            }
+            return params;
+        }
+
+        public void setParams(Map<String, String> params) {
+            this.params = params;
+        }
+
+        public String getParameter(String key) {
+            return params.get(key);
+        }
+
+        public String getMethodParameter(String method, String key, String defaultValue) {
+            if (methodParams == null) {
+                methodParams = URL.toMethodParameters(params);
+            }
+
+            Map<String, String> keyMap = methodParams.get(method);
+            String value = null;
+            if (keyMap != null) {
+                value = keyMap.get(key);
+            }
+            return value == null ? defaultValue : value;
+        }
+    }
+}
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/NotifyListener.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/NotifyListener.java
index 0e41cc9..5b54000 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/NotifyListener.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/NotifyListener.java
@@ -17,6 +17,7 @@
 package org.apache.dubbo.registry;
 
 import org.apache.dubbo.common.URL;
+import org.apache.dubbo.registry.client.event.listener.ServiceInstancesChangedListener;
 
 import java.util.List;
 
@@ -41,4 +42,9 @@ public interface NotifyListener {
      */
     void notify(List<URL> urls);
 
+    default void addServiceListener(ServiceInstancesChangedListener instanceListener) {
+    }
+
+    default void notifyServiceInstances() {
+    }
 }
\ No newline at end of file
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/DefaultServiceInstance.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/DefaultServiceInstance.java
index 366f607..6c88e8c 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/DefaultServiceInstance.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/DefaultServiceInstance.java
@@ -16,6 +16,9 @@
  */
 package org.apache.dubbo.registry.client;
 
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.metadata.MetadataInfo;
+
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Objects;
@@ -43,6 +46,8 @@ public class DefaultServiceInstance implements ServiceInstance {
 
     private Map<String, String> metadata = new HashMap<>();
 
+    private MetadataInfo serviceMetadata;
+
     public DefaultServiceInstance() {
     }
 
@@ -125,6 +130,19 @@ public class DefaultServiceInstance implements ServiceInstance {
         this.metadata = metadata;
     }
 
+    public MetadataInfo getServiceMetadata() {
+        return serviceMetadata;
+    }
+
+    public void setServiceMetadata(MetadataInfo serviceMetadata) {
+        this.serviceMetadata = serviceMetadata;
+    }
+
+    @Override
+    public URL toURL(String protocol, String path, String interfaceName, String group, String version, String serviceKey) {
+        return new InstanceAddressURL(protocol, host, port, path, interfaceName, group, version, serviceKey);
+    }
+
     @Override
     public boolean equals(Object o) {
         if (this == o) return true;
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/InstanceAddressURL.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/InstanceAddressURL.java
new file mode 100644
index 0000000..a4ece68
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/InstanceAddressURL.java
@@ -0,0 +1,132 @@
+/*
+ * 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;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.utils.StringUtils;
+import org.apache.dubbo.metadata.MetadataInfo;
+
+import java.util.HashMap;
+import java.util.Map;
+
+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.VERSION_KEY;
+
+public class InstanceAddressURL extends URL {
+
+    private MetadataInfo metadataInfo; // points to metadata of one revision
+    private String interfaceName;
+    private String group;
+    private String version;
+    private String serviceKey;
+
+    public InstanceAddressURL(String protocol, String host, int port, String path, String interfaceName, String group, String version, String serviceKey) {
+        super(protocol, host, port, path);
+        this.interfaceName = interfaceName;
+        this.group = group;
+        this.version = version;
+        this.serviceKey = serviceKey;
+    }
+
+    @Override
+    public String getServiceInterface() {
+        return interfaceName;
+    }
+
+    public String getGroup() {
+        return group;
+    }
+
+    public String getVersion() {
+        return version;
+    }
+
+    @Override
+    public String getServiceKey() {
+        return serviceKey;
+    }
+
+    @Override
+    public String getParameter(String key) {
+        if (VERSION_KEY.equals(key)) {
+            return getVersion();
+        } else if (GROUP_KEY.equals(key)) {
+            return getGroup();
+        } else if (INTERFACE_KEY.equals(key)) {
+            return getServiceInterface();
+        }
+
+        String value = super.getParameter(key);
+        if (StringUtils.isEmpty(value)) {
+            value = metadataInfo.getParameter(key, this.getServiceKey());
+        }
+        return value;
+    }
+
+    @Override
+    public String getParameter(String key, String defaultValue) {
+        if (VERSION_KEY.equals(key)) {
+            return getVersion();
+        } else if (GROUP_KEY.equals(key)) {
+            return getGroup();
+        } else if (INTERFACE_KEY.equals(key)) {
+            return getServiceInterface();
+        }
+
+        String value = getParameter(key);
+        if (StringUtils.isEmpty(value)) {
+            return defaultValue;
+        }
+        return value;
+    }
+
+    @Override
+    public String getMethodParameter(String method, String key) {
+        Map<String, Map<String, String>> instanceMethodParams = super.getMethodParameters();
+        Map<String, String> keyMap = instanceMethodParams.get(method);
+        String value = null;
+        if (keyMap != null) {
+            value = keyMap.get(key);
+        }
+
+        MetadataInfo.ServiceInfo serviceInfo = metadataInfo.getServiceInfo(getServiceKey());
+        value = serviceInfo.getMethodParameter(method, key, value);
+        return value;
+    }
+
+    @Override
+    public Map<String, String> getParameters() {
+        Map<String, String> instanceParams = super.getParameters();
+        Map<String, String> metadataParams = metadataInfo.getParameters(getServiceKey());
+        int i = instanceParams == null ? 0 : instanceParams.size();
+        int j = metadataParams == null ? 0 : metadataParams.size();
+        Map<String, String> params = new HashMap<>((int) ((i + j) / 0.75) + 1);
+        if (instanceParams != null) {
+            params.putAll(instanceParams);
+        }
+        if (metadataParams != null) {
+            params.putAll(metadataParams);
+        }
+        return params;
+    }
+
+    public void setMetadata(MetadataInfo metadataInfo) {
+        this.metadataInfo = metadataInfo;
+    }
+
+}
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscovery.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscovery.java
index f2d428a..bc73cd0 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscovery.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscovery.java
@@ -27,7 +27,6 @@ import org.apache.dubbo.registry.NotifyListener;
 import org.apache.dubbo.registry.client.event.ServiceInstancesChangedEvent;
 import org.apache.dubbo.registry.client.event.listener.ServiceInstancesChangedListener;
 
-import java.util.Collection;
 import java.util.LinkedHashMap;
 import java.util.LinkedList;
 import java.util.List;
@@ -247,7 +246,7 @@ public interface ServiceDiscovery extends Prioritized {
      * @param serviceName      the name of service whose service instances have been changed
      * @param serviceInstances the service instances have been changed
      */
-    default void dispatchServiceInstancesChangedEvent(String serviceName, Collection<ServiceInstance> serviceInstances) {
+    default void dispatchServiceInstancesChangedEvent(String serviceName, List<ServiceInstance> serviceInstances) {
         dispatchServiceInstancesChangedEvent(new ServiceInstancesChangedEvent(serviceName, serviceInstances));
     }
 
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 db03a3c..6232850 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
@@ -17,71 +17,54 @@
 package org.apache.dubbo.registry.client;
 
 import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.URLBuilder;
 import org.apache.dubbo.common.extension.ExtensionLoader;
 import org.apache.dubbo.common.extension.SPI;
 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.MetadataService;
 import org.apache.dubbo.metadata.ServiceNameMapping;
 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.ServiceInstancesChangedEvent;
 import org.apache.dubbo.registry.client.event.listener.ServiceInstancesChangedListener;
-import org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils;
 import org.apache.dubbo.registry.client.metadata.SubscribedURLsSynthesizer;
-import org.apache.dubbo.registry.client.metadata.proxy.MetadataServiceProxyFactory;
-import org.apache.dubbo.registry.client.selector.ServiceInstanceSelector;
 import org.apache.dubbo.registry.support.FailbackRegistry;
 
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.HashSet;
+import java.util.HashMap;
 import java.util.LinkedHashMap;
 import java.util.LinkedHashSet;
-import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
-import java.util.SortedSet;
-import java.util.concurrent.locks.Lock;
-import java.util.concurrent.locks.ReadWriteLock;
-import java.util.concurrent.locks.ReentrantReadWriteLock;
-import java.util.function.Supplier;
 import java.util.stream.Collectors;
 
 import static java.lang.String.format;
-import static java.util.Collections.emptyList;
-import static org.apache.dubbo.common.URLBuilder.from;
-import static org.apache.dubbo.common.constants.CommonConstants.COMMA_SEPARATOR_CHAR;
+import static java.util.Collections.emptySet;
+import static java.util.Collections.unmodifiableSet;
+import static java.util.stream.Collectors.toSet;
+import static java.util.stream.Stream.of;
+import static org.apache.dubbo.common.constants.CommonConstants.DUBBO_PROTOCOL;
 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.PROTOCOL_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;
-import static org.apache.dubbo.common.constants.RegistryConstants.CATEGORY_KEY;
-import static org.apache.dubbo.common.constants.RegistryConstants.EMPTY_PROTOCOL;
 import static org.apache.dubbo.common.constants.RegistryConstants.PROVIDED_BY;
 import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_TYPE_KEY;
 import static org.apache.dubbo.common.constants.RegistryConstants.SERVICE_REGISTRY_TYPE;
 import static org.apache.dubbo.common.constants.RegistryConstants.SUBSCRIBED_SERVICE_NAMES_KEY;
-import static org.apache.dubbo.common.extension.ExtensionLoader.getExtensionLoader;
 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.CollectionUtils.isNotEmpty;
-import static org.apache.dubbo.common.utils.StringUtils.splitToSet;
-import static org.apache.dubbo.metadata.MetadataService.toURLs;
+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.getExportedServicesRevision;
 import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.getMetadataStorageType;
-import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.getProtocolPort;
 
 /**
  * Being different to the traditional registry, {@link ServiceDiscoveryRegistry} that is a new service-oriented
@@ -104,7 +87,7 @@ import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataU
  * {@link ServiceNameMapping} will help to figure out one or more services that exported correlative Dubbo services. If
  * the service names can be found, the exported {@link URL URLs} will be get from the remote {@link MetadataService}
  * being deployed on all {@link ServiceInstance instances} of services. The whole process runs under the
- * {@link #subscribeURLs(URL, List, String, Collection)} method. It's very expensive to invoke
+ * {@link #subscribeURLs(URL, NotifyListener, String, Collection)} method. It's very expensive to invoke
  * {@link MetadataService} for each {@link ServiceInstance service instance}, thus {@link ServiceDiscoveryRegistry}
  * introduces a cache to optimize the calculation with "revisions". If the revisions of N
  * {@link ServiceInstance service instances} are same, {@link MetadataService} is invoked just only once, and then it
@@ -126,7 +109,7 @@ public class ServiceDiscoveryRegistry extends FailbackRegistry {
 
     private final ServiceDiscovery serviceDiscovery;
 
-    private Set<String> subscribedServices;
+    private final Set<String> subscribedServices;
 
     private final ServiceNameMapping serviceNameMapping;
 
@@ -134,9 +117,8 @@ public class ServiceDiscoveryRegistry extends FailbackRegistry {
 
     private final Set<String> registeredListeners = new LinkedHashSet<>();
 
-    private final List<SubscribedURLsSynthesizer> subscribedURLsSynthesizers;
-
-    private final ReadWriteLock lock = new ReentrantReadWriteLock();
+    /* app - listener */
+    private final Map<String, ServiceInstancesChangedListener> serviceListeners = new HashMap<>();
 
     /**
      * A cache for all URLs of services that the subscribed services exported
@@ -148,10 +130,10 @@ public class ServiceDiscoveryRegistry extends FailbackRegistry {
     public ServiceDiscoveryRegistry(URL registryURL) {
         super(registryURL);
         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.subscribedURLsSynthesizers = initSubscribedURLsSynthesizers();
     }
 
     public ServiceDiscovery getServiceDiscovery() {
@@ -159,16 +141,6 @@ public class ServiceDiscoveryRegistry extends FailbackRegistry {
     }
 
     /**
-     * Get the subscribed services from the specified registry {@link URL url}
-     *
-     * @param registryURL the specified registry {@link URL url}
-     * @return non-null
-     */
-    public static Set<String> getSubscribedServices(URL registryURL) {
-        return parseServices(registryURL.getParameter(SUBSCRIBED_SERVICE_NAMES_KEY));
-    }
-
-    /**
      * Create the {@link ServiceDiscovery} from the registry {@link URL}
      *
      * @param registryURL the {@link URL} to connect the registry
@@ -317,73 +289,24 @@ public class ServiceDiscoveryRegistry extends FailbackRegistry {
         writableMetadataService.subscribeURL(url);
 
         Set<String> serviceNames = getServices(url);
-
-        List<URL> subscribedURLs = new LinkedList<>();
-
-        serviceNames.forEach(serviceName -> {
-
-            subscribeURLs(url, subscribedURLs, serviceName);
-
-            // register ServiceInstancesChangedListener
-            registerServiceInstancesChangedListener(url, new ServiceInstancesChangedListener(serviceName) {
-
-                @Override
-                public void onEvent(ServiceInstancesChangedEvent event) {
-                    List<URL> subscribedURLs = new LinkedList<>();
-                    Set<String> others = new HashSet<>(serviceNames);
-                    others.remove(serviceName);
-
-                    // Collect the subscribedURLs
-                    subscribeURLs(url, subscribedURLs, serviceName, () -> event.getServiceInstances());
-                    subscribeURLs(url, subscribedURLs, others.toString(), () -> getServiceInstances(others));
-
-                    // Notify all
-                    notifyAllSubscribedURLs(url, subscribedURLs, listener);
-
-                }
-            });
-        });
-
-        // Notify all
-        notifyAllSubscribedURLs(url, subscribedURLs, listener);
-
-    }
-
-    private void notifyAllSubscribedURLs(URL url, List<URL> subscribedURLs, NotifyListener listener) {
-
-        if (subscribedURLs.isEmpty()) {
-            // Add the EMPTY_PROTOCOL URL
-            subscribedURLs.add(from(url).setProtocol(EMPTY_PROTOCOL).removeParameter(CATEGORY_KEY).build());
+        if (CollectionUtils.isEmpty(serviceNames)) {
+            throw new IllegalStateException("Should has at least one way to know which services this interface belongs to, subscription url: " + url);
         }
 
-        // Notify all
-        listener.notify(subscribedURLs);
+        serviceNames.forEach(serviceName -> subscribeURLs(url, listener, serviceName));
     }
 
-    private List<ServiceInstance> getServiceInstances(Set<String> serviceNames) {
-        if (isEmpty(serviceNames)) {
-            return emptyList();
-        }
-        List<ServiceInstance> allServiceInstances = new LinkedList<>();
-        for (String serviceName : serviceNames) {
-            List<ServiceInstance> serviceInstances = serviceDiscovery.getInstances(serviceName);
-            if (!isEmpty(serviceInstances)) {
-                allServiceInstances.addAll(serviceInstances);
-            }
-        }
-        return allServiceInstances;
-    }
-
-    protected void subscribeURLs(URL subscribedURL, List<URL> subscribedURLs,
-                                 String serviceName, Supplier<Collection<ServiceInstance>> serviceInstancesSupplier) {
-        Collection<ServiceInstance> serviceInstances = serviceInstancesSupplier.get();
-        subscribeURLs(subscribedURL, subscribedURLs, serviceName, serviceInstances);
-    }
-
-
-    protected void subscribeURLs(URL url, List<URL> subscribedURLs, String serviceName) {
-        List<ServiceInstance> serviceInstances = serviceDiscovery.getInstances(serviceName);
-        subscribeURLs(url, subscribedURLs, serviceName, serviceInstances);
+    protected void subscribeURLs(URL url, NotifyListener listener, String serviceName) {
+        // register ServiceInstancesChangedListener
+        ServiceInstancesChangedListener serviceListener = serviceListeners.computeIfAbsent(serviceName,
+                k -> new ServiceInstancesChangedListener(serviceName) {
+                    @Override
+                    protected void notifyAddresses() {
+                        listener.notifyServiceInstances();
+                    }
+                });
+        listener.addServiceListener(serviceListener);
+        registerServiceInstancesChangedListener(url, serviceListener);
     }
 
     /**
@@ -403,432 +326,24 @@ public class ServiceDiscoveryRegistry extends FailbackRegistry {
         return listener.getServiceName() + ":" + url.toString(VERSION_KEY, GROUP_KEY, PROTOCOL_KEY);
     }
 
-    /**
-     * Subscribe the {@link URL URLs} that the specified service exported are
-     * {@link #getExportedURLs(ServiceInstance) get} from {@link MetadataService} if present, or try to
-     * be {@link #synthesizeSubscribedURLs(URL, Collection) synthesized} by
-     * the instances of {@link SubscribedURLsSynthesizer}
-     *
-     * @param subscribedURL    the subscribed {@link URL url}
-     * @param subscribedURLs   {@link NotifyListener}
-     * @param serviceName
-     * @param serviceInstances
-     * @see #getExportedURLs(URL, Collection)
-     * @see #synthesizeSubscribedURLs(URL, Collection)
-     */
-    protected void subscribeURLs(URL subscribedURL, List<URL> subscribedURLs, String serviceName,
-                                 Collection<ServiceInstance> serviceInstances) {
-
-        if (isEmpty(serviceInstances)) {
-            logger.warn(format("There is no instance in service[name : %s]", serviceName));
-            return;
-        }
-
-        /**
-         * Add the exported URLs from {@link MetadataService}
-         */
-        subscribedURLs.addAll(getExportedURLs(subscribedURL, serviceInstances));
-
-        if (subscribedURLs.isEmpty()) { // If empty, try to synthesize
-            /**
-             * Add the subscribed URLs that were synthesized
-             */
-            subscribedURLs.addAll(synthesizeSubscribedURLs(subscribedURL, serviceInstances));
-        }
-    }
-
-    /**
-     * Get the exported {@link URL URLs} from the  {@link MetadataService} in the specified
-     * {@link ServiceInstance service instances}
-     *
-     * @param subscribedURL the subscribed {@link URL url}
-     * @param instances     {@link ServiceInstance service instances}
-     * @return the exported {@link URL URLs} if present, or <code>{@link Collections#emptyList() empty list}</code>
-     */
-    private List<URL> getExportedURLs(URL subscribedURL, Collection<ServiceInstance> instances) {
-
-        // local service instances could be mutable
-        List<ServiceInstance> serviceInstances = instances.stream()
-                .filter(ServiceInstance::isEnabled)
-                .filter(ServiceInstance::isHealthy)
-                .filter(ServiceInstanceMetadataUtils::isDubboServiceInstance)
-                .collect(Collectors.toList());
-
-        int size = serviceInstances.size();
-
-        if (size == 0) {
-            return emptyList();
-        }
-
-        // Prepare revision exported URLs
-        prepareServiceRevisionExportedURLs(serviceInstances);
-
-        // Clone the subscribed URLs from the template URLs
-        List<URL> subscribedURLs = cloneExportedURLs(subscribedURL, serviceInstances);
-
-        // clear local service instances
-        serviceInstances.clear();
-
-        return subscribedURLs;
-    }
-
-    /**
-     * Prepare the {@link #serviceRevisionExportedURLsCache} exclusively
-     *
-     * @param serviceInstances {@link ServiceInstance service instances}
-     * @see #expungeStaleRevisionExportedURLs(List)
-     * @see #initializeRevisionExportedURLs(List)
-     */
-    private void prepareServiceRevisionExportedURLs(List<ServiceInstance> serviceInstances) {
-        executeExclusively(() -> {
-            // 1. expunge stale
-            expungeStaleRevisionExportedURLs(serviceInstances);
-            // 2. Initialize
-            initializeRevisionExportedURLs(serviceInstances);
-        });
-    }
-
-    /**
-     * Initialize the {@link URL URLs} that {@link ServiceInstance service instances} exported into
-     * {@link #serviceRevisionExportedURLsCache the cache}.
-     * <p>
-     * Typically, the {@link URL URLs} that one {@link ServiceInstance service instance} exported can be get from
-     * the same instances' {@link MetadataService}, but the cost is very expensive if there are a lot of instances
-     * in this service. Thus, the exported {@link URL URls} should be cached  and stored into
-     * {@link #serviceRevisionExportedURLsCache the cache}.
-     * <p>
-     * In most cases, {@link #serviceRevisionExportedURLsCache the cache} only holds a single list of exported URLs for
-     * each service because there is no difference on the Dubbo services(interfaces) between the service instances.
-     * However, if there are one or more upgrading or increasing Dubbo services that are deploying on the some of
-     * instances, other instances still maintain the previous ones, in this way, there are two versions of the services,
-     * they are called "revisions", in other words, one revision associates a list of exported URLs that can be reused
-     * for other instances with same revision, and one service allows one or more revisions.
-     *
-     * @param serviceInstances {@link ServiceInstance service instances}
-     */
-    private void initializeRevisionExportedURLs(List<ServiceInstance> serviceInstances) {
-        // initialize the revision exported URLs that the selected service instance exported
-        initializeSelectedRevisionExportedURLs(serviceInstances);
-        // initialize the revision exported URLs that other service instances exported
-        serviceInstances.forEach(this::initializeRevisionExportedURLs);
-    }
-
-    /**
-     * Initialize the {@link URL URLs} that the {@link #selectServiceInstance(List) selected service instance} exported
-     * into {@link #serviceRevisionExportedURLsCache the cache}.
-     *
-     * @param serviceInstances {@link ServiceInstance service instances}
-     */
-    private void initializeSelectedRevisionExportedURLs(List<ServiceInstance> serviceInstances) {
-        // Try to initialize revision exported URLs until success
-        for (int i = 0; i < serviceInstances.size(); i++) {
-            // select a instance of {@link ServiceInstance}
-            ServiceInstance selectedInstance = selectServiceInstance(serviceInstances);
-            List<URL> revisionExportedURLs = initializeRevisionExportedURLs(selectedInstance);
-            if (isNotEmpty(revisionExportedURLs)) {    // If the result is valid
-                break;
-            }
-        }
-    }
-
-    /**
-     * Expunge the revision exported {@link URL URLs} in {@link #serviceRevisionExportedURLsCache the cache} if
-     * some revisions of {@link ServiceInstance service instance} had been out of date possibly
-     *
-     * @param serviceInstances {@link ServiceInstance service instances}
-     */
-    private void expungeStaleRevisionExportedURLs(List<ServiceInstance> serviceInstances) {
-
-        String serviceName = serviceInstances.get(0).getServiceName();
-        // revisionExportedURLsMap is mutable
-        Map<String, List<URL>> revisionExportedURLsMap = getRevisionExportedURLsMap(serviceName);
-
-        if (revisionExportedURLsMap.isEmpty()) { // if empty, return immediately
-            return;
-        }
-
-        Set<String> existedRevisions = revisionExportedURLsMap.keySet(); // read-only
-        Set<String> currentRevisions = serviceInstances.stream()
-                .map(ServiceInstanceMetadataUtils::getExportedServicesRevision)
-                .collect(Collectors.toSet());
-        // staleRevisions = existedRevisions(copy) - currentRevisions
-        Set<String> staleRevisions = new HashSet<>(existedRevisions);
-        staleRevisions.removeAll(currentRevisions);
-        // remove exported URLs if staled
-        staleRevisions.forEach(revisionExportedURLsMap::remove);
-    }
-
-    /**
-     * Clone the exported URLs that are based on {@link #getTemplateExportedURLs(URL, ServiceInstance) the template URLs}
-     * from the some of {@link ServiceInstance service instances} with different revisions
-     *
-     * @param subscribedURL    the subscribed {@link URL url}
-     * @param serviceInstances {@link ServiceInstance service instances}
-     * @return non-null
-     */
-    private List<URL> cloneExportedURLs(URL subscribedURL, Collection<ServiceInstance> serviceInstances) {
-
-        if (isEmpty(serviceInstances)) {
-            return emptyList();
-        }
-
-        List<URL> clonedExportedURLs = new LinkedList<>();
-
-        serviceInstances.forEach(serviceInstance -> {
-
-            String host = serviceInstance.getHost();
-
-            getTemplateExportedURLs(subscribedURL, serviceInstance)
-                    .stream()
-                    .map(templateURL -> templateURL.removeParameter(TIMESTAMP_KEY))
-                    .map(templateURL -> templateURL.removeParameter(PID_KEY))
-                    .map(templateURL -> {
-                        String protocol = templateURL.getProtocol();
-                        int port = getProtocolPort(serviceInstance, protocol);
-                        if (Objects.equals(templateURL.getHost(), host)
-                                && Objects.equals(templateURL.getPort(), port)) { // use templateURL if equals
-                            return templateURL;
-                        }
-
-                        URLBuilder clonedURLBuilder = from(templateURL) // remove the parameters from the template URL
-                                .setHost(host)  // reset the host
-                                .setPort(port); // reset the port
-
-                        return clonedURLBuilder.build();
-                    })
-                    .forEach(clonedExportedURLs::add);
-        });
-        return clonedExportedURLs;
-    }
-
-
-    /**
-     * Select one {@link ServiceInstance} by {@link ServiceInstanceSelector the strategy} if there are more that one
-     * instances in order to avoid the hot spot appearing the some instance
-     *
-     * @param serviceInstances the {@link List list} of {@link ServiceInstance}
-     * @return <code>null</code> if <code>serviceInstances</code> is empty.
-     * @see ServiceInstanceSelector
-     */
-    private ServiceInstance selectServiceInstance(List<ServiceInstance> serviceInstances) {
-        int size = serviceInstances.size();
-        if (size == 0) {
-            return null;
-        } else if (size == 1) {
-            return serviceInstances.get(0);
-        }
-        ServiceInstanceSelector selector = getExtensionLoader(ServiceInstanceSelector.class).getAdaptiveExtension();
-        return selector.select(getUrl(), serviceInstances);
-    }
-
-    /**
-     * Get the template exported {@link URL urls} from the specified {@link ServiceInstance}.
-     * <p>
-     * First, put the revision {@link ServiceInstance service instance}
-     * associating {@link #getExportedURLs(ServiceInstance) exported URLs} into cache.
-     * <p>
-     * And then compare a new {@link ServiceInstance service instances'} revision with cached one,If they are equal,
-     * return the cached template {@link URL urls} immediately, or to get template {@link URL urls} that the provider
-     * {@link ServiceInstance instance} exported via executing {@link ##getExportedURLs(ServiceInstance) (ServiceInstance)}
-     * method.
-     * <p>
-     * Eventually, the retrieving result will be cached and returned.
-     *
-     * @param subscribedURL    the subscribed {@link URL url}
-     * @param selectedInstance the {@link ServiceInstance}
-     *                         associating with the {@link URL urls}
-     * @return non-null {@link List} of {@link URL urls}
-     */
-    private List<URL> getTemplateExportedURLs(URL subscribedURL, ServiceInstance selectedInstance) {
-
-        List<URL> exportedURLs = getRevisionExportedURLs(selectedInstance);
-
-        if (isEmpty(exportedURLs)) {
-            return emptyList();
-        }
-
-        return filterSubscribedURLs(subscribedURL, exportedURLs);
-    }
-
-    /**
-     * Initialize the URLs that the specified {@link ServiceInstance service instance} exported
-     *
-     * @param serviceInstance the {@link ServiceInstance} exports the Dubbo Services
-     * @return the {@link URL URLs} that the {@link ServiceInstance} exported, it's calculated from
-     * The invocation to remote {@link MetadataService}, or get from {@link #serviceRevisionExportedURLsCache cache} if
-     * {@link ServiceInstanceMetadataUtils#getExportedServicesRevision(ServiceInstance) revision} is hit
-     */
-    private List<URL> initializeRevisionExportedURLs(ServiceInstance serviceInstance) {
-
-        if (serviceInstance == null) {
-            return emptyList();
-        }
-
-        String serviceName = serviceInstance.getServiceName();
-        // get the revision from the specified {@link ServiceInstance}
-        String revision = getExportedServicesRevision(serviceInstance);
-
-        Map<String, List<URL>> revisionExportedURLsMap = getRevisionExportedURLsMap(serviceName);
-
-        List<URL> revisionExportedURLs = revisionExportedURLsMap.get(revision);
-
-        boolean firstGet = false;
-
-        if (revisionExportedURLs == null) { // The hit is missing in cache
-
-            if (!revisionExportedURLsMap.isEmpty()) { // The case is that current ServiceInstance with the different revision
-                if (logger.isWarnEnabled()) {
-                    logger.warn(format("The ServiceInstance[id: %s, host : %s , port : %s] has different revision : %s" +
-                                    ", please make sure the service [name : %s] is changing or not.",
-                            serviceInstance.getId(),
-                            serviceInstance.getHost(),
-                            serviceInstance.getPort(),
-                            revision,
-                            serviceInstance.getServiceName()
-                    ));
-                }
-            } else { // Else, it's the first time to get the exported URLs
-                firstGet = true;
-            }
-
-            revisionExportedURLs = getExportedURLs(serviceInstance);
-
-            if (revisionExportedURLs != null) { // just allow the valid result into exportedURLsMap
-
-                revisionExportedURLsMap.put(revision, revisionExportedURLs);
-
-                if (logger.isDebugEnabled()) {
-                    logger.debug(format("Get the exported URLs[size : %s, first : %s] from the target service " +
-                                    "instance [id: %s , service : %s , host : %s , port : %s , revision : %s]",
-                            revisionExportedURLs.size(), firstGet,
-                            serviceInstance.getId(),
-                            serviceInstance.getServiceName(),
-                            serviceInstance.getHost(),
-                            serviceInstance.getPort(),
-                            revision
-                    ));
-                }
-            }
-        } else { // Else, The cache is hit
-            if (logger.isDebugEnabled()) {
-                logger.debug(format("Get the exported URLs[size : %s] from cache, the instance" +
-                                "[id: %s , service : %s , host : %s , port : %s , revision : %s]",
-                        revisionExportedURLs.size(),
-                        serviceInstance.getId(),
-                        serviceInstance.getServiceName(),
-                        serviceInstance.getHost(),
-                        serviceInstance.getPort(),
-                        revision
-                ));
-            }
-        }
-
-        return revisionExportedURLs;
-    }
-
     private Map<String, List<URL>> getRevisionExportedURLsMap(String serviceName) {
         return serviceRevisionExportedURLsCache.computeIfAbsent(serviceName, s -> new LinkedHashMap());
     }
 
     /**
-     * Get all services {@link URL URLs} that the specified {@link ServiceInstance service instance} exported from cache
-     *
-     * @param serviceInstance the {@link ServiceInstance} exports the Dubbo Services
-     * @return the same as {@link #getExportedURLs(ServiceInstance)}
-     */
-    private List<URL> getRevisionExportedURLs(ServiceInstance serviceInstance) {
-
-        if (serviceInstance == null) {
-            return emptyList();
-        }
-
-        String serviceName = serviceInstance.getServiceName();
-        // get the revision from the specified {@link ServiceInstance}
-        String revision = getExportedServicesRevision(serviceInstance);
-
-        return getRevisionExportedURLs(serviceName, revision);
-    }
-
-    private List<URL> getRevisionExportedURLs(String serviceName, String revision) {
-        return executeShared(() -> {
-            Map<String, List<URL>> revisionExportedURLsMap = getRevisionExportedURLsMap(serviceName);
-            List<URL> exportedURLs = revisionExportedURLsMap.get(revision);
-            // Get a copy from source in order to prevent the caller trying to change the cached data
-            return exportedURLs != null ? new ArrayList<>(exportedURLs) : emptyList();
-        });
-    }
-
-    /**
-     * Get all services {@link URL URLs} that the specified {@link ServiceInstance service instance} exported
-     * via the proxy to invoke the {@link MetadataService}
-     *
-     * @param providerServiceInstance the {@link ServiceInstance} exported the Dubbo services
-     * @return The possible result :
-     * <ol>
-     * <li>The normal result</li>
-     * <li>The empty result if the {@link ServiceInstance service instance} did not export yet</li>
-     * <li><code>null</code> if there is an invocation error on {@link MetadataService} proxy</li>
-     * </ol>
-     * @see MetadataServiceProxyFactory
-     * @see MetadataService
-     */
-    private List<URL> getExportedURLs(ServiceInstance providerServiceInstance) {
-
-        List<URL> exportedURLs = null;
-
-        String metadataStorageType = getMetadataStorageType(providerServiceInstance);
-
-        try {
-            MetadataService metadataService = MetadataServiceProxyFactory.getExtension(metadataStorageType)
-                    .getProxy(providerServiceInstance);
-            if (metadataService != null) {
-                SortedSet<String> urls = metadataService.getExportedURLs();
-                exportedURLs = toURLs(urls);
-            }
-        } catch (Throwable e) {
-            if (logger.isErrorEnabled()) {
-                logger.error(format("Failed to get the exported URLs from the target service instance[%s]",
-                        providerServiceInstance), e);
-            }
-            exportedURLs = null; // set the result to be null if failed to get
-        }
-        return exportedURLs;
-    }
-
-    private void executeExclusively(Runnable runnable) {
-        Lock writeLock = lock.writeLock();
-        writeLock.lock();
-        try {
-            runnable.run();
-        } finally {
-            writeLock.unlock();
-        }
-    }
-
-    private <T> T executeShared(Supplier<T> supplier) {
-        Lock readLock = lock.readLock();
-        readLock.lock();
-        try {
-            return supplier.get();
-        } finally {
-            readLock.unlock();
-        }
-    }
-
-    /**
      * Synthesize new subscribed {@link URL URLs} from old one
      *
      * @param subscribedURL
      * @param serviceInstances
      * @return non-null
      */
-    private Collection<? extends URL> synthesizeSubscribedURLs(URL subscribedURL, Collection<ServiceInstance> serviceInstances) {
-        return subscribedURLsSynthesizers.stream()
-                .filter(synthesizer -> synthesizer.supports(subscribedURL))
-                .map(synthesizer -> synthesizer.synthesize(subscribedURL, serviceInstances))
-                .flatMap(Collection::stream)
-                .collect(Collectors.toList());
-    }
+//    private Collection<? extends URL> synthesizeSubscribedURLs(URL subscribedURL, Collection<ServiceInstance> serviceInstances) {
+//        return subscribedURLsSynthesizers.stream()
+//                .filter(synthesizer -> synthesizer.supports(subscribedURL))
+//                .map(synthesizer -> synthesizer.synthesize(subscribedURL, serviceInstances))
+//                .flatMap(Collection::stream)
+//                .collect(Collectors.toList());
+//    }
 
     /**
      * 1.developer explicitly specifies the application name this interface belongs to
@@ -837,11 +352,9 @@ public class ServiceDiscoveryRegistry extends FailbackRegistry {
      *
      * @param subscribedURL
      * @return
-     * @throws IllegalStateException If no service name is not found
      */
-    protected Set<String> getServices(URL subscribedURL) throws IllegalStateException {
-
-        Set<String> subscribedServices = null;
+    protected Set<String> getServices(URL subscribedURL) {
+        Set<String> subscribedServices = new LinkedHashSet<>();
 
         String serviceNames = subscribedURL.getParameter(PROVIDED_BY);
         if (StringUtils.isNotEmpty(serviceNames)) {
@@ -850,21 +363,19 @@ public class ServiceDiscoveryRegistry extends FailbackRegistry {
 
         if (isEmpty(subscribedServices)) {
             subscribedServices = findMappedServices(subscribedURL);
+            if (isEmpty(subscribedServices)) {
+                subscribedServices = getSubscribedServices();
+            }
         }
-
-        if (isEmpty(subscribedServices)) {
-            subscribedServices = getSubscribedServices();
-        }
-
-        if (isEmpty(subscribedServices)) {
-            throw new IllegalStateException("Should has at least one way to know which services this interface belongs to, subscription url: " + subscribedURL);
-        }
-
         return subscribedServices;
     }
 
     public static Set<String> parseServices(String literalServices) {
-        return splitToSet(literalServices, COMMA_SEPARATOR_CHAR, true);
+        return isBlank(literalServices) ? emptySet() :
+                unmodifiableSet(of(literalServices.split(","))
+                        .map(String::trim)
+                        .filter(StringUtils::isNotEmpty)
+                        .collect(toSet()));
     }
 
     /**
@@ -873,20 +384,21 @@ public class ServiceDiscoveryRegistry extends FailbackRegistry {
      * @return non-null
      */
     public Set<String> getSubscribedServices() {
-        if (subscribedServices == null) {
-            subscribedServices = findMappedServices(getUrl());
-        }
         return subscribedServices;
     }
 
     /**
      * Get the mapped services name by the specified {@link URL}
      *
-     * @param url the specified {@link URL}
-     * @return empty {@link Set} if not found
+     * @param subscribedURL
+     * @return
      */
-    protected Set<String> findMappedServices(URL url) {
-        return serviceNameMapping.get(url);
+    protected Set<String> findMappedServices(URL subscribedURL) {
+        String serviceInterface = subscribedURL.getServiceInterface();
+        String group = subscribedURL.getParameter(GROUP_KEY);
+        String version = subscribedURL.getParameter(VERSION_KEY);
+        String protocol = subscribedURL.getParameter(PROTOCOL_KEY, DUBBO_PROTOCOL);
+        return serviceNameMapping.get(serviceInterface, group, version, protocol);
     }
 
     /**
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceInstance.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceInstance.java
index 896af9c..164d146 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceInstance.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceInstance.java
@@ -16,6 +16,8 @@
  */
 package org.apache.dubbo.registry.client;
 
+import org.apache.dubbo.common.URL;
+
 import java.io.Serializable;
 import java.util.Map;
 
@@ -115,4 +117,6 @@ public interface ServiceInstance extends Serializable {
      */
     boolean equals(Object another);
 
+    URL toURL(String protocol, String path, String interfaceName, String group, String version, String serviceKey);
+
 }
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/event/ServiceInstancesChangedEvent.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/event/ServiceInstancesChangedEvent.java
index 4b91265..b206eb7 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/event/ServiceInstancesChangedEvent.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/event/ServiceInstancesChangedEvent.java
@@ -20,9 +20,9 @@ import org.apache.dubbo.event.Event;
 import org.apache.dubbo.registry.client.ServiceInstance;
 import org.apache.dubbo.registry.client.event.listener.ServiceInstancesChangedListener;
 
-import java.util.Collection;
+import java.util.List;
 
-import static java.util.Collections.unmodifiableCollection;
+import static java.util.Collections.unmodifiableList;
 
 /**
  * An event raised after the {@link ServiceInstance instances} of one service has been changed.
@@ -34,17 +34,17 @@ public class ServiceInstancesChangedEvent extends Event {
 
     private final String serviceName;
 
-    private final Collection<ServiceInstance> serviceInstances;
+    private final List<ServiceInstance> serviceInstances;
 
     /**
      * @param serviceName      The name of service that was changed
      * @param serviceInstances all {@link ServiceInstance service instances}
      * @throws IllegalArgumentException if source is null.
      */
-    public ServiceInstancesChangedEvent(String serviceName, Collection<ServiceInstance> serviceInstances) {
+    public ServiceInstancesChangedEvent(String serviceName, List<ServiceInstance> serviceInstances) {
         super(serviceName);
         this.serviceName = serviceName;
-        this.serviceInstances = unmodifiableCollection(serviceInstances);
+        this.serviceInstances = unmodifiableList(serviceInstances);
     }
 
     /**
@@ -58,7 +58,7 @@ public class ServiceInstancesChangedEvent extends Event {
     /**
      * @return all {@link ServiceInstance service instances}
      */
-    public Collection<ServiceInstance> getServiceInstances() {
+    public List<ServiceInstance> getServiceInstances() {
         return serviceInstances;
     }
 
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 1028301..33f0bcc 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,11 +16,30 @@
  */
 package org.apache.dubbo.registry.client.event.listener;
 
+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.ServiceInstance;
 import org.apache.dubbo.registry.client.event.ServiceInstancesChangedEvent;
+import org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils;
 
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
 import java.util.Objects;
+import java.util.Set;
+
+import static org.apache.dubbo.common.constants.CommonConstants.REMOTE_METADATA_STORAGE_TYPE;
+import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.getExportedServicesRevision;
 
 /**
  * The Service Discovery Changed {@link EventListener Event Listener}
@@ -32,6 +51,16 @@ public abstract class ServiceInstancesChangedListener implements ConditionalEven
 
     private final String serviceName;
 
+    private List<ServiceInstance> instances;
+
+    private Map<String, List<ServiceInstance>> revisionToInstances;
+
+    private Map<String, MetadataInfo> revisionToMetadata;
+
+    private Map<String, Set<String>> serviceToRevisions;
+
+    private Map<String, List<ServiceInstance>> serviceToInstances;
+
     protected ServiceInstancesChangedListener(String serviceName) {
         this.serviceName = serviceName;
     }
@@ -41,7 +70,87 @@ public abstract class ServiceInstancesChangedListener implements ConditionalEven
      *
      * @param event {@link ServiceInstancesChangedEvent}
      */
-    public abstract void onEvent(ServiceInstancesChangedEvent event);
+    public void onEvent(ServiceInstancesChangedEvent event) {
+        instances = event.getServiceInstances();
+
+        Map<String, List<ServiceInstance>> localRevisionToInstances = new HashMap<>();
+        Map<String, MetadataInfo> localRevisionToMetadata = new HashMap<>();
+        Map<String, Set<String>> localServiceToRevisions = new HashMap<>();
+        for (ServiceInstance instance : instances) {
+            String revision = getExportedServicesRevision(instance);
+            Collection<ServiceInstance> rInstances = localRevisionToInstances.computeIfAbsent(revision, r -> new ArrayList<>());
+            rInstances.add(instance);
+
+            MetadataInfo metadata = revisionToMetadata.get(revision);
+            if (metadata != null) {
+                localRevisionToMetadata.put(revision, metadata);
+            } else {
+                metadata = getMetadataInfo(instance);
+                localRevisionToMetadata.put(revision, getMetadataInfo(instance));
+            }
+            parse(revision, metadata, localServiceToRevisions);
+        }
+
+        this.revisionToInstances = localRevisionToInstances;
+        this.revisionToMetadata = localRevisionToMetadata;
+        this.serviceToRevisions = localServiceToRevisions;
+
+        Map<String, List<ServiceInstance>> localServiceToInstances = new HashMap<>();
+        for (String serviceKey : localServiceToRevisions.keySet()) {
+            if (CollectionUtils.equals(localRevisionToInstances.keySet(), localServiceToRevisions.get(serviceKey))) {
+                localServiceToInstances.put(serviceKey, instances);
+            }
+        }
+
+        this.serviceToInstances = localServiceToInstances;
+    }
+
+    public List<ServiceInstance> getInstances(String serviceKey) {
+        if (serviceToInstances.containsKey(serviceKey)) {
+            return serviceToInstances.get(serviceKey);
+        }
+
+        Set<String> revisions = serviceToRevisions.get(serviceKey);
+        List<ServiceInstance> allInstances = new LinkedList<>();
+        for (String r : revisions) {
+            allInstances.addAll(revisionToInstances.get(r));
+        }
+        return allInstances;
+    }
+
+    private Map<String, Set<String>> parse(String revision, MetadataInfo metadata, Map<String, Set<String>> localServiceToRevisions) {
+        Map<String, ServiceInfo> serviceInfos = metadata.getServices();
+        for (Map.Entry<String, ServiceInfo> serviceInfo : serviceInfos.entrySet()) {
+            String serviceKey = serviceInfo.getValue().getServiceKey();
+            Set<String> set = localServiceToRevisions.computeIfAbsent(serviceKey, k -> new HashSet<>());
+            set.add(revision);
+        }
+
+        return localServiceToRevisions;
+    }
+
+    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);
+            } else {
+                MetadataService localMetadataService = MetadataUtils.getLocalMetadataService();
+                metadataInfo = localMetadataService.getMetadataInfo();
+            }
+        } catch (Exception e) {
+            // TODO, load metadata backup
+            metadataInfo = null;
+        }
+        return metadataInfo;
+    }
+
+
+    protected abstract void notifyAddresses();
 
     /**
      * Get the correlative service name
@@ -57,7 +166,7 @@ public abstract class ServiceInstancesChangedListener implements ConditionalEven
      * @return If service name matches, return <code>true</code>, or <code>false</code>
      */
     public final boolean accept(ServiceInstancesChangedEvent event) {
-        return Objects.equals(getServiceName(), event.getServiceName());
+        return Objects.equals(serviceName, event.getServiceName());
     }
 
     @Override
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 01bde1d..16f5446 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
@@ -31,6 +31,8 @@ import org.apache.dubbo.common.utils.UrlUtils;
 import org.apache.dubbo.registry.AddressListener;
 import org.apache.dubbo.registry.NotifyListener;
 import org.apache.dubbo.registry.Registry;
+import org.apache.dubbo.registry.client.ServiceInstance;
+import org.apache.dubbo.registry.client.event.listener.ServiceInstancesChangedListener;
 import org.apache.dubbo.remoting.Constants;
 import org.apache.dubbo.rpc.Invocation;
 import org.apache.dubbo.rpc.Invoker;
@@ -53,6 +55,7 @@ import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
@@ -68,6 +71,7 @@ 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.MONITOR_KEY;
 import static org.apache.dubbo.common.constants.CommonConstants.PROTOCOL_KEY;
+import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY;
 import static org.apache.dubbo.common.constants.RegistryConstants.APP_DYNAMIC_CONFIGURATORS_CATEGORY;
 import static org.apache.dubbo.common.constants.RegistryConstants.CATEGORY_KEY;
 import static org.apache.dubbo.common.constants.RegistryConstants.COMPATIBLE_CONFIG_KEY;
@@ -133,6 +137,7 @@ public class RegistryDirectory<T> extends AbstractDirectory<T> implements Notify
     private static final ConsumerConfigurationListener CONSUMER_CONFIGURATION_LISTENER = new ConsumerConfigurationListener();
     private ReferenceConfigurationListener serviceConfigurationListener;
 
+    private Set<ServiceInstancesChangedListener> serviceListeners;
 
     public RegistryDirectory(Class<T> serviceType, URL url) {
         super(url);
@@ -151,6 +156,34 @@ public class RegistryDirectory<T> extends AbstractDirectory<T> implements Notify
         this.overrideDirectoryUrl = this.directoryUrl = turnRegistryUrlToConsumerUrl(url);
         String group = directoryUrl.getParameter(GROUP_KEY, "");
         this.multiGroup = group != null && (ANY_VALUE.equals(group) || group.contains(","));
+
+        this.serviceListeners = new HashSet<>();
+    }
+
+    @Override
+    public void addServiceListener(ServiceInstancesChangedListener instanceListener) {
+        this.serviceListeners.add(instanceListener);
+    }
+
+    @Override
+    public void notifyServiceInstances() {
+        List<URL> urls = new LinkedList<>();
+        for (ServiceInstancesChangedListener listener : serviceListeners) {
+            List<ServiceInstance> instances = listener.getInstances(serviceKey);
+            for (ServiceInstance instance : instances) {
+                // FIXME, the right protocol? the right path?
+                urls.add(
+                        instance.toURL(
+                                "dubbo",
+                                serviceType.getName(),
+                                serviceType.getName(),
+                                queryMap.get(GROUP_KEY),
+                                queryMap.get(VERSION_KEY),
+                                serviceKey)
+                );
+            }
+        }
+        notify(urls);
     }
 
     private URL turnRegistryUrlToConsumerUrl(URL url) {


[dubbo] 04/27: metadata read & write

Posted by li...@apache.org.
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

commit c682389634fed5f6b13bb6de0ab88e5757a7fe44
Author: ken.lj <ke...@gmail.com>
AuthorDate: Mon Jun 15 13:41:07 2020 +0800

    metadata read & write
---
 .../dubbo/config/bootstrap/DubboBootstrap.java     |   4 +-
 .../metadata/DefaultMetadataParamsFilter.java      |   3 +
 .../org/apache/dubbo/metadata/MetadataInfo.java    |  47 +++--
 .../apache/dubbo/metadata/RevisionResolver.java    |   2 +
 .../dubbo/metadata/WritableMetadataService.java    |   6 +-
 .../org/apache/dubbo/metadata/DemoService.java     |  30 ++-
 .../apache/dubbo/metadata/MetadataInfoTest.java    |  27 ++-
 .../dubbo/metadata/URLRevisionResolverTest.java    |  50 -----
 .../store/InMemoryWritableMetadataServiceTest.java | 111 -----------
 .../RemoteWritableMetadataServiceDelegateTest.java | 216 ---------------------
 .../store/RemoteWritableMetadataServiceTest.java   | 188 ------------------
 .../registry/client/ServiceDiscoveryRegistry.java  |   3 +-
 ...MetadataServiceURLParamsMetadataCustomizer.java |   8 +-
 .../registry/client/metadata/MetadataUtils.java    |  16 +-
 .../metadata/ProtocolPortsMetadataCustomizer.java  |   7 +-
 .../metadata/store/RemoteMetadataServiceImpl.java  |  10 +-
 ...g.apache.dubbo.metadata.WritableMetadataService |   1 +
 ...ient.metadata.proxy.MetadataServiceProxyFactory |   3 -
 .../registry/nacos/NacosServiceDiscovery.java      |   3 +-
 19 files changed, 84 insertions(+), 651 deletions(-)

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 3bdffa9..dd28b81 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
@@ -97,7 +97,7 @@ import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_METADATA
 import static org.apache.dubbo.common.constants.CommonConstants.REMOTE_METADATA_STORAGE_TYPE;
 import static org.apache.dubbo.common.function.ThrowableAction.execute;
 import static org.apache.dubbo.common.utils.StringUtils.isNotEmpty;
-import static org.apache.dubbo.metadata.WritableMetadataService.getExtension;
+import static org.apache.dubbo.metadata.WritableMetadataService.getDefaultExtension;
 import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.setMetadataStorageType;
 import static org.apache.dubbo.remoting.Constants.CLIENT_KEY;
 
@@ -718,7 +718,7 @@ public class DubboBootstrap extends GenericEventListener {
      */
     private void initMetadataService() {
         startMetadataReport();
-        this.metadataService = getExtension(getMetadataType());
+        this.metadataService = getDefaultExtension();
         this.metadataServiceExporter = new ConfigurableMetadataServiceExporter(metadataService);
     }
 
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
index 7f4b85c..0563e43 100644
--- 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
@@ -16,6 +16,8 @@
  */
 package org.apache.dubbo.metadata;
 
+import org.apache.dubbo.common.extension.Activate;
+
 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;
@@ -35,6 +37,7 @@ 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;
 
+@Activate
 public class DefaultMetadataParamsFilter implements MetadataParamsFilter {
     @Override
     public String[] include() {
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 bf9aa68..01e3c09 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,15 +17,19 @@
 package org.apache.dubbo.metadata;
 
 import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.compiler.support.ClassUtils;
 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.lang.reflect.Method;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.SortedSet;
+import java.util.TreeSet;
 
 import static org.apache.dubbo.common.constants.CommonConstants.DOT_SEPARATOR;
 import static org.apache.dubbo.common.constants.CommonConstants.GROUP_CHAR_SEPERATOR;
@@ -39,6 +43,7 @@ public class MetadataInfo implements Serializable {
     private Map<String, ServiceInfo> services;
 
     public MetadataInfo() {
+        this.services = new HashMap<>();
     }
 
     public MetadataInfo(String app, String revision, Map<String, ServiceInfo> services) {
@@ -68,15 +73,6 @@ public class MetadataInfo implements Serializable {
         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;
     }
@@ -86,6 +82,15 @@ public class MetadataInfo implements Serializable {
     }
 
     public String getRevision() {
+        if (revision != null) {
+            return revision;
+        }
+        StringBuilder sb = new StringBuilder();
+        sb.append(app);
+        for (Map.Entry<String, ServiceInfo> entry : services.entrySet()) {
+            sb.append(entry.getValue().toDescString());
+        }
+        this.revision = RevisionResolver.calRevision(sb.toString());
         return revision;
     }
 
@@ -159,10 +164,12 @@ public class MetadataInfo implements Serializable {
                             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);
+                        if (methods != null) {
+                            for (String method : methods) {
+                                String mValue = url.getMethodParameter(method, p);
+                                if (StringUtils.isNotEmpty(mValue)) {
+                                    params.put(method + DOT_SEPARATOR + p, mValue);
+                                }
                             }
                         }
                     }
@@ -277,5 +284,19 @@ public class MetadataInfo implements Serializable {
             }
             return value == null ? defaultValue : value;
         }
+
+        public String toDescString() {
+            return this.getMatchKey() + getMethodSignaturesString() + getParams();
+        }
+
+        private String getMethodSignaturesString() {
+            SortedSet<String> methodStrings = new TreeSet();
+
+            Method[] methods = ClassUtils.forName(name).getMethods();
+            for (Method method : methods) {
+                methodStrings.add(method.toString());
+            }
+            return methodStrings.toString();
+        }
     }
 }
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
index 76d8cfb..f8ebd3b 100644
--- 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
@@ -54,4 +54,6 @@ public class RevisionResolver {
         }
         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 b453842..0de9683 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,6 +18,7 @@ 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.rpc.model.ApplicationModel;
 
 import static org.apache.dubbo.common.extension.ExtensionLoader.getExtensionLoader;
@@ -28,6 +29,7 @@ import static org.apache.dubbo.common.extension.ExtensionLoader.getExtensionLoad
  *
  * @since 2.7.5
  */
+@SPI("default")
 public interface WritableMetadataService extends MetadataService {
     /**
      * Gets the current Dubbo Service name
@@ -81,8 +83,4 @@ public interface WritableMetadataService extends MetadataService {
     static WritableMetadataService getDefaultExtension() {
         return getExtensionLoader(WritableMetadataService.class).getDefaultExtension();
     }
-
-    static WritableMetadataService getExtension(String name) {
-        return getExtensionLoader(WritableMetadataService.class).getOrDefaultExtension(name);
-    }
 }
diff --git a/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/event/listener/CustomizableServiceInstanceListenerTest.java b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/DemoService.java
similarity index 53%
copy from dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/event/listener/CustomizableServiceInstanceListenerTest.java
copy to dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/DemoService.java
index bf8d9d2..33bcbc7 100644
--- a/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/event/listener/CustomizableServiceInstanceListenerTest.java
+++ b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/DemoService.java
@@ -14,26 +14,24 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.registry.client.event.listener;
+package org.apache.dubbo.metadata;
 
-import org.apache.dubbo.registry.client.DefaultServiceInstanceTest;
-import org.apache.dubbo.registry.client.event.ServiceInstancePreRegisteredEvent;
+import org.apache.dubbo.metadata.rest.User;
+
+import java.util.List;
 
-import org.junit.jupiter.api.Test;
 
 /**
- * {@link CustomizableServiceInstanceListener} Test
- *
- * @since 2.7.5
+ * DemoService
  */
-public class CustomizableServiceInstanceListenerTest {
+public interface DemoService {
+
+    String sayName(String name);
+
+    void throwRuntimeException() throws RuntimeException;
+
+    List<User> getUsers(List<User> users);
 
-    private CustomizableServiceInstanceListener listener = new CustomizableServiceInstanceListener();
+    int echo(int i);
 
-    @Test
-    public void testOnEvent() {
-        ServiceInstancePreRegisteredEvent event = new ServiceInstancePreRegisteredEvent(this, DefaultServiceInstanceTest.createInstance());
-        // breaking test
-        listener.onEvent(event);
-    }
-}
+}
\ No newline at end of file
diff --git a/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/event/listener/CustomizableServiceInstanceListenerTest.java b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/MetadataInfoTest.java
similarity index 56%
rename from dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/event/listener/CustomizableServiceInstanceListenerTest.java
rename to dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/MetadataInfoTest.java
index bf8d9d2..1d2f42e 100644
--- a/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/event/listener/CustomizableServiceInstanceListenerTest.java
+++ b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/MetadataInfoTest.java
@@ -14,26 +14,23 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.registry.client.event.listener;
+package org.apache.dubbo.metadata;
 
-import org.apache.dubbo.registry.client.DefaultServiceInstanceTest;
-import org.apache.dubbo.registry.client.event.ServiceInstancePreRegisteredEvent;
+import org.apache.dubbo.common.URL;
 
 import org.junit.jupiter.api.Test;
 
-/**
- * {@link CustomizableServiceInstanceListener} Test
- *
- * @since 2.7.5
- */
-public class CustomizableServiceInstanceListenerTest {
+public class MetadataInfoTest {
+    @Test
+    public void revisionTest() {
+        MetadataInfo metadataInfo = new MetadataInfo();
+        metadataInfo.setApp("demo");
 
-    private CustomizableServiceInstanceListener listener = new CustomizableServiceInstanceListener();
+        URL url = URL.valueOf("dubbo://10.230.11.211:20880/org.apache.dubbo.metadata.DemoService?timeout=1000&testKey=aaa");
+        MetadataInfo.ServiceInfo serviceInfo = new MetadataInfo.ServiceInfo(url);
+        metadataInfo.addService(serviceInfo);
 
-    @Test
-    public void testOnEvent() {
-        ServiceInstancePreRegisteredEvent event = new ServiceInstancePreRegisteredEvent(this, DefaultServiceInstanceTest.createInstance());
-        // breaking test
-        listener.onEvent(event);
+        System.out.println(serviceInfo.toDescString());
+        System.out.println(metadataInfo.getRevision());
     }
 }
diff --git a/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/URLRevisionResolverTest.java b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/URLRevisionResolverTest.java
deleted file mode 100644
index b36ebcb..0000000
--- a/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/URLRevisionResolverTest.java
+++ /dev/null
@@ -1,50 +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.junit.jupiter.api.Test;
-
-import java.util.Arrays;
-import java.util.Collection;
-
-import static java.util.Arrays.asList;
-import static org.apache.dubbo.metadata.URLRevisionResolver.UNKNOWN_REVISION;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-
-/**
- * {@link URLRevisionResolver} Test
- *
- * @since 2.7.5
- */
-public class URLRevisionResolverTest {
-
-    private static final String URL = "dubbo://192.168.0.102:20881/org.apache.dubbo.metadata.URLRevisionResolverTest";
-
-    private final URLRevisionResolver resolver = URLRevisionResolver.INSTANCE;
-
-    @Test
-    public void testResolve() {
-        String revision = resolver.resolve(Arrays.<String>asList());
-        assertEquals(UNKNOWN_REVISION, revision);
-
-        revision = resolver.resolve((Collection<String>) null);
-        assertEquals(UNKNOWN_REVISION, revision);
-
-        revision = resolver.resolve(asList(URL));
-        assertEquals("2ca0638f189ce569", revision);
-    }
-}
diff --git a/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/store/InMemoryWritableMetadataServiceTest.java b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/store/InMemoryWritableMetadataServiceTest.java
deleted file mode 100644
index b7db3fb..0000000
--- a/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/store/InMemoryWritableMetadataServiceTest.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.
- */
-
-/*
- * 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.utils.NetUtils;
-
-import org.junit.jupiter.api.Assertions;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-
-/**
- * 2019-08-29
- */
-public class InMemoryWritableMetadataServiceTest {
-
-    String interfaceName = "org.apache.dubbo.metadata.store.InterfaceNameTestService2", version = "0.9.9", group = null;
-    URL url = URL.valueOf("dubbo://" + NetUtils.getLocalAddress().getHostName() + ":4444/?interface=" + interfaceName + "&version="
-            + version + "&application=vicpubprovder&side=provider");
-
-    @BeforeEach
-    public void before() {
-    }
-
-    @Test
-    public void testPublishServiceDefinition() {
-        InMemoryWritableMetadataService inMemoryWritableMetadataService = new InMemoryWritableMetadataService();
-        inMemoryWritableMetadataService.publishServiceDefinition(url);
-
-        String v = inMemoryWritableMetadataService.getServiceDefinition(interfaceName, version, group);
-        Assertions.assertNotNull(v);
-    }
-
-    @Test
-    public void testExportURL() {
-        InMemoryWritableMetadataService inMemoryWritableMetadataService = new InMemoryWritableMetadataService();
-        URL url = URL.valueOf("dubbo://" + NetUtils.getLocalAddress().getHostName() + ":4444/org.apache.dubbo.Test567Service?version=1.0.44&application=vicpubprovder&side=provider");
-        inMemoryWritableMetadataService.exportURL(url);
-
-        Assertions.assertTrue(inMemoryWritableMetadataService.getExportedServiceURLs().size() == 1);
-        Assertions.assertEquals(inMemoryWritableMetadataService.getExportedServiceURLs().get(url.getServiceKey()).first(), url);
-    }
-
-    @Test
-    public void testSubscribeURL() {
-        InMemoryWritableMetadataService inMemoryWritableMetadataService = new InMemoryWritableMetadataService();
-        URL url = URL.valueOf("subscriber://" + NetUtils.getLocalAddress().getHostName() + ":4444/org.apache.dubbo.Test678Service?version=1.0.44&application=vicpubprovder&side=provider");
-        inMemoryWritableMetadataService.subscribeURL(url);
-
-        Assertions.assertTrue(inMemoryWritableMetadataService.getSubscribedServiceURLs().size() == 1);
-        Assertions.assertEquals(inMemoryWritableMetadataService.getSubscribedServiceURLs().get(url.getServiceKey()).first(), url);
-    }
-
-    @Test
-    public void testUnExportURL() {
-        InMemoryWritableMetadataService inMemoryWritableMetadataService = new InMemoryWritableMetadataService();
-        URL url = URL.valueOf("dubbo://" + NetUtils.getLocalAddress().getHostName() + ":4444/org.apache.dubbo.Test567Service?version=1.0.44&application=vicpubprovder&side=provider");
-        inMemoryWritableMetadataService.exportURL(url);
-
-        Assertions.assertTrue(inMemoryWritableMetadataService.getExportedServiceURLs().size() == 1);
-        Assertions.assertEquals(inMemoryWritableMetadataService.getExportedServiceURLs().get(url.getServiceKey()).first(), url);
-
-        inMemoryWritableMetadataService.unexportURL(url);
-        Assertions.assertTrue(inMemoryWritableMetadataService.getExportedServiceURLs().size() == 0);
-    }
-
-    @Test
-    public void testUnSubscribeURL() {
-        InMemoryWritableMetadataService inMemoryWritableMetadataService = new InMemoryWritableMetadataService();
-        URL url = URL.valueOf("subscriber://" + NetUtils.getLocalAddress().getHostName() + ":4444/org.apache.dubbo.Test678Service?version=1.0.44&application=vicpubprovder&side=provider");
-        inMemoryWritableMetadataService.subscribeURL(url);
-
-        Assertions.assertTrue(inMemoryWritableMetadataService.getSubscribedServiceURLs().size() == 1);
-        Assertions.assertEquals(inMemoryWritableMetadataService.getSubscribedServiceURLs().get(url.getServiceKey()).first(), url);
-
-        inMemoryWritableMetadataService.unsubscribeURL(url);
-        Assertions.assertTrue(inMemoryWritableMetadataService.getSubscribedServiceURLs().size() == 0);
-    }
-
-}
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
deleted file mode 100644
index 7c95e31..0000000
--- a/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/store/RemoteWritableMetadataServiceDelegateTest.java
+++ /dev/null
@@ -1,216 +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.
- */
-
-/*
- * 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.utils.NetUtils;
-import org.apache.dubbo.metadata.WritableMetadataService;
-import org.apache.dubbo.metadata.report.MetadataReportInstance;
-import org.apache.dubbo.metadata.report.identifier.KeyTypeEnum;
-import org.apache.dubbo.metadata.report.identifier.ServiceMetadataIdentifier;
-import org.apache.dubbo.metadata.report.identifier.SubscriberMetadataIdentifier;
-import org.apache.dubbo.metadata.test.JTestMetadataReport4Test;
-import org.apache.dubbo.rpc.model.ApplicationModel;
-
-import org.junit.jupiter.api.Assertions;
-import org.junit.jupiter.api.BeforeAll;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-
-import java.util.SortedSet;
-
-
-/**
- * 2019-08-27
- */
-public class RemoteWritableMetadataServiceDelegateTest {
-    static URL metadataURL = URL.valueOf("JTest://" + NetUtils.getLocalAddress().getHostName() + ":4444/org.apache.dubbo.Tes33tService?version=1.0.0&application=vic");
-
-    RemoteWritableMetadataServiceDelegate metadataReportService;
-
-    String interfaceName = "org.apache.dubbo.metadata.store.InterfaceNameTestService80", version = "0.6.9", group = null;
-    URL url = URL.valueOf("dubbo://" + NetUtils.getLocalAddress().getHostName() + ":4444/?interface=" + interfaceName + "&version="
-            + version + "&application=vicpubprovder&side=provider");
-
-    @BeforeAll
-    public static void beforeAll() {
-        MetadataReportInstance.init(metadataURL);
-    }
-
-    @BeforeEach
-    public void before() {
-        metadataReportService = new RemoteWritableMetadataServiceDelegate();
-    }
-
-
-    @Test
-    public void testInstance() {
-        WritableMetadataService metadataReportService1 = WritableMetadataService.getExtension("remote");
-        WritableMetadataService metadataReportService2 = WritableMetadataService.getExtension("remote");
-        Assertions.assertSame(metadataReportService1, metadataReportService2);
-        Assertions.assertTrue(metadataReportService1 instanceof RemoteWritableMetadataServiceDelegate);
-    }
-
-    @Test
-    public void testPublishServiceDefinition() throws InterruptedException {
-        String interfaceName = "org.apache.dubbo.metadata.store.InterfaceNameTestService2", version = "0.9.9", group = null;
-        URL tmpUrl = URL.valueOf("dubbo://" + NetUtils.getLocalAddress().getHostName() + ":4444/?interface=" + interfaceName + "&version="
-                + version + "&application=vicpubprovder&side=provider");
-        metadataReportService.publishServiceDefinition(tmpUrl);
-        Thread.sleep(150);
-        String v = metadataReportService.getServiceDefinition(interfaceName, version, group);
-        Assertions.assertNotNull(v);
-    }
-
-    @Test
-    public void testExportURL() throws InterruptedException {
-        URL url = URL.valueOf("dubbo://" + NetUtils.getLocalAddress().getHostName() + ":4444/org.apache.dubbo.Test567Service?version=1.0.44&application=vicpubprovder&side=provider");
-        metadataReportService.exportURL(url);
-        Thread.sleep(100);
-        Assertions.assertTrue(getInMemoryWriableMetadataService().exportedServiceURLs.size() == 1);
-        Assertions.assertEquals(getInMemoryWriableMetadataService().exportedServiceURLs.get(url.getServiceKey()).first(), url);
-    }
-
-    @Test
-    public void testSubscribeURL() throws InterruptedException {
-        URL url = URL.valueOf("subscriber://" + NetUtils.getLocalAddress().getHostName() + ":4444/org.apache.dubbo.Test0678Service?version=1.3.144&application=vicpubprovder&side=provider");
-        int origSize = getInMemoryWriableMetadataService().subscribedServiceURLs.size();
-        metadataReportService.subscribeURL(url);
-        Thread.sleep(100);
-        int size = getInMemoryWriableMetadataService().subscribedServiceURLs.size();
-        Assertions.assertTrue(size - origSize == 1);
-        Assertions.assertEquals(getInMemoryWriableMetadataService().subscribedServiceURLs.get(url.getServiceKey()).first(), url);
-    }
-
-    @Test
-    public void testUnExportURL() throws InterruptedException {
-        URL url = URL.valueOf("dubbo://" + NetUtils.getLocalAddress().getHostName() + ":4444/org.apache.dubbo.Test0567Service?version=1.2.44&application=vicpubprovder&side=provider");
-        int origSize = getInMemoryWriableMetadataService().exportedServiceURLs.size();
-        metadataReportService.exportURL(url);
-        Thread.sleep(100);
-        int size = getInMemoryWriableMetadataService().exportedServiceURLs.size();
-        Assertions.assertTrue(size - origSize == 1);
-        Assertions.assertEquals(getInMemoryWriableMetadataService().exportedServiceURLs.get(url.getServiceKey()).first(), url);
-
-        metadataReportService.unexportURL(url);
-        int unexportSize = getInMemoryWriableMetadataService().exportedServiceURLs.size();
-        Assertions.assertTrue(size - unexportSize == 1);
-    }
-
-    @Test
-    public void testUnSubscribeURL() throws InterruptedException {
-        URL url = URL.valueOf("subscriber://" + NetUtils.getLocalAddress().getHostName() + ":4444/org.apache.dubbo.Test0678Service?version=1.5.477&application=vicpubprovder&side=provider");
-        int origSize = getInMemoryWriableMetadataService().subscribedServiceURLs.size();
-        metadataReportService.subscribeURL(url);
-        Thread.sleep(100);
-        int size = getInMemoryWriableMetadataService().subscribedServiceURLs.size();
-        Assertions.assertTrue(size - origSize == 1);
-        Assertions.assertEquals(getInMemoryWriableMetadataService().subscribedServiceURLs.get(url.getServiceKey()).first(), url);
-
-        metadataReportService.unsubscribeURL(url);
-        Thread.sleep(100);
-        Assertions.assertTrue(getInMemoryWriableMetadataService().subscribedServiceURLs.size() == 0);
-    }
-
-    @Test
-    public void testRefreshMetadataService() throws InterruptedException {
-        URL publishUrl = URL.valueOf("dubbo://" + NetUtils.getLocalAddress().getHostName() + ":4444/org.apache.dubbo.TestRefreshMetadataService?version=1.6.8&application=vicpubprovder&side=provider");
-        URL publishUrl2 = URL.valueOf("dubbo://" + NetUtils.getLocalAddress().getHostName() + ":4444/org.apache.dubbo.TestRefreshMetadata2Service?version=1.6.5&application=vicpubprovder&side=provider");
-        metadataReportService.exportURL(publishUrl);
-        metadataReportService.exportURL(publishUrl2);
-        String exportedRevision = "9999";
-        JTestMetadataReport4Test jTestMetadataReport4Test = (JTestMetadataReport4Test) MetadataReportInstance.getMetadataReports(true);
-        int origSize = jTestMetadataReport4Test.store.size();
-        int num = countNum();
-        Assertions.assertTrue(metadataReportService.refreshMetadata(exportedRevision, "1109"));
-        Thread.sleep(200);
-        int size = jTestMetadataReport4Test.store.size();
-        Assertions.assertTrue(size - origSize == num);
-        Assertions.assertEquals(jTestMetadataReport4Test.store.get(getServiceMetadataIdentifier(publishUrl, exportedRevision).getUniqueKey(KeyTypeEnum.UNIQUE_KEY)), publishUrl.toFullString());
-        Assertions.assertEquals(jTestMetadataReport4Test.store.get(getServiceMetadataIdentifier(publishUrl2, exportedRevision).getUniqueKey(KeyTypeEnum.UNIQUE_KEY)), publishUrl2.toFullString());
-    }
-
-
-    // unstable test
-//    @Test
-//    public void testRefreshMetadataSubscription() throws InterruptedException {
-//        URL subscriberUrl1 = URL.valueOf("subscriber://" + NetUtils.getLocalAddress().getHostName() + ":4444/org.apache.dubbo.TestRefreshMetadata00Service?version=2.0.8&application=vicpubprovder&side=provider");
-//        URL subscriberUrl2 = URL.valueOf("subscriber://" + NetUtils.getLocalAddress().getHostName() + ":4444/org.apache.dubbo.TestRefreshMetadata09Service?version=2.0.5&application=vicpubprovder&side=provider");
-//        metadataReportService.subscribeURL(subscriberUrl1);
-//        metadataReportService.subscribeURL(subscriberUrl2);
-//        String exportedRevision = "9999";
-//        String subscriberRevision = "2099";
-//        String applicationName = "wriableMetadataService";
-//        JTestMetadataReport4Test jTestMetadataReport4Test = (JTestMetadataReport4Test) MetadataReportInstance.getMetadataReport(true);
-//        int origSize = jTestMetadataReport4Test.store.size();
-//        ApplicationModel.setApplication(applicationName);
-//        Assertions.assertTrue(metadataReportService.refreshMetadata(exportedRevision, subscriberRevision));
-//        Thread.sleep(200);
-//        int size = jTestMetadataReport4Test.store.size();
-//        Assertions.assertTrue(size - origSize == 1);
-//        String r = jTestMetadataReport4Test.store.get(getSubscriberMetadataIdentifier(
-//                subscriberRevision).getUniqueKey(KeyTypeEnum.UNIQUE_KEY));
-//        Assertions.assertNotNull(r);
-//    }
-
-
-    private ServiceMetadataIdentifier getServiceMetadataIdentifier(URL publishUrl, String exportedRevision) {
-        ServiceMetadataIdentifier serviceMetadataIdentifier = new ServiceMetadataIdentifier(publishUrl);
-        serviceMetadataIdentifier.setRevision(exportedRevision);
-        serviceMetadataIdentifier.setProtocol(publishUrl.getProtocol());
-        return serviceMetadataIdentifier;
-    }
-
-    private SubscriberMetadataIdentifier getSubscriberMetadataIdentifier(String subscriberRevision) {
-        SubscriberMetadataIdentifier subscriberMetadataIdentifier = new SubscriberMetadataIdentifier();
-        subscriberMetadataIdentifier.setRevision(subscriberRevision);
-        subscriberMetadataIdentifier.setApplication(ApplicationModel.getApplication());
-        return subscriberMetadataIdentifier;
-    }
-
-    private InMemoryWritableMetadataService getInMemoryWriableMetadataService() {
-        return (InMemoryWritableMetadataService) metadataReportService.defaultWritableMetadataService;
-    }
-
-    private int countNum() {
-        int num = 0;
-        for (SortedSet<URL> tmp : getInMemoryWriableMetadataService().exportedServiceURLs.values()) {
-            num += tmp.size();
-        }
-        if (!getInMemoryWriableMetadataService().subscribedServiceURLs.values().isEmpty()) {
-            num++;
-        }
-        return num;
-    }
-}
diff --git a/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/store/RemoteWritableMetadataServiceTest.java b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/store/RemoteWritableMetadataServiceTest.java
deleted file mode 100644
index e9ca89f..0000000
--- a/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/store/RemoteWritableMetadataServiceTest.java
+++ /dev/null
@@ -1,188 +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.utils.NetUtils;
-import org.apache.dubbo.metadata.definition.model.FullServiceDefinition;
-import org.apache.dubbo.metadata.report.MetadataReportInstance;
-import org.apache.dubbo.metadata.report.identifier.KeyTypeEnum;
-import org.apache.dubbo.metadata.report.identifier.ServiceMetadataIdentifier;
-import org.apache.dubbo.metadata.report.identifier.SubscriberMetadataIdentifier;
-import org.apache.dubbo.metadata.test.JTestMetadataReport4Test;
-import org.apache.dubbo.rpc.model.ApplicationModel;
-
-import com.google.gson.Gson;
-import org.junit.jupiter.api.Assertions;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-
-import java.util.Map;
-
-/**
- * 2018/9/14
- */
-public class RemoteWritableMetadataServiceTest {
-    URL url = URL.valueOf("JTest://" + NetUtils.getLocalAddress().getHostName() + ":4444/org.apache.dubbo.TestService?version=1.0.0&application=vic");
-    RemoteWritableMetadataService metadataReportService1;
-
-    @BeforeEach
-    public void before() {
-        metadataReportService1 = new RemoteWritableMetadataService();
-        MetadataReportInstance.init(url);
-    }
-
-    @Test
-    public void testPublishProviderNoInterfaceName() {
-        URL publishUrl = URL.valueOf("dubbo://" + NetUtils.getLocalAddress().getHostName() + ":4444/org.apache.dubbo.TestService?version=1.0.0&application=vicpubprovder&side=provider");
-        metadataReportService1.publishServiceDefinition(publishUrl);
-
-        Assertions.assertTrue(metadataReportService1.getMetadataReport() instanceof JTestMetadataReport4Test);
-
-        JTestMetadataReport4Test jTestMetadataReport4Test = (JTestMetadataReport4Test) metadataReportService1.getMetadataReport();
-        Assertions.assertTrue(!jTestMetadataReport4Test.store.containsKey(JTestMetadataReport4Test.getProviderKey(publishUrl)));
-
-    }
-
-    @Test
-    public void testPublishProviderWrongInterface() {
-
-        URL publishUrl = URL.valueOf("dubbo://" + NetUtils.getLocalAddress().getHostName() + ":4444/org.apache.dubbo.TestService?version=1.0.0&application=vicpu&interface=ccc&side=provider");
-        metadataReportService1.publishServiceDefinition(publishUrl);
-
-        Assertions.assertTrue(metadataReportService1.getMetadataReport() instanceof JTestMetadataReport4Test);
-
-        JTestMetadataReport4Test jTestMetadataReport4Test = (JTestMetadataReport4Test) metadataReportService1.getMetadataReport();
-        Assertions.assertTrue(!jTestMetadataReport4Test.store.containsKey(JTestMetadataReport4Test.getProviderKey(publishUrl)));
-
-    }
-
-    @Test
-    public void testPublishProviderContainInterface() throws InterruptedException {
-
-        URL publishUrl = URL.valueOf("dubbo://" + NetUtils.getLocalAddress().getHostName() + ":4444/org.apache.dubbo.TestService?version=1.0.3&application=vicpubp&interface=org.apache.dubbo.metadata.store.InterfaceNameTestService&side=provider");
-        metadataReportService1.publishServiceDefinition(publishUrl);
-        Thread.sleep(300);
-
-        Assertions.assertTrue(metadataReportService1.getMetadataReport() instanceof JTestMetadataReport4Test);
-
-        JTestMetadataReport4Test jTestMetadataReport4Test = (JTestMetadataReport4Test) metadataReportService1.getMetadataReport();
-        Assertions.assertTrue(jTestMetadataReport4Test.store.containsKey(JTestMetadataReport4Test.getProviderKey(publishUrl)));
-
-        String value = jTestMetadataReport4Test.store.get(JTestMetadataReport4Test.getProviderKey(publishUrl));
-        FullServiceDefinition fullServiceDefinition = toServiceDefinition(value);
-        Map<String, String> map = fullServiceDefinition.getParameters();
-        Assertions.assertEquals(map.get("application"), "vicpubp");
-        Assertions.assertEquals(map.get("version"), "1.0.3");
-        Assertions.assertEquals(map.get("interface"), "org.apache.dubbo.metadata.store.InterfaceNameTestService");
-    }
-
-    @Test
-    public void testPublishConsumer() throws InterruptedException {
-
-        URL publishUrl = URL.valueOf("dubbo://" + NetUtils.getLocalAddress().getHostName() + ":4444/org.apache.dubbo.TestService?version=1.0.x&application=vicpubconsumer&side=consumer");
-        metadataReportService1.publishServiceDefinition(publishUrl);
-        Thread.sleep(300);
-
-        Assertions.assertTrue(metadataReportService1.getMetadataReport() instanceof JTestMetadataReport4Test);
-
-        JTestMetadataReport4Test jTestMetadataReport4Test = (JTestMetadataReport4Test) metadataReportService1.getMetadataReport();
-        Assertions.assertTrue(jTestMetadataReport4Test.store.containsKey(JTestMetadataReport4Test.getConsumerKey(publishUrl)));
-
-        String value = jTestMetadataReport4Test.store.get(JTestMetadataReport4Test.getConsumerKey(publishUrl));
-        Gson gson = new Gson();
-        Map<String, String> map = gson.fromJson(value, Map.class);
-        Assertions.assertEquals(map.get("application"), "vicpubconsumer");
-        Assertions.assertEquals(map.get("version"), "1.0.x");
-
-    }
-
-    @Test
-    public void testPublishServiceDefinition() {
-        URL publishUrl = URL.valueOf("dubbo://" + NetUtils.getLocalAddress().getHostName() + ":4444/org.apache.dubbo.TestService?version=1.0.0&application=vicpubprovder&side=provider");
-        metadataReportService1.publishServiceDefinition(publishUrl);
-
-        Assertions.assertTrue(metadataReportService1.getMetadataReport() instanceof JTestMetadataReport4Test);
-
-        JTestMetadataReport4Test jTestMetadataReport4Test = (JTestMetadataReport4Test) metadataReportService1.getMetadataReport();
-        Assertions.assertTrue(!jTestMetadataReport4Test.store.containsKey(JTestMetadataReport4Test.getProviderKey(publishUrl)));
-
-    }
-
-    @Test
-    public void testUnexportURL() {
-
-    }
-
-    @Test
-    public void testRefreshMetadataService() throws InterruptedException {
-        URL publishUrl = URL.valueOf("dubbo://" + NetUtils.getLocalAddress().getHostName() + ":4444/org.apache.dubbo.TestRefreshMetadataService?version=1.0.8&application=vicpubprovder&side=provider");
-        URL publishUrl2 = URL.valueOf("dubbo://" + NetUtils.getLocalAddress().getHostName() + ":4444/org.apache.dubbo.TestRefreshMetadata2Service?version=1.0.5&application=vicpubprovder&side=provider");
-        metadataReportService1.exportURL(publishUrl);
-        metadataReportService1.exportURL(publishUrl2);
-        String exportedRevision = "9999";
-        JTestMetadataReport4Test jTestMetadataReport4Test = (JTestMetadataReport4Test) metadataReportService1.getMetadataReport();
-        int origSize = jTestMetadataReport4Test.store.size();
-        Assertions.assertTrue(metadataReportService1.refreshMetadata(exportedRevision, "1109"));
-        Thread.sleep(200);
-        int size = jTestMetadataReport4Test.store.size();
-        Assertions.assertEquals(origSize, size);
-        Assertions.assertNull(jTestMetadataReport4Test.store.get(getServiceMetadataIdentifier(publishUrl, exportedRevision).getUniqueKey(KeyTypeEnum.UNIQUE_KEY)));
-        Assertions.assertNull(jTestMetadataReport4Test.store.get(getServiceMetadataIdentifier(publishUrl2, exportedRevision).getUniqueKey(KeyTypeEnum.UNIQUE_KEY)));
-    }
-
-    @Test
-    public void testRefreshMetadataSubscription() throws InterruptedException {
-        URL subscriberUrl1 = URL.valueOf("subscriber://" + NetUtils.getLocalAddress().getHostName() + ":4444/org.apache.dubbo.TestRefreshMetadata00Service?version=1.0.8&application=vicpubprovder&side=provider");
-        URL subscriberUrl2 = URL.valueOf("subscriber://" + NetUtils.getLocalAddress().getHostName() + ":4444/org.apache.dubbo.TestRefreshMetadata09Service?version=1.0.5&application=vicpubprovder&side=provider");
-        metadataReportService1.subscribeURL(subscriberUrl1);
-        metadataReportService1.subscribeURL(subscriberUrl2);
-        String exportedRevision = "9999";
-        String subscriberRevision = "2099";
-        String applicationName = "wriableMetadataService";
-        JTestMetadataReport4Test jTestMetadataReport4Test = (JTestMetadataReport4Test) metadataReportService1.getMetadataReport();
-        int origSize = jTestMetadataReport4Test.store.size();
-        ApplicationModel.setApplication(applicationName);
-        Assertions.assertTrue(metadataReportService1.refreshMetadata(exportedRevision, subscriberRevision));
-        Thread.sleep(200);
-        int size = jTestMetadataReport4Test.store.size();
-        Assertions.assertEquals(origSize, size);
-        String r = jTestMetadataReport4Test.store.get(getSubscriberMetadataIdentifier(
-                subscriberRevision).getUniqueKey(KeyTypeEnum.UNIQUE_KEY));
-        Assertions.assertNull(r);
-    }
-
-    private ServiceMetadataIdentifier getServiceMetadataIdentifier(URL publishUrl, String exportedRevision) {
-        ServiceMetadataIdentifier serviceMetadataIdentifier = new ServiceMetadataIdentifier(publishUrl);
-        serviceMetadataIdentifier.setRevision(exportedRevision);
-        serviceMetadataIdentifier.setProtocol(publishUrl.getProtocol());
-        return serviceMetadataIdentifier;
-    }
-
-    private SubscriberMetadataIdentifier getSubscriberMetadataIdentifier(String subscriberRevision) {
-        SubscriberMetadataIdentifier subscriberMetadataIdentifier = new SubscriberMetadataIdentifier();
-        subscriberMetadataIdentifier.setRevision(subscriberRevision);
-        subscriberMetadataIdentifier.setApplication(ApplicationModel.getApplication());
-        return subscriberMetadataIdentifier;
-    }
-
-    private FullServiceDefinition toServiceDefinition(String urlQuery) {
-        Gson gson = new Gson();
-        return gson.fromJson(urlQuery, FullServiceDefinition.class);
-    }
-
-}
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 2607054..48ceffd 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,7 +29,6 @@ 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;
 
@@ -132,7 +131,7 @@ public class ServiceDiscoveryRegistry extends FailbackRegistry {
         this.serviceDiscovery = createServiceDiscovery(registryURL);
         this.subscribedServices = parseServices(registryURL.getParameter(SUBSCRIBED_SERVICE_NAMES_KEY));
         this.serviceNameMapping = ServiceNameMapping.getDefaultExtension();
-        this.writableMetadataService = MetadataUtils.getLocalMetadataService();
+        this.writableMetadataService = WritableMetadataService.getDefaultExtension();
     }
 
     public ServiceDiscovery getServiceDiscovery() {
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/MetadataServiceURLParamsMetadataCustomizer.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/MetadataServiceURLParamsMetadataCustomizer.java
index 5ad373a..be78440 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/MetadataServiceURLParamsMetadataCustomizer.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/MetadataServiceURLParamsMetadataCustomizer.java
@@ -25,10 +25,9 @@ import org.apache.dubbo.registry.client.ServiceInstanceMetadataCustomizer;
 import java.util.SortedSet;
 
 import static org.apache.dubbo.metadata.MetadataService.toURLs;
-import static org.apache.dubbo.metadata.WritableMetadataService.getExtension;
+import static org.apache.dubbo.metadata.WritableMetadataService.getDefaultExtension;
 import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.METADATA_SERVICE_URL_PARAMS_PROPERTY_NAME;
 import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.getMetadataServiceParameter;
-import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.getMetadataStorageType;
 
 /**
  * An {@link ServiceInstanceMetadataCustomizer} to customize the {@link URL urls} of {@link MetadataService}
@@ -46,10 +45,7 @@ public class MetadataServiceURLParamsMetadataCustomizer extends ServiceInstanceM
 
     @Override
     public String resolveMetadataPropertyValue(ServiceInstance serviceInstance) {
-
-        String metadataStorageType = getMetadataStorageType(serviceInstance);
-
-        WritableMetadataService writableMetadataService = getExtension(metadataStorageType);
+        WritableMetadataService writableMetadataService = getDefaultExtension();
 
         String serviceInterface = MetadataService.class.getName();
 
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
index 0926f7d..9cbe577 100644
--- 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
@@ -23,7 +23,6 @@ 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;
@@ -55,27 +54,16 @@ public class MetadataUtils {
         if (remoteMetadataService == null) {
             synchronized (REMOTE_LOCK) {
                 if (remoteMetadataService == null) {
-                    remoteMetadataService = new RemoteMetadataServiceImpl(getLocalMetadataService());
+                    remoteMetadataService = new RemoteMetadataServiceImpl(WritableMetadataService.getDefaultExtension());
                 }
             }
         }
         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);
+        WritableMetadataService.getDefaultExtension().publishServiceDefinition(url);
         // send to remote
         getRemoteMetadataService().publishServiceDefinition(url);
     }
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/ProtocolPortsMetadataCustomizer.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/ProtocolPortsMetadataCustomizer.java
index 2d83295..c8dffc4 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/ProtocolPortsMetadataCustomizer.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/ProtocolPortsMetadataCustomizer.java
@@ -26,8 +26,6 @@ import org.apache.dubbo.rpc.Protocol;
 import java.util.HashMap;
 import java.util.Map;
 
-import static org.apache.dubbo.metadata.WritableMetadataService.getExtension;
-import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.getMetadataStorageType;
 import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.setEndpoints;
 
 /**
@@ -40,10 +38,7 @@ public class ProtocolPortsMetadataCustomizer implements ServiceInstanceCustomize
 
     @Override
     public void customize(ServiceInstance serviceInstance) {
-
-        String metadataStoredType = getMetadataStorageType(serviceInstance);
-
-        WritableMetadataService writableMetadataService = getExtension(metadataStoredType);
+        WritableMetadataService writableMetadataService = WritableMetadataService.getDefaultExtension();
 
         Map<String, Integer> protocols = new HashMap<>();
         writableMetadataService.getExportedURLs()
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
index ac0cbc2..799d2aa 100644
--- 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
@@ -44,6 +44,7 @@ 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;
+import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.EXPORTED_SERVICES_REVISION_PROPERTY_NAME;
 
 public class RemoteMetadataServiceImpl {
 
@@ -59,14 +60,17 @@ public class RemoteMetadataServiceImpl {
     }
 
     public void publishMetadata(ServiceInstance instance) {
-        SubscriberMetadataIdentifier identifier = new SubscriberMetadataIdentifier(instance.getServiceName(), ServiceInstanceMetadataUtils.getExportedServicesRevision(instance));
+        MetadataInfo metadataInfo = localMetadataService.getMetadataInfo();
+        SubscriberMetadataIdentifier identifier = new SubscriberMetadataIdentifier(instance.getServiceName(), metadataInfo.getRevision());
         getMetadataReports().forEach(metadataReport -> {
-            metadataReport.publishAppMetadata(identifier, localMetadataService.getMetadataInfo());
+            instance.getMetadata().put(EXPORTED_SERVICES_REVISION_PROPERTY_NAME, metadataInfo.getRevision());
+            metadataReport.publishAppMetadata(identifier, metadataInfo);
         });
     }
 
     public MetadataInfo getMetadata(ServiceInstance instance) {
-        SubscriberMetadataIdentifier identifier = new SubscriberMetadataIdentifier(instance.getServiceName(), ServiceInstanceMetadataUtils.getExportedServicesRevision(instance));
+        SubscriberMetadataIdentifier identifier = new SubscriberMetadataIdentifier(instance.getServiceName(),
+                ServiceInstanceMetadataUtils.getExportedServicesRevision(instance));
         for (MetadataReport reporter : getMetadataReports()) {
             MetadataInfo metadataInfo = reporter.getAppMetadata(identifier, instance.getMetadata());
             if (metadataInfo != null) {
diff --git a/dubbo-registry/dubbo-registry-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metadata.WritableMetadataService b/dubbo-registry/dubbo-registry-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metadata.WritableMetadataService
new file mode 100644
index 0000000..0030270
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metadata.WritableMetadataService
@@ -0,0 +1 @@
+default=org.apache.dubbo.registry.client.metadata.store.InMemoryWritableMetadataService
\ No newline at end of file
diff --git a/dubbo-registry/dubbo-registry-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.metadata.proxy.MetadataServiceProxyFactory b/dubbo-registry/dubbo-registry-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.metadata.proxy.MetadataServiceProxyFactory
deleted file mode 100644
index a42e022..0000000
--- a/dubbo-registry/dubbo-registry-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.metadata.proxy.MetadataServiceProxyFactory
+++ /dev/null
@@ -1,3 +0,0 @@
-local=org.apache.dubbo.registry.client.metadata.proxy.DefaultMetadataServiceProxyFactory
-remote=org.apache.dubbo.registry.client.metadata.proxy.RemoteMetadataServiceProxyFactory
-composite = org.apache.dubbo.registry.client.metadata.proxy.CompositeMetadataServiceProxyFactory
\ No newline at end of file
diff --git a/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/NacosServiceDiscovery.java b/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/NacosServiceDiscovery.java
index f27a50d..b84bfa9 100644
--- a/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/NacosServiceDiscovery.java
+++ b/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/NacosServiceDiscovery.java
@@ -30,7 +30,6 @@ import com.alibaba.nacos.api.naming.listener.NamingEvent;
 import com.alibaba.nacos.api.naming.pojo.Instance;
 import com.alibaba.nacos.api.naming.pojo.ListView;
 
-import java.util.Collection;
 import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Set;
@@ -121,7 +120,7 @@ public class NacosServiceDiscovery implements ServiceDiscovery {
 
     private void handleEvent(NamingEvent event, ServiceInstancesChangedListener listener) {
         String serviceName = event.getServiceName();
-        Collection<ServiceInstance> serviceInstances = event.getInstances()
+        List<ServiceInstance> serviceInstances = event.getInstances()
                 .stream()
                 .map(NacosNamingServiceUtils::toServiceInstance)
                 .collect(Collectors.toList());


[dubbo] 18/27: unify registry_cluster key

Posted by li...@apache.org.
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

commit cd44607560a24c1440f48c1d3bebbd5e4bca76c3
Author: ken.lj <ke...@gmail.com>
AuthorDate: Fri Jul 24 16:55:14 2020 +0800

    unify registry_cluster key
---
 .../org/apache/dubbo/common/constants/RegistryConstants.java     | 2 ++
 .../dubbo/registry/client/DefaultRegistryClusterIdentifier.java  | 9 +++------
 .../apache/dubbo/registry/client/RegistryClusterIdentifier.java  | 4 ++--
 .../apache/dubbo/registry/client/ServiceDiscoveryRegistry.java   | 5 +++--
 .../client/metadata/store/RemoteMetadataServiceImpl.java         | 3 ++-
 5 files changed, 12 insertions(+), 11 deletions(-)

diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/constants/RegistryConstants.java b/dubbo-common/src/main/java/org/apache/dubbo/common/constants/RegistryConstants.java
index ce83136..420a909 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/constants/RegistryConstants.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/constants/RegistryConstants.java
@@ -21,6 +21,8 @@ public interface RegistryConstants {
 
     String REGISTRY_KEY = "registry";
 
+    String REGISTRY_CLUSTER = "REGISTRY_CLUSTER";
+
     String REGISTRY_PROTOCOL = "registry";
 
     String DYNAMIC_KEY = "dynamic";
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/DefaultRegistryClusterIdentifier.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/DefaultRegistryClusterIdentifier.java
index b225c06..6c88265 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/DefaultRegistryClusterIdentifier.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/DefaultRegistryClusterIdentifier.java
@@ -18,19 +18,16 @@ package org.apache.dubbo.registry.client;
 
 import org.apache.dubbo.common.URL;
 
-import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_KEY;
+import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_CLUSTER;
 
 public class DefaultRegistryClusterIdentifier implements RegistryClusterIdentifier {
     @Override
     public String providerKey(URL url) {
-        // url.getParameter("registry_cluster");
-        // ServiceMetadata.get("registry_cluster");
-        // return;
-        return url.getParameter(REGISTRY_KEY);
+        return url.getParameter(REGISTRY_CLUSTER);
     }
 
     @Override
     public String consumerKey(URL url) {
-        return url.getParameter(REGISTRY_KEY);
+        return url.getParameter(REGISTRY_CLUSTER);
     }
 }
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/RegistryClusterIdentifier.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/RegistryClusterIdentifier.java
index b48b233..76d141a 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/RegistryClusterIdentifier.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/RegistryClusterIdentifier.java
@@ -17,7 +17,6 @@
 package org.apache.dubbo.registry.client;
 
 import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.config.ConfigurationUtils;
 import org.apache.dubbo.common.extension.ExtensionLoader;
 import org.apache.dubbo.common.extension.SPI;
 
@@ -30,6 +29,7 @@ public interface RegistryClusterIdentifier {
     static RegistryClusterIdentifier getExtension() {
         ExtensionLoader<RegistryClusterIdentifier> loader
                 = ExtensionLoader.getExtensionLoader(RegistryClusterIdentifier.class);
-        return loader.getExtension(ConfigurationUtils.getProperty("dubbo.application.sd.type", "default"));
+//        return loader.getExtension(ConfigurationUtils.getProperty("dubbo.application.sd.type", "default"));
+        return loader.getExtension("default");
     }
 }
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 8e5a800..f3d41c5 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
@@ -62,6 +62,7 @@ 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.VERSION_KEY;
 import static org.apache.dubbo.common.constants.RegistryConstants.PROVIDED_BY;
+import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_CLUSTER;
 import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_KEY;
 import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_TYPE_KEY;
 import static org.apache.dubbo.common.constants.RegistryConstants.SERVICE_REGISTRY_TYPE;
@@ -206,7 +207,7 @@ public class ServiceDiscoveryRegistry implements Registry {
     public void doRegister(URL url) {
         String registryCluster = serviceDiscovery.getUrl().getParameter(ID_KEY);
         if (registryCluster != null) {
-            url = url.addParameter(REGISTRY_KEY, registryCluster);
+            url = url.addParameter(REGISTRY_CLUSTER, registryCluster);
         }
         if (writableMetadataService.exportURL(url)) {
             if (logger.isInfoEnabled()) {
@@ -230,7 +231,7 @@ public class ServiceDiscoveryRegistry implements Registry {
     public void doUnregister(URL url) {
         String registryCluster = serviceDiscovery.getUrl().getParameter(ID_KEY);
         if (registryCluster != null) {
-            url = url.addParameter(REGISTRY_KEY, registryCluster);
+            url = url.addParameter(REGISTRY_CLUSTER, registryCluster);
         }
         if (writableMetadataService.unexportURL(url)) {
             if (logger.isInfoEnabled()) {
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
index 00aa92a..2d543e5 100644
--- 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
@@ -44,6 +44,7 @@ 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;
+import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_CLUSTER;
 import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_KEY;
 
 public class RemoteMetadataServiceImpl {
@@ -79,7 +80,7 @@ public class RemoteMetadataServiceImpl {
         SubscriberMetadataIdentifier identifier = new SubscriberMetadataIdentifier(instance.getServiceName(),
                 ServiceInstanceMetadataUtils.getExportedServicesRevision(instance));
 
-        String registryCluster = instance.getExtendParams().get(REGISTRY_KEY);
+        String registryCluster = instance.getExtendParams().get(REGISTRY_CLUSTER);
 
         MetadataReport metadataReport = getMetadataReports().get(registryCluster);
         if (metadataReport == null) {


[dubbo] 27/27: Merge branch '3.0' of https://github.com/apache/dubbo into 3.0

Posted by li...@apache.org.
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

commit 0e4bbb8eadb12e41eca5846030260b895516ff2a
Merge: 7acf2db 99c4166
Author: ken.lj <ke...@gmail.com>
AuthorDate: Tue Aug 4 14:51:11 2020 +0800

    Merge branch '3.0' of https://github.com/apache/dubbo into 3.0
    
     Conflicts:
    	dubbo-common/src/main/java/org/apache/dubbo/common/URL.java



[dubbo] 10/27: service discovery

Posted by li...@apache.org.
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

commit ca62d8352d7398a73f1a72097c665a9c3fceb1af
Author: ken.lj <ke...@gmail.com>
AuthorDate: Thu Jul 9 18:01:19 2020 +0800

    service discovery
---
 .../org.apache.dubbo.rpc.cluster.RouterFactory     |  1 -
 .../dubbo/common/config/ConfigurationUtils.java    |  4 ++
 .../dubbo/config/bootstrap/DubboBootstrap.java     | 58 ++++++++------------
 .../apache/dubbo/metadata/MetadataConstants.java   | 27 ++++------
 .../org/apache/dubbo/metadata/MetadataInfo.java    |  7 +++
 .../dubbo/qos/command/impl/PublishMetadata.java    | 63 ++++++++++++++++++++++
 .../org.apache.dubbo.qos.command.BaseCommand       |  1 +
 .../client/EventPublishingServiceDiscovery.java    |  5 ++
 .../client/FileSystemServiceDiscovery.java         |  8 +++
 .../dubbo/registry/client/ServiceDiscovery.java    |  2 +
 .../client/ServiceDiscoveryRegistryDirectory.java  |  2 +-
 .../listener/ServiceInstancesChangedListener.java  | 14 +++--
 .../metadata/ServiceInstanceMetadataUtils.java     | 37 +++++++++++++
 .../store/InMemoryWritableMetadataService.java     | 12 +++++
 .../metadata/store/RemoteMetadataServiceImpl.java  |  6 +--
 .../registry/support/AbstractRegistryFactory.java  | 12 +++++
 .../registry/client/InMemoryServiceDiscovery.java  |  8 +++
 .../zookeeper/ZookeeperServiceDiscovery.java       | 18 +++++--
 18 files changed, 218 insertions(+), 67 deletions(-)

diff --git a/dubbo-cluster/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.RouterFactory b/dubbo-cluster/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.RouterFactory
index 13e307b..2a807f0 100644
--- a/dubbo-cluster/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.RouterFactory
+++ b/dubbo-cluster/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.RouterFactory
@@ -5,4 +5,3 @@ service=org.apache.dubbo.rpc.cluster.router.condition.config.ServiceRouterFactor
 app=org.apache.dubbo.rpc.cluster.router.condition.config.AppRouterFactory
 tag=org.apache.dubbo.rpc.cluster.router.tag.TagRouterFactory
 mock=org.apache.dubbo.rpc.cluster.router.mock.MockRouterFactory
-instance=org.apache.dubbo.rpc.cluster.router.service.InstanceRouterFactory
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/config/ConfigurationUtils.java b/dubbo-common/src/main/java/org/apache/dubbo/common/config/ConfigurationUtils.java
index 70fd9f1..a7c0693 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/config/ConfigurationUtils.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/config/ConfigurationUtils.java
@@ -100,6 +100,10 @@ public class ConfigurationUtils {
         return StringUtils.trim(getGlobalConfiguration().getString(property, defaultValue));
     }
 
+    public static int get(String property, int defaultValue) {
+        return getGlobalConfiguration().getInt(property, defaultValue);
+    }
+
     public static Map<String, String> parseProperties(String content) throws IOException {
         Map<String, String> map = new HashMap<>();
         if (StringUtils.isEmpty(content)) {
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 455535c6..a9f2bcf 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
@@ -17,6 +17,7 @@
 package org.apache.dubbo.config.bootstrap;
 
 import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.config.ConfigurationUtils;
 import org.apache.dubbo.common.config.Environment;
 import org.apache.dubbo.common.config.configcenter.DynamicConfiguration;
 import org.apache.dubbo.common.config.configcenter.wrapper.CompositeDynamicConfiguration;
@@ -58,17 +59,16 @@ import org.apache.dubbo.config.utils.ReferenceConfigCache;
 import org.apache.dubbo.event.EventDispatcher;
 import org.apache.dubbo.event.EventListener;
 import org.apache.dubbo.event.GenericEventListener;
-import org.apache.dubbo.metadata.MetadataInfo;
 import org.apache.dubbo.metadata.MetadataService;
 import org.apache.dubbo.metadata.MetadataServiceExporter;
 import org.apache.dubbo.metadata.WritableMetadataService;
 import org.apache.dubbo.metadata.report.MetadataReportInstance;
 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.ServiceInstanceMetadataUtils;
+import org.apache.dubbo.registry.client.metadata.store.InMemoryWritableMetadataService;
 import org.apache.dubbo.registry.client.metadata.store.RemoteMetadataServiceImpl;
 import org.apache.dubbo.registry.support.AbstractRegistryFactory;
 import org.apache.dubbo.rpc.model.ApplicationModel;
@@ -89,7 +89,6 @@ import java.util.concurrent.locks.Condition;
 import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReentrantLock;
 import java.util.function.Consumer;
-import java.util.stream.Collectors;
 
 import static java.util.Arrays.asList;
 import static java.util.concurrent.Executors.newSingleThreadExecutor;
@@ -99,11 +98,13 @@ import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_METADATA
 import static org.apache.dubbo.common.constants.CommonConstants.REMOTE_METADATA_STORAGE_TYPE;
 import static org.apache.dubbo.common.function.ThrowableAction.execute;
 import static org.apache.dubbo.common.utils.StringUtils.isNotEmpty;
+import static org.apache.dubbo.metadata.MetadataConstants.DEFAULT_METADATA_PUBLISH_DELAY;
+import static org.apache.dubbo.metadata.MetadataConstants.METADATA_PUBLISH_DELAY_KEY;
 import static org.apache.dubbo.metadata.WritableMetadataService.getDefaultExtension;
-import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.EXPORTED_SERVICES_REVISION_PROPERTY_NAME;
+import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.calInstanceRevision;
 import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.setMetadataStorageType;
+import static org.apache.dubbo.registry.support.AbstractRegistryFactory.getServiceDiscoveries;
 import static org.apache.dubbo.remoting.Constants.CLIENT_KEY;
-import static org.apache.dubbo.rpc.Constants.ID_KEY;
 
 /**
  * See {@link ApplicationModel} and {@link ExtensionLoader} for why this class is designed to be singleton.
@@ -734,15 +735,6 @@ public class DubboBootstrap extends GenericEventListener {
         addEventListener(this);
     }
 
-    private List<ServiceDiscovery> getServiceDiscoveries() {
-        return AbstractRegistryFactory.getRegistries()
-                .stream()
-                .filter(registry -> registry instanceof ServiceDiscoveryRegistry)
-                .map(registry -> (ServiceDiscoveryRegistry) registry)
-                .map(ServiceDiscoveryRegistry::getServiceDiscovery)
-                .collect(Collectors.toList());
-    }
-
     /**
      * Start the bootstrap
      */
@@ -1024,6 +1016,18 @@ public class DubboBootstrap extends GenericEventListener {
 
         ServiceInstance serviceInstance = createServiceInstance(serviceName, host, port);
 
+        doRegisterServiceInstance(serviceInstance);
+
+        // scheduled task for updating Metadata and ServiceInstance
+        executorRepository.nextScheduledExecutor().scheduleAtFixedRate(() -> {
+            InMemoryWritableMetadataService localMetadataService = (InMemoryWritableMetadataService) WritableMetadataService.getDefaultExtension();
+            localMetadataService.blockUntilUpdated();
+            ServiceInstanceMetadataUtils.refreshMetadataAndInstance();
+        }, 0, ConfigurationUtils.get(METADATA_PUBLISH_DELAY_KEY, DEFAULT_METADATA_PUBLISH_DELAY), TimeUnit.MICROSECONDS);
+    }
+
+    private void doRegisterServiceInstance(ServiceInstance serviceInstance) {
+        //FIXME
         publishMetadataToRemote(serviceInstance);
 
         getServiceDiscoveries().forEach(serviceDiscovery ->
@@ -1032,31 +1036,13 @@ public class DubboBootstrap extends GenericEventListener {
             // register metadata
             serviceDiscovery.register(serviceInstance);
         });
-
-        // scheduled task for updating Metadata and ServiceInstance
-        executorRepository.nextScheduledExecutor().scheduleAtFixedRate(() -> {
-            publishMetadataToRemote(serviceInstance);
-
-            getServiceDiscoveries().forEach(serviceDiscovery ->
-            {
-                calInstanceRevision(serviceDiscovery, serviceInstance);
-                // register metadata
-                serviceDiscovery.register(serviceInstance);
-            });
-        }, 0, 5000, TimeUnit.MICROSECONDS);
     }
 
     private void publishMetadataToRemote(ServiceInstance serviceInstance) {
+//        InMemoryWritableMetadataService localMetadataService = (InMemoryWritableMetadataService)WritableMetadataService.getDefaultExtension();
+//        localMetadataService.blockUntilUpdated();
         RemoteMetadataServiceImpl remoteMetadataService = MetadataUtils.getRemoteMetadataService();
-        remoteMetadataService.publishMetadata(serviceInstance);
-    }
-
-    private void calInstanceRevision(ServiceDiscovery serviceDiscovery, ServiceInstance instance) {
-        String registryCluster = serviceDiscovery.getUrl().getParameter(ID_KEY);
-        MetadataInfo metadataInfo = WritableMetadataService.getDefaultExtension().getMetadataInfos().get(registryCluster);
-        if (metadataInfo != null) {
-            instance.getMetadata().put(EXPORTED_SERVICES_REVISION_PROPERTY_NAME, metadataInfo.getRevision());
-        }
+        remoteMetadataService.publishMetadata(serviceInstance.getServiceName());
     }
 
     private URL selectMetadataServiceExportedURL() {
diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataConstants.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataConstants.java
index d670089..7ba0a43 100644
--- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataConstants.java
+++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataConstants.java
@@ -16,22 +16,13 @@
  */
 package org.apache.dubbo.metadata;
 
-public interface MetadataConstants {
-    String KEY_SEPARATOR = ":";
-    String DEFAULT_PATH_TAG = "metadata";
-    String KEY_REVISON_PREFIX = "revision";
-    String META_DATA_STORE_TAG = ".metaData";
-    String SERVICE_META_DATA_STORE_TAG = ".smd";
-    String CONSUMER_META_DATA_STORE_TAG = ".cmd";
-
-    /**
-     * @since 2.7.8
-     */
-    String EXPORTED_URLS_TAG = "exported-urls";
-
-    /**
-     * @since 2.7.8
-     */
-    String SUBSCRIBED_URLS_TAG = "subscribed-urls";
-
+public class MetadataConstants {
+    public static final String KEY_SEPARATOR = ":";
+    public static final String DEFAULT_PATH_TAG = "metadata";
+    public static final String KEY_REVISON_PREFIX = "revision";
+    public static final String META_DATA_STORE_TAG = ".metaData";
+    public static final String SERVICE_META_DATA_STORE_TAG = ".smd";
+    public static final String CONSUMER_META_DATA_STORE_TAG = ".cmd";
+    public static final String METADATA_PUBLISH_DELAY_KEY = "dubbo.application.metadata.delay";
+    public static final int DEFAULT_METADATA_PUBLISH_DELAY = 5000;
 }
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 73e736f..4db0298 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
@@ -20,6 +20,7 @@ import org.apache.dubbo.common.URL;
 import org.apache.dubbo.common.compiler.support.ClassUtils;
 import org.apache.dubbo.common.extension.ExtensionLoader;
 import org.apache.dubbo.common.utils.ArrayUtils;
+import org.apache.dubbo.common.utils.CollectionUtils;
 import org.apache.dubbo.common.utils.StringUtils;
 
 import java.io.Serializable;
@@ -39,6 +40,7 @@ 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 {
+    public static String DEFAULT_REVISION = "0";
     private String app;
     private String revision;
     private Map<String, ServiceInfo> services;
@@ -85,6 +87,11 @@ public class MetadataInfo implements Serializable {
         if (revision != null && hasReported()) {
             return revision;
         }
+
+        if (CollectionUtils.isEmptyMap(services)) {
+            return DEFAULT_REVISION;
+        }
+
         StringBuilder sb = new StringBuilder();
         sb.append(app);
         for (Map.Entry<String, ServiceInfo> entry : services.entrySet()) {
diff --git a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/PublishMetadata.java b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/PublishMetadata.java
new file mode 100644
index 0000000..990854c
--- /dev/null
+++ b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/PublishMetadata.java
@@ -0,0 +1,63 @@
+/*
+ * 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.qos.command.impl;
+
+import org.apache.dubbo.common.extension.ExtensionLoader;
+import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.common.threadpool.manager.ExecutorRepository;
+import org.apache.dubbo.common.utils.ArrayUtils;
+import org.apache.dubbo.qos.command.BaseCommand;
+import org.apache.dubbo.qos.command.CommandContext;
+import org.apache.dubbo.qos.command.annotation.Cmd;
+import org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils;
+
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+
+@Cmd(name = "publishMetadata", summary = "update service metadata and service instance", example = {
+        "publishMetadata",
+        "publishMetadata 5"
+})
+public class PublishMetadata implements BaseCommand {
+    private static final Logger logger = LoggerFactory.getLogger(PublishMetadata.class);
+    private final ExecutorRepository executorRepository = ExtensionLoader.getExtensionLoader(ExecutorRepository.class).getDefaultExtension();
+    private ScheduledFuture future;
+
+    @Override
+    public String execute(CommandContext commandContext, String[] args) {
+        logger.info("received publishMetadata command.");
+
+        if (ArrayUtils.isEmpty(args)) {
+            ServiceInstanceMetadataUtils.refreshMetadataAndInstance();
+            return "publish metadata succeeded.";
+        }
+
+        try {
+            int delay = Integer.parseInt(args[0]);
+            if (future == null || future.isDone() || future.isCancelled()) {
+                future = executorRepository.nextScheduledExecutor()
+                        .scheduleWithFixedDelay(ServiceInstanceMetadataUtils::refreshMetadataAndInstance, 0, delay, TimeUnit.MILLISECONDS);
+            }
+        } catch (NumberFormatException e) {
+            logger.error("Wrong delay param", e);
+            return "publishMetadata failed! Wrong delay param!";
+        }
+        return "publish task submitted, will publish in " + args[0] + " seconds.";
+    }
+
+}
diff --git a/dubbo-plugin/dubbo-qos/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.qos.command.BaseCommand b/dubbo-plugin/dubbo-qos/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.qos.command.BaseCommand
index cb6e9a7..b92b6b2 100644
--- a/dubbo-plugin/dubbo-qos/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.qos.command.BaseCommand
+++ b/dubbo-plugin/dubbo-qos/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.qos.command.BaseCommand
@@ -5,3 +5,4 @@ ls=org.apache.dubbo.qos.command.impl.Ls
 offline=org.apache.dubbo.qos.command.impl.Offline
 ready=org.apache.dubbo.qos.command.impl.Ready
 version=org.apache.dubbo.qos.command.impl.Version
+publish-metadata=org.apache.dubbo.qos.command.impl.PublishMetadata
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/EventPublishingServiceDiscovery.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/EventPublishingServiceDiscovery.java
index b5517b7..ee99000 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/EventPublishingServiceDiscovery.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/EventPublishingServiceDiscovery.java
@@ -229,6 +229,11 @@ final class EventPublishingServiceDiscovery implements ServiceDiscovery {
     }
 
     @Override
+    public ServiceInstance getLocalInstance() {
+        return serviceDiscovery.getLocalInstance();
+    }
+
+    @Override
     public void initialize(URL registryURL) {
 
         assertInitialized(INITIALIZE_ACTION);
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/FileSystemServiceDiscovery.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/FileSystemServiceDiscovery.java
index ba8d7d3..2a51168 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/FileSystemServiceDiscovery.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/FileSystemServiceDiscovery.java
@@ -62,6 +62,8 @@ public class FileSystemServiceDiscovery implements ServiceDiscovery, EventListen
 
     private FileSystemDynamicConfiguration dynamicConfiguration;
 
+    private ServiceInstance serviceInstance;
+
     @Override
     public void onEvent(ServiceInstancesChangedEvent event) {
 
@@ -134,7 +136,13 @@ public class FileSystemServiceDiscovery implements ServiceDiscovery, EventListen
     }
 
     @Override
+    public ServiceInstance getLocalInstance() {
+        return serviceInstance;
+    }
+
+    @Override
     public void register(ServiceInstance serviceInstance) throws RuntimeException {
+        this.serviceInstance = serviceInstance;
         String serviceInstanceId = getServiceInstanceId(serviceInstance);
         String serviceName = getServiceName(serviceInstance);
         String content = toJSONString(serviceInstance);
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscovery.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscovery.java
index f9e667b..9800c35 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscovery.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscovery.java
@@ -267,6 +267,8 @@ public interface ServiceDiscovery extends Prioritized {
         return null;
     }
 
+    ServiceInstance getLocalInstance();
+
     /**
      * A human-readable description of the implementation
      *
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistryDirectory.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistryDirectory.java
index 9890988..3fd7713 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistryDirectory.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistryDirectory.java
@@ -64,7 +64,7 @@ public class ServiceDiscoveryRegistryDirectory<T> extends DynamicDirectory<T> im
     }
 
     private void refreshInvoker(List<URL> invokerUrls) {
-        Assert.notNull(invokerUrls, "invokerUrls should not be null");
+        Assert.notNull(invokerUrls, "invokerUrls should not be null, use empty:// to clear address.");
 
         if (invokerUrls.size() == 1
                 && invokerUrls.get(0) != null
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 1901e34..8af48a3 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
@@ -45,6 +45,7 @@ import java.util.TreeSet;
 
 import static org.apache.dubbo.common.constants.CommonConstants.REGISTER_KEY;
 import static org.apache.dubbo.common.constants.CommonConstants.REMOTE_METADATA_STORAGE_TYPE;
+import static org.apache.dubbo.metadata.MetadataInfo.DEFAULT_REVISION;
 import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.getExportedServicesRevision;
 
 /**
@@ -86,11 +87,17 @@ public class ServiceInstancesChangedListener implements ConditionalEventListener
         String appName = event.getServiceName();
         allInstances.put(appName, event.getServiceInstances());
 
+        Map<String, List<ServiceInstance>> revisionToInstances = new HashMap<>();
+        Map<String, Set<String>> localServiceToRevisions = new HashMap<>();
+        Map<Set<String>, List<URL>> revisionsToUrls = new HashMap();
         for (Map.Entry<String, List<ServiceInstance>> entry : allInstances.entrySet()) {
             List<ServiceInstance> instances = entry.getValue();
             for (ServiceInstance instance : instances) {
                 String revision = getExportedServicesRevision(instance);
-                Map<String, List<ServiceInstance>> revisionToInstances = new HashMap<>();
+                if (DEFAULT_REVISION.equals(revision)) {
+                    logger.info("Find instance without valid service metadata: " + instance.getAddress());
+                    continue;
+                }
                 List<ServiceInstance> subInstances = revisionToInstances.computeIfAbsent(revision, r -> new LinkedList<>());
                 subInstances.add(instance);
 
@@ -104,7 +111,6 @@ public class ServiceInstancesChangedListener implements ConditionalEventListener
                     }
                 }
 
-                Map<String, Set<String>> localServiceToRevisions = new HashMap<>();
                 if (metadata != null) {
                     parseMetadata(revision, metadata, localServiceToRevisions);
                     ((DefaultServiceInstance) instance).setServiceMetadata(metadata);
@@ -115,7 +121,6 @@ public class ServiceInstancesChangedListener implements ConditionalEventListener
 //                    set.add(revision);
 //                }
 
-                Map<Set<String>, List<URL>> revisionsToUrls = new HashMap();
                 localServiceToRevisions.forEach((serviceKey, revisions) -> {
                     List<URL> urls = revisionsToUrls.get(revisions);
                     if (urls != null) {
@@ -140,8 +145,7 @@ public class ServiceInstancesChangedListener implements ConditionalEventListener
     private Map<String, Set<String>> parseMetadata(String revision, MetadataInfo metadata, Map<String, Set<String>> localServiceToRevisions) {
         Map<String, ServiceInfo> serviceInfos = metadata.getServices();
         for (Map.Entry<String, ServiceInfo> entry : serviceInfos.entrySet()) {
-            String serviceKey = entry.getValue().getServiceKey();
-            Set<String> set = localServiceToRevisions.computeIfAbsent(serviceKey, k -> new TreeSet<>());
+            Set<String> set = localServiceToRevisions.computeIfAbsent(entry.getKey(), k -> new TreeSet<>());
             set.add(revision);
         }
 
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 e55f6f2..686d4cb 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
@@ -18,9 +18,14 @@ package org.apache.dubbo.registry.client.metadata;
 
 import org.apache.dubbo.common.URL;
 import org.apache.dubbo.common.utils.StringUtils;
+import org.apache.dubbo.metadata.MetadataInfo;
 import org.apache.dubbo.metadata.MetadataService;
 import org.apache.dubbo.metadata.WritableMetadataService;
+import org.apache.dubbo.registry.client.ServiceDiscovery;
 import org.apache.dubbo.registry.client.ServiceInstance;
+import org.apache.dubbo.registry.client.metadata.store.RemoteMetadataServiceImpl;
+import org.apache.dubbo.registry.support.AbstractRegistryFactory;
+import org.apache.dubbo.rpc.model.ApplicationModel;
 
 import com.alibaba.fastjson.JSON;
 
@@ -39,6 +44,7 @@ import static org.apache.dubbo.common.constants.CommonConstants.TIMESTAMP_KEY;
 import static org.apache.dubbo.common.utils.StringUtils.isBlank;
 import static org.apache.dubbo.registry.integration.RegistryProtocol.DEFAULT_REGISTER_PROVIDER_KEYS;
 import static org.apache.dubbo.rpc.Constants.DEPRECATED_KEY;
+import static org.apache.dubbo.rpc.Constants.ID_KEY;
 
 /**
  * The Utilities class for the {@link ServiceInstance#getMetadata() metadata of the service instance}
@@ -87,6 +93,8 @@ public class ServiceInstanceMetadataUtils {
 
     public static String METADATA_CLUSTER_PROPERTY_NAME = "dubbo.metadata.cluster";
 
+    public static String INSTANCE_REVISION_UPDATED_KEY = "dubbo.instance.revision.updated";
+
     /**
      * Get the multiple {@link URL urls'} parameters of {@link MetadataService MetadataService's} Metadata
      *
@@ -247,6 +255,35 @@ public class ServiceInstanceMetadataUtils {
         return null;
     }
 
+    public static void calInstanceRevision(ServiceDiscovery serviceDiscovery, ServiceInstance instance) {
+        String registryCluster = serviceDiscovery.getUrl().getParameter(ID_KEY);
+        MetadataInfo metadataInfo = WritableMetadataService.getDefaultExtension().getMetadataInfos().get(registryCluster);
+        if (metadataInfo != null) {
+            String existingInstanceRevision = instance.getMetadata().get(EXPORTED_SERVICES_REVISION_PROPERTY_NAME);
+            if (!metadataInfo.getRevision().equals(existingInstanceRevision)) {
+                instance.getMetadata().put(EXPORTED_SERVICES_REVISION_PROPERTY_NAME, metadataInfo.getRevision());
+                if (existingInstanceRevision != null) {// skip the first registration.
+                    instance.getExtendParams().put(INSTANCE_REVISION_UPDATED_KEY, "true");
+                }
+            }
+        }
+    }
+
+    public static boolean isInstanceUpdated(ServiceInstance instance) {
+        return "true".equals(instance.getExtendParams().get(INSTANCE_REVISION_UPDATED_KEY));
+    }
+
+    public static void refreshMetadataAndInstance() {
+        RemoteMetadataServiceImpl remoteMetadataService = MetadataUtils.getRemoteMetadataService();
+        remoteMetadataService.publishMetadata(ApplicationModel.getName());
+
+        AbstractRegistryFactory.getServiceDiscoveries().forEach(serviceDiscovery -> {
+            calInstanceRevision(serviceDiscovery, serviceDiscovery.getLocalInstance());
+            // update service instance revision
+            serviceDiscovery.update(serviceDiscovery.getLocalInstance());
+        });
+    }
+
     /**
      * Set the default parameters via the specified {@link URL providerURL}
      *
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/store/InMemoryWritableMetadataService.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/store/InMemoryWritableMetadataService.java
index 8605eaa..2b747de 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/store/InMemoryWritableMetadataService.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/store/InMemoryWritableMetadataService.java
@@ -41,6 +41,7 @@ import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
 import java.util.concurrent.ConcurrentNavigableMap;
 import java.util.concurrent.ConcurrentSkipListMap;
+import java.util.concurrent.Semaphore;
 import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReentrantLock;
 
@@ -74,6 +75,7 @@ public class InMemoryWritableMetadataService implements WritableMetadataService
      */
     ConcurrentNavigableMap<String, SortedSet<URL>> exportedServiceURLs = new ConcurrentSkipListMap<>();
     ConcurrentMap<String, MetadataInfo> metadataInfos;
+    final Semaphore metadataSemaphore = new Semaphore(1);
 
     // ==================================================================================== //
 
@@ -131,6 +133,7 @@ public class InMemoryWritableMetadataService implements WritableMetadataService
             });
             metadataInfo.addService(new ServiceInfo(url));
         }
+        metadataSemaphore.release();
         return addURL(exportedServiceURLs, url);
     }
 
@@ -145,6 +148,7 @@ public class InMemoryWritableMetadataService implements WritableMetadataService
                 metadataInfos.remove(key);
             }
         }
+        metadataSemaphore.release();
         return removeURL(exportedServiceURLs, url);
     }
 
@@ -202,6 +206,14 @@ public class InMemoryWritableMetadataService implements WritableMetadataService
         return null;
     }
 
+    public void blockUntilUpdated() {
+        try {
+            metadataSemaphore.acquire();
+        } catch (InterruptedException e) {
+            logger.warn("metadata refresh thread has been interrupted unexpectedly while wating for update.", e);
+        }
+    }
+
     public Map<String, MetadataInfo> getMetadataInfos() {
         return metadataInfos;
     }
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
index ee41050..00aa92a 100644
--- 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
@@ -47,7 +47,6 @@ import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY;
 import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_KEY;
 
 public class RemoteMetadataServiceImpl {
-
     protected final Logger logger = LoggerFactory.getLogger(getClass());
     private WritableMetadataService localMetadataService;
 
@@ -59,11 +58,11 @@ public class RemoteMetadataServiceImpl {
         return MetadataReportInstance.getMetadataReports(true);
     }
 
-    public void publishMetadata(ServiceInstance instance) {
+    public void publishMetadata(String serviceName) {
         Map<String, MetadataInfo> metadataInfos = localMetadataService.getMetadataInfos();
         metadataInfos.forEach((registryKey, metadataInfo) -> {
             if (!metadataInfo.hasReported()) {
-                SubscriberMetadataIdentifier identifier = new SubscriberMetadataIdentifier(instance.getServiceName(), metadataInfo.getRevision());
+                SubscriberMetadataIdentifier identifier = new SubscriberMetadataIdentifier(serviceName, metadataInfo.getRevision());
                 metadataInfo.getRevision();
                 metadataInfo.getExtendParams().put(REGISTRY_KEY, registryKey);
                 MetadataReport metadataReport = getMetadataReports().get(registryKey);
@@ -71,6 +70,7 @@ public class RemoteMetadataServiceImpl {
                     metadataReport = getMetadataReports().entrySet().iterator().next().getValue();
                 }
                 metadataReport.publishAppMetadata(identifier, metadataInfo);
+                metadataInfo.markReported();
             }
         });
     }
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/support/AbstractRegistryFactory.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/support/AbstractRegistryFactory.java
index 7ea5559..541c22a 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/support/AbstractRegistryFactory.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/support/AbstractRegistryFactory.java
@@ -24,6 +24,8 @@ import org.apache.dubbo.registry.NotifyListener;
 import org.apache.dubbo.registry.Registry;
 import org.apache.dubbo.registry.RegistryFactory;
 import org.apache.dubbo.registry.RegistryService;
+import org.apache.dubbo.registry.client.ServiceDiscovery;
+import org.apache.dubbo.registry.client.ServiceDiscoveryRegistry;
 
 import java.util.Collection;
 import java.util.Collections;
@@ -33,6 +35,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.locks.ReentrantLock;
+import java.util.stream.Collectors;
 
 import static org.apache.dubbo.common.constants.CommonConstants.INTERFACE_KEY;
 import static org.apache.dubbo.rpc.cluster.Constants.EXPORT_KEY;
@@ -69,6 +72,15 @@ public abstract class AbstractRegistryFactory implements RegistryFactory {
         return REGISTRIES.get(key);
     }
 
+    public static List<ServiceDiscovery> getServiceDiscoveries() {
+        return AbstractRegistryFactory.getRegistries()
+                .stream()
+                .filter(registry -> registry instanceof ServiceDiscoveryRegistry)
+                .map(registry -> (ServiceDiscoveryRegistry) registry)
+                .map(ServiceDiscoveryRegistry::getServiceDiscovery)
+                .collect(Collectors.toList());
+    }
+
     /**
      * Close all created registries
      */
diff --git a/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/InMemoryServiceDiscovery.java b/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/InMemoryServiceDiscovery.java
index fbc410b..93043ba 100644
--- a/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/InMemoryServiceDiscovery.java
+++ b/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/InMemoryServiceDiscovery.java
@@ -42,6 +42,8 @@ public class InMemoryServiceDiscovery implements ServiceDiscovery {
 
     private Map<String, List<ServiceInstance>> repository = new HashMap<>();
 
+    private ServiceInstance serviceInstance;
+
     @Override
     public Set<String> getServices() {
         return repository.keySet();
@@ -68,12 +70,18 @@ public class InMemoryServiceDiscovery implements ServiceDiscovery {
         return new DefaultPage<>(offset, pageSize, data, totalSize);
     }
 
+    @Override
+    public ServiceInstance getLocalInstance() {
+        return serviceInstance;
+    }
+
     public String toString() {
         return "InMemoryServiceDiscovery";
     }
 
     @Override
     public void register(ServiceInstance serviceInstance) throws RuntimeException {
+        this.serviceInstance = serviceInstance;
         String serviceName = serviceInstance.getServiceName();
         List<ServiceInstance> serviceInstances = repository.computeIfAbsent(serviceName, s -> new LinkedList<>());
         if (!serviceInstances.contains(serviceInstance)) {
diff --git a/dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/ZookeeperServiceDiscovery.java b/dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/ZookeeperServiceDiscovery.java
index 29e6790..a4cdd6c 100644
--- a/dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/ZookeeperServiceDiscovery.java
+++ b/dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/ZookeeperServiceDiscovery.java
@@ -41,6 +41,7 @@ import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 
 import static org.apache.dubbo.common.function.ThrowableFunction.execute;
+import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.isInstanceUpdated;
 import static org.apache.dubbo.registry.zookeeper.util.CuratorFrameworkParams.ROOT_PATH;
 import static org.apache.dubbo.registry.zookeeper.util.CuratorFrameworkUtils.build;
 import static org.apache.dubbo.registry.zookeeper.util.CuratorFrameworkUtils.buildCuratorFramework;
@@ -64,6 +65,8 @@ public class ZookeeperServiceDiscovery implements ServiceDiscovery {
 
     private org.apache.curator.x.discovery.ServiceDiscovery<ZookeeperInstance> serviceDiscovery;
 
+    private ServiceInstance serviceInstance;
+
     /**
      * The Key is watched Zookeeper path, the value is an instance of {@link CuratorWatcher}
      */
@@ -87,16 +90,25 @@ public class ZookeeperServiceDiscovery implements ServiceDiscovery {
         serviceDiscovery.close();
     }
 
+    @Override
+    public ServiceInstance getLocalInstance() {
+        return serviceInstance;
+    }
+
     public void register(ServiceInstance serviceInstance) throws RuntimeException {
+        this.serviceInstance = serviceInstance;
         doInServiceRegistry(serviceDiscovery -> {
             serviceDiscovery.registerService(build(serviceInstance));
         });
     }
 
     public void update(ServiceInstance serviceInstance) throws RuntimeException {
-        doInServiceRegistry(serviceDiscovery -> {
-            serviceDiscovery.updateService(build(serviceInstance));
-        });
+        this.serviceInstance = serviceInstance;
+        if (isInstanceUpdated(serviceInstance)) {
+            doInServiceRegistry(serviceDiscovery -> {
+                serviceDiscovery.updateService(build(serviceInstance));
+            });
+        }
     }
 
     public void unregister(ServiceInstance serviceInstance) throws RuntimeException {


[dubbo] 01/27: Merge branch 'master' of https://github.com/apache/dubbo

Posted by li...@apache.org.
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

commit adba6d010d66af0af7eb148f35a0acb681e7360f
Merge: 7702fcf 043da68
Author: ken.lj <ke...@gmail.com>
AuthorDate: Tue Aug 4 14:19:02 2020 +0800

    Merge branch 'master' of https://github.com/apache/dubbo

 README.md                                          |  2 +-
 .../org/apache/dubbo/config/AbstractConfig.java    |  8 ++++++-
 .../apache/dubbo/config/MetadataReportConfig.java  | 26 +++++++++++++---------
 .../consul/ConsulDynamicConfiguration.java         | 24 ++++++++++++++------
 dubbo-dependencies-bom/pom.xml                     |  2 +-
 .../dubbo-dependencies-zookeeper/pom.xml           |  2 +-
 pom.xml                                            |  2 +-
 7 files changed, 44 insertions(+), 22 deletions(-)


[dubbo] 06/27: service discovery demo

Posted by li...@apache.org.
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

commit 193465525c3ddcfe7f10e3610880ac0d059e9277
Author: ken.lj <ke...@gmail.com>
AuthorDate: Tue Jun 16 00:39:08 2020 +0800

    service discovery demo
---
 .../java/org/apache/dubbo/demo/consumer/Application.java     |  7 +++++--
 .../src/main/resources/spring/dubbo-consumer.xml             | 12 +++++++++---
 .../src/main/resources/spring/dubbo-provider.xml             |  7 +++++--
 3 files changed, 19 insertions(+), 7 deletions(-)

diff --git a/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-consumer/src/main/java/org/apache/dubbo/demo/consumer/Application.java b/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-consumer/src/main/java/org/apache/dubbo/demo/consumer/Application.java
index 955d3eb..f637eb2 100644
--- a/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-consumer/src/main/java/org/apache/dubbo/demo/consumer/Application.java
+++ b/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-consumer/src/main/java/org/apache/dubbo/demo/consumer/Application.java
@@ -31,7 +31,10 @@ public class Application {
         ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring/dubbo-consumer.xml");
         context.start();
         DemoService demoService = context.getBean("demoService", DemoService.class);
-        CompletableFuture<String> hello = demoService.sayHelloAsync("world");
-        System.out.println("result: " + hello.get());
+        while (true) {
+            CompletableFuture<String> hello = demoService.sayHelloAsync("world");
+            System.out.println("result: " + hello.get());
+            Thread.sleep(500);
+        }
     }
 }
diff --git a/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-consumer/src/main/resources/spring/dubbo-consumer.xml b/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-consumer/src/main/resources/spring/dubbo-consumer.xml
index 742195f..17aa9d6 100644
--- a/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-consumer/src/main/resources/spring/dubbo-consumer.xml
+++ b/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-consumer/src/main/resources/spring/dubbo-consumer.xml
@@ -21,10 +21,16 @@
        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
        http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
 
-    <dubbo:application name="demo-consumer"/>
+    <dubbo:application metadata-type="remote" name="demo-consumer">
+        <dubbo:parameter key="mapping-type" value="metadata"/>
+    </dubbo:application>
 
-    <dubbo:registry address="zookeeper://127.0.0.1:2181"/>
+    <dubbo:metadata-report address="zookeeper://127.0.0.1:2181"/>
 
-    <dubbo:reference id="demoService" check="false" interface="org.apache.dubbo.demo.DemoService"/>
+    <dubbo:registry address="zookeeper://127.0.0.1:2181?registry-type=service"/>
+
+
+    <dubbo:reference provided-by="demo-provider" id="demoService" check="false"
+                     interface="org.apache.dubbo.demo.DemoService"/>
 
 </beans>
diff --git a/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-provider/src/main/resources/spring/dubbo-provider.xml b/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-provider/src/main/resources/spring/dubbo-provider.xml
index c809ff3..d7ab19a 100644
--- a/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-provider/src/main/resources/spring/dubbo-provider.xml
+++ b/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-provider/src/main/resources/spring/dubbo-provider.xml
@@ -21,10 +21,13 @@
        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
        http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
 
-    <dubbo:application metadata-type="remote" name="demo-provider"/>
+    <dubbo:application metadata-type="remote" name="demo-provider">
+        <dubbo:parameter key="mapping-type" value="metadata"/>
+    </dubbo:application>
+
     <dubbo:metadata-report address="zookeeper://127.0.0.1:2181"/>
 
-    <dubbo:registry address="zookeeper://127.0.0.1:2181"/>
+    <dubbo:registry address="zookeeper://127.0.0.1:2181?registry-type=service"/>
 
     <dubbo:protocol name="dubbo"/>
 


[dubbo] 26/27: fix address notification issue

Posted by li...@apache.org.
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

commit 7acf2db35c614c0716301b0923b5df0517bd5a38
Author: ken.lj <ke...@gmail.com>
AuthorDate: Tue Aug 4 12:59:48 2020 +0800

    fix address notification issue
---
 .../client/ServiceDiscoveryRegistryDirectory.java  | 35 ++++++++++++----------
 .../listener/ServiceInstancesChangedListener.java  | 10 +++----
 .../ServiceInstanceMetadataCustomizer.java         |  2 +-
 .../metadata/store/RemoteMetadataServiceImpl.java  |  2 +-
 4 files changed, 27 insertions(+), 22 deletions(-)

diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistryDirectory.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistryDirectory.java
index f65a927..15c187f 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistryDirectory.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistryDirectory.java
@@ -23,6 +23,7 @@ import org.apache.dubbo.common.logger.LoggerFactory;
 import org.apache.dubbo.common.utils.Assert;
 import org.apache.dubbo.common.utils.CollectionUtils;
 import org.apache.dubbo.common.utils.NetUtils;
+import org.apache.dubbo.registry.AddressListener;
 import org.apache.dubbo.registry.NotifyListener;
 import org.apache.dubbo.registry.client.event.listener.ServiceInstancesChangedListener;
 import org.apache.dubbo.registry.integration.DynamicDirectory;
@@ -73,26 +74,30 @@ public class ServiceDiscoveryRegistryDirectory<T> extends DynamicDirectory<T> im
     public synchronized void notify(List<URL> instanceUrls) {
         // Set the context of the address notification thread.
         RpcContext.setRpcContext(getConsumerUrl());
+
+        /**
+         * 3.x added for extend URL address
+         */
+        ExtensionLoader<AddressListener> addressListenerExtensionLoader = ExtensionLoader.getExtensionLoader(AddressListener.class);
+        List<AddressListener> supportedListeners = addressListenerExtensionLoader.getActivateExtension(getUrl(), (String[]) null);
+        if (supportedListeners != null && !supportedListeners.isEmpty()) {
+            for (AddressListener addressListener : supportedListeners) {
+                instanceUrls = addressListener.notify(instanceUrls, getConsumerUrl(), this);
+            }
+        }
+
         refreshInvoker(instanceUrls);
     }
 
     private void refreshInvoker(List<URL> invokerUrls) {
-        Assert.notNull(invokerUrls, "invokerUrls should not be null, use empty InstanceAddressURL to clear address.");
+        Assert.notNull(invokerUrls, "invokerUrls should not be null, use empty url list to clear address.");
 
-        if (invokerUrls.size() == 1) {
-            URL url = invokerUrls.get(0);
-            if (!(url instanceof InstanceAddressURL)) {
-                throw new IllegalStateException("use empty InstanceAddressURL to clear address");
-            } else {
-                InstanceAddressURL instanceAddressURL = (InstanceAddressURL) url;
-                if (instanceAddressURL.getInstance() == null) {
-                    this.forbidden = true; // Forbid to access
-                    this.invokers = Collections.emptyList();
-                    routerChain.setInvokers(this.invokers);
-                    destroyAllInvokers(); // Close all invokers
-                    return;
-                }
-            }
+        if (invokerUrls.size() == 0) {
+            this.forbidden = true; // Forbid to access
+            this.invokers = Collections.emptyList();
+            routerChain.setInvokers(this.invokers);
+            destroyAllInvokers(); // Close all invokers
+            return;
         }
 
         this.forbidden = false; // Allow to access
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 b915cb3..e99b40c 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
@@ -19,7 +19,6 @@ package org.apache.dubbo.registry.client.event.listener;
 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.event.ConditionalEventListener;
 import org.apache.dubbo.event.EventListener;
 import org.apache.dubbo.metadata.MetadataInfo;
@@ -27,7 +26,6 @@ import org.apache.dubbo.metadata.MetadataInfo.ServiceInfo;
 import org.apache.dubbo.metadata.MetadataService;
 import org.apache.dubbo.registry.NotifyListener;
 import org.apache.dubbo.registry.client.DefaultServiceInstance;
-import org.apache.dubbo.registry.client.InstanceAddressURL;
 import org.apache.dubbo.registry.client.RegistryClusterIdentifier;
 import org.apache.dubbo.registry.client.ServiceDiscovery;
 import org.apache.dubbo.registry.client.ServiceInstance;
@@ -37,6 +35,7 @@ import org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils;
 import org.apache.dubbo.registry.client.metadata.store.RemoteMetadataServiceImpl;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.LinkedList;
 import java.util.List;
@@ -86,6 +85,7 @@ public class ServiceInstancesChangedListener implements ConditionalEventListener
      * @param event {@link ServiceInstancesChangedEvent}
      */
     public synchronized void onEvent(ServiceInstancesChangedEvent event) {
+        logger.info("Received instance notification, serviceName: " + event.getServiceName() + ", instances: " + event.getServiceInstances().size());
         String appName = event.getServiceName();
         allInstances.put(appName, event.getServiceInstances());
 
@@ -106,6 +106,7 @@ public class ServiceInstancesChangedListener implements ConditionalEventListener
                 MetadataInfo metadata = revisionToMetadata.get(revision);
                 if (metadata == null) {
                     metadata = getMetadataInfo(instance);
+                    logger.info("MetadataInfo for instance " + instance.getAddress() + "?revision=" + revision + " is " + metadata);
                     if (metadata != null) {
                         revisionToMetadata.put(revision, getMetadataInfo(instance));
                     } else {
@@ -183,9 +184,8 @@ public class ServiceInstancesChangedListener implements ConditionalEventListener
     }
 
     private List<URL> toUrlsWithEmpty(List<URL> urls) {
-        if (CollectionUtils.isEmpty(urls)) {
-            urls = new ArrayList<>();
-            urls.add(new InstanceAddressURL());
+        if (urls == null) {
+            urls = Collections.emptyList();
         }
         return urls;
     }
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/ServiceInstanceMetadataCustomizer.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/ServiceInstanceMetadataCustomizer.java
index 76fb765..8b8a5b7 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/ServiceInstanceMetadataCustomizer.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/ServiceInstanceMetadataCustomizer.java
@@ -51,7 +51,7 @@ public class ServiceInstanceMetadataCustomizer implements ServiceInstanceCustomi
         // FIXME, check the same key in different urls has the same value
         MetadataInfo metadataInfo = localMetadataService.getMetadataInfos().values().iterator().next();
         MetadataInfo.ServiceInfo serviceInfo = metadataInfo.getServices().values().iterator().next();
-        Map<String, String> allParams = new HashMap<>(serviceInfo.getParams());
+        Map<String, String> allParams = new HashMap<>(serviceInfo.getUrl().getParameters());
 
         // load instance params users want to load.
         // TODO, duplicate logic with that in ApplicationConfig
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
index 93c02fe..01c4eff 100644
--- 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
@@ -85,7 +85,7 @@ public class RemoteMetadataServiceImpl {
         if (metadataReport == null) {
             metadataReport = getMetadataReports().entrySet().iterator().next().getValue();
         }
-        return metadataReport.getAppMetadata(identifier, instance.getMetadata());
+        return metadataReport.getAppMetadata(identifier, instance.getExtendParams());
     }
 
     public void publishServiceDefinition(URL url) {


[dubbo] 08/27: Service Discovery Enhancement

Posted by li...@apache.org.
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

commit c572c2cd2e5dd2d11a81825f50d22a1363eee1fb
Author: ken.lj <ke...@gmail.com>
AuthorDate: Fri Jul 3 12:24:00 2020 +0800

    Service Discovery Enhancement
---
 .../rpc/cluster/directory/AbstractDirectory.java   |  15 +-
 .../org.apache.dubbo.rpc.cluster.RouterFactory     |   1 +
 .../src/main/java/org/apache/dubbo/common/URL.java |  49 ++--
 .../dubbo/common/config/ConfigurationUtils.java    |   2 +-
 .../manager/DefaultExecutorRepository.java         |   8 +-
 .../apache/dubbo/config/MetadataReportConfig.java  |  13 ++
 .../org/apache/dubbo/config/RegistryConfig.java    |   5 +
 .../org/apache/dubbo/config/ReferenceConfig.java   |   6 +
 .../dubbo/config/bootstrap/DubboBootstrap.java     |  32 ++-
 .../src/main/resources/META-INF/compat/dubbo.xsd   |   5 +
 .../src/main/resources/META-INF/dubbo.xsd          |   5 +
 .../src/main/resources/spring/dubbo-consumer.xml   |   4 +-
 .../src/main/resources/spring/dubbo-provider.xml   |   8 +-
 .../DynamicConfigurationServiceNameMapping.java    |   2 +-
 .../apache/dubbo/metadata/MappingChangedEvent.java |  27 ++-
 .../org/apache/dubbo/metadata/MappingListener.java |   8 +-
 .../org/apache/dubbo/metadata/MetadataInfo.java    |  37 +--
 .../org/apache/dubbo/metadata/MetadataService.java |   5 +-
 .../dubbo/metadata/MetadataServiceNameMapping.java |  17 +-
 .../apache/dubbo/metadata/ServiceNameMapping.java  |   2 +-
 .../dubbo/metadata/WritableMetadataService.java    |   1 +
 .../dubbo/metadata/report/MetadataReport.java      |   4 +-
 .../metadata/report/MetadataReportInstance.java    |  18 +-
 .../org/apache/dubbo/registry/NotifyListener.java  |   2 -
 .../client/DefaultRegistryClusterIdentifier.java   |  23 +-
 .../registry/client/DefaultServiceInstance.java    |  26 ++-
 .../client/EventPublishingServiceDiscovery.java    |   5 +
 .../client/FileSystemServiceDiscovery.java         |   5 +
 .../dubbo/registry/client/InstanceAddressURL.java  | 100 +++++---
 .../registry/client/RegistryClusterIdentifier.java |  22 +-
 .../dubbo/registry/client/ServiceDiscovery.java    |   6 +
 .../registry/client/ServiceDiscoveryRegistry.java  | 155 +++++++------
 .../client/ServiceDiscoveryRegistryDirectory.java  | 253 +++++++++++++++++++++
 .../client/ServiceDiscoveryRegistryProtocol.java   |   6 +-
 ... ServiceDiscoveryRegistryProtocolListener.java} |  28 +--
 .../dubbo/registry/client/ServiceInstance.java     |   8 +-
 .../listener/ServiceInstancesChangedListener.java  | 165 ++++++++------
 .../store/InMemoryWritableMetadataService.java     |  45 +++-
 .../metadata/store/RemoteMetadataServiceImpl.java  |  37 +--
 .../registry/integration/DynamicDirectory.java     | 243 ++++++++++++++++++++
 .../registry/integration/RegistryDirectory.java    | 178 +++------------
 .../integration/RegistryInvokerWrapper.java        |  22 +-
 .../registry/integration/RegistryProtocol.java     |  11 +-
 ...dubbo.registry.client.RegistryClusterIdentifier |   1 +
 ...o.registry.integration.RegistryProtocolListener |   1 +
 .../registry/consul/ConsulServiceDiscovery.java    |   7 +-
 .../consul/ConsulServiceDiscoveryFactory.java      |  17 +-
 ...g.apache.dubbo.registry.client.ServiceDiscovery |   1 -
 ...e.dubbo.registry.client.ServiceDiscoveryFactory |   1 +
 .../registry/dubbo/RegistryDirectoryTest.java      |   2 +-
 .../dubbo/registry/etcd/EtcdServiceDiscovery.java  |   2 +-
 .../registry/nacos/NacosServiceDiscovery.java      |  10 +-
 .../nacos/NacosServiceDiscoveryFactory.java        |  14 +-
 ...e.dubbo.registry.client.ServiceDiscoveryFactory |   1 +
 .../zookeeper/ZookeeperServiceDiscovery.java       |  27 +--
 .../ZookeeperServiceDiscoveryChangeWatcher.java    |   8 +-
 .../ZookeeperServiceDiscoveryFactory.java          |  16 +-
 dubbo-registry/pom.xml                             |  18 +-
 .../main/java/org/apache/dubbo/rpc/RpcContext.java |  79 +++++++
 .../dubbo/rpc/proxy/InvokerInvocationHandler.java  |   5 +-
 60 files changed, 1302 insertions(+), 522 deletions(-)

diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/directory/AbstractDirectory.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/directory/AbstractDirectory.java
index 7984d7d..6b4773f 100644
--- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/directory/AbstractDirectory.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/directory/AbstractDirectory.java
@@ -29,8 +29,13 @@ import org.apache.dubbo.rpc.cluster.RouterChain;
 
 import java.util.Collections;
 import java.util.List;
+import java.util.Map;
 
+import static org.apache.dubbo.common.constants.CommonConstants.DUBBO;
+import static org.apache.dubbo.common.constants.CommonConstants.INTERFACE_KEY;
 import static org.apache.dubbo.common.constants.CommonConstants.MONITOR_KEY;
+import static org.apache.dubbo.common.constants.CommonConstants.PATH_KEY;
+import static org.apache.dubbo.common.constants.CommonConstants.PROTOCOL_KEY;
 import static org.apache.dubbo.rpc.cluster.Constants.REFER_KEY;
 
 /**
@@ -48,6 +53,9 @@ public abstract class AbstractDirectory<T> implements Directory<T> {
 
     private volatile URL consumerUrl;
 
+    protected final Map<String, String> queryMap; // Initialization at construction time, assertion not null
+    protected final String consumedProtocol;
+
     protected RouterChain<T> routerChain;
 
     public AbstractDirectory(URL url) {
@@ -59,8 +67,13 @@ public abstract class AbstractDirectory<T> implements Directory<T> {
             throw new IllegalArgumentException("url == null");
         }
 
+        queryMap = StringUtils.parseQueryString(url.getParameterAndDecoded(REFER_KEY));
+        String path = queryMap.get(PATH_KEY);
+        this.consumedProtocol = this.queryMap.get(PROTOCOL_KEY) == null ? DUBBO : this.queryMap.get(PROTOCOL_KEY);
         this.url = url.removeParameter(REFER_KEY).removeParameter(MONITOR_KEY);
-        this.consumerUrl = this.url.addParameters(StringUtils.parseQueryString(url.getParameterAndDecoded(REFER_KEY)));
+
+        this.consumerUrl = this.url.setProtocol(consumedProtocol).setPath(path == null ? queryMap.get(INTERFACE_KEY) : path).addParameters(queryMap)
+                .removeParameter(MONITOR_KEY);
 
         setRouterChain(routerChain);
     }
diff --git a/dubbo-cluster/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.RouterFactory b/dubbo-cluster/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.RouterFactory
index 2a807f0..13e307b 100644
--- a/dubbo-cluster/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.RouterFactory
+++ b/dubbo-cluster/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.RouterFactory
@@ -5,3 +5,4 @@ service=org.apache.dubbo.rpc.cluster.router.condition.config.ServiceRouterFactor
 app=org.apache.dubbo.rpc.cluster.router.condition.config.AppRouterFactory
 tag=org.apache.dubbo.rpc.cluster.router.tag.TagRouterFactory
 mock=org.apache.dubbo.rpc.cluster.router.mock.MockRouterFactory
+instance=org.apache.dubbo.rpc.cluster.router.service.InstanceRouterFactory
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/URL.java b/dubbo-common/src/main/java/org/apache/dubbo/common/URL.java
index a396cc9..cc2b66f 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/URL.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/URL.java
@@ -134,6 +134,7 @@ class URL implements Serializable {
     private volatile transient String string;
 
     private transient String serviceKey;
+    private transient String protocolServiceKey;
 
     private transient String address;
 
@@ -448,7 +449,7 @@ class URL implements Serializable {
     }
 
     public URL setUsername(String username) {
-        return new URL(protocol, username, password, host, port, path, getParameters());
+        return new URL(getProtocol(), username, password, host, port, path, getParameters());
     }
 
     public String getPassword() {
@@ -456,7 +457,7 @@ class URL implements Serializable {
     }
 
     public URL setPassword(String password) {
-        return new URL(protocol, username, password, host, port, path, getParameters());
+        return new URL(getProtocol(), username, password, host, port, path, getParameters());
     }
 
     public String getAuthority() {
@@ -473,7 +474,7 @@ class URL implements Serializable {
     }
 
     public URL setHost(String host) {
-        return new URL(protocol, username, password, host, port, path, getParameters());
+        return new URL(getProtocol(), username, password, host, port, path, getParameters());
     }
 
     /**
@@ -496,7 +497,7 @@ class URL implements Serializable {
     }
 
     public URL setPort(int port) {
-        return new URL(protocol, username, password, host, port, path, getParameters());
+        return new URL(getProtocol(), username, password, host, port, path, getParameters());
     }
 
     public int getPort(int defaultPort) {
@@ -520,7 +521,7 @@ class URL implements Serializable {
         } else {
             host = address;
         }
-        return new URL(protocol, username, password, host, port, path, getParameters());
+        return new URL(getProtocol(), username, password, host, port, path, getParameters());
     }
 
     public String getBackupAddress() {
@@ -556,7 +557,7 @@ class URL implements Serializable {
     }
 
     public URL setPath(String path) {
-        return new URL(protocol, username, password, host, port, path, getParameters());
+        return new URL(getProtocol(), username, password, host, port, path, getParameters());
     }
 
     public String getAbsolutePath() {
@@ -1142,7 +1143,7 @@ class URL implements Serializable {
         Map<String, String> map = new HashMap<>(getParameters());
         map.put(key, value);
 
-        return new URL(protocol, username, password, host, port, path, map);
+        return new URL(getProtocol(), username, password, host, port, path, map);
     }
 
     public URL addParameterIfAbsent(String key, String value) {
@@ -1156,7 +1157,7 @@ class URL implements Serializable {
         Map<String, String> map = new HashMap<>(getParameters());
         map.put(key, value);
 
-        return new URL(protocol, username, password, host, port, path, map);
+        return new URL(getProtocol(), username, password, host, port, path, map);
     }
 
     public URL addMethodParameter(String method, String key, String value) {
@@ -1171,7 +1172,7 @@ class URL implements Serializable {
         Map<String, Map<String, String>> methodMap = toMethodParameters(map);
         URL.putMethodParameter(method, key, value, methodMap);
 
-        return new URL(protocol, username, password, host, port, path, map, methodMap);
+        return new URL(getProtocol(), username, password, host, port, path, map, methodMap);
     }
 
     public URL addMethodParameterIfAbsent(String method, String key, String value) {
@@ -1189,7 +1190,7 @@ class URL implements Serializable {
         Map<String, Map<String, String>> methodMap = toMethodParameters(map);
         URL.putMethodParameter(method, key, value, methodMap);
 
-        return new URL(protocol, username, password, host, port, path, map, methodMap);
+        return new URL(getProtocol(), username, password, host, port, path, map, methodMap);
     }
 
     /**
@@ -1225,7 +1226,7 @@ class URL implements Serializable {
 
         Map<String, String> map = new HashMap<>(getParameters());
         map.putAll(parameters);
-        return new URL(protocol, username, password, host, port, path, map);
+        return new URL(getProtocol(), username, password, host, port, path, map);
     }
 
     public URL addParametersIfAbsent(Map<String, String> parameters) {
@@ -1234,7 +1235,7 @@ class URL implements Serializable {
         }
         Map<String, String> map = new HashMap<>(parameters);
         map.putAll(getParameters());
-        return new URL(protocol, username, password, host, port, path, map);
+        return new URL(getProtocol(), username, password, host, port, path, map);
     }
 
     public URL addParameters(String... pairs) {
@@ -1284,11 +1285,11 @@ class URL implements Serializable {
         if (map.size() == getParameters().size()) {
             return this;
         }
-        return new URL(protocol, username, password, host, port, path, map);
+        return new URL(getProtocol(), username, password, host, port, path, map);
     }
 
     public URL clearParameters() {
-        return new URL(protocol, username, password, host, port, path, new HashMap<>());
+        return new URL(getProtocol(), username, password, host, port, path, new HashMap<>());
     }
 
     public String getRawParameter(String key) {
@@ -1525,6 +1526,14 @@ class URL implements Serializable {
         return BaseServiceMetadata.buildServiceKey(path, group, version);
     }
 
+    public String getProtocolServiceKey() {
+        if (protocolServiceKey != null) {
+            return protocolServiceKey;
+        }
+        this.protocolServiceKey = getServiceKey() + ":" + getProtocol();
+        return protocolServiceKey;
+    }
+
     public String toServiceStringWithoutResolving() {
         return buildString(true, false, false, true);
     }
@@ -1701,4 +1710,16 @@ class URL implements Serializable {
         subParameter.put(key, value);
     }
 
+//    public String getServiceParameter(String service, String key) {
+//        return getParameter(key);
+//    }
+//
+//    public String getServiceMethodParameter(String service, String key) {
+//        return getParameter(key);
+//    }
+//
+//    public String getServiceParameter(String service, String key) {
+//        return getParameter(key);
+//    }
+
 }
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/config/ConfigurationUtils.java b/dubbo-common/src/main/java/org/apache/dubbo/common/config/ConfigurationUtils.java
index b0447c8..70fd9f1 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/config/ConfigurationUtils.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/config/ConfigurationUtils.java
@@ -97,7 +97,7 @@ public class ConfigurationUtils {
     }
 
     public static String getProperty(String property, String defaultValue) {
-        return StringUtils.trim(ApplicationModel.getEnvironment().getConfiguration().getString(property, defaultValue));
+        return StringUtils.trim(getGlobalConfiguration().getString(property, defaultValue));
     }
 
     public static Map<String, String> parseProperties(String content) throws IOException {
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/manager/DefaultExecutorRepository.java b/dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/manager/DefaultExecutorRepository.java
index dd37bff..1669089 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/manager/DefaultExecutorRepository.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/threadpool/manager/DefaultExecutorRepository.java
@@ -55,10 +55,10 @@ public class DefaultExecutorRepository implements ExecutorRepository {
     private ConcurrentMap<String, ConcurrentMap<Integer, ExecutorService>> data = new ConcurrentHashMap<>();
 
     public DefaultExecutorRepository() {
-//        for (int i = 0; i < DEFAULT_SCHEDULER_SIZE; i++) {
-//            ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(new NamedThreadFactory("Dubbo-framework-scheduler"));
-//            scheduledExecutors.addItem(scheduler);
-//        }
+        for (int i = 0; i < DEFAULT_SCHEDULER_SIZE; i++) {
+            ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(new NamedThreadFactory("Dubbo-framework-scheduler"));
+            scheduledExecutors.addItem(scheduler);
+        }
 //
 //        reconnectScheduledExecutor = Executors.newSingleThreadScheduledExecutor(new NamedThreadFactory("Dubbo-reconnect-scheduler"));
         serviceExporterExecutor = Executors.newScheduledThreadPool(1, new NamedThreadFactory("Dubbo-exporter-scheduler"));
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/MetadataReportConfig.java b/dubbo-common/src/main/java/org/apache/dubbo/config/MetadataReportConfig.java
index dcd43e2..c3f8c93 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/config/MetadataReportConfig.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/config/MetadataReportConfig.java
@@ -79,6 +79,11 @@ public class MetadataReportConfig extends AbstractConfig {
      */
     private Boolean cluster;
 
+    /**
+     * registry id
+     */
+    private String registry;
+
     public MetadataReportConfig() {
     }
 
@@ -211,4 +216,12 @@ public class MetadataReportConfig extends AbstractConfig {
     public void setCluster(Boolean cluster) {
         this.cluster = cluster;
     }
+
+    public String getRegistry() {
+        return registry;
+    }
+
+    public void setRegistry(String registry) {
+        this.registry = registry;
+    }
 }
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/RegistryConfig.java b/dubbo-common/src/main/java/org/apache/dubbo/config/RegistryConfig.java
index 3b65b40..817bbc7 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/config/RegistryConfig.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/config/RegistryConfig.java
@@ -194,6 +194,11 @@ public class RegistryConfig extends AbstractConfig {
         setProtocol(protocol);
     }
 
+    @Override
+    public String getId() {
+        return super.getId();
+    }
+
     public String getProtocol() {
         return protocol;
     }
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 23f7bf6..f38b80b 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
@@ -430,6 +430,12 @@ public class ReferenceConfig<T> extends ReferenceConfigBase<T> {
         }
         // get consumer's global configuration
         checkDefault();
+
+        // init some null configuration.
+        List<ConfigInitializer> configInitializers = ExtensionLoader.getExtensionLoader(ConfigInitializer.class)
+                .getActivateExtension(URL.valueOf("configInitializer://"), (String[]) null);
+        configInitializers.forEach(e -> e.initReferConfig(this));
+
         this.refresh();
         if (getGeneric() == null && getConsumer() != null) {
             setGeneric(getConsumer().getGeneric());
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 dd28b81..455535c6 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
@@ -58,6 +58,7 @@ import org.apache.dubbo.config.utils.ReferenceConfigCache;
 import org.apache.dubbo.event.EventDispatcher;
 import org.apache.dubbo.event.EventListener;
 import org.apache.dubbo.event.GenericEventListener;
+import org.apache.dubbo.metadata.MetadataInfo;
 import org.apache.dubbo.metadata.MetadataService;
 import org.apache.dubbo.metadata.MetadataServiceExporter;
 import org.apache.dubbo.metadata.WritableMetadataService;
@@ -82,6 +83,7 @@ import java.util.SortedSet;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.locks.Condition;
 import java.util.concurrent.locks.Lock;
@@ -98,8 +100,10 @@ import static org.apache.dubbo.common.constants.CommonConstants.REMOTE_METADATA_
 import static org.apache.dubbo.common.function.ThrowableAction.execute;
 import static org.apache.dubbo.common.utils.StringUtils.isNotEmpty;
 import static org.apache.dubbo.metadata.WritableMetadataService.getDefaultExtension;
+import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.EXPORTED_SERVICES_REVISION_PROPERTY_NAME;
 import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.setMetadataStorageType;
 import static org.apache.dubbo.remoting.Constants.CLIENT_KEY;
+import static org.apache.dubbo.rpc.Constants.ID_KEY;
 
 /**
  * See {@link ApplicationModel} and {@link ExtensionLoader} for why this class is designed to be singleton.
@@ -1020,10 +1024,26 @@ public class DubboBootstrap extends GenericEventListener {
 
         ServiceInstance serviceInstance = createServiceInstance(serviceName, host, port);
 
-        // register metadata
         publishMetadataToRemote(serviceInstance);
 
-        getServiceDiscoveries().forEach(serviceDiscovery -> serviceDiscovery.register(serviceInstance));
+        getServiceDiscoveries().forEach(serviceDiscovery ->
+        {
+            calInstanceRevision(serviceDiscovery, serviceInstance);
+            // register metadata
+            serviceDiscovery.register(serviceInstance);
+        });
+
+        // scheduled task for updating Metadata and ServiceInstance
+        executorRepository.nextScheduledExecutor().scheduleAtFixedRate(() -> {
+            publishMetadataToRemote(serviceInstance);
+
+            getServiceDiscoveries().forEach(serviceDiscovery ->
+            {
+                calInstanceRevision(serviceDiscovery, serviceInstance);
+                // register metadata
+                serviceDiscovery.register(serviceInstance);
+            });
+        }, 0, 5000, TimeUnit.MICROSECONDS);
     }
 
     private void publishMetadataToRemote(ServiceInstance serviceInstance) {
@@ -1031,6 +1051,14 @@ public class DubboBootstrap extends GenericEventListener {
         remoteMetadataService.publishMetadata(serviceInstance);
     }
 
+    private void calInstanceRevision(ServiceDiscovery serviceDiscovery, ServiceInstance instance) {
+        String registryCluster = serviceDiscovery.getUrl().getParameter(ID_KEY);
+        MetadataInfo metadataInfo = WritableMetadataService.getDefaultExtension().getMetadataInfos().get(registryCluster);
+        if (metadataInfo != null) {
+            instance.getMetadata().put(EXPORTED_SERVICES_REVISION_PROPERTY_NAME, metadataInfo.getRevision());
+        }
+    }
+
     private URL selectMetadataServiceExportedURL() {
 
         URL selectedURL = null;
diff --git a/dubbo-config/dubbo-config-spring/src/main/resources/META-INF/compat/dubbo.xsd b/dubbo-config/dubbo-config-spring/src/main/resources/META-INF/compat/dubbo.xsd
index 1805b2a..ab229eb 100644
--- a/dubbo-config/dubbo-config-spring/src/main/resources/META-INF/compat/dubbo.xsd
+++ b/dubbo-config/dubbo-config-spring/src/main/resources/META-INF/compat/dubbo.xsd
@@ -690,6 +690,11 @@
                 <xsd:documentation><![CDATA[ Need cluster support, default false. ]]></xsd:documentation>
             </xsd:annotation>
         </xsd:attribute>
+        <xsd:attribute name="registry" type="xsd:string" use="optional">
+            <xsd:annotation>
+                <xsd:documentation><![CDATA[ registry config id. ]]></xsd:documentation>
+            </xsd:annotation>
+        </xsd:attribute>
     </xsd:complexType>
 
     <xsd:complexType name="configCenterType">
diff --git a/dubbo-config/dubbo-config-spring/src/main/resources/META-INF/dubbo.xsd b/dubbo-config/dubbo-config-spring/src/main/resources/META-INF/dubbo.xsd
index b12d815..a79ebb4 100644
--- a/dubbo-config/dubbo-config-spring/src/main/resources/META-INF/dubbo.xsd
+++ b/dubbo-config/dubbo-config-spring/src/main/resources/META-INF/dubbo.xsd
@@ -685,6 +685,11 @@
                 <xsd:documentation><![CDATA[ Need cluster support, default false. ]]></xsd:documentation>
             </xsd:annotation>
         </xsd:attribute>
+        <xsd:attribute name="registry" type="xsd:string" use="optional">
+            <xsd:annotation>
+                <xsd:documentation><![CDATA[ registry config id. ]]></xsd:documentation>
+            </xsd:annotation>
+        </xsd:attribute>
     </xsd:complexType>
 
     <xsd:complexType name="configCenterType">
diff --git a/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-consumer/src/main/resources/spring/dubbo-consumer.xml b/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-consumer/src/main/resources/spring/dubbo-consumer.xml
index 17aa9d6..bec81fe 100644
--- a/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-consumer/src/main/resources/spring/dubbo-consumer.xml
+++ b/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-consumer/src/main/resources/spring/dubbo-consumer.xml
@@ -21,11 +21,11 @@
        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
        http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
 
-    <dubbo:application metadata-type="remote" name="demo-consumer">
+    <dubbo:application name="demo-consumer">
         <dubbo:parameter key="mapping-type" value="metadata"/>
     </dubbo:application>
 
-    <dubbo:metadata-report address="zookeeper://127.0.0.1:2181"/>
+    <!--    <dubbo:metadata-report address="zookeeper://127.0.0.1:2181"/>-->
 
     <dubbo:registry address="zookeeper://127.0.0.1:2181?registry-type=service"/>
 
diff --git a/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-provider/src/main/resources/spring/dubbo-provider.xml b/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-provider/src/main/resources/spring/dubbo-provider.xml
index d7ab19a..90a6ae3 100644
--- a/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-provider/src/main/resources/spring/dubbo-provider.xml
+++ b/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-provider/src/main/resources/spring/dubbo-provider.xml
@@ -21,18 +21,18 @@
        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
        http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
 
-    <dubbo:application metadata-type="remote" name="demo-provider">
+    <dubbo:application name="demo-provider">
         <dubbo:parameter key="mapping-type" value="metadata"/>
     </dubbo:application>
 
+    <dubbo:config-center address="zookeeper://127.0.0.1:2181"/>
     <dubbo:metadata-report address="zookeeper://127.0.0.1:2181"/>
-
-    <dubbo:registry address="zookeeper://127.0.0.1:2181?registry-type=service"/>
+    <dubbo:registry id="registry1" address="zookeeper://127.0.0.1:2181?registry-type=service"/>
 
     <dubbo:protocol name="dubbo"/>
 
     <bean id="demoService" class="org.apache.dubbo.demo.provider.DemoServiceImpl"/>
 
-    <dubbo:service interface="org.apache.dubbo.demo.DemoService" ref="demoService"/>
+    <dubbo:service interface="org.apache.dubbo.demo.DemoService" ref="demoService" registry="registry1"/>
 
 </beans>
diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/DynamicConfigurationServiceNameMapping.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/DynamicConfigurationServiceNameMapping.java
index a25d07a..21df199 100644
--- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/DynamicConfigurationServiceNameMapping.java
+++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/DynamicConfigurationServiceNameMapping.java
@@ -72,7 +72,7 @@ public class DynamicConfigurationServiceNameMapping implements ServiceNameMappin
     }
 
     @Override
-    public Set<String> get(URL url) {
+    public Set<String> get(URL url, MappingListener mappingListener) {
         String serviceInterface = url.getServiceInterface();
         String group = url.getParameter(GROUP_KEY);
         String version = url.getParameter(VERSION_KEY);
diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/config/AbstractPrefixConfigurationTest.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MappingChangedEvent.java
similarity index 63%
copy from dubbo-common/src/test/java/org/apache/dubbo/common/config/AbstractPrefixConfigurationTest.java
copy to dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MappingChangedEvent.java
index ba4b5b8..27a7a3f 100644
--- a/dubbo-common/src/test/java/org/apache/dubbo/common/config/AbstractPrefixConfigurationTest.java
+++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MappingChangedEvent.java
@@ -14,10 +14,27 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.common.config;
+package org.apache.dubbo.metadata;
 
-/**
- *
- */
-public class AbstractPrefixConfigurationTest {
+import java.util.Set;
+
+public class MappingChangedEvent {
+    private String serviceKey;
+    private Set<String> apps;
+
+    public String getServiceKey() {
+        return serviceKey;
+    }
+
+    public void setServiceKey(String serviceKey) {
+        this.serviceKey = serviceKey;
+    }
+
+    public Set<String> getApps() {
+        return apps;
+    }
+
+    public void setApps(Set<String> apps) {
+        this.apps = apps;
+    }
 }
diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/config/AbstractPrefixConfigurationTest.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MappingListener.java
similarity index 87%
copy from dubbo-common/src/test/java/org/apache/dubbo/common/config/AbstractPrefixConfigurationTest.java
copy to dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MappingListener.java
index ba4b5b8..f709d75 100644
--- a/dubbo-common/src/test/java/org/apache/dubbo/common/config/AbstractPrefixConfigurationTest.java
+++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MappingListener.java
@@ -14,10 +14,8 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.common.config;
+package org.apache.dubbo.metadata;
 
-/**
- *
- */
-public class AbstractPrefixConfigurationTest {
+public interface MappingListener {
+    void onEvent(MappingChangedEvent event);
 }
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 f235655..932517d 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
@@ -42,15 +42,17 @@ public class MetadataInfo implements Serializable {
     private String revision;
     private Map<String, ServiceInfo> services;
 
+    private transient Map<String, String> extendParams;
+
     public MetadataInfo(String app) {
-        this.app = app;
-        this.services = new HashMap<>();
+        this(app, null, null);
     }
 
     public MetadataInfo(String app, String revision, Map<String, ServiceInfo> services) {
         this.app = app;
         this.revision = revision;
         this.services = services == null ? new HashMap<>() : services;
+        this.extendParams = new HashMap<>();
     }
 
     public void addService(ServiceInfo serviceInfo) {
@@ -111,6 +113,10 @@ public class MetadataInfo implements Serializable {
         return services.get(serviceKey);
     }
 
+    public Map<String, String> getExtendParams() {
+        return extendParams;
+    }
+
     public String getParameter(String key, String serviceKey) {
         ServiceInfo serviceInfo = services.get(serviceKey);
         if (serviceInfo == null) {
@@ -133,7 +139,6 @@ public class MetadataInfo implements Serializable {
         private String group;
         private String version;
         private String protocol;
-        private String registry;
         private Map<String, String> params;
 
         private transient Map<String, Map<String, String>> methodParams;
@@ -150,7 +155,6 @@ public class MetadataInfo implements Serializable {
                     url.getParameter(GROUP_KEY),
                     url.getParameter(VERSION_KEY),
                     url.getProtocol(),
-                    "",
                     null
             );
 
@@ -179,12 +183,11 @@ public class MetadataInfo implements Serializable {
             this.params = params;
         }
 
-        public ServiceInfo(String name, String group, String version, String protocol, String registry, Map<String, String> params) {
+        public ServiceInfo(String name, String group, String version, String protocol, Map<String, String> params) {
             this.name = name;
             this.group = group;
             this.version = version;
             this.protocol = protocol;
-            this.registry = registry;
             this.params = params == null ? new HashMap<>() : params;
 
             this.serviceKey = URL.buildKey(name, group, version);
@@ -204,9 +207,6 @@ public class MetadataInfo implements Serializable {
             if (StringUtils.isNotEmpty(protocol)) {
                 matchKey = getServiceKey() + GROUP_CHAR_SEPERATOR + protocol;
             }
-            if (StringUtils.isNotEmpty(registry)) {
-                matchKey = getServiceKey() + GROUP_CHAR_SEPERATOR + registry;
-            }
             return matchKey;
         }
 
@@ -242,22 +242,6 @@ public class MetadataInfo implements Serializable {
             this.version = version;
         }
 
-        public String getProtocol() {
-            return protocol;
-        }
-
-        public void setProtocol(String protocol) {
-            this.protocol = protocol;
-        }
-
-        public String getRegistry() {
-            return registry;
-        }
-
-        public void setRegistry(String registry) {
-            this.registry = registry;
-        }
-
         public Map<String, String> getParams() {
             if (params == null) {
                 return Collections.emptyMap();
@@ -283,6 +267,9 @@ public class MetadataInfo implements Serializable {
             if (keyMap != null) {
                 value = keyMap.get(key);
             }
+            if (StringUtils.isEmpty(value)) {
+                value = getParameter(key);
+            }
             return value == null ? defaultValue : value;
         }
 
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 ba9e50b..761cd88 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
@@ -19,6 +19,7 @@ package org.apache.dubbo.metadata;
 import org.apache.dubbo.common.URL;
 
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 import java.util.SortedSet;
 import java.util.TreeSet;
@@ -177,7 +178,9 @@ public interface MetadataService {
      */
     String getServiceDefinition(String serviceKey);
 
-    MetadataInfo getMetadataInfo();
+    MetadataInfo getMetadataInfo(String revision);
+
+    Map<String, MetadataInfo> getMetadataInfos();
 
     /**
      * 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/MetadataServiceNameMapping.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataServiceNameMapping.java
index 9c0c600..5389e63 100644
--- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataServiceNameMapping.java
+++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataServiceNameMapping.java
@@ -23,6 +23,7 @@ import org.apache.dubbo.metadata.report.MetadataReportInstance;
 
 import java.util.LinkedHashSet;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 
 import static java.util.Arrays.asList;
@@ -44,23 +45,27 @@ public class MetadataServiceNameMapping implements ServiceNameMapping {
             return;
         }
 
-        List<MetadataReport> metadataReports = MetadataReportInstance.getMetadataReports(true);
-        metadataReports.forEach(reporter -> {
+        Map<String, MetadataReport> metadataReports = MetadataReportInstance.getMetadataReports(true);
+        metadataReports.forEach((key, reporter) -> {
             reporter.registerServiceAppMapping(ServiceNameMapping.buildGroup(serviceInterface, group, version, protocol), getName(), url);
         });
     }
 
     @Override
-    public Set<String> get(URL url) {
+    public Set<String> get(URL url, MappingListener mappingListener) {
         String serviceInterface = url.getServiceInterface();
         String group = url.getParameter(GROUP_KEY);
         String version = url.getParameter(VERSION_KEY);
         String protocol = url.getProtocol();
 
-        List<MetadataReport> metadataReports = MetadataReportInstance.getMetadataReports(true);
+        Map<String, MetadataReport> metadataReports = MetadataReportInstance.getMetadataReports(true);
         Set<String> serviceNames = new LinkedHashSet<>();
-        for (MetadataReport reporter : metadataReports) {
-            Set<String> apps = reporter.getServiceAppMapping(ServiceNameMapping.buildGroup(serviceInterface, group, version, protocol), url);
+        for (Map.Entry<String, MetadataReport> entry : metadataReports.entrySet()) {
+            MetadataReport reporter = entry.getValue();
+            Set<String> apps = reporter.getServiceAppMapping(
+                    ServiceNameMapping.buildGroup(serviceInterface, group, version, protocol),
+                    mappingListener,
+                    url);
             if (CollectionUtils.isNotEmpty(apps)) {
                 serviceNames.addAll(apps);
                 break;
diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/ServiceNameMapping.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/ServiceNameMapping.java
index 44d55c5..b4ad31e 100644
--- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/ServiceNameMapping.java
+++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/ServiceNameMapping.java
@@ -44,7 +44,7 @@ public interface ServiceNameMapping {
      *
      * @return
      */
-    Set<String> get(URL url);
+    Set<String> get(URL url, MappingListener mappingListener);
 
 
     /**
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 0de9683..b870fff 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
@@ -75,6 +75,7 @@ public interface WritableMetadataService extends MetadataService {
 
     void publishServiceDefinition(URL providerUrl);
 
+
     /**
      * Get {@link ExtensionLoader#getDefaultExtension() the defautl extension} of {@link WritableMetadataService}
      *
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 256ae6e..012c162 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.MappingListener;
 import org.apache.dubbo.metadata.MetadataInfo;
 import org.apache.dubbo.metadata.definition.model.ServiceDefinition;
 import org.apache.dubbo.metadata.report.identifier.MetadataIdentifier;
@@ -30,7 +31,6 @@ import java.util.Map;
 import java.util.Set;
 
 public interface MetadataReport {
-
     /**
      * Service Definition -- START
      **/
@@ -51,7 +51,7 @@ public interface MetadataReport {
     /**
      * Service<-->Application Mapping -- START
      **/
-    default Set<String> getServiceAppMapping(String serviceKey, URL url) {
+    default Set<String> getServiceAppMapping(String serviceKey, MappingListener listener, URL url) {
         return Collections.emptySet();
     }
 
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 4b66fe4..c3afdb0 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
@@ -21,11 +21,12 @@ 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.HashMap;
+import java.util.Map;
 import java.util.concurrent.atomic.AtomicBoolean;
 
 import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_DIRECTORY;
+import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_KEY;
 import static org.apache.dubbo.metadata.report.support.Constants.METADATA_REPORT_KEY;
 
 /**
@@ -35,7 +36,7 @@ public class MetadataReportInstance {
 
     private static AtomicBoolean init = new AtomicBoolean(false);
 
-    private static final List<MetadataReport> metadataReports = new ArrayList<>();
+    private static final Map<String, MetadataReport> metadataReports = new HashMap<>();
 
     public static void init(MetadataReportConfig config) {
         if (init.get()) {
@@ -50,15 +51,14 @@ public class MetadataReportInstance {
                     .removeParameter(METADATA_REPORT_KEY)
                     .build();
         }
-        metadataReports.add(metadataReportFactory.getMetadataReport(url));
+        String relatedRegistryId = config.getRegistry() == null ? DEFAULT_KEY : config.getRegistry();
+//        RegistryConfig registryConfig = ApplicationModel.getConfigManager().getRegistry(relatedRegistryId)
+//                .orElseThrow(() -> new IllegalStateException("Registry id " + relatedRegistryId + " does not exist."));
+        metadataReports.put(relatedRegistryId, metadataReportFactory.getMetadataReport(url));
         init.set(true);
     }
 
-    public static List<MetadataReport> getMetadataReports() {
-        return getMetadataReports(false);
-    }
-
-    public static List<MetadataReport> getMetadataReports(boolean checked) {
+    public static Map<String, MetadataReport> getMetadataReports(boolean checked) {
         if (checked) {
             checkInit();
         }
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/NotifyListener.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/NotifyListener.java
index 5b54000..89e3e75 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/NotifyListener.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/NotifyListener.java
@@ -45,6 +45,4 @@ public interface NotifyListener {
     default void addServiceListener(ServiceInstancesChangedListener instanceListener) {
     }
 
-    default void notifyServiceInstances() {
-    }
 }
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/config/AbstractPrefixConfigurationTest.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/DefaultRegistryClusterIdentifier.java
similarity index 58%
copy from dubbo-common/src/test/java/org/apache/dubbo/common/config/AbstractPrefixConfigurationTest.java
copy to dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/DefaultRegistryClusterIdentifier.java
index ba4b5b8..b225c06 100644
--- a/dubbo-common/src/test/java/org/apache/dubbo/common/config/AbstractPrefixConfigurationTest.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/DefaultRegistryClusterIdentifier.java
@@ -14,10 +14,23 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.common.config;
+package org.apache.dubbo.registry.client;
 
-/**
- *
- */
-public class AbstractPrefixConfigurationTest {
+import org.apache.dubbo.common.URL;
+
+import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_KEY;
+
+public class DefaultRegistryClusterIdentifier implements RegistryClusterIdentifier {
+    @Override
+    public String providerKey(URL url) {
+        // url.getParameter("registry_cluster");
+        // ServiceMetadata.get("registry_cluster");
+        // return;
+        return url.getParameter(REGISTRY_KEY);
+    }
+
+    @Override
+    public String consumerKey(URL url) {
+        return url.getParameter(REGISTRY_KEY);
+    }
 }
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/DefaultServiceInstance.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/DefaultServiceInstance.java
index b394024..e44bd4f 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/DefaultServiceInstance.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/DefaultServiceInstance.java
@@ -16,7 +16,6 @@
  */
 package org.apache.dubbo.registry.client;
 
-import org.apache.dubbo.common.URL;
 import org.apache.dubbo.metadata.MetadataInfo;
 
 import java.util.HashMap;
@@ -46,7 +45,9 @@ public class DefaultServiceInstance implements ServiceInstance {
 
     private Map<String, String> metadata = new HashMap<>();
 
+    private transient String address;
     private transient MetadataInfo serviceMetadata;
+    private transient Map<String, String> extendParams = new HashMap<>();
 
     public DefaultServiceInstance() {
     }
@@ -104,6 +105,18 @@ public class DefaultServiceInstance implements ServiceInstance {
     }
 
     @Override
+    public String getAddress() {
+        if (address == null) {
+            address = getAddress(host, port);
+        }
+        return address;
+    }
+
+    private static String getAddress(String host, int port) {
+        return port <= 0 ? host : host + ':' + port;
+    }
+
+    @Override
     public boolean isEnabled() {
         return enabled;
     }
@@ -126,6 +139,11 @@ public class DefaultServiceInstance implements ServiceInstance {
         return metadata;
     }
 
+    @Override
+    public Map<String, String> getExtendParams() {
+        return extendParams;
+    }
+
     public void setMetadata(Map<String, String> metadata) {
         this.metadata = metadata;
     }
@@ -139,10 +157,8 @@ public class DefaultServiceInstance implements ServiceInstance {
     }
 
     @Override
-    public URL toURL(String protocol, String path, String interfaceName, String group, String version, String serviceKey) {
-        InstanceAddressURL url = new InstanceAddressURL(protocol, host, port, path, interfaceName, group, version, serviceKey);
-        url.setMetadata(this.getServiceMetadata());
-        return url;
+    public InstanceAddressURL toURL() {
+        return new InstanceAddressURL(this, serviceMetadata);
     }
 
     @Override
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/EventPublishingServiceDiscovery.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/EventPublishingServiceDiscovery.java
index b3a8b92..b5517b7 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/EventPublishingServiceDiscovery.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/EventPublishingServiceDiscovery.java
@@ -224,6 +224,11 @@ final class EventPublishingServiceDiscovery implements ServiceDiscovery {
     }
 
     @Override
+    public URL getUrl() {
+        return serviceDiscovery.getUrl();
+    }
+
+    @Override
     public void initialize(URL registryURL) {
 
         assertInitialized(INITIALIZE_ACTION);
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/FileSystemServiceDiscovery.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/FileSystemServiceDiscovery.java
index 82bc860..ba8d7d3 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/FileSystemServiceDiscovery.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/FileSystemServiceDiscovery.java
@@ -129,6 +129,11 @@ public class FileSystemServiceDiscovery implements ServiceDiscovery, EventListen
     }
 
     @Override
+    public URL getUrl() {
+        return null;
+    }
+
+    @Override
     public void register(ServiceInstance serviceInstance) throws RuntimeException {
         String serviceInstanceId = getServiceInstanceId(serviceInstance);
         String serviceName = getServiceName(serviceInstance);
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/InstanceAddressURL.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/InstanceAddressURL.java
index 6ec4df2..18632bf 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/InstanceAddressURL.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/InstanceAddressURL.java
@@ -19,6 +19,7 @@ package org.apache.dubbo.registry.client;
 import org.apache.dubbo.common.URL;
 import org.apache.dubbo.common.utils.StringUtils;
 import org.apache.dubbo.metadata.MetadataInfo;
+import org.apache.dubbo.rpc.RpcContext;
 
 import java.util.HashMap;
 import java.util.Map;
@@ -28,37 +29,45 @@ import static org.apache.dubbo.common.constants.CommonConstants.INTERFACE_KEY;
 import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY;
 
 public class InstanceAddressURL extends URL {
-
-    private MetadataInfo metadataInfo; // points to metadata of one revision
-    private String interfaceName;
-    private String group;
-    private String version;
-    private String serviceKey;
-
-    public InstanceAddressURL(String protocol, String host, int port, String path, String interfaceName, String group, String version, String serviceKey) {
-        super(protocol, host, port, path);
-        this.interfaceName = interfaceName;
-        this.group = group;
-        this.version = version;
-        this.serviceKey = serviceKey;
+    private ServiceInstance instance;
+    private MetadataInfo metadataInfo;
+
+    public InstanceAddressURL(
+            ServiceInstance instance,
+            MetadataInfo metadataInfo
+    ) {
+        this.instance = instance;
+        this.metadataInfo = metadataInfo;
+        this.setHost(instance.getHost());
+        this.setPort(instance.getPort());
     }
 
     @Override
     public String getServiceInterface() {
-        return interfaceName;
+        return RpcContext.getContext().getInterfaceName();
     }
 
     public String getGroup() {
-        return group;
+        return RpcContext.getContext().getGroup();
     }
 
     public String getVersion() {
-        return version;
+        return RpcContext.getContext().getVersion();
+    }
+
+    @Override
+    public String getProtocol() {
+        return RpcContext.getContext().getProtocol();
     }
 
     @Override
     public String getServiceKey() {
-        return serviceKey;
+        return RpcContext.getContext().getServiceKey();
+    }
+
+    @Override
+    public String getAddress() {
+        return instance.getAddress();
     }
 
     @Override
@@ -71,9 +80,12 @@ public class InstanceAddressURL extends URL {
             return getServiceInterface();
         }
 
-        String value = super.getParameter(key);
+        String value = getConsumerParameters().get(key);
+        if (StringUtils.isEmpty(value)) {
+            value = getInstanceMetadata().get(key);
+        }
         if (StringUtils.isEmpty(value) && metadataInfo != null) {
-            value = metadataInfo.getParameter(key, this.getServiceKey());
+            value = metadataInfo.getParameter(key, RpcContext.getContext().getProtocolServiceKey());
         }
         return value;
     }
@@ -97,22 +109,18 @@ public class InstanceAddressURL extends URL {
 
     @Override
     public String getMethodParameter(String method, String key) {
-        Map<String, Map<String, String>> instanceMethodParams = super.getMethodParameters();
-        Map<String, String> keyMap = instanceMethodParams.get(method);
-        String value = null;
-        if (keyMap != null) {
-            value = keyMap.get(key);
+        String value = getMethodParameter(method, key);
+        if (StringUtils.isNotEmpty(value)) {
+            return value;
         }
-
         MetadataInfo.ServiceInfo serviceInfo = metadataInfo.getServiceInfo(getServiceKey());
-        value = serviceInfo.getMethodParameter(method, key, value);
-        return value;
+        return serviceInfo.getMethodParameter(method, key, null);
     }
 
     @Override
     public Map<String, String> getParameters() {
-        Map<String, String> instanceParams = super.getParameters();
-        Map<String, String> metadataParams = (metadataInfo == null ? new HashMap<>() : metadataInfo.getParameters(getServiceKey()));
+        Map<String, String> instanceParams = getInstanceMetadata();
+        Map<String, String> metadataParams = (metadataInfo == null ? new HashMap<>() : metadataInfo.getParameters(RpcContext.getContext().getProtocolServiceKey()));
         int i = instanceParams == null ? 0 : instanceParams.size();
         int j = metadataParams == null ? 0 : metadataParams.size();
         Map<String, String> params = new HashMap<>((int) ((i + j) / 0.75) + 1);
@@ -122,11 +130,41 @@ public class InstanceAddressURL extends URL {
         if (metadataParams != null) {
             params.putAll(metadataParams);
         }
+
+        params.putAll(getConsumerParameters());
         return params;
     }
 
-    public void setMetadata(MetadataInfo metadataInfo) {
-        this.metadataInfo = metadataInfo;
+    private Map<String, String> getInstanceMetadata() {
+        return this.instance.getMetadata();
     }
 
+    private Map<String, String> getConsumerParameters() {
+        return RpcContext.getContext().getConsumerUrl().getParameters();
+    }
+
+    private String getConsumerParameter(String key) {
+        return RpcContext.getContext().getConsumerUrl().getParameter(key);
+    }
+
+    private String getConsumerMethodParameter(String method, String key) {
+        return RpcContext.getContext().getConsumerUrl().getMethodParameter(method, key);
+    }
+
+    @Override
+    public URL addParameter(String key, String value) {
+        throw new UnsupportedOperationException("");
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        // instance metadata equals
+        // service metadata equals
+        return super.equals(obj);
+    }
+
+    @Override
+    public int hashCode() {
+        return super.hashCode();
+    }
 }
diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/config/AbstractPrefixConfigurationTest.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/RegistryClusterIdentifier.java
similarity index 54%
copy from dubbo-common/src/test/java/org/apache/dubbo/common/config/AbstractPrefixConfigurationTest.java
copy to dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/RegistryClusterIdentifier.java
index ba4b5b8..b48b233 100644
--- a/dubbo-common/src/test/java/org/apache/dubbo/common/config/AbstractPrefixConfigurationTest.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/RegistryClusterIdentifier.java
@@ -14,10 +14,22 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.common.config;
+package org.apache.dubbo.registry.client;
 
-/**
- *
- */
-public class AbstractPrefixConfigurationTest {
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.config.ConfigurationUtils;
+import org.apache.dubbo.common.extension.ExtensionLoader;
+import org.apache.dubbo.common.extension.SPI;
+
+@SPI
+public interface RegistryClusterIdentifier {
+    String providerKey(URL url);
+
+    String consumerKey(URL url);
+
+    static RegistryClusterIdentifier getExtension() {
+        ExtensionLoader<RegistryClusterIdentifier> loader
+                = ExtensionLoader.getExtensionLoader(RegistryClusterIdentifier.class);
+        return loader.getExtension(ConfigurationUtils.getProperty("dubbo.application.sd.type", "default"));
+    }
 }
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscovery.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscovery.java
index bc73cd0..f9e667b 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscovery.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscovery.java
@@ -261,6 +261,12 @@ public interface ServiceDiscovery extends Prioritized {
 
     // ==================================================================================== //
 
+//    String getKey(URL exportedURL);
+
+    default URL getUrl() {
+        return null;
+    }
+
     /**
      * A human-readable description of the implementation
      *
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 878b9e6..9b129f9 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
@@ -23,6 +23,8 @@ 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.MappingChangedEvent;
+import org.apache.dubbo.metadata.MappingListener;
 import org.apache.dubbo.metadata.ServiceNameMapping;
 import org.apache.dubbo.metadata.WritableMetadataService;
 import org.apache.dubbo.registry.NotifyListener;
@@ -30,6 +32,7 @@ import org.apache.dubbo.registry.Registry;
 import org.apache.dubbo.registry.client.event.ServiceInstancesChangedEvent;
 import org.apache.dubbo.registry.client.event.listener.ServiceInstancesChangedListener;
 import org.apache.dubbo.registry.client.metadata.SubscribedURLsSynthesizer;
+import org.apache.dubbo.registry.support.AbstractRegistryFactory;
 import org.apache.dubbo.registry.support.FailbackRegistry;
 
 import java.util.ArrayList;
@@ -41,6 +44,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
+import java.util.TreeSet;
 import java.util.stream.Collectors;
 
 import static java.lang.String.format;
@@ -56,6 +60,7 @@ 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.VERSION_KEY;
 import static org.apache.dubbo.common.constants.RegistryConstants.PROVIDED_BY;
+import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_KEY;
 import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_TYPE_KEY;
 import static org.apache.dubbo.common.constants.RegistryConstants.SERVICE_REGISTRY_TYPE;
 import static org.apache.dubbo.common.constants.RegistryConstants.SUBSCRIBED_SERVICE_NAMES_KEY;
@@ -63,6 +68,7 @@ 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.rpc.Constants.ID_KEY;
 
 /**
  * Being different to the traditional registry, {@link ServiceDiscoveryRegistry} that is a new service-oriented
@@ -87,7 +93,7 @@ import static org.apache.dubbo.registry.client.ServiceDiscoveryFactory.getExtens
  * @see WritableMetadataService
  * @since 2.7.5
  */
-public class ServiceDiscoveryRegistry extends FailbackRegistry {
+public class ServiceDiscoveryRegistry implements Registry {
 
     protected final Logger logger = LoggerFactory.getLogger(getClass());
 
@@ -101,9 +107,11 @@ public class ServiceDiscoveryRegistry extends FailbackRegistry {
 
     private final Set<String> registeredListeners = new LinkedHashSet<>();
 
-    /* app - listener */
+    /* apps - listener */
     private final Map<String, ServiceInstancesChangedListener> serviceListeners = new HashMap<>();
 
+    private URL registryURL;
+
     /**
      * A cache for all URLs of services that the subscribed services exported
      * The key is the service name
@@ -112,7 +120,7 @@ public class ServiceDiscoveryRegistry extends FailbackRegistry {
     private final Map<String, Map<String, List<URL>>> serviceRevisionExportedURLsCache = new LinkedHashMap<>();
 
     public ServiceDiscoveryRegistry(URL registryURL) {
-        super(registryURL);
+        this.registryURL = registryURL;
         this.serviceDiscovery = createServiceDiscovery(registryURL);
         this.subscribedServices = parseServices(registryURL.getParameter(SUBSCRIBED_SERVICE_NAMES_KEY));
         this.serviceNameMapping = ServiceNameMapping.getExtension(registryURL.getParameter(MAPPING_KEY));
@@ -190,11 +198,14 @@ public class ServiceDiscoveryRegistry extends FailbackRegistry {
         if (!shouldRegister(url)) { // Should Not Register
             return;
         }
-        super.register(url);
+        doRegister(url);
     }
 
-    @Override
     public void doRegister(URL url) {
+        String registryCluster = serviceDiscovery.getUrl().getParameter(ID_KEY);
+        if (registryCluster != null) {
+            url = url.addParameter(REGISTRY_KEY, registryCluster);
+        }
         if (writableMetadataService.exportURL(url)) {
             if (logger.isInfoEnabled()) {
                 logger.info(format("The URL[%s] registered successfully.", url.toString()));
@@ -211,11 +222,14 @@ public class ServiceDiscoveryRegistry extends FailbackRegistry {
         if (!shouldRegister(url)) {
             return;
         }
-        super.unregister(url);
+        doUnregister(url);
     }
 
-    @Override
     public void doUnregister(URL url) {
+        String registryCluster = serviceDiscovery.getUrl().getParameter(ID_KEY);
+        if (registryCluster != null) {
+            url = url.addParameter(REGISTRY_KEY, registryCluster);
+        }
         if (writableMetadataService.unexportURL(url)) {
             if (logger.isInfoEnabled()) {
                 logger.info(format("The URL[%s] deregistered successfully.", url.toString()));
@@ -232,12 +246,22 @@ public class ServiceDiscoveryRegistry extends FailbackRegistry {
         if (!shouldSubscribe(url)) { // Should Not Subscribe
             return;
         }
-        super.subscribe(url, listener);
+        String registryCluster = serviceDiscovery.getUrl().getParameter(ID_KEY);
+        if (registryCluster != null) {
+            url = url.addParameter(REGISTRY_KEY, registryCluster);
+        }
+        doSubscribe(url, listener);
     }
 
-    @Override
     public void doSubscribe(URL url, NotifyListener listener) {
-        subscribeURLs(url, listener);
+        writableMetadataService.subscribeURL(url);
+
+        Set<String> serviceNames = getServices(url, listener);
+        if (CollectionUtils.isEmpty(serviceNames)) {
+            throw new IllegalStateException("Should has at least one way to know which services this interface belongs to, subscription url: " + url);
+        }
+
+        subscribeURLs(url, listener, serviceNames);
     }
 
     @Override
@@ -245,57 +269,56 @@ public class ServiceDiscoveryRegistry extends FailbackRegistry {
         if (!shouldSubscribe(url)) { // Should Not Subscribe
             return;
         }
-        super.unsubscribe(url, listener);
+        String registryCluster = serviceDiscovery.getUrl().getParameter(ID_KEY);
+        if (registryCluster != null) {
+            url = url.addParameter(REGISTRY_KEY, registryCluster);
+        }
+        doUnsubscribe(url, listener);
     }
 
-    @Override
     public void doUnsubscribe(URL url, NotifyListener listener) {
         writableMetadataService.unsubscribeURL(url);
     }
 
     @Override
+    public List<URL> lookup(URL url) {
+        throw new UnsupportedOperationException("");
+    }
+
+    @Override
+    public URL getUrl() {
+        return registryURL;
+    }
+
+    @Override
     public boolean isAvailable() {
         return !serviceDiscovery.getServices().isEmpty();
     }
 
     @Override
     public void destroy() {
-        super.destroy();
+        AbstractRegistryFactory.removeDestroyedRegistry(this);
         execute(() -> {
             // stop ServiceDiscovery
             serviceDiscovery.destroy();
         });
     }
 
-    protected void subscribeURLs(URL url, NotifyListener listener) {
-
-        writableMetadataService.subscribeURL(url);
-
-        Set<String> serviceNames = getServices(url);
-        if (CollectionUtils.isEmpty(serviceNames)) {
-            throw new IllegalStateException("Should has at least one way to know which services this interface belongs to, subscription url: " + url);
-        }
-
-        serviceNames.forEach(serviceName -> subscribeURLs(url, listener, serviceName));
-    }
-
-    protected void subscribeURLs(URL url, NotifyListener listener, String serviceName) {
+    protected void subscribeURLs(URL url, NotifyListener listener, Set<String> serviceNames) {
+        String serviceNamesKey = serviceNames.toString();
         // register ServiceInstancesChangedListener
-        ServiceInstancesChangedListener serviceListener = serviceListeners.computeIfAbsent(serviceName,
-                k -> new ServiceInstancesChangedListener(serviceName, serviceDiscovery, url) {
-                    @Override
-                    protected void notifyAddresses() {
-                        listener.notifyServiceInstances();
-                    }
-                });
+        ServiceInstancesChangedListener serviceListener = serviceListeners.computeIfAbsent(serviceNamesKey,
+                k -> new ServiceInstancesChangedListener(serviceNames, serviceDiscovery));
         serviceListener.setUrl(url);
         listener.addServiceListener(serviceListener);
 
-        // FIXME, sync notification
-        List<ServiceInstance> serviceInstances = serviceDiscovery.getInstances(serviceName);
-        serviceListener.onEvent(new ServiceInstancesChangedEvent(serviceName, serviceInstances));
-        listener.notifyServiceInstances();
+        serviceNames.forEach(serviceName -> {
+            List<ServiceInstance> serviceInstances = serviceDiscovery.getInstances(serviceName);
+            serviceListener.onEvent(new ServiceInstancesChangedEvent(serviceName, serviceInstances));
+        });
+        listener.notify(serviceListener.getUrls(url.getProtocolServiceKey()));
 
+        serviceListener.addListener(url.getProtocolServiceKey(), listener);
         registerServiceInstancesChangedListener(url, serviceListener);
     }
 
@@ -313,29 +336,10 @@ public class ServiceDiscoveryRegistry extends FailbackRegistry {
     }
 
     private String createListenerId(URL url, ServiceInstancesChangedListener listener) {
-        return listener.getServiceName() + ":" + url.toString(VERSION_KEY, GROUP_KEY, PROTOCOL_KEY);
-    }
-
-    private Map<String, List<URL>> getRevisionExportedURLsMap(String serviceName) {
-        return serviceRevisionExportedURLsCache.computeIfAbsent(serviceName, s -> new LinkedHashMap());
+        return listener.getServiceNames() + ":" + url.toString(VERSION_KEY, GROUP_KEY, PROTOCOL_KEY);
     }
 
     /**
-     * Synthesize new subscribed {@link URL URLs} from old one
-     *
-     * @param subscribedURL
-     * @param serviceInstances
-     * @return non-null
-     */
-//    private Collection<? extends URL> synthesizeSubscribedURLs(URL subscribedURL, Collection<ServiceInstance> serviceInstances) {
-//        return subscribedURLsSynthesizers.stream()
-//                .filter(synthesizer -> synthesizer.supports(subscribedURL))
-//                .map(synthesizer -> synthesizer.synthesize(subscribedURL, serviceInstances))
-//                .flatMap(Collection::stream)
-//                .collect(Collectors.toList());
-//    }
-
-    /**
      * 1.developer explicitly specifies the application name this interface belongs to
      * 2.check Interface-App mapping
      * 3.use the services specified in registry url.
@@ -343,18 +347,18 @@ public class ServiceDiscoveryRegistry extends FailbackRegistry {
      * @param subscribedURL
      * @return
      */
-    protected Set<String> getServices(URL subscribedURL) {
-        Set<String> subscribedServices = new LinkedHashSet<>();
+    protected Set<String> getServices(URL subscribedURL, final NotifyListener listener) {
+        Set<String> subscribedServices = new TreeSet<>();
 
         String serviceNames = subscribedURL.getParameter(PROVIDED_BY);
         if (StringUtils.isNotEmpty(serviceNames)) {
-            subscribedServices = parseServices(serviceNames);
+            subscribedServices.addAll(parseServices(serviceNames));
         }
 
         if (isEmpty(subscribedServices)) {
-            subscribedServices = findMappedServices(subscribedURL);
+            subscribedServices.addAll(findMappedServices(subscribedURL, new DefaultMappingListener(subscribedURL, subscribedServices, listener)));
             if (isEmpty(subscribedServices)) {
-                subscribedServices = getSubscribedServices();
+                subscribedServices.addAll(getSubscribedServices());
             }
         }
         return subscribedServices;
@@ -383,8 +387,8 @@ public class ServiceDiscoveryRegistry extends FailbackRegistry {
      * @param subscribedURL
      * @return
      */
-    protected Set<String> findMappedServices(URL subscribedURL) {
-        return serviceNameMapping.get(subscribedURL);
+    protected Set<String> findMappedServices(URL subscribedURL, MappingListener listener) {
+        return serviceNameMapping.get(subscribedURL, listener);
     }
 
     /**
@@ -433,4 +437,27 @@ public class ServiceDiscoveryRegistry extends FailbackRegistry {
         return protocol == null || Objects.equals(protocol, targetURL.getParameter(PROTOCOL_KEY))
                 || Objects.equals(protocol, targetURL.getProtocol());
     }
+
+    private class DefaultMappingListener implements MappingListener {
+        private URL url;
+        private Set<String> oldApps;
+        private NotifyListener listener;
+
+        public DefaultMappingListener(URL subscribedURL, Set<String> serviceNames, NotifyListener listener) {
+            this.url = subscribedURL;
+            this.oldApps = serviceNames;
+            this.listener = listener;
+        }
+
+        @Override
+        public void onEvent(MappingChangedEvent event) {
+            Set<String> newApps = event.getApps();
+            if (CollectionUtils.isEmpty(newApps)) {
+                return;
+            }
+            if (!CollectionUtils.equals(oldApps, newApps) && newApps.size() >= oldApps.size()) {
+                subscribeURLs(url, listener, newApps);
+            }
+        }
+    }
 }
\ No newline at end of file
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistryDirectory.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistryDirectory.java
new file mode 100644
index 0000000..9890988
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistryDirectory.java
@@ -0,0 +1,253 @@
+/*
+ * 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;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.extension.ExtensionLoader;
+import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.common.utils.Assert;
+import org.apache.dubbo.common.utils.CollectionUtils;
+import org.apache.dubbo.common.utils.NetUtils;
+import org.apache.dubbo.registry.NotifyListener;
+import org.apache.dubbo.registry.client.event.listener.ServiceInstancesChangedListener;
+import org.apache.dubbo.registry.integration.DynamicDirectory;
+import org.apache.dubbo.rpc.Invoker;
+import org.apache.dubbo.rpc.Protocol;
+import org.apache.dubbo.rpc.RpcContext;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.apache.dubbo.common.constants.CommonConstants.DISABLED_KEY;
+import static org.apache.dubbo.common.constants.CommonConstants.ENABLED_KEY;
+import static org.apache.dubbo.common.constants.RegistryConstants.EMPTY_PROTOCOL;
+
+public class ServiceDiscoveryRegistryDirectory<T> extends DynamicDirectory<T> implements NotifyListener {
+    private static final Logger logger = LoggerFactory.getLogger(ServiceDiscoveryRegistryDirectory.class);
+
+    // Map<url, Invoker> cache service url to invoker mapping.
+    private volatile Map<URL, Invoker<T>> urlInvokerMap; // The initial value is null and the midway may be assigned to null, please use the local variable reference
+
+    private ServiceInstancesChangedListener listener;
+
+    public ServiceDiscoveryRegistryDirectory(Class<T> serviceType, URL url) {
+        super(serviceType, url);
+    }
+
+    @Override
+    public synchronized void notify(List<URL> instanceUrls) {
+        // Set the context of the address notification thread.
+        RpcContext.setRpcContext(getConsumerUrl());
+        if (CollectionUtils.isEmpty(instanceUrls)) {
+            // FIXME, empty protocol
+        }
+        refreshInvoker(instanceUrls);
+    }
+
+    private void refreshInvoker(List<URL> invokerUrls) {
+        Assert.notNull(invokerUrls, "invokerUrls should not be null");
+
+        if (invokerUrls.size() == 1
+                && invokerUrls.get(0) != null
+                && EMPTY_PROTOCOL.equals(invokerUrls.get(0).getProtocol())) {
+            this.forbidden = true; // Forbid to access
+            this.invokers = Collections.emptyList();
+            routerChain.setInvokers(this.invokers);
+            destroyAllInvokers(); // Close all invokers
+        } else {
+            this.forbidden = false; // Allow to access
+            Map<URL, Invoker<T>> oldUrlInvokerMap = this.urlInvokerMap; // local reference
+            if (CollectionUtils.isEmpty(invokerUrls)) {
+                return;
+            }
+            Map<URL, Invoker<T>> newUrlInvokerMap = toInvokers(invokerUrls);// Translate url list to Invoker map
+
+            if (CollectionUtils.isEmptyMap(newUrlInvokerMap)) {
+                logger.error(new IllegalStateException("Cannot create invokers from url address list (total " + invokerUrls.size() + ")"));
+                return;
+            }
+
+            List<Invoker<T>> newInvokers = Collections.unmodifiableList(new ArrayList<>(newUrlInvokerMap.values()));
+            // pre-route and build cache, notice that route cache should build on original Invoker list.
+            // toMergeMethodInvokerMap() will wrap some invokers having different groups, those wrapped invokers not should be routed.
+            routerChain.setInvokers(newInvokers);
+            this.invokers = multiGroup ? toMergeInvokerList(newInvokers) : newInvokers;
+            this.urlInvokerMap = newUrlInvokerMap;
+
+            if (oldUrlInvokerMap != null) {
+                try {
+                    destroyUnusedInvokers(oldUrlInvokerMap, newUrlInvokerMap); // Close the unused Invoker
+                } catch (Exception e) {
+                    logger.warn("destroyUnusedInvokers error. ", e);
+                }
+            }
+        }
+    }
+
+    /**
+     * Turn urls into invokers, and if url has been refer, will not re-reference.
+     *
+     * @param urls
+     * @return invokers
+     */
+    private Map<URL, Invoker<T>> toInvokers(List<URL> urls) {
+        Map<URL, Invoker<T>> newUrlInvokerMap = new HashMap<>();
+        if (urls == null || urls.isEmpty()) {
+            return newUrlInvokerMap;
+        }
+        for (URL url : urls) {
+            if (EMPTY_PROTOCOL.equals(url.getProtocol())) {
+                continue;
+            }
+            if (!ExtensionLoader.getExtensionLoader(Protocol.class).hasExtension(url.getProtocol())) {
+                logger.error(new IllegalStateException("Unsupported protocol " + url.getProtocol() +
+                        " in notified url: " + url + " from registry " + getUrl().getAddress() +
+                        " to consumer " + NetUtils.getLocalHost() + ", supported protocol: " +
+                        ExtensionLoader.getExtensionLoader(Protocol.class).getSupportedExtensions()));
+                continue;
+            }
+
+            if (urlInvokerMap != null && urlInvokerMap.containsKey(url)) { // Repeated url
+                continue;
+            }
+            Invoker<T> invoker = urlInvokerMap == null ? null : urlInvokerMap.get(url);
+            if (invoker == null) { // Not in the cache, refer again
+                try {
+                    boolean enabled = true;
+                    if (url.hasParameter(DISABLED_KEY)) {
+                        enabled = !url.getParameter(DISABLED_KEY, false);
+                    } else {
+                        enabled = url.getParameter(ENABLED_KEY, true);
+                    }
+                    if (enabled) {
+                        invoker = protocol.refer(serviceType, url);
+                    }
+                } catch (Throwable t) {
+                    logger.error("Failed to refer invoker for interface:" + serviceType + ",url:(" + url + ")" + t.getMessage(), t);
+                }
+                if (invoker != null) { // Put new invoker in cache
+                    newUrlInvokerMap.put(url, invoker);
+                }
+            } else {
+                newUrlInvokerMap.put(url, invoker);
+            }
+        }
+        return newUrlInvokerMap;
+    }
+
+    private List<Invoker<T>> toMergeInvokerList(List<Invoker<T>> invokers) {
+        return invokers;
+    }
+
+    /**
+     * Close all invokers
+     */
+    private void destroyAllInvokers() {
+        Map<URL, Invoker<T>> localUrlInvokerMap = this.urlInvokerMap; // local reference
+        if (localUrlInvokerMap != null) {
+            for (Invoker<T> invoker : new ArrayList<>(localUrlInvokerMap.values())) {
+                try {
+                    invoker.destroy();
+                } catch (Throwable t) {
+                    logger.warn("Failed to destroy service " + serviceKey + " to provider " + invoker.getUrl(), t);
+                }
+            }
+            localUrlInvokerMap.clear();
+        }
+        invokers = null;
+    }
+
+    /**
+     * Check whether the invoker in the cache needs to be destroyed
+     * If set attribute of url: refer.autodestroy=false, the invokers will only increase without decreasing,there may be a refer leak
+     *
+     * @param oldUrlInvokerMap
+     * @param newUrlInvokerMap
+     */
+    private void destroyUnusedInvokers(Map<URL, Invoker<T>> oldUrlInvokerMap, Map<URL, Invoker<T>> newUrlInvokerMap) {
+        if (newUrlInvokerMap == null || newUrlInvokerMap.size() == 0) {
+            destroyAllInvokers();
+            return;
+        }
+        // check deleted invoker
+        List<URL> deleted = null;
+        if (oldUrlInvokerMap != null) {
+            Collection<Invoker<T>> newInvokers = newUrlInvokerMap.values();
+            for (Map.Entry<URL, Invoker<T>> entry : oldUrlInvokerMap.entrySet()) {
+                if (!newInvokers.contains(entry.getValue())) {
+                    if (deleted == null) {
+                        deleted = new ArrayList<>();
+                    }
+                    deleted.add(entry.getKey());
+                }
+            }
+        }
+
+        if (deleted != null) {
+            for (URL url : deleted) {
+                if (url != null) {
+                    Invoker<T> invoker = oldUrlInvokerMap.remove(url);
+                    if (invoker != null) {
+                        try {
+                            invoker.destroy();
+                            if (logger.isDebugEnabled()) {
+                                logger.debug("destroy invoker[" + invoker.getUrl() + "] success. ");
+                            }
+                        } catch (Exception e) {
+                            logger.warn("destroy invoker[" + invoker.getUrl() + "] failed. " + e.getMessage(), e);
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    @Override
+    public void destroy() {
+        if (isDestroyed()) {
+            return;
+        }
+
+        // unregister.
+        try {
+            if (getRegisteredConsumerUrl() != null && registry != null && registry.isAvailable()) {
+                registry.unregister(getRegisteredConsumerUrl());
+            }
+        } catch (Throwable t) {
+            logger.warn("unexpected error when unregister service " + serviceKey + "from registry" + registry.getUrl(), t);
+        }
+        // unsubscribe.
+        try {
+            if (getConsumerUrl() != null && registry != null && registry.isAvailable()) {
+                registry.unsubscribe(getConsumerUrl(), this);
+            }
+        } catch (Throwable t) {
+            logger.warn("unexpected error when unsubscribe service " + serviceKey + "from registry" + registry.getUrl(), t);
+        }
+        super.destroy(); // must be executed after unsubscribing
+        try {
+            destroyAllInvokers();
+        } catch (Throwable t) {
+            logger.warn("Failed to destroy service " + serviceKey, t);
+        }
+    }
+}
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistryProtocol.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistryProtocol.java
index c89c6c9..f4f872f 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistryProtocol.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistryProtocol.java
@@ -17,6 +17,7 @@
 package org.apache.dubbo.registry.client;
 
 import org.apache.dubbo.common.URL;
+import org.apache.dubbo.registry.integration.DynamicDirectory;
 import org.apache.dubbo.registry.integration.RegistryProtocol;
 import org.apache.dubbo.rpc.Invoker;
 
@@ -26,7 +27,6 @@ import static org.apache.dubbo.common.constants.RegistryConstants.SERVICE_REGIST
  * TODO, replace RegistryProtocol completely in the future.
  */
 public class ServiceDiscoveryRegistryProtocol extends RegistryProtocol {
-
     @Override
     protected URL getRegistryUrl(Invoker<?> originInvoker) {
         URL registryUrl = originInvoker.getUrl();
@@ -44,4 +44,8 @@ public class ServiceDiscoveryRegistryProtocol extends RegistryProtocol {
         return super.getRegistryUrl(url);
     }
 
+    @Override
+    protected <T> DynamicDirectory<T> createDirectory(Class<T> type, URL url) {
+        return new ServiceDiscoveryRegistryDirectory<>(type, url);
+    }
 }
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistryProtocol.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistryProtocolListener.java
similarity index 57%
copy from dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistryProtocol.java
copy to dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistryProtocolListener.java
index c89c6c9..d213171 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistryProtocol.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistryProtocolListener.java
@@ -16,32 +16,24 @@
  */
 package org.apache.dubbo.registry.client;
 
-import org.apache.dubbo.common.URL;
 import org.apache.dubbo.registry.integration.RegistryProtocol;
+import org.apache.dubbo.registry.integration.RegistryProtocolListener;
+import org.apache.dubbo.rpc.Exporter;
 import org.apache.dubbo.rpc.Invoker;
 
-import static org.apache.dubbo.common.constants.RegistryConstants.SERVICE_REGISTRY_PROTOCOL;
+public class ServiceDiscoveryRegistryProtocolListener implements RegistryProtocolListener {
+    @Override
+    public void onExport(RegistryProtocol registryProtocol, Exporter<?> exporter) {
 
-/**
- * TODO, replace RegistryProtocol completely in the future.
- */
-public class ServiceDiscoveryRegistryProtocol extends RegistryProtocol {
+    }
 
     @Override
-    protected URL getRegistryUrl(Invoker<?> originInvoker) {
-        URL registryUrl = originInvoker.getUrl();
-        if (SERVICE_REGISTRY_PROTOCOL.equals(registryUrl.getProtocol())) {
-            return registryUrl;
-        }
-        return super.getRegistryUrl(originInvoker);
+    public void onRefer(RegistryProtocol registryProtocol, Invoker<?> invoker) {
+
     }
 
     @Override
-    protected URL getRegistryUrl(URL url) {
-        if (SERVICE_REGISTRY_PROTOCOL.equals(url.getProtocol())) {
-            return url;
-        }
-        return super.getRegistryUrl(url);
-    }
+    public void onDestroy() {
 
+    }
 }
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceInstance.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceInstance.java
index 164d146..ad950c1 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceInstance.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceInstance.java
@@ -16,8 +16,6 @@
  */
 package org.apache.dubbo.registry.client;
 
-import org.apache.dubbo.common.URL;
-
 import java.io.Serializable;
 import java.util.Map;
 
@@ -57,6 +55,8 @@ public interface ServiceInstance extends Serializable {
      */
     Integer getPort();
 
+    String getAddress();
+
     /**
      * The enable status of the registered service instance.
      *
@@ -84,6 +84,8 @@ public interface ServiceInstance extends Serializable {
      */
     Map<String, String> getMetadata();
 
+    Map<String, String> getExtendParams();
+
     /**
      * Get the value of metadata by the specified name
      *
@@ -117,6 +119,6 @@ public interface ServiceInstance extends Serializable {
      */
     boolean equals(Object another);
 
-    URL toURL(String protocol, String path, String interfaceName, String group, String version, String serviceKey);
+    InstanceAddressURL toURL();
 
 }
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 36365c1..1901e34 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
@@ -19,13 +19,14 @@ package org.apache.dubbo.registry.client.event.listener;
 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.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.registry.NotifyListener;
 import org.apache.dubbo.registry.client.DefaultServiceInstance;
+import org.apache.dubbo.registry.client.RegistryClusterIdentifier;
 import org.apache.dubbo.registry.client.ServiceDiscovery;
 import org.apache.dubbo.registry.client.ServiceInstance;
 import org.apache.dubbo.registry.client.event.ServiceInstancesChangedEvent;
@@ -34,15 +35,15 @@ 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;
 import java.util.HashMap;
-import java.util.HashSet;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
+import java.util.TreeSet;
 
+import static org.apache.dubbo.common.constants.CommonConstants.REGISTER_KEY;
 import static org.apache.dubbo.common.constants.CommonConstants.REMOTE_METADATA_STORAGE_TYPE;
 import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.getExportedServicesRevision;
 
@@ -52,28 +53,28 @@ import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataU
  * @see ServiceInstancesChangedEvent
  * @since 2.7.5
  */
-public abstract class ServiceInstancesChangedListener implements ConditionalEventListener<ServiceInstancesChangedEvent> {
+public class ServiceInstancesChangedListener implements ConditionalEventListener<ServiceInstancesChangedEvent> {
 
     private static final Logger logger = LoggerFactory.getLogger(ServiceInstancesChangedListener.class);
 
-    private final String serviceName;
+    private final Set<String> serviceNames;
     private final ServiceDiscovery serviceDiscovery;
     private URL url;
+    private Map<String, NotifyListener> listeners;
 
-    private List<ServiceInstance> instances;
+    private Map<String, List<ServiceInstance>> allInstances;
 
-    private Map<String, List<ServiceInstance>> revisionToInstances;
+    private Map<String, List<URL>> serviceUrls;
 
     private Map<String, MetadataInfo> revisionToMetadata;
 
-    private Map<String, Set<String>> serviceToRevisions;
-
-    private Map<String, List<ServiceInstance>> serviceToInstances;
-
-    protected ServiceInstancesChangedListener(String serviceName, ServiceDiscovery serviceDiscovery, URL url) {
-        this.serviceName = serviceName;
+    public ServiceInstancesChangedListener(Set<String> serviceNames, ServiceDiscovery serviceDiscovery) {
+        this.serviceNames = serviceNames;
         this.serviceDiscovery = serviceDiscovery;
-        this.url = url;
+        this.listeners = new HashMap<>();
+        this.allInstances = new HashMap<>();
+        this.serviceUrls = new HashMap<>();
+        this.revisionToMetadata = new HashMap<>();
     }
 
     /**
@@ -81,69 +82,66 @@ public abstract class ServiceInstancesChangedListener implements ConditionalEven
      *
      * @param event {@link ServiceInstancesChangedEvent}
      */
-    public void onEvent(ServiceInstancesChangedEvent event) {
-        instances = event.getServiceInstances();
-
-        Map<String, List<ServiceInstance>> localRevisionToInstances = new HashMap<>();
-        Map<String, MetadataInfo> localRevisionToMetadata = new HashMap<>();
-        Map<String, Set<String>> localServiceToRevisions = new HashMap<>();
-        for (ServiceInstance instance : instances) {
-            String revision = getExportedServicesRevision(instance);
-            Collection<ServiceInstance> rInstances = localRevisionToInstances.computeIfAbsent(revision, r -> new ArrayList<>());
-            rInstances.add(instance);
-
-            MetadataInfo metadata = null;
-            if (revisionToMetadata != null && ((metadata = revisionToMetadata.get(revision)) != null)) {
-                localRevisionToMetadata.put(revision, metadata);
-            } else {
-                metadata = getMetadataInfo(instance);
-                if (metadata != null) {
-                    localRevisionToMetadata.put(revision, getMetadataInfo(instance));
+    public synchronized void onEvent(ServiceInstancesChangedEvent event) {
+        String appName = event.getServiceName();
+        allInstances.put(appName, event.getServiceInstances());
+
+        for (Map.Entry<String, List<ServiceInstance>> entry : allInstances.entrySet()) {
+            List<ServiceInstance> instances = entry.getValue();
+            for (ServiceInstance instance : instances) {
+                String revision = getExportedServicesRevision(instance);
+                Map<String, List<ServiceInstance>> revisionToInstances = new HashMap<>();
+                List<ServiceInstance> subInstances = revisionToInstances.computeIfAbsent(revision, r -> new LinkedList<>());
+                subInstances.add(instance);
+
+                MetadataInfo metadata = revisionToMetadata.get(revision);
+                if (metadata == null) {
+                    metadata = getMetadataInfo(instance);
+                    if (metadata != null) {
+                        revisionToMetadata.put(revision, getMetadataInfo(instance));
+                    } else {
+
+                    }
                 }
-            }
 
-            if (metadata != null) {
-                parse(revision, metadata, localServiceToRevisions);
-                ((DefaultServiceInstance) instance).setServiceMetadata(metadata);
-            } else {
-                logger.error("Failed to load service metadata for instance " + instance);
-                Set<String> set = localServiceToRevisions.computeIfAbsent(url.getServiceKey(), k -> new HashSet<>());
-                set.add(revision);
-            }
-        }
-
-        this.revisionToInstances = localRevisionToInstances;
-        this.revisionToMetadata = localRevisionToMetadata;
-        this.serviceToRevisions = localServiceToRevisions;
-
-        Map<String, List<ServiceInstance>> localServiceToInstances = new HashMap<>();
-        for (String serviceKey : localServiceToRevisions.keySet()) {
-            if (CollectionUtils.equals(localRevisionToInstances.keySet(), localServiceToRevisions.get(serviceKey))) {
-                localServiceToInstances.put(serviceKey, instances);
+                Map<String, Set<String>> localServiceToRevisions = new HashMap<>();
+                if (metadata != null) {
+                    parseMetadata(revision, metadata, localServiceToRevisions);
+                    ((DefaultServiceInstance) instance).setServiceMetadata(metadata);
+                }
+//                else {
+//                    logger.error("Failed to load service metadata for instance " + instance);
+//                    Set<String> set = localServiceToRevisions.computeIfAbsent(url.getServiceKey(), k -> new TreeSet<>());
+//                    set.add(revision);
+//                }
+
+                Map<Set<String>, List<URL>> revisionsToUrls = new HashMap();
+                localServiceToRevisions.forEach((serviceKey, revisions) -> {
+                    List<URL> urls = revisionsToUrls.get(revisions);
+                    if (urls != null) {
+                        serviceUrls.put(serviceKey, urls);
+                    } else {
+                        urls = new ArrayList<>();
+                        for (String r : revisions) {
+                            for (ServiceInstance i : revisionToInstances.get(r)) {
+                                urls.add(i.toURL());
+                            }
+                        }
+                        revisionsToUrls.put(revisions, urls);
+                        serviceUrls.put(serviceKey, urls);
+                    }
+                });
             }
         }
 
-        this.serviceToInstances = localServiceToInstances;
+        this.notifyAddressChanged();
     }
 
-    public List<ServiceInstance> getInstances(String serviceKey) {
-        if (serviceToInstances.containsKey(serviceKey)) {
-            return serviceToInstances.get(serviceKey);
-        }
-
-        Set<String> revisions = serviceToRevisions.get(serviceKey);
-        List<ServiceInstance> allInstances = new LinkedList<>();
-        for (String r : revisions) {
-            allInstances.addAll(revisionToInstances.get(r));
-        }
-        return allInstances;
-    }
-
-    private Map<String, Set<String>> parse(String revision, MetadataInfo metadata, Map<String, Set<String>> localServiceToRevisions) {
+    private Map<String, Set<String>> parseMetadata(String revision, MetadataInfo metadata, Map<String, Set<String>> localServiceToRevisions) {
         Map<String, ServiceInfo> serviceInfos = metadata.getServices();
         for (Map.Entry<String, ServiceInfo> entry : serviceInfos.entrySet()) {
             String serviceKey = entry.getValue().getServiceKey();
-            Set<String> set = localServiceToRevisions.computeIfAbsent(serviceKey, k -> new HashSet<>());
+            Set<String> set = localServiceToRevisions.computeIfAbsent(serviceKey, k -> new TreeSet<>());
             set.add(revision);
         }
 
@@ -152,7 +150,7 @@ public abstract class ServiceInstancesChangedListener implements ConditionalEven
 
     private MetadataInfo getMetadataInfo(ServiceInstance instance) {
         String metadataType = ServiceInstanceMetadataUtils.getMetadataStorageType(instance);
-
+        instance.getExtendParams().putIfAbsent(REGISTER_KEY, RegistryClusterIdentifier.getExtension().consumerKey(url));
         MetadataInfo metadataInfo;
         try {
             if (REMOTE_METADATA_STORAGE_TYPE.equals(metadataType)) {
@@ -160,7 +158,7 @@ public abstract class ServiceInstancesChangedListener implements ConditionalEven
                 metadataInfo = remoteMetadataService.getMetadata(instance);
             } else {
                 MetadataService metadataServiceProxy = MetadataUtils.getMetadataServiceProxy(instance);
-                metadataInfo = metadataServiceProxy.getMetadataInfo();
+                metadataInfo = metadataServiceProxy.getMetadataInfo(ServiceInstanceMetadataUtils.getExportedServicesRevision(instance));
             }
         } catch (Exception e) {
             // TODO, load metadata backup
@@ -169,15 +167,32 @@ public abstract class ServiceInstancesChangedListener implements ConditionalEven
         return metadataInfo;
     }
 
-    protected abstract void notifyAddresses();
+    private void notifyAddressChanged() {
+        listeners.forEach((key, notifyListener) -> {
+            //FIXME, group wildcard match
+            notifyListener.notify(serviceUrls.get(key));
+        });
+    }
+
+    public void addListener(String serviceKey, NotifyListener listener) {
+        this.listeners.put(serviceKey, listener);
+    }
+
+    public void removeListener(String serviceKey) {
+        this.listeners.remove(serviceKey);
+    }
+
+    public List<URL> getUrls(String serviceKey) {
+        return serviceUrls.get(serviceKey);
+    }
 
     /**
      * Get the correlative service name
      *
      * @return the correlative service name
      */
-    public final String getServiceName() {
-        return serviceName;
+    public final Set<String> getServiceNames() {
+        return serviceNames;
     }
 
     public void setUrl(URL url) {
@@ -193,7 +208,7 @@ public abstract class ServiceInstancesChangedListener implements ConditionalEven
      * @return If service name matches, return <code>true</code>, or <code>false</code>
      */
     public final boolean accept(ServiceInstancesChangedEvent event) {
-        return Objects.equals(serviceName, event.getServiceName());
+        return serviceNames.contains(event.getServiceName());
     }
 
     @Override
@@ -201,11 +216,11 @@ public abstract class ServiceInstancesChangedListener implements ConditionalEven
         if (this == o) return true;
         if (!(o instanceof ServiceInstancesChangedListener)) return false;
         ServiceInstancesChangedListener that = (ServiceInstancesChangedListener) o;
-        return Objects.equals(getServiceName(), that.getServiceName());
+        return Objects.equals(getServiceNames(), that.getServiceNames());
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(getClass(), getServiceName());
+        return Objects.hash(getClass(), getServiceNames());
     }
 }
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/store/InMemoryWritableMetadataService.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/store/InMemoryWritableMetadataService.java
index 588c7ae..8605eaa 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/store/InMemoryWritableMetadataService.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/store/InMemoryWritableMetadataService.java
@@ -26,6 +26,7 @@ import org.apache.dubbo.metadata.MetadataService;
 import org.apache.dubbo.metadata.WritableMetadataService;
 import org.apache.dubbo.metadata.definition.ServiceDefinitionBuilder;
 import org.apache.dubbo.metadata.definition.model.ServiceDefinition;
+import org.apache.dubbo.registry.client.RegistryClusterIdentifier;
 import org.apache.dubbo.rpc.model.ApplicationModel;
 import org.apache.dubbo.rpc.support.ProtocolUtils;
 
@@ -36,6 +37,8 @@ import java.util.Map;
 import java.util.SortedSet;
 import java.util.TreeSet;
 import java.util.concurrent.Callable;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
 import java.util.concurrent.ConcurrentNavigableMap;
 import java.util.concurrent.ConcurrentSkipListMap;
 import java.util.concurrent.locks.Lock;
@@ -70,7 +73,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;
+    ConcurrentMap<String, MetadataInfo> metadataInfos;
 
     // ==================================================================================== //
 
@@ -86,7 +89,7 @@ public class InMemoryWritableMetadataService implements WritableMetadataService
     ConcurrentNavigableMap<String, String> serviceDefinitions = new ConcurrentSkipListMap<>();
 
     public InMemoryWritableMetadataService() {
-        this.metadataInfo = new MetadataInfo(ApplicationModel.getName());
+        this.metadataInfos = new ConcurrentHashMap<>();
     }
 
     @Override
@@ -120,15 +123,28 @@ public class InMemoryWritableMetadataService implements WritableMetadataService
 
     @Override
     public boolean exportURL(URL url) {
-        ServiceInfo serviceInfo = new ServiceInfo(url);
-        metadataInfo.addService(serviceInfo);
+        String registryKey = RegistryClusterIdentifier.getExtension().providerKey(url);
+        String[] keys = registryKey.split(",");
+        for (String key : keys) {
+            MetadataInfo metadataInfo = metadataInfos.computeIfAbsent(key, k -> {
+                return new MetadataInfo(ApplicationModel.getName());
+            });
+            metadataInfo.addService(new ServiceInfo(url));
+        }
         return addURL(exportedServiceURLs, url);
     }
 
     @Override
     public boolean unexportURL(URL url) {
-        ServiceInfo serviceInfo = new ServiceInfo(url);
-        metadataInfo.removeService(serviceInfo);
+        String registryKey = RegistryClusterIdentifier.getExtension().providerKey(url);
+        String[] keys = registryKey.split(",");
+        for (String key : keys) {
+            MetadataInfo metadataInfo = metadataInfos.get(key);
+            metadataInfo.removeService(url.getProtocolServiceKey());
+            if (metadataInfo.getServices().isEmpty()) {
+                metadataInfos.remove(key);
+            }
+        }
         return removeURL(exportedServiceURLs, url);
     }
 
@@ -173,8 +189,21 @@ public class InMemoryWritableMetadataService implements WritableMetadataService
     }
 
     @Override
-    public MetadataInfo getMetadataInfo() {
-        return metadataInfo;
+    public MetadataInfo getMetadataInfo(String revision) {
+        if (StringUtils.isEmpty(revision)) {
+            return null;
+        }
+        for (Map.Entry<String, MetadataInfo> entry : metadataInfos.entrySet()) {
+            MetadataInfo metadataInfo = entry.getValue();
+            if (revision.equals(metadataInfo.getRevision())) {
+                return metadataInfo;
+            }
+        }
+        return null;
+    }
+
+    public Map<String, MetadataInfo> getMetadataInfos() {
+        return metadataInfos;
     }
 
     boolean addURL(Map<String, SortedSet<URL>> serviceURLs, URL url) {
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
index 799d2aa..85d6855 100644
--- 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
@@ -33,7 +33,7 @@ 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 java.util.Map;
 
 import static org.apache.dubbo.common.constants.CommonConstants.APPLICATION_KEY;
 import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER_SIDE;
@@ -44,7 +44,7 @@ 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;
-import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.EXPORTED_SERVICES_REVISION_PROPERTY_NAME;
+import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_KEY;
 
 public class RemoteMetadataServiceImpl {
 
@@ -55,15 +55,20 @@ public class RemoteMetadataServiceImpl {
         this.localMetadataService = writableMetadataService;
     }
 
-    public List<MetadataReport> getMetadataReports() {
+    public Map<String, MetadataReport> getMetadataReports() {
         return MetadataReportInstance.getMetadataReports(true);
     }
 
     public void publishMetadata(ServiceInstance instance) {
-        MetadataInfo metadataInfo = localMetadataService.getMetadataInfo();
-        SubscriberMetadataIdentifier identifier = new SubscriberMetadataIdentifier(instance.getServiceName(), metadataInfo.getRevision());
-        getMetadataReports().forEach(metadataReport -> {
-            instance.getMetadata().put(EXPORTED_SERVICES_REVISION_PROPERTY_NAME, metadataInfo.getRevision());
+        Map<String, MetadataInfo> metadataInfos = localMetadataService.getMetadataInfos();
+        metadataInfos.forEach((registryKey, metadataInfo) -> {
+            SubscriberMetadataIdentifier identifier = new SubscriberMetadataIdentifier(instance.getServiceName(), metadataInfo.getRevision());
+            metadataInfo.getRevision();
+            metadataInfo.getExtendParams().put(REGISTRY_KEY, registryKey);
+            MetadataReport metadataReport = getMetadataReports().get(registryKey);
+            if (metadataReport == null) {
+                metadataReport = getMetadataReports().entrySet().iterator().next().getValue();
+            }
             metadataReport.publishAppMetadata(identifier, metadataInfo);
         });
     }
@@ -71,13 +76,14 @@ public class RemoteMetadataServiceImpl {
     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;
-            }
+
+        String registryCluster = instance.getExtendParams().get(REGISTRY_KEY);
+
+        MetadataReport metadataReport = getMetadataReports().get(registryCluster);
+        if (metadataReport == null) {
+            metadataReport = getMetadataReports().entrySet().iterator().next().getValue();
         }
-        return null;
+        return metadataReport.getAppMetadata(identifier, instance.getMetadata());
     }
 
     public void publishServiceDefinition(URL url) {
@@ -103,7 +109,8 @@ public class RemoteMetadataServiceImpl {
                 Class interfaceClass = Class.forName(interfaceName);
                 FullServiceDefinition fullServiceDefinition = ServiceDefinitionBuilder.buildFullDefinition(interfaceClass,
                         providerUrl.getParameters());
-                for (MetadataReport metadataReport : getMetadataReports()) {
+                for (Map.Entry<String, MetadataReport> entry : getMetadataReports().entrySet()) {
+                    MetadataReport metadataReport = entry.getValue();
                     metadataReport.storeProviderMetadata(new MetadataIdentifier(providerUrl.getServiceInterface(),
                             providerUrl.getParameter(VERSION_KEY), providerUrl.getParameter(GROUP_KEY),
                             PROVIDER_SIDE, providerUrl.getParameter(APPLICATION_KEY)), fullServiceDefinition);
@@ -120,7 +127,7 @@ public class RemoteMetadataServiceImpl {
     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 -> {
+        getMetadataReports().forEach((registryKey, 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/DynamicDirectory.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/DynamicDirectory.java
new file mode 100644
index 0000000..8268361
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/DynamicDirectory.java
@@ -0,0 +1,243 @@
+/*
+ * 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.integration;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.URLBuilder;
+import org.apache.dubbo.common.Version;
+import org.apache.dubbo.common.extension.ExtensionLoader;
+import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.common.utils.NetUtils;
+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.rpc.Invocation;
+import org.apache.dubbo.rpc.Invoker;
+import org.apache.dubbo.rpc.Protocol;
+import org.apache.dubbo.rpc.RpcException;
+import org.apache.dubbo.rpc.cluster.Cluster;
+import org.apache.dubbo.rpc.cluster.Configurator;
+import org.apache.dubbo.rpc.cluster.RouterChain;
+import org.apache.dubbo.rpc.cluster.RouterFactory;
+import org.apache.dubbo.rpc.cluster.directory.AbstractDirectory;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import static org.apache.dubbo.common.constants.CommonConstants.ANY_VALUE;
+import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY;
+import static org.apache.dubbo.common.constants.CommonConstants.MONITOR_KEY;
+import static org.apache.dubbo.common.constants.RegistryConstants.CATEGORY_KEY;
+import static org.apache.dubbo.common.constants.RegistryConstants.CONSUMERS_CATEGORY;
+import static org.apache.dubbo.registry.Constants.REGISTER_KEY;
+import static org.apache.dubbo.registry.Constants.SIMPLIFIED_KEY;
+import static org.apache.dubbo.registry.integration.RegistryProtocol.DEFAULT_REGISTER_CONSUMER_KEYS;
+import static org.apache.dubbo.remoting.Constants.CHECK_KEY;
+
+
+/**
+ * RegistryDirectory
+ */
+public abstract class DynamicDirectory<T> extends AbstractDirectory<T> implements NotifyListener {
+
+    private static final Logger logger = LoggerFactory.getLogger(DynamicDirectory.class);
+
+    protected static final Cluster CLUSTER = ExtensionLoader.getExtensionLoader(Cluster.class).getAdaptiveExtension();
+
+    protected static final RouterFactory ROUTER_FACTORY = ExtensionLoader.getExtensionLoader(RouterFactory.class)
+            .getAdaptiveExtension();
+
+    protected final String serviceKey; // Initialization at construction time, assertion not null
+    protected final Class<T> serviceType; // Initialization at construction time, assertion not null
+    protected final URL directoryUrl; // Initialization at construction time, assertion not null, and always assign non null value
+    protected final boolean multiGroup;
+    protected Protocol protocol; // Initialization at the time of injection, the assertion is not null
+    protected Registry registry; // Initialization at the time of injection, the assertion is not null
+    protected volatile boolean forbidden = false;
+    protected boolean shouldRegister;
+    protected boolean shouldSimplified;
+
+    protected volatile URL overrideDirectoryUrl; // Initialization at construction time, assertion not null, and always assign non null value
+
+    protected volatile URL registeredConsumerUrl;
+
+    /**
+     * override rules
+     * Priority: override>-D>consumer>provider
+     * Rule one: for a certain provider <ip:port,timeout=100>
+     * Rule two: for all providers <* ,timeout=5000>
+     */
+    protected volatile List<Configurator> configurators; // The initial value is null and the midway may be assigned to null, please use the local variable reference
+
+    // Map<url, Invoker> cache service url to invoker mapping.
+    protected volatile Map<URL, Invoker<T>> urlInvokerMap; // The initial value is null and the midway may be assigned to null, please use the local variable reference
+    protected volatile List<Invoker<T>> invokers;
+
+    // Set<invokerUrls> cache invokeUrls to invokers mapping.
+    protected volatile Set<URL> cachedInvokerUrls; // The initial value is null and the midway may be assigned to null, please use the local variable reference
+
+    protected ServiceInstancesChangedListener serviceListener;
+
+    public DynamicDirectory(Class<T> serviceType, URL url) {
+        super(url);
+        if (serviceType == null) {
+            throw new IllegalArgumentException("service type is null.");
+        }
+
+        shouldRegister = !ANY_VALUE.equals(url.getServiceInterface()) && url.getParameter(REGISTER_KEY, true);
+        shouldSimplified = url.getParameter(SIMPLIFIED_KEY, false);
+        if (url.getServiceKey() == null || url.getServiceKey().length() == 0) {
+            throw new IllegalArgumentException("registry serviceKey is null.");
+        }
+        this.serviceType = serviceType;
+        this.serviceKey = super.getConsumerUrl().getServiceKey();
+
+        this.overrideDirectoryUrl = this.directoryUrl = turnRegistryUrlToConsumerUrl(url);
+        String group = directoryUrl.getParameter(GROUP_KEY, "");
+        this.multiGroup = group != null && (ANY_VALUE.equals(group) || group.contains(","));
+    }
+
+    @Override
+    public void addServiceListener(ServiceInstancesChangedListener instanceListener) {
+        this.serviceListener = instanceListener;
+    }
+
+    private URL turnRegistryUrlToConsumerUrl(URL url) {
+        return URLBuilder.from(url)
+                .setPath(url.getServiceInterface())
+                .clearParameters()
+                .addParameters(queryMap)
+                .removeParameter(MONITOR_KEY)
+                .build();
+    }
+
+    public void setProtocol(Protocol protocol) {
+        this.protocol = protocol;
+    }
+
+    public void setRegistry(Registry registry) {
+        this.registry = registry;
+    }
+
+    public Registry getRegistry() {
+        return registry;
+    }
+
+    public boolean isShouldRegister() {
+        return shouldRegister;
+    }
+
+    public void subscribe(URL url) {
+        setConsumerUrl(url);
+        registry.subscribe(url, this);
+    }
+
+    public void unSubscribe(URL url) {
+        setConsumerUrl(null);
+        registry.unsubscribe(url, this);
+    }
+
+    @Override
+    public List<Invoker<T>> doList(Invocation invocation) {
+        if (forbidden) {
+            // 1. No service provider 2. Service providers are disabled
+            throw new RpcException(RpcException.FORBIDDEN_EXCEPTION, "No provider available from registry " +
+                    getUrl().getAddress() + " for service " + getConsumerUrl().getServiceKey() + " on consumer " +
+                    NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion() +
+                    ", please check status of providers(disabled, not registered or in blacklist).");
+        }
+
+        if (multiGroup) {
+            return this.invokers == null ? Collections.emptyList() : this.invokers;
+        }
+
+        List<Invoker<T>> invokers = null;
+        try {
+            // Get invokers from cache, only runtime routers will be executed.
+            invokers = routerChain.route(getConsumerUrl(), invocation);
+        } catch (Throwable t) {
+            logger.error("Failed to execute router: " + getUrl() + ", cause: " + t.getMessage(), t);
+        }
+
+        return invokers == null ? Collections.emptyList() : invokers;
+    }
+
+    @Override
+    public Class<T> getInterface() {
+        return serviceType;
+    }
+
+    @Override
+    public List<Invoker<T>> getAllInvokers() {
+        return invokers;
+    }
+
+    @Override
+    public URL getConsumerUrl() {
+        return this.overrideDirectoryUrl;
+    }
+
+    public URL getRegisteredConsumerUrl() {
+        return registeredConsumerUrl;
+    }
+
+    public void setRegisteredConsumerUrl(URL url) {
+        if (!shouldSimplified) {
+            this.registeredConsumerUrl = url.addParameters(CATEGORY_KEY, CONSUMERS_CATEGORY, CHECK_KEY,
+                    String.valueOf(false));
+        } else {
+            this.registeredConsumerUrl = URL.valueOf(url, DEFAULT_REGISTER_CONSUMER_KEYS, null).addParameters(
+                    CATEGORY_KEY, CONSUMERS_CATEGORY, CHECK_KEY, String.valueOf(false));
+        }
+    }
+
+    @Override
+    public boolean isAvailable() {
+        if (isDestroyed()) {
+            return false;
+        }
+        Map<URL, Invoker<T>> localUrlInvokerMap = urlInvokerMap;
+        if (localUrlInvokerMap != null && localUrlInvokerMap.size() > 0) {
+            for (Invoker<T> invoker : new ArrayList<>(localUrlInvokerMap.values())) {
+                if (invoker.isAvailable()) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    public void buildRouterChain(URL url) {
+        this.setRouterChain(RouterChain.buildChain(url));
+    }
+
+    /**
+     * Haomin: added for test purpose
+     */
+    public Map<URL, Invoker<T>> getUrlInvokerMap() {
+        return urlInvokerMap;
+    }
+
+    public List<Invoker<T>> getInvokers() {
+        return invokers;
+    }
+
+}
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 54104e9..c8cd6dd 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
@@ -17,7 +17,6 @@
 package org.apache.dubbo.registry.integration;
 
 import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.URLBuilder;
 import org.apache.dubbo.common.Version;
 import org.apache.dubbo.common.config.configcenter.DynamicConfiguration;
 import org.apache.dubbo.common.extension.ExtensionLoader;
@@ -30,20 +29,14 @@ import org.apache.dubbo.common.utils.StringUtils;
 import org.apache.dubbo.common.utils.UrlUtils;
 import org.apache.dubbo.registry.AddressListener;
 import org.apache.dubbo.registry.NotifyListener;
-import org.apache.dubbo.registry.Registry;
-import org.apache.dubbo.registry.client.ServiceInstance;
-import org.apache.dubbo.registry.client.event.listener.ServiceInstancesChangedListener;
 import org.apache.dubbo.remoting.Constants;
 import org.apache.dubbo.rpc.Invocation;
 import org.apache.dubbo.rpc.Invoker;
 import org.apache.dubbo.rpc.Protocol;
 import org.apache.dubbo.rpc.RpcException;
-import org.apache.dubbo.rpc.cluster.Cluster;
 import org.apache.dubbo.rpc.cluster.Configurator;
 import org.apache.dubbo.rpc.cluster.Router;
 import org.apache.dubbo.rpc.cluster.RouterChain;
-import org.apache.dubbo.rpc.cluster.RouterFactory;
-import org.apache.dubbo.rpc.cluster.directory.AbstractDirectory;
 import org.apache.dubbo.rpc.cluster.directory.StaticDirectory;
 import org.apache.dubbo.rpc.cluster.governance.GovernanceRuleRepository;
 import org.apache.dubbo.rpc.cluster.support.ClusterUtils;
@@ -55,7 +48,6 @@ import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
-import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
@@ -63,15 +55,12 @@ import java.util.Optional;
 import java.util.Set;
 import java.util.stream.Collectors;
 
-import static org.apache.dubbo.common.constants.CommonConstants.ANY_VALUE;
 import static org.apache.dubbo.common.constants.CommonConstants.DISABLED_KEY;
 import static org.apache.dubbo.common.constants.CommonConstants.DUBBO_PROTOCOL;
 import static org.apache.dubbo.common.constants.CommonConstants.ENABLED_KEY;
 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.MONITOR_KEY;
 import static org.apache.dubbo.common.constants.CommonConstants.PROTOCOL_KEY;
-import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY;
 import static org.apache.dubbo.common.constants.RegistryConstants.APP_DYNAMIC_CONFIGURATORS_CATEGORY;
 import static org.apache.dubbo.common.constants.RegistryConstants.CATEGORY_KEY;
 import static org.apache.dubbo.common.constants.RegistryConstants.COMPATIBLE_CONFIG_KEY;
@@ -84,149 +73,40 @@ import static org.apache.dubbo.common.constants.RegistryConstants.PROVIDERS_CATE
 import static org.apache.dubbo.common.constants.RegistryConstants.ROUTERS_CATEGORY;
 import static org.apache.dubbo.common.constants.RegistryConstants.ROUTE_PROTOCOL;
 import static org.apache.dubbo.registry.Constants.CONFIGURATORS_SUFFIX;
-import static org.apache.dubbo.registry.Constants.REGISTER_KEY;
-import static org.apache.dubbo.registry.Constants.SIMPLIFIED_KEY;
 import static org.apache.dubbo.registry.integration.RegistryProtocol.DEFAULT_REGISTER_CONSUMER_KEYS;
 import static org.apache.dubbo.remoting.Constants.CHECK_KEY;
-import static org.apache.dubbo.rpc.cluster.Constants.REFER_KEY;
 import static org.apache.dubbo.rpc.cluster.Constants.ROUTER_KEY;
 
 
 /**
  * RegistryDirectory
  */
-public class RegistryDirectory<T> extends AbstractDirectory<T> implements NotifyListener {
-
+public class RegistryDirectory<T> extends DynamicDirectory<T> implements NotifyListener {
     private static final Logger logger = LoggerFactory.getLogger(RegistryDirectory.class);
 
-    private static final Cluster CLUSTER = ExtensionLoader.getExtensionLoader(Cluster.class).getAdaptiveExtension();
-
-    private static final RouterFactory ROUTER_FACTORY = ExtensionLoader.getExtensionLoader(RouterFactory.class)
-            .getAdaptiveExtension();
-
-    private final String serviceKey; // Initialization at construction time, assertion not null
-    private final Class<T> serviceType; // Initialization at construction time, assertion not null
-    private final Map<String, String> queryMap; // Initialization at construction time, assertion not null
-    private final URL directoryUrl; // Initialization at construction time, assertion not null, and always assign non null value
-    private final boolean multiGroup;
-    private Protocol protocol; // Initialization at the time of injection, the assertion is not null
-    private Registry registry; // Initialization at the time of injection, the assertion is not null
-    private volatile boolean forbidden = false;
-    private boolean shouldRegister;
-    private boolean shouldSimplified;
-
-    private volatile URL overrideDirectoryUrl; // Initialization at construction time, assertion not null, and always assign non null value
-
-    private volatile URL registeredConsumerUrl;
-
-    /**
-     * override rules
-     * Priority: override>-D>consumer>provider
-     * Rule one: for a certain provider <ip:port,timeout=100>
-     * Rule two: for all providers <* ,timeout=5000>
-     */
-    private volatile List<Configurator> configurators; // The initial value is null and the midway may be assigned to null, please use the local variable reference
-
-    // Map<url, Invoker> cache service url to invoker mapping.
-    private volatile Map<String, Invoker<T>> urlInvokerMap; // The initial value is null and the midway may be assigned to null, please use the local variable reference
-    private volatile List<Invoker<T>> invokers;
-
-    // Set<invokerUrls> cache invokeUrls to invokers mapping.
-    private volatile Set<URL> cachedInvokerUrls; // The initial value is null and the midway may be assigned to null, please use the local variable reference
-
     private static final ConsumerConfigurationListener CONSUMER_CONFIGURATION_LISTENER = new ConsumerConfigurationListener();
-    private ReferenceConfigurationListener serviceConfigurationListener;
-
-    private Set<ServiceInstancesChangedListener> serviceListeners;
+    private ReferenceConfigurationListener referenceConfigurationListener;
 
     public RegistryDirectory(Class<T> serviceType, URL url) {
-        super(url);
-        if (serviceType == null) {
-            throw new IllegalArgumentException("service type is null.");
-        }
-
-        shouldRegister = !ANY_VALUE.equals(url.getServiceInterface()) && url.getParameter(REGISTER_KEY, true);
-        shouldSimplified = url.getParameter(SIMPLIFIED_KEY, false);
-        if (url.getServiceKey() == null || url.getServiceKey().length() == 0) {
-            throw new IllegalArgumentException("registry serviceKey is null.");
-        }
-        this.serviceType = serviceType;
-        this.serviceKey = super.getConsumerUrl().getServiceKey();
-        this.queryMap = StringUtils.parseQueryString(url.getParameterAndDecoded(REFER_KEY));
-        this.overrideDirectoryUrl = this.directoryUrl = turnRegistryUrlToConsumerUrl(url);
-        String group = directoryUrl.getParameter(GROUP_KEY, "");
-        this.multiGroup = group != null && (ANY_VALUE.equals(group) || group.contains(","));
-
-        this.serviceListeners = new HashSet<>();
+        super(serviceType, url);
     }
 
     @Override
-    public void addServiceListener(ServiceInstancesChangedListener instanceListener) {
-        this.serviceListeners.add(instanceListener);
-    }
-
-    @Override
-    public void notifyServiceInstances() {
-        List<URL> urls = new LinkedList<>();
-        for (ServiceInstancesChangedListener listener : serviceListeners) {
-            List<ServiceInstance> instances = listener.getInstances(serviceKey);
-            for (ServiceInstance instance : instances) {
-                // FIXME, the right protocol? the right path?
-                urls.add(
-                        instance.toURL(
-                                "dubbo",
-                                serviceType.getName(),
-                                serviceType.getName(),
-                                queryMap.get(GROUP_KEY),
-                                queryMap.get(VERSION_KEY),
-                                serviceKey)
-                );
-            }
-        }
-        // FIXME, filter out unmatched urls before notify: version, group, protocol, registry
-        notify(urls);
-    }
-
-    private URL turnRegistryUrlToConsumerUrl(URL url) {
-        return URLBuilder.from(url)
-                .setPath(url.getServiceInterface())
-                .clearParameters()
-                .addParameters(queryMap)
-                .removeParameter(MONITOR_KEY)
-                .build();
-    }
-
-    public void setProtocol(Protocol protocol) {
-        this.protocol = protocol;
-    }
-
-    public void setRegistry(Registry registry) {
-        this.registry = registry;
-    }
-
-    public Registry getRegistry() {
-        return registry;
-    }
-
-    public boolean isShouldRegister() {
-        return shouldRegister;
-    }
-
     public void subscribe(URL url) {
         setConsumerUrl(url);
         CONSUMER_CONFIGURATION_LISTENER.addNotifyListener(this);
-        serviceConfigurationListener = new ReferenceConfigurationListener(this, url);
+        referenceConfigurationListener = new ReferenceConfigurationListener(this, url);
         registry.subscribe(url, this);
     }
 
+    @Override
     public void unSubscribe(URL url) {
         setConsumerUrl(null);
         CONSUMER_CONFIGURATION_LISTENER.removeNotifyListener(this);
-        serviceConfigurationListener.stop();
+        referenceConfigurationListener.stop();
         registry.unsubscribe(url, this);
     }
 
-
     @Override
     public void destroy() {
         if (isDestroyed()) {
@@ -317,7 +197,6 @@ public class RegistryDirectory<T> extends AbstractDirectory<T> implements Notify
      *
      * @param invokerUrls this parameter can't be null
      */
-    // TODO: 2017/8/31 FIXME The thread pool should be used to refresh the address, otherwise the task may be accumulated.
     private void refreshInvoker(List<URL> invokerUrls) {
         Assert.notNull(invokerUrls, "invokerUrls should not be null");
 
@@ -330,7 +209,7 @@ public class RegistryDirectory<T> extends AbstractDirectory<T> implements Notify
             destroyAllInvokers(); // Close all invokers
         } else {
             this.forbidden = false; // Allow to access
-            Map<String, Invoker<T>> oldUrlInvokerMap = this.urlInvokerMap; // local reference
+            Map<URL, Invoker<T>> oldUrlInvokerMap = this.urlInvokerMap; // local reference
             if (invokerUrls == Collections.<URL>emptyList()) {
                 invokerUrls = new ArrayList<>();
             }
@@ -343,7 +222,7 @@ public class RegistryDirectory<T> extends AbstractDirectory<T> implements Notify
             if (invokerUrls.isEmpty()) {
                 return;
             }
-            Map<String, Invoker<T>> newUrlInvokerMap = toInvokers(invokerUrls);// Translate url list to Invoker map
+            Map<URL, Invoker<T>> newUrlInvokerMap = toInvokers(invokerUrls);// Translate url list to Invoker map
 
             /**
              * If the calculation is wrong, it is not processed.
@@ -435,12 +314,12 @@ public class RegistryDirectory<T> extends AbstractDirectory<T> implements Notify
      * @param urls
      * @return invokers
      */
-    private Map<String, Invoker<T>> toInvokers(List<URL> urls) {
-        Map<String, Invoker<T>> newUrlInvokerMap = new HashMap<>();
+    private Map<URL, Invoker<T>> toInvokers(List<URL> urls) {
+        Map<URL, Invoker<T>> newUrlInvokerMap = new HashMap<>();
         if (urls == null || urls.isEmpty()) {
             return newUrlInvokerMap;
         }
-        Set<String> keys = new HashSet<>();
+        Set<URL> keys = new HashSet<>();
         String queryProtocols = this.queryMap.get(PROTOCOL_KEY);
         for (URL providerUrl : urls) {
             // If protocol is configured at the reference side, only the matching protocol is selected
@@ -469,14 +348,13 @@ public class RegistryDirectory<T> extends AbstractDirectory<T> implements Notify
             }
             URL url = mergeUrl(providerUrl);
 
-            String key = url.toFullString(); // The parameter urls are sorted
-            if (keys.contains(key)) { // Repeated url
+            if (keys.contains(url)) { // Repeated url
                 continue;
             }
-            keys.add(key);
+            keys.add(url);
             // Cache key is url that does not merge with consumer side parameters, regardless of how the consumer combines parameters, if the server url changes, then refer again
-            Map<String, Invoker<T>> localUrlInvokerMap = this.urlInvokerMap; // local reference
-            Invoker<T> invoker = localUrlInvokerMap == null ? null : localUrlInvokerMap.get(key);
+            Map<URL, Invoker<T>> localUrlInvokerMap = this.urlInvokerMap; // local reference
+            Invoker<T> invoker = localUrlInvokerMap == null ? null : localUrlInvokerMap.get(url);
             if (invoker == null) { // Not in the cache, refer again
                 try {
                     boolean enabled = true;
@@ -492,10 +370,10 @@ public class RegistryDirectory<T> extends AbstractDirectory<T> implements Notify
                     logger.error("Failed to refer invoker for interface:" + serviceType + ",url:(" + url + ")" + t.getMessage(), t);
                 }
                 if (invoker != null) { // Put new invoker in cache
-                    newUrlInvokerMap.put(key, invoker);
+                    newUrlInvokerMap.put(url, invoker);
                 }
             } else {
-                newUrlInvokerMap.put(key, invoker);
+                newUrlInvokerMap.put(url, invoker);
             }
         }
         keys.clear();
@@ -545,8 +423,8 @@ public class RegistryDirectory<T> extends AbstractDirectory<T> implements Notify
         providerUrl = overrideWithConfigurators(CONSUMER_CONFIGURATION_LISTENER.getConfigurators(), providerUrl);
 
         // override url with configurator from configurators from "service-name.configurators"
-        if (serviceConfigurationListener != null) {
-            providerUrl = overrideWithConfigurators(serviceConfigurationListener.getConfigurators(), providerUrl);
+        if (referenceConfigurationListener != null) {
+            providerUrl = overrideWithConfigurators(referenceConfigurationListener.getConfigurators(), providerUrl);
         }
 
         return providerUrl;
@@ -565,7 +443,7 @@ public class RegistryDirectory<T> extends AbstractDirectory<T> implements Notify
      * Close all invokers
      */
     private void destroyAllInvokers() {
-        Map<String, Invoker<T>> localUrlInvokerMap = this.urlInvokerMap; // local reference
+        Map<URL, Invoker<T>> localUrlInvokerMap = this.urlInvokerMap; // local reference
         if (localUrlInvokerMap != null) {
             for (Invoker<T> invoker : new ArrayList<>(localUrlInvokerMap.values())) {
                 try {
@@ -586,16 +464,16 @@ public class RegistryDirectory<T> extends AbstractDirectory<T> implements Notify
      * @param oldUrlInvokerMap
      * @param newUrlInvokerMap
      */
-    private void destroyUnusedInvokers(Map<String, Invoker<T>> oldUrlInvokerMap, Map<String, Invoker<T>> newUrlInvokerMap) {
+    private void destroyUnusedInvokers(Map<URL, Invoker<T>> oldUrlInvokerMap, Map<URL, Invoker<T>> newUrlInvokerMap) {
         if (newUrlInvokerMap == null || newUrlInvokerMap.size() == 0) {
             destroyAllInvokers();
             return;
         }
         // check deleted invoker
-        List<String> deleted = null;
+        List<URL> deleted = null;
         if (oldUrlInvokerMap != null) {
             Collection<Invoker<T>> newInvokers = newUrlInvokerMap.values();
-            for (Map.Entry<String, Invoker<T>> entry : oldUrlInvokerMap.entrySet()) {
+            for (Map.Entry<URL, Invoker<T>> entry : oldUrlInvokerMap.entrySet()) {
                 if (!newInvokers.contains(entry.getValue())) {
                     if (deleted == null) {
                         deleted = new ArrayList<>();
@@ -606,7 +484,7 @@ public class RegistryDirectory<T> extends AbstractDirectory<T> implements Notify
         }
 
         if (deleted != null) {
-            for (String url : deleted) {
+            for (URL url : deleted) {
                 if (url != null) {
                     Invoker<T> invoker = oldUrlInvokerMap.remove(url);
                     if (invoker != null) {
@@ -683,7 +561,7 @@ public class RegistryDirectory<T> extends AbstractDirectory<T> implements Notify
         if (isDestroyed()) {
             return false;
         }
-        Map<String, Invoker<T>> localUrlInvokerMap = urlInvokerMap;
+        Map<URL, Invoker<T>> localUrlInvokerMap = urlInvokerMap;
         if (localUrlInvokerMap != null && localUrlInvokerMap.size() > 0) {
             for (Invoker<T> invoker : new ArrayList<>(localUrlInvokerMap.values())) {
                 if (invoker.isAvailable()) {
@@ -701,7 +579,7 @@ public class RegistryDirectory<T> extends AbstractDirectory<T> implements Notify
     /**
      * Haomin: added for test purpose
      */
-    public Map<String, Invoker<T>> getUrlInvokerMap() {
+    public Map<URL, Invoker<T>> getUrlInvokerMap() {
         return urlInvokerMap;
     }
 
@@ -733,8 +611,8 @@ public class RegistryDirectory<T> extends AbstractDirectory<T> implements Notify
         doOverrideUrl(localConfigurators);
         List<Configurator> localAppDynamicConfigurators = CONSUMER_CONFIGURATION_LISTENER.getConfigurators(); // local reference
         doOverrideUrl(localAppDynamicConfigurators);
-        if (serviceConfigurationListener != null) {
-            List<Configurator> localDynamicConfigurators = serviceConfigurationListener.getConfigurators(); // local reference
+        if (referenceConfigurationListener != null) {
+            List<Configurator> localDynamicConfigurators = referenceConfigurationListener.getConfigurators(); // local reference
             doOverrideUrl(localDynamicConfigurators);
         }
     }
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryInvokerWrapper.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryInvokerWrapper.java
index b7e03c4..c6ce46f 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryInvokerWrapper.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryInvokerWrapper.java
@@ -22,17 +22,18 @@ import org.apache.dubbo.rpc.Invoker;
 import org.apache.dubbo.rpc.Result;
 import org.apache.dubbo.rpc.RpcException;
 import org.apache.dubbo.rpc.cluster.Cluster;
-import org.apache.dubbo.rpc.cluster.ClusterInvoker;
 
-class RegistryInvokerWrapper<T> implements ClusterInvoker<T> {
-    private RegistryDirectory<T> directory;
+class RegistryInvokerWrapper<T> implements Invoker<T> {
+    private DynamicDirectory<T> directory;
     private Cluster cluster;
     private Invoker<T> invoker;
+    private URL url;
 
-    public RegistryInvokerWrapper(RegistryDirectory<T> directory, Cluster cluster, Invoker<T> invoker) {
+    public RegistryInvokerWrapper(DynamicDirectory<T> directory, Cluster cluster, Invoker<T> invoker, URL url) {
         this.directory = directory;
         this.cluster = cluster;
         this.invoker = invoker;
+        this.url = url;
     }
 
     @Override
@@ -47,14 +48,18 @@ class RegistryInvokerWrapper<T> implements ClusterInvoker<T> {
 
     @Override
     public URL getUrl() {
-        return invoker.getUrl();
+        return url;
+    }
+
+    public void setUrl(URL url) {
+        this.url = url;
     }
 
     public void setInvoker(Invoker<T> invoker) {
         this.invoker = invoker;
     }
 
-    public RegistryDirectory<T> getDirectory() {
+    public DynamicDirectory<T> getDirectory() {
         return directory;
     }
 
@@ -71,9 +76,4 @@ class RegistryInvokerWrapper<T> implements ClusterInvoker<T> {
     public void destroy() {
         invoker.destroy();
     }
-
-    @Override
-    public URL getRegistryUrl() {
-        return directory.getUrl();
-    }
 }
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocol.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocol.java
index 9a4acc8..976d0da 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocol.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocol.java
@@ -454,8 +454,9 @@ public class RegistryProtocol implements Protocol {
         return doRefer(cluster, registry, type, url);
     }
 
-    private <T> Invoker<T> doRefer(Cluster cluster, Registry registry, Class<T> type, URL url) {
-        RegistryDirectory<T> directory = new RegistryDirectory<T>(type, url);
+    protected <T> Invoker<T> doRefer(Cluster cluster, Registry registry, Class<T> type, URL url) {
+        // FIXME, SPI extension, support prototype instance
+        DynamicDirectory<T> directory = createDirectory(type, url);
         directory.setRegistry(registry);
         directory.setProtocol(protocol);
         // all attributes of REFER_KEY
@@ -481,6 +482,10 @@ public class RegistryProtocol implements Protocol {
         return registryInvokerWrapper;
     }
 
+    protected <T> DynamicDirectory<T> createDirectory(Class<T> type, URL url) {
+        return new RegistryDirectory<T>(type, url);
+    }
+
     public <T> void reRefer(Invoker<T> invoker, URL newSubscribeUrl) {
         if (!(invoker instanceof RegistryInvokerWrapper)) {
             return;
@@ -488,7 +493,7 @@ public class RegistryProtocol implements Protocol {
 
         RegistryInvokerWrapper<T> invokerWrapper = (RegistryInvokerWrapper<T>) invoker;
         URL oldSubscribeUrl = invokerWrapper.getUrl();
-        RegistryDirectory<T> directory = invokerWrapper.getDirectory();
+        DynamicDirectory<T> directory = invokerWrapper.getDirectory();
         Registry registry = directory.getRegistry();
         registry.unregister(directory.getRegisteredConsumerUrl());
         directory.unSubscribe(toSubscribeUrl(oldSubscribeUrl));
diff --git a/dubbo-registry/dubbo-registry-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.RegistryClusterIdentifier b/dubbo-registry/dubbo-registry-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.RegistryClusterIdentifier
new file mode 100644
index 0000000..2b435cc
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.RegistryClusterIdentifier
@@ -0,0 +1 @@
+default=org.apache.dubbo.registry.client.DefaultRegistryClusterIdentifier
\ No newline at end of file
diff --git a/dubbo-registry/dubbo-registry-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.integration.RegistryProtocolListener b/dubbo-registry/dubbo-registry-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.integration.RegistryProtocolListener
new file mode 100644
index 0000000..d60633c
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.integration.RegistryProtocolListener
@@ -0,0 +1 @@
+service-discovery=org.apache.dubbo.registry.client.ServiceDiscoveryRegistryProtocolListener
\ No newline at end of file
diff --git a/dubbo-registry/dubbo-registry-consul/src/main/java/org/apache/dubbo/registry/consul/ConsulServiceDiscovery.java b/dubbo-registry/dubbo-registry-consul/src/main/java/org/apache/dubbo/registry/consul/ConsulServiceDiscovery.java
index b05f1d8..0c330aa 100644
--- a/dubbo-registry/dubbo-registry-consul/src/main/java/org/apache/dubbo/registry/consul/ConsulServiceDiscovery.java
+++ b/dubbo-registry/dubbo-registry-consul/src/main/java/org/apache/dubbo/registry/consul/ConsulServiceDiscovery.java
@@ -161,6 +161,11 @@ public class ConsulServiceDiscovery implements ServiceDiscovery, EventListener<S
         return StringUtils.splitToList(value, COMMA_SEPARATOR_CHAR);
     }
 
+    @Override
+    public URL getUrl() {
+        return url;
+    }
+
     private List<String> getRegisteringTags(URL url) {
         List<String> tags = new ArrayList<>();
         String rawTag = url.getParameter(REGISTER_TAG);
@@ -190,7 +195,7 @@ public class ConsulServiceDiscovery implements ServiceDiscovery, EventListener<S
     @Override
     public void addServiceInstancesChangedListener(ServiceInstancesChangedListener listener) throws NullPointerException, IllegalArgumentException {
         if (notifier == null) {
-            String serviceName = listener.getServiceName();
+            String serviceName = listener.getServiceNames();
             Response<List<HealthService>> response = getHealthServices(serviceName, -1, buildWatchTimeout());
             Long consulIndex = response.getConsulIndex();
             notifier = new ConsulNotifier(serviceName, consulIndex);
diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/config/AbstractPrefixConfigurationTest.java b/dubbo-registry/dubbo-registry-consul/src/main/java/org/apache/dubbo/registry/consul/ConsulServiceDiscoveryFactory.java
similarity index 65%
copy from dubbo-common/src/test/java/org/apache/dubbo/common/config/AbstractPrefixConfigurationTest.java
copy to dubbo-registry/dubbo-registry-consul/src/main/java/org/apache/dubbo/registry/consul/ConsulServiceDiscoveryFactory.java
index ba4b5b8..bd77db8 100644
--- a/dubbo-common/src/test/java/org/apache/dubbo/common/config/AbstractPrefixConfigurationTest.java
+++ b/dubbo-registry/dubbo-registry-consul/src/main/java/org/apache/dubbo/registry/consul/ConsulServiceDiscoveryFactory.java
@@ -14,10 +14,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.common.config;
+package org.apache.dubbo.registry.consul;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.registry.client.AbstractServiceDiscoveryFactory;
+import org.apache.dubbo.registry.client.ServiceDiscovery;
+
+public class ConsulServiceDiscoveryFactory extends AbstractServiceDiscoveryFactory {
+
+    @Override
+    protected ServiceDiscovery createDiscovery(URL registryURL) {
+        return new ConsulServiceDiscovery();
+    }
 
-/**
- *
- */
-public class AbstractPrefixConfigurationTest {
 }
diff --git a/dubbo-registry/dubbo-registry-consul/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceDiscovery b/dubbo-registry/dubbo-registry-consul/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceDiscovery
deleted file mode 100644
index e46973b..0000000
--- a/dubbo-registry/dubbo-registry-consul/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceDiscovery
+++ /dev/null
@@ -1 +0,0 @@
-consul=org.apache.dubbo.registry.consul.ConsulServiceDiscovery
\ No newline at end of file
diff --git a/dubbo-registry/dubbo-registry-consul/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceDiscoveryFactory b/dubbo-registry/dubbo-registry-consul/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceDiscoveryFactory
new file mode 100644
index 0000000..a0f1252
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-consul/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceDiscoveryFactory
@@ -0,0 +1 @@
+consul=org.apache.dubbo.registry.consul.ConsulServiceDiscoveryFactory
\ No newline at end of file
diff --git a/dubbo-registry/dubbo-registry-default/src/test/java/org/apache/dubbo/registry/dubbo/RegistryDirectoryTest.java b/dubbo-registry/dubbo-registry-default/src/test/java/org/apache/dubbo/registry/dubbo/RegistryDirectoryTest.java
index c38a7f2..9cc954e 100644
--- a/dubbo-registry/dubbo-registry-default/src/test/java/org/apache/dubbo/registry/dubbo/RegistryDirectoryTest.java
+++ b/dubbo-registry/dubbo-registry-default/src/test/java/org/apache/dubbo/registry/dubbo/RegistryDirectoryTest.java
@@ -440,7 +440,7 @@ public class RegistryDirectoryTest {
         registryDirectory.destroy();
 
         List<Invoker<RegistryDirectoryTest>> cachedInvokers = registryDirectory.getInvokers();
-        Map<String, Invoker<RegistryDirectoryTest>> urlInvokerMap = registryDirectory.getUrlInvokerMap();
+        Map<URL, Invoker<RegistryDirectoryTest>> urlInvokerMap = registryDirectory.getUrlInvokerMap();
 
         Assertions.assertNull(cachedInvokers);
         Assertions.assertEquals(0, urlInvokerMap.size());
diff --git a/dubbo-registry/dubbo-registry-etcd3/src/main/java/org/apache/dubbo/registry/etcd/EtcdServiceDiscovery.java b/dubbo-registry/dubbo-registry-etcd3/src/main/java/org/apache/dubbo/registry/etcd/EtcdServiceDiscovery.java
index a26ebc6..7916de2 100644
--- a/dubbo-registry/dubbo-registry-etcd3/src/main/java/org/apache/dubbo/registry/etcd/EtcdServiceDiscovery.java
+++ b/dubbo-registry/dubbo-registry-etcd3/src/main/java/org/apache/dubbo/registry/etcd/EtcdServiceDiscovery.java
@@ -158,7 +158,7 @@ public class EtcdServiceDiscovery implements ServiceDiscovery, EventListener<Ser
 
     @Override
     public void addServiceInstancesChangedListener(ServiceInstancesChangedListener listener) throws NullPointerException, IllegalArgumentException {
-        registerServiceWatcher(listener.getServiceName());
+        registerServiceWatcher(listener.getServiceNames());
     }
 
     @Override
diff --git a/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/NacosServiceDiscovery.java b/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/NacosServiceDiscovery.java
index b84bfa9..91de49d 100644
--- a/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/NacosServiceDiscovery.java
+++ b/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/NacosServiceDiscovery.java
@@ -54,10 +54,13 @@ public class NacosServiceDiscovery implements ServiceDiscovery {
 
     private NamingService namingService;
 
+    private URL registryURL;
+
     @Override
     public void initialize(URL registryURL) throws Exception {
         this.namingService = createNamingService(registryURL);
         this.group = getGroup(registryURL);
+        this.registryURL = registryURL;
     }
 
     @Override
@@ -109,7 +112,7 @@ public class NacosServiceDiscovery implements ServiceDiscovery {
     public void addServiceInstancesChangedListener(ServiceInstancesChangedListener listener)
             throws NullPointerException, IllegalArgumentException {
         execute(namingService, service -> {
-            service.subscribe(listener.getServiceName(), e -> { // Register Nacos EventListener
+            service.subscribe(listener.getServiceNames(), e -> { // Register Nacos EventListener
                 if (e instanceof NamingEvent) {
                     NamingEvent event = (NamingEvent) e;
                     handleEvent(event, listener);
@@ -118,6 +121,11 @@ public class NacosServiceDiscovery implements ServiceDiscovery {
         });
     }
 
+    @Override
+    public URL getUrl() {
+        return registryURL;
+    }
+
     private void handleEvent(NamingEvent event, ServiceInstancesChangedListener listener) {
         String serviceName = event.getServiceName();
         List<ServiceInstance> serviceInstances = event.getInstances()
diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/config/AbstractPrefixConfigurationTest.java b/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/NacosServiceDiscoveryFactory.java
similarity index 65%
copy from dubbo-common/src/test/java/org/apache/dubbo/common/config/AbstractPrefixConfigurationTest.java
copy to dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/NacosServiceDiscoveryFactory.java
index ba4b5b8..c9c7fca 100644
--- a/dubbo-common/src/test/java/org/apache/dubbo/common/config/AbstractPrefixConfigurationTest.java
+++ b/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/NacosServiceDiscoveryFactory.java
@@ -14,10 +14,20 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.common.config;
+package org.apache.dubbo.registry.nacos;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.registry.client.AbstractServiceDiscoveryFactory;
+import org.apache.dubbo.registry.client.ServiceDiscovery;
 
 /**
  *
  */
-public class AbstractPrefixConfigurationTest {
+public class NacosServiceDiscoveryFactory extends AbstractServiceDiscoveryFactory {
+
+    @Override
+    protected ServiceDiscovery createDiscovery(URL registryURL) {
+        return new NacosServiceDiscovery();
+    }
+
 }
diff --git a/dubbo-registry/dubbo-registry-nacos/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceDiscoveryFactory b/dubbo-registry/dubbo-registry-nacos/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceDiscoveryFactory
new file mode 100644
index 0000000..4fc3f4a
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-nacos/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceDiscoveryFactory
@@ -0,0 +1 @@
+nacos=org.apache.dubbo.registry.nacos.NacosServiceDiscoveryFactory
\ No newline at end of file
diff --git a/dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/ZookeeperServiceDiscovery.java b/dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/ZookeeperServiceDiscovery.java
index 037b5af..29e6790 100644
--- a/dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/ZookeeperServiceDiscovery.java
+++ b/dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/ZookeeperServiceDiscovery.java
@@ -24,10 +24,8 @@ import org.apache.dubbo.common.logger.LoggerFactory;
 import org.apache.dubbo.common.utils.DefaultPage;
 import org.apache.dubbo.common.utils.Page;
 import org.apache.dubbo.event.EventDispatcher;
-import org.apache.dubbo.event.EventListener;
 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.event.listener.ServiceInstancesChangedListener;
 
 import org.apache.curator.framework.CuratorFramework;
@@ -52,10 +50,12 @@ import static org.apache.dubbo.registry.zookeeper.util.CuratorFrameworkUtils.bui
  * Zookeeper {@link ServiceDiscovery} implementation based on
  * <a href="https://curator.apache.org/curator-x-discovery/index.html">Apache Curator X Discovery</a>
  */
-public class ZookeeperServiceDiscovery implements ServiceDiscovery, EventListener<ServiceInstancesChangedEvent> {
+public class ZookeeperServiceDiscovery implements ServiceDiscovery {
 
     private final Logger logger = LoggerFactory.getLogger(getClass());
 
+    private URL registryURL;
+
     private EventDispatcher dispatcher;
 
     private CuratorFramework curatorFramework;
@@ -71,14 +71,18 @@ public class ZookeeperServiceDiscovery implements ServiceDiscovery, EventListene
 
     @Override
     public void initialize(URL registryURL) throws Exception {
-        this.dispatcher = EventDispatcher.getDefaultExtension();
-        this.dispatcher.addEventListener(this);
+        this.registryURL = registryURL;
         this.curatorFramework = buildCuratorFramework(registryURL);
         this.rootPath = ROOT_PATH.getParameterValue(registryURL);
         this.serviceDiscovery = buildServiceDiscovery(curatorFramework, rootPath);
         this.serviceDiscovery.start();
     }
 
+    @Override
+    public URL getUrl() {
+        return registryURL;
+    }
+
     public void destroy() throws Exception {
         serviceDiscovery.close();
     }
@@ -147,7 +151,7 @@ public class ZookeeperServiceDiscovery implements ServiceDiscovery, EventListene
     @Override
     public void addServiceInstancesChangedListener(ServiceInstancesChangedListener listener)
             throws NullPointerException, IllegalArgumentException {
-        registerServiceWatcher(listener.getServiceName());
+        listener.getServiceNames().forEach(serviceName -> registerServiceWatcher(serviceName, listener));
     }
 
     private void doInServiceRegistry(ThrowableConsumer<org.apache.curator.x.discovery.ServiceDiscovery> consumer) {
@@ -160,10 +164,10 @@ public class ZookeeperServiceDiscovery implements ServiceDiscovery, EventListene
         return execute(serviceDiscovery, function);
     }
 
-    protected void registerServiceWatcher(String serviceName) {
+    protected void registerServiceWatcher(String serviceName, ServiceInstancesChangedListener listener) {
         String path = buildServicePath(serviceName);
         CuratorWatcher watcher = watcherCaches.computeIfAbsent(path, key ->
-                new ZookeeperServiceDiscoveryChangeWatcher(this, serviceName));
+                new ZookeeperServiceDiscoveryChangeWatcher(this, serviceName, listener));
         try {
             curatorFramework.getChildren().usingWatcher(watcher).forPath(path);
         } catch (KeeperException.NoNodeException e) {
@@ -179,11 +183,4 @@ public class ZookeeperServiceDiscovery implements ServiceDiscovery, EventListene
     private String buildServicePath(String serviceName) {
         return rootPath + "/" + serviceName;
     }
-
-    @Override
-    public void onEvent(ServiceInstancesChangedEvent event) {
-        String serviceName = event.getServiceName();
-        // re-register again
-        registerServiceWatcher(serviceName);
-    }
 }
diff --git a/dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/ZookeeperServiceDiscoveryChangeWatcher.java b/dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/ZookeeperServiceDiscoveryChangeWatcher.java
index 325c80c..5ee429a 100644
--- a/dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/ZookeeperServiceDiscoveryChangeWatcher.java
+++ b/dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/ZookeeperServiceDiscoveryChangeWatcher.java
@@ -18,6 +18,7 @@ package org.apache.dubbo.registry.zookeeper;
 
 import org.apache.dubbo.registry.client.ServiceDiscovery;
 import org.apache.dubbo.registry.client.event.ServiceInstancesChangedEvent;
+import org.apache.dubbo.registry.client.event.listener.ServiceInstancesChangedListener;
 
 import org.apache.curator.framework.api.CuratorWatcher;
 import org.apache.zookeeper.WatchedEvent;
@@ -34,15 +35,18 @@ import static org.apache.zookeeper.Watcher.Event.EventType.NodeDataChanged;
  * @since 2.7.5
  */
 public class ZookeeperServiceDiscoveryChangeWatcher implements CuratorWatcher {
+    private ServiceInstancesChangedListener listener;
 
     private final ZookeeperServiceDiscovery zookeeperServiceDiscovery;
 
     private final String serviceName;
 
     public ZookeeperServiceDiscoveryChangeWatcher(ZookeeperServiceDiscovery zookeeperServiceDiscovery,
-                                                  String serviceName) {
+                                                  String serviceName,
+                                                  ServiceInstancesChangedListener listener) {
         this.zookeeperServiceDiscovery = zookeeperServiceDiscovery;
         this.serviceName = serviceName;
+        this.listener = listener;
     }
 
     @Override
@@ -51,6 +55,8 @@ public class ZookeeperServiceDiscoveryChangeWatcher implements CuratorWatcher {
         Watcher.Event.EventType eventType = event.getType();
 
         if (NodeChildrenChanged.equals(eventType) || NodeDataChanged.equals(eventType)) {
+            listener.onEvent(new ServiceInstancesChangedEvent(serviceName, zookeeperServiceDiscovery.getInstances(serviceName)));
+            zookeeperServiceDiscovery.registerServiceWatcher(serviceName, listener);
             zookeeperServiceDiscovery.dispatchServiceInstancesChangedEvent(serviceName);
         }
     }
diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/config/AbstractPrefixConfigurationTest.java b/dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/ZookeeperServiceDiscoveryFactory.java
similarity index 64%
rename from dubbo-common/src/test/java/org/apache/dubbo/common/config/AbstractPrefixConfigurationTest.java
rename to dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/ZookeeperServiceDiscoveryFactory.java
index ba4b5b8..9db8235 100644
--- a/dubbo-common/src/test/java/org/apache/dubbo/common/config/AbstractPrefixConfigurationTest.java
+++ b/dubbo-registry/dubbo-registry-zookeeper/src/main/java/org/apache/dubbo/registry/zookeeper/ZookeeperServiceDiscoveryFactory.java
@@ -14,10 +14,16 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.common.config;
+package org.apache.dubbo.registry.zookeeper;
 
-/**
- *
- */
-public class AbstractPrefixConfigurationTest {
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.registry.client.AbstractServiceDiscoveryFactory;
+import org.apache.dubbo.registry.client.ServiceDiscovery;
+
+public class ZookeeperServiceDiscoveryFactory extends AbstractServiceDiscoveryFactory {
+
+    @Override
+    protected ServiceDiscovery createDiscovery(URL registryURL) {
+        return new ZookeeperServiceDiscovery();
+    }
 }
diff --git a/dubbo-registry/pom.xml b/dubbo-registry/pom.xml
index d141b29..5ab688a 100644
--- a/dubbo-registry/pom.xml
+++ b/dubbo-registry/pom.xml
@@ -31,15 +31,15 @@
     </properties>
     <modules>
         <module>dubbo-registry-api</module>
-        <module>dubbo-registry-default</module>
-        <module>dubbo-registry-multicast</module>
+        <!--        <module>dubbo-registry-default</module>-->
+        <!--        <module>dubbo-registry-multicast</module>-->
         <module>dubbo-registry-zookeeper</module>
-        <module>dubbo-registry-redis</module>
-        <module>dubbo-registry-consul</module>
-        <module>dubbo-registry-etcd3</module>
-        <module>dubbo-registry-nacos</module>
-        <module>dubbo-registry-multiple</module>
-        <module>dubbo-registry-sofa</module>
-        <module>dubbo-registry-eureka</module>
+        <!--        <module>dubbo-registry-redis</module>-->
+        <!--        <module>dubbo-registry-consul</module>-->
+        <!--        <module>dubbo-registry-etcd3</module>-->
+        <!--        <module>dubbo-registry-nacos</module>-->
+        <!--        <module>dubbo-registry-multiple</module>-->
+        <!--        <module>dubbo-registry-sofa</module>-->
+        <!--        <module>dubbo-registry-eureka</module>-->
     </modules>
 </project>
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/RpcContext.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/RpcContext.java
index 66ac06f..2c3d2b8 100644
--- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/RpcContext.java
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/RpcContext.java
@@ -34,8 +34,12 @@ import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.Future;
 
 import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER_SIDE;
+import static org.apache.dubbo.common.constants.CommonConstants.DUBBO;
+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.PROVIDER_SIDE;
 import static org.apache.dubbo.common.constants.CommonConstants.SIDE_KEY;
+import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY;
 import static org.apache.dubbo.rpc.Constants.ASYNC_KEY;
 import static org.apache.dubbo.rpc.Constants.RETURN_KEY;
 
@@ -795,4 +799,79 @@ public class RpcContext {
         return asyncContext;
     }
 
+    // RPC service context updated before each service call.
+    private String group;
+    private String version;
+    private String interfaceName;
+    private String protocol;
+    private String serviceKey;
+    private String protocolServiceKey;
+    private URL consumerUrl;
+
+    public String getGroup() {
+        return group;
+    }
+
+    public void setGroup(String group) {
+        this.group = group;
+    }
+
+    public String getVersion() {
+        return version;
+    }
+
+    public void setVersion(String version) {
+        this.version = version;
+    }
+
+    public String getInterfaceName() {
+        return interfaceName;
+    }
+
+    public void setInterfaceName(String interfaceName) {
+        this.interfaceName = interfaceName;
+    }
+
+    public String getProtocol() {
+        return protocol;
+    }
+
+    public void setProtocol(String protocol) {
+        this.protocol = protocol;
+    }
+
+    public String getServiceKey() {
+        return serviceKey;
+    }
+
+    public void setServiceKey(String serviceKey) {
+        this.serviceKey = serviceKey;
+    }
+
+    public String getProtocolServiceKey() {
+        return protocolServiceKey;
+    }
+
+    public void setProtocolServiceKey(String protocolServiceKey) {
+        this.protocolServiceKey = protocolServiceKey;
+    }
+
+    public URL getConsumerUrl() {
+        return consumerUrl;
+    }
+
+    public void setConsumerUrl(URL consumerUrl) {
+        this.consumerUrl = consumerUrl;
+    }
+
+    public static void setRpcContext(URL url) {
+        RpcContext rpcContext = RpcContext.getContext();
+        rpcContext.setConsumerUrl(url);
+        rpcContext.setInterfaceName(url.getServiceInterface());
+        rpcContext.setVersion(url.getParameter(VERSION_KEY));
+        rpcContext.setGroup(url.getParameter(GROUP_KEY));
+        rpcContext.setProtocol(url.getParameter(PROTOCOL_KEY, DUBBO));
+        rpcContext.setServiceKey(url.getServiceKey());
+        rpcContext.setProtocolServiceKey(url.getProtocolServiceKey());
+    }
 }
\ No newline at end of file
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/proxy/InvokerInvocationHandler.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/proxy/InvokerInvocationHandler.java
index 9041975..b5c9e67 100644
--- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/proxy/InvokerInvocationHandler.java
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/proxy/InvokerInvocationHandler.java
@@ -20,6 +20,7 @@ import org.apache.dubbo.common.logger.Logger;
 import org.apache.dubbo.common.logger.LoggerFactory;
 import org.apache.dubbo.rpc.Constants;
 import org.apache.dubbo.rpc.Invoker;
+import org.apache.dubbo.rpc.RpcContext;
 import org.apache.dubbo.rpc.RpcInvocation;
 import org.apache.dubbo.rpc.model.ApplicationModel;
 import org.apache.dubbo.rpc.model.ConsumerModel;
@@ -65,7 +66,9 @@ public class InvokerInvocationHandler implements InvocationHandler {
         RpcInvocation rpcInvocation = new RpcInvocation(method, invoker.getInterface().getName(), args);
         String serviceKey = invoker.getUrl().getServiceKey();
         rpcInvocation.setTargetServiceUniqueName(serviceKey);
-      
+
+        RpcContext.setRpcContext(invoker.getUrl());
+
         if (consumerModel != null) {
             rpcInvocation.put(Constants.CONSUMER_MODEL, consumerModel);
             rpcInvocation.put(Constants.METHOD_MODEL, consumerModel.getMethodModel(method));


[dubbo] 19/27: customize instance metadata.

Posted by li...@apache.org.
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

commit 69202d871ff1b5168bb1af09811173b13b215436
Author: ken.lj <ke...@gmail.com>
AuthorDate: Fri Jul 24 17:36:54 2020 +0800

    customize instance metadata.
---
 .../client/ServiceInstanceMetadataCustomizer.java  | 73 ------------------
 ...MetadataServiceURLParamsMetadataCustomizer.java | 31 ++++----
 .../ServiceInstanceMetadataCustomizer.java         | 86 ++++++++++++++++++++++
 ...dubbo.registry.client.ServiceInstanceCustomizer |  1 +
 4 files changed, 105 insertions(+), 86 deletions(-)

diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceInstanceMetadataCustomizer.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceInstanceMetadataCustomizer.java
deleted file mode 100644
index 457441f..0000000
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceInstanceMetadataCustomizer.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.registry.client;
-
-import java.util.Map;
-
-import static org.apache.dubbo.common.utils.StringUtils.isBlank;
-
-/**
- * The abstract class to customize {@link ServiceInstance#getMetadata()}  the service instances' metadata}
- *
- * @see ServiceInstance#getMetadata()
- * @see ServiceInstanceCustomizer
- * @since 2.7.5
- */
-public abstract class ServiceInstanceMetadataCustomizer implements ServiceInstanceCustomizer {
-
-    @Override
-    public final void customize(ServiceInstance serviceInstance) {
-
-        Map<String, String> metadata = serviceInstance.getMetadata();
-
-        String propertyName = resolveMetadataPropertyName(serviceInstance);
-        String propertyValue = resolveMetadataPropertyValue(serviceInstance);
-
-        if (!isBlank(propertyName) && !isBlank(propertyValue)) {
-            String existedValue = metadata.get(propertyName);
-            boolean put = existedValue == null || isOverride();
-            if (put) {
-                metadata.put(propertyName, propertyValue);
-            }
-        }
-    }
-
-    /**
-     * Resolve the property name of metadata
-     *
-     * @param serviceInstance the instance of {@link ServiceInstance}
-     * @return non-null key
-     */
-    protected abstract String resolveMetadataPropertyName(ServiceInstance serviceInstance);
-
-    /**
-     * Resolve the property value of metadata
-     *
-     * @param serviceInstance the instance of {@link ServiceInstance}
-     * @return non-null value
-     */
-    protected abstract String resolveMetadataPropertyValue(ServiceInstance serviceInstance);
-
-    /**
-     * Is override {@link ServiceInstance#getMetadata()}  the service instances' metadata} or not
-     *
-     * @return default is <code>false</code>
-     */
-    protected boolean isOverride() {
-        return false;
-    }
-}
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/MetadataServiceURLParamsMetadataCustomizer.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/MetadataServiceURLParamsMetadataCustomizer.java
index be78440..8b525c7 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/MetadataServiceURLParamsMetadataCustomizer.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/MetadataServiceURLParamsMetadataCustomizer.java
@@ -16,35 +16,40 @@
  */
 package org.apache.dubbo.registry.client.metadata;
 
-import org.apache.dubbo.common.URL;
 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.ServiceInstanceMetadataCustomizer;
+import org.apache.dubbo.registry.client.ServiceInstanceCustomizer;
 
+import java.util.Map;
 import java.util.SortedSet;
 
+import static org.apache.dubbo.common.utils.StringUtils.isBlank;
 import static org.apache.dubbo.metadata.MetadataService.toURLs;
 import static org.apache.dubbo.metadata.WritableMetadataService.getDefaultExtension;
 import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.METADATA_SERVICE_URL_PARAMS_PROPERTY_NAME;
 import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.getMetadataServiceParameter;
 
-/**
- * An {@link ServiceInstanceMetadataCustomizer} to customize the {@link URL urls} of {@link MetadataService}
- * into {@link ServiceInstance#getMetadata() the service instances' metadata}
- *
- * @see ServiceInstanceMetadataCustomizer
- * @since 2.7.5
- */
-public class MetadataServiceURLParamsMetadataCustomizer extends ServiceInstanceMetadataCustomizer {
+public class MetadataServiceURLParamsMetadataCustomizer implements ServiceInstanceCustomizer {
 
     @Override
-    public String resolveMetadataPropertyName(ServiceInstance serviceInstance) {
+    public void customize(ServiceInstance serviceInstance) {
+
+        Map<String, String> metadata = serviceInstance.getMetadata();
+
+        String propertyName = resolveMetadataPropertyName(serviceInstance);
+        String propertyValue = resolveMetadataPropertyValue(serviceInstance);
+
+        if (!isBlank(propertyName) && !isBlank(propertyValue)) {
+            metadata.put(propertyName, metadata.get(propertyName));
+        }
+    }
+
+    private String resolveMetadataPropertyName(ServiceInstance serviceInstance) {
         return METADATA_SERVICE_URL_PARAMS_PROPERTY_NAME;
     }
 
-    @Override
-    public String resolveMetadataPropertyValue(ServiceInstance serviceInstance) {
+    private String resolveMetadataPropertyValue(ServiceInstance serviceInstance) {
         WritableMetadataService writableMetadataService = getDefaultExtension();
 
         String serviceInterface = MetadataService.class.getName();
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/ServiceInstanceMetadataCustomizer.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/ServiceInstanceMetadataCustomizer.java
new file mode 100644
index 0000000..8d9824a
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/ServiceInstanceMetadataCustomizer.java
@@ -0,0 +1,86 @@
+/*
+ * 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.extension.ExtensionLoader;
+import org.apache.dubbo.common.infra.InfraAdapter;
+import org.apache.dubbo.common.utils.CollectionUtils;
+import org.apache.dubbo.metadata.MetadataInfo;
+import org.apache.dubbo.metadata.MetadataParamsFilter;
+import org.apache.dubbo.metadata.WritableMetadataService;
+import org.apache.dubbo.registry.client.ServiceInstance;
+import org.apache.dubbo.registry.client.ServiceInstanceCustomizer;
+import org.apache.dubbo.registry.client.metadata.store.InMemoryWritableMetadataService;
+import org.apache.dubbo.rpc.model.ApplicationModel;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import static org.apache.dubbo.common.constants.CommonConstants.APPLICATION_KEY;
+
+/**
+ *
+ */
+public class ServiceInstanceMetadataCustomizer implements ServiceInstanceCustomizer {
+
+    @Override
+    public void customize(ServiceInstance serviceInstance) {
+        Map<String, String> params = new HashMap<>();
+
+        ExtensionLoader<MetadataParamsFilter> loader = ExtensionLoader.getExtensionLoader(MetadataParamsFilter.class);
+        Set<MetadataParamsFilter> paramsFilters = loader.getSupportedExtensionInstances();
+
+        InMemoryWritableMetadataService localMetadataService
+                = (InMemoryWritableMetadataService) WritableMetadataService.getDefaultExtension();
+        // pick the first interface metadata available.
+        // FIXME, check the same key in different urls has the same value
+        MetadataInfo metadataInfo = localMetadataService.getMetadataInfos().values().iterator().next();
+        MetadataInfo.ServiceInfo serviceInfo = metadataInfo.getServices().values().iterator().next();
+        Map<String, String> allParams = new HashMap<>(serviceInfo.getParams());
+
+        // load instance params users want to load.
+        // TODO, duplicate logic with that in ApplicationConfig
+        Set<InfraAdapter> adapters = ExtensionLoader.getExtensionLoader(InfraAdapter.class).getSupportedExtensionInstances();
+        if (CollectionUtils.isNotEmpty(adapters)) {
+            Map<String, String> inputParameters = new HashMap<>();
+            inputParameters.put(APPLICATION_KEY, ApplicationModel.getName());
+            for (InfraAdapter adapter : adapters) {
+                Map<String, String> extraParameters = adapter.getExtraAttributes(inputParameters);
+                if (CollectionUtils.isNotEmptyMap(extraParameters)) {
+                    extraParameters.forEach(allParams::putIfAbsent);
+                }
+            }
+        }
+
+        if (CollectionUtils.isEmpty(paramsFilters)) {
+            serviceInstance.getMetadata().putAll(allParams);
+            return;
+        }
+
+        paramsFilters.forEach(filter -> {
+            String[] included = filter.instanceParamsIncluded();
+            if (included == null) {
+                serviceInstance.getMetadata().putAll(allParams);
+            } else {
+                for (String p : included) {
+                    serviceInstance.getMetadata().put(p, allParams.get(p));
+                }
+            }
+        });
+    }
+}
diff --git a/dubbo-registry/dubbo-registry-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceInstanceCustomizer b/dubbo-registry/dubbo-registry-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceInstanceCustomizer
index 9307410..1f517c8 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceInstanceCustomizer
+++ b/dubbo-registry/dubbo-registry-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceInstanceCustomizer
@@ -3,3 +3,4 @@ exported-revision=org.apache.dubbo.registry.client.metadata.ExportedServicesRevi
 subscribed-revision=org.apache.dubbo.registry.client.metadata.SubscribedServicesRevisionMetadataCustomizer
 protocol-ports=org.apache.dubbo.registry.client.metadata.ProtocolPortsMetadataCustomizer
 instance-port=org.apache.dubbo.config.metadata.ServiceInstancePortCustomizer
+instance-metadata=org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataCustomizer


[dubbo] 11/27: can basically work with InstanceAddressURL

Posted by li...@apache.org.
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

commit 71189cbe4bf6abae73771c94a4a2fd9500428b50
Author: ken.lj <ke...@gmail.com>
AuthorDate: Tue Jul 14 01:51:51 2020 +0800

    can basically work with InstanceAddressURL
---
 .../src/main/java/org/apache/dubbo/common/URL.java |  12 +-
 .../org/apache/dubbo/metadata/MetadataInfo.java    | 118 ++++++++++++++++++--
 .../registry/client/DefaultServiceInstance.java    |  27 +++--
 .../dubbo/registry/client/InstanceAddressURL.java  | 123 +++++++++++++++++----
 .../registry/client/ServiceDiscoveryRegistry.java  |   4 +-
 .../client/ServiceDiscoveryRegistryDirectory.java  |  69 +++++++-----
 .../metadata/ServiceInstanceMetadataUtils.java     |  18 +--
 .../registry/integration/DynamicDirectory.java     |   9 +-
 .../registry/integration/RegistryDirectory.java    |   2 +-
 .../apache/dubbo/remoting/exchange/Exchangers.java |   2 +-
 .../dubbo/remoting/transport/AbstractEndpoint.java |   2 +-
 .../dubbo/rpc/proxy/InvokerInvocationHandler.java  |   1 +
 .../dubbo/rpc/protocol/dubbo/DubboProtocol.java    |   5 +-
 .../dubbo/rpc/protocol/thrift/ThriftProtocol.java  |   2 +-
 14 files changed, 292 insertions(+), 102 deletions(-)

diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/URL.java b/dubbo-common/src/main/java/org/apache/dubbo/common/URL.java
index cc2b66f..e645822 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/URL.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/URL.java
@@ -97,19 +97,19 @@ class URL implements Serializable {
 
     private static final long serialVersionUID = -1985165475234910535L;
 
-    private final String protocol;
+    protected String protocol;
 
-    private final String username;
+    protected String username;
 
-    private final String password;
+    protected String password;
 
     // by default, host to registry
-    private final String host;
+    protected String host;
 
     // by default, port to registry
-    private final int port;
+    protected int port;
 
-    private final String path;
+    protected String path;
 
     private final Map<String, String> parameters;
 
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 4db0298..931a331 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
@@ -29,6 +29,7 @@ import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.SortedSet;
 import java.util.TreeSet;
 import java.util.concurrent.atomic.AtomicBoolean;
@@ -154,7 +155,13 @@ public class MetadataInfo implements Serializable {
         if (serviceInfo == null) {
             return Collections.emptyMap();
         }
-        return serviceInfo.getParams();
+        return serviceInfo.getAllParams();
+    }
+
+    @Override
+    public String toString() {
+        // FIXME
+        return super.toString();
     }
 
     public static class ServiceInfo implements Serializable {
@@ -163,9 +170,12 @@ public class MetadataInfo implements Serializable {
         private String group;
         private String version;
         private String protocol;
+        private String path; // most of the time, path is the same with the interface name.
         private Map<String, String> params;
 
+        private transient Map<String, String> consumerParams;
         private transient Map<String, Map<String, String>> methodParams;
+        private transient Map<String, Map<String, String>> consumerMethodParams;
         private transient String serviceKey;
         private transient String matchKey;
 
@@ -173,14 +183,7 @@ public class MetadataInfo implements Serializable {
         }
 
         public ServiceInfo(URL url) {
-            // FIXME, how to match registry.
-            this(
-                    url.getServiceInterface(),
-                    url.getParameter(GROUP_KEY),
-                    url.getParameter(VERSION_KEY),
-                    url.getProtocol(),
-                    null
-            );
+            this(url.getServiceInterface(), url.getParameter(GROUP_KEY), url.getParameter(VERSION_KEY), url.getProtocol(), url.getPath(), null);
 
             Map<String, String> params = new HashMap<>();
             List<MetadataParamsFilter> filters = loader.getActivateExtension(url, "params-filter");
@@ -207,11 +210,12 @@ public class MetadataInfo implements Serializable {
             this.params = params;
         }
 
-        public ServiceInfo(String name, String group, String version, String protocol, Map<String, String> params) {
+        public ServiceInfo(String name, String group, String version, String protocol, String path, Map<String, String> params) {
             this.name = name;
             this.group = group;
             this.version = version;
             this.protocol = protocol;
+            this.path = path;
             this.params = params == null ? new HashMap<>() : params;
 
             this.serviceKey = URL.buildKey(name, group, version);
@@ -266,6 +270,14 @@ public class MetadataInfo implements Serializable {
             this.version = version;
         }
 
+        public String getPath() {
+            return path;
+        }
+
+        public void setPath(String path) {
+            this.path = path;
+        }
+
         public Map<String, String> getParams() {
             if (params == null) {
                 return Collections.emptyMap();
@@ -277,16 +289,42 @@ public class MetadataInfo implements Serializable {
             this.params = params;
         }
 
+        public Map<String, String> getAllParams() {
+            if (consumerParams != null) {
+                Map<String, String> allParams = new HashMap<>((int) ((params.size() + consumerParams.size()) / 0.75f + 1));
+                allParams.putAll(params);
+                allParams.putAll(consumerParams);
+                return allParams;
+            }
+            return params;
+        }
+
         public String getParameter(String key) {
+            if (consumerParams != null) {
+                String value = consumerParams.get(key);
+                if (value != null) {
+                    return value;
+                }
+            }
             return params.get(key);
         }
 
         public String getMethodParameter(String method, String key, String defaultValue) {
             if (methodParams == null) {
                 methodParams = URL.toMethodParameters(params);
+                consumerMethodParams = URL.toMethodParameters(consumerParams);
+            }
+
+            String value = getMethodParameter(method, key, consumerMethodParams);
+            if (value != null) {
+                return value;
             }
+            value = getMethodParameter(method, key, methodParams);
+            return value == null ? defaultValue : value;
+        }
 
-            Map<String, String> keyMap = methodParams.get(method);
+        private String getMethodParameter(String method, String key, Map<String, Map<String, String>> map) {
+            Map<String, String> keyMap = map.get(method);
             String value = null;
             if (keyMap != null) {
                 value = keyMap.get(key);
@@ -294,7 +332,21 @@ public class MetadataInfo implements Serializable {
             if (StringUtils.isEmpty(value)) {
                 value = getParameter(key);
             }
-            return value == null ? defaultValue : value;
+            return value;
+        }
+
+        public boolean hasMethodParameter(String method, String key) {
+            String value = this.getMethodParameter(method, key, (String) null);
+            return StringUtils.isNotEmpty(value);
+        }
+
+        public boolean hasMethodParameter(String method) {
+            if (methodParams == null) {
+                methodParams = URL.toMethodParameters(params);
+                consumerMethodParams = URL.toMethodParameters(consumerParams);
+            }
+
+            return consumerMethodParams.containsKey(method) || methodParams.containsKey(method);
         }
 
         public String toDescString() {
@@ -310,5 +362,47 @@ public class MetadataInfo implements Serializable {
             }
             return methodStrings.toString();
         }
+
+        public void addParameter(String key, String value) {
+            if (consumerParams != null) {
+                this.consumerParams.put(key, value);
+            }
+        }
+
+        public void addParameterIfAbsent(String key, String value) {
+            if (consumerParams != null) {
+                this.consumerParams.putIfAbsent(key, value);
+            }
+        }
+
+        public void addConsumerParams(Map<String, String> params) {
+            // copy once for one service subscription
+            if (consumerParams == null) {
+                consumerParams = new HashMap<>(params);
+            }
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (obj == null) {
+                return false;
+            }
+            if (!(obj instanceof ServiceInfo)) {
+                return false;
+            }
+
+            ServiceInfo serviceInfo = (ServiceInfo) obj;
+            return this.getMatchKey().equals(serviceInfo.getMatchKey()) && this.getParams().equals(serviceInfo.getParams());
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(getMatchKey(), getParams());
+        }
+
+        @Override
+        public String toString() {
+            return super.toString();
+        }
     }
 }
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/DefaultServiceInstance.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/DefaultServiceInstance.java
index e44bd4f..3ee5dd2 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/DefaultServiceInstance.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/DefaultServiceInstance.java
@@ -22,6 +22,8 @@ import java.util.HashMap;
 import java.util.Map;
 import java.util.Objects;
 
+import static org.apache.dubbo.common.constants.CommonConstants.REVISION_KEY;
+
 /**
  * The default implementation of {@link ServiceInstance}.
  *
@@ -166,18 +168,29 @@ public class DefaultServiceInstance implements ServiceInstance {
         if (this == o) return true;
         if (!(o instanceof DefaultServiceInstance)) return false;
         DefaultServiceInstance that = (DefaultServiceInstance) o;
-        return isEnabled() == that.isEnabled() &&
-                isHealthy() == that.isHealthy() &&
-                Objects.equals(getId(), that.getId()) &&
-                Objects.equals(getServiceName(), that.getServiceName()) &&
+        boolean equals = Objects.equals(getServiceName(), that.getServiceName()) &&
                 Objects.equals(getHost(), that.getHost()) &&
-                Objects.equals(getPort(), that.getPort()) &&
-                Objects.equals(getMetadata(), that.getMetadata());
+                Objects.equals(getPort(), that.getPort());
+        for (Map.Entry<String, String> entry : this.getMetadata().entrySet()) {
+            if (entry.getKey().equals(REVISION_KEY)) {
+                continue;
+            }
+            equals = equals && !entry.getValue().equals(that.getMetadata().get(entry.getKey()));
+        }
+
+        return equals;
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(getId(), getServiceName(), getHost(), getPort(), isEnabled(), isHealthy(), getMetadata());
+        int result = Objects.hash(getServiceName(), getHost(), getPort());
+        for (Map.Entry<String, String> entry : this.getMetadata().entrySet()) {
+            if (entry.getKey().equals(REVISION_KEY)) {
+                continue;
+            }
+            result = 31 * result + (entry.getValue() == null ? 0 : entry.getValue().hashCode());
+        }
+        return result;
     }
 
     @Override
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/InstanceAddressURL.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/InstanceAddressURL.java
index 18632bf..149c6e2 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/InstanceAddressURL.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/InstanceAddressURL.java
@@ -28,6 +28,9 @@ 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.VERSION_KEY;
 
+/**
+ * FIXME, replace RpcContext operations with explicitly defined APIs
+ */
 public class InstanceAddressURL extends URL {
     private ServiceInstance instance;
     private MetadataInfo metadataInfo;
@@ -36,10 +39,20 @@ public class InstanceAddressURL extends URL {
             ServiceInstance instance,
             MetadataInfo metadataInfo
     ) {
+//        super()
         this.instance = instance;
         this.metadataInfo = metadataInfo;
-        this.setHost(instance.getHost());
-        this.setPort(instance.getPort());
+        this.host = instance.getHost();
+        this.port = instance.getPort();
+    }
+
+
+    public ServiceInstance getInstance() {
+        return instance;
+    }
+
+    public MetadataInfo getMetadataInfo() {
+        return metadataInfo;
     }
 
     @Override
@@ -71,6 +84,12 @@ public class InstanceAddressURL extends URL {
     }
 
     @Override
+    public String getPath() {
+        MetadataInfo.ServiceInfo serviceInfo = metadataInfo.getServiceInfo(getProtocolServiceKey());
+        return serviceInfo.getPath();
+    }
+
+    @Override
     public String getParameter(String key) {
         if (VERSION_KEY.equals(key)) {
             return getVersion();
@@ -80,10 +99,7 @@ public class InstanceAddressURL extends URL {
             return getServiceInterface();
         }
 
-        String value = getConsumerParameters().get(key);
-        if (StringUtils.isEmpty(value)) {
-            value = getInstanceMetadata().get(key);
-        }
+        String value = getInstanceMetadata().get(key);
         if (StringUtils.isEmpty(value) && metadataInfo != null) {
             value = metadataInfo.getParameter(key, RpcContext.getContext().getProtocolServiceKey());
         }
@@ -109,15 +125,52 @@ public class InstanceAddressURL extends URL {
 
     @Override
     public String getMethodParameter(String method, String key) {
-        String value = getMethodParameter(method, key);
+        MetadataInfo.ServiceInfo serviceInfo = metadataInfo.getServiceInfo(getProtocolServiceKey());
+        String value = serviceInfo.getMethodParameter(method, key, null);
         if (StringUtils.isNotEmpty(value)) {
             return value;
         }
-        MetadataInfo.ServiceInfo serviceInfo = metadataInfo.getServiceInfo(getServiceKey());
-        return serviceInfo.getMethodParameter(method, key, null);
+        return getParameter(key);
     }
 
     @Override
+    public boolean hasMethodParameter(String method, String key) {
+        MetadataInfo.ServiceInfo serviceInfo = metadataInfo.getServiceInfo(getProtocolServiceKey());
+
+        if (method == null) {
+            String suffix = "." + key;
+            for (String fullKey : getParameters().keySet()) {
+                if (fullKey.endsWith(suffix)) {
+                    return true;
+                }
+            }
+            return false;
+        }
+        if (key == null) {
+            String prefix = method + ".";
+            for (String fullKey : getParameters().keySet()) {
+                if (fullKey.startsWith(prefix)) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        return serviceInfo.hasMethodParameter(method, key);
+    }
+
+    @Override
+    public boolean hasMethodParameter(String method) {
+        MetadataInfo.ServiceInfo serviceInfo = metadataInfo.getServiceInfo(getProtocolServiceKey());
+        return serviceInfo.hasMethodParameter(method);
+    }
+
+    /**
+     * Avoid calling this method in RPC call.
+     *
+     * @return
+     */
+    @Override
     public Map<String, String> getParameters() {
         Map<String, String> instanceParams = getInstanceMetadata();
         Map<String, String> metadataParams = (metadataInfo == null ? new HashMap<>() : metadataInfo.getParameters(RpcContext.getContext().getProtocolServiceKey()));
@@ -130,8 +183,6 @@ public class InstanceAddressURL extends URL {
         if (metadataParams != null) {
             params.putAll(metadataParams);
         }
-
-        params.putAll(getConsumerParameters());
         return params;
     }
 
@@ -139,32 +190,56 @@ public class InstanceAddressURL extends URL {
         return this.instance.getMetadata();
     }
 
-    private Map<String, String> getConsumerParameters() {
-        return RpcContext.getContext().getConsumerUrl().getParameters();
-    }
+    @Override
+    public URL addParameter(String key, String value) {
+        if (StringUtils.isEmpty(key) || StringUtils.isEmpty(value)) {
+            return this;
+        }
 
-    private String getConsumerParameter(String key) {
-        return RpcContext.getContext().getConsumerUrl().getParameter(key);
+        String protocolServiceKey = RpcContext.getContext().getProtocolServiceKey();
+        getMetadataInfo().getServiceInfo(protocolServiceKey).addParameter(key, value);
+        return this;
     }
 
-    private String getConsumerMethodParameter(String method, String key) {
-        return RpcContext.getContext().getConsumerUrl().getMethodParameter(method, key);
+    @Override
+    public URL addParameterIfAbsent(String key, String value) {
+        if (StringUtils.isEmpty(key) || StringUtils.isEmpty(value)) {
+            return this;
+        }
+
+        String protocolServiceKey = RpcContext.getContext().getProtocolServiceKey();
+        getMetadataInfo().getServiceInfo(protocolServiceKey).addParameterIfAbsent(key, value);
+        return this;
     }
 
-    @Override
-    public URL addParameter(String key, String value) {
-        throw new UnsupportedOperationException("");
+    public URL addConsumerParams(Map<String, String> params) {
+        String protocolServiceKey = RpcContext.getContext().getProtocolServiceKey();
+        getMetadataInfo().getServiceInfo(protocolServiceKey).addConsumerParams(params);
+        return this;
     }
 
     @Override
     public boolean equals(Object obj) {
         // instance metadata equals
-        // service metadata equals
-        return super.equals(obj);
+        if (obj == null) {
+            return false;
+        }
+        if (!(obj instanceof InstanceAddressURL)) {
+            return false;
+        }
+
+        InstanceAddressURL that = (InstanceAddressURL) obj;
+
+        return this.getInstance().equals(that.getInstance());
     }
 
     @Override
     public int hashCode() {
-        return super.hashCode();
+        return getInstance().hashCode();
+    }
+
+    @Override
+    public String toString() {
+        return super.toString();
     }
 }
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 9b129f9..7d4d48e 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
@@ -52,6 +52,8 @@ import static java.util.Collections.emptySet;
 import static java.util.Collections.unmodifiableSet;
 import static java.util.stream.Collectors.toSet;
 import static java.util.stream.Stream.of;
+import static org.apache.dubbo.common.constants.CommonConstants.DUBBO;
+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.INTERFACE_KEY;
 import static org.apache.dubbo.common.constants.CommonConstants.MAPPING_KEY;
@@ -316,7 +318,7 @@ public class ServiceDiscoveryRegistry implements Registry {
             List<ServiceInstance> serviceInstances = serviceDiscovery.getInstances(serviceName);
             serviceListener.onEvent(new ServiceInstancesChangedEvent(serviceName, serviceInstances));
         });
-        listener.notify(serviceListener.getUrls(url.getProtocolServiceKey()));
+        listener.notify(serviceListener.getUrls(url.getServiceKey() + GROUP_CHAR_SEPERATOR + url.getParameter(PROTOCOL_KEY, DUBBO)));
 
         serviceListener.addListener(url.getProtocolServiceKey(), listener);
         registerServiceInstancesChangedListener(url, serviceListener);
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistryDirectory.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistryDirectory.java
index 3fd7713..e01a36e 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistryDirectory.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistryDirectory.java
@@ -45,7 +45,7 @@ public class ServiceDiscoveryRegistryDirectory<T> extends DynamicDirectory<T> im
     private static final Logger logger = LoggerFactory.getLogger(ServiceDiscoveryRegistryDirectory.class);
 
     // Map<url, Invoker> cache service url to invoker mapping.
-    private volatile Map<URL, Invoker<T>> urlInvokerMap; // The initial value is null and the midway may be assigned to null, please use the local variable reference
+    private volatile Map<String, Invoker<T>> urlInvokerMap; // The initial value is null and the midway may be assigned to null, please use the local variable reference
 
     private ServiceInstancesChangedListener listener;
 
@@ -75,11 +75,12 @@ public class ServiceDiscoveryRegistryDirectory<T> extends DynamicDirectory<T> im
             destroyAllInvokers(); // Close all invokers
         } else {
             this.forbidden = false; // Allow to access
-            Map<URL, Invoker<T>> oldUrlInvokerMap = this.urlInvokerMap; // local reference
+            Map<String, Invoker<T>> oldUrlInvokerMap = this.urlInvokerMap; // local reference
             if (CollectionUtils.isEmpty(invokerUrls)) {
                 return;
             }
-            Map<URL, Invoker<T>> newUrlInvokerMap = toInvokers(invokerUrls);// Translate url list to Invoker map
+
+            Map<String, Invoker<T>> newUrlInvokerMap = toInvokers(invokerUrls);// Translate url list to Invoker map
 
             if (CollectionUtils.isEmptyMap(newUrlInvokerMap)) {
                 logger.error(new IllegalStateException("Cannot create invokers from url address list (total " + invokerUrls.size() + ")"));
@@ -109,51 +110,63 @@ public class ServiceDiscoveryRegistryDirectory<T> extends DynamicDirectory<T> im
      * @param urls
      * @return invokers
      */
-    private Map<URL, Invoker<T>> toInvokers(List<URL> urls) {
-        Map<URL, Invoker<T>> newUrlInvokerMap = new HashMap<>();
+    private Map<String, Invoker<T>> toInvokers(List<URL> urls) {
+        Map<String, Invoker<T>> newUrlInvokerMap = new HashMap<>();
         if (urls == null || urls.isEmpty()) {
             return newUrlInvokerMap;
         }
         for (URL url : urls) {
-            if (EMPTY_PROTOCOL.equals(url.getProtocol())) {
+            InstanceAddressURL instanceAddressURL = (InstanceAddressURL) url;
+            if (EMPTY_PROTOCOL.equals(instanceAddressURL.getProtocol())) {
                 continue;
             }
-            if (!ExtensionLoader.getExtensionLoader(Protocol.class).hasExtension(url.getProtocol())) {
-                logger.error(new IllegalStateException("Unsupported protocol " + url.getProtocol() +
-                        " in notified url: " + url + " from registry " + getUrl().getAddress() +
+            if (!ExtensionLoader.getExtensionLoader(Protocol.class).hasExtension(instanceAddressURL.getProtocol())) {
+                logger.error(new IllegalStateException("Unsupported protocol " + instanceAddressURL.getProtocol() +
+                        " in notified url: " + instanceAddressURL + " from registry " + getUrl().getAddress() +
                         " to consumer " + NetUtils.getLocalHost() + ", supported protocol: " +
                         ExtensionLoader.getExtensionLoader(Protocol.class).getSupportedExtensions()));
                 continue;
             }
 
-            if (urlInvokerMap != null && urlInvokerMap.containsKey(url)) { // Repeated url
-                continue;
-            }
-            Invoker<T> invoker = urlInvokerMap == null ? null : urlInvokerMap.get(url);
-            if (invoker == null) { // Not in the cache, refer again
+            // FIXME, some keys may need to be removed.
+            instanceAddressURL.addConsumerParams(queryMap);
+
+            Invoker<T> invoker = urlInvokerMap == null ? null : urlInvokerMap.get(instanceAddressURL.getAddress());
+            if (invoker == null || urlChanged(invoker, instanceAddressURL)) { // Not in the cache, refer again
                 try {
                     boolean enabled = true;
-                    if (url.hasParameter(DISABLED_KEY)) {
-                        enabled = !url.getParameter(DISABLED_KEY, false);
+                    if (instanceAddressURL.hasParameter(DISABLED_KEY)) {
+                        enabled = !instanceAddressURL.getParameter(DISABLED_KEY, false);
                     } else {
-                        enabled = url.getParameter(ENABLED_KEY, true);
+                        enabled = instanceAddressURL.getParameter(ENABLED_KEY, true);
                     }
                     if (enabled) {
-                        invoker = protocol.refer(serviceType, url);
+                        invoker = protocol.refer(serviceType, instanceAddressURL);
                     }
                 } catch (Throwable t) {
-                    logger.error("Failed to refer invoker for interface:" + serviceType + ",url:(" + url + ")" + t.getMessage(), t);
+                    logger.error("Failed to refer invoker for interface:" + serviceType + ",url:(" + instanceAddressURL + ")" + t.getMessage(), t);
                 }
                 if (invoker != null) { // Put new invoker in cache
-                    newUrlInvokerMap.put(url, invoker);
+                    newUrlInvokerMap.put(instanceAddressURL.getAddress(), invoker);
                 }
             } else {
-                newUrlInvokerMap.put(url, invoker);
+                newUrlInvokerMap.put(instanceAddressURL.getAddress(), invoker);
             }
         }
         return newUrlInvokerMap;
     }
 
+    private boolean urlChanged(Invoker<T> invoker, InstanceAddressURL newURL) {
+        InstanceAddressURL oldURL = (InstanceAddressURL) invoker.getUrl();
+
+        if (!newURL.getInstance().equals(oldURL.getInstance())) {
+            return true;
+        }
+
+        return !oldURL.getMetadataInfo().getServiceInfo(getConsumerUrl().getProtocolServiceKey())
+                .equals(newURL.getMetadataInfo().getServiceInfo(getConsumerUrl().getProtocolServiceKey()));
+    }
+
     private List<Invoker<T>> toMergeInvokerList(List<Invoker<T>> invokers) {
         return invokers;
     }
@@ -162,7 +175,7 @@ public class ServiceDiscoveryRegistryDirectory<T> extends DynamicDirectory<T> im
      * Close all invokers
      */
     private void destroyAllInvokers() {
-        Map<URL, Invoker<T>> localUrlInvokerMap = this.urlInvokerMap; // local reference
+        Map<String, Invoker<T>> localUrlInvokerMap = this.urlInvokerMap; // local reference
         if (localUrlInvokerMap != null) {
             for (Invoker<T> invoker : new ArrayList<>(localUrlInvokerMap.values())) {
                 try {
@@ -183,16 +196,16 @@ public class ServiceDiscoveryRegistryDirectory<T> extends DynamicDirectory<T> im
      * @param oldUrlInvokerMap
      * @param newUrlInvokerMap
      */
-    private void destroyUnusedInvokers(Map<URL, Invoker<T>> oldUrlInvokerMap, Map<URL, Invoker<T>> newUrlInvokerMap) {
+    private void destroyUnusedInvokers(Map<String, Invoker<T>> oldUrlInvokerMap, Map<String, Invoker<T>> newUrlInvokerMap) {
         if (newUrlInvokerMap == null || newUrlInvokerMap.size() == 0) {
             destroyAllInvokers();
             return;
         }
         // check deleted invoker
-        List<URL> deleted = null;
+        List<String> deleted = null;
         if (oldUrlInvokerMap != null) {
             Collection<Invoker<T>> newInvokers = newUrlInvokerMap.values();
-            for (Map.Entry<URL, Invoker<T>> entry : oldUrlInvokerMap.entrySet()) {
+            for (Map.Entry<String, Invoker<T>> entry : oldUrlInvokerMap.entrySet()) {
                 if (!newInvokers.contains(entry.getValue())) {
                     if (deleted == null) {
                         deleted = new ArrayList<>();
@@ -203,9 +216,9 @@ public class ServiceDiscoveryRegistryDirectory<T> extends DynamicDirectory<T> im
         }
 
         if (deleted != null) {
-            for (URL url : deleted) {
-                if (url != null) {
-                    Invoker<T> invoker = oldUrlInvokerMap.remove(url);
+            for (String addressKey : deleted) {
+                if (addressKey != null) {
+                    Invoker<T> invoker = oldUrlInvokerMap.remove(addressKey);
                     if (invoker != null) {
                         try {
                             invoker.destroy();
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 686d4cb..5713256 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
@@ -79,12 +79,7 @@ public class ServiceInstanceMetadataUtils {
     /**
      * The property name of The revision for all exported Dubbo services.
      */
-    public static String EXPORTED_SERVICES_REVISION_PROPERTY_NAME = "dubbo.exported-services.revision";
-
-    /**
-     * The property name of The revision for all subscribed Dubbo services.
-     */
-    public static String SUBSCRIBER_SERVICES_REVISION_PROPERTY_NAME = "dubbo.subscribed-services.revision";
+    public static String EXPORTED_SERVICES_REVISION_PROPERTY_NAME = "dubbo.metadata.revision";
 
     /**
      * The property name of metadata storage type.
@@ -163,17 +158,6 @@ public class ServiceInstanceMetadataUtils {
     }
 
     /**
-     * The revision for all subscribed Dubbo services from the specified {@link ServiceInstance}.
-     *
-     * @param serviceInstance the specified {@link ServiceInstance}
-     * @return <code>null</code> if not exits
-     */
-    public static String getSubscribedServicesRevision(ServiceInstance serviceInstance) {
-        Map<String, String> metadata = serviceInstance.getMetadata();
-        return metadata.get(SUBSCRIBER_SERVICES_REVISION_PROPERTY_NAME);
-    }
-
-    /**
      * Get metadata's storage type
      *
      * @param registryURL the {@link URL} to connect the registry
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/DynamicDirectory.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/DynamicDirectory.java
index 8268361..80aadc8 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/DynamicDirectory.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/DynamicDirectory.java
@@ -43,10 +43,14 @@ import java.util.Map;
 import java.util.Set;
 
 import static org.apache.dubbo.common.constants.CommonConstants.ANY_VALUE;
+import static org.apache.dubbo.common.constants.CommonConstants.DUBBO;
 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.MONITOR_KEY;
+import static org.apache.dubbo.common.constants.CommonConstants.PROTOCOL_KEY;
 import static org.apache.dubbo.common.constants.RegistryConstants.CATEGORY_KEY;
 import static org.apache.dubbo.common.constants.RegistryConstants.CONSUMERS_CATEGORY;
+import static org.apache.dubbo.registry.Constants.REGISTER_IP_KEY;
 import static org.apache.dubbo.registry.Constants.REGISTER_KEY;
 import static org.apache.dubbo.registry.Constants.SIMPLIFIED_KEY;
 import static org.apache.dubbo.registry.integration.RegistryProtocol.DEFAULT_REGISTER_CONSUMER_KEYS;
@@ -122,7 +126,10 @@ public abstract class DynamicDirectory<T> extends AbstractDirectory<T> implement
 
     private URL turnRegistryUrlToConsumerUrl(URL url) {
         return URLBuilder.from(url)
-                .setPath(url.getServiceInterface())
+                .setHost(queryMap.get(REGISTER_IP_KEY))
+                .setPort(0)
+                .setProtocol(queryMap.get(PROTOCOL_KEY) == null ? DUBBO : queryMap.get(PROTOCOL_KEY))
+                .setPath(queryMap.get(INTERFACE_KEY))
                 .clearParameters()
                 .addParameters(queryMap)
                 .removeParameter(MONITOR_KEY)
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 c8cd6dd..f0d7938 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
@@ -394,7 +394,7 @@ public class RegistryDirectory<T> extends DynamicDirectory<T> implements NotifyL
         providerUrl = providerUrl.addParameter(Constants.CHECK_KEY, String.valueOf(false)); // Do not check whether the connection is successful or not, always create Invoker!
 
         // The combination of directoryUrl and override is at the end of notify, which can't be handled here
-        this.overrideDirectoryUrl = this.overrideDirectoryUrl.addParametersIfAbsent(providerUrl.getParameters()); // Merge the provider side parameters
+//        this.overrideDirectoryUrl = this.overrideDirectoryUrl.addParametersIfAbsent(providerUrl.getParameters()); // Merge the provider side parameters
 
         if ((providerUrl.getPath() == null || providerUrl.getPath()
                 .length() == 0) && DUBBO_PROTOCOL.equals(providerUrl.getProtocol())) { // Compatible version 1.0
diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/Exchangers.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/Exchangers.java
index 36bcc74..1e3b278 100644
--- a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/Exchangers.java
+++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/Exchangers.java
@@ -105,7 +105,7 @@ public class Exchangers {
         if (handler == null) {
             throw new IllegalArgumentException("handler == null");
         }
-        url = url.addParameterIfAbsent(Constants.CODEC_KEY, "exchange");
+//        url = url.addParameterIfAbsent(Constants.CODEC_KEY, "exchange");
         return getExchanger(url).connect(url, handler);
     }
 
diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/AbstractEndpoint.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/AbstractEndpoint.java
index 94738a8..53ce72f 100644
--- a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/AbstractEndpoint.java
+++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/AbstractEndpoint.java
@@ -51,7 +51,7 @@ public abstract class AbstractEndpoint extends AbstractPeer implements Resetable
     }
 
     protected static Codec2 getChannelCodec(URL url) {
-        String codecName = url.getParameter(Constants.CODEC_KEY, "telnet");
+        String codecName = url.getProtocol(); // codec extension name must stay the same with protocol name
         if (ExtensionLoader.getExtensionLoader(Codec2.class).hasExtension(codecName)) {
             return ExtensionLoader.getExtensionLoader(Codec2.class).getExtension(codecName);
         } else {
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/proxy/InvokerInvocationHandler.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/proxy/InvokerInvocationHandler.java
index b5c9e67..0eae664 100644
--- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/proxy/InvokerInvocationHandler.java
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/proxy/InvokerInvocationHandler.java
@@ -67,6 +67,7 @@ public class InvokerInvocationHandler implements InvocationHandler {
         String serviceKey = invoker.getUrl().getServiceKey();
         rpcInvocation.setTargetServiceUniqueName(serviceKey);
 
+        // invoker.getUrl() returns consumer url.
         RpcContext.setRpcContext(invoker.getUrl());
 
         if (consumerModel != null) {
diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboProtocol.java b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboProtocol.java
index 83190d2..e095523 100644
--- a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboProtocol.java
+++ b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboProtocol.java
@@ -572,9 +572,10 @@ public class DubboProtocol extends AbstractProtocol {
         // client type setting.
         String str = url.getParameter(CLIENT_KEY, url.getParameter(SERVER_KEY, DEFAULT_REMOTING_CLIENT));
 
-        url = url.addParameter(CODEC_KEY, DubboCodec.NAME);
+//        url = url.addParameter(CODEC_KEY, DubboCodec.NAME);
         // enable heartbeat by default
-        url = url.addParameterIfAbsent(HEARTBEAT_KEY, String.valueOf(DEFAULT_HEARTBEAT));
+        // FIXME,
+//        url = url.addParameterIfAbsent(HEARTBEAT_KEY, String.valueOf(DEFAULT_HEARTBEAT));
 
         // BIO is not allowed since it has severe performance issue.
         if (str != null && str.length() > 0 && !ExtensionLoader.getExtensionLoader(Transporter.class).hasExtension(str)) {
diff --git a/dubbo-rpc/dubbo-rpc-thrift/src/main/java/org/apache/dubbo/rpc/protocol/thrift/ThriftProtocol.java b/dubbo-rpc/dubbo-rpc-thrift/src/main/java/org/apache/dubbo/rpc/protocol/thrift/ThriftProtocol.java
index 1737d54..2758c5e 100644
--- a/dubbo-rpc/dubbo-rpc-thrift/src/main/java/org/apache/dubbo/rpc/protocol/thrift/ThriftProtocol.java
+++ b/dubbo-rpc/dubbo-rpc-thrift/src/main/java/org/apache/dubbo/rpc/protocol/thrift/ThriftProtocol.java
@@ -191,7 +191,7 @@ public class ThriftProtocol extends AbstractProtocol {
 
         ExchangeClient client;
 
-        url = url.addParameter(CODEC_KEY, ThriftCodec.NAME);
+//        url = url.addParameter(CODEC_KEY, ThriftCodec.NAME);
 
         try {
             client = Exchangers.connect(url);


[dubbo] 14/27: fix uts

Posted by li...@apache.org.
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

commit 730602c0c2cbb4119305fc53f52ddcf4f27b9d13
Author: ken.lj <ke...@gmail.com>
AuthorDate: Tue Jul 21 21:39:44 2020 +0800

    fix uts
---
 dubbo-compatible/src/main/java/com/alibaba/dubbo/rpc/Invocation.java | 5 +++++
 .../src/main/java/com/alibaba/dubbo/rpc/RpcInvocation.java           | 5 +++++
 dubbo-compatible/src/test/java/org/apache/dubbo/cache/CacheTest.java | 5 +++++
 .../src/test/java/org/apache/dubbo/filter/LegacyInvocation.java      | 5 +++++
 .../src/test/java/org/apache/dubbo/service/MockInvocation.java       | 5 +++++
 5 files changed, 25 insertions(+)

diff --git a/dubbo-compatible/src/main/java/com/alibaba/dubbo/rpc/Invocation.java b/dubbo-compatible/src/main/java/com/alibaba/dubbo/rpc/Invocation.java
index 3710afd..f53e159 100644
--- a/dubbo-compatible/src/main/java/com/alibaba/dubbo/rpc/Invocation.java
+++ b/dubbo-compatible/src/main/java/com/alibaba/dubbo/rpc/Invocation.java
@@ -118,6 +118,11 @@ public interface Invocation extends org.apache.dubbo.rpc.Invocation {
         }
 
         @Override
+        public String getProtocolServiceKey() {
+            return delegate.getProtocolServiceKey();
+        }
+
+        @Override
         public String getMethodName() {
             return delegate.getMethodName();
         }
diff --git a/dubbo-compatible/src/main/java/com/alibaba/dubbo/rpc/RpcInvocation.java b/dubbo-compatible/src/main/java/com/alibaba/dubbo/rpc/RpcInvocation.java
index c02c8b7..514dc97 100644
--- a/dubbo-compatible/src/main/java/com/alibaba/dubbo/rpc/RpcInvocation.java
+++ b/dubbo-compatible/src/main/java/com/alibaba/dubbo/rpc/RpcInvocation.java
@@ -108,6 +108,11 @@ public class RpcInvocation implements Invocation, Serializable {
         this.invoker = invoker;
     }
 
+    @Override
+    public String getProtocolServiceKey() {
+        return null;
+    }
+
     public String getMethodName() {
         return methodName;
     }
diff --git a/dubbo-compatible/src/test/java/org/apache/dubbo/cache/CacheTest.java b/dubbo-compatible/src/test/java/org/apache/dubbo/cache/CacheTest.java
index df85265..5f5b7ad 100644
--- a/dubbo-compatible/src/test/java/org/apache/dubbo/cache/CacheTest.java
+++ b/dubbo-compatible/src/test/java/org/apache/dubbo/cache/CacheTest.java
@@ -54,6 +54,11 @@ public class CacheTest {
         }
 
         @Override
+        public String getProtocolServiceKey() {
+            return null;
+        }
+
+        @Override
         public String getMethodName() {
             return null;
         }
diff --git a/dubbo-compatible/src/test/java/org/apache/dubbo/filter/LegacyInvocation.java b/dubbo-compatible/src/test/java/org/apache/dubbo/filter/LegacyInvocation.java
index cab2c53..a2f5d8e 100644
--- a/dubbo-compatible/src/test/java/org/apache/dubbo/filter/LegacyInvocation.java
+++ b/dubbo-compatible/src/test/java/org/apache/dubbo/filter/LegacyInvocation.java
@@ -45,6 +45,11 @@ public class LegacyInvocation implements Invocation {
         return null;
     }
 
+    @Override
+    public String getProtocolServiceKey() {
+        return null;
+    }
+
     public String getMethodName() {
         return "echo";
     }
diff --git a/dubbo-compatible/src/test/java/org/apache/dubbo/service/MockInvocation.java b/dubbo-compatible/src/test/java/org/apache/dubbo/service/MockInvocation.java
index 9b3856e..a2cb52e 100644
--- a/dubbo-compatible/src/test/java/org/apache/dubbo/service/MockInvocation.java
+++ b/dubbo-compatible/src/test/java/org/apache/dubbo/service/MockInvocation.java
@@ -56,6 +56,11 @@ public class MockInvocation implements Invocation {
         return null;
     }
 
+    @Override
+    public String getProtocolServiceKey() {
+        return null;
+    }
+
     public String getMethodName() {
         return "echo";
     }


[dubbo] 13/27: fix uts

Posted by li...@apache.org.
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

commit 52b146faef367f9eac6b43fa2aa485e3fad218ef
Author: ken.lj <ke...@gmail.com>
AuthorDate: Tue Jul 21 13:57:42 2020 +0800

    fix uts
---
 .../dubbo/monitor/dubbo/MetricsFilterTest.java       | 20 ++++++++++----------
 .../dubbo/rpc/protocol/rest/RestProtocolTest.java    |  2 +-
 2 files changed, 11 insertions(+), 11 deletions(-)

diff --git a/dubbo-monitor/dubbo-monitor-default/src/test/java/org/apache/dubbo/monitor/dubbo/MetricsFilterTest.java b/dubbo-monitor/dubbo-monitor-default/src/test/java/org/apache/dubbo/monitor/dubbo/MetricsFilterTest.java
index c1808c5..0583259 100644
--- a/dubbo-monitor/dubbo-monitor-default/src/test/java/org/apache/dubbo/monitor/dubbo/MetricsFilterTest.java
+++ b/dubbo-monitor/dubbo-monitor-default/src/test/java/org/apache/dubbo/monitor/dubbo/MetricsFilterTest.java
@@ -116,7 +116,7 @@ public class MetricsFilterTest {
         IMetricManager metricManager = MetricManager.getIMetricManager();
         metricManager.clear();
         MetricsFilter metricsFilter = new MetricsFilter();
-        Invocation invocation = new RpcInvocation("sayName", DemoService.class.getName(), new Class<?>[]{Integer.class}, new Object[0]);
+        Invocation invocation = new RpcInvocation("sayName", DemoService.class.getName(), "", new Class<?>[]{Integer.class}, new Object[0]);
         RpcContext.getContext().setRemoteAddress(NetUtils.getLocalHost(), 20880).setLocalAddress(NetUtils.getLocalHost(), 2345);
         RpcContext.getContext().setUrl(serviceInvoker.getUrl().addParameter(SIDE_KEY, CONSUMER_SIDE));
         AppResponse response = AppResponseBuilder.create()
@@ -144,7 +144,7 @@ public class MetricsFilterTest {
         IMetricManager metricManager = MetricManager.getIMetricManager();
         metricManager.clear();
         MetricsFilter metricsFilter = new MetricsFilter();
-        Invocation invocation = new RpcInvocation("timeoutException", DemoService.class.getName(), null, null);
+        Invocation invocation = new RpcInvocation("timeoutException", DemoService.class.getName(), "", null, null);
         RpcContext.getContext().setRemoteAddress(NetUtils.getLocalHost(), 20880).setLocalAddress(NetUtils.getLocalHost(), 2345);
         RpcContext.getContext().setUrl(timeoutInvoker.getUrl().addParameter(SIDE_KEY, CONSUMER_SIDE)
             .addParameter(TIMEOUT_KEY, 300));
@@ -177,7 +177,7 @@ public class MetricsFilterTest {
         IMetricManager metricManager = MetricManager.getIMetricManager();
         metricManager.clear();
         MetricsFilter metricsFilter = new MetricsFilter();
-        Invocation invocation = new RpcInvocation("sayName", DemoService.class.getName(), new Class<?>[0], new Object[0]);
+        Invocation invocation = new RpcInvocation("sayName", DemoService.class.getName(), "", new Class<?>[0], new Object[0]);
         RpcContext.getContext().setRemoteAddress(NetUtils.getLocalHost(), 20880).setLocalAddress(NetUtils.getLocalHost(), 2345);
         RpcContext.getContext().setUrl(serviceInvoker.getUrl().addParameter(SIDE_KEY, PROVIDER));
         AppResponse response = AppResponseBuilder.create()
@@ -204,7 +204,7 @@ public class MetricsFilterTest {
         IMetricManager metricManager = MetricManager.getIMetricManager();
         metricManager.clear();
         MetricsFilter metricsFilter = new MetricsFilter();
-        Invocation invocation = new RpcInvocation("sayName", DemoService.class.getName(), new Class<?>[0], new Object[0]);
+        Invocation invocation = new RpcInvocation("sayName", DemoService.class.getName(), "", new Class<?>[0], new Object[0]);
         RpcContext.getContext().setRemoteAddress(NetUtils.getLocalHost(), 20880).setLocalAddress(NetUtils.getLocalHost(), 2345);
         RpcContext.getContext().setUrl(serviceInvoker.getUrl().addParameter(SIDE_KEY, PROVIDER_SIDE)
             .addParameter(TIMEOUT_KEY, 300));
@@ -222,7 +222,7 @@ public class MetricsFilterTest {
         Protocol protocol = new DubboProtocol();
         URL url = URL.valueOf("dubbo://" + NetUtils.getLocalAddress().getHostName() + ":20880/" + MetricsService.class.getName());
         Invoker<MetricsService> invoker = protocol.refer(MetricsService.class, url);
-        invocation = new RpcInvocation("getMetricsByGroup", DemoService.class.getName(), new Class<?>[]{String.class}, new Object[]{DUBBO_GROUP});
+        invocation = new RpcInvocation("getMetricsByGroup", DemoService.class.getName(), "", new Class<?>[]{String.class}, new Object[]{DUBBO_GROUP});
         try {
             Thread.sleep(5000);
         } catch (Exception e) {
@@ -251,13 +251,13 @@ public class MetricsFilterTest {
         IMetricManager metricManager = MetricManager.getIMetricManager();
         metricManager.clear();
         MetricsFilter metricsFilter = new MetricsFilter();
-        Invocation sayNameInvocation = new RpcInvocation("sayName", DemoService.class.getName(), new Class<?>[0], new Object[0]);
-        Invocation echoInvocation = new RpcInvocation("echo", DemoService.class.getName(), new Class<?>[]{Integer.class}, new Integer[]{1});
+        Invocation sayNameInvocation = new RpcInvocation("sayName", DemoService.class.getName(), "", new Class<?>[0], new Object[0]);
+        Invocation echoInvocation = new RpcInvocation("echo", DemoService.class.getName(), "", new Class<?>[]{Integer.class}, new Integer[]{1});
         RpcContext.getContext().setRemoteAddress(NetUtils.getLocalHost(), 20880).setLocalAddress(NetUtils.getLocalHost(), 2345);
         RpcContext.getContext().setUrl(serviceInvoker.getUrl().addParameter(SIDE_KEY, PROVIDER_SIDE)
-            .addParameter(TIMEOUT_KEY, 300));
+                .addParameter(TIMEOUT_KEY, 300));
         AppResponse response = AppResponseBuilder.create()
-            .build();
+                .build();
         onInvokeReturns(response);
         for (int i = 0; i < 50; i++) {
             metricsFilter.invoke(serviceInvoker, sayNameInvocation);
@@ -277,7 +277,7 @@ public class MetricsFilterTest {
         Protocol protocol = new DubboProtocol();
         URL url = URL.valueOf("dubbo://" + NetUtils.getLocalAddress().getHostName() + ":20880/" + MetricsService.class.getName());
         Invoker<MetricsService> invoker = protocol.refer(MetricsService.class, url);
-        Invocation invocation = new RpcInvocation("getMetricsByGroup", DemoService.class.getName(), new Class<?>[]{String.class}, new Object[]{DUBBO_GROUP});
+        Invocation invocation = new RpcInvocation("getMetricsByGroup", DemoService.class.getName(), "", new Class<?>[]{String.class}, new Object[]{DUBBO_GROUP});
         try {
             Thread.sleep(15000);
         } catch (Exception e) {
diff --git a/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/RestProtocolTest.java b/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/RestProtocolTest.java
index b07997f..c6eaba9 100644
--- a/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/RestProtocolTest.java
+++ b/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/RestProtocolTest.java
@@ -177,7 +177,7 @@ public class RestProtocolTest {
 
         Exporter<DemoService> exporter = protocol.export(proxy.getInvoker(server, DemoService.class, exportUrl));
 
-        RpcInvocation rpcInvocation = new RpcInvocation("hello", DemoService.class.getName(), new Class[]{Integer.class, Integer.class}, new Integer[]{2, 3});
+        RpcInvocation rpcInvocation = new RpcInvocation("hello", DemoService.class.getName(), "", new Class[]{Integer.class, Integer.class}, new Integer[]{2, 3});
 
         Result result = exporter.getInvoker().invoke(rpcInvocation);
         assertThat(result.getValue(), CoreMatchers.<Object>is(5));


[dubbo] 23/27: add side key to MetadataService

Posted by li...@apache.org.
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

commit 0f53367da09aa76720694cf17f5567eddc093266
Author: ken.lj <ke...@gmail.com>
AuthorDate: Mon Jul 27 16:13:55 2020 +0800

    add side key to MetadataService
---
 .../client/ServiceDiscoveryRegistryDirectory.java      | 18 +++++++++++++++++-
 .../metadata/StandardMetadataServiceURLBuilder.java    |  5 ++++-
 2 files changed, 21 insertions(+), 2 deletions(-)

diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistryDirectory.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistryDirectory.java
index 9c725ac..f65a927 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistryDirectory.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistryDirectory.java
@@ -44,7 +44,7 @@ import static org.apache.dubbo.common.constants.RegistryConstants.EMPTY_PROTOCOL
 public class ServiceDiscoveryRegistryDirectory<T> extends DynamicDirectory<T> implements NotifyListener {
     private static final Logger logger = LoggerFactory.getLogger(ServiceDiscoveryRegistryDirectory.class);
 
-    // Map<url, Invoker> cache service url to invoker mapping.
+    // instance address to invoker mapping.
     private volatile Map<String, Invoker<T>> urlInvokerMap; // The initial value is null and the midway may be assigned to null, please use the local variable reference
 
     private ServiceInstancesChangedListener listener;
@@ -54,6 +54,22 @@ public class ServiceDiscoveryRegistryDirectory<T> extends DynamicDirectory<T> im
     }
 
     @Override
+    public boolean isAvailable() {
+        if (isDestroyed()) {
+            return false;
+        }
+        Map<String, Invoker<T>> localUrlInvokerMap = urlInvokerMap;
+        if (localUrlInvokerMap != null && localUrlInvokerMap.size() > 0) {
+            for (Invoker<T> invoker : new ArrayList<>(localUrlInvokerMap.values())) {
+                if (invoker.isAvailable()) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    @Override
     public synchronized void notify(List<URL> instanceUrls) {
         // Set the context of the address notification thread.
         RpcContext.setRpcContext(getConsumerUrl());
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/StandardMetadataServiceURLBuilder.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/StandardMetadataServiceURLBuilder.java
index a542012..c90eea0 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/StandardMetadataServiceURLBuilder.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/StandardMetadataServiceURLBuilder.java
@@ -27,8 +27,10 @@ import java.util.List;
 import java.util.Map;
 
 import static java.lang.String.valueOf;
+import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER;
 import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY;
 import static org.apache.dubbo.common.constants.CommonConstants.PORT_KEY;
+import static org.apache.dubbo.common.constants.CommonConstants.SIDE_KEY;
 import static org.apache.dubbo.common.constants.CommonConstants.TIMEOUT_KEY;
 import static org.apache.dubbo.metadata.MetadataConstants.DEFAULT_METADATA_TIMEOUT_VALUE;
 import static org.apache.dubbo.metadata.MetadataConstants.METADATA_PROXY_TIMEOUT_KEY;
@@ -70,7 +72,8 @@ public class StandardMetadataServiceURLBuilder implements MetadataServiceURLBuil
                     .setPort(port)
                     .setProtocol(protocol)
                     .setPath(MetadataService.class.getName())
-                    .addParameter(TIMEOUT_KEY, ConfigurationUtils.get(METADATA_PROXY_TIMEOUT_KEY, DEFAULT_METADATA_TIMEOUT_VALUE));
+                    .addParameter(TIMEOUT_KEY, ConfigurationUtils.get(METADATA_PROXY_TIMEOUT_KEY, DEFAULT_METADATA_TIMEOUT_VALUE))
+                    .addParameter(SIDE_KEY, CONSUMER);
 
             // add parameters
             params.forEach((name, value) -> urlBuilder.addParameter(name, valueOf(value)));


[dubbo] 20/27: unify registry-cluster key

Posted by li...@apache.org.
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

commit 540f4959fd7a6df4cc9c0b49ec277c0eae5887a2
Author: ken.lj <ke...@gmail.com>
AuthorDate: Mon Jul 27 15:20:51 2020 +0800

    unify registry-cluster key
---
 .../dubbo/common/constants/RegistryConstants.java  |  4 +++-
 .../org/apache/dubbo/metadata/MetadataInfo.java    | 28 +++++++++++++++++-----
 .../apache/dubbo/metadata/MetadataInfoTest.java    |  2 +-
 .../client/DefaultRegistryClusterIdentifier.java   |  6 ++---
 .../registry/client/DefaultServiceInstance.java    |  1 +
 .../dubbo/registry/client/InstanceAddressURL.java  | 12 +++++++++-
 .../registry/client/RegistryClusterIdentifier.java |  7 +++---
 .../registry/client/ServiceDiscoveryRegistry.java  | 25 +++++++++----------
 .../listener/ServiceInstancesChangedListener.java  |  5 ++--
 ...MetadataServiceURLParamsMetadataCustomizer.java |  2 +-
 .../metadata/ServiceInstanceMetadataUtils.java     |  4 ++--
 .../store/InMemoryWritableMetadataService.java     | 20 ++++++++--------
 .../metadata/store/RemoteMetadataServiceImpl.java  | 15 ++++++------
 13 files changed, 81 insertions(+), 50 deletions(-)

diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/constants/RegistryConstants.java b/dubbo-common/src/main/java/org/apache/dubbo/common/constants/RegistryConstants.java
index 420a909..d25e9fc 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/constants/RegistryConstants.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/constants/RegistryConstants.java
@@ -21,7 +21,9 @@ public interface RegistryConstants {
 
     String REGISTRY_KEY = "registry";
 
-    String REGISTRY_CLUSTER = "REGISTRY_CLUSTER";
+    String REGISTRY_CLUSTER_KEY = "REGISTRY_CLUSTER";
+
+    String REGISTRY_CLUSTER_TYPE_KEY = "registry-cluster-type";
 
     String REGISTRY_PROTOCOL = "registry";
 
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 315e56e..47e8705 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
@@ -47,6 +47,7 @@ public class MetadataInfo implements Serializable {
     private String revision;
     private Map<String, ServiceInfo> services;
 
+    // used at runtime
     private transient Map<String, String> extendParams;
     private transient AtomicBoolean reported = new AtomicBoolean(false);
 
@@ -85,7 +86,7 @@ public class MetadataInfo implements Serializable {
         markChanged();
     }
 
-    public String getRevision() {
+    public String calAndGetRevision() {
         if (revision != null && hasReported()) {
             return revision;
         }
@@ -161,8 +162,11 @@ public class MetadataInfo implements Serializable {
 
     @Override
     public String toString() {
-        // FIXME
-        return super.toString();
+        return "metadata{" +
+                "app='" + app + "'," +
+                "revision='" + revision + "'," +
+                "services=" + services +
+                "}";
     }
 
     public static class ServiceInfo implements Serializable {
@@ -174,12 +178,17 @@ public class MetadataInfo implements Serializable {
         private String path; // most of the time, path is the same with the interface name.
         private Map<String, String> params;
 
+        // params configuried on consumer side,
         private transient Map<String, String> consumerParams;
+        // cached method params
         private transient Map<String, Map<String, String>> methodParams;
         private transient Map<String, Map<String, String>> consumerMethodParams;
-        private volatile transient Map<String, Number> numbers;
-        private volatile transient Map<String, Map<String, Number>> methodNumbers;
+        // cached numbers
+        private transient Map<String, Number> numbers;
+        private transient Map<String, Map<String, Number>> methodNumbers;
+        // service + group + version
         private transient String serviceKey;
+        // service + group + version + protocol
         private transient String matchKey;
 
         public ServiceInfo() {
@@ -420,7 +429,14 @@ public class MetadataInfo implements Serializable {
 
         @Override
         public String toString() {
-            return super.toString();
+            return "service{" +
+                    "name='" + name + "'," +
+                    "group='" + group + "'," +
+                    "version='" + version + "'," +
+                    "protocol='" + protocol + "'," +
+                    "params=" + params + "," +
+                    "consumerParams=" + consumerParams +
+                    "}";
         }
     }
 }
diff --git a/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/MetadataInfoTest.java b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/MetadataInfoTest.java
index be8c3fe..fdb7021 100644
--- a/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/MetadataInfoTest.java
+++ b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/MetadataInfoTest.java
@@ -31,6 +31,6 @@ public class MetadataInfoTest {
         metadataInfo.addService(serviceInfo);
 
         System.out.println(serviceInfo.toDescString());
-        System.out.println(metadataInfo.getRevision());
+        System.out.println(metadataInfo.calAndGetRevision());
     }
 }
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/DefaultRegistryClusterIdentifier.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/DefaultRegistryClusterIdentifier.java
index 6c88265..42cc10b 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/DefaultRegistryClusterIdentifier.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/DefaultRegistryClusterIdentifier.java
@@ -18,16 +18,16 @@ package org.apache.dubbo.registry.client;
 
 import org.apache.dubbo.common.URL;
 
-import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_CLUSTER;
+import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_CLUSTER_KEY;
 
 public class DefaultRegistryClusterIdentifier implements RegistryClusterIdentifier {
     @Override
     public String providerKey(URL url) {
-        return url.getParameter(REGISTRY_CLUSTER);
+        return url.getParameter(REGISTRY_CLUSTER_KEY);
     }
 
     @Override
     public String consumerKey(URL url) {
-        return url.getParameter(REGISTRY_CLUSTER);
+        return url.getParameter(REGISTRY_CLUSTER_KEY);
     }
 }
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/DefaultServiceInstance.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/DefaultServiceInstance.java
index 3c7204d..eb581f9 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/DefaultServiceInstance.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/DefaultServiceInstance.java
@@ -49,6 +49,7 @@ public class DefaultServiceInstance implements ServiceInstance {
 
     private transient String address;
     private transient MetadataInfo serviceMetadata;
+    // used at runtime
     private transient Map<String, String> extendParams = new HashMap<>();
 
     public DefaultServiceInstance() {
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/InstanceAddressURL.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/InstanceAddressURL.java
index ca8f9be..f4ec3fe 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/InstanceAddressURL.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/InstanceAddressURL.java
@@ -32,6 +32,8 @@ import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY;
 public class InstanceAddressURL extends URL {
     private ServiceInstance instance;
     private MetadataInfo metadataInfo;
+
+    // cached numbers
     private volatile transient Map<String, Number> numbers;
     private volatile transient Map<String, Map<String, Number>> methodNumbers;
 
@@ -353,8 +355,16 @@ public class InstanceAddressURL extends URL {
         return getInstance().hashCode();
     }
 
+    public String getServiceString(String service) {
+        MetadataInfo.ServiceInfo serviceInfo = metadataInfo.getServiceInfo(service);
+        if (serviceInfo == null) {
+            return instance.toString();
+        }
+        return instance.toString() + serviceInfo.toString();
+    }
+
     @Override
     public String toString() {
-        return super.toString();
+        return instance.toString() + metadataInfo.toString();
     }
 }
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/RegistryClusterIdentifier.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/RegistryClusterIdentifier.java
index 76d141a..9d0e4a0 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/RegistryClusterIdentifier.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/RegistryClusterIdentifier.java
@@ -20,16 +20,17 @@ import org.apache.dubbo.common.URL;
 import org.apache.dubbo.common.extension.ExtensionLoader;
 import org.apache.dubbo.common.extension.SPI;
 
+import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_CLUSTER_TYPE_KEY;
+
 @SPI
 public interface RegistryClusterIdentifier {
     String providerKey(URL url);
 
     String consumerKey(URL url);
 
-    static RegistryClusterIdentifier getExtension() {
+    static RegistryClusterIdentifier getExtension(URL url) {
         ExtensionLoader<RegistryClusterIdentifier> loader
                 = ExtensionLoader.getExtensionLoader(RegistryClusterIdentifier.class);
-//        return loader.getExtension(ConfigurationUtils.getProperty("dubbo.application.sd.type", "default"));
-        return loader.getExtension("default");
+        return loader.getExtension(url.getParameter(REGISTRY_CLUSTER_TYPE_KEY, "default"));
     }
 }
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 f3d41c5..efd3c57 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
@@ -62,8 +62,7 @@ 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.VERSION_KEY;
 import static org.apache.dubbo.common.constants.RegistryConstants.PROVIDED_BY;
-import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_CLUSTER;
-import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_KEY;
+import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_CLUSTER_KEY;
 import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_TYPE_KEY;
 import static org.apache.dubbo.common.constants.RegistryConstants.SERVICE_REGISTRY_TYPE;
 import static org.apache.dubbo.common.constants.RegistryConstants.SUBSCRIBED_SERVICE_NAMES_KEY;
@@ -206,8 +205,8 @@ public class ServiceDiscoveryRegistry implements Registry {
 
     public void doRegister(URL url) {
         String registryCluster = serviceDiscovery.getUrl().getParameter(ID_KEY);
-        if (registryCluster != null) {
-            url = url.addParameter(REGISTRY_CLUSTER, registryCluster);
+        if (registryCluster != null && url.getParameter(REGISTRY_CLUSTER_KEY) == null) {
+            url = url.addParameter(REGISTRY_CLUSTER_KEY, registryCluster);
         }
         if (writableMetadataService.exportURL(url)) {
             if (logger.isInfoEnabled()) {
@@ -230,8 +229,8 @@ public class ServiceDiscoveryRegistry implements Registry {
 
     public void doUnregister(URL url) {
         String registryCluster = serviceDiscovery.getUrl().getParameter(ID_KEY);
-        if (registryCluster != null) {
-            url = url.addParameter(REGISTRY_CLUSTER, registryCluster);
+        if (registryCluster != null && url.getParameter(REGISTRY_CLUSTER_KEY) == null) {
+            url = url.addParameter(REGISTRY_CLUSTER_KEY, registryCluster);
         }
         if (writableMetadataService.unexportURL(url)) {
             if (logger.isInfoEnabled()) {
@@ -250,8 +249,8 @@ public class ServiceDiscoveryRegistry implements Registry {
             return;
         }
         String registryCluster = serviceDiscovery.getUrl().getParameter(ID_KEY);
-        if (registryCluster != null) {
-            url = url.addParameter(REGISTRY_KEY, registryCluster);
+        if (registryCluster != null && url.getParameter(REGISTRY_CLUSTER_KEY) == null) {
+            url = url.addParameter(REGISTRY_CLUSTER_KEY, registryCluster);
         }
         doSubscribe(url, listener);
     }
@@ -273,8 +272,8 @@ public class ServiceDiscoveryRegistry implements Registry {
             return;
         }
         String registryCluster = serviceDiscovery.getUrl().getParameter(ID_KEY);
-        if (registryCluster != null) {
-            url = url.addParameter(REGISTRY_KEY, registryCluster);
+        if (registryCluster != null && url.getParameter(REGISTRY_CLUSTER_KEY) == null) {
+            url = url.addParameter(REGISTRY_CLUSTER_KEY, registryCluster);
         }
         doUnsubscribe(url, listener);
     }
@@ -319,9 +318,11 @@ public class ServiceDiscoveryRegistry implements Registry {
             List<ServiceInstance> serviceInstances = serviceDiscovery.getInstances(serviceName);
             serviceListener.onEvent(new ServiceInstancesChangedEvent(serviceName, serviceInstances));
         });
-        listener.notify(serviceListener.getUrls(url.getServiceKey() + GROUP_CHAR_SEPARATOR + url.getParameter(PROTOCOL_KEY, DUBBO)));
+        String protocolServiceKey = url.getServiceKey() + GROUP_CHAR_SEPARATOR + url.getParameter(PROTOCOL_KEY, DUBBO);
 
-        serviceListener.addListener(url.getProtocolServiceKey(), listener);
+        listener.notify(serviceListener.getUrls(protocolServiceKey));
+
+        serviceListener.addListener(protocolServiceKey, listener);
         registerServiceInstancesChangedListener(url, serviceListener);
     }
 
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 37b6bf0..6ff7c2a 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
@@ -45,8 +45,8 @@ import java.util.Objects;
 import java.util.Set;
 import java.util.TreeSet;
 
-import static org.apache.dubbo.common.constants.CommonConstants.REGISTER_KEY;
 import static org.apache.dubbo.common.constants.CommonConstants.REMOTE_METADATA_STORAGE_TYPE;
+import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_CLUSTER_KEY;
 import static org.apache.dubbo.metadata.MetadataInfo.DEFAULT_REVISION;
 import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.getExportedServicesRevision;
 
@@ -156,7 +156,8 @@ public class ServiceInstancesChangedListener implements ConditionalEventListener
 
     private MetadataInfo getMetadataInfo(ServiceInstance instance) {
         String metadataType = ServiceInstanceMetadataUtils.getMetadataStorageType(instance);
-        instance.getExtendParams().putIfAbsent(REGISTER_KEY, RegistryClusterIdentifier.getExtension().consumerKey(url));
+        // FIXME, check "REGISTRY_CLUSTER_KEY" must be set by every registry implementation.
+        instance.getExtendParams().putIfAbsent(REGISTRY_CLUSTER_KEY, RegistryClusterIdentifier.getExtension(url).consumerKey(url));
         MetadataInfo metadataInfo;
         try {
             if (REMOTE_METADATA_STORAGE_TYPE.equals(metadataType)) {
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/MetadataServiceURLParamsMetadataCustomizer.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/MetadataServiceURLParamsMetadataCustomizer.java
index 8b525c7..9cc28a7 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/MetadataServiceURLParamsMetadataCustomizer.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/MetadataServiceURLParamsMetadataCustomizer.java
@@ -41,7 +41,7 @@ public class MetadataServiceURLParamsMetadataCustomizer implements ServiceInstan
         String propertyValue = resolveMetadataPropertyValue(serviceInstance);
 
         if (!isBlank(propertyName) && !isBlank(propertyValue)) {
-            metadata.put(propertyName, metadata.get(propertyName));
+            metadata.put(propertyName, propertyValue);
         }
     }
 
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 5713256..0f0cf3c 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
@@ -244,8 +244,8 @@ public class ServiceInstanceMetadataUtils {
         MetadataInfo metadataInfo = WritableMetadataService.getDefaultExtension().getMetadataInfos().get(registryCluster);
         if (metadataInfo != null) {
             String existingInstanceRevision = instance.getMetadata().get(EXPORTED_SERVICES_REVISION_PROPERTY_NAME);
-            if (!metadataInfo.getRevision().equals(existingInstanceRevision)) {
-                instance.getMetadata().put(EXPORTED_SERVICES_REVISION_PROPERTY_NAME, metadataInfo.getRevision());
+            if (!metadataInfo.calAndGetRevision().equals(existingInstanceRevision)) {
+                instance.getMetadata().put(EXPORTED_SERVICES_REVISION_PROPERTY_NAME, metadataInfo.calAndGetRevision());
                 if (existingInstanceRevision != null) {// skip the first registration.
                     instance.getExtendParams().put(INSTANCE_REVISION_UPDATED_KEY, "true");
                 }
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/store/InMemoryWritableMetadataService.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/store/InMemoryWritableMetadataService.java
index 2b747de..f0281da 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/store/InMemoryWritableMetadataService.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/store/InMemoryWritableMetadataService.java
@@ -125,10 +125,10 @@ public class InMemoryWritableMetadataService implements WritableMetadataService
 
     @Override
     public boolean exportURL(URL url) {
-        String registryKey = RegistryClusterIdentifier.getExtension().providerKey(url);
-        String[] keys = registryKey.split(",");
-        for (String key : keys) {
-            MetadataInfo metadataInfo = metadataInfos.computeIfAbsent(key, k -> {
+        String registryCluster = RegistryClusterIdentifier.getExtension().providerKey(url);
+        String[] clusters = registryCluster.split(",");
+        for (String cluster : clusters) {
+            MetadataInfo metadataInfo = metadataInfos.computeIfAbsent(cluster, k -> {
                 return new MetadataInfo(ApplicationModel.getName());
             });
             metadataInfo.addService(new ServiceInfo(url));
@@ -139,13 +139,13 @@ public class InMemoryWritableMetadataService implements WritableMetadataService
 
     @Override
     public boolean unexportURL(URL url) {
-        String registryKey = RegistryClusterIdentifier.getExtension().providerKey(url);
-        String[] keys = registryKey.split(",");
-        for (String key : keys) {
-            MetadataInfo metadataInfo = metadataInfos.get(key);
+        String registryCluster = RegistryClusterIdentifier.getExtension().providerKey(url);
+        String[] clusters = registryCluster.split(",");
+        for (String cluster : clusters) {
+            MetadataInfo metadataInfo = metadataInfos.get(cluster);
             metadataInfo.removeService(url.getProtocolServiceKey());
             if (metadataInfo.getServices().isEmpty()) {
-                metadataInfos.remove(key);
+                metadataInfos.remove(cluster);
             }
         }
         metadataSemaphore.release();
@@ -199,7 +199,7 @@ public class InMemoryWritableMetadataService implements WritableMetadataService
         }
         for (Map.Entry<String, MetadataInfo> entry : metadataInfos.entrySet()) {
             MetadataInfo metadataInfo = entry.getValue();
-            if (revision.equals(metadataInfo.getRevision())) {
+            if (revision.equals(metadataInfo.calAndGetRevision())) {
                 return metadataInfo;
             }
         }
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
index 2d543e5..c4235f4 100644
--- 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
@@ -44,8 +44,7 @@ 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;
-import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_CLUSTER;
-import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_KEY;
+import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_CLUSTER_KEY;
 
 public class RemoteMetadataServiceImpl {
     protected final Logger logger = LoggerFactory.getLogger(getClass());
@@ -61,12 +60,12 @@ public class RemoteMetadataServiceImpl {
 
     public void publishMetadata(String serviceName) {
         Map<String, MetadataInfo> metadataInfos = localMetadataService.getMetadataInfos();
-        metadataInfos.forEach((registryKey, metadataInfo) -> {
+        metadataInfos.forEach((registryCluster, metadataInfo) -> {
             if (!metadataInfo.hasReported()) {
-                SubscriberMetadataIdentifier identifier = new SubscriberMetadataIdentifier(serviceName, metadataInfo.getRevision());
-                metadataInfo.getRevision();
-                metadataInfo.getExtendParams().put(REGISTRY_KEY, registryKey);
-                MetadataReport metadataReport = getMetadataReports().get(registryKey);
+                SubscriberMetadataIdentifier identifier = new SubscriberMetadataIdentifier(serviceName, metadataInfo.calAndGetRevision());
+                metadataInfo.calAndGetRevision();
+                metadataInfo.getExtendParams().put(REGISTRY_CLUSTER_KEY, registryCluster);
+                MetadataReport metadataReport = getMetadataReports().get(registryCluster);
                 if (metadataReport == null) {
                     metadataReport = getMetadataReports().entrySet().iterator().next().getValue();
                 }
@@ -80,7 +79,7 @@ public class RemoteMetadataServiceImpl {
         SubscriberMetadataIdentifier identifier = new SubscriberMetadataIdentifier(instance.getServiceName(),
                 ServiceInstanceMetadataUtils.getExportedServicesRevision(instance));
 
-        String registryCluster = instance.getExtendParams().get(REGISTRY_CLUSTER);
+        String registryCluster = instance.getExtendParams().get(REGISTRY_CLUSTER_KEY);
 
         MetadataReport metadataReport = getMetadataReports().get(registryCluster);
         if (metadataReport == null) {