You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@dubbo.apache.org by al...@apache.org on 2021/08/15 14:33:02 UTC

[dubbo] branch 3.0 updated: test: verify service-discovery-registry protocol in multiple registry center (#8493)

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

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


The following commit(s) were added to refs/heads/3.0 by this push:
     new d89aa89  test: verify service-discovery-registry protocol in multiple registry center (#8493)
d89aa89 is described below

commit d89aa89ba0711cd8fa5a8b760a7c78de371bb51b
Author: Xiong, Pin <pi...@foxmail.com>
AuthorDate: Sun Aug 15 09:32:42 2021 -0500

    test: verify service-discovery-registry protocol in multiple registry center (#8493)
    
    1. Check ServiceDiscoveryRegistry
    2. Check InMemoryWritableMetadataService
    3. Define a SPI for RegistryServiceListener
---
 .../integration/multiple/AbstractStorage.java      |  82 ++++++++
 .../apache/dubbo/integration/multiple/Storage.java |  58 ++++++
 ...terServiceDiscoveryRegistryIntegrationTest.java | 220 +++++++++++++++++++++
 ...ceDiscoveryRegistryRegistryServiceListener.java | 103 ++++++++++
 ...istryCenterServiceDiscoveryRegistryService.java |  26 +++
 ...yCenterServiceDiscoveryRegistryServiceImpl.java |  29 +++
 .../ServiceDiscoveryRegistryInfoWrapper.java       |  82 ++++++++
 .../ServiceDiscoveryRegistryStorage.java           |  26 +++
 ...RegistryCenterDubboProtocolIntegrationTest.java |   3 -
 ...g.apache.dubbo.registry.RegistryServiceListener |  18 ++
 10 files changed, 644 insertions(+), 3 deletions(-)

diff --git a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/integration/multiple/AbstractStorage.java b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/integration/multiple/AbstractStorage.java
new file mode 100644
index 0000000..7a0b938
--- /dev/null
+++ b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/integration/multiple/AbstractStorage.java
@@ -0,0 +1,82 @@
+/*
+ * 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.integration.multiple;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * This abstraction class to implement the basic methods for {@link Storage}.
+ *
+ * @param <T> The type to store
+ */
+public abstract class AbstractStorage<T> implements Storage<T> {
+
+    private Map<String, T> storage = new ConcurrentHashMap<>();
+
+
+    /**
+     * Generate the key for storage
+     *
+     * @param host the host in the register center.
+     * @param port the port in the register center.
+     * @return the generated key with the given host and port.
+     */
+    private String generateKey(String host, int port) {
+        return String.format("%s:%d", host, port);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public T get(String host, int port) {
+        return storage.get(generateKey(host, port));
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void put(String host, int port, T value) {
+        storage.put(generateKey(host, port), value);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean contains(String host, int port) {
+        return storage.containsKey(generateKey(host, port));
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public int size() {
+        return storage.size();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void clear() {
+        storage.clear();
+    }
+}
diff --git a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/integration/multiple/Storage.java b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/integration/multiple/Storage.java
new file mode 100644
index 0000000..064289c
--- /dev/null
+++ b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/integration/multiple/Storage.java
@@ -0,0 +1,58 @@
+/*
+ * 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.integration.multiple;
+
+/**
+ * This interface to store the given type instance in multiple registry center.
+ * @param <T> The type to store
+ */
+public interface Storage <T>{
+
+    /**
+     * Gets the stored instance with the given host and port.
+     * @param host the host in the register center.
+     * @param port the port in the register center.
+     * @return the stored instance.
+     */
+    T get(String host, int port);
+
+    /**
+     * Sets the stored instance with the given host and port as key.
+     * @param host the host in the register center.
+     * @param port the port in the register center.
+     * @param value the instance to store.
+     */
+    void put(String host, int port, T value);
+
+    /**
+     * Checks if the instance exists with the given host and port.
+     * @param host the host in the register center.
+     * @param port the port in the register center.
+     * @return {@code true} if the instance exists with the given host and port, otherwise {@code false}
+     */
+    boolean contains(String host, int port);
+
+    /**
+     * Returns the size of all stored values.
+     */
+    int size();
+
+    /**
+     * Clear all data.
+     */
+    void clear();
+}
diff --git a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/integration/multiple/servicediscoveryregistry/MultipleRegistryCenterServiceDiscoveryRegistryIntegrationTest.java b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/integration/multiple/servicediscoveryregistry/MultipleRegistryCenterServiceDiscoveryRegistryIntegrationTest.java
new file mode 100644
index 0000000..b493812
--- /dev/null
+++ b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/integration/multiple/servicediscoveryregistry/MultipleRegistryCenterServiceDiscoveryRegistryIntegrationTest.java
@@ -0,0 +1,220 @@
+/*
+ * 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.integration.multiple.servicediscoveryregistry;
+
+import org.apache.dubbo.common.constants.CommonConstants;
+import org.apache.dubbo.common.extension.ExtensionLoader;
+import org.apache.dubbo.config.*;
+import org.apache.dubbo.config.bootstrap.DubboBootstrap;
+import org.apache.dubbo.integration.IntegrationTest;
+import org.apache.dubbo.integration.multiple.injvm.*;
+import org.apache.dubbo.registry.RegistryServiceListener;
+import org.apache.dubbo.registry.client.metadata.store.InMemoryWritableMetadataService;
+import org.apache.dubbo.registrycenter.DefaultMultipleRegistryCenter;
+import org.apache.dubbo.registrycenter.MultipleRegistryCenter;
+import org.apache.dubbo.rpc.ExporterListener;
+import org.apache.dubbo.rpc.Filter;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.Set;
+
+import static org.apache.dubbo.rpc.Constants.SCOPE_LOCAL;
+
+/**
+ * The testcases are only for checking the process of exporting provider using service-discovery-registry protocol.
+ */
+public class MultipleRegistryCenterServiceDiscoveryRegistryIntegrationTest implements IntegrationTest {
+
+    private static final Logger logger = LoggerFactory.getLogger(MultipleRegistryCenterServiceDiscoveryRegistryIntegrationTest.class);
+
+    /**
+     * Define the provider application name.
+     */
+    public static String PROVIDER_APPLICATION_NAME = "multiple-registry-center-provider-for-service-discovery-registry-protocol";
+
+    /**
+     * The name for getting the specified instance, which is loaded using SPI.
+     */
+    private static String SPI_NAME = "multipleConfigCenterServiceDiscoveryRegistry";
+
+    /**
+     * Define the protocol's name.
+     */
+    private static String PROTOCOL_NAME = CommonConstants.DUBBO;
+    /**
+     * Define the protocol's port.
+     */
+    private static int PROTOCOL_PORT = 20880;
+
+    /**
+     * Define the {@link ServiceConfig} instance.
+     */
+    private ServiceConfig<MultipleRegistryCenterServiceDiscoveryRegistryService> serviceConfig;
+
+    /**
+     * Default a registry center.
+     */
+    private MultipleRegistryCenter registryCenter;
+
+    /**
+     * Define a {@link RegistryServiceListener} instance.
+     */
+    private MultipleRegistryCenterServiceDiscoveryRegistryRegistryServiceListener registryServiceListener;
+
+    /**
+     * The localhost.
+     */
+    private static String HOST = "127.0.0.1";
+
+    /**
+     * The port of register center.
+     */
+    private Set<Integer> ports = new HashSet<>(2);
+
+    @BeforeEach
+    public void setUp() throws Exception {
+        logger.info(getClass().getSimpleName() + " testcase is beginning...");
+        DubboBootstrap.reset();
+        //start all zookeeper services only once
+        registryCenter = new DefaultMultipleRegistryCenter();
+        registryCenter.startup();
+        // initialize service config
+        serviceConfig = new ServiceConfig<>();
+        serviceConfig.setInterface(MultipleRegistryCenterServiceDiscoveryRegistryService.class);
+        serviceConfig.setRef(new MultipleRegistryCenterServiceDiscoveryRegistryServiceImpl());
+        serviceConfig.setAsync(false);
+
+        // initailize bootstrap
+        for (RegistryConfig registryConfig : registryCenter.getRegistryConfigs()) {
+            DubboBootstrap.getInstance().registry(registryConfig);
+            ports.add(registryConfig.getPort());
+        }
+        DubboBootstrap.getInstance()
+            .application(new ApplicationConfig(PROVIDER_APPLICATION_NAME))
+            .protocol(new ProtocolConfig(PROTOCOL_NAME, PROTOCOL_PORT))
+            .service(serviceConfig);
+        // ---------------initialize--------------- //
+        registryServiceListener = (MultipleRegistryCenterServiceDiscoveryRegistryRegistryServiceListener) ExtensionLoader
+            .getExtensionLoader(RegistryServiceListener.class).getExtension(SPI_NAME);
+        // RegistryServiceListener is not null
+        Assertions.assertNotNull(registryServiceListener);
+        registryServiceListener.getStorage().clear();
+
+    }
+
+    /**
+     * Define a {@link RegistryServiceListener} for helping check.<p>
+     * There are some checkpoints need to verify as follow:
+     * <ul>
+     *     <li>ServiceConfig is exported or not</li>
+     *     <li>ServiceDiscoveryRegistryStorage is empty or not</li>
+     * </ul>
+     */
+    private void beforeExport() {
+        // ---------------checkpoints--------------- //
+        // ServiceConfig isn't exported
+        Assertions.assertFalse(serviceConfig.isExported());
+
+        // ServiceDiscoveryRegistryStorage is empty
+        Assertions.assertEquals(registryServiceListener.getStorage().size(),0);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Test
+    @Override
+    public void integrate() {
+        beforeExport();
+        DubboBootstrap.getInstance().start();
+        afterExport();
+        ReferenceConfig<MultipleRegistryCenterServiceDiscoveryRegistryService> referenceConfig = new ReferenceConfig<>();
+        referenceConfig.setInterface(MultipleRegistryCenterServiceDiscoveryRegistryService.class);
+        referenceConfig.setBootstrap(DubboBootstrap.getInstance());
+        referenceConfig.get().hello("Dubbo in multiple registry center");
+        afterInvoke();
+    }
+
+    /**
+     * There are some checkpoints need to check after exported as follow:
+     * <ul>
+     *     <li>ServiceDiscoveryRegistry is right or not</li>
+     *     <li>All register center has been registered and subscribed</li>
+     * </ul>
+     */
+    private void afterExport() {
+        // ServiceDiscoveryRegistry is not null
+        Assertions.assertEquals(registryServiceListener.getStorage().size(),2);
+        // All register center has been registered and subscribed
+        for (int port: ports){
+            Assertions.assertTrue(registryServiceListener.getStorage().contains(HOST,port));
+            ServiceDiscoveryRegistryInfoWrapper serviceDiscoveryRegistryInfoWrapper = registryServiceListener.getStorage().get(HOST,port);
+            // check if it's registered
+            Assertions.assertTrue(serviceDiscoveryRegistryInfoWrapper.isRegistered());
+            // check if it's subscribed
+            Assertions.assertTrue(serviceDiscoveryRegistryInfoWrapper.isSubscribed());
+            InMemoryWritableMetadataService inMemoryWritableMetadataService = serviceDiscoveryRegistryInfoWrapper.getInMemoryWritableMetadataService();
+            // check if the count of exported urls is right or not
+            Assertions.assertEquals(inMemoryWritableMetadataService.getExportedURLs().size(),1);
+            // check the exported url is right or not.
+            Assertions.assertTrue(inMemoryWritableMetadataService.getExportedURLs()
+                .first()
+                .contains(MultipleRegistryCenterServiceDiscoveryRegistryService.class.getName()));
+            // check the count of metadatainfo is right or not.
+            Assertions.assertEquals(inMemoryWritableMetadataService.getMetadataInfos().size(),1);
+        }
+    }
+
+    /**
+     * There are some checkpoints need to check after invoked as follow:
+     */
+    private void afterInvoke() {
+
+    }
+
+    @AfterEach
+    public void tearDown() throws IOException {
+        DubboBootstrap.reset();
+        PROVIDER_APPLICATION_NAME = null;
+        serviceConfig = null;
+        // TODO: we need to check whether this scenario is normal
+        // TODO: the Exporter and ServiceDiscoveryRegistry are same in multiple registry center
+        /*
+        for (int port: ports) {
+            Assertions.assertTrue(registryServiceListener.getStorage().contains(HOST, port));
+            ServiceDiscoveryRegistryInfoWrapper serviceDiscoveryRegistryInfoWrapper = registryServiceListener.getStorage().get(HOST, port);
+            // check if it's registered
+            Assertions.assertFalse(serviceDiscoveryRegistryInfoWrapper.isRegistered());
+            // check if it's subscribed
+            Assertions.assertFalse(serviceDiscoveryRegistryInfoWrapper.isSubscribed());
+        }
+        */
+        registryServiceListener.getStorage().clear();
+        registryServiceListener = null;
+        logger.info(getClass().getSimpleName() + " testcase is ending...");
+        // destroy registry center
+        registryCenter.shutdown();
+        registryCenter = null;
+    }
+}
diff --git a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/integration/multiple/servicediscoveryregistry/MultipleRegistryCenterServiceDiscoveryRegistryRegistryServiceListener.java b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/integration/multiple/servicediscoveryregistry/MultipleRegistryCenterServiceDiscoveryRegistryRegistryServiceListener.java
new file mode 100644
index 0000000..a1dd4d7
--- /dev/null
+++ b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/integration/multiple/servicediscoveryregistry/MultipleRegistryCenterServiceDiscoveryRegistryRegistryServiceListener.java
@@ -0,0 +1,103 @@
+/*
+ * 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.integration.multiple.servicediscoveryregistry;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.constants.CommonConstants;
+import org.apache.dubbo.common.extension.Activate;
+import org.apache.dubbo.metadata.WritableMetadataService;
+import org.apache.dubbo.registry.Registry;
+import org.apache.dubbo.registry.RegistryServiceListener;
+import org.apache.dubbo.registry.client.ServiceDiscoveryRegistry;
+import org.apache.dubbo.registry.client.metadata.store.InMemoryWritableMetadataService;
+
+@Activate
+public class MultipleRegistryCenterServiceDiscoveryRegistryRegistryServiceListener implements RegistryServiceListener {
+
+    private ServiceDiscoveryRegistryStorage storage = new ServiceDiscoveryRegistryStorage();
+
+    /**
+     * Create an {@link ServiceDiscoveryRegistryInfoWrapper} instance.
+     */
+    private ServiceDiscoveryRegistryInfoWrapper createServiceDiscoveryRegistryInfoWrapper(ServiceDiscoveryRegistry serviceDiscoveryRegistry){
+        String host = serviceDiscoveryRegistry.getUrl().getHost();
+        int port = serviceDiscoveryRegistry.getUrl().getPort();
+        ServiceDiscoveryRegistryInfoWrapper serviceDiscoveryRegistryInfoWrapper = new ServiceDiscoveryRegistryInfoWrapper();
+        serviceDiscoveryRegistryInfoWrapper.setHost(host);
+        serviceDiscoveryRegistryInfoWrapper.setPort(port);
+        serviceDiscoveryRegistryInfoWrapper.setServiceDiscoveryRegistry(serviceDiscoveryRegistry);
+        serviceDiscoveryRegistryInfoWrapper.setInMemoryWritableMetadataService((InMemoryWritableMetadataService) WritableMetadataService.getDefaultExtension());
+        serviceDiscoveryRegistryInfoWrapper.setRegistered(true);
+        return serviceDiscoveryRegistryInfoWrapper;
+    }
+
+    /**
+     * Checks if the registry is checked application
+     */
+    private boolean isCheckedApplication(Registry registry){
+        return registry.getUrl().getParameter(CommonConstants.APPLICATION_KEY)
+            .equals(MultipleRegistryCenterServiceDiscoveryRegistryIntegrationTest
+                .PROVIDER_APPLICATION_NAME);
+    }
+
+    public void onRegister(URL url, Registry registry) {
+        if (registry instanceof ServiceDiscoveryRegistry && isCheckedApplication(registry)) {
+            ServiceDiscoveryRegistry serviceDiscoveryRegistry = (ServiceDiscoveryRegistry)registry;
+            String host = serviceDiscoveryRegistry.getUrl().getHost();
+            int port = serviceDiscoveryRegistry.getUrl().getPort();
+            if (!storage.contains(host,port)){
+                storage.put(host,port,createServiceDiscoveryRegistryInfoWrapper(serviceDiscoveryRegistry));
+            }
+            storage.get(host,port).setRegistered(true);
+        }
+    }
+
+    public void onUnregister(URL url, Registry registry) {
+        if (registry instanceof ServiceDiscoveryRegistry && isCheckedApplication(registry)) {
+            String host = registry.getUrl().getHost();
+            int port = registry.getUrl().getPort();
+            storage.get(host,port).setRegistered(false);
+        }
+    }
+
+    public void onSubscribe(URL url, Registry registry) {
+        if (registry instanceof ServiceDiscoveryRegistry && isCheckedApplication(registry)) {
+            ServiceDiscoveryRegistry serviceDiscoveryRegistry = (ServiceDiscoveryRegistry)registry;
+            String host = serviceDiscoveryRegistry.getUrl().getHost();
+            int port = serviceDiscoveryRegistry.getUrl().getPort();
+            if (!storage.contains(host,port)){
+                storage.put(host,port,createServiceDiscoveryRegistryInfoWrapper(serviceDiscoveryRegistry));
+            }
+            storage.get(host,port).setSubscribed(true);
+        }
+    }
+
+    public void onUnsubscribe(URL url, Registry registry) {
+        if (registry instanceof ServiceDiscoveryRegistry && isCheckedApplication(registry)) {
+            String host = registry.getUrl().getHost();
+            int port = registry.getUrl().getPort();
+            storage.get(host,port).setSubscribed(false);
+        }
+    }
+
+    /**
+     * Return the stored {@link ServiceDiscoveryRegistryInfoWrapper} instances.
+     */
+    public ServiceDiscoveryRegistryStorage getStorage() {
+        return storage;
+    }
+}
diff --git a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/integration/multiple/servicediscoveryregistry/MultipleRegistryCenterServiceDiscoveryRegistryService.java b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/integration/multiple/servicediscoveryregistry/MultipleRegistryCenterServiceDiscoveryRegistryService.java
new file mode 100644
index 0000000..476ccb9
--- /dev/null
+++ b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/integration/multiple/servicediscoveryregistry/MultipleRegistryCenterServiceDiscoveryRegistryService.java
@@ -0,0 +1,26 @@
+/*
+ * 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.integration.multiple.servicediscoveryregistry;
+/**
+ * This interface is used to check if the exported service-discovery-registry protocol works well or not.
+ */
+public interface MultipleRegistryCenterServiceDiscoveryRegistryService {
+    /**
+     * The simple method for testing.
+     */
+    String hello(String name);
+}
diff --git a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/integration/multiple/servicediscoveryregistry/MultipleRegistryCenterServiceDiscoveryRegistryServiceImpl.java b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/integration/multiple/servicediscoveryregistry/MultipleRegistryCenterServiceDiscoveryRegistryServiceImpl.java
new file mode 100644
index 0000000..104a513
--- /dev/null
+++ b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/integration/multiple/servicediscoveryregistry/MultipleRegistryCenterServiceDiscoveryRegistryServiceImpl.java
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.integration.multiple.servicediscoveryregistry;
+/**
+ * The simple implementation for {@link MultipleRegistryCenterServiceDiscoveryRegistryService}
+ */
+public class MultipleRegistryCenterServiceDiscoveryRegistryServiceImpl implements MultipleRegistryCenterServiceDiscoveryRegistryService {
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public String hello(String name) {
+        return "Hello " + name;
+    }
+}
diff --git a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/integration/multiple/servicediscoveryregistry/ServiceDiscoveryRegistryInfoWrapper.java b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/integration/multiple/servicediscoveryregistry/ServiceDiscoveryRegistryInfoWrapper.java
new file mode 100644
index 0000000..583eec9
--- /dev/null
+++ b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/integration/multiple/servicediscoveryregistry/ServiceDiscoveryRegistryInfoWrapper.java
@@ -0,0 +1,82 @@
+/*
+ * 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.integration.multiple.servicediscoveryregistry;
+
+import org.apache.dubbo.registry.client.ServiceDiscoveryRegistry;
+import org.apache.dubbo.registry.client.metadata.store.InMemoryWritableMetadataService;
+
+/**
+ * The instance to wrap {@link org.apache.dubbo.registry.client.ServiceDiscoveryRegistry}
+ * and {@link org.apache.dubbo.registry.client.metadata.store.InMemoryWritableMetadataService}
+ */
+public class ServiceDiscoveryRegistryInfoWrapper {
+
+    private ServiceDiscoveryRegistry serviceDiscoveryRegistry;
+    private InMemoryWritableMetadataService inMemoryWritableMetadataService;
+    private boolean registered;
+    private boolean subscribed;
+    private String host;
+    private int port;
+
+    public ServiceDiscoveryRegistry getServiceDiscoveryRegistry() {
+        return serviceDiscoveryRegistry;
+    }
+
+    public void setServiceDiscoveryRegistry(ServiceDiscoveryRegistry serviceDiscoveryRegistry) {
+        this.serviceDiscoveryRegistry = serviceDiscoveryRegistry;
+    }
+
+    public InMemoryWritableMetadataService getInMemoryWritableMetadataService() {
+        return inMemoryWritableMetadataService;
+    }
+
+    public void setInMemoryWritableMetadataService(InMemoryWritableMetadataService inMemoryWritableMetadataService) {
+        this.inMemoryWritableMetadataService = inMemoryWritableMetadataService;
+    }
+
+    public boolean isRegistered() {
+        return registered;
+    }
+
+    public void setRegistered(boolean registered) {
+        this.registered = registered;
+    }
+
+    public boolean isSubscribed() {
+        return subscribed;
+    }
+
+    public void setSubscribed(boolean subscribed) {
+        this.subscribed = subscribed;
+    }
+
+    public String getHost() {
+        return host;
+    }
+
+    public void setHost(String host) {
+        this.host = host;
+    }
+
+    public int getPort() {
+        return port;
+    }
+
+    public void setPort(int port) {
+        this.port = port;
+    }
+}
diff --git a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/integration/multiple/servicediscoveryregistry/ServiceDiscoveryRegistryStorage.java b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/integration/multiple/servicediscoveryregistry/ServiceDiscoveryRegistryStorage.java
new file mode 100644
index 0000000..ea84bea
--- /dev/null
+++ b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/integration/multiple/servicediscoveryregistry/ServiceDiscoveryRegistryStorage.java
@@ -0,0 +1,26 @@
+/*
+ * 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.integration.multiple.servicediscoveryregistry;
+
+import org.apache.dubbo.integration.multiple.AbstractStorage;
+
+/**
+ * The storage to store {@link ServiceDiscoveryRegistryInfoWrapper} instances in multiple registry center.
+ */
+public class ServiceDiscoveryRegistryStorage extends AbstractStorage<ServiceDiscoveryRegistryInfoWrapper> {
+
+}
diff --git a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/integration/single/SingleRegistryCenterDubboProtocolIntegrationTest.java b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/integration/single/SingleRegistryCenterDubboProtocolIntegrationTest.java
index f428ef4..e1ffb81 100644
--- a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/integration/single/SingleRegistryCenterDubboProtocolIntegrationTest.java
+++ b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/integration/single/SingleRegistryCenterDubboProtocolIntegrationTest.java
@@ -177,7 +177,6 @@ public class SingleRegistryCenterDubboProtocolIntegrationTest implements Integra
      *     <li>Protocol name is right or not</li>
      *     <li>Protocol port is right or not</li>
      *     <li>ServiceDiscoveryRegistry's protocol is right or not</li>
-     *     <li>ServiceDiscoveryRegistry is destroy or not</li>
      *     <li>Registered service in registry center is right or not</li>
      *     <li>Exported url is right or not in InMemoryWritableMetadataService</li>
      *     <li>MetadataInfo exists or not in InMemoryWritableMetadataService</li>
@@ -221,8 +220,6 @@ public class SingleRegistryCenterDubboProtocolIntegrationTest implements Integra
         Assertions.assertTrue(serviceDiscoveryRegistry.getServiceDiscovery() instanceof ZookeeperServiceDiscovery);
         // Convert to ZookeeperServiceDiscovery instance
         ZookeeperServiceDiscovery zookeeperServiceDiscovery = (ZookeeperServiceDiscovery) serviceDiscoveryRegistry.getServiceDiscovery();
-        // ServiceDiscoveryRegistry is destroy or not
-        Assertions.assertFalse(zookeeperServiceDiscovery.isDestroy());
         // Gets registered service by ZookeeperServiceDiscovery
         Set<String> services = zookeeperServiceDiscovery.getServices();
         // check service exists
diff --git a/dubbo-config/dubbo-config-api/src/test/resources/META-INF/services/org.apache.dubbo.registry.RegistryServiceListener b/dubbo-config/dubbo-config-api/src/test/resources/META-INF/services/org.apache.dubbo.registry.RegistryServiceListener
new file mode 100644
index 0000000..4caa712
--- /dev/null
+++ b/dubbo-config/dubbo-config-api/src/test/resources/META-INF/services/org.apache.dubbo.registry.RegistryServiceListener
@@ -0,0 +1,18 @@
+#
+# 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.
+#
+
+multipleConfigCenterServiceDiscoveryRegistry=org.apache.dubbo.integration.multiple.servicediscoveryregistry.MultipleRegistryCenterServiceDiscoveryRegistryRegistryServiceListener