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

[dubbo] branch 3.0-k8s updated: Feat: extend MetadataService (#6574)

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

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


The following commit(s) were added to refs/heads/3.0-k8s by this push:
     new b606cf3  Feat: extend MetadataService (#6574)
b606cf3 is described below

commit b606cf3f11fa7abe08a2810bfff45d05b1d42ce4
Author: Albumen Kevin <jh...@gmail.com>
AuthorDate: Thu Aug 13 13:46:09 2020 +0800

    Feat: extend MetadataService (#6574)
    
    Fix #6538
---
 .../org/apache/dubbo/config/ApplicationConfig.java | 14 ++++
 .../bootstrap/builders/ApplicationBuilder.java     |  7 ++
 .../ConfigurableMetadataServiceExporter.java       | 53 +++++++++++--
 .../bootstrap/builders/ApplicationBuilderTest.java | 10 ++-
 .../dubbo/metadata/MetadataChangeListener.java     | 27 +++++++
 .../org/apache/dubbo/metadata/MetadataService.java | 42 +++++++++++
 .../registry/client/ServiceDiscoveryRegistry.java  |  5 ++
 .../registry/client/metadata/MetadataUtils.java    |  8 +-
 .../StandardMetadataServiceURLBuilder.java         | 87 +++++++++++++++++-----
 .../store/InMemoryWritableMetadataService.java     | 20 ++++-
 10 files changed, 242 insertions(+), 31 deletions(-)

diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/ApplicationConfig.java b/dubbo-common/src/main/java/org/apache/dubbo/config/ApplicationConfig.java
index 7e54e42..e482e35 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/config/ApplicationConfig.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/config/ApplicationConfig.java
@@ -159,6 +159,11 @@ public class ApplicationConfig extends AbstractConfig {
 
     private String repository;
 
+    /**
+     * Metadata Service, used in Service Discovery
+     */
+    private Integer metadataServicePort;
+
     public ApplicationConfig() {
     }
 
@@ -445,6 +450,15 @@ public class ApplicationConfig extends AbstractConfig {
         this.repository = repository;
     }
 
+    @Parameter(key = "metadata-service-port")
+    public Integer getMetadataServicePort() {
+        return metadataServicePort;
+    }
+
+    public void setMetadataServicePort(Integer metadataServicePort) {
+        this.metadataServicePort = metadataServicePort;
+    }
+
     @Override
     public void refresh() {
         super.refresh();
diff --git a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/builders/ApplicationBuilder.java b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/builders/ApplicationBuilder.java
index 33e6f54..c226ac6 100644
--- a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/builders/ApplicationBuilder.java
+++ b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/builders/ApplicationBuilder.java
@@ -51,6 +51,7 @@ public class ApplicationBuilder extends AbstractBuilder<ApplicationConfig, Appli
     private Boolean qosAcceptForeignIp;
     private Map<String, String> parameters;
     private String shutwait;
+    private Integer metadataServicePort;
 
     public static ApplicationBuilder newBuilder() {
         return new ApplicationBuilder();
@@ -172,6 +173,11 @@ public class ApplicationBuilder extends AbstractBuilder<ApplicationConfig, Appli
         return getThis();
     }
 
+    public ApplicationBuilder metadataServicePort(Integer metadataServicePort) {
+        this.metadataServicePort = metadataServicePort;
+        return getThis();
+    }
+
     public ApplicationConfig build() {
         ApplicationConfig config = new ApplicationConfig();
         super.build(config);
@@ -193,6 +199,7 @@ public class ApplicationBuilder extends AbstractBuilder<ApplicationConfig, Appli
         config.setQosEnable(this.qosEnable);
         config.setQosPort(this.qosPort);
         config.setQosAcceptForeignIp(this.qosAcceptForeignIp);
+        config.setMetadataServicePort(this.metadataServicePort);
         config.setParameters(this.parameters);
         if (!StringUtils.isEmpty(shutwait)) {
             config.setShutwait(shutwait);
diff --git a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/metadata/ConfigurableMetadataServiceExporter.java b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/metadata/ConfigurableMetadataServiceExporter.java
index fdb011f..d7d60fc 100644
--- a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/metadata/ConfigurableMetadataServiceExporter.java
+++ b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/metadata/ConfigurableMetadataServiceExporter.java
@@ -20,6 +20,8 @@ import org.apache.dubbo.common.URL;
 import org.apache.dubbo.common.logger.Logger;
 import org.apache.dubbo.common.logger.LoggerFactory;
 import org.apache.dubbo.config.ApplicationConfig;
+import org.apache.dubbo.config.ArgumentConfig;
+import org.apache.dubbo.config.MethodConfig;
 import org.apache.dubbo.config.ProtocolConfig;
 import org.apache.dubbo.config.RegistryConfig;
 import org.apache.dubbo.config.ServiceConfig;
@@ -29,10 +31,11 @@ import org.apache.dubbo.metadata.MetadataServiceExporter;
 import org.apache.dubbo.rpc.model.ApplicationModel;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 
 import static java.util.Collections.emptyList;
-import static org.apache.dubbo.common.constants.CommonConstants.DUBBO;
+import static org.apache.dubbo.common.constants.CommonConstants.DUBBO_PROTOCOL;
 
 /**
  * {@link MetadataServiceExporter} implementation based on {@link ConfigManager Dubbo configurations}, the clients
@@ -74,6 +77,7 @@ public class ConfigurableMetadataServiceExporter implements MetadataServiceExpor
             serviceConfig.setRef(metadataService);
             serviceConfig.setGroup(getApplicationConfig().getName());
             serviceConfig.setVersion(metadataService.version());
+            serviceConfig.setMethods(generateMethodConfig());
 
             // export
             serviceConfig.export();
@@ -93,6 +97,28 @@ public class ConfigurableMetadataServiceExporter implements MetadataServiceExpor
         return this;
     }
 
+    /**
+     * Generate Method Config for Service Discovery Metadata <p/>
+     * <p>
+     * Make {@link MetadataService} support argument callback,
+     * used to notify {@link org.apache.dubbo.registry.client.ServiceInstance}'s
+     * metadata change event
+     *
+     * @since 3.0
+     */
+    private List<MethodConfig> generateMethodConfig() {
+        MethodConfig methodConfig = new MethodConfig();
+        methodConfig.setName("getAndListenServiceDiscoveryMetadata");
+
+        ArgumentConfig argumentConfig = new ArgumentConfig();
+        argumentConfig.setIndex(1);
+        argumentConfig.setCallback(true);
+
+        methodConfig.setArguments(Collections.singletonList(argumentConfig));
+
+        return Collections.singletonList(methodConfig);
+    }
+
     @Override
     public ConfigurableMetadataServiceExporter unexport() {
         if (isExported()) {
@@ -120,10 +146,27 @@ public class ConfigurableMetadataServiceExporter implements MetadataServiceExpor
 
     private ProtocolConfig generateMetadataProtocol() {
         ProtocolConfig defaultProtocol = new ProtocolConfig();
-        defaultProtocol.setName(DUBBO);
-        // defaultProtocol.setHost() ?
-        // auto-increment port
-        defaultProtocol.setPort(-1);
+        Integer port = getApplicationConfig().getMetadataServicePort();
+
+        if (port == null || port < -1) {
+            if (logger.isInfoEnabled()) {
+                logger.info("Metadata Service Port hasn't been set. " +
+                        "Use default protocol defined in protocols.");
+            }
+            List<ProtocolConfig> defaultProtocols = ApplicationModel.getConfigManager().getDefaultProtocols();
+
+            if (defaultProtocols.isEmpty()) {
+                defaultProtocol.setName(DUBBO_PROTOCOL);
+                defaultProtocol.setPort(-1);
+            } else {
+                return defaultProtocols.get(0);
+            }
+
+        } else {
+            defaultProtocol.setName(DUBBO_PROTOCOL);
+            defaultProtocol.setPort(port);
+        }
+
         return defaultProtocol;
     }
 }
diff --git a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/bootstrap/builders/ApplicationBuilderTest.java b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/bootstrap/builders/ApplicationBuilderTest.java
index 6c37eae..f6da86a 100644
--- a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/bootstrap/builders/ApplicationBuilderTest.java
+++ b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/bootstrap/builders/ApplicationBuilderTest.java
@@ -214,6 +214,13 @@ class ApplicationBuilderTest {
     }
 
     @Test
+    void metadataServicePort() {
+        ApplicationBuilder builder = new ApplicationBuilder();
+        builder.metadataServicePort(12345);
+        Assertions.assertEquals(12345, builder.build().getMetadataServicePort());
+    }
+
+    @Test
     void build() {
         MonitorConfig monitor = new MonitorConfig("monitor-addr");
         RegistryConfig registry = new RegistryConfig();
@@ -223,7 +230,7 @@ class ApplicationBuilderTest {
                 .environment("develop").compiler("compiler").logger("log4j").monitor(monitor).isDefault(false)
                 .dumpDirectory("dumpDirectory").qosEnable(true).qosPort(8080).qosAcceptForeignIp(false)
                 .shutwait("shutwait").registryIds("registryIds").addRegistry(registry)
-                .appendParameter("default.num", "one");
+                .appendParameter("default.num", "one").metadataServicePort(12345);
 
         ApplicationConfig config = builder.build();
         ApplicationConfig config2 = builder.build();
@@ -249,6 +256,7 @@ class ApplicationBuilderTest {
         Assertions.assertSame(registry, config.getRegistry());
         Assertions.assertTrue(config.getParameters().containsKey("default.num"));
         Assertions.assertEquals("one", config.getParameters().get("default.num"));
+        Assertions.assertEquals(12345, config.getMetadataServicePort());
 
         Assertions.assertNotSame(config, config2);
     }
diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataChangeListener.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataChangeListener.java
new file mode 100644
index 0000000..cd2e2c9
--- /dev/null
+++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataChangeListener.java
@@ -0,0 +1,27 @@
+/*
+ * 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;
+
+public interface MetadataChangeListener {
+    /**
+     * Call when metadata in provider side update <p/>
+     * Used to notify consumer to update metadata of ServiceInstance
+     *
+     * @param metadata latest metadata
+     */
+    void onEvent(String metadata);
+}
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 761cd88..4971d72 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
@@ -228,4 +228,46 @@ public interface MetadataService {
     static SortedSet<String> toSortedStrings(Stream<URL> stream) {
         return unmodifiableSortedSet(stream.map(URL::toFullString).collect(TreeSet::new, Set::add, Set::addAll));
     }
+
+    /**
+     * Export Metadata in Service Instance of Service Discovery
+     * <p>
+     * Used for consumer to get Service Instance Metadata
+     * if Registry is unsupported with publishing metadata
+     *
+     * @param metadata {@link Map} of provider Service Instance Metadata
+     * @since 3.0
+     */
+    default void exportServiceDiscoveryMetadata(String metadata) {
+        throw new UnsupportedOperationException("This operation is not supported for consumer.");
+    }
+
+    /**
+     * Get all Metadata listener from local
+     * <p>
+     * Used for consumer to get Service Instance Metadata
+     * if Registry is unsupported with publishing metadata
+     *
+     * @return {@link Map} of {@link MetadataChangeListener}
+     * @since 3.0
+     */
+    default Map<String, MetadataChangeListener> getMetadataChangeListenerMap() {
+        throw new UnsupportedOperationException("This operation is not supported for consumer.");
+    }
+
+    /**
+     * 1. Fetch Metadata in Service Instance of Service Discovery
+     * 2. Add a metadata change listener
+     * <p>
+     * Used for consumer to get Service Instance Metadata
+     * if Registry is unsupported with publishing metadata
+     *
+     * @param consumerId consumerId
+     * @param listener   {@link MetadataChangeListener} used to notify event
+     * @return {@link Map} of provider Service Instance Metadata
+     * @since 3.0
+     */
+    default String getAndListenServiceDiscoveryMetadata(String consumerId, MetadataChangeListener listener) {
+        throw new UnsupportedOperationException("This operation is not supported for consumer.");
+    }
 }
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistry.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistry.java
index 1315c54..08eef0b 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistry.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistry.java
@@ -359,6 +359,11 @@ public class ServiceDiscoveryRegistry implements Registry {
             subscribedServices.addAll(parseServices(serviceNames));
         }
 
+        serviceNames = subscribedURL.getParameter(SUBSCRIBED_SERVICE_NAMES_KEY);
+        if (StringUtils.isNotEmpty(serviceNames)) {
+            subscribedServices.addAll(parseServices(serviceNames));
+        }
+
         if (isEmpty(subscribedServices)) {
             subscribedServices.addAll(findMappedServices(subscribedURL, new DefaultMappingListener(subscribedURL, subscribedServices, listener)));
             if (isEmpty(subscribedServices)) {
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/MetadataUtils.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/MetadataUtils.java
index 0a6a106..4684a27 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/MetadataUtils.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/MetadataUtils.java
@@ -71,7 +71,7 @@ public class MetadataUtils {
     }
 
     public static MetadataService getMetadataServiceProxy(ServiceInstance instance, ServiceDiscovery serviceDiscovery) {
-        String key = instance.getServiceName() + "##" +
+        String key = instance.getServiceName() + "##" + instance.getId() + "##" +
                 ServiceInstanceMetadataUtils.getExportedServicesRevision(instance);
         return metadataServiceProxies.computeIfAbsent(key, k -> {
             MetadataServiceURLBuilder builder = null;
@@ -81,10 +81,10 @@ public class MetadataUtils {
             Map<String, String> metadata = instance.getMetadata();
             // METADATA_SERVICE_URLS_PROPERTY_NAME is a unique key exists only on instances of spring-cloud-alibaba.
             String dubboURLsJSON = metadata.get(METADATA_SERVICE_URLS_PROPERTY_NAME);
-            if (StringUtils.isNotEmpty(dubboURLsJSON)) {
-                builder = loader.getExtension(SpringCloudMetadataServiceURLBuilder.NAME);
-            } else {
+            if (metadata.isEmpty() || StringUtils.isEmpty(dubboURLsJSON)) {
                 builder = loader.getExtension(StandardMetadataServiceURLBuilder.NAME);
+            } else {
+                builder = loader.getExtension(SpringCloudMetadataServiceURLBuilder.NAME);
             }
 
             List<URL> urls = builder.build(instance);
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 c90eea0..34dce94 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
@@ -19,8 +19,12 @@ package org.apache.dubbo.registry.client.metadata;
 import org.apache.dubbo.common.URL;
 import org.apache.dubbo.common.URLBuilder;
 import org.apache.dubbo.common.config.ConfigurationUtils;
+import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.LoggerFactory;
 import org.apache.dubbo.metadata.MetadataService;
 import org.apache.dubbo.registry.client.ServiceInstance;
+import org.apache.dubbo.remoting.Constants;
+import org.apache.dubbo.rpc.model.ApplicationModel;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -28,10 +32,12 @@ import java.util.Map;
 
 import static java.lang.String.valueOf;
 import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER;
+import static org.apache.dubbo.common.constants.CommonConstants.DUBBO_PROTOCOL;
 import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY;
 import static org.apache.dubbo.common.constants.CommonConstants.PORT_KEY;
 import static org.apache.dubbo.common.constants.CommonConstants.SIDE_KEY;
 import static org.apache.dubbo.common.constants.CommonConstants.TIMEOUT_KEY;
+import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY;
 import static org.apache.dubbo.metadata.MetadataConstants.DEFAULT_METADATA_TIMEOUT_VALUE;
 import static org.apache.dubbo.metadata.MetadataConstants.METADATA_PROXY_TIMEOUT_KEY;
 import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.getMetadataServiceURLsParams;
@@ -43,7 +49,9 @@ import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataU
  * @since 2.7.5
  */
 public class StandardMetadataServiceURLBuilder implements MetadataServiceURLBuilder {
-    
+
+    private final Logger logger = LoggerFactory.getLogger(getClass());
+
     public static final String NAME = "standard";
 
     /**
@@ -63,27 +71,66 @@ public class StandardMetadataServiceURLBuilder implements MetadataServiceURLBuil
 
         String host = serviceInstance.getHost();
 
-        for (Map.Entry<String, Map<String, String>> entry : paramsMap.entrySet()) {
-            String protocol = entry.getKey();
-            Map<String, String> params = entry.getValue();
-            int port = Integer.parseInt(params.get(PORT_KEY));
-            URLBuilder urlBuilder = new URLBuilder()
-                    .setHost(host)
-                    .setPort(port)
-                    .setProtocol(protocol)
-                    .setPath(MetadataService.class.getName())
-                    .addParameter(TIMEOUT_KEY, ConfigurationUtils.get(METADATA_PROXY_TIMEOUT_KEY, DEFAULT_METADATA_TIMEOUT_VALUE))
-                    .addParameter(SIDE_KEY, CONSUMER);
-
-            // add parameters
-            params.forEach((name, value) -> urlBuilder.addParameter(name, valueOf(value)));
-
-            // add the default parameters
-            urlBuilder.addParameter(GROUP_KEY, serviceName);
-
-            urls.add(urlBuilder.build());
+        if (paramsMap.isEmpty()) {
+            // ServiceInstance Metadata is empty. Happened when registry not support metadata write.
+            urls.add(generateUrlWithoutMetadata(serviceName, host));
+        } else {
+            for (Map.Entry<String, Map<String, String>> entry : paramsMap.entrySet()) {
+                String protocol = entry.getKey();
+                Map<String, String> params = entry.getValue();
+
+                urls.add(generateWithMetadata(serviceName, host, protocol, params));
+            }
         }
 
         return urls;
     }
+
+    private URL generateWithMetadata(String serviceName, String host, String protocol, Map<String, String> params) {
+        int port = Integer.parseInt(params.get(PORT_KEY));
+        URLBuilder urlBuilder = new URLBuilder()
+                .setHost(host)
+                .setPort(port)
+                .setProtocol(protocol)
+                .setPath(MetadataService.class.getName())
+                .addParameter(TIMEOUT_KEY, ConfigurationUtils.get(METADATA_PROXY_TIMEOUT_KEY, DEFAULT_METADATA_TIMEOUT_VALUE))
+                .addParameter(SIDE_KEY, CONSUMER);
+
+        // add parameters
+        params.forEach((name, value) -> urlBuilder.addParameter(name, valueOf(value)));
+
+        // add the default parameters
+        urlBuilder.addParameter(GROUP_KEY, serviceName);
+        return urlBuilder.build();
+    }
+
+    private URL generateUrlWithoutMetadata(String serviceName, String host) {
+        Integer port = ApplicationModel.getApplicationConfig().getMetadataServicePort();
+
+        if (port == null || port < 1) {
+            String message = "Metadata Service Port should be specified for consumer. " +
+                    "Please set dubbo.application.metadataServicePort and " +
+                    "make sure it has been set in provider side. " +
+                    "ServiceName: " + serviceName + " Host: " + host;
+
+            logger.error(message);
+            throw new IllegalStateException(message);
+        }
+
+        URLBuilder urlBuilder = new URLBuilder()
+                .setHost(host)
+                .setPort(port)
+                .setProtocol(DUBBO_PROTOCOL)
+                .setPath(MetadataService.class.getName())
+                .addParameter(TIMEOUT_KEY, ConfigurationUtils.get(METADATA_PROXY_TIMEOUT_KEY, DEFAULT_METADATA_TIMEOUT_VALUE))
+                .addParameter(Constants.RECONNECT_KEY, false)
+                .addParameter(SIDE_KEY, CONSUMER)
+                .addParameter(GROUP_KEY, serviceName)
+                .addParameter(VERSION_KEY, MetadataService.VERSION);
+
+        // add ServiceInstance Metadata notify support
+        urlBuilder.addParameter("getAndListenServiceDiscoveryMetadata.1.callback", true);
+
+        return urlBuilder.build();
+    }
 }
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/store/InMemoryWritableMetadataService.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/store/InMemoryWritableMetadataService.java
index 44c6bbc..c2403ea 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/store/InMemoryWritableMetadataService.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/store/InMemoryWritableMetadataService.java
@@ -20,6 +20,7 @@ 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.MetadataChangeListener;
 import org.apache.dubbo.metadata.MetadataInfo;
 import org.apache.dubbo.metadata.MetadataInfo.ServiceInfo;
 import org.apache.dubbo.metadata.MetadataService;
@@ -76,6 +77,8 @@ public class InMemoryWritableMetadataService implements WritableMetadataService
     ConcurrentNavigableMap<String, SortedSet<URL>> exportedServiceURLs = new ConcurrentSkipListMap<>();
     ConcurrentMap<String, MetadataInfo> metadataInfos;
     final Semaphore metadataSemaphore = new Semaphore(1);
+    String serviceDiscoveryMetadata;
+    ConcurrentMap<String, MetadataChangeListener> metadataChangeListenerMap = new ConcurrentHashMap<>();
 
     // ==================================================================================== //
 
@@ -206,6 +209,22 @@ public class InMemoryWritableMetadataService implements WritableMetadataService
         return null;
     }
 
+    @Override
+    public void exportServiceDiscoveryMetadata(String metadata) {
+        this.serviceDiscoveryMetadata = metadata;
+    }
+
+    @Override
+    public Map<String, MetadataChangeListener> getMetadataChangeListenerMap() {
+        return metadataChangeListenerMap;
+    }
+
+    @Override
+    public String getAndListenServiceDiscoveryMetadata(String consumerId, MetadataChangeListener listener) {
+        metadataChangeListenerMap.put(consumerId, listener);
+        return serviceDiscoveryMetadata;
+    }
+
     public void blockUntilUpdated() {
         try {
             metadataSemaphore.acquire();
@@ -291,5 +310,4 @@ public class InMemoryWritableMetadataService implements WritableMetadataService
             return o1.toFullString().compareTo(o2.toFullString());
         }
     }
-
 }