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 2018/11/27 11:09:12 UTC
[incubator-dubbo] 03/06: try different zookeeper implementations
This is an automated email from the ASF dual-hosted git repository.
liujun pushed a commit to branch dev-metadata
in repository https://gitbox.apache.org/repos/asf/incubator-dubbo.git
commit 6f618226c179f76cf921f4792df6878320917ef1
Author: ken.lj <ke...@gmail.com>
AuthorDate: Tue Nov 27 17:29:09 2018 +0800
try different zookeeper implementations
---
.../configcenter/AbstractDynamicConfiguration.java | 31 +++--
.../dubbo/configcenter/ConfigChangeEvent.java | 16 +--
.../dubbo/configcenter/DynamicConfiguration.java | 2 +
.../support/nop/NopDynamicConfiguration.java | 9 +-
.../mock/MockDynamicConfiguration.java | 23 ++++
....apache.dubbo.configcenter.DynamicConfiguration | 1 +
.../support/apollo/ApolloDynamicConfiguration.java | 48 ++++---
.../pom.xml | 4 +-
.../zookeeper}/ArchaiusDynamicConfiguration.java | 60 ++++-----
.../sources/ZooKeeperConfigurationSource.java | 4 +-
....apache.dubbo.configcenter.DynamicConfiguration | 1 +
.../pom.xml | 16 +--
.../support/zookeeper/CacheListener.java | 107 +++++++++++++++
.../zookeeper/ZookeeperDynamicConfiguration.java | 144 ++++++++++++++++++++
....apache.dubbo.configcenter.DynamicConfiguration | 1 +
.../ZookeeperDynamicConfigurationTest.java | 150 +++++++++++++++++++++
.../dubbo-configcenter-zookeeper/pom.xml | 30 +++--
....apache.dubbo.configcenter.DynamicConfiguration | 3 +-
.../ZookeeperDynamicConfigurationTest.java | 150 +++++++++++++++++++++
dubbo-configcenter/pom.xml | 6 +-
20 files changed, 699 insertions(+), 107 deletions(-)
diff --git a/dubbo-configcenter/dubbo-configcenter-api/src/main/java/org/apache/dubbo/configcenter/AbstractDynamicConfiguration.java b/dubbo-configcenter/dubbo-configcenter-api/src/main/java/org/apache/dubbo/configcenter/AbstractDynamicConfiguration.java
index 258ce52..6fcf8c3 100644
--- a/dubbo-configcenter/dubbo-configcenter-api/src/main/java/org/apache/dubbo/configcenter/AbstractDynamicConfiguration.java
+++ b/dubbo-configcenter/dubbo-configcenter-api/src/main/java/org/apache/dubbo/configcenter/AbstractDynamicConfiguration.java
@@ -26,8 +26,8 @@ import java.util.concurrent.ConcurrentMap;
* Dynamic configuration template class. The concrete implementation needs to provide implementation for three methods.
*
* @see AbstractDynamicConfiguration#getTargetConfig(String, String, long)
- * @see AbstractDynamicConfiguration#addListener(String, ConfigurationListener)
- * @see AbstractDynamicConfiguration#createTargetListener(String, ConfigurationListener)
+ * @see AbstractDynamicConfiguration#addConfigurationListener(TargetListener, ConfigurationListener)
+ * @see AbstractDynamicConfiguration#createTargetListener(String)
*/
public abstract class AbstractDynamicConfiguration<TargetListener> extends AbstractConfiguration
implements DynamicConfiguration {
@@ -36,7 +36,7 @@ public abstract class AbstractDynamicConfiguration<TargetListener> extends Abstr
protected URL url;
// One key can register multiple target listeners, but one target listener only maps to one configuration listener
- private ConcurrentMap<String, ConcurrentMap<ConfigurationListener, TargetListener>> targetListeners =
+ protected ConcurrentMap<String, TargetListener> targetListeners =
new ConcurrentHashMap<>();
public AbstractDynamicConfiguration() {
@@ -49,11 +49,8 @@ public abstract class AbstractDynamicConfiguration<TargetListener> extends Abstr
@Override
public void addListener(String key, ConfigurationListener listener) {
- ConcurrentMap<ConfigurationListener, TargetListener> listeners = targetListeners.computeIfAbsent(key,
- k -> new ConcurrentHashMap<>());
- TargetListener targetListener = listeners.computeIfAbsent(listener,
- k -> createTargetListener(key, listener));
- addTargetListener(key, targetListener);
+ TargetListener targetListener = targetListeners.computeIfAbsent(key, this::createTargetListener);
+ addConfigurationListener(key, targetListener, listener);
}
@Override
@@ -88,6 +85,13 @@ public abstract class AbstractDynamicConfiguration<TargetListener> extends Abstr
}
}
+ @Override
+ public void removeListener(String key) {
+
+ }
+
+ protected abstract void recover();
+
/**
* Fetch dynamic configuration from backend config storage. If timeout exceeds, exception should be thrown.
*
@@ -101,20 +105,19 @@ public abstract class AbstractDynamicConfiguration<TargetListener> extends Abstr
/**
* Register a native listener to the backend config storage so that Dubbo has chance to get notified when the
* value changes.
- *
- * @param key property key listener is interested.
- * @param listener native listener for the backend config storage
+ * @param key
+ * @param targetListener Implementation dependent listener, such as, zookeeper watcher, Apollo listener, ...
+ * @param configurationListener Listener in Dubbo that will handle notification.
*/
- protected abstract void addTargetListener(String key, TargetListener listener);
+ protected abstract void addConfigurationListener(String key, TargetListener targetListener, ConfigurationListener configurationListener);
/**
* Create a native listener for the backend config storage, eventually ConfigurationListener will get notified once
* the value changes.
*
* @param key property key the native listener will listen on
- * @param listener ConfigurationListener instance
* @return native listener for the backend config storage
*/
- protected abstract TargetListener createTargetListener(String key, ConfigurationListener listener);
+ protected abstract TargetListener createTargetListener(String key);
}
diff --git a/dubbo-configcenter/dubbo-configcenter-api/src/main/java/org/apache/dubbo/configcenter/ConfigChangeEvent.java b/dubbo-configcenter/dubbo-configcenter-api/src/main/java/org/apache/dubbo/configcenter/ConfigChangeEvent.java
index 403ec75..068f21d 100644
--- a/dubbo-configcenter/dubbo-configcenter-api/src/main/java/org/apache/dubbo/configcenter/ConfigChangeEvent.java
+++ b/dubbo-configcenter/dubbo-configcenter-api/src/main/java/org/apache/dubbo/configcenter/ConfigChangeEvent.java
@@ -25,16 +25,14 @@ public class ConfigChangeEvent {
private String key;
private String newValue;
private ConfigChangeType changeType;
- private ConfigType type;
- public ConfigChangeEvent(String key, String value, ConfigType type) {
- this(key, value, type, ConfigChangeType.MODIFIED);
+ public ConfigChangeEvent(String key, String value) {
+ this(key, value, ConfigChangeType.MODIFIED);
}
- public ConfigChangeEvent(String key, String value, ConfigType type, ConfigChangeType changeType) {
+ public ConfigChangeEvent(String key, String value, ConfigChangeType changeType) {
this.key = key;
this.newValue = value;
- this.type = type;
this.changeType = changeType;
}
@@ -61,12 +59,4 @@ public class ConfigChangeEvent {
public void setChangeType(ConfigChangeType changeType) {
this.changeType = changeType;
}
-
- public ConfigType getType() {
- return type;
- }
-
- public void setType(ConfigType type) {
- this.type = type;
- }
}
diff --git a/dubbo-configcenter/dubbo-configcenter-api/src/main/java/org/apache/dubbo/configcenter/DynamicConfiguration.java b/dubbo-configcenter/dubbo-configcenter-api/src/main/java/org/apache/dubbo/configcenter/DynamicConfiguration.java
index 83faee6..83ff936 100644
--- a/dubbo-configcenter/dubbo-configcenter-api/src/main/java/org/apache/dubbo/configcenter/DynamicConfiguration.java
+++ b/dubbo-configcenter/dubbo-configcenter-api/src/main/java/org/apache/dubbo/configcenter/DynamicConfiguration.java
@@ -41,6 +41,8 @@ public interface DynamicConfiguration extends Configuration {
*/
void addListener(String key, ConfigurationListener listener);
+ void removeListener(String key);
+
/**
* Get the configuration mapped to the given key
*
diff --git a/dubbo-configcenter/dubbo-configcenter-api/src/main/java/org/apache/dubbo/configcenter/support/nop/NopDynamicConfiguration.java b/dubbo-configcenter/dubbo-configcenter-api/src/main/java/org/apache/dubbo/configcenter/support/nop/NopDynamicConfiguration.java
index 1dc0794..1250233 100644
--- a/dubbo-configcenter/dubbo-configcenter-api/src/main/java/org/apache/dubbo/configcenter/support/nop/NopDynamicConfiguration.java
+++ b/dubbo-configcenter/dubbo-configcenter-api/src/main/java/org/apache/dubbo/configcenter/support/nop/NopDynamicConfiguration.java
@@ -32,12 +32,12 @@ public class NopDynamicConfiguration extends AbstractDynamicConfiguration {
}
@Override
- protected void addTargetListener(String key, Object o) {
+ protected void addConfigurationListener(String key, Object targetListener, ConfigurationListener configurationListener) {
// no-op
}
@Override
- protected Object createTargetListener(String key, ConfigurationListener listener) {
+ protected Object createTargetListener(String key) {
return null;
}
@@ -45,4 +45,9 @@ public class NopDynamicConfiguration extends AbstractDynamicConfiguration {
protected Object getInternalProperty(String key) {
return null;
}
+
+ @Override
+ protected void recover() {
+
+ }
}
diff --git a/dubbo-configcenter/dubbo-configcenter-api/src/test/java/org/apache/dubbo/configcenter/mock/MockDynamicConfiguration.java b/dubbo-configcenter/dubbo-configcenter-api/src/test/java/org/apache/dubbo/configcenter/mock/MockDynamicConfiguration.java
new file mode 100644
index 0000000..012322d
--- /dev/null
+++ b/dubbo-configcenter/dubbo-configcenter-api/src/test/java/org/apache/dubbo/configcenter/mock/MockDynamicConfiguration.java
@@ -0,0 +1,23 @@
+/*
+ * 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.configcenter.mock;
+
+/**
+ *
+ */
+public class MockDynamicConfiguration {
+}
diff --git a/dubbo-configcenter/dubbo-configcenter-api/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.configcenter.DynamicConfiguration b/dubbo-configcenter/dubbo-configcenter-api/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.configcenter.DynamicConfiguration
new file mode 100644
index 0000000..944a089
--- /dev/null
+++ b/dubbo-configcenter/dubbo-configcenter-api/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.configcenter.DynamicConfiguration
@@ -0,0 +1 @@
+mock=org.apache.dubbo.configcenter.support.mock.MockDynamicConfiguration
\ No newline at end of file
diff --git a/dubbo-configcenter/dubbo-configcenter-apollo/src/main/java/org/apache/dubbo/configcenter/support/apollo/ApolloDynamicConfiguration.java b/dubbo-configcenter/dubbo-configcenter-apollo/src/main/java/org/apache/dubbo/configcenter/support/apollo/ApolloDynamicConfiguration.java
index 8c056dc..0c0d95c 100644
--- a/dubbo-configcenter/dubbo-configcenter-apollo/src/main/java/org/apache/dubbo/configcenter/support/apollo/ApolloDynamicConfiguration.java
+++ b/dubbo-configcenter/dubbo-configcenter-apollo/src/main/java/org/apache/dubbo/configcenter/support/apollo/ApolloDynamicConfiguration.java
@@ -22,7 +22,6 @@ import com.ctrip.framework.apollo.ConfigService;
import com.ctrip.framework.apollo.enums.ConfigSourceType;
import com.ctrip.framework.apollo.enums.PropertyChangeType;
import com.ctrip.framework.apollo.model.ConfigChange;
-
import org.apache.dubbo.common.Constants;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.logger.Logger;
@@ -33,15 +32,13 @@ import org.apache.dubbo.configcenter.ConfigChangeEvent;
import org.apache.dubbo.configcenter.ConfigChangeType;
import org.apache.dubbo.configcenter.ConfigurationListener;
-import java.util.Collections;
-
-import static org.apache.dubbo.configcenter.ConfigType.CONFIGURATORS;
-import static org.apache.dubbo.configcenter.ConfigType.ROUTERS;
+import java.util.Set;
+import java.util.concurrent.CopyOnWriteArraySet;
/**
* Apollo implementation, https://github.com/ctripcorp/apollo
*/
-public class ApolloDynamicConfiguration extends AbstractDynamicConfiguration<ConfigChangeListener> {
+public class ApolloDynamicConfiguration extends AbstractDynamicConfiguration<ApolloDynamicConfiguration.ApolloListener> {
private static final Logger logger = LoggerFactory.getLogger(ApolloDynamicConfiguration.class);
private static final String APOLLO_ENV_KEY = "env";
private static final String APOLLO_ADDR_KEY = "apollo.meta";
@@ -104,7 +101,7 @@ public class ApolloDynamicConfiguration extends AbstractDynamicConfiguration<Con
}
/**
- * This method will used by Configuration to get valid value at runtime.
+ * This method will be used by Configuration to get valid value at runtime.
* The group is expected to be 'app level', which can be fetched from the 'config.appnamespace' in url if necessary.
* But I think Apollo's inheritance feature of namespace can solve the problem .
*/
@@ -114,21 +111,31 @@ public class ApolloDynamicConfiguration extends AbstractDynamicConfiguration<Con
}
@Override
- protected void addTargetListener(String key, ConfigChangeListener listener) {
- this.dubboConfig.addChangeListener(listener, Collections.singleton(key));
+ protected void addConfigurationListener(String key, ApolloListener listener, ConfigurationListener configurationListener) {
+ listener.addListener(configurationListener);
+ }
+
+ @Override
+ protected ApolloListener createTargetListener(String key) {
+ ApolloListener listener = new ApolloListener();
+ this.dubboConfig.addChangeListener(listener);
+ return listener;
}
@Override
- protected ConfigChangeListener createTargetListener(String key, ConfigurationListener listener) {
- return new ApolloListener(listener);
+ protected void recover() {
+ // Apollo will handle things well.
}
- private class ApolloListener implements ConfigChangeListener {
+ public class ApolloListener implements ConfigChangeListener {
- private ConfigurationListener listener;
+ private Set<ConfigurationListener> listeners = new CopyOnWriteArraySet<>();
- ApolloListener(ConfigurationListener listener) {
- this.listener = listener;
+ ApolloListener() {
+ }
+
+ public void addListener(ConfigurationListener configurationListener) {
+ this.listeners.add(configurationListener);
}
@Override
@@ -140,13 +147,10 @@ public class ApolloDynamicConfiguration extends AbstractDynamicConfiguration<Con
change.getOldValue() + ", the empty rule will not take effect.");
return;
}
- // TODO Maybe we no longer need to identify the type of change. Because there's no scenario that
- // a callback will subscribe for both configurators and routers
- if (change.getPropertyName().endsWith(Constants.CONFIGURATORS_SUFFIX)) {
- listener.process(new ConfigChangeEvent(key, change.getNewValue(), CONFIGURATORS, getChangeType(change)));
- } else {
- listener.process(new ConfigChangeEvent(key, change.getNewValue(), ROUTERS, getChangeType(change)));
- }
+
+ listeners.forEach(
+ listener -> listener.process(new ConfigChangeEvent(key, change.getNewValue(), getChangeType(change)))
+ );
}
}
diff --git a/dubbo-configcenter/dubbo-configcenter-zookeeper/pom.xml b/dubbo-configcenter/dubbo-configcenter-archaius/pom.xml
similarity index 94%
copy from dubbo-configcenter/dubbo-configcenter-zookeeper/pom.xml
copy to dubbo-configcenter/dubbo-configcenter-archaius/pom.xml
index 8e3d7fe..a122d72 100644
--- a/dubbo-configcenter/dubbo-configcenter-zookeeper/pom.xml
+++ b/dubbo-configcenter/dubbo-configcenter-archaius/pom.xml
@@ -22,10 +22,10 @@
<artifactId>dubbo-configcenter</artifactId>
<version>2.7.0-SNAPSHOT</version>
</parent>
- <artifactId>dubbo-configcenter-zookeeper</artifactId>
+ <artifactId>dubbo-configcenter-archaius</artifactId>
<packaging>jar</packaging>
<name>${project.artifactId}</name>
- <description>The zookeeper implementation of the configcenter api</description>
+ <description>The archaius implementation of the config-center api</description>
<dependencies>
<dependency>
diff --git a/dubbo-configcenter/dubbo-configcenter-zookeeper/src/main/java/org/apache/dubbo/configcenter/support/archaius/ArchaiusDynamicConfiguration.java b/dubbo-configcenter/dubbo-configcenter-archaius/src/main/java/org/apache/dubbo/configcenter/support/zookeeper/ArchaiusDynamicConfiguration.java
similarity index 74%
rename from dubbo-configcenter/dubbo-configcenter-zookeeper/src/main/java/org/apache/dubbo/configcenter/support/archaius/ArchaiusDynamicConfiguration.java
rename to dubbo-configcenter/dubbo-configcenter-archaius/src/main/java/org/apache/dubbo/configcenter/support/zookeeper/ArchaiusDynamicConfiguration.java
index d739009..e0c64e3 100644
--- a/dubbo-configcenter/dubbo-configcenter-zookeeper/src/main/java/org/apache/dubbo/configcenter/support/archaius/ArchaiusDynamicConfiguration.java
+++ b/dubbo-configcenter/dubbo-configcenter-archaius/src/main/java/org/apache/dubbo/configcenter/support/zookeeper/ArchaiusDynamicConfiguration.java
@@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.dubbo.configcenter.support.archaius;
+package org.apache.dubbo.configcenter.support.zookeeper;
import com.netflix.config.ConfigurationManager;
import com.netflix.config.DynamicPropertyFactory;
@@ -24,26 +24,29 @@ import org.apache.dubbo.common.Constants;
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.ConcurrentHashSet;
import org.apache.dubbo.common.utils.StringUtils;
import org.apache.dubbo.configcenter.AbstractDynamicConfiguration;
import org.apache.dubbo.configcenter.ConfigChangeEvent;
import org.apache.dubbo.configcenter.ConfigChangeType;
import org.apache.dubbo.configcenter.ConfigType;
import org.apache.dubbo.configcenter.ConfigurationListener;
-import org.apache.dubbo.configcenter.support.archaius.sources.ZooKeeperConfigurationSource;
+import org.apache.dubbo.configcenter.support.zookeeper.sources.ZooKeeperConfigurationSource;
+
+import java.util.Set;
import static org.apache.dubbo.common.Constants.CONFIG_NAMESPACE_KEY;
-import static org.apache.dubbo.configcenter.support.archaius.sources.ZooKeeperConfigurationSource.ARCHAIUS_CONFIG_CHECK_KEY;
-import static org.apache.dubbo.configcenter.support.archaius.sources.ZooKeeperConfigurationSource.ARCHAIUS_CONFIG_ROOT_PATH_KEY;
-import static org.apache.dubbo.configcenter.support.archaius.sources.ZooKeeperConfigurationSource.ARCHAIUS_SOURCE_ADDRESS_KEY;
-import static org.apache.dubbo.configcenter.support.archaius.sources.ZooKeeperConfigurationSource.DEFAULT_CONFIG_ROOT_PATH;
+import static org.apache.dubbo.configcenter.support.zookeeper.sources.ZooKeeperConfigurationSource.ARCHAIUS_CONFIG_CHECK_KEY;
+import static org.apache.dubbo.configcenter.support.zookeeper.sources.ZooKeeperConfigurationSource.ARCHAIUS_CONFIG_ROOT_PATH_KEY;
+import static org.apache.dubbo.configcenter.support.zookeeper.sources.ZooKeeperConfigurationSource.ARCHAIUS_SOURCE_ADDRESS_KEY;
+import static org.apache.dubbo.configcenter.support.zookeeper.sources.ZooKeeperConfigurationSource.DEFAULT_CONFIG_ROOT_PATH;
/**
* Archaius supports various sources and it's extensiable: JDBC, ZK, Properties, ..., so should we make it extensiable?
* FIXME: we should get rid of Archaius or move it to eco system since Archaius is out of maintenance, instead, we
* should rely on curator-x-async which looks quite promising.
*/
-public class ArchaiusDynamicConfiguration extends AbstractDynamicConfiguration<Runnable> {
+public class ArchaiusDynamicConfiguration extends AbstractDynamicConfiguration<ArchaiusDynamicConfiguration.ArchaiusListener> {
private static final Logger logger = LoggerFactory.getLogger(ArchaiusDynamicConfiguration.class);
public ArchaiusDynamicConfiguration() {
@@ -107,37 +110,35 @@ public class ArchaiusDynamicConfiguration extends AbstractDynamicConfiguration<R
}
@Override
- protected void addTargetListener(String key, Runnable runnable) {
+ protected void addConfigurationListener(String key, ArchaiusListener targetListener, ConfigurationListener configurationListener) {
+ targetListener.addListener(configurationListener);
+ }
+
+ @Override
+ protected ArchaiusListener createTargetListener(String key) {
+ ArchaiusListener archaiusListener = new ArchaiusListener(key);
DynamicStringProperty prop = DynamicPropertyFactory.getInstance()
.getStringProperty(key, null);
- prop.addCallback(runnable);
+ prop.addCallback(archaiusListener);
+ return archaiusListener;
}
@Override
- protected Runnable createTargetListener(String key, ConfigurationListener listener) {
- return new ArchaiusListener(key, listener);
+ protected void recover() {
+ // FIXME will Archaius recover automatically?
}
- private class ArchaiusListener implements Runnable {
- private ConfigurationListener listener;
+ public class ArchaiusListener implements Runnable {
+ private Set<ConfigurationListener> listeners = new ConcurrentHashSet<>();
private String key;
private ConfigType type;
- public ArchaiusListener(String key, ConfigurationListener listener) {
+ public ArchaiusListener(String key) {
this.key = key;
- this.listener = listener;
- // Maybe we no longer need to identify the type of change. Because there's no scenario that a callback
- // will subscribe for both configurators and routers
- if (key.endsWith(Constants.CONFIGURATORS_SUFFIX)) {
- type = ConfigType.CONFIGURATORS;
- } else {
- /**
- * used for all router rules:
- * {@link Constants.ROUTERS_SUFFIX}
- * {@link org.apache.dubbo.rpc.cluster.router.tag.TagRouter.TAGRULE_DATAID}
- */
- type = ConfigType.ROUTERS;
- }
+ }
+
+ public void addListener(ConfigurationListener listener) {
+ this.listeners.add(listener);
}
@Override
@@ -145,10 +146,9 @@ public class ArchaiusDynamicConfiguration extends AbstractDynamicConfiguration<R
DynamicStringProperty prop = DynamicPropertyFactory.getInstance()
.getStringProperty(key, null);
String newValue = prop.get();
- ConfigChangeEvent event = new ConfigChangeEvent(key, newValue, type);
+ ConfigChangeEvent event = new ConfigChangeEvent(key, newValue);
if (newValue == null) {
event.setChangeType(ConfigChangeType.DELETED);
- listener.process(event);
} else {
if (newValue.equals("")) {
logger.warn("an empty rule is received for " + key + ", the current working rule is unknown, " +
@@ -156,8 +156,8 @@ public class ArchaiusDynamicConfiguration extends AbstractDynamicConfiguration<R
return;
}
event.setChangeType(ConfigChangeType.MODIFIED);
- listener.process(event);
}
+ listeners.forEach(listener -> listener.process(event));
}
}
}
diff --git a/dubbo-configcenter/dubbo-configcenter-zookeeper/src/main/java/org/apache/dubbo/configcenter/support/archaius/sources/ZooKeeperConfigurationSource.java b/dubbo-configcenter/dubbo-configcenter-archaius/src/main/java/org/apache/dubbo/configcenter/support/zookeeper/sources/ZooKeeperConfigurationSource.java
similarity index 98%
rename from dubbo-configcenter/dubbo-configcenter-zookeeper/src/main/java/org/apache/dubbo/configcenter/support/archaius/sources/ZooKeeperConfigurationSource.java
rename to dubbo-configcenter/dubbo-configcenter-archaius/src/main/java/org/apache/dubbo/configcenter/support/zookeeper/sources/ZooKeeperConfigurationSource.java
index c7d5082..e77e434 100644
--- a/dubbo-configcenter/dubbo-configcenter-zookeeper/src/main/java/org/apache/dubbo/configcenter/support/archaius/sources/ZooKeeperConfigurationSource.java
+++ b/dubbo-configcenter/dubbo-configcenter-archaius/src/main/java/org/apache/dubbo/configcenter/support/zookeeper/sources/ZooKeeperConfigurationSource.java
@@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.dubbo.configcenter.support.archaius.sources;
+package org.apache.dubbo.configcenter.support.zookeeper.sources;
import com.google.common.io.Closeables;
import com.netflix.config.WatchedConfigurationSource;
@@ -185,7 +185,7 @@ public class ZooKeeperConfigurationSource implements WatchedConfigurationSource,
}
}, executor);
- // passing true to trigger an initial rebuild upon starting. (blocking call)
+ // it's not blocking, so we use an extra latch 'initializedLatch' to make sure cache fully initialized before use.
treeCache.start();
}
diff --git a/dubbo-configcenter/dubbo-configcenter-archaius/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.configcenter.DynamicConfiguration b/dubbo-configcenter/dubbo-configcenter-archaius/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.configcenter.DynamicConfiguration
new file mode 100644
index 0000000..d271f16
--- /dev/null
+++ b/dubbo-configcenter/dubbo-configcenter-archaius/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.configcenter.DynamicConfiguration
@@ -0,0 +1 @@
+archaius=org.apache.dubbo.configcenter.support.archaius.ArchaiusDynamicConfiguration
\ No newline at end of file
diff --git a/dubbo-configcenter/dubbo-configcenter-zookeeper/pom.xml b/dubbo-configcenter/dubbo-configcenter-zookeeper-cache/pom.xml
similarity index 81%
copy from dubbo-configcenter/dubbo-configcenter-zookeeper/pom.xml
copy to dubbo-configcenter/dubbo-configcenter-zookeeper-cache/pom.xml
index 8e3d7fe..191b4ae 100644
--- a/dubbo-configcenter/dubbo-configcenter-zookeeper/pom.xml
+++ b/dubbo-configcenter/dubbo-configcenter-zookeeper-cache/pom.xml
@@ -22,10 +22,10 @@
<artifactId>dubbo-configcenter</artifactId>
<version>2.7.0-SNAPSHOT</version>
</parent>
- <artifactId>dubbo-configcenter-zookeeper</artifactId>
+ <artifactId>dubbo-configcenter-zookeeper-cache</artifactId>
<packaging>jar</packaging>
<name>${project.artifactId}</name>
- <description>The zookeeper implementation of the configcenter api</description>
+ <description>The zookeeper implementation of the config-center api</description>
<dependencies>
<dependency>
@@ -34,20 +34,20 @@
<version>${project.parent.version}</version>
</dependency>
<dependency>
- <groupId>com.netflix.archaius</groupId>
- <artifactId>archaius-core</artifactId>
- </dependency>
- <dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
+ <version>2.12.0</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
+ <version>2.12.0</version>
</dependency>
<dependency>
- <groupId>commons-configuration</groupId>
- <artifactId>commons-configuration</artifactId>
+ <groupId>org.apache.curator</groupId>
+ <artifactId>curator-test</artifactId>
+ <version>2.12.0</version>
+ <scope>test</scope>
</dependency>
</dependencies>
</project>
\ No newline at end of file
diff --git a/dubbo-configcenter/dubbo-configcenter-zookeeper-cache/src/main/java/org/apache/dubbo/configcenter/support/zookeeper/CacheListener.java b/dubbo-configcenter/dubbo-configcenter-zookeeper-cache/src/main/java/org/apache/dubbo/configcenter/support/zookeeper/CacheListener.java
new file mode 100644
index 0000000..bbfa057
--- /dev/null
+++ b/dubbo-configcenter/dubbo-configcenter-zookeeper-cache/src/main/java/org/apache/dubbo/configcenter/support/zookeeper/CacheListener.java
@@ -0,0 +1,107 @@
+/*
+ * 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.configcenter.support.zookeeper;
+
+import org.apache.curator.framework.CuratorFramework;
+import org.apache.curator.framework.recipes.cache.ChildData;
+import org.apache.curator.framework.recipes.cache.TreeCacheEvent;
+import org.apache.curator.framework.recipes.cache.TreeCacheListener;
+import org.apache.dubbo.common.config.Configuration;
+import org.apache.dubbo.common.utils.StringUtils;
+import org.apache.dubbo.configcenter.ConfigChangeEvent;
+import org.apache.dubbo.configcenter.ConfigChangeType;
+import org.apache.dubbo.configcenter.ConfigurationListener;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CopyOnWriteArraySet;
+import java.util.concurrent.CountDownLatch;
+
+/**
+ *
+ */
+public class CacheListener implements TreeCacheListener {
+ private Map<String, Set<ConfigurationListener>> listeners = new ConcurrentHashMap<>();
+ private CountDownLatch initializedLatch;
+ private String rootPath;
+
+ public CacheListener(String rootPath, CountDownLatch initializedLatch) {
+ this.rootPath = rootPath;
+ this.initializedLatch = initializedLatch;
+ }
+
+ @Override
+ public void childEvent(CuratorFramework aClient, TreeCacheEvent event) throws Exception {
+
+ TreeCacheEvent.Type type = event.getType();
+ ChildData data = event.getData();
+ if (type == TreeCacheEvent.Type.INITIALIZED) {
+ initializedLatch.countDown();
+ return;
+ }
+
+ // TODO, ignore other event types
+ if (data == null) {
+ return;
+ }
+
+ // TODO We limit the notification of config changes to a specific path level, for example
+ // /dubbo/config/service/configurators, other config changes not in this level will not get notified,
+ // say /dubbo/config/dubbo.properties
+ if (data.getPath().split("/").length == 5) {
+ byte[] value = data.getData();
+ String key = pathToKey(data.getPath());
+ ConfigChangeType changeType;
+ switch (type) {
+ case NODE_ADDED:
+ changeType = ConfigChangeType.ADDED;
+ break;
+ case NODE_REMOVED:
+ changeType = ConfigChangeType.DELETED;
+ break;
+ case NODE_UPDATED:
+ changeType = ConfigChangeType.MODIFIED;
+ break;
+ default:
+ return;
+ }
+
+ ConfigChangeEvent configChangeEvent = new ConfigChangeEvent(key, new String(value, StandardCharsets.UTF_8), changeType);
+ listeners.get(key).forEach(listener -> listener.process(configChangeEvent));
+ }
+ }
+
+ public void addListener(String key, ConfigurationListener configurationListener) {
+ Set<ConfigurationListener> set = this.listeners.computeIfAbsent(key, k -> new CopyOnWriteArraySet<>());
+ set.add(configurationListener);
+ }
+
+ /**
+ * This is used to convert a configuration nodePath into a key
+ *
+ * @param path
+ * @return key (nodePath less the config root path)
+ */
+ private String pathToKey(String path) {
+ if (StringUtils.isEmpty(path)) {
+ return path;
+ }
+ return path.replace(rootPath + "/", "").replaceAll("/", ".");
+ }
+}
diff --git a/dubbo-configcenter/dubbo-configcenter-zookeeper-cache/src/main/java/org/apache/dubbo/configcenter/support/zookeeper/ZookeeperDynamicConfiguration.java b/dubbo-configcenter/dubbo-configcenter-zookeeper-cache/src/main/java/org/apache/dubbo/configcenter/support/zookeeper/ZookeeperDynamicConfiguration.java
new file mode 100644
index 0000000..6e78079
--- /dev/null
+++ b/dubbo-configcenter/dubbo-configcenter-zookeeper-cache/src/main/java/org/apache/dubbo/configcenter/support/zookeeper/ZookeeperDynamicConfiguration.java
@@ -0,0 +1,144 @@
+/*
+ * 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.configcenter.support.zookeeper;
+
+import org.apache.curator.RetryPolicy;
+import org.apache.curator.framework.CuratorFramework;
+import org.apache.curator.framework.recipes.cache.ChildData;
+import org.apache.curator.framework.recipes.cache.TreeCache;
+import org.apache.curator.retry.ExponentialBackoffRetry;
+import org.apache.dubbo.common.Constants;
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.utils.StringUtils;
+import org.apache.dubbo.configcenter.AbstractDynamicConfiguration;
+import org.apache.dubbo.configcenter.ConfigurationListener;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.nio.charset.StandardCharsets;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+
+import static org.apache.curator.framework.CuratorFrameworkFactory.newClient;
+import static org.apache.dubbo.common.Constants.CONFIG_NAMESPACE_KEY;
+
+/**
+ *
+ */
+public class ZookeeperDynamicConfiguration extends AbstractDynamicConfiguration<CacheListener> {
+ private static final Logger logger = LoggerFactory.getLogger(ZookeeperDynamicConfiguration.class);
+ private Executor executor = Executors.newFixedThreadPool(1);
+ private CuratorFramework client;
+
+ // The final root path would be: /configRootPath/"config"
+ private String rootPath;
+ private TreeCache treeCache;
+ private CountDownLatch initializedLatch = new CountDownLatch(1);
+
+ private CacheListener cacheListener;
+
+ @Override
+ public void initWith(URL url) {
+ super.initWith(url);
+
+ rootPath = "/" + url.getParameter(CONFIG_NAMESPACE_KEY, Constants.DUBBO) + "/config";
+
+ RetryPolicy policy = new ExponentialBackoffRetry(1000, 3);
+ int sessionTimeout = 60 * 1000;
+ int connectTimeout = 10 * 1000;
+ String connectString = url.getBackupAddress();
+ client = newClient(connectString, sessionTimeout, connectTimeout, policy);
+ client.start();
+
+ try {
+ boolean connected = client.blockUntilConnected(3 * connectTimeout, TimeUnit.MILLISECONDS);
+ if (!connected) {
+ if (url.getParameter(Constants.CONFIG_CHECK_KEY, false)) {
+ throw new IllegalStateException("Failed to connect to config center (zookeeper): "
+ + connectString + " in " + 3 * connectTimeout + "ms.");
+ } else {
+ logger.warn("The config center (zookeeper) is not fully initialized in " + 3 * connectTimeout + "ms, address is: " + connectString);
+ }
+ }
+ } catch (InterruptedException e) {
+ throw new IllegalStateException("The thread was interrupted unexpectedly when trying connecting to zookeeper "
+ + connectString + " config center, ", e);
+ }
+
+ this.cacheListener = new CacheListener(rootPath, initializedLatch);
+
+ // build local cache
+ try {
+ this.buildCache();
+ } catch (Exception e) {
+ logger.warn("Failed to build local cache for config center (zookeeper), address is ." + connectString);
+ }
+ }
+
+ @Override
+ protected String getTargetConfig(String key, String group, long timeout) {
+ if (StringUtils.isNotEmpty(group)) {
+ key = group + "." + key;
+ }
+
+ return (String) getInternalProperty(key);
+ }
+
+ @Override
+ protected void addConfigurationListener(String key, CacheListener cacheListener, ConfigurationListener listener) {
+ cacheListener.addListener(key, listener);
+ }
+
+ @Override
+ protected CacheListener createTargetListener(String key) {
+ return cacheListener;
+ }
+
+ /**
+ *
+ * @param key e.g., {service}.configurators, {service}.tagrouters, {group}.dubbo.properties
+ * @return
+ */
+ @Override
+ protected Object getInternalProperty(String key) {
+ ChildData childData = treeCache.getCurrentData(rootPath + "/" + key.replaceFirst("\\.", "/"));
+ if (childData != null) {
+ return new String(childData.getData(), StandardCharsets.UTF_8);
+ }
+ return null;
+ }
+
+ protected void recover() {
+
+ }
+
+ /**
+ * Adds a listener to the pathChildrenCache, initializes the cache, then starts the cache-management background
+ * thread
+ */
+ private void buildCache() throws Exception {
+ this.treeCache = new TreeCache(client, rootPath);
+ // create the watcher for future configuration updates
+ treeCache.getListenable().addListener(cacheListener, executor);
+
+ // it's not blocking, so we use an extra latch 'initializedLatch' to make sure cache fully initialized before use.
+ treeCache.start();
+ initializedLatch.await();
+ }
+}
diff --git a/dubbo-configcenter/dubbo-configcenter-zookeeper-cache/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.configcenter.DynamicConfiguration b/dubbo-configcenter/dubbo-configcenter-zookeeper-cache/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.configcenter.DynamicConfiguration
new file mode 100644
index 0000000..0379c0e
--- /dev/null
+++ b/dubbo-configcenter/dubbo-configcenter-zookeeper-cache/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.configcenter.DynamicConfiguration
@@ -0,0 +1 @@
+zookeeper=org.apache.dubbo.configcenter.support.zookeeper.ZookeeperDynamicConfiguration
\ No newline at end of file
diff --git a/dubbo-configcenter/dubbo-configcenter-zookeeper-cache/src/test/java/org/apache/dubbo/configcenter/support/zookeeper/ZookeeperDynamicConfigurationTest.java b/dubbo-configcenter/dubbo-configcenter-zookeeper-cache/src/test/java/org/apache/dubbo/configcenter/support/zookeeper/ZookeeperDynamicConfigurationTest.java
new file mode 100644
index 0000000..382e77d
--- /dev/null
+++ b/dubbo-configcenter/dubbo-configcenter-zookeeper-cache/src/test/java/org/apache/dubbo/configcenter/support/zookeeper/ZookeeperDynamicConfigurationTest.java
@@ -0,0 +1,150 @@
+/*
+ * 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.configcenter.support.zookeeper;
+
+import org.apache.curator.framework.CuratorFramework;
+import org.apache.curator.framework.CuratorFrameworkFactory;
+import org.apache.curator.retry.ExponentialBackoffRetry;
+import org.apache.curator.test.TestingServer;
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.extension.ExtensionLoader;
+import org.apache.dubbo.common.utils.NetUtils;
+import org.apache.dubbo.configcenter.ConfigChangeEvent;
+import org.apache.dubbo.configcenter.ConfigurationListener;
+import org.apache.dubbo.configcenter.DynamicConfiguration;
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.CountDownLatch;
+
+/**
+ *
+ */
+public class ZookeeperDynamicConfigurationTest {
+ private static CuratorFramework client;
+
+ private static URL configUrl;
+ private static int zkServerPort = NetUtils.getAvailablePort();
+ private static TestingServer zkServer;
+ private static DynamicConfiguration configuration;
+
+ @BeforeClass
+ public static void setUp() throws Exception {
+ zkServer = new TestingServer(zkServerPort, true);
+
+ client = CuratorFrameworkFactory.newClient("localhost:" + zkServerPort, 60 * 1000, 60 * 1000,
+ new ExponentialBackoffRetry(1000, 3));
+ client.start();
+
+ try {
+ setData("/dubbo/config/dubbo/dubbo.properties", "The content from dubbo.properties");
+ setData("/dubbo/config/group*service:version/configurators", "The content from configurators");
+ setData("/dubbo/config/appname", "The content from higer level node");
+ setData("/dubbo/config/appname/tagrouters", "The content from appname tagrouters");
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+
+ configUrl = URL.valueOf("zookeeper://localhost:" + zkServerPort);
+
+ configuration = ExtensionLoader.getExtensionLoader(DynamicConfiguration.class).getExtension(configUrl.getProtocol());
+ configuration.initWith(configUrl);
+ }
+
+ @AfterClass
+ public static void tearDown() throws Exception {
+ zkServer.stop();
+ }
+
+ @Test
+ public void testGetConfig() throws Exception {
+ Assert.assertEquals("The content from dubbo.properties", configuration.getConfig("dubbo.dubbo.properties"));
+ Assert.assertEquals("The content from dubbo.properties", configuration.getConfig("dubbo.properties", "dubbo"));
+ }
+
+ @Test
+ public void testAddListener() throws Exception {
+ CountDownLatch latch = new CountDownLatch(4);
+ TestListener listener1 = new TestListener(latch);
+ TestListener listener2 = new TestListener(latch);
+ TestListener listener3 = new TestListener(latch);
+ TestListener listener4 = new TestListener(latch);
+ configuration.addListener("group*service:version.configurators", listener1);
+ configuration.addListener("group*service:version.configurators", listener2);
+ configuration.addListener("appname.tagrouters", listener3);
+ configuration.addListener("appname.tagrouters", listener4);
+
+ setData("/dubbo/config/group*service:version/configurators", "new value1");
+ Thread.sleep(100);
+ setData("/dubbo/config/appname/tagrouters", "new value2");
+ Thread.sleep(100);
+ setData("/dubbo/config/appname", "new value3");
+
+ Thread.sleep(5000);
+
+ latch.await();
+ Assert.assertEquals(1, listener1.getCount("group*service:version.configurators"));
+ Assert.assertEquals(1, listener2.getCount("group*service:version.configurators"));
+ Assert.assertEquals(1, listener3.getCount("appname.tagrouters"));
+ Assert.assertEquals(1, listener4.getCount("appname.tagrouters"));
+
+ Assert.assertEquals("new value1", listener1.getValue());
+ Assert.assertEquals("new value1", listener2.getValue());
+ Assert.assertEquals("new value2", listener3.getValue());
+ Assert.assertEquals("new value2", listener4.getValue());
+ }
+
+ private static void setData(String path, String data) throws Exception {
+ if (client.checkExists().forPath(path) == null) {
+ client.create().creatingParentsIfNeeded().forPath(path);
+ }
+ client.setData().forPath(path, data.getBytes());
+ }
+
+ private class TestListener implements ConfigurationListener {
+ private CountDownLatch latch;
+ private String value;
+ private Map<String, Integer> countMap = new HashMap<>();
+
+ public TestListener(CountDownLatch latch) {
+ this.latch = latch;
+ }
+
+ @Override
+ public void process(ConfigChangeEvent event) {
+ Integer count = countMap.computeIfAbsent(event.getKey(), k -> new Integer(0));
+ countMap.put(event.getKey(), ++count);
+
+ value = event.getNewValue();
+ latch.countDown();
+ }
+
+ public int getCount(String key) {
+ return countMap.get(key);
+ }
+
+ public String getValue() {
+ return value;
+ }
+ }
+
+}
diff --git a/dubbo-configcenter/dubbo-configcenter-zookeeper/pom.xml b/dubbo-configcenter/dubbo-configcenter-zookeeper/pom.xml
index 8e3d7fe..717f5ef 100644
--- a/dubbo-configcenter/dubbo-configcenter-zookeeper/pom.xml
+++ b/dubbo-configcenter/dubbo-configcenter-zookeeper/pom.xml
@@ -25,7 +25,7 @@
<artifactId>dubbo-configcenter-zookeeper</artifactId>
<packaging>jar</packaging>
<name>${project.artifactId}</name>
- <description>The zookeeper implementation of the configcenter api</description>
+ <description>The zookeeper implementation of the config-center api</description>
<dependencies>
<dependency>
@@ -33,21 +33,31 @@
<artifactId>dubbo-configcenter-api</artifactId>
<version>${project.parent.version}</version>
</dependency>
- <dependency>
- <groupId>com.netflix.archaius</groupId>
- <artifactId>archaius-core</artifactId>
- </dependency>
+ <!--<dependency>-->
+ <!--<groupId>org.apache.curator</groupId>-->
+ <!--<artifactId>curator-framework</artifactId>-->
+ <!--</dependency>-->
<dependency>
<groupId>org.apache.curator</groupId>
- <artifactId>curator-framework</artifactId>
+ <artifactId>curator-recipes</artifactId>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
- <artifactId>curator-recipes</artifactId>
+ <artifactId>curator-x-async</artifactId>
+ <version>4.0.1</version>
+ <exclusions>
+ <exclusion>
+ <groupId>org.apache.zookeeper</groupId>
+ <artifactId>zookeeper</artifactId>
+ </exclusion>
+ </exclusions>
</dependency>
+
<dependency>
- <groupId>commons-configuration</groupId>
- <artifactId>commons-configuration</artifactId>
+ <groupId>org.apache.curator</groupId>
+ <artifactId>curator-test</artifactId>
+ <version>2.12.0</version>
+ <scope>test</scope>
</dependency>
</dependencies>
-</project>
\ No newline at end of file
+</project>
diff --git a/dubbo-configcenter/dubbo-configcenter-zookeeper/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.configcenter.DynamicConfiguration b/dubbo-configcenter/dubbo-configcenter-zookeeper/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.configcenter.DynamicConfiguration
index b948be5..0379c0e 100644
--- a/dubbo-configcenter/dubbo-configcenter-zookeeper/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.configcenter.DynamicConfiguration
+++ b/dubbo-configcenter/dubbo-configcenter-zookeeper/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.configcenter.DynamicConfiguration
@@ -1,2 +1 @@
-archaius=org.apache.dubbo.configcenter.support.archaius.ArchaiusDynamicConfiguration
-zookeeper=org.apache.dubbo.configcenter.support.archaius.ArchaiusDynamicConfiguration
\ No newline at end of file
+zookeeper=org.apache.dubbo.configcenter.support.zookeeper.ZookeeperDynamicConfiguration
\ No newline at end of file
diff --git a/dubbo-configcenter/dubbo-configcenter-zookeeper/src/test/java/org/apache/dubbo/configcenter/support/zookeeper/ZookeeperDynamicConfigurationTest.java b/dubbo-configcenter/dubbo-configcenter-zookeeper/src/test/java/org/apache/dubbo/configcenter/support/zookeeper/ZookeeperDynamicConfigurationTest.java
new file mode 100644
index 0000000..382e77d
--- /dev/null
+++ b/dubbo-configcenter/dubbo-configcenter-zookeeper/src/test/java/org/apache/dubbo/configcenter/support/zookeeper/ZookeeperDynamicConfigurationTest.java
@@ -0,0 +1,150 @@
+/*
+ * 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.configcenter.support.zookeeper;
+
+import org.apache.curator.framework.CuratorFramework;
+import org.apache.curator.framework.CuratorFrameworkFactory;
+import org.apache.curator.retry.ExponentialBackoffRetry;
+import org.apache.curator.test.TestingServer;
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.extension.ExtensionLoader;
+import org.apache.dubbo.common.utils.NetUtils;
+import org.apache.dubbo.configcenter.ConfigChangeEvent;
+import org.apache.dubbo.configcenter.ConfigurationListener;
+import org.apache.dubbo.configcenter.DynamicConfiguration;
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.CountDownLatch;
+
+/**
+ *
+ */
+public class ZookeeperDynamicConfigurationTest {
+ private static CuratorFramework client;
+
+ private static URL configUrl;
+ private static int zkServerPort = NetUtils.getAvailablePort();
+ private static TestingServer zkServer;
+ private static DynamicConfiguration configuration;
+
+ @BeforeClass
+ public static void setUp() throws Exception {
+ zkServer = new TestingServer(zkServerPort, true);
+
+ client = CuratorFrameworkFactory.newClient("localhost:" + zkServerPort, 60 * 1000, 60 * 1000,
+ new ExponentialBackoffRetry(1000, 3));
+ client.start();
+
+ try {
+ setData("/dubbo/config/dubbo/dubbo.properties", "The content from dubbo.properties");
+ setData("/dubbo/config/group*service:version/configurators", "The content from configurators");
+ setData("/dubbo/config/appname", "The content from higer level node");
+ setData("/dubbo/config/appname/tagrouters", "The content from appname tagrouters");
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+
+ configUrl = URL.valueOf("zookeeper://localhost:" + zkServerPort);
+
+ configuration = ExtensionLoader.getExtensionLoader(DynamicConfiguration.class).getExtension(configUrl.getProtocol());
+ configuration.initWith(configUrl);
+ }
+
+ @AfterClass
+ public static void tearDown() throws Exception {
+ zkServer.stop();
+ }
+
+ @Test
+ public void testGetConfig() throws Exception {
+ Assert.assertEquals("The content from dubbo.properties", configuration.getConfig("dubbo.dubbo.properties"));
+ Assert.assertEquals("The content from dubbo.properties", configuration.getConfig("dubbo.properties", "dubbo"));
+ }
+
+ @Test
+ public void testAddListener() throws Exception {
+ CountDownLatch latch = new CountDownLatch(4);
+ TestListener listener1 = new TestListener(latch);
+ TestListener listener2 = new TestListener(latch);
+ TestListener listener3 = new TestListener(latch);
+ TestListener listener4 = new TestListener(latch);
+ configuration.addListener("group*service:version.configurators", listener1);
+ configuration.addListener("group*service:version.configurators", listener2);
+ configuration.addListener("appname.tagrouters", listener3);
+ configuration.addListener("appname.tagrouters", listener4);
+
+ setData("/dubbo/config/group*service:version/configurators", "new value1");
+ Thread.sleep(100);
+ setData("/dubbo/config/appname/tagrouters", "new value2");
+ Thread.sleep(100);
+ setData("/dubbo/config/appname", "new value3");
+
+ Thread.sleep(5000);
+
+ latch.await();
+ Assert.assertEquals(1, listener1.getCount("group*service:version.configurators"));
+ Assert.assertEquals(1, listener2.getCount("group*service:version.configurators"));
+ Assert.assertEquals(1, listener3.getCount("appname.tagrouters"));
+ Assert.assertEquals(1, listener4.getCount("appname.tagrouters"));
+
+ Assert.assertEquals("new value1", listener1.getValue());
+ Assert.assertEquals("new value1", listener2.getValue());
+ Assert.assertEquals("new value2", listener3.getValue());
+ Assert.assertEquals("new value2", listener4.getValue());
+ }
+
+ private static void setData(String path, String data) throws Exception {
+ if (client.checkExists().forPath(path) == null) {
+ client.create().creatingParentsIfNeeded().forPath(path);
+ }
+ client.setData().forPath(path, data.getBytes());
+ }
+
+ private class TestListener implements ConfigurationListener {
+ private CountDownLatch latch;
+ private String value;
+ private Map<String, Integer> countMap = new HashMap<>();
+
+ public TestListener(CountDownLatch latch) {
+ this.latch = latch;
+ }
+
+ @Override
+ public void process(ConfigChangeEvent event) {
+ Integer count = countMap.computeIfAbsent(event.getKey(), k -> new Integer(0));
+ countMap.put(event.getKey(), ++count);
+
+ value = event.getNewValue();
+ latch.countDown();
+ }
+
+ public int getCount(String key) {
+ return countMap.get(key);
+ }
+
+ public String getValue() {
+ return value;
+ }
+ }
+
+}
diff --git a/dubbo-configcenter/pom.xml b/dubbo-configcenter/pom.xml
index fa1be8e..4d7dd84 100644
--- a/dubbo-configcenter/pom.xml
+++ b/dubbo-configcenter/pom.xml
@@ -25,14 +25,16 @@
<artifactId>dubbo-configcenter</artifactId>
<packaging>pom</packaging>
<name>${project.artifactId}</name>
- <description>The service configcenter module of the Dubbo project</description>
+ <description>The service config-center module of the Dubbo project</description>
<properties>
<skip_maven_deploy>false</skip_maven_deploy>
</properties>
<modules>
<module>dubbo-configcenter-api</module>
- <module>dubbo-configcenter-apollo</module>
<module>dubbo-configcenter-zookeeper</module>
+ <!--<module>dubbo-configcenter-zookeeper-cache</module>-->
+ <module>dubbo-configcenter-apollo</module>
+ <module>dubbo-configcenter-archaius</module>
</modules>
</project>
\ No newline at end of file