提交 58351732 编写于 作者: 张乐 提交者: GitHub

Merge pull request #531 from nobodyiam/optimize-client-parse

Optimize client parse performance
......@@ -4,7 +4,7 @@
<parent>
<groupId>com.ctrip.framework.apollo</groupId>
<artifactId>apollo</artifactId>
<version>0.4.1</version>
<version>0.5.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
......
......@@ -4,7 +4,7 @@
<parent>
<groupId>com.ctrip.framework.apollo</groupId>
<artifactId>apollo</artifactId>
<version>0.4.1</version>
<version>0.5.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
......
......@@ -4,7 +4,7 @@
<parent>
<artifactId>apollo</artifactId>
<groupId>com.ctrip.framework.apollo</groupId>
<version>0.4.1</version>
<version>0.5.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>apollo-biz</artifactId>
......
......@@ -4,7 +4,7 @@
<parent>
<groupId>com.ctrip.framework.apollo</groupId>
<artifactId>apollo</artifactId>
<version>0.4.1</version>
<version>0.5.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
......
......@@ -4,7 +4,7 @@
<parent>
<groupId>com.ctrip.framework.apollo</groupId>
<artifactId>apollo</artifactId>
<version>0.4.1</version>
<version>0.5.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
......
......@@ -21,8 +21,8 @@ public class ConfigService {
private static final ConfigService s_instance = new ConfigService();
private PlexusContainer m_container;
private ConfigManager m_configManager;
private ConfigRegistry m_configRegistry;
private volatile ConfigManager m_configManager;
private volatile ConfigRegistry m_configRegistry;
private ConfigService() {
m_container = ContainerLoader.getDefaultContainer();
......@@ -128,8 +128,10 @@ public class ConfigService {
// for test only
static void setContainer(PlexusContainer m_container) {
s_instance.m_container = m_container;
s_instance.m_configManager = null;
s_instance.m_configRegistry = null;
synchronized (s_instance) {
s_instance.m_container = m_container;
s_instance.m_configManager = null;
s_instance.m_configRegistry = null;
}
}
}
package com.ctrip.framework.apollo.internals;
import com.google.common.base.Function;
import com.google.common.base.Objects;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.ctrip.framework.apollo.Config;
......@@ -13,37 +17,63 @@ import com.ctrip.framework.apollo.model.ConfigChange;
import com.ctrip.framework.apollo.model.ConfigChangeEvent;
import com.ctrip.framework.apollo.tracer.Tracer;
import com.ctrip.framework.apollo.tracer.spi.Transaction;
import com.ctrip.framework.apollo.util.ConfigUtil;
import com.ctrip.framework.apollo.util.function.Functions;
import com.ctrip.framework.apollo.util.parser.Parsers;
import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.unidal.lookup.ContainerLoader;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicLong;
/**
* @author Jason Song(song_s@ctrip.com)
*/
public abstract class AbstractConfig implements Config {
private static final Logger logger = LoggerFactory.getLogger(AbstractConfig.class);
private static final String LONG_DATE_FORMAT = "yyyy-MM-dd HH:mm:ss.SSS";
private static final String MEDIUM_DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";
private static final String SHORT_DATE_FORMAT = "yyyy-MM-dd";
private static final String[] DATE_FORMATS = {LONG_DATE_FORMAT, MEDIUM_DATE_FORMAT,
SHORT_DATE_FORMAT};
private static ExecutorService m_executorService;
private List<ConfigChangeListener> m_listeners = Lists.newCopyOnWriteArrayList();
private ConfigUtil m_configUtil;
private volatile Cache<String, Integer> m_integerCache;
private volatile Cache<String, Long> m_longCache;
private volatile Cache<String, Short> m_shortCache;
private volatile Cache<String, Float> m_floatCache;
private volatile Cache<String, Double> m_doubleCache;
private volatile Cache<String, Byte> m_byteCache;
private volatile Cache<String, Boolean> m_booleanCache;
private volatile Cache<String, Date> m_dateCache;
private volatile Cache<String, Long> m_durationCache;
private Map<String, Cache<String, String[]>> m_arrayCache;
private List<Cache> allCaches;
private AtomicLong m_configVersion; //indicate config version
static {
m_executorService = Executors.newCachedThreadPool(ApolloThreadFactory
.create("Config", true));
}
public AbstractConfig() {
try {
m_configUtil = ContainerLoader.getDefaultContainer().lookup(ConfigUtil.class);
m_configVersion = new AtomicLong();
m_arrayCache = Maps.newConcurrentMap();
allCaches = Lists.newArrayList();
} catch (ComponentLookupException ex) {
Tracer.logError(ex);
throw new ApolloConfigException("Unable to load component!", ex);
}
}
@Override
......@@ -56,11 +86,15 @@ public abstract class AbstractConfig implements Config {
@Override
public Integer getIntProperty(String key, Integer defaultValue) {
try {
String value = getProperty(key, null);
if (value != null) {
return Integer.parseInt(value);
if (m_integerCache == null) {
synchronized (this) {
if (m_integerCache == null) {
m_integerCache = newCache();
}
}
}
return getValueFromCache(key, Functions.TO_INT_FUNCTION, m_integerCache, defaultValue);
} catch (Throwable ex) {
Tracer.logError(new ApolloConfigException(
String.format("getIntProperty for %s failed, return default value %d", key,
......@@ -72,11 +106,15 @@ public abstract class AbstractConfig implements Config {
@Override
public Long getLongProperty(String key, Long defaultValue) {
try {
String value = getProperty(key, null);
if (value != null) {
return Long.parseLong(value);
if (m_longCache == null) {
synchronized (this) {
if (m_longCache == null) {
m_longCache = newCache();
}
}
}
return getValueFromCache(key, Functions.TO_LONG_FUNCTION, m_longCache, defaultValue);
} catch (Throwable ex) {
Tracer.logError(new ApolloConfigException(
String.format("getLongProperty for %s failed, return default value %d", key,
......@@ -88,11 +126,15 @@ public abstract class AbstractConfig implements Config {
@Override
public Short getShortProperty(String key, Short defaultValue) {
try {
String value = getProperty(key, null);
if (value != null) {
return Short.parseShort(value);
if (m_shortCache == null) {
synchronized (this) {
if (m_shortCache == null) {
m_shortCache = newCache();
}
}
}
return getValueFromCache(key, Functions.TO_SHORT_FUNCTION, m_shortCache, defaultValue);
} catch (Throwable ex) {
Tracer.logError(new ApolloConfigException(
String.format("getShortProperty for %s failed, return default value %d", key,
......@@ -104,11 +146,15 @@ public abstract class AbstractConfig implements Config {
@Override
public Float getFloatProperty(String key, Float defaultValue) {
try {
String value = getProperty(key, null);
if (value != null) {
return Float.parseFloat(value);
if (m_floatCache == null) {
synchronized (this) {
if (m_floatCache == null) {
m_floatCache = newCache();
}
}
}
return getValueFromCache(key, Functions.TO_FLOAT_FUNCTION, m_floatCache, defaultValue);
} catch (Throwable ex) {
Tracer.logError(new ApolloConfigException(
String.format("getFloatProperty for %s failed, return default value %f", key,
......@@ -120,11 +166,15 @@ public abstract class AbstractConfig implements Config {
@Override
public Double getDoubleProperty(String key, Double defaultValue) {
try {
String value = getProperty(key, null);
if (value != null) {
return Double.parseDouble(value);
if (m_doubleCache == null) {
synchronized (this) {
if (m_doubleCache == null) {
m_doubleCache = newCache();
}
}
}
return getValueFromCache(key, Functions.TO_DOUBLE_FUNCTION, m_doubleCache, defaultValue);
} catch (Throwable ex) {
Tracer.logError(new ApolloConfigException(
String.format("getDoubleProperty for %s failed, return default value %f", key,
......@@ -136,11 +186,15 @@ public abstract class AbstractConfig implements Config {
@Override
public Byte getByteProperty(String key, Byte defaultValue) {
try {
String value = getProperty(key, null);
if (value != null) {
return Byte.parseByte(value);
if (m_byteCache == null) {
synchronized (this) {
if (m_byteCache == null) {
m_byteCache = newCache();
}
}
}
return getValueFromCache(key, Functions.TO_BYTE_FUNCTION, m_byteCache, defaultValue);
} catch (Throwable ex) {
Tracer.logError(new ApolloConfigException(
String.format("getByteProperty for %s failed, return default value %d", key,
......@@ -152,11 +206,15 @@ public abstract class AbstractConfig implements Config {
@Override
public Boolean getBooleanProperty(String key, Boolean defaultValue) {
try {
String value = getProperty(key, null);
if (value != null) {
return Boolean.parseBoolean(value);
if (m_booleanCache == null) {
synchronized (this) {
if (m_booleanCache == null) {
m_booleanCache = newCache();
}
}
}
return getValueFromCache(key, Functions.TO_BOOLEAN_FUNCTION, m_booleanCache, defaultValue);
} catch (Throwable ex) {
Tracer.logError(new ApolloConfigException(
String.format("getBooleanProperty for %s failed, return default value %b", key,
......@@ -166,13 +224,29 @@ public abstract class AbstractConfig implements Config {
}
@Override
public String[] getArrayProperty(String key, String delimiter, String[] defaultValue) {
public String[] getArrayProperty(String key, final String delimiter, String[] defaultValue) {
try {
String value = getProperty(key, null);
if (!m_arrayCache.containsKey(delimiter)) {
synchronized (this) {
if (!m_arrayCache.containsKey(delimiter)) {
m_arrayCache.put(delimiter, this.<String[]>newCache());
}
}
}
if (value != null) {
return value.split(delimiter);
Cache<String, String[]> cache = m_arrayCache.get(delimiter);
String[] result = cache.getIfPresent(key);
if (result != null) {
return result;
}
return getValueAndStoreToCache(key, new Function<String, String[]>() {
@Override
public String[] apply(String input) {
return input.split(delimiter);
}
}, cache, defaultValue);
} catch (Throwable ex) {
Tracer.logError(new ApolloConfigException(
String.format("getArrayProperty for %s failed, return default value", key), ex));
......@@ -200,11 +274,15 @@ public abstract class AbstractConfig implements Config {
@Override
public Date getDateProperty(String key, Date defaultValue) {
try {
String value = getProperty(key, null);
if (value != null) {
return Parsers.forDate().parse(value);
if (m_dateCache == null) {
synchronized (this) {
if (m_dateCache == null) {
m_dateCache = newCache();
}
}
}
return getValueFromCache(key, Functions.TO_DATE_FUNCTION, m_dateCache, defaultValue);
} catch (Throwable ex) {
Tracer.logError(new ApolloConfigException(
String.format("getDateProperty for %s failed, return default value %s", key,
......@@ -251,11 +329,15 @@ public abstract class AbstractConfig implements Config {
@Override
public long getDurationProperty(String key, long defaultValue) {
try {
String value = getProperty(key, null);
if (value != null) {
return Parsers.forDuration().parseToMillis(value);
if (m_durationCache == null) {
synchronized (this) {
if (m_durationCache == null) {
m_durationCache = newCache();
}
}
}
return getValueFromCache(key, Functions.TO_DURATION_FUNCTION, m_durationCache, defaultValue);
} catch (Throwable ex) {
Tracer.logError(new ApolloConfigException(
String.format("getDurationProperty for %s failed, return default value %d", key,
......@@ -265,6 +347,59 @@ public abstract class AbstractConfig implements Config {
return defaultValue;
}
private <T> T getValueFromCache(String key, Function<String, T> parser, Cache<String, T> cache, T defaultValue) {
T result = cache.getIfPresent(key);
if (result != null) {
return result;
}
return getValueAndStoreToCache(key, parser, cache, defaultValue);
}
private <T> T getValueAndStoreToCache(String key, Function<String, T> parser, Cache<String, T> cache, T defaultValue) {
long currentConfigVersion = m_configVersion.get();
String value = getProperty(key, null);
if (value != null) {
T result = parser.apply(value);
if (result != null) {
synchronized (this) {
if (m_configVersion.get() == currentConfigVersion) {
cache.put(key, result);
}
}
return result;
}
}
return defaultValue;
}
private <T> Cache<String, T> newCache() {
Cache<String, T> cache = CacheBuilder.newBuilder()
.maximumSize(m_configUtil.getMaxConfigCacheSize())
.expireAfterAccess(m_configUtil.getConfigCacheExpireTime(), m_configUtil.getConfigCacheExpireTimeUnit())
.build();
allCaches.add(cache);
return cache;
}
/**
* Clear config cache
*/
protected void clearConfigCache() {
synchronized (this) {
for (Cache c : allCaches) {
if (c != null) {
c.invalidateAll();
}
}
m_configVersion.incrementAndGet();
}
}
protected void fireConfigChange(final ConfigChangeEvent changeEvent) {
for (final ConfigChangeListener listener : m_listeners) {
m_executorService.submit(new Runnable() {
......
......@@ -131,7 +131,7 @@ public class ConfigServiceLocator implements Initializable {
}
try {
TimeUnit.SECONDS.sleep(1);
m_configUtil.getOnErrorRetryIntervalTimeUnit().sleep(m_configUtil.getOnErrorRetryInterval());
} catch (InterruptedException ex) {
//ignore
}
......
......@@ -86,7 +86,8 @@ public class DefaultConfig extends AbstractConfig implements RepositoryChangeLis
}
if (value == null && m_configProperties.get() == null) {
logger.warn("Could not load config for namespace {} from Apollo, please check whether the configs are released in Apollo! Return default value now!", m_namespace);
logger.warn("Could not load config for namespace {} from Apollo, please check whether the configs are released " +
"in Apollo! Return default value now!", m_namespace);
}
return value == null ? defaultValue : value;
......@@ -138,6 +139,7 @@ public class DefaultConfig extends AbstractConfig implements RepositoryChangeLis
//2. update m_configProperties
m_configProperties.set(newConfigProperties);
clearConfigCache();
//3. use getProperty to update configChange's new value and calc the final changes
for (ConfigChange change : configChanges) {
......
......@@ -228,7 +228,7 @@ public class RemoteConfigRepository extends AbstractConfigRepository {
}
try {
TimeUnit.SECONDS.sleep(1);
m_configUtil.getOnErrorRetryIntervalTimeUnit().sleep(m_configUtil.getOnErrorRetryInterval());
} catch (InterruptedException ex) {
//ignore
}
......
......@@ -90,6 +90,7 @@ public class SimpleConfig extends AbstractConfig implements RepositoryChangeList
});
m_configProperties = newConfigProperties;
clearConfigCache();
this.fireConfigChange(new ConfigChangeEvent(m_namespace, changeMap));
......
......@@ -29,6 +29,13 @@ public class ConfigUtil {
private String cluster;
private int loadConfigQPS = 2; //2 times per second
private int longPollQPS = 2; //2 times per second
//for on error retry
private long onErrorRetryInterval = 1;//1 second
private TimeUnit onErrorRetryIntervalTimeUnit = TimeUnit.SECONDS;//1 second
//for typed config cache of parser result, e.g. integer, double, long, etc.
private long maxConfigCacheSize = 500;//500 cache key
private long configCacheExpireTime = 1;//1 minute
private TimeUnit configCacheExpireTimeUnit = TimeUnit.MINUTES;//1 minute
public ConfigUtil() {
initRefreshInterval();
......@@ -36,6 +43,7 @@ public class ConfigUtil {
initReadTimeout();
initCluster();
initQPS();
initMaxConfigCacheSize();
}
/**
......@@ -47,7 +55,8 @@ public class ConfigUtil {
String appId = Foundation.app().getAppId();
if (Strings.isNullOrEmpty(appId)) {
appId = ConfigConsts.NO_APPID_PLACEHOLDER;
logger.warn("app.id is not set, please make sure it is set in classpath:/META-INF/app.properties, now apollo will only load public namespace configurations!");
logger.warn("app.id is not set, please make sure it is set in classpath:/META-INF/app.properties, now apollo " +
"will only load public namespace configurations!");
}
return appId;
}
......@@ -206,6 +215,14 @@ public class ConfigUtil {
return longPollQPS;
}
public long getOnErrorRetryInterval() {
return onErrorRetryInterval;
}
public TimeUnit getOnErrorRetryIntervalTimeUnit() {
return onErrorRetryIntervalTimeUnit;
}
public String getDefaultLocalCacheDir() {
String cacheRoot = isOSWindows() ? "C:\\opt\\data\\%s" : "/opt/data/%s";
return String.format(cacheRoot, getAppId());
......@@ -228,4 +245,27 @@ public class ConfigUtil {
}
return osName.startsWith("Windows");
}
private void initMaxConfigCacheSize() {
String customizedConfigCacheSize = System.getProperty("apollo.configCacheSize");
if (!Strings.isNullOrEmpty(customizedConfigCacheSize)) {
try {
maxConfigCacheSize = Long.valueOf(customizedConfigCacheSize);
} catch (Throwable ex) {
logger.error("Config for apollo.configCacheSize is invalid: {}", customizedConfigCacheSize);
}
}
}
public long getMaxConfigCacheSize() {
return maxConfigCacheSize;
}
public long getConfigCacheExpireTime() {
return configCacheExpireTime;
}
public TimeUnit getConfigCacheExpireTimeUnit() {
return configCacheExpireTimeUnit;
}
}
package com.ctrip.framework.apollo.util.function;
import com.google.common.base.Function;
import com.ctrip.framework.apollo.exceptions.ApolloConfigException;
import com.ctrip.framework.apollo.util.parser.ParserException;
import com.ctrip.framework.apollo.util.parser.Parsers;
import java.util.Date;
/**
* @author Jason Song(song_s@ctrip.com)
*/
public interface Functions {
Function<String, Integer> TO_INT_FUNCTION = new Function<String, Integer>() {
@Override
public Integer apply(String input) {
return Integer.parseInt(input);
}
};
Function<String, Long> TO_LONG_FUNCTION = new Function<String, Long>() {
@Override
public Long apply(String input) {
return Long.parseLong(input);
}
};
Function<String, Short> TO_SHORT_FUNCTION = new Function<String, Short>() {
@Override
public Short apply(String input) {
return Short.parseShort(input);
}
};
Function<String, Float> TO_FLOAT_FUNCTION = new Function<String, Float>() {
@Override
public Float apply(String input) {
return Float.parseFloat(input);
}
};
Function<String, Double> TO_DOUBLE_FUNCTION = new Function<String, Double>() {
@Override
public Double apply(String input) {
return Double.parseDouble(input);
}
};
Function<String, Byte> TO_BYTE_FUNCTION = new Function<String, Byte>() {
@Override
public Byte apply(String input) {
return Byte.parseByte(input);
}
};
Function<String, Boolean> TO_BOOLEAN_FUNCTION = new Function<String, Boolean>() {
@Override
public Boolean apply(String input) {
return Boolean.parseBoolean(input);
}
};
Function<String, Date> TO_DATE_FUNCTION = new Function<String, Date>() {
@Override
public Date apply(String input) {
try {
return Parsers.forDate().parse(input);
} catch (ParserException ex) {
throw new ApolloConfigException("Parse date failed", ex);
}
}
};
Function<String, Long> TO_DURATION_FUNCTION = new Function<String, Long>() {
@Override
public Long apply(String input) {
try {
return Parsers.forDuration().parseToMillis(input);
} catch (ParserException ex) {
throw new ApolloConfigException("Parse duration failed", ex);
}
}
};
}
......@@ -56,6 +56,7 @@ public abstract class BaseIntegrationTest extends ComponentTestCase {
@Before
public void setUp() throws Exception {
super.tearDown();//clear the container
super.setUp();
someAppId = "1003171";
someClusterName = "someClusterName";
......@@ -181,6 +182,16 @@ public abstract class BaseIntegrationTest extends ComponentTestCase {
public String getDefaultLocalCacheDir() {
return ClassLoaderUtil.getClassPath();
}
@Override
public long getOnErrorRetryInterval() {
return 10;
}
@Override
public TimeUnit getOnErrorRetryIntervalTimeUnit() {
return TimeUnit.MILLISECONDS;
}
}
/**
......
......@@ -23,6 +23,7 @@ public class ConfigServiceTest extends ComponentTestCase {
@Override
@Before
public void setUp() throws Exception {
super.tearDown();//clear the container
super.setUp();
someAppId = "someAppId";
//as ConfigService is singleton, so we must manually clear its container
......
......@@ -26,6 +26,7 @@ public class DefaultConfigManagerTest extends ComponentTestCase {
@Before
public void setUp() throws Exception {
super.tearDown();//clear the container
super.setUp();
defineComponent(ConfigFactoryManager.class, MockConfigManager.class);
defaultConfigManager = (DefaultConfigManager) lookup(ConfigManager.class);
......
......@@ -12,10 +12,12 @@ import com.ctrip.framework.apollo.core.utils.ClassLoaderUtil;
import com.ctrip.framework.apollo.enums.PropertyChangeType;
import com.ctrip.framework.apollo.model.ConfigChange;
import com.ctrip.framework.apollo.model.ConfigChangeEvent;
import com.ctrip.framework.apollo.util.ConfigUtil;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.unidal.lookup.ComponentTestCase;
import java.io.File;
import java.util.Calendar;
......@@ -26,12 +28,14 @@ import java.util.concurrent.TimeUnit;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
/**
* @author Jason Song(song_s@ctrip.com)
*/
public class DefaultConfigTest {
public class DefaultConfigTest extends ComponentTestCase {
private File someResourceDir;
private String someNamespace;
private ConfigRepository configRepository;
......@@ -39,6 +43,10 @@ public class DefaultConfigTest {
@Before
public void setUp() throws Exception {
super.tearDown();//clear the container
super.setUp();
defineComponent(ConfigUtil.class, MockConfigUtil.class);
someResourceDir = new File(ClassLoaderUtil.getClassPath() + "/META-INF/config");
someResourceDir.mkdirs();
someNamespace = "someName";
......@@ -47,6 +55,7 @@ public class DefaultConfigTest {
@After
public void tearDown() throws Exception {
super.tearDown();
recursiveDelete(someResourceDir);
}
......@@ -130,6 +139,120 @@ public class DefaultConfigTest {
assertEquals(someDefaultValue, defaultConfig.getIntProperty(someStringKey, someDefaultValue));
}
@Test
public void testGetIntPropertyMultipleTimesWithCache() throws Exception {
String someKey = "someKey";
Integer someValue = 2;
Integer someDefaultValue = -1;
//set up config repo
someProperties = mock(Properties.class);
when(someProperties.getProperty(someKey)).thenReturn(String.valueOf(someValue));
when(configRepository.getConfig()).thenReturn(someProperties);
DefaultConfig defaultConfig =
new DefaultConfig(someNamespace, configRepository);
assertEquals(someValue, defaultConfig.getIntProperty(someKey, someDefaultValue));
assertEquals(someValue, defaultConfig.getIntProperty(someKey, someDefaultValue));
assertEquals(someValue, defaultConfig.getIntProperty(someKey, someDefaultValue));
verify(someProperties, times(1)).getProperty(someKey);
}
@Test
public void testGetIntPropertyMultipleTimesWithPropertyChanges() throws Exception {
String someKey = "someKey";
Integer someValue = 2;
Integer anotherValue = 3;
Integer someDefaultValue = -1;
//set up config repo
someProperties = new Properties();
someProperties.setProperty(someKey, String.valueOf(someValue));
when(configRepository.getConfig()).thenReturn(someProperties);
DefaultConfig defaultConfig =
new DefaultConfig(someNamespace, configRepository);
assertEquals(someValue, defaultConfig.getIntProperty(someKey, someDefaultValue));
Properties anotherProperties = new Properties();
anotherProperties.setProperty(someKey, String.valueOf(anotherValue));
defaultConfig.onRepositoryChange(someNamespace, anotherProperties);
assertEquals(anotherValue, defaultConfig.getIntProperty(someKey, someDefaultValue));
}
@Test
public void testGetIntPropertyMultipleTimesWithSmallCache() throws Exception {
String someKey = "someKey";
Integer someValue = 2;
String anotherKey = "anotherKey";
Integer anotherValue = 3;
Integer someDefaultValue = -1;
defineComponent(ConfigUtil.class, MockConfigUtilWithSmallCache.class);
//set up config repo
someProperties = mock(Properties.class);
when(someProperties.getProperty(someKey)).thenReturn(String.valueOf(someValue));
when(someProperties.getProperty(anotherKey)).thenReturn(String.valueOf(anotherValue));
when(configRepository.getConfig()).thenReturn(someProperties);
DefaultConfig defaultConfig =
new DefaultConfig(someNamespace, configRepository);
assertEquals(someValue, defaultConfig.getIntProperty(someKey, someDefaultValue));
assertEquals(someValue, defaultConfig.getIntProperty(someKey, someDefaultValue));
verify(someProperties, times(1)).getProperty(someKey);
assertEquals(anotherValue, defaultConfig.getIntProperty(anotherKey, someDefaultValue));
assertEquals(anotherValue, defaultConfig.getIntProperty(anotherKey, someDefaultValue));
verify(someProperties, times(1)).getProperty(anotherKey);
assertEquals(someValue, defaultConfig.getIntProperty(someKey, someDefaultValue));
verify(someProperties, times(2)).getProperty(someKey);
}
@Test
public void testGetIntPropertyMultipleTimesWithShortExpireTime() throws Exception {
String someKey = "someKey";
Integer someValue = 2;
Integer someDefaultValue = -1;
defineComponent(ConfigUtil.class, MockConfigUtilWithShortExpireTime.class);
//set up config repo
someProperties = mock(Properties.class);
when(someProperties.getProperty(someKey)).thenReturn(String.valueOf(someValue));
when(configRepository.getConfig()).thenReturn(someProperties);
DefaultConfig defaultConfig =
new DefaultConfig(someNamespace, configRepository);
assertEquals(someValue, defaultConfig.getIntProperty(someKey, someDefaultValue));
assertEquals(someValue, defaultConfig.getIntProperty(someKey, someDefaultValue));
verify(someProperties, times(1)).getProperty(someKey);
TimeUnit.MILLISECONDS.sleep(50);
assertEquals(someValue, defaultConfig.getIntProperty(someKey, someDefaultValue));
assertEquals(someValue, defaultConfig.getIntProperty(someKey, someDefaultValue));
verify(someProperties, times(2)).getProperty(someKey);
}
@Test
public void testGetLongProperty() throws Exception {
String someStringKey = "someStringKey";
......@@ -288,7 +411,70 @@ public class DefaultConfigTest {
new DefaultConfig(someNamespace, configRepository);
assertArrayEquals(values, defaultConfig.getArrayProperty(someKey, someDelimiter, someDefaultValue));
assertArrayEquals(someDefaultValue, defaultConfig.getArrayProperty(someKey, someInvalidDelimiter, someDefaultValue));
assertArrayEquals(someDefaultValue, defaultConfig.getArrayProperty(someKey, someInvalidDelimiter,
someDefaultValue));
}
@Test
public void testGetArrayPropertyMultipleTimesWithCache() throws Exception {
String someKey = "someKey";
String someDelimiter = ",";
String someInvalidDelimiter = "{";
String[] values = new String[]{"a", "b", "c"};
String someValue = Joiner.on(someDelimiter).join(values);
String[] someDefaultValue = new String[]{"1", "2"};
//set up config repo
someProperties = mock(Properties.class);
when(someProperties.getProperty(someKey)).thenReturn(someValue);
when(configRepository.getConfig()).thenReturn(someProperties);
DefaultConfig defaultConfig =
new DefaultConfig(someNamespace, configRepository);
assertArrayEquals(values, defaultConfig.getArrayProperty(someKey, someDelimiter, someDefaultValue));
assertArrayEquals(values, defaultConfig.getArrayProperty(someKey, someDelimiter, someDefaultValue));
verify(someProperties, times(1)).getProperty(someKey);
assertArrayEquals(someDefaultValue, defaultConfig.getArrayProperty(someKey, someInvalidDelimiter,
someDefaultValue));
assertArrayEquals(someDefaultValue, defaultConfig.getArrayProperty(someKey, someInvalidDelimiter,
someDefaultValue));
verify(someProperties, times(3)).getProperty(someKey);
}
@Test
public void testGetArrayPropertyMultipleTimesWithCacheAndValueChanges() throws Exception {
String someKey = "someKey";
String someDelimiter = ",";
String[] values = new String[]{"a", "b", "c"};
String[] anotherValues = new String[]{"b", "c", "d"};
String someValue = Joiner.on(someDelimiter).join(values);
String anotherValue = Joiner.on(someDelimiter).join(anotherValues);
String[] someDefaultValue = new String[]{"1", "2"};
//set up config repo
someProperties = new Properties();
someProperties.setProperty(someKey, someValue);
when(configRepository.getConfig()).thenReturn(someProperties);
Properties anotherProperties = new Properties();
anotherProperties.setProperty(someKey, anotherValue);
DefaultConfig defaultConfig =
new DefaultConfig(someNamespace, configRepository);
assertArrayEquals(values, defaultConfig.getArrayProperty(someKey, someDelimiter, someDefaultValue));
defaultConfig.onRepositoryChange(someNamespace, anotherProperties);
assertArrayEquals(anotherValues, defaultConfig.getArrayProperty(someKey, someDelimiter, someDefaultValue));
}
@Test
......@@ -311,9 +497,11 @@ public class DefaultConfigTest {
new DefaultConfig(someNamespace, configRepository);
checkDatePropertyWithFormat(defaultConfig, shortDate, "shortDateProperty", "yyyy-MM-dd", someDefaultValue);
checkDatePropertyWithFormat(defaultConfig, mediumDate, "mediumDateProperty", "yyyy-MM-dd HH:mm:ss", someDefaultValue);
checkDatePropertyWithFormat(defaultConfig, mediumDate, "mediumDateProperty", "yyyy-MM-dd HH:mm:ss",
someDefaultValue);
checkDatePropertyWithFormat(defaultConfig, shortDate, "mediumDateProperty", "yyyy-MM-dd", someDefaultValue);
checkDatePropertyWithFormat(defaultConfig, longDate, "longDateProperty", "yyyy-MM-dd HH:mm:ss.SSS", someDefaultValue);
checkDatePropertyWithFormat(defaultConfig, longDate, "longDateProperty", "yyyy-MM-dd HH:mm:ss.SSS",
someDefaultValue);
checkDatePropertyWithFormat(defaultConfig, mediumDate, "longDateProperty", "yyyy-MM-dd HH:mm:ss", someDefaultValue);
checkDatePropertyWithFormat(defaultConfig, shortDate, "longDateProperty", "yyyy-MM-dd", someDefaultValue);
checkDatePropertyWithFormat(defaultConfig, someDefaultValue, "stringProperty", "yyyy-MM-dd", someDefaultValue);
......@@ -460,7 +648,8 @@ public class DefaultConfigTest {
assertEquals(PropertyChangeType.ADDED, newKeyChange.getChangeType());
}
private void checkDatePropertyWithFormat(Config config, Date expected, String propertyName, String format, Date defaultValue) {
private void checkDatePropertyWithFormat(Config config, Date expected, String propertyName, String format, Date
defaultValue) {
assertEquals(expected, config.getDateProperty(propertyName, format, defaultValue));
}
......@@ -479,4 +668,40 @@ public class DefaultConfigTest {
private enum SomeEnum {
someValue, defaultValue
}
public static class MockConfigUtil extends ConfigUtil {
@Override
public long getMaxConfigCacheSize() {
return 10;
}
@Override
public long getConfigCacheExpireTime() {
return 1;
}
@Override
public TimeUnit getConfigCacheExpireTimeUnit() {
return TimeUnit.MINUTES;
}
}
public static class MockConfigUtilWithSmallCache extends MockConfigUtil {
@Override
public long getMaxConfigCacheSize() {
return 1;
}
}
public static class MockConfigUtilWithShortExpireTime extends MockConfigUtil {
@Override
public long getConfigCacheExpireTime() {
return 50;
}
@Override
public TimeUnit getConfigCacheExpireTimeUnit() {
return TimeUnit.MILLISECONDS;
}
}
}
......@@ -42,6 +42,7 @@ public class LocalFileConfigRepositoryTest extends ComponentTestCase {
@Before
public void setUp() throws Exception {
super.tearDown();//clear the container
super.setUp();
someBaseDir = new File("src/test/resources/config-cache");
someBaseDir.mkdir();
......
......@@ -58,6 +58,7 @@ public class RemoteConfigLongPollServiceTest extends ComponentTestCase {
@Before
public void setUp() throws Exception {
super.tearDown();//clear the container
super.setUp();
defineComponent(ConfigUtil.class, MockConfigUtil.class);
......
......@@ -59,6 +59,7 @@ public class RemoteConfigRepositoryTest extends ComponentTestCase {
@Before
public void setUp() throws Exception {
super.tearDown();//clear the container
super.setUp();
someNamespace = "someName";
......@@ -232,6 +233,16 @@ public class RemoteConfigRepositoryTest extends ComponentTestCase {
public int getLongPollQPS() {
return 200;
}
@Override
public long getOnErrorRetryInterval() {
return 10;
}
@Override
public TimeUnit getOnErrorRetryIntervalTimeUnit() {
return TimeUnit.MILLISECONDS;
}
}
public static class MockConfigServiceLocator extends ConfigServiceLocator {
......
......@@ -21,6 +21,7 @@ public class DefaultConfigFactoryManagerTest extends ComponentTestCase {
@Before
public void setUp() throws Exception {
super.tearDown();//clear the container
super.setUp();
defineComponent(ConfigRegistry.class, MockConfigRegistry.class);
defaultConfigFactoryManager =
......
......@@ -40,6 +40,7 @@ public class DefaultConfigFactoryTest extends ComponentTestCase {
@Before
public void setUp() throws Exception {
super.tearDown();//clear the container
super.setUp();
someAppId = "someId";
someEnv = Env.DEV;
......
......@@ -20,6 +20,7 @@ public class DefaultConfigRegistryTest extends ComponentTestCase {
@Before
public void setUp() throws Exception {
super.tearDown();//clear the container
super.setUp();
defaultConfigRegistry = (DefaultConfigRegistry) lookup(ConfigRegistry.class);
}
......
......@@ -4,7 +4,7 @@
<parent>
<groupId>com.ctrip.framework.apollo</groupId>
<artifactId>apollo</artifactId>
<version>0.4.1</version>
<version>0.5.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
......
......@@ -4,7 +4,7 @@
<parent>
<groupId>com.ctrip.framework.apollo</groupId>
<artifactId>apollo</artifactId>
<version>0.4.1</version>
<version>0.5.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
......
......@@ -4,7 +4,7 @@
<parent>
<groupId>com.ctrip.framework.apollo</groupId>
<artifactId>apollo</artifactId>
<version>0.4.1</version>
<version>0.5.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
......
......@@ -16,7 +16,7 @@ public abstract class Tracer {
private static final Logger logger = LoggerFactory.getLogger(Tracer.class);
private static final MessageProducerManager NULL_MESSAGE_PRODUCER_MANAGER =
new NullMessageProducerManager();
private static MessageProducerManager producerManager;
private static volatile MessageProducerManager producerManager;
private static Object lock = new Object();
static {
......
......@@ -4,7 +4,7 @@
<parent>
<artifactId>apollo</artifactId>
<groupId>com.ctrip.framework.apollo</groupId>
<version>0.4.1</version>
<version>0.5.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>apollo-demo</artifactId>
......
......@@ -4,7 +4,7 @@
<parent>
<groupId>com.ctrip.framework.apollo</groupId>
<artifactId>apollo</artifactId>
<version>0.4.1</version>
<version>0.5.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
......
......@@ -10,7 +10,7 @@
<groupId>com.ctrip.framework.apollo</groupId>
<artifactId>apollo</artifactId>
<version>0.4.1</version>
<version>0.5.0-SNAPSHOT</version>
<name>Apollo</name>
<packaging>pom</packaging>
<description>Ctrip Configuration Center</description>
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册