一、概述
上文讲了客户端定时轮询的接口,这里我们看下Config Service配置读取的接口的实现。
二、代码流程
1. ConfigController#queryConfig
方法
代码很长,但是大体流程就是根据客户端的参数从服务端查询出来最新的配置,然后根据clientSideReleaseKey和服务端的ReleaseKey进行比较,判断配置有没有更新,这里重点看下
loadConfig
方法,怎么加载最新的配置
@GetMapping(value = "/{appId}/{clusterName}/{namespace:.+}")
public ApolloConfig queryConfig(@PathVariable String appId, @PathVariable String clusterName,
@PathVariable String namespace,
@RequestParam(value = "dataCenter", required = false) String dataCenter,
@RequestParam(value = "releaseKey", defaultValue = "-1") String clientSideReleaseKey,
@RequestParam(value = "ip", required = false) String clientIp,
@RequestParam(value = "messages", required = false) String messagesAsString,
HttpServletRequest request, HttpServletResponse response) throws IOException {
String originalNamespace = namespace;
//strip out .properties suffix
// 若 Namespace 名以 .properties 结尾, 移除该结尾,并设置到 ApolloConfigNotification中
namespace = namespaceUtil.filterNamespaceName(namespace);
//fix the character case issue, such as FX.apollo <-> fx.apollo
// 获得归一化的 Namespace 名字。因为,客户端 Namespace 会填错大消息
namespace = namespaceUtil.normalizeNamespace(appId, namespace);
// 若 clientIp 未提交,从 Request 中获取
if (Strings.isNullOrEmpty(clientIp)) {
clientIp = tryToGetClientIp(request);
}
// 解析 messageAsString 参数,创建 ApolloNotificationMessages 对象
ApolloNotificationMessages clientMessages = transformMessages(messagesAsString);
// 获取 Release 数组
List<Release> releases = Lists.newLinkedList();
// 获得 Namespace 对应的 Release 对象
String appClusterNameLoaded = clusterName;
if (!ConfigConsts.NO_APPID_PLACEHOLDER.equalsIgnoreCase(appId)) {
// 获取 release 对象
Release currentAppRelease = configService.loadConfig(appId, clientIp, appId, clusterName, namespace,
dataCenter, clientMessages);
if (currentAppRelease != null) {
// 添加到 Release 数组中
releases.add(currentAppRelease);
//we have cluster search process, so the cluster name might be overridden
// 获得 Release 对应的Cluster 名字
appClusterNameLoaded = currentAppRelease.getClusterName();
}
}
//if namespace does not belong to this appId, should check if there is a public configuration
// 若 Namespace 未关联类型,则获取关联的 namespace 的 Release 对象
if (!namespaceBelongsToAppId(appId, namespace)) {
// 获得 Release 对象
Release publicRelease = this.findPublicConfig(appId, clientIp, clusterName, namespace,
dataCenter, clientMessages);
// 添加到 Release 数组中
if (Objects.nonNull(publicRelease)) {
releases.add(publicRelease);
}
}
// 若获得不到 Release , 返回状态码为 404 的响应
if (releases.isEmpty()) {
response.sendError(HttpServletResponse.SC_NOT_FOUND,
String.format(
"Could not load configurations with appId: %s, clusterName: %s, namespace: %s",
appId, clusterName, originalNamespace));
Tracer.logEvent("Apollo.Config.NotFound",
assembleKey(appId, clusterName, originalNamespace, dataCenter));
return null;
}
// 记录 InstanceConfig
auditReleases(appId, clusterName, dataCenter, clientIp, releases);
// 计算 Config Service 的合并 ReleaseKey
String mergedReleaseKey = releases.stream().map(Release::getReleaseKey)
.collect(Collectors.joining(ConfigConsts.CLUSTER_NAMESPACE_SEPARATOR));
// 对于 Client 的合并 Release key 。 若相等,说明没有改变,返回状态码为 302 的响应
if (mergedReleaseKey.equals(clientSideReleaseKey)) {
// Client side configuration is the same with server side, return 304
response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
Tracer.logEvent("Apollo.Config.NotModified",
assembleKey(appId, appClusterNameLoaded, originalNamespace, dataCenter));
return null;
}
// 创建 ApolloConfig 对象
ApolloConfig apolloConfig = new ApolloConfig(appId, appClusterNameLoaded, originalNamespace,
mergedReleaseKey);
// 合并 Release 的配置,并将结果设置到 ApolloConfig中
apolloConfig.setConfigurations(mergeReleaseConfigurations(releases));
Tracer.logEvent("Apollo.Config.Found", assembleKey(appId, appClusterNameLoaded,
originalNamespace, dataCenter));
return apolloConfig;
}
2. AbstractConfigService#loadConfig
方法
在方法中会继续调用findRelease来查找对应配置最新的Release版本
@Override
public Release loadConfig(String clientAppId, String clientIp, String configAppId, String configClusterName,
String configNamespace, String dataCenter, ApolloNotificationMessages clientMessages) {
// load from specified cluster first
if (!Objects.equals(ConfigConsts.CLUSTER_NAME_DEFAULT, configClusterName)) {
Release clusterRelease = findRelease(clientAppId, clientIp, configAppId, configClusterName, configNamespace,
clientMessages);
if (Objects.nonNull(clusterRelease)) {
return clusterRelease;
}
}
// try to load via data center
if (!Strings.isNullOrEmpty(dataCenter) && !Objects.equals(dataCenter, configClusterName)) {
Release dataCenterRelease = findRelease(clientAppId, clientIp, configAppId, dataCenter, configNamespace,
clientMessages);
if (Objects.nonNull(dataCenterRelease)) {
return dataCenterRelease;
}
}
// fallback to default release
return findRelease(clientAppId, clientIp, configAppId, ConfigConsts.CLUSTER_NAME_DEFAULT, configNamespace,
clientMessages);
}
AbstractConfigService#findRelease
方法
这里会判断是不是灰度,如果是灰度调用
findActiveOne
方法,不是灰度,调用findLatestActiveRelease
方法,下面有两个不同的实现DefaultConfigService
和ConfigServiceWithCache
private Release findRelease(String clientAppId, String clientIp, String configAppId, String configClusterName,
String configNamespace, ApolloNotificationMessages clientMessages) {
// 读取灰度发布编号
Long grayReleaseId = grayReleaseRulesHolder.findReleaseIdFromGrayReleaseRule(clientAppId, clientIp, configAppId,
configClusterName, configNamespace);
Release release = null;
// 读取灰度 Release 对象
if (grayReleaseId != null) {
release = findActiveOne(grayReleaseId, clientMessages);
}
//非灰度,获取最新的,并且有效的 Release 对象
if (release == null) {
release = findLatestActiveRelease(configAppId, configClusterName, configNamespace, clientMessages);
}
return release;
}
DefaultConfigService
这里其实可以看出就是直接去数据库中查询配置最新的版本了
@Autowired
private ReleaseService releaseService;
@Override
protected Release findActiveOne(long id, ApolloNotificationMessages clientMessages) {
return releaseService.findActiveOne(id);
}
@Override
protected Release findLatestActiveRelease(String configAppId, String configClusterName, String configNamespace,
ApolloNotificationMessages clientMessages) {
return releaseService.findLatestActiveRelease(configAppId, configClusterName,
configNamespace);
}
@Override
public void handleMessage(ReleaseMessage message, String channel) {
// since there is no cache, so do nothing
}
5.ConfigServiceWithCache
基于Guava Cache的实现类
public class ConfigServiceWithCache extends AbstractConfigService {
private static final Logger logger = LoggerFactory.getLogger(ConfigServiceWithCache.class);
private static final long DEFAULT_EXPIRED_AFTER_ACCESS_IN_MINUTES = 60;//1 hour
private static final String TRACER_EVENT_CACHE_INVALIDATE = "ConfigCache.Invalidate";
private static final String TRACER_EVENT_CACHE_LOAD = "ConfigCache.LoadFromDB";
private static final String TRACER_EVENT_CACHE_LOAD_ID = "ConfigCache.LoadFromDBById";
private static final String TRACER_EVENT_CACHE_GET = "ConfigCache.Get";
private static final String TRACER_EVENT_CACHE_GET_ID = "ConfigCache.GetById";
private static final Splitter STRING_SPLITTER =
Splitter.on(ConfigConsts.CLUSTER_NAMESPACE_SEPARATOR).omitEmptyStrings();
@Autowired
private ReleaseService releaseService;
@Autowired
private ReleaseMessageService releaseMessageService;
private LoadingCache<String, ConfigCacheEntry> configCache;
private LoadingCache<Long, Optional<Release>> configIdCache;
private ConfigCacheEntry nullConfigCacheEntry;
ConfigServiceWithCache#initialize
方法
初始化缓存对象
@PostConstruct
void initialize() {
// 初始化 configCache
configCache = CacheBuilder.newBuilder()
.expireAfterAccess(DEFAULT_EXPIRED_AFTER_ACCESS_IN_MINUTES, TimeUnit.MINUTES)
.build(new CacheLoader<String, ConfigCacheEntry>() {
@Override
public ConfigCacheEntry load(String key) throws Exception {
//格式不正确,返回 nullConfigCacheEntry
List<String> namespaceInfo = STRING_SPLITTER.splitToList(key);
if (namespaceInfo.size() != 3) {
Tracer.logError(
new IllegalArgumentException(String.format("Invalid cache load key %s", key)));
return nullConfigCacheEntry;
}
Transaction transaction = Tracer.newTransaction(TRACER_EVENT_CACHE_LOAD, key);
try {
// 获取最新的 ReleaseMessage 对象
ReleaseMessage latestReleaseMessage = releaseMessageService.findLatestReleaseMessageForMessages(Lists
.newArrayList(key));
// 获得最新的,并且有效的 Release 对象
Release latestRelease = releaseService.findLatestActiveRelease(namespaceInfo.get(0), namespaceInfo.get(1),
namespaceInfo.get(2));
transaction.setStatus(Transaction.SUCCESS);
// 获得通知编号
long notificationId = latestReleaseMessage == null ? ConfigConsts.NOTIFICATION_ID_PLACEHOLDER : latestReleaseMessage
.getId();
//若 latestReleaseMessage 和 lasestReleaseMessage 都为空,返回 nullConfigCacheEntry
if (notificationId == ConfigConsts.NOTIFICATION_ID_PLACEHOLDER && latestRelease == null) {
return nullConfigCacheEntry;
}
//创建 ConfigCacheEntry
return new ConfigCacheEntry(notificationId, latestRelease);
} catch (Throwable ex) {
transaction.setStatus(ex);
throw ex;
} finally {
transaction.complete();
}
}
});
// 初始化 configIdCache
configIdCache = CacheBuilder.newBuilder()
.expireAfterAccess(DEFAULT_EXPIRED_AFTER_ACCESS_IN_MINUTES, TimeUnit.MINUTES)
.build(new CacheLoader<Long, Optional<Release>>() {
@Override
public Optional<Release> load(Long key) throws Exception {
Transaction transaction = Tracer.newTransaction(TRACER_EVENT_CACHE_LOAD_ID, String.valueOf(key));
try {
// 获得 Release 对象
Release release = releaseService.findActiveOne(key);
transaction.setStatus(Transaction.SUCCESS);
return Optional.ofNullable(release);
} catch (Throwable ex) {
transaction.setStatus(ex);
throw ex;
} finally {
transaction.complete();
}
}
});
}
ConfigServiceWithCache#handleMessage
方法
这里发现他也监听了配置变更的消息,然后清空对应的缓存,重新从db中load
public void handleMessage(ReleaseMessage message, String channel) {
logger.info("message received - channel: {}, message: {}", channel, message);
if (!Topics.APOLLO_RELEASE_TOPIC.equals(channel) || Strings.isNullOrEmpty(message.getMessage())) {
return;
}
try {
//清空对应的缓存
invalidate(message.getMessage());
//warm up the cache
// 预热缓存,读取 ConfigCacheEntry 对象 ,重新从 DB 中加载
configCache.getUnchecked(message.getMessage());
} catch (Throwable ex) {
//ignore
}
}
ConfigServiceWithCache#findLatestActiveRelease
方法
首先从缓存中读取,然后根据通知编号比较,如果客户端比较大,说明缓存已经过期了,那么清空对应的缓存,重新从db中加载返回
protected Release findLatestActiveRelease(String appId, String clusterName, String namespaceName,
ApolloNotificationMessages clientMessages) {
//根据 appId + clusterName + namesapceName ,获得 ReleaseMessage 的 'message'
String key = ReleaseMessageKeyGenerator.generate(appId, clusterName, namespaceName);
Tracer.logEvent(TRACER_EVENT_CACHE_GET, key);
// 从缓存 configCache 中,读取 ConfigCacheEntry对象
ConfigCacheEntry cacheEntry = configCache.getUnchecked(key);
//cache is out-dated
//若客户端的通知编号更大,说明缓存已经过期
if (clientMessages != null && clientMessages.has(key) &&
clientMessages.get(key) > cacheEntry.getNotificationId()) {
//invalidate the cache and try to load from db again
// 清空对应的缓存
invalidate(key);
//读取 ConfigCacheEntry 对象,重新从 DB 中加载
cacheEntry = configCache.getUnchecked(key);
}
ConfigServiceWithCache#findActiveOne
方法
这里直接从缓存中读取
protected Release findActiveOne(long id, ApolloNotificationMessages clientMessages) {
Tracer.logEvent(TRACER_EVENT_CACHE_GET_ID, String.valueOf(id));
// 从缓存 configIdCache 中,读取 Release 对象
return configIdCache.getUnchecked(id).orElse(null);
}