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

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


The following commit(s) were added to refs/heads/master by this push:
     new 543a52a  Multiple registry  (#4066)
543a52a is described below

commit 543a52abc0a86efad59f0f97f32de282321a4ab5
Author: cvictory <[email protected]>
AuthorDate: Fri May 24 14:54:37 2019 +0800

    Multiple registry  (#4066)
    
    fixes #3932, #3599, #3084
---
 dubbo-all/pom.xml                                  |   8 +
 dubbo-registry/dubbo-registry-multiple/pom.xml     |  60 ++++
 .../dubbo/registry/multiple/MultipleRegistry.java  | 314 +++++++++++++++++++++
 .../registry/multiple/MultipleRegistryFactory.java |  33 +++
 .../org.apache.dubbo.registry.RegistryFactory      |   1 +
 .../multiple/MultipleRegistry2S2RTest.java         | 206 ++++++++++++++
 .../multiple/MultipleRegistryTestUtil.java         | 138 +++++++++
 dubbo-registry/pom.xml                             |   1 +
 8 files changed, 761 insertions(+)

diff --git a/dubbo-all/pom.xml b/dubbo-all/pom.xml
index be5a2e3..d8c8949 100644
--- a/dubbo-all/pom.xml
+++ b/dubbo-all/pom.xml
@@ -284,6 +284,13 @@
         </dependency>
         <dependency>
             <groupId>org.apache.dubbo</groupId>
+            <artifactId>dubbo-registry-multiple</artifactId>
+            <version>${project.version}</version>
+            <scope>compile</scope>
+            <optional>true</optional>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.dubbo</groupId>
             <artifactId>dubbo-monitor-api</artifactId>
             <version>${project.version}</version>
             <scope>compile</scope>
@@ -596,6 +603,7 @@
                                     
<include>org.apache.dubbo:dubbo-registry-etcd3</include>
                                     
<include>org.apache.dubbo:dubbo-registry-nacos</include>
                                     
<include>org.apache.dubbo:dubbo-registry-sofa</include>
+                                    
<include>org.apache.dubbo:dubbo-registry-multiple</include>
                                     
<include>org.apache.dubbo:dubbo-monitor-api</include>
                                     
<include>org.apache.dubbo:dubbo-monitor-default</include>
                                     
<include>org.apache.dubbo:dubbo-config-api</include>
diff --git a/dubbo-registry/dubbo-registry-multiple/pom.xml 
b/dubbo-registry/dubbo-registry-multiple/pom.xml
new file mode 100644
index 0000000..e62e2fe
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-multiple/pom.xml
@@ -0,0 +1,60 @@
+<!--
+  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.
+  -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"; 
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/maven-v4_0_0.xsd";>
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache.dubbo</groupId>
+        <artifactId>dubbo-registry</artifactId>
+        <version>${revision}</version>
+    </parent>
+    <artifactId>dubbo-registry-multiple</artifactId>
+    <packaging>jar</packaging>
+    <name>${project.artifactId}</name>
+    <description>The multiple registry module of dubbo project</description>
+    <properties>
+        <skip_maven_deploy>false</skip_maven_deploy>
+    </properties>
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.dubbo</groupId>
+            <artifactId>dubbo-registry-api</artifactId>
+            <version>${project.parent.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.dubbo</groupId>
+            <artifactId>dubbo-registry-zookeeper</artifactId>
+            <version>${project.parent.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.dubbo</groupId>
+            <artifactId>dubbo-registry-redis</artifactId>
+            <version>${project.parent.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.curator</groupId>
+            <artifactId>curator-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.github.kstyrc</groupId>
+            <artifactId>embedded-redis</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+</project>
diff --git 
a/dubbo-registry/dubbo-registry-multiple/src/main/java/org/apache/dubbo/registry/multiple/MultipleRegistry.java
 
b/dubbo-registry/dubbo-registry-multiple/src/main/java/org/apache/dubbo/registry/multiple/MultipleRegistry.java
new file mode 100644
index 0000000..ea59481
--- /dev/null
+++ 
b/dubbo-registry/dubbo-registry-multiple/src/main/java/org/apache/dubbo/registry/multiple/MultipleRegistry.java
@@ -0,0 +1,314 @@
+/*
+ * 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.multiple;
+
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.constants.CommonConstants;
+import org.apache.dubbo.common.extension.ExtensionLoader;
+import org.apache.dubbo.common.utils.CollectionUtils;
+import org.apache.dubbo.registry.NotifyListener;
+import org.apache.dubbo.registry.Registry;
+import org.apache.dubbo.registry.RegistryFactory;
+import org.apache.dubbo.registry.support.AbstractRegistry;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import static 
org.apache.dubbo.common.constants.RegistryConstants.EMPTY_PROTOCOL;
+
+/**
+ * MultipleRegistry
+ */
+public class MultipleRegistry extends AbstractRegistry {
+
+    public static final String REGISTRY_FOR_SERVICE = "service-registry";
+    public static final String REGISTRY_FOR_REFERENCE = "reference-registry";
+
+    protected RegistryFactory registryFactory = 
ExtensionLoader.getExtensionLoader(RegistryFactory.class).getAdaptiveExtension();
+    private final Map<String, Registry> serviceRegistries = new 
ConcurrentHashMap<>(4);
+    private final Map<String, Registry> referenceRegistries = new 
ConcurrentHashMap<String, Registry>(4);
+    private final Map<NotifyListener, MultipleNotifyListenerWrapper> 
multipleNotifyListenerMap = new ConcurrentHashMap<NotifyListener, 
MultipleNotifyListenerWrapper>(32);
+    protected List<String> origServiceRegistryURLs;
+    protected List<String> origReferenceRegistryURLs;
+    protected List<String> effectServiceRegistryURLs;
+    protected List<String> effectReferenceRegistryURLs;
+    private URL registryUrl;
+    private String applicationName;
+
+
+    public MultipleRegistry(URL url) {
+        super(url);
+        this.registryUrl = url;
+        this.applicationName = 
url.getParameter(CommonConstants.APPLICATION_KEY);
+        init();
+        checkApplicationName(this.applicationName);
+        // This urls contain parameter and it donot inherit from the parameter 
of url in MultipleRegistry
+        origServiceRegistryURLs = url.getParameter(REGISTRY_FOR_SERVICE, new 
ArrayList<String>());
+        origReferenceRegistryURLs = url.getParameter(REGISTRY_FOR_REFERENCE, 
new ArrayList<String>());
+        effectServiceRegistryURLs = 
this.filterServiceRegistry(origServiceRegistryURLs);
+        effectReferenceRegistryURLs = 
this.filterReferenceRegistry(origReferenceRegistryURLs);
+
+        boolean defaultRegistry = 
url.getParameter(CommonConstants.DEFAULT_KEY, true);
+        if (defaultRegistry && effectServiceRegistryURLs.isEmpty() && 
effectReferenceRegistryURLs.isEmpty()) {
+            throw new IllegalArgumentException("Illegal registry url. You need 
to configure parameter " +
+                    REGISTRY_FOR_SERVICE + " or " + REGISTRY_FOR_REFERENCE);
+        }
+        Set<String> allURLs = new HashSet<String>(effectServiceRegistryURLs);
+        allURLs.addAll(effectReferenceRegistryURLs);
+        Map<String, Registry> tmpMap = new HashMap<String, Registry>(4);
+        for (String tmpUrl : allURLs) {
+            tmpMap.put(tmpUrl, 
registryFactory.getRegistry(URL.valueOf(tmpUrl)));
+        }
+        for (String serviceRegistyURL : effectServiceRegistryURLs) {
+            serviceRegistries.put(serviceRegistyURL, 
tmpMap.get(serviceRegistyURL));
+        }
+        for (String referenceReigstyURL : effectReferenceRegistryURLs) {
+            referenceRegistries.put(referenceReigstyURL, 
tmpMap.get(referenceReigstyURL));
+        }
+    }
+
+
+    @Override
+    public URL getUrl() {
+        return registryUrl;
+    }
+
+    @Override
+    public boolean isAvailable() {
+        boolean available = serviceRegistries.isEmpty() ? true : false;
+        for (Registry serviceRegistry : serviceRegistries.values()) {
+            if (serviceRegistry.isAvailable()) {
+                available = true;
+            }
+        }
+        if (!available) {
+            return false;
+        }
+
+        available = referenceRegistries.isEmpty() ? true : false;
+        for (Registry referenceRegistry : referenceRegistries.values()) {
+            if (referenceRegistry.isAvailable()) {
+                available = true;
+            }
+        }
+        if (!available) {
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public void destroy() {
+        Set<Registry> registries = new 
HashSet<Registry>(serviceRegistries.values());
+        registries.addAll(referenceRegistries.values());
+        for (Registry registry : registries) {
+            registry.destroy();
+        }
+    }
+
+    @Override
+    public void register(URL url) {
+        super.register(url);
+        for (Registry registry : serviceRegistries.values()) {
+            registry.register(url);
+        }
+    }
+
+    @Override
+    public void unregister(URL url) {
+        super.unregister(url);
+        for (Registry registry : serviceRegistries.values()) {
+            registry.unregister(url);
+        }
+    }
+
+    @Override
+    public void subscribe(URL url, NotifyListener listener) {
+        MultipleNotifyListenerWrapper multipleNotifyListenerWrapper = new 
MultipleNotifyListenerWrapper(listener);
+        multipleNotifyListenerMap.put(listener, multipleNotifyListenerWrapper);
+        for (Registry registry : referenceRegistries.values()) {
+            SingleNotifyListener singleNotifyListener = new 
SingleNotifyListener(multipleNotifyListenerWrapper, registry);
+            multipleNotifyListenerWrapper.putRegistryMap(registry.getUrl(), 
singleNotifyListener);
+            registry.subscribe(url, singleNotifyListener);
+        }
+        super.subscribe(url, multipleNotifyListenerWrapper);
+    }
+
+    @Override
+    public void unsubscribe(URL url, NotifyListener listener) {
+        MultipleNotifyListenerWrapper notifyListener = 
multipleNotifyListenerMap.remove(listener);
+        for (Registry registry : referenceRegistries.values()) {
+            SingleNotifyListener singleNotifyListener = 
notifyListener.registryMap.get(registry.getUrl());
+            registry.unsubscribe(url, singleNotifyListener);
+        }
+
+        if (notifyListener != null) {
+            super.unsubscribe(url, notifyListener);
+            notifyListener.destroy();
+        }
+    }
+
+    @Override
+    public List<URL> lookup(URL url) {
+        List<URL> urls = new ArrayList<URL>();
+        for (Registry registry : referenceRegistries.values()) {
+            List<URL> tmpUrls = registry.lookup(url);
+            if (!CollectionUtils.isEmpty(tmpUrls)) {
+                urls.addAll(tmpUrls);
+            }
+        }
+        return urls;
+    }
+
+    protected void init() {
+    }
+
+    protected List<String> filterServiceRegistry(List<String> 
serviceRegistryURLs) {
+        return serviceRegistryURLs;
+    }
+
+    protected List<String> filterReferenceRegistry(List<String> 
referenceRegistryURLs) {
+        return referenceRegistryURLs;
+    }
+
+
+    protected void checkApplicationName(String applicationName) {
+    }
+
+    protected String getApplicationName() {
+        return applicationName;
+    }
+
+    public Map<String, Registry> getServiceRegistries() {
+        return serviceRegistries;
+    }
+
+    public Map<String, Registry> getReferenceRegistries() {
+        return referenceRegistries;
+    }
+
+    public List<String> getOrigServiceRegistryURLs() {
+        return origServiceRegistryURLs;
+    }
+
+    public List<String> getOrigReferenceRegistryURLs() {
+        return origReferenceRegistryURLs;
+    }
+
+    public List<String> getEffectServiceRegistryURLs() {
+        return effectServiceRegistryURLs;
+    }
+
+    public List<String> getEffectReferenceRegistryURLs() {
+        return effectReferenceRegistryURLs;
+    }
+
+    static protected class MultipleNotifyListenerWrapper implements 
NotifyListener {
+
+        Map<URL, SingleNotifyListener> registryMap = new 
ConcurrentHashMap<URL, SingleNotifyListener>(4);
+        NotifyListener sourceNotifyListener;
+
+        public MultipleNotifyListenerWrapper(NotifyListener 
sourceNotifyListener) {
+            this.sourceNotifyListener = sourceNotifyListener;
+        }
+
+        public void putRegistryMap(URL registryURL, SingleNotifyListener 
singleNotifyListener) {
+            this.registryMap.put(registryURL, singleNotifyListener);
+        }
+
+        public void destroy() {
+            for (SingleNotifyListener singleNotifyListener : 
registryMap.values()) {
+                if (singleNotifyListener != null) {
+                    singleNotifyListener.destroy();
+                }
+            }
+            registryMap.clear();
+            sourceNotifyListener = null;
+        }
+
+        public synchronized void notifySourceListener() {
+            List<URL> notifyURLs = new ArrayList<URL>();
+            URL emptyURL = null;
+            for (SingleNotifyListener singleNotifyListener : 
registryMap.values()) {
+                List<URL> tmpUrls = singleNotifyListener.getUrlList();
+                if (CollectionUtils.isEmpty(tmpUrls)) {
+                    continue;
+                }
+                // empty protocol
+                if (tmpUrls.size() == 1
+                        && tmpUrls.get(0) != null
+                        && 
EMPTY_PROTOCOL.equals(tmpUrls.get(0).getProtocol())) {
+                    // if only one empty
+                    if (emptyURL == null) {
+                        emptyURL = tmpUrls.get(0);
+                    }
+                    continue;
+                }
+                notifyURLs.addAll(tmpUrls);
+            }
+            // if no notify URL, add empty protocol URL
+            if (emptyURL != null && notifyURLs.isEmpty()) {
+                notifyURLs.add(emptyURL);
+            }
+            this.notify(notifyURLs);
+        }
+
+        @Override
+        public void notify(List<URL> urls) {
+            sourceNotifyListener.notify(urls);
+        }
+
+        public Map<URL, SingleNotifyListener> getRegistryMap() {
+            return registryMap;
+        }
+    }
+
+    static protected class SingleNotifyListener implements NotifyListener {
+
+        MultipleNotifyListenerWrapper multipleNotifyListenerWrapper;
+        Registry registry;
+        volatile List<URL> urlList;
+
+        public SingleNotifyListener(MultipleNotifyListenerWrapper 
multipleNotifyListenerWrapper, Registry registry) {
+            this.registry = registry;
+            this.multipleNotifyListenerWrapper = multipleNotifyListenerWrapper;
+        }
+
+        @Override
+        public synchronized void notify(List<URL> urls) {
+            this.urlList = urls;
+            if (multipleNotifyListenerWrapper != null) {
+                this.multipleNotifyListenerWrapper.notifySourceListener();
+            }
+        }
+
+        public void destroy() {
+            this.multipleNotifyListenerWrapper = null;
+            this.registry = null;
+        }
+
+        public List<URL> getUrlList() {
+            return urlList;
+        }
+    }
+}
diff --git 
a/dubbo-registry/dubbo-registry-multiple/src/main/java/org/apache/dubbo/registry/multiple/MultipleRegistryFactory.java
 
b/dubbo-registry/dubbo-registry-multiple/src/main/java/org/apache/dubbo/registry/multiple/MultipleRegistryFactory.java
new file mode 100644
index 0000000..69d695b
--- /dev/null
+++ 
b/dubbo-registry/dubbo-registry-multiple/src/main/java/org/apache/dubbo/registry/multiple/MultipleRegistryFactory.java
@@ -0,0 +1,33 @@
+/*
+ * 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.multiple;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.registry.Registry;
+import org.apache.dubbo.registry.support.AbstractRegistryFactory;
+
+/**
+ * MultipleRegistryFactory
+ */
+public class MultipleRegistryFactory extends AbstractRegistryFactory {
+
+    @Override
+    protected Registry createRegistry(URL url) {
+        return new MultipleRegistry(url);
+    }
+
+}
diff --git 
a/dubbo-registry/dubbo-registry-multiple/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.RegistryFactory
 
b/dubbo-registry/dubbo-registry-multiple/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.RegistryFactory
new file mode 100644
index 0000000..defb7a2
--- /dev/null
+++ 
b/dubbo-registry/dubbo-registry-multiple/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.RegistryFactory
@@ -0,0 +1 @@
+multiple=org.apache.dubbo.registry.multiple.MultipleRegistryFactory
diff --git 
a/dubbo-registry/dubbo-registry-multiple/src/test/java/org/apache/dubbo/registry/multiple/MultipleRegistry2S2RTest.java
 
b/dubbo-registry/dubbo-registry-multiple/src/test/java/org/apache/dubbo/registry/multiple/MultipleRegistry2S2RTest.java
new file mode 100644
index 0000000..cfc15b2
--- /dev/null
+++ 
b/dubbo-registry/dubbo-registry-multiple/src/test/java/org/apache/dubbo/registry/multiple/MultipleRegistry2S2RTest.java
@@ -0,0 +1,206 @@
+/*
+ * 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.multiple;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.utils.NetUtils;
+import org.apache.dubbo.registry.NotifyListener;
+import org.apache.dubbo.registry.Registry;
+import org.apache.dubbo.registry.redis.RedisRegistry;
+import org.apache.dubbo.registry.zookeeper.ZookeeperRegistry;
+import org.apache.dubbo.remoting.zookeeper.ZookeeperClient;
+import org.apache.dubbo.remoting.zookeeper.curator.CuratorZookeeperClient;
+
+import org.apache.curator.test.TestingServer;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+import redis.embedded.RedisServer;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 2019-04-30
+ */
+public class MultipleRegistry2S2RTest {
+
+    private static final String SERVICE_NAME = 
"org.apache.dubbo.registry.MultipleService2S2R";
+    private static final String SERVICE2_NAME = 
"org.apache.dubbo.registry.MultipleService2S2R2";
+
+    private static TestingServer zkServer;
+    private static RedisServer redisServer;
+    static int zkServerPort;
+    static int redisServerPort;
+
+    private static String zookeeperRegistryURLStr;
+    private static String redisRegistryURLStr;
+
+    private static MultipleRegistry multipleRegistry;
+    // for test content
+    private static ZookeeperClient zookeeperClient;
+
+    private static ZookeeperRegistry zookeeperRegistry;
+    private static RedisRegistry redisRegistry;
+
+
+    @BeforeAll
+    public static void setUp() throws Exception {
+        zkServerPort = NetUtils.getAvailablePort();
+        zkServer = new TestingServer(zkServerPort, true);
+        zookeeperRegistryURLStr = "zookeeper://127.0.0.1:" + zkServerPort;
+
+        redisServerPort = NetUtils.getAvailablePort();
+        redisServer = new RedisServer(redisServerPort);
+        redisServer.start();
+        redisRegistryURLStr = "redis://127.0.0.1:" + redisServerPort;
+
+
+        URL url = URL.valueOf("multiple://127.0.0.1?application=vic&" +
+                MultipleRegistry.REGISTRY_FOR_SERVICE + "=" + 
zookeeperRegistryURLStr + "," + redisRegistryURLStr + "&"
+                + MultipleRegistry.REGISTRY_FOR_REFERENCE + "=" + 
zookeeperRegistryURLStr + "," + redisRegistryURLStr);
+        multipleRegistry = (MultipleRegistry) new 
MultipleRegistryFactory().createRegistry(url);
+
+        // for test validation
+        zookeeperClient = new 
CuratorZookeeperClient(URL.valueOf(zookeeperRegistryURLStr));
+        zookeeperRegistry = 
MultipleRegistryTestUtil.getZookeeperRegistry(multipleRegistry.getServiceRegistries().values());
+        redisRegistry = 
MultipleRegistryTestUtil.getRedisRegistry(multipleRegistry.getServiceRegistries().values());
+    }
+
+    @AfterAll
+    public static void tearDown() throws Exception {
+        zkServer.stop();
+        redisServer.stop();
+    }
+
+    @Test
+    public void testParamConfig() {
+
+        
Assertions.assertTrue(multipleRegistry.origReferenceRegistryURLs.size() == 2);
+        
Assertions.assertTrue(multipleRegistry.origReferenceRegistryURLs.contains(zookeeperRegistryURLStr));
+        
Assertions.assertTrue(multipleRegistry.origReferenceRegistryURLs.contains(redisRegistryURLStr));
+
+        Assertions.assertTrue(multipleRegistry.origServiceRegistryURLs.size() 
== 2);
+        
Assertions.assertTrue(multipleRegistry.origServiceRegistryURLs.contains(zookeeperRegistryURLStr));
+        
Assertions.assertTrue(multipleRegistry.origServiceRegistryURLs.contains(redisRegistryURLStr));
+
+        
Assertions.assertTrue(multipleRegistry.effectReferenceRegistryURLs.size() == 2);
+        
Assertions.assertTrue(multipleRegistry.effectReferenceRegistryURLs.contains(zookeeperRegistryURLStr));
+        
Assertions.assertTrue(multipleRegistry.effectReferenceRegistryURLs.contains(redisRegistryURLStr));
+
+        
Assertions.assertTrue(multipleRegistry.effectServiceRegistryURLs.size() == 2);
+        
Assertions.assertTrue(multipleRegistry.effectServiceRegistryURLs.contains(zookeeperRegistryURLStr));
+        
Assertions.assertTrue(multipleRegistry.effectServiceRegistryURLs.contains(redisRegistryURLStr));
+
+        
Assertions.assertTrue(multipleRegistry.getServiceRegistries().containsKey(zookeeperRegistryURLStr));
+        
Assertions.assertTrue(multipleRegistry.getServiceRegistries().containsKey(redisRegistryURLStr));
+        
Assertions.assertTrue(multipleRegistry.getServiceRegistries().values().size() 
== 2);
+//        java.util.Iterator<Registry> registryIterable = 
multipleRegistry.getServiceRegistries().values().iterator();
+//        Registry firstRegistry = registryIterable.next();
+//        Registry secondRegistry = registryIterable.next();
+        
Assertions.assertNotNull(MultipleRegistryTestUtil.getZookeeperRegistry(multipleRegistry.getServiceRegistries().values()));
+        
Assertions.assertNotNull(MultipleRegistryTestUtil.getRedisRegistry(multipleRegistry.getServiceRegistries().values()));
+        
Assertions.assertNotNull(MultipleRegistryTestUtil.getZookeeperRegistry(multipleRegistry.getReferenceRegistries().values()));
+        
Assertions.assertNotNull(MultipleRegistryTestUtil.getRedisRegistry(multipleRegistry.getReferenceRegistries().values()));
+
+        
Assertions.assertEquals(MultipleRegistryTestUtil.getZookeeperRegistry(multipleRegistry.getServiceRegistries().values()),
+                
MultipleRegistryTestUtil.getZookeeperRegistry(multipleRegistry.getReferenceRegistries().values()));
+
+        
Assertions.assertEquals(MultipleRegistryTestUtil.getRedisRegistry(multipleRegistry.getServiceRegistries().values()),
+                
MultipleRegistryTestUtil.getRedisRegistry(multipleRegistry.getReferenceRegistries().values()));
+
+        Assertions.assertEquals(multipleRegistry.getApplicationName(), "vic");
+
+        Assertions.assertTrue(multipleRegistry.isAvailable());
+    }
+
+    @Test
+    public void testRegistryAndUnRegistry() throws InterruptedException {
+        URL serviceUrl = URL.valueOf("http2://multiple/" + SERVICE_NAME + 
"?notify=false&methods=test1,test2&category=providers");
+//        URL serviceUrl2 = URL.valueOf("http2://multiple2/" + SERVICE_NAME + 
"?notify=false&methods=test1,test2&category=providers");
+        multipleRegistry.register(serviceUrl);
+
+        String path = "/dubbo/" + SERVICE_NAME + "/providers";
+        List<String> providerList = zookeeperClient.getChildren(path);
+        Assertions.assertTrue(!providerList.isEmpty());
+        System.out.println(providerList.get(0));
+
+        
Assertions.assertNotNull(MultipleRegistryTestUtil.getRedisHashContent(redisServerPort,
 path, serviceUrl.toFullString()));
+
+        final List<URL> list = new ArrayList<URL>();
+        multipleRegistry.subscribe(serviceUrl, new NotifyListener() {
+            @Override
+            public void notify(List<URL> urls) {
+                System.out.println("invoke notify: " + urls);
+                list.clear();
+                list.addAll(urls);
+            }
+        });
+        Thread.sleep(1500);
+        Assertions.assertTrue(list.size() == 2);
+
+        multipleRegistry.unregister(serviceUrl);
+        Thread.sleep(1500);
+        Assertions.assertTrue(list.size() == 1);
+        List<URL> urls = 
MultipleRegistryTestUtil.getProviderURLsFromNotifyURLS(list);
+        Assertions.assertTrue(list.size() == 1);
+        Assertions.assertTrue("empty".equals(list.get(0).getProtocol()));
+    }
+
+    @Test
+    public void testSubscription() throws InterruptedException {
+        URL serviceUrl = URL.valueOf("http2://multiple/" + SERVICE2_NAME + 
"?notify=false&methods=test1,test2&category=providers");
+//        URL serviceUrl2 = URL.valueOf("http2://multiple2/" + SERVICE_NAME + 
"?notify=false&methods=test1,test2&category=providers");
+        multipleRegistry.register(serviceUrl);
+
+        String path = "/dubbo/" + SERVICE2_NAME + "/providers";
+        List<String> providerList = zookeeperClient.getChildren(path);
+        Assertions.assertTrue(!providerList.isEmpty());
+        System.out.println(providerList.get(0));
+
+        
Assertions.assertNotNull(MultipleRegistryTestUtil.getRedisHashContent(redisServerPort,
 path, serviceUrl.toFullString()));
+
+        final List<URL> list = new ArrayList<URL>();
+        multipleRegistry.subscribe(serviceUrl, new NotifyListener() {
+            @Override
+            public void notify(List<URL> urls) {
+                System.out.println("invoke notify: " + urls);
+                list.clear();
+                list.addAll(urls);
+            }
+        });
+        Thread.sleep(1500);
+        Assertions.assertTrue(list.size() == 2);
+
+        List<Registry> serviceRegistries = new 
ArrayList<Registry>(multipleRegistry.getServiceRegistries().values());
+        serviceRegistries.get(0).unregister(serviceUrl);
+        Thread.sleep(1500);
+        Assertions.assertTrue(list.size() == 1);
+        List<URL> urls = 
MultipleRegistryTestUtil.getProviderURLsFromNotifyURLS(list);
+        Assertions.assertTrue(list.size() == 1);
+        Assertions.assertTrue(!"empty".equals(list.get(0).getProtocol()));
+
+        serviceRegistries.get(1).unregister(serviceUrl);
+        Thread.sleep(1500);
+        Assertions.assertTrue(list.size() == 1);
+        urls = MultipleRegistryTestUtil.getProviderURLsFromNotifyURLS(list);
+        Assertions.assertTrue(list.size() == 1);
+        Assertions.assertTrue("empty".equals(list.get(0).getProtocol()));
+    }
+
+}
diff --git 
a/dubbo-registry/dubbo-registry-multiple/src/test/java/org/apache/dubbo/registry/multiple/MultipleRegistryTestUtil.java
 
b/dubbo-registry/dubbo-registry-multiple/src/test/java/org/apache/dubbo/registry/multiple/MultipleRegistryTestUtil.java
new file mode 100644
index 0000000..2c70a83
--- /dev/null
+++ 
b/dubbo-registry/dubbo-registry-multiple/src/test/java/org/apache/dubbo/registry/multiple/MultipleRegistryTestUtil.java
@@ -0,0 +1,138 @@
+/*
+ * 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.multiple;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.utils.StringUtils;
+import org.apache.dubbo.common.utils.UrlUtils;
+import org.apache.dubbo.registry.Registry;
+import org.apache.dubbo.registry.redis.RedisRegistry;
+import org.apache.dubbo.registry.zookeeper.ZookeeperRegistry;
+import org.apache.dubbo.rpc.RpcException;
+
+import redis.clients.jedis.Jedis;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+import static 
org.apache.dubbo.common.constants.RegistryConstants.APP_DYNAMIC_CONFIGURATORS_CATEGORY;
+import static org.apache.dubbo.common.constants.RegistryConstants.CATEGORY_KEY;
+import static 
org.apache.dubbo.common.constants.RegistryConstants.COMPATIBLE_CONFIG_KEY;
+import static 
org.apache.dubbo.common.constants.RegistryConstants.CONFIGURATORS_CATEGORY;
+import static 
org.apache.dubbo.common.constants.RegistryConstants.DEFAULT_CATEGORY;
+import static 
org.apache.dubbo.common.constants.RegistryConstants.DYNAMIC_CONFIGURATORS_CATEGORY;
+import static 
org.apache.dubbo.common.constants.RegistryConstants.PROVIDERS_CATEGORY;
+import static 
org.apache.dubbo.common.constants.RegistryConstants.ROUTERS_CATEGORY;
+import static 
org.apache.dubbo.common.constants.RegistryConstants.ROUTE_PROTOCOL;
+
+/**
+ * 2019-05-13
+ */
+public class MultipleRegistryTestUtil {
+    public static ZookeeperRegistry getZookeeperRegistry(Collection<Registry> 
registryCollection) {
+        for (Registry registry : registryCollection) {
+            if (registry instanceof ZookeeperRegistry) {
+                return (ZookeeperRegistry) registry;
+            }
+        }
+        return null;
+    }
+
+    public static RedisRegistry getRedisRegistry(Collection<Registry> 
registryCollection) {
+        for (Registry registry : registryCollection) {
+            if (registry instanceof RedisRegistry) {
+                return (RedisRegistry) registry;
+            }
+        }
+        return null;
+    }
+
+    public static String getRedisContent(int port, String key) {
+        Jedis jedis = null;
+        try {
+            jedis = new Jedis("127.0.0.1", port);
+            return jedis.get(key);
+        } catch (Throwable e) {
+            throw new RpcException("Failed to put to redis . cause: " + 
e.getMessage(), e);
+        } finally {
+            if (jedis != null) {
+                jedis.close();
+            }
+        }
+    }
+
+    public static String getRedisHashContent(int port, String key, String 
field) {
+        Jedis jedis = null;
+        try {
+            jedis = new Jedis("127.0.0.1", port);
+            return jedis.hget(key, field);
+        } catch (Throwable e) {
+            throw new RpcException("Failed to put to redis . cause: " + 
e.getMessage(), e);
+        } finally {
+            if (jedis != null) {
+                jedis.close();
+            }
+        }
+    }
+
+    /**
+     * copy from 
@org.apache.dubbo.registry.integration.RegistryDirectory#notify(java.util.List)
+     *
+     * @param urls
+     * @return
+     */
+    public static List<URL> getProviderURLsFromNotifyURLS(List<URL> urls) {
+        Map<String, List<URL>> categoryUrls = urls.stream()
+                .filter(Objects::nonNull)
+                .filter(MultipleRegistryTestUtil::isValidCategory)
+                .filter(MultipleRegistryTestUtil::isNotCompatibleFor26x)
+                .collect(Collectors.groupingBy(url -> {
+                    if (UrlUtils.isConfigurator(url)) {
+                        return CONFIGURATORS_CATEGORY;
+                    } else if (UrlUtils.isRoute(url)) {
+                        return ROUTERS_CATEGORY;
+                    } else if (UrlUtils.isProvider(url)) {
+                        return PROVIDERS_CATEGORY;
+                    }
+                    return "";
+                }));
+
+        // providers
+        List<URL> providerURLs = categoryUrls.getOrDefault(PROVIDERS_CATEGORY, 
Collections.emptyList());
+        return providerURLs;
+
+    }
+
+    private static boolean isValidCategory(URL url) {
+        String category = url.getParameter(CATEGORY_KEY, DEFAULT_CATEGORY);
+        if ((ROUTERS_CATEGORY.equals(category) || 
ROUTE_PROTOCOL.equals(url.getProtocol())) ||
+                PROVIDERS_CATEGORY.equals(category) ||
+                CONFIGURATORS_CATEGORY.equals(category) || 
DYNAMIC_CONFIGURATORS_CATEGORY.equals(category) ||
+                APP_DYNAMIC_CONFIGURATORS_CATEGORY.equals(category)) {
+            return true;
+        }
+        return false;
+    }
+
+    private static boolean isNotCompatibleFor26x(URL url) {
+        return StringUtils.isEmpty(url.getParameter(COMPATIBLE_CONFIG_KEY));
+    }
+}
diff --git a/dubbo-registry/pom.xml b/dubbo-registry/pom.xml
index 962148b..32db5d4 100644
--- a/dubbo-registry/pom.xml
+++ b/dubbo-registry/pom.xml
@@ -37,6 +37,7 @@
         <module>dubbo-registry-consul</module>
         <module>dubbo-registry-etcd3</module>
         <module>dubbo-registry-nacos</module>
+        <module>dubbo-registry-multiple</module>
         <module>dubbo-registry-sofa</module>
     </modules>
 </project>

Reply via email to