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

mercyblitz pushed a commit to branch 2.7.8-dev
in repository https://gitbox.apache.org/repos/asf/dubbo.git


The following commit(s) were added to refs/heads/2.7.8-dev by this push:
     new b086a95  2.7.8 service introspection (#6300)
b086a95 is described below

commit b086a95419e5aa1e0c072a07aa816c3f48a9659d
Author: Mercy Ma <mercybl...@gmail.com>
AuthorDate: Wed Jun 10 16:28:10 2020 +0800

    2.7.8 service introspection (#6300)
    
    * Polish apache/dubbo#6296 : Adding the new methods into MetadataReport to 
manipulate the exported URLs for service introspection
    
    * Polish apache/dubbo#6296 : Adding the new methods into MetadataReport to 
manipulate the exported URLs for service introspection
    
    * Polish apache/dubbo#6171 : [Feature] Introducing the composite 
implementation of MetadataService
---
 .../java/org/apache/dubbo/event/EventListener.java |   2 +-
 .../dubbo/config/bootstrap/DubboBootstrap.java     | 126 ++++++++----
 .../ZookeeperDubboServiceConsumerBootstrap.java    |   6 +-
 .../ZookeeperDubboServiceProviderBootstrap.java    |   6 +-
 .../apache/dubbo/metadata/MetadataConstants.java   |  25 ++-
 .../org/apache/dubbo/metadata/MetadataService.java |   7 +-
 .../org/apache/dubbo/metadata/MetadataUtil.java    |  49 -----
 .../dubbo}/metadata/URLRevisionResolver.java       |  69 +++++--
 .../dubbo/metadata/WritableMetadataService.java    |  18 +-
 .../dubbo/metadata/report/MetadataReport.java      |  18 +-
 .../AbstractAbstractWritableMetadataService.java   |  71 +++++++
 .../store/BaseWritableMetadataService.java         |  73 -------
 .../store/InMemoryWritableMetadataService.java     |  62 +++---
 .../store/RemoteWritableMetadataService.java       | 125 +++---------
 .../RemoteWritableMetadataServiceDelegate.java     | 100 ----------
 ...g.apache.dubbo.metadata.WritableMetadataService |   2 +-
 .../dubbo}/metadata/URLRevisionResolverTest.java   |  17 +-
 .../store/InMemoryWritableMetadataServiceTest.java |  20 +-
 .../store/RemoteWritableMeatadataServiceTest.java  |  12 +-
 .../RemoteWritableMetadataServiceDelegateTest.java | 216 ---------------------
 .../store/zookeeper/ZookeeperMetadataReport.java   |  32 +++
 .../dubbo/registry/client/ServiceInstance.java     |  22 +++
 .../listener/PublishingRemoteMetadataListener.java |  64 ++++++
 ...ExportedServicesRevisionMetadataCustomizer.java |   1 +
 .../metadata/RefreshServiceMetadataCustomizer.java |  47 -----
 .../StandardMetadataServiceURLBuilder.java         |   1 +
 ...bscribedServicesRevisionMetadataCustomizer.java |   1 +
 .../proxy/BaseMetadataServiceProxyFactory.java     |   3 +
 .../metadata/proxy/RemoteMetadataServiceProxy.java |  52 +++--
 .../internal/org.apache.dubbo.event.EventListener  |   4 +-
 ...dubbo.registry.client.ServiceInstanceCustomizer |   3 +-
 31 files changed, 518 insertions(+), 736 deletions(-)

diff --git 
a/dubbo-common/src/main/java/org/apache/dubbo/event/EventListener.java 
b/dubbo-common/src/main/java/org/apache/dubbo/event/EventListener.java
index 06c6f6a..bb36c77 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/event/EventListener.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/event/EventListener.java
@@ -56,7 +56,7 @@ public interface EventListener<E extends Event> extends 
java.util.EventListener,
      * The comparison rule , refer to {@link #compareTo}.
      */
     default int getPriority() {
-        return MIN_PRIORITY;
+        return NORMAL_PRIORITY;
     }
 
     /**
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 cd92fe5..16d331c 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
@@ -101,9 +101,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.
  *
@@ -509,12 +509,13 @@ public class DubboBootstrap extends GenericEventListener {
 
         startConfigCenter();
 
-        useRegistryAsConfigCenterIfNecessary();
-
         loadRemoteConfigs();
 
         checkGlobalConfigs();
 
+        // @since 2.7.8
+        startMetadataCenter();
+
         initMetadataService();
 
         initEventListener();
@@ -583,6 +584,9 @@ public class DubboBootstrap extends GenericEventListener {
     }
 
     private void startConfigCenter() {
+
+        useRegistryAsConfigCenterIfNecessary();
+
         Collection<ConfigCenterConfig> configCenters = 
configManager.getConfigCenters();
 
         // check Config Center
@@ -610,7 +614,10 @@ public class DubboBootstrap extends GenericEventListener {
         configManager.refreshAll();
     }
 
-    private void startMetadataReport() {
+    private void startMetadataCenter() {
+
+        useRegistryAsMetadataCenterIfNecessary();
+
         ApplicationConfig applicationConfig = getApplication();
 
         String metadataType = applicationConfig.getMetadataType();
@@ -646,33 +653,87 @@ 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(getRegistryCompatibleAddress(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) {
+        // TODO: confirm ? registryConfig.getUseAsConfigCenter() == null || 
registryConfig.getUseAsConfigCenter()
+        return Boolean.TRUE.equals(registryConfig.getUseAsConfigCenter());
+    }
+
+    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.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);
+        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) {
+        // TODO: confirm ? registryConfig.getUseAsMetadataCenter() == null || 
registryConfig.getUseAsMetadataCenter()
+        return Boolean.TRUE.equals(registryConfig.getUseAsMetadataCenter());
+    }
+
+    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.getAddress()));
+        metadataReportConfig.setUsername(registryConfig.getUsername());
+        metadataReportConfig.setPassword(registryConfig.getPassword());
+        metadataReportConfig.setTimeout(registryConfig.getTimeout());
+        return metadataReportConfig;
     }
 
     private String getRegistryCompatibleAddress(String registryAddress) {
@@ -722,7 +783,6 @@ public class DubboBootstrap extends GenericEventListener {
      * Initialize {@link MetadataService} from {@link 
WritableMetadataService}'s extension
      */
     private void initMetadataService() {
-        startMetadataReport();
         this.metadataService = getExtension(getMetadataType());
         this.metadataServiceExporter = new 
ConfigurableMetadataServiceExporter(metadataService);
     }
diff --git 
a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/bootstrap/ZookeeperDubboServiceConsumerBootstrap.java
 
b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/bootstrap/ZookeeperDubboServiceConsumerBootstrap.java
index 9b0d866..d720ff0 100644
--- 
a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/bootstrap/ZookeeperDubboServiceConsumerBootstrap.java
+++ 
b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/bootstrap/ZookeeperDubboServiceConsumerBootstrap.java
@@ -18,6 +18,8 @@ package org.apache.dubbo.config.bootstrap;
 
 import org.apache.dubbo.config.bootstrap.rest.UserService;
 
+import static 
org.apache.dubbo.common.constants.CommonConstants.REMOTE_METADATA_STORAGE_TYPE;
+
 /**
  * Dubbo Provider Bootstrap
  *
@@ -28,8 +30,8 @@ public class ZookeeperDubboServiceConsumerBootstrap {
     public static void main(String[] args) throws Exception {
 
         DubboBootstrap bootstrap = DubboBootstrap.getInstance()
-                .application("zookeeper-dubbo-consumer")
-                .registry("zookeeper", builder -> 
builder.address("zookeeper://127.0.0.1:2181?registry-type=service&subscribed-services=zookeeper-dubbo-provider"))
+                .application("zookeeper-dubbo-consumer", app -> 
app.metadata(REMOTE_METADATA_STORAGE_TYPE))
+                .registry("zookeeper", builder -> 
builder.address("zookeeper://127.0.0.1:2181?registry-type=service&subscribed-services=zookeeper-dubbo-provider").useAsMetadataCenter(true))
                 .reference("echo", builder -> 
builder.interfaceClass(EchoService.class).protocol("dubbo"))
                 .reference("user", builder -> 
builder.interfaceClass(UserService.class).protocol("rest"))
                 .start();
diff --git 
a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/bootstrap/ZookeeperDubboServiceProviderBootstrap.java
 
b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/bootstrap/ZookeeperDubboServiceProviderBootstrap.java
index c653fe7..0f8fb98 100644
--- 
a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/bootstrap/ZookeeperDubboServiceProviderBootstrap.java
+++ 
b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/bootstrap/ZookeeperDubboServiceProviderBootstrap.java
@@ -19,6 +19,8 @@ package org.apache.dubbo.config.bootstrap;
 import org.apache.dubbo.config.bootstrap.rest.UserService;
 import org.apache.dubbo.config.bootstrap.rest.UserServiceImpl;
 
+import static 
org.apache.dubbo.common.constants.CommonConstants.REMOTE_METADATA_STORAGE_TYPE;
+
 /**
  * TODO
  */
@@ -26,8 +28,8 @@ public class ZookeeperDubboServiceProviderBootstrap {
 
     public static void main(String[] args) {
         DubboBootstrap.getInstance()
-                .application("zookeeper-dubbo-provider")
-                .registry(builder -> 
builder.address("zookeeper://127.0.0.1:2181?registry-type=service"))
+                .application("zookeeper-dubbo-provider", app -> 
app.metadata(REMOTE_METADATA_STORAGE_TYPE))
+                .registry(builder -> 
builder.address("zookeeper://127.0.0.1:2181?registry-type=service").useAsMetadataCenter(true))
                 .protocol("dubbo", builder -> builder.port(-1).name("dubbo"))
                 .protocol("rest", builder -> builder.port(8081).name("rest"))
                 .service("echo", builder -> 
builder.interfaceClass(EchoService.class).ref(new 
EchoServiceImpl()).protocolIds("dubbo"))
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 e03ddd6..d670089 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,11 +16,22 @@
  */
 package org.apache.dubbo.metadata;
 
-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 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";
+
 }
diff --git 
a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataService.java
 
b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataService.java
index 0780149..660ac42 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
@@ -28,6 +28,7 @@ import java.util.stream.StreamSupport;
 
 import static java.util.Collections.unmodifiableSortedSet;
 import static java.util.stream.StreamSupport.stream;
+import static org.apache.dubbo.common.URL.buildKey;
 
 /**
  * A framework interface of Dubbo Metadata Service defines the contract of 
Dubbo Services registartion and subscription
@@ -90,7 +91,7 @@ public interface MetadataService {
      * @see #toSortedStrings(Stream)
      * @see URL#toFullString()
      */
-    default SortedSet<String> getSubscribedURLs(){
+    default SortedSet<String> getSubscribedURLs() {
         throw new UnsupportedOperationException("This operation is not 
supported for consumer.");
     }
 
@@ -165,7 +166,9 @@ public interface MetadataService {
      *
      * @return
      */
-    String getServiceDefinition(String interfaceName, String version, String 
group);
+    default String getServiceDefinition(String interfaceName, String version, 
String group) {
+        return getServiceDefinition(buildKey(interfaceName, group, version));
+    }
 
     /**
      * Interface definition.
diff --git 
a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataUtil.java
 
b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataUtil.java
deleted file mode 100644
index 85ce30e..0000000
--- 
a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataUtil.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.dubbo.metadata;
-
-/**
- * FIXME
- * 2019-07-31
- */
-public class MetadataUtil {
-
-//    public static String getMetadataKey(URL url, KeyTypeEnum keyType){
-//
-//    }
-//
-//    private String getIdentifierKey() {
-//        return serviceInterface
-//                + KEY_SEPARATOR + (version == null ? "" : version)
-//                + KEY_SEPARATOR + (group == null ? "" : group)
-//                + KEY_SEPARATOR + (side == null ? "" : side)
-//                + KEY_SEPARATOR + application;
-//    }
-//
-//    private String getFilePathKey() {
-//        return getFilePathKey(DEFAULT_PATH_TAG);
-//    }
-//
-//    private String getFilePathKey(String pathTag) {
-//        return pathTag
-//                + (StringUtils.isEmpty(toServicePath()) ? "" : 
(PATH_SEPARATOR + toServicePath()))
-//                + (version == null ? "" : (PATH_SEPARATOR + version))
-//                + (group == null ? "" : (PATH_SEPARATOR + group))
-//                + (side == null ? "" : (PATH_SEPARATOR + side))
-//                + (getApplication() == null ? "" : (PATH_SEPARATOR + 
getApplication()));
-//    }
-}
diff --git 
a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/URLRevisionResolver.java
 
b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/URLRevisionResolver.java
similarity index 67%
rename from 
dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/URLRevisionResolver.java
rename to 
dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/URLRevisionResolver.java
index 66f4dbf..f3fe79c 100644
--- 
a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/URLRevisionResolver.java
+++ 
b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/URLRevisionResolver.java
@@ -14,12 +14,12 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.registry.client.metadata;
+package org.apache.dubbo.metadata;
 
 import org.apache.dubbo.common.URL;
 import org.apache.dubbo.common.compiler.support.ClassUtils;
-import org.apache.dubbo.metadata.MetadataService;
 
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.List;
@@ -27,7 +27,9 @@ 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;
@@ -35,29 +37,64 @@ 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 {
 
-    public static final String NO_REVISION = "N/A";
+    /**
+     * @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(Collection<String> urls) {
+    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 NO_REVISION;
+            return UNKNOWN_REVISION;
         }
 
-        List<URL> urlsList = toURLsList(urls);
-
-        SortedSet<String> methodSignatures = resolveMethodSignatures(urlsList);
+        SortedSet<String> methodSignatures = resolveMethodSignatures(urls);
 
-        SortedSet<String> urlParameters = resolveURLParameters(urlsList);
+        SortedSet<String> urlParameters = resolveURLParameters(urls);
 
         SortedSet<String> values = new TreeSet<>(methodSignatures);
 
@@ -66,18 +103,22 @@ public class URLRevisionResolver {
         return values.stream()
                 .map(this::hashCode)                     // generate Long 
hashCode
                 .reduce(Long::sum)                       // sum hashCode
-                .map(String::valueOf)                    // Long to String
-                .orElse(NO_REVISION);                    // NO_REVISION as 
default
+                .map(Long::toHexString)                  // Using Hex for the 
shorten content
+                .orElse(UNKNOWN_REVISION);               // NO_REVISION as 
default
     }
 
-    private List<URL> toURLsList(Collection<String> urls) {
-        return urls.stream()
+    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(List<URL> urls) {
+    private SortedSet<String> resolveMethodSignatures(Collection<URL> urls) {
         return urls.stream()
                 .map(URL::getServiceInterface)                 // get the 
service interface
                 .map(ClassUtils::forName)                      // load 
business interface class
diff --git 
a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/WritableMetadataService.java
 
b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/WritableMetadataService.java
index 3a17395..d971ed7 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
@@ -20,10 +20,10 @@ 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
@@ -40,7 +40,7 @@ public interface WritableMetadataService extends 
MetadataService {
      */
     @Override
     default String serviceName() {
-        return ApplicationModel.getApplication();
+        return getName();
     }
 
     /**
@@ -63,12 +63,24 @@ public interface WritableMetadataService extends 
MetadataService {
      * fresh Exports
      *
      * @return If success , return <code>true</code>
+     * @deprecated Recommend to use {@link #publishMetadata()}
      */
+    @Deprecated
     default boolean refreshMetadata(String exportedRevision, String 
subscribedRevision) {
         return true;
     }
 
     /**
+     * Publish metadata from the Service and consumer side
+     *
+     * @return If success , return <code>true</code>
+     * @since 2.7.8
+     */
+    default boolean publishMetadata() {
+        return true;
+    }
+
+    /**
      * Subscribes a {@link URL}
      *
      * @param url a {@link URL}
@@ -84,7 +96,7 @@ public interface WritableMetadataService extends 
MetadataService {
      */
     boolean unsubscribeURL(URL url);
 
-    void publishServiceDefinition(URL providerUrl);
+    void publishServiceDefinition(URL url);
 
     /**
      * 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 35fa401..8775e55 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,11 +24,11 @@ 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.Collection;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.SortedSet;
 
 import static org.apache.dubbo.rpc.model.ApplicationModel.getName;
 
@@ -54,30 +54,30 @@ public interface MetadataReport {
     String getServiceDefinition(MetadataIdentifier metadataIdentifier);
 
     /**
-     * Save the exported {@link URL urls} in bulk.
+     * 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(Collection<URL> exportedURLs) {
+    default boolean saveExportedURLs(SortedSet<String> exportedURLs) {
         return saveExportedURLs(getName(), exportedURLs);
     }
 
     /**
-     * Save the exported {@link URL urls} in bulk.
+     * 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, Collection<URL> 
exportedURLs) {
+    default boolean saveExportedURLs(String serviceName, SortedSet<String> 
exportedURLs) {
         return saveExportedURLs(serviceName, new 
URLRevisionResolver().resolve(exportedURLs), exportedURLs);
     }
 
     /**
-     * Save the exported {@link URL urls} in bulk.
+     * 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
@@ -85,7 +85,7 @@ public interface MetadataReport {
      * @return If successful, return <code>true</code>, or <code>false</code>
      * @since 2.7.8
      */
-    default boolean saveExportedURLs(String serviceName, String 
exportedServicesRevision, Collection<URL> exportedURLs) {
+    default boolean saveExportedURLs(String serviceName, String 
exportedServicesRevision, SortedSet<String> exportedURLs) {
         return true;
     }
 
@@ -97,7 +97,7 @@ public interface MetadataReport {
      * @return non-null
      * @since 2.7.8
      */
-    default List<String> getExportedURLs(String serviceName, String 
exportedServicesRevision) {
-        return Collections.emptyList();
+    default SortedSet<String> getExportedURLs(String serviceName, String 
exportedServicesRevision) {
+        return Collections.emptySortedSet();
     }
 }
diff --git 
a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/store/AbstractAbstractWritableMetadataService.java
 
b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/store/AbstractAbstractWritableMetadataService.java
new file mode 100644
index 0000000..7ed3e03
--- /dev/null
+++ 
b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/store/AbstractAbstractWritableMetadataService.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.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.WritableMetadataService;
+import org.apache.dubbo.metadata.definition.model.ServiceDefinition;
+
+import com.google.gson.Gson;
+
+import static org.apache.dubbo.common.constants.CommonConstants.INTERFACE_KEY;
+import static org.apache.dubbo.common.utils.ClassUtils.forName;
+import static 
org.apache.dubbo.metadata.definition.ServiceDefinitionBuilder.buildFullDefinition;
+import static org.apache.dubbo.rpc.Constants.GENERIC_KEY;
+import static org.apache.dubbo.rpc.support.ProtocolUtils.isGeneric;
+
+/**
+ * The abstract implementation of {@link WritableMetadataService}
+ *
+ * @see WritableMetadataService
+ * @since 2.7.8
+ */
+public abstract class AbstractAbstractWritableMetadataService implements 
WritableMetadataService {
+
+    protected final Logger logger = LoggerFactory.getLogger(getClass());
+
+    @Override
+    public void publishServiceDefinition(URL url) {
+        publishServiceDefinition(url.getServiceKey(), 
getServiceDefinition(url));
+    }
+
+    protected String getServiceDefinition(URL exportedURL) {
+        String interfaceName = exportedURL.getParameter(INTERFACE_KEY);
+        String json = null;
+        try {
+            if (StringUtils.isNotEmpty(interfaceName) && 
!isGeneric(exportedURL.getParameter(GENERIC_KEY))) {
+                Class interfaceClass = forName(interfaceName);
+                ServiceDefinition serviceDefinition = 
buildFullDefinition(interfaceClass, exportedURL.getParameters());
+                Gson gson = new Gson();
+                json = gson.toJson(serviceDefinition);
+            }
+        } catch (ClassNotFoundException e) {
+            //ignore error
+            if (logger.isErrorEnabled()) {
+                logger.error("The interface class[name : " + interfaceName + 
"] can't be found , providerUrl: "
+                        + exportedURL.toFullString());
+            }
+        }
+        return json;
+    }
+
+    protected abstract void publishServiceDefinition(String key, String json);
+
+}
diff --git 
a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/store/BaseWritableMetadataService.java
 
b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/store/BaseWritableMetadataService.java
deleted file mode 100644
index 84fcd15..0000000
--- 
a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/store/BaseWritableMetadataService.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.dubbo.metadata.store;
-
-import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.logger.Logger;
-import org.apache.dubbo.common.logger.LoggerFactory;
-import org.apache.dubbo.metadata.MetadataService;
-
-import java.util.Collection;
-import java.util.Map;
-import java.util.SortedSet;
-import java.util.concurrent.ConcurrentNavigableMap;
-import java.util.concurrent.ConcurrentSkipListMap;
-import java.util.function.Consumer;
-
-public class BaseWritableMetadataService {
-    final Logger logger = LoggerFactory.getLogger(getClass());
-
-    // =================================== Registration 
=================================== //
-
-    /**
-     * All exported {@link URL urls} {@link Map} whose key is the return value 
of {@link URL#getServiceKey()} method
-     * and value is the {@link SortedSet sorted set} of the {@link URL URLs}
-     */
-    static ConcurrentNavigableMap<String, SortedSet<URL>> exportedServiceURLs 
= new ConcurrentSkipListMap<>();
-
-    // 
====================================================================================
 //
-
-    // =================================== Subscription 
=================================== //
-
-    /**
-     * The subscribed {@link URL urls} {@link Map} of {@link MetadataService},
-     * whose key is the return value of {@link URL#getServiceKey()} method and 
value is
-     * the {@link SortedSet sorted set} of the {@link URL URLs}
-     */
-    final static ConcurrentNavigableMap<String, SortedSet<URL>> 
SUBSCRIBED_SERVICE_URLS = new ConcurrentSkipListMap<>();
-
-    final static ConcurrentNavigableMap<String, String> SERVICE_DEFINITIONS = 
new ConcurrentSkipListMap<>();
-
-
-    boolean throwableAction(Consumer<URL> consumer, URL url) {
-        try {
-            consumer.accept(url);
-        } catch (Exception e) {
-            logger.error("Failed to remove url metadata to remote center, url 
is: " + url);
-            return false;
-        }
-        return true;
-    }
-
-    public SortedSet<String> getSubscribedURLs() {
-        return getAllUnmodifiableServiceURLs(SUBSCRIBED_SERVICE_URLS);
-    }
-
-    static SortedSet<String> getAllUnmodifiableServiceURLs(Map<String, 
SortedSet<URL>> serviceURLs) {
-        return 
MetadataService.toSortedStrings(serviceURLs.values().stream().flatMap(Collection::stream));
-    }
-}
diff --git 
a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/store/InMemoryWritableMetadataService.java
 
b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/store/InMemoryWritableMetadataService.java
index 8d62ae1..a897d76 100644
--- 
a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/store/InMemoryWritableMetadataService.java
+++ 
b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/store/InMemoryWritableMetadataService.java
@@ -16,17 +16,11 @@
  */
 package org.apache.dubbo.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.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;
@@ -39,12 +33,11 @@ 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
@@ -54,9 +47,7 @@ import static org.apache.dubbo.rpc.Constants.GENERIC_KEY;
  * @see WritableMetadataService
  * @since 2.7.5
  */
-public class InMemoryWritableMetadataService implements 
WritableMetadataService {
-
-    final Logger logger = LoggerFactory.getLogger(getClass());
+public class InMemoryWritableMetadataService extends 
AbstractAbstractWritableMetadataService {
 
     private final Lock lock = new ReentrantLock();
 
@@ -66,7 +57,7 @@ public class InMemoryWritableMetadataService implements 
WritableMetadataService
      * 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}
      */
-    ConcurrentNavigableMap<String, SortedSet<URL>> exportedServiceURLs = new 
ConcurrentSkipListMap<>();
+    private final ConcurrentNavigableMap<String, SortedSet<URL>> 
exportedServiceURLs = new ConcurrentSkipListMap<>();
 
     // 
====================================================================================
 //
 
@@ -77,9 +68,13 @@ public class InMemoryWritableMetadataService implements 
WritableMetadataService
      * whose key is the return value of {@link URL#getServiceKey()} method and 
value is
      * the {@link SortedSet sorted set} of the {@link URL URLs}
      */
-    ConcurrentNavigableMap<String, SortedSet<URL>> subscribedServiceURLs = new 
ConcurrentSkipListMap<>();
+    private final ConcurrentNavigableMap<String, SortedSet<URL>> 
subscribedServiceURLs = new ConcurrentSkipListMap<>();
 
-    ConcurrentNavigableMap<String, String> serviceDefinitions = 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<>();
 
     @Override
     public SortedSet<String> getSubscribedURLs() {
@@ -131,33 +126,25 @@ public class InMemoryWritableMetadataService implements 
WritableMetadataService
     }
 
     @Override
-    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);
-        }
+    protected void publishServiceDefinition(String key, String json) {
+        serviceDefinitions.put(key, json);
     }
 
     @Override
-    public String getServiceDefinition(String interfaceName, String version, 
String group) {
-        return serviceDefinitions.get(URL.buildKey(interfaceName, group, 
version));
+    public String getServiceDefinition(String serviceDefinitionKey) {
+        return serviceDefinitions.get(serviceDefinitionKey);
     }
 
-    @Override
-    public String getServiceDefinition(String serviceKey) {
-        return serviceDefinitions.get(serviceKey);
+    public Map<String, SortedSet<URL>> getExportedServiceURLs() {
+        return unmodifiableSortedMap(exportedServiceURLs);
+    }
+
+    public Map<String, SortedSet<URL>> getSubscribedServiceURLs() {
+        return unmodifiableSortedMap(subscribedServiceURLs);
+    }
+
+    public Map<String, String> getServiceDefinitions() {
+        return unmodifiableSortedMap(serviceDefinitions);
     }
 
     boolean addURL(Map<String, SortedSet<URL>> serviceURLs, URL url) {
@@ -223,7 +210,6 @@ public class InMemoryWritableMetadataService implements 
WritableMetadataService
                 || protocol.equals(url.getProtocol());
     }
 
-
     static class URLComparator implements Comparator<URL> {
 
         public static final URLComparator INSTANCE = new URLComparator();
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
index 9891478..2c1e03d 100644
--- 
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
@@ -19,24 +19,19 @@ package org.apache.dubbo.metadata.store;
 import org.apache.dubbo.common.URL;
 import org.apache.dubbo.common.logger.Logger;
 import org.apache.dubbo.common.logger.LoggerFactory;
-import org.apache.dubbo.common.utils.CollectionUtils;
 import org.apache.dubbo.common.utils.StringUtils;
+import org.apache.dubbo.metadata.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 org.apache.dubbo.metadata.report.identifier.ServiceMetadataIdentifier;
 import 
org.apache.dubbo.metadata.report.identifier.SubscriberMetadataIdentifier;
 import org.apache.dubbo.remoting.Constants;
 import org.apache.dubbo.rpc.RpcException;
 
-import java.util.Iterator;
-import java.util.Set;
 import java.util.SortedSet;
-import java.util.function.BiConsumer;
-import java.util.function.Consumer;
 
 import static 
org.apache.dubbo.common.constants.CommonConstants.APPLICATION_KEY;
 import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER_SIDE;
@@ -58,12 +53,14 @@ import static 
org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY;
 public class RemoteWritableMetadataService implements WritableMetadataService {
 
     protected final Logger logger = LoggerFactory.getLogger(getClass());
-    private volatile String exportedRevision;
-    private volatile String subscribedRevision;
-    private InMemoryWritableMetadataService writableMetadataService;
 
-    public RemoteWritableMetadataService(InMemoryWritableMetadataService 
writableMetadataService) {
-        this.writableMetadataService = writableMetadataService;
+    private final InMemoryWritableMetadataService 
writableMetadataServiceDelegate;
+
+    private final URLRevisionResolver urlRevisionResolver;
+
+    public RemoteWritableMetadataService() {
+        this.writableMetadataServiceDelegate = 
(InMemoryWritableMetadataService) WritableMetadataService.getDefaultExtension();
+        urlRevisionResolver = URLRevisionResolver.INSTANCE;
     }
 
     public MetadataReport getMetadataReport() {
@@ -116,118 +113,58 @@ public class RemoteWritableMetadataService implements 
WritableMetadataService {
 
     @Override
     public boolean exportURL(URL url) {
-        // do nothing for one single url export, the actual report will be 
done in callback (refreshMetadata) after all urls are exported.
-        return true;
+        return writableMetadataServiceDelegate.exportURL(url);
     }
 
     @Override
     public boolean unexportURL(URL url) {
-        ServiceMetadataIdentifier metadataIdentifier = new 
ServiceMetadataIdentifier(url);
-        metadataIdentifier.setRevision(exportedRevision);
-        metadataIdentifier.setProtocol(url.getProtocol());
-        return throwableAction(getMetadataReport()::removeServiceMetadata, 
metadataIdentifier);
+        return writableMetadataServiceDelegate.unexportURL(url);
     }
 
     @Override
     public boolean subscribeURL(URL url) {
-        // do nothing for one single url export, the actual report will be 
done in callback (refreshMetadata) after all urls are exported.
-        return true;
+        return writableMetadataServiceDelegate.subscribeURL(url);
     }
 
     @Override
     public boolean unsubscribeURL(URL url) {
-        // do nothing for one single url export, the actual report will be 
done in callback (refreshMetadata) after all urls are exported.
-        return true;
+        return writableMetadataServiceDelegate.unsubscribeURL(url);
     }
 
     @Override
-    public boolean refreshMetadata(String exportedRevision, String 
subscribedRevision) {
-        boolean result = true;
-        if (!StringUtils.isEmpty(exportedRevision) && 
!exportedRevision.equals(this.exportedRevision)) {
-            this.exportedRevision = exportedRevision;
-            boolean executeResult = saveServiceMetadata();
-            if (!executeResult) {
-                result = false;
-            }
-        }
-        if (!StringUtils.isEmpty(subscribedRevision) && 
!subscribedRevision.equals(this.subscribedRevision)
-                && 
CollectionUtils.isNotEmpty(writableMetadataService.getSubscribedURLs())) {
-            this.subscribedRevision = subscribedRevision;
-            SubscriberMetadataIdentifier metadataIdentifier = new 
SubscriberMetadataIdentifier();
-            metadataIdentifier.setApplication(serviceName());
-            metadataIdentifier.setRevision(subscribedRevision);
-            boolean executeResult = 
throwableAction(getMetadataReport()::saveSubscribedData, metadataIdentifier,
-                    writableMetadataService.getSubscribedURLs());
-            if (!executeResult) {
-                result = false;
-            }
-        }
-        return result;
-    }
-
-    private boolean saveServiceMetadata() {
-        boolean result = true;
-        for (SortedSet<URL> urls : 
writableMetadataService.exportedServiceURLs.values()) {
-            Iterator<URL> iterator = urls.iterator();
-            while (iterator.hasNext()) {
-                URL url = iterator.next();
-                // refresh revision in urls
-                ServiceMetadataIdentifier metadataIdentifier = new 
ServiceMetadataIdentifier(url);
-                metadataIdentifier.setRevision(exportedRevision);
-                metadataIdentifier.setProtocol(url.getProtocol());
-
-                boolean tmpResult = 
throwableAction(getMetadataReport()::saveServiceMetadata, metadataIdentifier, 
url);
-                if (!tmpResult) result = tmpResult;
-            }
-        }
-        return result;
-    }
-
-
-    @Override
     public SortedSet<String> getExportedURLs(String serviceInterface, String 
group, String version, String protocol) {
-        return null;
+        return 
writableMetadataServiceDelegate.getExportedURLs(serviceInterface, group, 
version, protocol);
     }
 
     @Override
-    public String getServiceDefinition(String interfaceName, String version, 
String group) {
-        return null;
+    public String getServiceDefinition(String serviceKey) {
+        return 
writableMetadataServiceDelegate.getServiceDefinition(serviceKey);
     }
 
     @Override
-    public String getServiceDefinition(String serviceKey) {
-        return null;
+    public SortedSet<String> getSubscribedURLs() {
+        return writableMetadataServiceDelegate.getSubscribedURLs();
     }
 
-    boolean throwableAction(BiConsumer<ServiceMetadataIdentifier, URL> 
consumer,
-                            ServiceMetadataIdentifier metadataIdentifier, URL 
url) {
-        try {
-            consumer.accept(metadataIdentifier, url);
-        } catch (Exception e) {
-            logger.error("Failed to execute consumer, url is: " + url);
-            return false;
+    @Override
+    public boolean publishMetadata() {
+        String serviceName = writableMetadataServiceDelegate.serviceName();
+        if (publishServiceMetadata(serviceName)) {
+            return publicConsumerMetadata(serviceName);
         }
-        return true;
+        return false;
     }
 
-    boolean throwableAction(BiConsumer<SubscriberMetadataIdentifier, 
Set<String>> consumer,
-                            SubscriberMetadataIdentifier metadataIdentifier, 
Set<String> urls) {
-        try {
-            consumer.accept(metadataIdentifier, urls);
-        } catch (Exception e) {
-            logger.error("Failed to execute consumer, url is: " + urls);
-            return false;
-        }
-        return true;
+    protected boolean publishServiceMetadata(String serviceName) {
+        SortedSet<String> exportedURLs = 
writableMetadataServiceDelegate.getExportedURLs();
+        String revision = urlRevisionResolver.resolve(exportedURLs);
+        return getMetadataReport().saveExportedURLs(serviceName, revision, 
exportedURLs);
     }
 
-    boolean throwableAction(Consumer<ServiceMetadataIdentifier> consumer, 
ServiceMetadataIdentifier metadataIdentifier) {
-        try {
-            consumer.accept(metadataIdentifier);
-        } catch (Exception e) {
-            logger.error("Failed to remove url metadata to remote center, 
metadataIdentifier is: " + metadataIdentifier);
-            return false;
-        }
+    protected boolean publicConsumerMetadata(String serviceName) {
+        SortedSet<String> subscribedURLs = 
writableMetadataServiceDelegate.getSubscribedURLs();
+        String revision = urlRevisionResolver.resolve(subscribedURLs);
+        getMetadataReport().saveSubscribedData(new 
SubscriberMetadataIdentifier(serviceName, revision), subscribedURLs);
         return true;
     }
 }
diff --git 
a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/store/RemoteWritableMetadataServiceDelegate.java
 
b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/store/RemoteWritableMetadataServiceDelegate.java
deleted file mode 100644
index 132a0cb..0000000
--- 
a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/store/RemoteWritableMetadataServiceDelegate.java
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.dubbo.metadata.store;
-
-import org.apache.dubbo.common.URL;
-import org.apache.dubbo.metadata.WritableMetadataService;
-
-import java.util.SortedSet;
-import java.util.function.BiFunction;
-
-/**
- * 2019-08-14
- *
- * @since 2.7.5
- */
-public class RemoteWritableMetadataServiceDelegate implements 
WritableMetadataService {
-    InMemoryWritableMetadataService defaultWritableMetadataService;
-    RemoteWritableMetadataService remoteWritableMetadataService;
-
-    public RemoteWritableMetadataServiceDelegate() {
-        defaultWritableMetadataService = (InMemoryWritableMetadataService) 
WritableMetadataService.getExtension("local");
-        remoteWritableMetadataService = new 
RemoteWritableMetadataService(defaultWritableMetadataService);
-    }
-
-    private WritableMetadataService getDefaultWritableMetadataService() {
-        return defaultWritableMetadataService;
-    }
-
-    @Override
-    public boolean exportURL(URL url) {
-        return doFunction(WritableMetadataService::exportURL, url);
-    }
-
-    @Override
-    public boolean unexportURL(URL url) {
-        return doFunction(WritableMetadataService::unexportURL, url);
-    }
-
-    @Override
-    public boolean subscribeURL(URL url) {
-        return doFunction(WritableMetadataService::subscribeURL, url);
-    }
-
-    @Override
-    public boolean unsubscribeURL(URL url) {
-        return doFunction(WritableMetadataService::unsubscribeURL, url);
-    }
-
-    @Override
-    public boolean refreshMetadata(String exportedRevision, String 
subscribedRevision) {
-        boolean result = true;
-        result &= 
defaultWritableMetadataService.refreshMetadata(exportedRevision, 
subscribedRevision);
-        result &= 
remoteWritableMetadataService.refreshMetadata(exportedRevision, 
subscribedRevision);
-        return result;
-    }
-
-    @Override
-    public void publishServiceDefinition(URL providerUrl) {
-        defaultWritableMetadataService.publishServiceDefinition(providerUrl);
-        remoteWritableMetadataService.publishServiceDefinition(providerUrl);
-    }
-
-    @Override
-    public SortedSet<String> getExportedURLs(String serviceInterface, String 
group, String version, String protocol) {
-        return 
getDefaultWritableMetadataService().getExportedURLs(serviceInterface, group, 
version, protocol);
-    }
-
-    @Override
-    public SortedSet<String> getSubscribedURLs() {
-        return getDefaultWritableMetadataService().getSubscribedURLs();
-    }
-
-    @Override
-    public String getServiceDefinition(String interfaceName, String version, 
String group) {
-        return 
getDefaultWritableMetadataService().getServiceDefinition(interfaceName, 
version, group);
-    }
-
-    @Override
-    public String getServiceDefinition(String serviceKey) {
-        return 
getDefaultWritableMetadataService().getServiceDefinition(serviceKey);
-    }
-
-    private boolean doFunction(BiFunction<WritableMetadataService, URL, 
Boolean> func, URL url) {
-        return func.apply(defaultWritableMetadataService, url) && 
func.apply(remoteWritableMetadataService, url);
-    }
-}
diff --git 
a/dubbo-metadata/dubbo-metadata-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metadata.WritableMetadataService
 
b/dubbo-metadata/dubbo-metadata-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metadata.WritableMetadataService
index bfe794b..26bc942 100644
--- 
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
@@ -1,2 +1,2 @@
 local=org.apache.dubbo.metadata.store.InMemoryWritableMetadataService
-remote=org.apache.dubbo.metadata.store.RemoteWritableMetadataServiceDelegate
\ No newline at end of file
+remote=org.apache.dubbo.metadata.store.RemoteWritableMetadataService
\ No newline at end of file
diff --git 
a/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/metadata/URLRevisionResolverTest.java
 
b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/URLRevisionResolverTest.java
similarity index 74%
rename from 
dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/metadata/URLRevisionResolverTest.java
rename to 
dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/URLRevisionResolverTest.java
index 3386bba..11dd2ca 100644
--- 
a/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/metadata/URLRevisionResolverTest.java
+++ 
b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/URLRevisionResolverTest.java
@@ -14,12 +14,15 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.registry.client.metadata;
+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.registry.client.metadata.URLRevisionResolver.NO_REVISION;
+import static org.apache.dubbo.metadata.URLRevisionResolver.UNKNOWN_REVISION;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 
 /**
@@ -29,17 +32,17 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
  */
 public class URLRevisionResolverTest {
 
-    private static final String URL = 
"dubbo://192.168.0.102:20881/org.apache.dubbo.registry.client.metadata.URLRevisionResolverTest";
+    private static final String URL = 
"dubbo://192.168.0.102:20881/org.apache.dubbo.metadata.URLRevisionResolverTest";
 
     private final URLRevisionResolver resolver = new URLRevisionResolver();
 
     @Test
     public void testResolve() {
-        String revision = resolver.resolve(asList());
-        assertEquals(NO_REVISION, revision);
+        String revision = resolver.resolve(Arrays.<String>asList());
+        assertEquals(UNKNOWN_REVISION, revision);
 
-        revision = resolver.resolve(null);
-        assertEquals(NO_REVISION, revision);
+        revision = resolver.resolve((Collection<String>) null);
+        assertEquals(UNKNOWN_REVISION, revision);
 
         revision = resolver.resolve(asList(URL));
         assertEquals("7960327984321481979", 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
index 1995644..3a1f9ec 100644
--- 
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
@@ -68,8 +68,8 @@ public class InMemoryWritableMetadataServiceTest {
         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.exportedServiceURLs.size()
 == 1);
-        
Assertions.assertEquals(inMemoryWritableMetadataService.exportedServiceURLs.get(url.getServiceKey()).first(),
 url);
+        
Assertions.assertTrue(inMemoryWritableMetadataService.getExportedServiceURLs().size()
 == 1);
+        
Assertions.assertEquals(inMemoryWritableMetadataService.getExportedServiceURLs().get(url.getServiceKey()).first(),
 url);
     }
 
     @Test
@@ -78,8 +78,8 @@ public class InMemoryWritableMetadataServiceTest {
         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.subscribedServiceURLs.size()
 == 1);
-        
Assertions.assertEquals(inMemoryWritableMetadataService.subscribedServiceURLs.get(url.getServiceKey()).first(),
 url);
+        
Assertions.assertTrue(inMemoryWritableMetadataService.getExportedServiceURLs().size()
 == 1);
+        
Assertions.assertEquals(inMemoryWritableMetadataService.getExportedServiceURLs().get(url.getServiceKey()).first(),
 url);
     }
 
     @Test
@@ -88,11 +88,11 @@ public class InMemoryWritableMetadataServiceTest {
         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.exportedServiceURLs.size()
 == 1);
-        
Assertions.assertEquals(inMemoryWritableMetadataService.exportedServiceURLs.get(url.getServiceKey()).first(),
 url);
+        
Assertions.assertTrue(inMemoryWritableMetadataService.getExportedServiceURLs().size()
 == 1);
+        
Assertions.assertEquals(inMemoryWritableMetadataService.getExportedServiceURLs().get(url.getServiceKey()).first(),
 url);
 
         inMemoryWritableMetadataService.unexportURL(url);
-        
Assertions.assertTrue(inMemoryWritableMetadataService.exportedServiceURLs.size()
 == 0);
+        
Assertions.assertTrue(inMemoryWritableMetadataService.getExportedServiceURLs().size()
 == 0);
     }
 
     @Test
@@ -101,11 +101,11 @@ public class InMemoryWritableMetadataServiceTest {
         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.subscribedServiceURLs.size()
 == 1);
-        
Assertions.assertEquals(inMemoryWritableMetadataService.subscribedServiceURLs.get(url.getServiceKey()).first(),
 url);
+        
Assertions.assertTrue(inMemoryWritableMetadataService.getSubscribedServiceURLs().size()
 == 1);
+        
Assertions.assertEquals(inMemoryWritableMetadataService.getSubscribedServiceURLs().get(url.getServiceKey()).first(),
 url);
 
         inMemoryWritableMetadataService.unsubscribeURL(url);
-        
Assertions.assertTrue(inMemoryWritableMetadataService.subscribedServiceURLs.size()
 == 0);
+        
Assertions.assertTrue(inMemoryWritableMetadataService.getSubscribedServiceURLs().size()
 == 0);
     }
 
 }
diff --git 
a/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/store/RemoteWritableMeatadataServiceTest.java
 
b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/store/RemoteWritableMeatadataServiceTest.java
index bdd748c..2a9ed26 100644
--- 
a/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/store/RemoteWritableMeatadataServiceTest.java
+++ 
b/dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/store/RemoteWritableMeatadataServiceTest.java
@@ -39,12 +39,10 @@ import java.util.Map;
 public class RemoteWritableMeatadataServiceTest {
     URL url = URL.valueOf("JTest://" + 
NetUtils.getLocalAddress().getHostName() + 
":4444/org.apache.dubbo.TestService?version=1.0.0&application=vic");
     RemoteWritableMetadataService metadataReportService1;
-    InMemoryWritableMetadataService inMemoryWritableMetadataService;
 
     @BeforeEach
     public void before() {
-        inMemoryWritableMetadataService = new 
InMemoryWritableMetadataService();
-        metadataReportService1 = new 
RemoteWritableMetadataService(inMemoryWritableMetadataService);
+        metadataReportService1 = new RemoteWritableMetadataService();
         MetadataReportInstance.init(url);
     }
 
@@ -134,8 +132,8 @@ public class RemoteWritableMeatadataServiceTest {
     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");
-        inMemoryWritableMetadataService.exportURL(publishUrl);
-        inMemoryWritableMetadataService.exportURL(publishUrl2);
+        metadataReportService1.exportURL(publishUrl);
+        metadataReportService1.exportURL(publishUrl2);
         String exportedRevision = "9999";
         JTestMetadataReport4Test jTestMetadataReport4Test = 
(JTestMetadataReport4Test) metadataReportService1.getMetadataReport();
         int origSize = jTestMetadataReport4Test.store.size();
@@ -151,8 +149,8 @@ public class RemoteWritableMeatadataServiceTest {
     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");
-        inMemoryWritableMetadataService.subscribeURL(subscriberUrl1);
-        inMemoryWritableMetadataService.subscribeURL(subscriberUrl2);
+        metadataReportService1.subscribeURL(subscriberUrl1);
+        metadataReportService1.subscribeURL(subscriberUrl2);
         String exportedRevision = "9999";
         String subscriberRevision = "2099";
         String applicationName = "wriableMetadataService";
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 8db9534..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.getMetadataReport(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-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 9ca2e42..9fe8776 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,6 +19,7 @@ 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;
@@ -29,13 +30,19 @@ import 
org.apache.dubbo.metadata.report.support.AbstractMetadataReport;
 import org.apache.dubbo.remoting.zookeeper.ZookeeperClient;
 import org.apache.dubbo.remoting.zookeeper.ZookeeperTransporter;
 
+import com.google.gson.Gson;
+
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
+import java.util.SortedSet;
+import java.util.TreeSet;
 
 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
@@ -120,4 +127,29 @@ public class ZookeeperMetadataReport extends 
AbstractMetadataReport {
         return toRootDir() + metadataIdentifier.getUniqueKey(KeyTypeEnum.PATH);
     }
 
+    @Override
+    public boolean saveExportedURLs(String serviceName, String 
exportedServicesRevision, SortedSet<String> exportedURLs) {
+        String path = buildExportedURLsMetadataPath(serviceName, 
exportedServicesRevision);
+        Gson gson = new Gson();
+        String content = gson.toJson(exportedURLs);
+        zkClient.create(path, content, false);
+        return true;
+    }
+
+    @Override
+    public SortedSet<String> getExportedURLs(String serviceName, String 
exportedServicesRevision) {
+        String path = buildExportedURLsMetadataPath(serviceName, 
exportedServicesRevision);
+        String content = zkClient.getContent(path);
+        Gson gson = new Gson();
+        return gson.fromJson(content, TreeSet.class);
+    }
+
+    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-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 0422925..896af9c 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
@@ -83,6 +83,28 @@ public interface ServiceInstance extends Serializable {
     Map<String, String> getMetadata();
 
     /**
+     * Get the value of metadata by the specified name
+     *
+     * @param name the specified name
+     * @return the value of metadata if found, or <code>null</code>
+     * @since 2.7.8
+     */
+    default String getMetadata(String name) {
+        return getMetadata(name, null);
+    }
+
+    /**
+     * Get the value of metadata by the specified name
+     *
+     * @param name the specified name
+     * @return the value of metadata if found, or <code>defaultValue</code>
+     * @since 2.7.8
+     */
+    default String getMetadata(String name, String defaultValue) {
+        return getMetadata().getOrDefault(name, defaultValue);
+    }
+
+    /**
      * @return the hash code of current instance.
      */
     int hashCode();
diff --git 
a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/event/listener/PublishingRemoteMetadataListener.java
 
b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/event/listener/PublishingRemoteMetadataListener.java
new file mode 100644
index 0000000..a12e781
--- /dev/null
+++ 
b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/event/listener/PublishingRemoteMetadataListener.java
@@ -0,0 +1,64 @@
+/*
+ * 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.logger.Logger;
+import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.event.EventListener;
+import org.apache.dubbo.metadata.WritableMetadataService;
+import org.apache.dubbo.metadata.report.MetadataReport;
+import org.apache.dubbo.metadata.store.RemoteWritableMetadataService;
+import org.apache.dubbo.registry.client.ServiceInstance;
+import 
org.apache.dubbo.registry.client.event.ServiceInstancePreRegisteredEvent;
+
+import static org.apache.dubbo.metadata.WritableMetadataService.getExtension;
+import static 
org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.getMetadataStorageType;
+
+/**
+ * An {@link EventListener} of {@link ServiceInstancePreRegisteredEvent} is 
handling for publishing the
+ * metadata to {@link MetadataReport "metadata center"}.
+ *
+ * @see RemoteWritableMetadataService
+ * @see MetadataReport
+ * @since 2.7.8
+ */
+public class PublishingRemoteMetadataListener implements 
EventListener<ServiceInstancePreRegisteredEvent> {
+
+    private final Logger logger = LoggerFactory.getLogger(getClass());
+
+    @Override
+    public void onEvent(ServiceInstancePreRegisteredEvent event) {
+
+        ServiceInstance serviceInstance = event.getServiceInstance();
+
+        String metadataType = getMetadataStorageType(serviceInstance);
+
+        // Get the WritableMetadataService
+        WritableMetadataService metadataService = getExtension(metadataType);
+
+        if (metadataService.publishMetadata()) {
+            if (logger.isInfoEnabled()) {
+                logger.info(String.format("%s has publish the metadata", 
metadataService.getClass().getSimpleName()));
+            }
+        }
+    }
+
+    @Override
+    public int getPriority() {
+        return MIN_PRIORITY;
+    }
+}
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
index a356068..951f957 100644
--- 
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
@@ -16,6 +16,7 @@
  */
 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;
diff --git 
a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/RefreshServiceMetadataCustomizer.java
 
b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/RefreshServiceMetadataCustomizer.java
deleted file mode 100644
index 07b6bc7..0000000
--- 
a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/RefreshServiceMetadataCustomizer.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.registry.client.metadata;
-
-import org.apache.dubbo.metadata.WritableMetadataService;
-import org.apache.dubbo.registry.client.ServiceInstance;
-import org.apache.dubbo.registry.client.ServiceInstanceCustomizer;
-
-import static org.apache.dubbo.metadata.WritableMetadataService.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.getSubscribedServicesRevision;
-
-/**
- * An {@link ServiceInstanceCustomizer} to refresh metadata.
- */
-public class RefreshServiceMetadataCustomizer implements 
ServiceInstanceCustomizer {
-
-    public int getPriority() {
-        return MIN_PRIORITY;
-    }
-
-    @Override
-    public void customize(ServiceInstance serviceInstance) {
-
-        String metadataStoredType = getMetadataStorageType(serviceInstance);
-
-        WritableMetadataService writableMetadataService = 
getExtension(metadataStoredType);
-
-        
writableMetadataService.refreshMetadata(getExportedServicesRevision(serviceInstance),
-                getSubscribedServicesRevision(serviceInstance));
-    }
-}
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 026146e..216e832 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
@@ -37,6 +37,7 @@ import static 
org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataU
  * @since 2.7.5
  */
 public class StandardMetadataServiceURLBuilder implements 
MetadataServiceURLBuilder {
+    
     public static final String NAME = "standard";
 
     /**
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
index fd5646b..c9e6b04 100644
--- 
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
@@ -16,6 +16,7 @@
  */
 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;
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
index 1e8d9fa..e0e70ea 100644
--- 
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
@@ -25,8 +25,11 @@ import java.util.Map;
 
 /**
  * base class for remote and local implementations.
+ *
+ * @since 2.7.5
  */
 abstract class BaseMetadataServiceProxyFactory implements 
MetadataServiceProxyFactory {
+
     private final Map<String, MetadataService> proxies = new HashMap<>();
 
     public final MetadataService getProxy(ServiceInstance serviceInstance) {
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
index e6bc86d..a92cafb 100644
--- 
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
@@ -16,6 +16,7 @@
  */
 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;
@@ -23,30 +24,36 @@ import org.apache.dubbo.metadata.MetadataService;
 import org.apache.dubbo.metadata.report.MetadataReport;
 import org.apache.dubbo.metadata.report.MetadataReportInstance;
 import org.apache.dubbo.metadata.report.identifier.MetadataIdentifier;
-import org.apache.dubbo.metadata.report.identifier.ServiceMetadataIdentifier;
 import org.apache.dubbo.registry.client.ServiceInstance;
-import org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils;
 
-import java.util.Collection;
-import java.util.Collections;
+import java.util.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.registry.client.metadata.URLRevisionResolver.NO_REVISION;
+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 String serviceName;
-    private String revision;
+    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()
-                
.getOrDefault(ServiceInstanceMetadataUtils.EXPORTED_SERVICES_REVISION_PROPERTY_NAME,
 NO_REVISION);
+        this.revision = 
serviceInstance.getMetadata(EXPORTED_SERVICES_REVISION_PROPERTY_NAME, 
UNKNOWN_REVISION);
     }
 
     @Override
@@ -56,12 +63,23 @@ public class RemoteMetadataServiceProxy implements 
MetadataService {
 
     @Override
     public SortedSet<String> getExportedURLs(String serviceInterface, String 
group, String version, String protocol) {
-        return toSortedStrings(getMetadataReport().getExportedURLs(
-                new ServiceMetadataIdentifier(serviceInterface, group, 
version, PROVIDER_SIDE, revision, protocol)));
-    }
 
-    private static SortedSet<String> toSortedStrings(Collection<String> 
values) {
-        return Collections.unmodifiableSortedSet(new TreeSet<>(values));
+        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
@@ -71,8 +89,8 @@ public class RemoteMetadataServiceProxy implements 
MetadataService {
     }
 
     @Override
-    public String getServiceDefinition(String serviceKey) {
-        String[] services = UrlUtils.parseServiceKey(serviceKey);
+    public String getServiceDefinition(String serviceDefinitionKey) {
+        String[] services = UrlUtils.parseServiceKey(serviceDefinitionKey);
         String serviceInterface = services[0];
         // if version or group is not exist
         String version = null;
@@ -90,6 +108,4 @@ public class RemoteMetadataServiceProxy implements 
MetadataService {
     MetadataReport getMetadataReport() {
         return MetadataReportInstance.getMetadataReport(true);
     }
-
-
 }
diff --git 
a/dubbo-registry/dubbo-registry-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.event.EventListener
 
b/dubbo-registry/dubbo-registry-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.event.EventListener
index 729e8a5..2ac7539 100644
--- 
a/dubbo-registry/dubbo-registry-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.event.EventListener
+++ 
b/dubbo-registry/dubbo-registry-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.event.EventListener
@@ -1,2 +1,4 @@
 
service-instance=org.apache.dubbo.registry.client.event.listener.CustomizableServiceInstanceListener
-registry-logging=org.apache.dubbo.registry.client.event.listener.LoggingEventListener
\ No newline at end of file
+registry-logging=org.apache.dubbo.registry.client.event.listener.LoggingEventListener
+# "publishing-remote-metadata" in introduced since 2.7.8
+publishing-remote-metadata=org.apache.dubbo.registry.client.event.listener.PublishingRemoteMetadataListener
\ 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.ServiceInstanceCustomizer
 
b/dubbo-registry/dubbo-registry-api/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceInstanceCustomizer
index 3451e00..9307410 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
@@ -1,6 +1,5 @@
 
metadata-url=org.apache.dubbo.registry.client.metadata.MetadataServiceURLParamsMetadataCustomizer
 
exported-revision=org.apache.dubbo.registry.client.metadata.ExportedServicesRevisionMetadataCustomizer
-subscribe-revision=org.apache.dubbo.registry.client.metadata.SubscribedServicesRevisionMetadataCustomizer
-refresh=org.apache.dubbo.registry.client.metadata.RefreshServiceMetadataCustomizer
+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

Reply via email to