上一篇文章(https://blog.csdn.net/crystonesc/article/details/106630412)我们从Apollo社区给出的DEMO开始逐步分析了Apollo客户端配置的创建过程,作为Apollo配置中心Client源码学习的第二篇文章,我们继续学习,来看看在通过ConfigFactory创建Config后,Config如何来获取配置信息的。
我们知道Apollo的DefaultConfigFactory会调用create方法来创建默认的DefaultConfig,在DefaultConfig的构造函数中,会传入namespace(命名空间)和ConfigRepository(配置源),配置源会指定了我们客户端获取配置的位置,今天我们就来说说ConfigRepository。下面是DefaultConfigFactory中create方法的内容,可以看到对于非具体格式的配置,Apollo都会使用createLocalConfigRepository返回的LocalConfigRepository作为配置源。
@Override
//(3) 创建Config
public Config create(String namespace) {
//判断namespace的文件类型
ConfigFileFormat format = determineFileFormat(namespace);
//如果是property兼容的文件,比如yaml,yml
if (ConfigFileFormat.isPropertiesCompatible(format)) {
//创建了默认的Config,并通过参数制定了namespace和Repository(仓库)
return new DefaultConfig(namespace, createPropertiesCompatibleFileConfigRepository(namespace, format));
}
//否则创建LocalConfigRepository
return new DefaultConfig(namespace, createLocalConfigRepository(namespace));
}
一、ConfigRepository结构
我们首先来看下ConfigRepository,ConfigRepository是一个接口,其实现包含AbstractConfigRepository以及三个具体实现类:PropertiesCompatibleFileConfigRepository,LocalFileConfigRepository,RemoteConfigRepository。
首先研究下ConfigRepository接口类,接口类包含几个方法,如getConfig,用于获取一个Properties类型的配置;setUpstreamRepository用于设置一个备用配置源以及addChangeListener和removeChangeListener,用于添加监听和移除监听队列,最后getSourceType用于获取配置的来源。接下来我们看下抽象实现类AbstractConfigRepository。
/**
* (1)仓库接口类,主要表示一个配置源,Apollo的Server是一个数据源,本地缓存也算是一个数据源
* @author Jason Song([email protected])
*/
public interface ConfigRepository {
/**
* (2)获取配置,返回Properties类型
* Get the config from this repository.
* @return config
*/
public Properties getConfig();
/**
*
* (3)设置一个备用的仓库
* Set the fallback repo for this repository.
* @param upstreamConfigRepository the upstream repo
*/
public void setUpstreamRepository(ConfigRepository upstreamConfigRepository);
/**
* (4)添加仓库变更监听
* Add change listener.
* @param listener the listener to observe the changes
*/
public void addChangeListener(RepositoryChangeListener listener);
/**
* (5)移除仓库变更监听
* Remove change listener.
* @param listener the listener to remove
*/
public void removeChangeListener(RepositoryChangeListener listener);
/**
* (6)返回配置是从哪个源获取的
* Return the config's source type, i.e. where is the config loaded from
*
* @return the config's source type
*/
public ConfigSourceType getSourceType();
首先从总体上我们看到AbstractConfigRepository实现了一些ConfigRepository的方法,包括监听队列的添加和删除,监听队列事件调用,这些部分是所有配置源类的共有方法。同时AbstractConfgRepository还包括trySync和Sync方法,trySync实际调用的是sync抽象方法,用于从配置源获取数据,看到这里了我们可以继续查看其实现类的代码,我们先看下LocalFileConfigRepository的代码。
/**
* @author Jason Song([email protected])
*/
//(1)配置仓库抽象类
public abstract class AbstractConfigRepository implements ConfigRepository {
private static final Logger logger = LoggerFactory.getLogger(AbstractConfigRepository.class);
//(2)配置仓库变更监听队列
private List m_listeners = Lists.newCopyOnWriteArrayList();
//(3) 实际调用抽象的sync方法
protected boolean trySync() {
try {
sync();
return true;
} catch (Throwable ex) {
Tracer.logEvent("ApolloConfigException", ExceptionUtil.getDetailMessage(ex));
logger
.warn("Sync config failed, will retry. Repository {}, reason: {}", this.getClass(), ExceptionUtil
.getDetailMessage(ex));
}
return false;
}
//(4) 需要每个实现类实现的同步方法
protected abstract void sync();
@Override
//(5) 向监听队列添加监听器
public void addChangeListener(RepositoryChangeListener listener) {
if (!m_listeners.contains(listener)) {
m_listeners.add(listener);
}
}
@Override
//(6) 从队列移除监听器
public void removeChangeListener(RepositoryChangeListener listener) {
m_listeners.remove(listener);
}
//(7) 触发监听器修改事件,遍历监听器队列中的监听者,调用器处理方法
protected void fireRepositoryChange(String namespace, Properties newProperties) {
for (RepositoryChangeListener listener : m_listeners) {
try {
listener.onRepositoryChange(namespace, newProperties);
} catch (Throwable ex) {
Tracer.logError(ex);
logger.error("Failed to invoke repository change listener {}", listener.getClass(), ex);
}
}
}
}
二、LocalFileConfigRepository源码分析
LocalFileConfigRepository是一个用于表示本地文件配置源的类,我们知道Apollo从服务端同步的数据都会保留在本地缓存目录,以防止在不能够连接到远程配置源的情况下使用本地配置源,那么所以对于默认的配置,实际上是Config是一个拥有带远程配置源(RemoteConfigRepository)的LocalFileConfigRepository,这一点我们可以从createLocalConfigRepository方法中得到印证。如下图所示:
//(5) 创建LocalFileConfigRepository,如果Apollo是以本地模式运行,则创建没有upstream的LocalFileConfigRepository,否则
//创建一个带有远程仓库RemoteConfigRepository的创建LocalFileConfigRepository
LocalFileConfigRepository createLocalConfigRepository(String namespace) {
if (m_configUtil.isInLocalMode()) {
logger.warn(
"==== Apollo is in local mode! Won't pull configs from remote server for namespace {} ! ====",
namespace);
return new LocalFileConfigRepository(namespace);
}
return new LocalFileConfigRepository(namespace, createRemoteConfigRepository(namespace));
}
接下来我们仔细阅读LocalFileConfigRepository的源码,在LocalFileConfigRepository构造的时候就会进行一次trySync,用于获取配置数据,并且还会指定一个Upstream配置源(远程配置),从上面的分析我们可以知道,trySync会调用sync方法,而这里的sync方法会在LocalFileConfigRepository中被实现,我们继续研究sync方法(7),在sync方法中它先调用trySyncFromUpStream方法尝试从Upstream仓库获取配置,如果能够获取到配置,则直接返回,如果未能同步成功,则会调用loadFromLoaclCacheFile从本地加载配置。(具体分析建代码)
public class LocalFileConfigRepository extends AbstractConfigRepository
implements RepositoryChangeListener {
private static final Logger logger = LoggerFactory.getLogger(LocalFileConfigRepository.class);
private static final String CONFIG_DIR = "/config-cache";
private final String m_namespace;
private File m_baseDir;
private final ConfigUtil m_configUtil;
private volatile Properties m_fileProperties;
private volatile ConfigRepository m_upstream;
private volatile ConfigSourceType m_sourceType = ConfigSourceType.LOCAL;
/**
* Constructor.
*
* @param namespace the namespace
*/
//(1) 本地配置仓库构造函数(不指定upstream),实际调用LocalFileConfigRepository,
public LocalFileConfigRepository(String namespace) {
this(namespace, null);
}
//(2) 带upstream的构造函数,构造的同时就会调用trySync来同步配置
public LocalFileConfigRepository(String namespace, ConfigRepository upstream) {
m_namespace = namespace;
m_configUtil = ApolloInjector.getInstance(ConfigUtil.class);
this.setLocalCacheDir(findLocalCacheDir(), false);
this.setUpstreamRepository(upstream);
this.trySync();
}
//(3) 设置本地配置缓存目录,如果syncImmediately为true, 则会立即同步配置
void setLocalCacheDir(File baseDir, boolean syncImmediately) {
m_baseDir = baseDir;
this.checkLocalConfigCacheDir(m_baseDir);
if (syncImmediately) {
this.trySync();
}
}
//(4) 定位本地缓存目录,若不存在则创建目录,若存在文件则返回File类型对象
private File findLocalCacheDir() {
try {
String defaultCacheDir = m_configUtil.getDefaultLocalCacheDir();
Path path = Paths.get(defaultCacheDir);
if (!Files.exists(path)) {
Files.createDirectories(path);
}
if (Files.exists(path) && Files.isWritable(path)) {
return new File(defaultCacheDir, CONFIG_DIR);
}
} catch (Throwable ex) {
//ignore
}
return new File(ClassLoaderUtil.getClassPath(), CONFIG_DIR);
}
@Override
//(5) 获取配置,如果不存在就进行同步
public Properties getConfig() {
if (m_fileProperties == null) {
sync();
}
Properties result = new Properties();
result.putAll(m_fileProperties);
return result;
}
@Override
//(6) 设置fallback 配置源
public void setUpstreamRepository(ConfigRepository upstreamConfigRepository) {
if (upstreamConfigRepository == null) {
return;
}
//clear previous listener
if (m_upstream != null) {
m_upstream.removeChangeListener(this);
}
m_upstream = upstreamConfigRepository;
trySyncFromUpstream();
upstreamConfigRepository.addChangeListener(this);
}
@Override
public ConfigSourceType getSourceType() {
return m_sourceType;
}
@Override
public void onRepositoryChange(String namespace, Properties newProperties) {
if (newProperties.equals(m_fileProperties)) {
return;
}
Properties newFileProperties = new Properties();
newFileProperties.putAll(newProperties);
updateFileProperties(newFileProperties, m_upstream.getSourceType());
this.fireRepositoryChange(namespace, newProperties);
}
@Override
//(7) 同步配置
protected void sync() {
//sync with upstream immediately
//首先从Upstream获取配置
boolean syncFromUpstreamResultSuccess = trySyncFromUpstream();
//如果从upstream源同步成功则直接返回
if (syncFromUpstreamResultSuccess) {
return;
}
//如果未能从upstream同步成功,则通过本地缓存文件加载
Transaction transaction = Tracer.newTransaction("Apollo.ConfigService", "syncLocalConfig");
Throwable exception = null;
try {
transaction.addData("Basedir", m_baseDir.getAbsolutePath());
m_fileProperties = this.loadFromLocalCacheFile(m_baseDir, m_namespace);
m_sourceType = ConfigSourceType.LOCAL;
transaction.setStatus(Transaction.SUCCESS);
} catch (Throwable ex) {
Tracer.logEvent("ApolloConfigException", ExceptionUtil.getDetailMessage(ex));
transaction.setStatus(ex);
exception = ex;
//ignore
} finally {
transaction.complete();
}
if (m_fileProperties == null) {
m_sourceType = ConfigSourceType.NONE;
throw new ApolloConfigException(
"Load config from local config failed!", exception);
}
}
//(8) 从Upstream中获取配置
private boolean trySyncFromUpstream() {
if (m_upstream == null) {
return false;
}
try {
updateFileProperties(m_upstream.getConfig(), m_upstream.getSourceType());
return true;
} catch (Throwable ex) {
Tracer.logError(ex);
logger
.warn("Sync config from upstream repository {} failed, reason: {}", m_upstream.getClass(),
ExceptionUtil.getDetailMessage(ex));
}
return false;
}
//(9) 把新配置更新到本地缓存中
private synchronized void updateFileProperties(Properties newProperties, ConfigSourceType sourceType) {
this.m_sourceType = sourceType;
if (newProperties.equals(m_fileProperties)) {
return;
}
this.m_fileProperties = newProperties;
persistLocalCacheFile(m_baseDir, m_namespace);
}
//(10) 从本地缓存加载配置文件
private Properties loadFromLocalCacheFile(File baseDir, String namespace) throws IOException {
Preconditions.checkNotNull(baseDir, "Basedir cannot be null");
File file = assembleLocalCacheFile(baseDir, namespace);
Properties properties = null;
if (file.isFile() && file.canRead()) {
InputStream in = null;
try {
in = new FileInputStream(file);
properties = new Properties();
properties.load(in);
logger.debug("Loading local config file {} successfully!", file.getAbsolutePath());
} catch (IOException ex) {
Tracer.logError(ex);
throw new ApolloConfigException(String
.format("Loading config from local cache file %s failed", file.getAbsolutePath()), ex);
} finally {
try {
if (in != null) {
in.close();
}
} catch (IOException ex) {
// ignore
}
}
} else {
throw new ApolloConfigException(
String.format("Cannot read from local cache file %s", file.getAbsolutePath()));
}
return properties;
}
//(11) 持久化到本地文件实际操作
void persistLocalCacheFile(File baseDir, String namespace) {
if (baseDir == null) {
return;
}
File file = assembleLocalCacheFile(baseDir, namespace);
OutputStream out = null;
Transaction transaction = Tracer.newTransaction("Apollo.ConfigService", "persistLocalConfigFile");
transaction.addData("LocalConfigFile", file.getAbsolutePath());
try {
out = new FileOutputStream(file);
m_fileProperties.store(out, "Persisted by DefaultConfig");
transaction.setStatus(Transaction.SUCCESS);
} catch (IOException ex) {
ApolloConfigException exception =
new ApolloConfigException(
String.format("Persist local cache file %s failed", file.getAbsolutePath()), ex);
Tracer.logError(exception);
transaction.setStatus(exception);
logger.warn("Persist local cache file {} failed, reason: {}.", file.getAbsolutePath(),
ExceptionUtil.getDetailMessage(ex));
} finally {
if (out != null) {
try {
out.close();
} catch (IOException ex) {
//ignore
}
}
transaction.complete();
}
}
//(12) 检查缓存目录是否存在,若不存在则建立目录
private void checkLocalConfigCacheDir(File baseDir) {
if (baseDir.exists()) {
return;
}
Transaction transaction = Tracer.newTransaction("Apollo.ConfigService", "createLocalConfigDir");
transaction.addData("BaseDir", baseDir.getAbsolutePath());
try {
Files.createDirectory(baseDir.toPath());
transaction.setStatus(Transaction.SUCCESS);
} catch (IOException ex) {
ApolloConfigException exception =
new ApolloConfigException(
String.format("Create local config directory %s failed", baseDir.getAbsolutePath()),
ex);
Tracer.logError(exception);
transaction.setStatus(exception);
logger.warn(
"Unable to create local config cache directory {}, reason: {}. Will not able to cache config file.",
baseDir.getAbsolutePath(), ExceptionUtil.getDetailMessage(ex));
} finally {
transaction.complete();
}
}
File assembleLocalCacheFile(File baseDir, String namespace) {
String fileName =
String.format("%s.properties", Joiner.on(ConfigConsts.CLUSTER_NAMESPACE_SEPARATOR)
.join(m_configUtil.getAppId(), m_configUtil.getCluster(), namespace));
return new File(baseDir, fileName);
}
}
三、RemoteConfigRespository源码分析
接下来我们来分析RemoteConfigRespository,从上面我们可以看到RemoteConfigRespository代表的是远程配置源,在其构造函数中(1),就会调用trySync方法来进行同步,同样trySync方法也会调用sync方法来实际触发同步(5),在sync方法中我们可以看到,先从缓存中获取(ApolloConfig previous = m_configCache.get())之前获取的配置,然后通过loadApolloConfig方法从远程配置源获取配置,接下来通过判断previous和current来判断是否相同,这里它直接使用(previous != current)来做判断,实际上是对previous和current进行对象比较,看到这里可能大家比较疑惑,从loadApolloConfig的配置为什么可以直接与缓存中后去的previous进行比较,原来在loadApolloConfig方法中,从远程配置源获取配置的时候,如果配置源返回HTTP CODE 304的时候,loadApolloConfig就会将缓存中的配置直接返回给current,从而sync中可以通过判断对象是否相等的方法来决定是否更新缓存配置。如果previous!=current,则会将当前的配置设置到缓存当中,并且触发监听者更新回调。
接着我们就来研究下loadApolloConfig方法(7),这个方法的内容比较多,首先为了防止客户端频繁获取远程配置造成远程配置源压力,这里Apollo使用了一个限速器,保证在每次更新操作间隔5秒,接下来通过调用getConfigServices()方法来获取configservice地址,在getConfigServices 中会使用ConfigServiceLoactor来获取configservice的地址(具体怎么获取,我们之后来分析),拿到configServices地址后,随机从configServices地址中获取一个地址并获取配置,如果获取失败,Apollo还会又重试机制,最终获取到的结果会进行返回。除了在构造的时候去拉去配置,Apollo还会定期通过单独的线程去获取配置,代码见(4)schedulePeriodicRefresh。
/**
* @author Jason Song([email protected])
*/
public class RemoteConfigRepository extends AbstractConfigRepository {
private static final Logger logger = LoggerFactory.getLogger(RemoteConfigRepository.class);
private static final Joiner STRING_JOINER = Joiner.on(ConfigConsts.CLUSTER_NAMESPACE_SEPARATOR);
private static final Joiner.MapJoiner MAP_JOINER = Joiner.on("&").withKeyValueSeparator("=");
private static final Escaper pathEscaper = UrlEscapers.urlPathSegmentEscaper();
private static final Escaper queryParamEscaper = UrlEscapers.urlFormParameterEscaper();
private final ConfigServiceLocator m_serviceLocator;
private final HttpUtil m_httpUtil;
private final ConfigUtil m_configUtil;
private final RemoteConfigLongPollService remoteConfigLongPollService;
private volatile AtomicReference m_configCache;
private final String m_namespace;
private final static ScheduledExecutorService m_executorService;
private final AtomicReference m_longPollServiceDto;
private final AtomicReference m_remoteMessages;
private final RateLimiter m_loadConfigRateLimiter;
private final AtomicBoolean m_configNeedForceRefresh;
private final SchedulePolicy m_loadConfigFailSchedulePolicy;
private final Gson gson;
static {
m_executorService = Executors.newScheduledThreadPool(1,
ApolloThreadFactory.create("RemoteConfigRepository", true));
}
/**
* Constructor.
*
* @param namespace the namespace
*/
//(1) 远程配置源构造函数
public RemoteConfigRepository(String namespace) {
m_namespace = namespace;
//配置缓存
m_configCache = new AtomicReference<>();
//通过AoplloInjector注入configUtil工具类、httpUitl、serviceLocator、remoteConfigLongPollService
m_configUtil = ApolloInjector.getInstance(ConfigUtil.class);
m_httpUtil = ApolloInjector.getInstance(HttpUtil.class);
m_serviceLocator = ApolloInjector.getInstance(ConfigServiceLocator.class);
remoteConfigLongPollService = ApolloInjector.getInstance(RemoteConfigLongPollService.class);
m_longPollServiceDto = new AtomicReference<>();
m_remoteMessages = new AtomicReference<>();
m_loadConfigRateLimiter = RateLimiter.create(m_configUtil.getLoadConfigQPS());
m_configNeedForceRefresh = new AtomicBoolean(true);
m_loadConfigFailSchedulePolicy = new ExponentialSchedulePolicy(m_configUtil.getOnErrorRetryInterval(),
m_configUtil.getOnErrorRetryInterval() * 8);
gson = new Gson();
this.trySync();
this.schedulePeriodicRefresh();
this.scheduleLongPollingRefresh();
}
@Override
//(2) 获取配置方法实现
public Properties getConfig() {
if (m_configCache.get() == null) {
this.sync();
}
//返回的时候转换为Properties类型的格式
return transformApolloConfigToProperties(m_configCache.get());
}
@Override
//(3) 设置备用Upstream配置源,因为本生是远程仓库,所以这里并未实现
public void setUpstreamRepository(ConfigRepository upstreamConfigRepository) {
//remote config doesn't need upstream
}
@Override
public ConfigSourceType getSourceType() {
return ConfigSourceType.REMOTE;
}
//(4) 设置定期获取配置得线程,定期从远程配置源获取配置
private void schedulePeriodicRefresh() {
logger.debug("Schedule periodic refresh with interval: {} {}",
m_configUtil.getRefreshInterval(), m_configUtil.getRefreshIntervalTimeUnit());
m_executorService.scheduleAtFixedRate(
new Runnable() {
@Override
public void run() {
Tracer.logEvent("Apollo.ConfigService", String.format("periodicRefresh: %s", m_namespace));
logger.debug("refresh config for namespace: {}", m_namespace);
trySync();
Tracer.logEvent("Apollo.Client.Version", Apollo.VERSION);
}
}, m_configUtil.getRefreshInterval(), m_configUtil.getRefreshInterval(),
m_configUtil.getRefreshIntervalTimeUnit());
}
@Override
//(5) 从远程配置源获取配置,实际调用方法loadApolloConfig,如果远程配置源更新后,触发监听更新
protected synchronized void sync() {
Transaction transaction = Tracer.newTransaction("Apollo.ConfigService", "syncRemoteConfig");
try {
ApolloConfig previous = m_configCache.get();
ApolloConfig current = loadApolloConfig();
//reference equals means HTTP 304
if (previous != current) {
logger.debug("Remote Config refreshed!");
m_configCache.set(current);
this.fireRepositoryChange(m_namespace, this.getConfig());
}
if (current != null) {
Tracer.logEvent(String.format("Apollo.Client.Configs.%s", current.getNamespaceName()),
current.getReleaseKey());
}
transaction.setStatus(Transaction.SUCCESS);
} catch (Throwable ex) {
transaction.setStatus(ex);
throw ex;
} finally {
transaction.complete();
}
}
//(6) 将Apollo配置转换为Properties类型,并且返回,apolloConfig中的configurations是一个MAP类型
private Properties transformApolloConfigToProperties(ApolloConfig apolloConfig) {
Properties result = new Properties();
result.putAll(apolloConfig.getConfigurations());
return result;
}
//(7) 实际获取配置方法
private ApolloConfig loadApolloConfig() {
//获取限速器的允许,得到允许后才能进行配置获取
if (!m_loadConfigRateLimiter.tryAcquire(5, TimeUnit.SECONDS)) {
//wait at most 5 seconds
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
}
}
String appId = m_configUtil.getAppId();
String cluster = m_configUtil.getCluster();
String dataCenter = m_configUtil.getDataCenter();
Tracer.logEvent("Apollo.Client.ConfigMeta", STRING_JOINER.join(appId, cluster, m_namespace));
//配置获取重试次数
int maxRetries = m_configNeedForceRefresh.get() ? 2 : 1;
long onErrorSleepTime = 0; // 0 means no sleep
Throwable exception = null;
List configServices = getConfigServices();
String url = null;
for (int i = 0; i < maxRetries; i++) {
//随机的获取一个configService
List randomConfigServices = Lists.newLinkedList(configServices);
Collections.shuffle(randomConfigServices);
//Access the server which notifies the client first
if (m_longPollServiceDto.get() != null) {
randomConfigServices.add(0, m_longPollServiceDto.getAndSet(null));
}
for (ServiceDTO configService : randomConfigServices) {
if (onErrorSleepTime > 0) {
logger.warn(
"Load config failed, will retry in {} {}. appId: {}, cluster: {}, namespaces: {}",
onErrorSleepTime, m_configUtil.getOnErrorRetryIntervalTimeUnit(), appId, cluster, m_namespace);
try {
m_configUtil.getOnErrorRetryIntervalTimeUnit().sleep(onErrorSleepTime);
} catch (InterruptedException e) {
//ignore
}
}
url = assembleQueryConfigUrl(configService.getHomepageUrl(), appId, cluster, m_namespace,
dataCenter, m_remoteMessages.get(), m_configCache.get());
logger.debug("Loading config from {}", url);
HttpRequest request = new HttpRequest(url);
Transaction transaction = Tracer.newTransaction("Apollo.ConfigService", "queryConfig");
transaction.addData("Url", url);
try {
//实际获取配置的HTTP调用
HttpResponse response = m_httpUtil.doGet(request, ApolloConfig.class);
m_configNeedForceRefresh.set(false);
m_loadConfigFailSchedulePolicy.success();
transaction.addData("StatusCode", response.getStatusCode());
transaction.setStatus(Transaction.SUCCESS);
if (response.getStatusCode() == 304) {
logger.debug("Config server responds with 304 HTTP status code.");
return m_configCache.get();
}
ApolloConfig result = response.getBody();
logger.debug("Loaded config for {}: {}", m_namespace, result);
return result;
} catch (ApolloConfigStatusCodeException ex) {
ApolloConfigStatusCodeException statusCodeException = ex;
//config not found
if (ex.getStatusCode() == 404) {
String message = String.format(
"Could not find config for namespace - appId: %s, cluster: %s, namespace: %s, " +
"please check whether the configs are released in Apollo!",
appId, cluster, m_namespace);
statusCodeException = new ApolloConfigStatusCodeException(ex.getStatusCode(),
message);
}
Tracer.logEvent("ApolloConfigException", ExceptionUtil.getDetailMessage(statusCodeException));
transaction.setStatus(statusCodeException);
exception = statusCodeException;
} catch (Throwable ex) {
Tracer.logEvent("ApolloConfigException", ExceptionUtil.getDetailMessage(ex));
transaction.setStatus(ex);
exception = ex;
} finally {
transaction.complete();
}
// if force refresh, do normal sleep, if normal config load, do exponential sleep
onErrorSleepTime = m_configNeedForceRefresh.get() ? m_configUtil.getOnErrorRetryInterval() :
m_loadConfigFailSchedulePolicy.fail();
}
}
String message = String.format(
"Load Apollo Config failed - appId: %s, cluster: %s, namespace: %s, url: %s",
appId, cluster, m_namespace, url);
throw new ApolloConfigException(message, exception);
}
String assembleQueryConfigUrl(String uri, String appId, String cluster, String namespace,
String dataCenter, ApolloNotificationMessages remoteMessages, ApolloConfig previousConfig) {
String path = "configs/%s/%s/%s";
List pathParams =
Lists.newArrayList(pathEscaper.escape(appId), pathEscaper.escape(cluster),
pathEscaper.escape(namespace));
Map queryParams = Maps.newHashMap();
if (previousConfig != null) {
queryParams.put("releaseKey", queryParamEscaper.escape(previousConfig.getReleaseKey()));
}
if (!Strings.isNullOrEmpty(dataCenter)) {
queryParams.put("dataCenter", queryParamEscaper.escape(dataCenter));
}
String localIp = m_configUtil.getLocalIp();
if (!Strings.isNullOrEmpty(localIp)) {
queryParams.put("ip", queryParamEscaper.escape(localIp));
}
if (remoteMessages != null) {
queryParams.put("messages", queryParamEscaper.escape(gson.toJson(remoteMessages)));
}
String pathExpanded = String.format(path, pathParams.toArray());
if (!queryParams.isEmpty()) {
pathExpanded += "?" + MAP_JOINER.join(queryParams);
}
if (!uri.endsWith("/")) {
uri += "/";
}
return uri + pathExpanded;
}
private void scheduleLongPollingRefresh() {
remoteConfigLongPollService.submit(m_namespace, this);
}
public void onLongPollNotified(ServiceDTO longPollNotifiedServiceDto, ApolloNotificationMessages remoteMessages) {
m_longPollServiceDto.set(longPollNotifiedServiceDto);
m_remoteMessages.set(remoteMessages);
m_executorService.submit(new Runnable() {
@Override
public void run() {
m_configNeedForceRefresh.set(true);
trySync();
}
});
}
private List getConfigServices() {
List services = m_serviceLocator.getConfigServices();
if (services.size() == 0) {
throw new ApolloConfigException("No available config service");
}
return services;
}
}
四、总结
本小结我们从ConfigRespository入手,分析了Apollo客户端在创建了DefaultConfig后如何来获取配置,我们主要分析了ConfigRespository的主要两个实现类LocalFileConfigRepository和RemoteConfigRespository,接下来我们用一幅图来描述下本结描述的内容,方便理解: