Merge pull request #2810, code review and refactor for dubbo-configcenter.

This commit is contained in:
Ian Luo 2018-11-21 17:43:11 +08:00 committed by ken.lj
parent 6140bc2a65
commit 6a1b9ee657
19 changed files with 183 additions and 83 deletions

View File

@ -51,7 +51,7 @@ public class RouterChain<T> {
public static <T> RouterChain<T> buildChain(DynamicConfiguration dynamicConfiguration, URL url) {
RouterChain<T> routerChain = new RouterChain<>(url);
List<RouterFactory> extensionFactories = ExtensionLoader.getExtensionLoader(RouterFactory.class).getActivateExtension(dynamicConfiguration.getUrl(), (String[]) null);
List<RouterFactory> extensionFactories = ExtensionLoader.getExtensionLoader(RouterFactory.class).getActivateExtension(url, (String[]) null);
List<Router> routers = extensionFactories.stream()
.map(factory -> {
Router router = factory.getRouter(dynamicConfiguration, url);

View File

@ -17,7 +17,7 @@
package org.apache.dubbo.common.config;
/**
*
* Configuration interface, to fetch the value for the specified key.
*/
public interface Configuration {
/**
@ -58,6 +58,15 @@ public interface Configuration {
*/
Object getProperty(String key);
/**
* Gets a property from the configuration. The default value will return if the configuration doesn't contain
* the mapping for the specified key.
*
* @param key property to retrieve
* @param defaultValue default value
* @return the value to which this configuration maps the specified key, or default value if the configuration
* contains no mapping for this key.
*/
Object getProperty(String key, Object defaultValue);
/**

View File

@ -302,10 +302,6 @@ public class ExtensionLoader<T> {
return Collections.unmodifiableSet(new TreeSet<String>(cachedInstances.keySet()));
}
public Set<Object> getLoadedExtensionInstances() {
return Collections.unmodifiableSet(cachedInstances.values().stream().map(Holder::get).collect(Collectors.toSet()));
}
public Object getLoadedAdaptiveExtensionInstances() {
return cachedAdaptiveInstance.get();
}
@ -340,6 +336,14 @@ public class ExtensionLoader<T> {
return (T) instance;
}
/**
* Return all available extension instances.
*/
public Set<T> getExtensions() {
return Collections.unmodifiableSet(getSupportedExtensions().stream().map(this::getExtension)
.collect(Collectors.toSet()));
}
/**
* Return default extension, return <code>null</code> if it's not configured.
*/

View File

@ -157,7 +157,7 @@ public abstract class AbstractInterfaceConfig extends AbstractMethodConfig {
// For compatibility purpose, use registry as the default config center if the registry protocol is zookeeper and there's no config center specified explicitly.
RegistryConfig registry = registries.get(0);
if (registry.isZookeeperProtocol()) {
Set<Object> loadedConfigurations = ExtensionLoader.getExtensionLoader(DynamicConfiguration.class).getLoadedExtensionInstances();
Set<DynamicConfiguration> loadedConfigurations = ExtensionLoader.getExtensionLoader(DynamicConfiguration.class).getExtensions();
// we use the loading status of DynamicConfiguration to decide whether ConfigCenter has been initiated.
if (CollectionUtils.isEmpty(loadedConfigurations)) {
ConfigCenterConfig configCenterConfig = new ConfigCenterConfig();

View File

@ -20,7 +20,6 @@ import org.apache.dubbo.common.Constants;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.config.Environment;
import org.apache.dubbo.common.extension.ExtensionLoader;
import org.apache.dubbo.common.utils.CollectionUtils;
import org.apache.dubbo.common.utils.StringUtils;
import org.apache.dubbo.common.utils.UrlUtils;
import org.apache.dubbo.config.support.Parameter;
@ -31,7 +30,6 @@ import java.io.StringReader;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
/**
*
@ -102,16 +100,11 @@ public class ConfigCenterConfig extends AbstractConfig {
// checkConfigCenter();
URL url = toConfigUrl();
Set<Object> loadedConfigurations = ExtensionLoader.getExtensionLoader(DynamicConfiguration.class).getLoadedExtensionInstances();
if (CollectionUtils.isEmpty(loadedConfigurations)) {
DynamicConfiguration dynamicConfiguration = ExtensionLoader.getExtensionLoader(DynamicConfiguration.class).getExtension(url.getProtocol());
// TODO, maybe we need a factory to do this?
dynamicConfiguration.setUrl(url);
dynamicConfiguration.init();
return dynamicConfiguration;
}
return (DynamicConfiguration) loadedConfigurations.iterator().next();
DynamicConfiguration dynamicConfiguration = ExtensionLoader.getExtensionLoader(DynamicConfiguration.class).getExtension(url.getProtocol());
// TODO, maybe we need a factory to do this?
dynamicConfiguration.initWith(url);
return dynamicConfiguration;
}
private URL toConfigUrl() {

View File

@ -25,7 +25,7 @@
<artifactId>dubbo-configcenter-api</artifactId>
<packaging>jar</packaging>
<name>${project.artifactId}</name>
<description>The api definition of the service configcenter module</description>
<description>The api definition of the service config-center module</description>
<properties>
<skip_maven_deploy>false</skip_maven_deploy>
</properties>
@ -37,4 +37,4 @@
<version>${project.parent.version}</version>
</dependency>
</dependencies>
</project>
</project>

View File

@ -23,23 +23,36 @@ import java.util.concurrent.ConcurrentHashMap;
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)
*/
public abstract class AbstractDynamicConfiguration<TargetConfigListener> extends AbstractConfiguration implements DynamicConfiguration {
public static final String DEFAULT_GROUP = "dubbo";
public abstract class AbstractDynamicConfiguration<TargetListener> extends AbstractConfiguration
implements DynamicConfiguration {
protected static final String DEFAULT_GROUP = "dubbo";
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, TargetConfigListener>> listenerToTargetListenerMap = new ConcurrentHashMap<>();
// One key can register multiple target listeners, but one target listener only maps to one configuration listener
private ConcurrentMap<String, ConcurrentMap<ConfigurationListener, TargetListener>> targetListeners =
new ConcurrentHashMap<>();
public AbstractDynamicConfiguration() {
}
@Override
public void initWith(URL url) {
this.url = url;
}
@Override
public void addListener(String key, ConfigurationListener listener) {
ConcurrentMap<ConfigurationListener, TargetConfigListener> listeners = listenerToTargetListenerMap.computeIfAbsent(key, k -> new ConcurrentHashMap<>());
TargetConfigListener targetListener = listeners.computeIfAbsent(listener, k -> createTargetConfigListener(key, listener));
ConcurrentMap<ConfigurationListener, TargetListener> listeners = targetListeners.computeIfAbsent(key,
k -> new ConcurrentHashMap<>());
TargetListener targetListener = listeners.computeIfAbsent(listener,
k -> createTargetListener(key, listener));
addTargetListener(key, targetListener);
}
@ -60,33 +73,48 @@ public abstract class AbstractDynamicConfiguration<TargetConfigListener> extends
@Override
public String getConfig(String key, String group, ConfigurationListener listener) {
return getConfig(key, group, 0l, listener);
return getConfig(key, group, listener, 0L);
}
@Override
public String getConfig(String key, String group, long timeout, ConfigurationListener listener) {
public String getConfig(String key, String group, ConfigurationListener listener, long timeout) {
try {
if (listener != null) {
this.addListener(key, listener);
}
return getInternalProperty(key, group, timeout);
return getTargetConfig(key, group, timeout);
} catch (Exception e) {
throw new IllegalStateException(e.getMessage(), e);
}
}
public URL getUrl() {
return url;
}
/**
* Fetch dynamic configuration from backend config storage. If timeout exceeds, exception should be thrown.
*
* @param key property key
* @param group group
* @param timeout timeout
* @return target config value
*/
protected abstract String getTargetConfig(String key, String group, long timeout);
public void setUrl(URL url) {
this.url = url;
}
/**
* 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
*/
protected abstract void addTargetListener(String key, TargetListener listener);
protected abstract String getInternalProperty(String key, String group, long timeout);
protected abstract void addTargetListener(String key, TargetConfigListener listener);
protected abstract TargetConfigListener createTargetConfigListener(String key, ConfigurationListener listener);
/**
* 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);
}

View File

@ -17,7 +17,9 @@
package org.apache.dubbo.configcenter;
/**
* Config change event.
*
* @see ConfigChangeType
*/
public class ConfigChangeEvent {
private String key;

View File

@ -17,10 +17,21 @@
package org.apache.dubbo.configcenter;
/**
*
* Config change event type
*/
public enum ConfigChangeType {
/**
* A config is created.
*/
ADDED,
/**
* A config is updated.
*/
MODIFIED,
/**
* A config is deleted.
*/
DELETED
}

View File

@ -17,9 +17,16 @@
package org.apache.dubbo.configcenter;
/**
*
* Config type
*/
public enum ConfigType {
/**
* For Dubbo dynamic config other than routing rules.
*/
CONFIGURATORS,
/**
* For Dubbo routing rules
*/
ROUTERS
}

View File

@ -19,11 +19,18 @@ package org.apache.dubbo.configcenter;
import org.apache.dubbo.common.URL;
/**
*
* Config listener, will get notified when the config it listens on changes.
*/
public interface ConfigurationListener {
/**
* Listener call back method. Listener gets notified by this method once there's any change happens on the config
* the listener listens on.
*
* @param event config change event
*/
void process(ConfigChangeEvent event);
// FIXME: why we need this?
URL getUrl();
}

View File

@ -28,7 +28,7 @@ import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
/**
*
* Utilities for manipulating configurations from different sources
*/
public class ConfigurationUtils {
private static final CompositeConfiguration compositeConfiguration;
@ -64,17 +64,16 @@ public class ConfigurationUtils {
}
/**
* If user opens DynamicConfig, the extension instance must has been created during the initialization of ConfigCenterConfig with the right extension type user specified.
* If no DynamicConfig presents, NopDynamicConfiguration will be used.
*
* @return
* If user opens DynamicConfig, the extension instance must has been created during the initialization of
* ConfigCenterConfig with the right extension type user specified. If no DynamicConfig presents,
* NopDynamicConfiguration will be used.
*/
public static DynamicConfiguration getDynamicConfiguration() {
Set<Object> configurations = ExtensionLoader.getExtensionLoader(DynamicConfiguration.class).getLoadedExtensionInstances();
Set<DynamicConfiguration> configurations = ExtensionLoader.getExtensionLoader(DynamicConfiguration.class).getExtensions();
if (CollectionUtils.isEmpty(configurations)) {
return ExtensionLoader.getExtensionLoader(DynamicConfiguration.class).getDefaultExtension();
} else {
return (DynamicConfiguration) configurations.iterator().next();
return configurations.iterator().next();
}
}

View File

@ -20,7 +20,7 @@ import org.apache.dubbo.common.config.AbstractConfiguration;
import org.apache.dubbo.common.config.Configuration;
/**
*
* A wrapper to fetch a config for the specific key with the different prefix in the specified order.
*/
public class ConfigurationWrapper extends AbstractConfiguration {
private String application;
@ -36,6 +36,7 @@ public class ConfigurationWrapper extends AbstractConfiguration {
this.delegate = configuration;
}
// FIXME: I think the order is wrong, service.method.key go first, then service.key, and then application.key
@Override
protected Object getInternalProperty(String key) {
Object value = delegate.getProperty(application + "." + key);

View File

@ -21,27 +21,72 @@ import org.apache.dubbo.common.config.Configuration;
import org.apache.dubbo.common.extension.SPI;
/**
*
* Dynamic configuration
*/
@SPI("nop")
public interface DynamicConfiguration extends Configuration {
void init();
URL getUrl();
void setUrl(URL url);
/**
* Init dynamic configuration from URL
*
* @param url the url in which info for initializing dynamic configuration is contained.
*/
void initWith(URL url);
/**
* Register a configuration listener for a specified key
*
* @param key the key to represent a configuration
* @param listener configuration listener
*/
void addListener(String key, ConfigurationListener listener);
/**
* Get the configuration mapped to the given key
*
* @param key property key
* @return target configuration mapped to the given key
*/
String getConfig(String key);
/**
* Get the configuration mapped to the given key and the given group
*
* @param key property key
* @param group group
* @return target configuration mapped to the given key and the given group
*/
String getConfig(String key, String group);
/**
* Get the configuration mapped to the given key, and notify the passed-in listener
*
* @param key property key
* @param listener configuration listener
* @return
*/
String getConfig(String key, ConfigurationListener listener);
String getConfig(String key, String group, long timeout, ConfigurationListener listener);
/**
* Get the configuration mapped to the given key and the given group, and notify the passed-in listener
*
* @param key property key
* @param group group
* @param listener configuration listener
* @return target configuration mapped to the given key and the given group
*/
String getConfig(String key, String group, ConfigurationListener listener);
/**
* Get the configuration mapped to the given key and the given group, and notify the passed-in listener. If the
* configuration fails to fetch after timeout exceeds, IllegalStateException will be thrown.
*
* @param key property key
* @param group group
* @param listener configuration listener
* @param timeout timeout value for fetching the target config
* @return target configuration mapped to the given key and the given group, IllegalStateException will be thrown
* if timeout exceeds.
*/
String getConfig(String key, String group, ConfigurationListener listener, long timeout);
}

View File

@ -21,28 +21,23 @@ import org.apache.dubbo.configcenter.ConfigurationListener;
import org.apache.dubbo.configcenter.DynamicConfiguration;
/**
* The default extension of {@link DynamicConfiguration}.
* If user does not specify a config centre, or specifies one that is not a valid extension, it will default to this one.
* The default extension of {@link DynamicConfiguration}. If user does not specify a config centre, or specifies one
* that is not a valid extension, it will default to this one.
*/
public class NopDynamicConfiguration extends AbstractDynamicConfiguration {
@Override
public void init() {
}
@Override
protected String getInternalProperty(String key, String group, long timeout) {
protected String getTargetConfig(String key, String group, long timeout) {
return null;
}
@Override
protected void addTargetListener(String key, Object o) {
// no-op
}
@Override
protected Object createTargetConfigListener(String key, ConfigurationListener listener) {
protected Object createTargetListener(String key, ConfigurationListener listener) {
return null;
}

View File

@ -52,7 +52,8 @@ public class ApolloDynamicConfiguration extends AbstractDynamicConfiguration<Con
}
@Override
public void init() {
public void initWith(URL url) {
super.initWith(url);
/**
* Instead of using Dubbo's configuration, I would suggest use the original configuration method Apollo provides.
*/
@ -94,7 +95,7 @@ public class ApolloDynamicConfiguration extends AbstractDynamicConfiguration<Con
* @return
*/
@Override
protected String getInternalProperty(String key, String group, long timeout) {
protected String getTargetConfig(String key, String group, long timeout) {
if (StringUtils.isNotEmpty(group) && !url.getParameter(Constants.CONFIG_GROUP_KEY, DEFAULT_GROUP).equals(group)) {
Config config = ConfigService.getConfig(group);
if (config != null) {
@ -126,7 +127,7 @@ public class ApolloDynamicConfiguration extends AbstractDynamicConfiguration<Con
}
@Override
protected ConfigChangeListener createTargetConfigListener(String key, ConfigurationListener listener) {
protected ConfigChangeListener createTargetListener(String key, ConfigurationListener listener) {
return new ApolloListener(listener);
}

View File

@ -42,7 +42,9 @@ public class ArchaiusDynamicConfiguration extends AbstractDynamicConfiguration<R
}
@Override
public void init() {
public void initWith(URL url) {
super.initWith(url);
// String address = env.getCompositeConf().getString(ADDRESS_KEY);
// String app = env.getCompositeConf().getString(APP_KEY);
@ -80,7 +82,7 @@ public class ArchaiusDynamicConfiguration extends AbstractDynamicConfiguration<R
* @return
*/
@Override
protected String getInternalProperty(String key, String group, long timeout) {
protected String getTargetConfig(String key, String group, long timeout) {
if (StringUtils.isNotEmpty(group)) {
key = group + "." + key;
}
@ -112,7 +114,7 @@ public class ArchaiusDynamicConfiguration extends AbstractDynamicConfiguration<R
}
@Override
protected Runnable createTargetConfigListener(String key, ConfigurationListener listener) {
protected Runnable createTargetListener(String key, ConfigurationListener listener) {
return new ArchaiusListener(key, listener);
}

View File

@ -18,6 +18,7 @@ package org.apache.dubbo.container.log4j;
import org.apache.dubbo.configcenter.ConfigurationUtils;
import org.apache.dubbo.container.Container;
import org.apache.log4j.Appender;
import org.apache.log4j.FileAppender;
import org.apache.log4j.LogManager;

View File

@ -87,12 +87,7 @@ public class RegistryProtocol implements Protocol {
public RegistryProtocol() {
INSTANCE = this;
Set<Object> configurations = ExtensionLoader.getExtensionLoader(DynamicConfiguration.class).getLoadedExtensionInstances();
if (CollectionUtils.isEmpty(configurations)) {
dynamicConfiguration = ExtensionLoader.getExtensionLoader(DynamicConfiguration.class).getDefaultExtension();
} else {
dynamicConfiguration = (DynamicConfiguration) configurations.iterator().next();
}
dynamicConfiguration = ConfigurationUtils.getDynamicConfiguration();
}
public static RegistryProtocol getRegistryProtocol() {