Nacos 源码分析(二):获取配置流程
ConfigService
是提供给开发者使用的,用来对配置文件进行相关操作的核心接口,比如获取/监听/发布/删除配置项等,这一节我们来分析下获取配置内容的流程,对应的是 ConfigService#getConfig()
方法。
String getConfig(String dataId, String group, long timeoutMs)
方法有几个参数:
-
dataId
:配置文件名; -
group
:配置文件所属group
; -
timeoutMs nacos server timeoutMs
这里没有指定 namespace
,是因为 namespace
是和 ConfigService
绑定的,即每个 ConfigService
只能对应一个 namespace
, namespace
设计就是用来进行隔离。
准备工作
//group空则赋值DEFAULT_GROUP
group = null2defaultGroup(group);
//校验dataId、group是否合法,不为空且必须是数字、字母或[- _ . :]四个字符,其它都是非法
ParamUtils.checkKeyParam(dataId, group);
//返回结果封装
ConfigResponse cr = new ConfigResponse();
cr.setDataId(dataId);
cr.setTenant(tenant);
cr.setGroup(group);
主要是对参数进行校验,以及创建一个 ConfigResponse
用来封装返回结果信息。
从FailoverFile获取配置
在向 Nacos Server
发送 http
请求获取配置内容之前,会调用 LocalConfigInfoProcessor.getFailover
先尝试从本地配置文件 FailoverFile
获取,代码如下:
String content = LocalConfigInfoProcessor.getFailover(agent.getName(), dataId, group, tenant);
if (content != null) {
LOGGER.warn("[{}] [get-config] get failover ok, dataId={}, group={}, tenant={}, config={}", agent.getName(), dataId, group, tenant, ContentUtils.truncateContent(content));
cr.setContent(content);
//调用ConfigFilter.doFilter()方法
configFilterChainManager.doFilter(null, cr);
content = cr.getContent();
//如果本地配置存在,则直接返回本地配置,而不用去nacos server上去拉取
return content;
}
FailoverFile
在客户端不会自动生成,正常情况下 Failover
文件都是不存在的,设计这种机制可能主要是某种场景下扩展,比如用户系统自行维护配置变更,将最新的配置内容写入到本地 Failover
文件中,这样就不需要向 Nacos Server
发送 http
请求获取配置。
Failover
文件路径: 系统用户根目录+nacos/config+agentName+data+config-data+[group名称]+[dataId名称]
,比如: C:\Users\Administrator\nacos\config\fixed-127.0.0.1_8848-192.168.1.1_9999_nacos\data\config-data\DEFAULT_GROUP\other
从Nacos Server获取配置
如果 Failover
方式没有获取到配置信息,则向 Nacos Server
获取配置内容,核心代码如下:
try {
//去nacos server上拉取
String[] ct = worker.getServerConfig(dataId, group, tenant, timeoutMs);
cr.setContent(ct[0]);
//调用ConfigFilter chain对获取的配置进行处理
configFilterChainManager.doFilter(null, cr);
content = cr.getContent();
return content;
} catch (NacosException ioe) {
if (NacosException.NO_RIGHT == ioe.getErrCode()) {//403没有权限,拒绝访问
throw ioe;
}
LOGGER.warn("[{}] [get-config] get from server error, dataId={}, group={}, tenant={}, msg={}", agent.getName(), dataId, group, tenant, ioe.toString());
}
关键代码是: worker.getServerConfig(dataId, group, tenant, timeoutMs)
,即委托给 ClientWorker
对象:
public String[] getServerConfig(String dataId, String group, String tenant, long readTimeout)
throws NacosException {
String[] ct = new String[2];
if (StringUtils.isBlank(group)) {
group = Constants.DEFAULT_GROUP;
}
HttpResult result = null;
try {
List params = null;
if (StringUtils.isBlank(tenant)) {
params = new ArrayList(Arrays.asList("dataId", dataId, "group", group));
} else {
params = new ArrayList(Arrays.asList("dataId", dataId, "group", group, "tenant", tenant));
}
//利用ServerHttpAgent向nacos server发送get请求,path=/v1/cs/configs,params=dataId+group+tenant(namespace)
result = agent.httpGet(Constants.CONFIG_CONTROLLER_PATH, null, params, agent.getEncode(), readTimeout);
} catch (IOException e) {
String message = String.format(
"[%s] [sub-server] get server config exception, dataId=%s, group=%s, tenant=%s", agent.getName(), dataId, group, tenant);
LOGGER.error(message, e);
throw new NacosException(NacosException.SERVER_ERROR, e);
}
switch (result.code) {
case HttpURLConnection.HTTP_OK:
//更新本地对应的snapshot配置文件
LocalConfigInfoProcessor.saveSnapshot(agent.getName(), dataId, group, tenant, result.content);
ct[0] = result.content;
//设置配置文件类型,如:properties、yaml等
if (result.headers.containsKey(CONFIG_TYPE)) {
ct[1] = result.headers.get(CONFIG_TYPE).get(0);
} else {
ct[1] = ConfigType.TEXT.getType();
}
return ct;
case HttpURLConnection.HTTP_NOT_FOUND:
LocalConfigInfoProcessor.saveSnapshot(agent.getName(), dataId, group, tenant, null);
return ct;
case HttpURLConnection.HTTP_CONFLICT: {
LOGGER.error(
"[{}] [sub-server-error] get server config being modified concurrently, dataId={}, group={}, " + "tenant={}", agent.getName(), dataId, group, tenant);
throw new NacosException(NacosException.CONFLICT,
"data being modified, dataId=" + dataId + ",group=" + group + ",tenant=" + tenant);
}
case HttpURLConnection.HTTP_FORBIDDEN: {
LOGGER.error("[{}] [sub-server-error] no right, dataId={}, group={}, tenant={}", agent.getName(), dataId, group, tenant);
throw new NacosException(result.code, result.content);
}
default: {
LOGGER.error("[{}] [sub-server-error] dataId={}, group={}, tenant={}, code={}", agent.getName(), dataId, group, tenant, result.code);
throw new NacosException(result.code,
"http error, code=" + result.code + ",dataId=" + dataId + ",group=" + group + ",tenant=" + tenant);
}
}
}
代码看着比较多,但是逻辑还是比较简单,大致流程:
-
ServerHttpAgent Nacos http path=/v1/cs/configs,params=dataId+group+tenant(namespace) ServerHttpAgent Nacos http params
-
switch (result.code) http 200 saveSnapshot() SnapshotFile Nacos Server
- 然后就是请求异常处理,这里主要分为两类:
-
HTTP_NOT_FOUND
即404异常,会将本地对应的SnapshotFile文件内容清空,然后返回null
; - 其它异常都会抛出异常;
从SnapshotFile获取配置
如果从 Nacos Server
获取配置出现异常,即 ClientWorker.getServerConfig()
方法抛出异常,则会从本地 SnapshotFile
中获取配置内容,核心代码如下:
LOGGER.warn("[{}] [get-config] get snapshot ok, dataId={}, group={}, tenant={}, config={}", agent.getName(),
dataId, group, tenant, ContentUtils.truncateContent(content));
//如果从nacos server上获取配置失败,则从本地snapshot中获取配置内容
content = LocalConfigInfoProcessor.getSnapshot(agent.getName(), dataId, group, tenant);
cr.setContent(content);
//调用ConfigFilter chain对获取的配置进行处理
configFilterChainManager.doFilter(null, cr);
content = cr.getContent();
return content;
总结
调用 ConfigService.getConfig()
获取配置的流程分析完成,从上面分析流程来看,获取配置大致可以总结如下:
-
FailoverFile Nacos Server http FailoverFile FailoverFile
-
FailoverFile ServerHttpAgent Nacos http path=/v1/cs/configs,params=dataId+group+tenant(namespace) SnapshotFile
-
ServerHttpAgent SnapshotFile null SnapshotFile Nacos Server