Skip to content

Commit

Permalink
Reduce bootstrap time in the situation with large properties (#3800)
Browse files Browse the repository at this point in the history
  • Loading branch information
Shawyeok committed Jul 9, 2021
1 parent ab6a172 commit 2e89f18
Show file tree
Hide file tree
Showing 19 changed files with 413 additions and 213 deletions.
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ Apollo 1.9.0
* [feature: add email for select user on apollo portal](https://github.com/ctripcorp/apollo/pull/3797)
* [feature: modify item comment valid size](https://github.com/ctripcorp/apollo/pull/3803)
* [speed up the stale issue mark and close phase](https://github.com/ctripcorp/apollo/pull/3808)
* [Reduce bootstrap time in the situation with large properties](https://github.com/ctripcorp/apollo/pull/3816)
------------------
All issues and pull requests are [here](https://github.com/ctripcorp/apollo/milestone/6?closed=1)

Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@

import com.ctrip.framework.apollo.enums.ConfigSourceType;
import com.google.common.base.Function;

import java.util.Date;
import java.util.Locale;
import java.util.Set;
Expand All @@ -27,8 +26,12 @@
* @author Jason Song(song_s@ctrip.com)
*/
public interface Config {

String[] EMPTY_NAMES = new String[0];

/**
* Return the property value with the given key, or {@code defaultValue} if the key doesn't exist.
* Return the property value with the given key, or {@code defaultValue} if the key doesn't
* exist.
*
* @param key the property name
* @param defaultValue the default value when key is not found or any error occurred
Expand Down Expand Up @@ -224,11 +227,11 @@ void addChangeListener(ConfigChangeListener listener, Set<String> interestedKeys
boolean removeChangeListener(ConfigChangeListener listener);

/**
* Return a set of the property names
* Return the names of all properties
*
* @return the property names
* @return the property names (never null)
*/
Set<String> getPropertyNames();
String[] getPropertyNames();

/**
* Return the user-defined property value with the given key, or {@code defaultValue} if the key doesn't exist.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,29 +16,25 @@
*/
package com.ctrip.framework.apollo.internals;

import com.ctrip.framework.apollo.core.utils.ClassLoaderUtil;
import com.ctrip.framework.apollo.core.utils.DeferredLoggerFactory;
import com.ctrip.framework.apollo.enums.ConfigSourceType;
import com.ctrip.framework.apollo.enums.PropertyChangeType;
import com.ctrip.framework.apollo.model.ConfigChange;
import com.ctrip.framework.apollo.tracer.Tracer;
import com.ctrip.framework.apollo.util.ExceptionUtil;
import com.google.common.collect.ImmutableMap;
import com.google.common.util.concurrent.RateLimiter;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;

import org.slf4j.Logger;

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.tracer.Tracer;
import com.ctrip.framework.apollo.util.ExceptionUtil;
import com.google.common.collect.ImmutableMap;
import com.google.common.util.concurrent.RateLimiter;


/**
* @author Jason Song(song_s@ctrip.com)
Expand All @@ -49,6 +45,7 @@ public class DefaultConfig extends AbstractConfig implements RepositoryChangeLis
private final String m_namespace;
private final Properties m_resourceProperties;
private final AtomicReference<Properties> m_configProperties;
private String[] m_propertyNames;
private final ConfigRepository m_configRepository;
private final RateLimiter m_warnLogRateLimiter;

Expand All @@ -65,6 +62,7 @@ public DefaultConfig(String namespace, ConfigRepository configRepository) {
m_resourceProperties = loadFromResource(m_namespace);
m_configRepository = configRepository;
m_configProperties = new AtomicReference<>();
m_propertyNames = EMPTY_NAMES;
m_warnLogRateLimiter = RateLimiter.create(0.017); // 1 warning log output per minute
initialize();
}
Expand Down Expand Up @@ -117,31 +115,26 @@ public String getProperty(String key, String defaultValue) {
}

@Override
public Set<String> getPropertyNames() {
Properties properties = m_configProperties.get();
if (properties == null) {
return Collections.emptySet();
}

return stringPropertyNames(properties);
public String[] getPropertyNames() {
return m_propertyNames;
}

@Override
public ConfigSourceType getSourceType() {
return m_sourceType;
}

private Set<String> stringPropertyNames(Properties properties) {
private String[] stringPropertyNames(Properties properties) {
//jdk9以下版本Properties#enumerateStringProperties方法存在性能问题,keys() + get(k) 重复迭代, jdk9之后改为entrySet遍历.
Map<String, String> h = new LinkedHashMap<>();
List<String> names = new ArrayList<>(properties.size());
for (Map.Entry<Object, Object> e : properties.entrySet()) {
Object k = e.getKey();
Object v = e.getValue();
if (k instanceof String && v instanceof String) {
h.put((String) k, (String) v);
names.add((String) k);
}
}
return h.keySet();
return names.toArray(EMPTY_NAMES);
}

@Override
Expand Down Expand Up @@ -170,6 +163,11 @@ public synchronized void onRepositoryChange(String namespace, Properties newProp
private void updateConfig(Properties newConfigProperties, ConfigSourceType sourceType) {
m_configProperties.set(newConfigProperties);
m_sourceType = sourceType;
if (newConfigProperties == null) {
m_propertyNames = EMPTY_NAMES;
} else {
m_propertyNames = stringPropertyNames(newConfigProperties);
}
}

private Map<String, ConfigChange> updateAndCalcConfigChanges(Properties newConfigProperties,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,16 @@
package com.ctrip.framework.apollo.internals;

import com.ctrip.framework.apollo.enums.ConfigSourceType;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.ctrip.framework.apollo.model.ConfigChange;
import com.ctrip.framework.apollo.tracer.Tracer;
import com.ctrip.framework.apollo.util.ExceptionUtil;
import com.google.common.base.Function;
import com.google.common.collect.Maps;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* @author Jason Song(song_s@ctrip.com)
Expand All @@ -40,6 +36,7 @@ public class SimpleConfig extends AbstractConfig implements RepositoryChangeList
private final String m_namespace;
private final ConfigRepository m_configRepository;
private volatile Properties m_configProperties;
private String[] m_propertyNames;
private volatile ConfigSourceType m_sourceType = ConfigSourceType.NONE;

/**
Expand All @@ -51,6 +48,7 @@ public class SimpleConfig extends AbstractConfig implements RepositoryChangeList
public SimpleConfig(String namespace, ConfigRepository configRepository) {
m_namespace = namespace;
m_configRepository = configRepository;
m_propertyNames = EMPTY_NAMES;
this.initialize();
}

Expand Down Expand Up @@ -78,12 +76,8 @@ public String getProperty(String key, String defaultValue) {
}

@Override
public Set<String> getPropertyNames() {
if (m_configProperties == null) {
return Collections.emptySet();
}

return m_configProperties.stringPropertyNames();
public String[] getPropertyNames() {
return m_propertyNames;
}

@Override
Expand Down Expand Up @@ -119,5 +113,10 @@ public String apply(ConfigChange input) {
private void updateConfig(Properties newConfigProperties, ConfigSourceType sourceType) {
m_configProperties = newConfigProperties;
m_sourceType = sourceType;
if (newConfigProperties == null) {
m_propertyNames = EMPTY_NAMES;
} else {
m_propertyNames = newConfigProperties.stringPropertyNames().toArray(EMPTY_NAMES);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import com.ctrip.framework.apollo.core.ApolloClientSystemConsts;
import com.ctrip.framework.apollo.core.ConfigConsts;
import com.ctrip.framework.apollo.core.utils.DeferredLogger;
import com.ctrip.framework.apollo.spring.config.CompositeConfigPropertySource;
import com.ctrip.framework.apollo.spring.config.ConfigPropertySourceFactory;
import com.ctrip.framework.apollo.spring.config.PropertySourcesConstants;
import com.ctrip.framework.apollo.spring.util.SpringInjector;
Expand All @@ -34,7 +35,6 @@
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.Ordered;
import org.springframework.core.env.CompositePropertySource;
import org.springframework.core.env.ConfigurableEnvironment;

/**
Expand Down Expand Up @@ -113,21 +113,26 @@ public void initialize(ConfigurableApplicationContext context) {
*/
protected void initialize(ConfigurableEnvironment environment) {

if (environment.getPropertySources().contains(PropertySourcesConstants.APOLLO_BOOTSTRAP_PROPERTY_SOURCE_NAME)) {
if (environment.getPropertySources()
.contains(PropertySourcesConstants.APOLLO_BOOTSTRAP_PROPERTY_SOURCE_NAME)) {
//already initialized, replay the logs that were printed before the logging system was initialized
DeferredLogger.replayTo();
return;
}

String namespaces = environment.getProperty(PropertySourcesConstants.APOLLO_BOOTSTRAP_NAMESPACES, ConfigConsts.NAMESPACE_APPLICATION);
String namespaces = environment
.getProperty(PropertySourcesConstants.APOLLO_BOOTSTRAP_NAMESPACES,
ConfigConsts.NAMESPACE_APPLICATION);
logger.debug("Apollo bootstrap namespaces: {}", namespaces);
List<String> namespaceList = NAMESPACE_SPLITTER.splitToList(namespaces);

CompositePropertySource composite = new CompositePropertySource(PropertySourcesConstants.APOLLO_BOOTSTRAP_PROPERTY_SOURCE_NAME);
CompositeConfigPropertySource composite = new CompositeConfigPropertySource(
PropertySourcesConstants.APOLLO_BOOTSTRAP_PROPERTY_SOURCE_NAME);
for (String namespace : namespaceList) {
Config config = ConfigService.getConfig(namespace);

composite.addPropertySource(configPropertySourceFactory.getConfigPropertySource(namespace, config));
composite.addPropertySource(
configPropertySourceFactory.getConfigPropertySource(namespace, config));
}

environment.getPropertySources().addFirst(composite);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/*
* Copyright 2021 Apollo Authors
*
* Licensed 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 com.ctrip.framework.apollo.spring.config;

import com.ctrip.framework.apollo.ConfigChangeListener;
import com.ctrip.framework.apollo.model.ConfigChangeEvent;
import org.springframework.core.env.CompositePropertySource;
import org.springframework.core.env.PropertySource;

/**
* @author Shawyeok (shawyeok@outlook.com)
*/
public class CompositeConfigPropertySource extends CompositePropertySource {

private String[] names;

public CompositeConfigPropertySource(String name) {
super(name);
}

@Override
public String[] getPropertyNames() {
String[] propertyNames = this.names;
if (propertyNames == null) {
this.names = propertyNames = super.getPropertyNames();
}
return propertyNames;
}

@Override
public void addPropertySource(PropertySource<?> propertySource) {
super.addPropertySource(propertySource);
if (propertySource instanceof ConfigPropertySource) {
((ConfigPropertySource) propertySource).addChangeListener(new ClearNameCacheListener(this));
}
}

@Override
public void addFirstPropertySource(PropertySource<?> propertySource) {
super.addFirstPropertySource(propertySource);
if (propertySource instanceof ConfigPropertySource) {
((ConfigPropertySource) propertySource).addChangeListener(new ClearNameCacheListener(this));
}
}

static class ClearNameCacheListener implements ConfigChangeListener {

private final CompositeConfigPropertySource source;

public ClearNameCacheListener(CompositeConfigPropertySource source) {
this.source = source;
}

@Override
public void onChange(ConfigChangeEvent changeEvent) {
source.names = null;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,32 +16,29 @@
*/
package com.ctrip.framework.apollo.spring.config;

import com.ctrip.framework.apollo.Config;
import com.ctrip.framework.apollo.ConfigChangeListener;
import java.util.Set;

import org.springframework.core.env.EnumerablePropertySource;

import com.ctrip.framework.apollo.Config;

/**
* Property source wrapper for Config
*
* @author Jason Song(song_s@ctrip.com)
*/
public class ConfigPropertySource extends EnumerablePropertySource<Config> {
private static final String[] EMPTY_ARRAY = new String[0];

ConfigPropertySource(String name, Config source) {
super(name, source);
}

@Override
public boolean containsProperty(String name) {
return this.source.getProperty(name, null) != null;
}

@Override
public String[] getPropertyNames() {
Set<String> propertyNames = this.source.getPropertyNames();
if (propertyNames.isEmpty()) {
return EMPTY_ARRAY;
}
return propertyNames.toArray(new String[propertyNames.size()]);
return this.source.getPropertyNames();
}

@Override
Expand Down
Loading

0 comments on commit 2e89f18

Please sign in to comment.