Skip to content

Commit

Permalink
Support concurrent loading of Config for different namespaces (apollo…
Browse files Browse the repository at this point in the history
…config#29)

* Support concurrent loading of Config for different namespaces.

* Supports loading the apollo module through spi
  • Loading branch information
zth9 committed Aug 1, 2023
1 parent 265fe49 commit fd44b08
Show file tree
Hide file tree
Showing 4 changed files with 168 additions and 15 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package com.ctrip.framework.apollo.internals;

import com.ctrip.framework.apollo.Config;
import com.ctrip.framework.apollo.ConfigFile;
import com.ctrip.framework.apollo.core.enums.ConfigFileFormat;
import com.google.common.collect.Maps;

import java.util.Map;

/**
* @author tian
*/
public class ConcurrentConfigManager extends DefaultConfigManager {

private final Map<String, Object> m_configLocks = Maps.newConcurrentMap();
private final Map<String, Object> m_configFileLocks = Maps.newConcurrentMap();

@Override
public Config getConfig(String namespace) {
String lockKey = buildLockKey(namespace);
Object lock = m_configLocks.get(lockKey);
if (lock == null) {
synchronized (lockKey) {
lock = m_configLocks.computeIfAbsent(lockKey, k -> new Object());
}
}
return getConfigInLock(lock, namespace);
}

@Override
public ConfigFile getConfigFile(String namespace, ConfigFileFormat configFileFormat) {
String namespaceFileName = buildNamespaceFileName(namespace, configFileFormat);
String lockKey = buildLockKey(namespaceFileName);
Object lock = m_configFileLocks.get(lockKey);
if (lock == null) {
synchronized (lockKey) {
lock = m_configFileLocks.computeIfAbsent(lockKey, k -> new Object());
}
}
return getConfigFileInLock(lock, namespaceFileName, configFileFormat);
}

private String buildLockKey(String factor) {
return (String.format("%s.%s", getClass().getName(), factor)).intern();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,13 @@ public DefaultConfigManager() {

@Override
public Config getConfig(String namespace) {
Config config = m_configs.get(namespace);
return getConfigInLock(this, namespace);
}

protected Config getConfigInLock(Object lock, String namespace) {
Config config = m_configs.get(namespace);
if (config == null) {
synchronized (this) {
synchronized (lock) {
config = m_configs.get(namespace);

if (config == null) {
Expand All @@ -55,17 +58,21 @@ public Config getConfig(String namespace) {
}
}
}

return config;
}

@Override
public ConfigFile getConfigFile(String namespace, ConfigFileFormat configFileFormat) {
String namespaceFileName = String.format("%s.%s", namespace, configFileFormat.getValue());
String namespaceFileName = buildNamespaceFileName(namespace, configFileFormat);
return getConfigFileInLock(this, namespaceFileName, configFileFormat);
}

protected ConfigFile getConfigFileInLock(Object lock, String namespaceFileName,
ConfigFileFormat configFileFormat) {
ConfigFile configFile = m_configFiles.get(namespaceFileName);

if (configFile == null) {
synchronized (this) {
synchronized (lock) {
configFile = m_configFiles.get(namespaceFileName);

if (configFile == null) {
Expand All @@ -79,4 +86,8 @@ public ConfigFile getConfigFile(String namespace, ConfigFileFormat configFileFor

return configFile;
}

protected String buildNamespaceFileName(String namespace, ConfigFileFormat configFileFormat) {
return String.format("%s.%s", namespace, configFileFormat.getValue());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Singleton;
import java.lang.annotation.Annotation;
import java.util.List;

/**
Expand Down Expand Up @@ -95,16 +96,39 @@ public <T> T getInstance(Class<T> clazz, String name) {
private static class ApolloModule extends AbstractModule {
@Override
protected void configure() {
bind(ConfigManager.class).to(DefaultConfigManager.class).in(Singleton.class);
bind(ConfigFactoryManager.class).to(DefaultConfigFactoryManager.class).in(Singleton.class);
bind(ConfigRegistry.class).to(DefaultConfigRegistry.class).in(Singleton.class);
bind(ConfigFactory.class).to(DefaultConfigFactory.class).in(Singleton.class);
bind(ConfigUtil.class).in(Singleton.class);
bind(HttpClient.class).to(DefaultHttpClient.class).in(Singleton.class);
bind(ConfigServiceLocator.class).in(Singleton.class);
bind(RemoteConfigLongPollService.class).in(Singleton.class);
bind(YamlParser.class).in(Singleton.class);
bind(PropertiesFactory.class).to(DefaultPropertiesFactory.class).in(Singleton.class);
bind(ConfigManager.class, DefaultConfigManager.class, Singleton.class);
bind(ConfigFactoryManager.class, DefaultConfigFactoryManager.class, Singleton.class);
bind(ConfigRegistry.class, DefaultConfigRegistry.class, Singleton.class);
bind(ConfigFactory.class, DefaultConfigFactory.class, Singleton.class);
bind(ConfigUtil.class, Singleton.class);
bind(HttpClient.class, DefaultHttpClient.class, Singleton.class);
bind(ConfigServiceLocator.class, Singleton.class);
bind(RemoteConfigLongPollService.class, Singleton.class);
bind(YamlParser.class, Singleton.class);
bind(PropertiesFactory.class, DefaultPropertiesFactory.class, Singleton.class);
}

/**
* bind the type's implementation from spi
* defaultImpl will be bound if the implementation cannot be found
*/
private <S> void bind(Class<S> bindClass, Class<? extends S> defaultImpl, Class<? extends Annotation> in) {
List<Class<?>> classList = ServiceBootstrap.loadClass(bindClass);
Class<? extends S> impl = defaultImpl;
if (!classList.isEmpty()) {
Class<?> spiClass = classList.get(0);
if (bindClass.isAssignableFrom(spiClass)) {
impl = (Class<? extends S>) spiClass;
}
}
bind(bindClass).to(impl).in(in);
}

/**
* bind the determining type
*/
private <T> void bind(Class<T> bindClass, Class<? extends Annotation> in) {
bind(bindClass).in(in);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,28 @@

import com.ctrip.framework.apollo.core.spi.Ordered;
import com.google.common.collect.Lists;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.ServiceLoader;
import java.util.Set;

public class ServiceBootstrap {

private static final String PREFIX = "META-INF/services/";

private static final Set<String> loadedUrls = new HashSet<>();

/**
* @deprecated use {@link ServiceBootstrap#loadPrimary(Class)} instead
*/
Expand Down Expand Up @@ -66,4 +80,62 @@ public static <S extends Ordered> S loadPrimary(Class<S> clazz) {

return candidates.get(0);
}

public static <T> List<Class<?>> loadClass(Class<T> clazz) {
return loadClass(clazz, Thread.currentThread().getContextClassLoader());
}

/**
* load spi class without instancing
*/
public static <T> List<Class<?>> loadClass(Class<T> clazz, ClassLoader classLoader) {
if (classLoader == null) {
return Collections.emptyList();
}
List<Class<?>> serviceClassList = new ArrayList<>();
String className = clazz.getName();
String path = PREFIX + className;
Set<String> serviceNames = new HashSet<>();
try {
Enumeration<URL> urls = classLoader.getResources(path);
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
if (loadedUrls.contains(url.toString())) {
continue;
}
load(url, serviceNames);
loadedUrls.add(url.toString());
}
} catch (Throwable ignored) {
}
for (String serviceName : serviceNames) {
try {
Class<?> serviceClass = classLoader.loadClass(serviceName);
serviceClassList.add(serviceClass);
} catch (Exception ignored) {
}
}
return serviceClassList;
}

private static void load(URL url, Set<String> set) throws IOException {
try (InputStream is = url.openStream(); BufferedReader reader = new BufferedReader(
new InputStreamReader(is, StandardCharsets.UTF_8))) {
while (true) {
String line = reader.readLine();
if (line == null) {
break;
}
int ci = line.indexOf('#');
if (ci >= 0) {
line = line.substring(0, ci);
}
line = line.trim();
if (line.isEmpty()) {
continue;
}
set.add(line);
}
}
}
}

0 comments on commit fd44b08

Please sign in to comment.