Apollo通过请求URL如:http://host:port/configs/{appId}/{clusterName}/{namespace}?ip=XXX&dataCenter=xxx&releaseKey=XXXX,来拉取应用的配置。那么服务端的逻辑是如何查询应用配置的呢?我们来分析一下。
源码分析
服务端提供配置查询的逻辑在ConfigController#queryConfig方法中。
@RequestMapping(value = "/{appId}/{clusterName}/{namespace:.+}", method = RequestMethod.GET)
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,
HttpServletRequest request,
HttpServletResponse response) throws IOException {
String originalNamespace = namespace;
//strip out .properties suffix
namespace = namespaceUtil.filterNamespaceName(namespace);@1
if (Strings.isNullOrEmpty(clientIp)) {@2
clientIp = tryToGetClientIp(request);
}
List<Release> releases = Lists.newLinkedList();
String appClusterNameLoaded = clusterName;
if (!ConfigConsts.NO_APPID_PLACEHOLDER.equalsIgnoreCase(appId)) {
Release currentAppRelease = loadConfig(appId, clientIp, appId, clusterName, namespace,
dataCenter);@3
if (currentAppRelease != null) {
releases.add(currentAppRelease);
//we have cluster search process, so the cluster name might be overridden
appClusterNameLoaded = currentAppRelease.getClusterName();
}
}
//if namespace does not belong to this appId, should check if there is a public configuration
if (!namespaceBelongsToAppId(appId, namespace)) {@4
Release publicRelease = this.findPublicConfig(appId, clientIp, clusterName, namespace,
dataCenter);
if (!Objects.isNull(publicRelease)) {
releases.add(publicRelease);
}
}
if (releases.isEmpty()) {@5
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;
}
auditReleases(appId, clusterName, dataCenter, clientIp, releases);
String mergedReleaseKey = FluentIterable.from(releases).transform(
input -> input.getReleaseKey()).join(STRING_JOINER);
if (mergedReleaseKey.equals(clientSideReleaseKey)) {@6
// 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 = new ApolloConfig(appId, appClusterNameLoaded, originalNamespace,
mergedReleaseKey);
apolloConfig.setConfigurations(mergeReleaseConfigurations(releases));
Tracer.logEvent("Apollo.Config.Found", assembleKey(appId, appClusterNameLoaded,
originalNamespace, dataCenter));
return apolloConfig;
}
代码@1:删除namespace名的.properties后缀。
代码@2:如果clientIp为空,获取请求IP。
代码@3:如果appId不等于ApolloNoAppIdPlaceHolder,从Release表查询发布的配置。
代码@4:如果namespace不属于该appId,从Release表查询发布的公共配置。
代码@5:如果私有和公共的配置都没有查询到,返回404。
代码@6:如果client请求的releaseKey和查询到的releaseKey值一样,则说明客户端配置是最新的,返回304.
总结
为了更直观理解服务端查询配置的流程,下面给出一张上述代码的流程图。