0
点赞
收藏
分享

微信扫一扫

图解+源码讲解 Nacos 客户端启动拉取配置流程

图解+源码讲解 Nacos 客户端启动拉取配置流程


成功永远属于马上行动的人


Nacos 源码分析系列相关文章

  1. 从零开始看 Nacos 源码环境搭建
  2. 图解+源码讲解 Nacos 客户端发起注册流程
  3. 图解+源码讲解 Nacos 服务端处理注册请求逻辑
  4. 图解+源码讲解 Nacos 客户端下线流程
  5. 图解+源码讲解 Nacos 服务端处理下线请求
  6. 图解+源码讲解 Nacos 客户端发起心跳请求
  7. 图解+源码讲解 Nacos 服务端处理心跳请求
  8. 图解+源码讲解 Nacos 服务端处理配置获取请求

从哪里分析

    SpringCloud 的 PropertySourceBootstrapConfiguration类里面的 initialize方法是进行配置资源加载的,我们就从这里开始分析加载的过程
图解+源码讲解 Nacos 客户端启动拉取配置流程_后端
图解+源码讲解 Nacos 客户端启动拉取配置流程_数据_02
    source = locator.locate(environment); 这行代码是核心的属性资源加载,我们就看这个方法就行了,就知道是怎么进行资源加载的

资源定位locator.locate

@Override
public PropertySource<?> locate(Environment env) {
// 设置环境
nacosConfigProperties.setEnvironment(env);
// 创建 ConfigService 服务
ConfigService configService = nacosConfigManager.getConfigService();

if (null == configService) {
log.warn("no instance of config service found, can't load config from nacos");
return null;
}
// 获取超时时间 3000s
long timeout = nacosConfigProperties.getTimeout();
// nacos 属性资源构造器
nacosPropertySourceBuilder = new NacosPropertySourceBuilder(configService,timeout);
// 获取名字,此时为null
String name = nacosConfigProperties.getName();
// 命名空间ID,也就是Nacos的dataId,此时 dataIdPrefix 为空
String dataIdPrefix = nacosConfigProperties.getPrefix();
if (StringUtils.isEmpty(dataIdPrefix)) {
// null
dataIdPrefix = name;
}

if (StringUtils.isEmpty(dataIdPrefix)) {
// 获取环境配置的 dataId = service-consumer
dataIdPrefix = env.getProperty("spring.application.name");
}
// 自定义配置属性源
CompositePropertySource composite = new CompositePropertySource(
NACOS_PROPERTY_SOURCE_NAME);
// 加载共享配置,没做啥先过
loadSharedConfiguration(composite);
// 加载扩展配置,同样没做啥先过
loadExtConfiguration(composite);
// 加载应用配置,主要的核心方法
loadApplicationConfiguration(composite, dataIdPrefix, nacosConfigProperties, env);
return composite;
}

ConfigService 主要方法分析

图解+源码讲解 Nacos 客户端启动拉取配置流程_Spring Cloud_03
    其实看到这个 ConfigService里面包含了很多的方法,有监听器相关的,有获取配置发布配置以及关闭的方法,我们其实主要的就是围绕这个获取配置以及配置监听的核心流程

核心方法 loadApplicationConfiguration

    这个方法是加载配置的核心方法

private void loadApplicationConfiguration(
CompositePropertySource compositePropertySource, String dataIdPrefix,
NacosConfigProperties properties, Environment environment) {
// 获取文件扩展名 properties
String fileExtension = properties.getFileExtension();
// 获取组信息 DEFAULT_GROUP
String nacosGroup = properties.getGroup();
// 默认情况下直接加载一次,不过这里没加载到啥东西,一会分析这个方法
loadNacosDataIfPresent(compositePropertySource, dataIdPrefix, nacosGroup,
fileExtension, true);
// 使用后缀加载,其优先级高于默认值,这个是核心的能获取到的方法调用
loadNacosDataIfPresent(compositePropertySource,
dataIdPrefix + DOT + fileExtension, nacosGroup, fileExtension, true);
// 已加载配置文件,其优先级高于后缀
for (String profile : environment.getActiveProfiles()) {
String dataId = dataIdPrefix + SEP1 + profile + DOT + fileExtension;
loadNacosDataIfPresent(compositePropertySource, dataId, nacosGroup,
fileExtension, true);
}
}

加载Nacos数据 loadNacosDataIfPresent

    第一次访问的时候404没获取到,第二次是能获取到的,所以我们来分析第二次 ,
参数:dataId = service-consumer.properties,group = DEFAULT_GROUP

private void loadNacosDataIfPresent(final CompositePropertySource composite,
final String dataId, final String group, String fileExtension,
boolean isRefreshable) {
// dataId = service-consumer.properties
if (null == dataId || dataId.trim().length() < 1) {
return;
}
// group = DEFAULT_GROUP
if (null == group || group.trim().length() < 1) {
return;
}
// 加载Nacos属性资源
NacosPropertySource propertySource = this.loadNacosPropertySource(dataId, group,
fileExtension, isRefreshable);
// 把属性源保存到compositePropertySource中
this.addFirstPropertySource(composite, propertySource, false);
}

核心加载方法 loadNacosPropertySource

    参数:dataId = service-consumer.properties,group = DEFAULT_GROUP,isRefreshable = true,fileExtension = properties

private NacosPropertySource loadNacosPropertySource(final String dataId,
final String group, String fileExtension, boolean isRefreshable) {
if (NacosContextRefresher.getRefreshCount() != 0) {
//是否支持自动刷新,如果不支持自动刷新配置则自动从缓存获取返回,不从远程服务器加载
if (!isRefreshable) {
return NacosPropertySourceRepository.getNacosPropertySource(dataId, group);
}
}
// nacos 属性资源构建
return nacosPropertySourceBuilder.build(dataId, group, fileExtension,
isRefreshable);
}

NacosPropertySource build(String dataId, String group, String fileExtension,
boolean isRefreshable) {
// 从配置中加载 Nacos 配置数据
List<PropertySource<?>> propertySources = loadNacosData(dataId, group,
fileExtension);
// 创建 Nacos 属性资源
NacosPropertySource nacosPropertySource = new NacosPropertySource(propertySources,
group, dataId, new Date(), isRefreshable);
// 将创建好的 Nacos 的属性资源放入到 NacosPropertySourceRepository 的 Map 集合缓存中
NacosPropertySourceRepository.collectNacosPropertySource(nacosPropertySource);
return nacosPropertySource;
}

从配置中心获取配置数据 loadNacosData

    通过配置中心客户端进行配置中心服务端访问,获取配置数据

private List<PropertySource<?>> loadNacosData(String dataId, String group,
String fileExtension) {
String data = null;
try {
// getConfig方法就是核心的客户端访问方法
data = configService.getConfig(dataId, group, timeout);
// 如果返回的数据为空那么就直接返回一个空的配置
if (StringUtils.isEmpty(data)) {
return Collections.emptyList();
}
// 解析Nacos配置中心的数据并且返回
return NacosDataParserHandler.getInstance().parseNacosData(dataId, data,
fileExtension);
}
return Collections.emptyList();
}

通过 configService#getConfig 获取数据

    走的是 NacosConfigService#getConfig方法

@Override
public String getConfig(String dataId, String group, long timeoutMs)
throws NacosException {
// 通过相关参数进行配置获取,namespace="",dataId=service-consumer.properties
// group = DEFAULT_GROUP,timeoutMs = 3000
return getConfigInner(namespace, dataId, group, timeoutMs);
}

private String getConfigInner(String tenant, String dataId, String group, long timeoutMs) throws NacosException {
group = null2defaultGroup(group); // DEFAULT_GROUP
ParamUtils.checkKeyParam(dataId, group);
// 创建配置响应体
ConfigResponse cr = new ConfigResponse();
// 设置相关参数值
cr.setDataId(dataId);
cr.setTenant(tenant);
cr.setGroup(group);

// 优先使用本地配置
String content = LocalConfigInfoProcessor.getFailover(agent.getName(),
dataId, group, tenant);
if (content != null) {
cr.setContent(content);
configFilterChainManager.doFilter(null, cr);
content = cr.getContent();
return content;
}
try {
// 通过 ClientWorker 进行服务端访问获取配置数据
String[] ct = worker.getServerConfig(dataId, group, tenant, timeoutMs);
// 设置响应参数
cr.setContent(ct[0]);
configFilterChainManager.doFilter(null, cr);
content = cr.getContent();
// 返回信息
return content;
}
.........
}

通过 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;
}
HttpRestResult<String> result = null;
// 设置请求参数
try {
Map<String, String> params = new HashMap<String, String>(3);
if (StringUtils.isBlank(tenant)) {
params.put("dataId", dataId);
params.put("group", group);
} else {
params.put("dataId", dataId);
params.put("group", group);
params.put("tenant", tenant);
}
// 发起请求,请求地址是 /v1/cs/configs
result = agent.httpGet(Constants.CONFIG_CONTROLLER_PATH, null,
params, agent.getEncode(), readTimeout);
}
// 处理返回请求结果
switch (result.getCode()) {
// 如果返回成功 200
case HttpURLConnection.HTTP_OK:
// 保存快照,在客户端本地创建一个文件快照
LocalConfigInfoProcessor.saveSnapshot(agent.getName(),
dataId, group, tenant, result.getData());
ct[0] = result.getData();
if (result.getHeader().getValue(CONFIG_TYPE) != null) {
ct[1] = result.getHeader().getValue(CONFIG_TYPE);
} else {
ct[1] = ConfigType.TEXT.getType();
}
// 返回结果
return ct;
// 如果是404 就是没找到,405 方法不允许 等等
case HttpURLConnection.HTTP_NOT_FOUND:
LocalConfigInfoProcessor.saveSnapshot(agent.getName(),
dataId, group, tenant, null);
return ct;
........
}
}

保存快照 LocalConfigInfoProcessor.saveSnapshot

public static void saveSnapshot(String envName, String dataId, String group,
String tenant, String config) {
if (!SnapShotSwitch.getIsSnapShot()) {
return;
}
// 创建本地快照文件
File file = getSnapshotFile(envName, dataId, group, tenant);
// 如果配置为空那么就删除创建好的文件
if (null == config) {
try {
IoUtils.delete(file);
} catch (IOException ioe) {
LOGGER.error("[" + envName + "] delete snapshot error, " + file, ioe);
}
} else {
try {
File parentFile = file.getParentFile();
if (!parentFile.exists()) {
boolean isMdOk = parentFile.mkdirs();
if (!isMdOk) {
LOGGER.error("[{}] save snapshot error", envName);
}
}
// 将配置写入本地文件
if (JvmUtil.isMultiInstance()) {

ConcurrentDiskUtil.writeFileContent(file, config, Constants.ENCODE);
} else {
IoUtils.writeStringToFile(file, config, Constants.ENCODE);
}
}
}
}
本地文件

    路径C:\Users\yueyue\nacos\config\fixed-localhost_8848_nacos\snapshot\DEFAULT_GROUP
图解+源码讲解 Nacos 客户端启动拉取配置流程_Spring Cloud_04

文件内容

图解+源码讲解 Nacos 客户端启动拉取配置流程_客户端_05

总结

    通过上述分析,我们知道了Cloud 项目在启动的时候是如何向配置中心进行配置拉取的,以及保存配置中心的数据到本地文件

小结

    其实就是通过客户端启动的时候向配置中心进行配置拉取操作,之后将其放入到本地缓存,创建了一个配置文件的快照信息

其他系列源码分析

feign 源码分析系列相关文章

图解+源码讲解 Feign 如何将客户端注入到容器中

图解+源码讲解动态代理获取 FeignClient 代理对象

图解+源码讲解代理对象 ReflectiveFeign 分析

图解+源码讲解 Feign 如何选取指定服务

图解+源码讲解 Feign 请求的流程

ribbon 源码分析系列相关文章

  1. Ribbon 原理初探
  2. 图解+源码讲解 Ribbon 如何获取注册中心的实例
  3. 图解+源码讲解 Ribbon 服务列表更新
  4. 图解+源码讲解 Ribbon 服务选择原理
  5. 图解+源码讲解 Ribbon 如何发起网络请求

eureka 源码分析系列相关文章

  1. eureka-server 项目结构分析
  2. 图解+源码讲解 Eureka Server 启动流程分析
  3. 图解+源码讲解 Eureka Client 启动流程分析
  4. 图解+源码讲解 Eureka Server 注册表缓存逻辑
  5. 图解+源码讲解 Eureka Client 拉取注册表流程
  6. 图解+源码讲解 Eureka Client 服务注册流程
  7. 图解+源码讲解 Eureka Client 心跳机制流程
  8. 图解+源码讲解 Eureka Client 下线流程分析
  9. 图解+源码讲解 Eureka Server 服务剔除逻辑
  10. 图解+源码讲解 Eureka Server 集群注册表同步机制
举报

相关推荐

0 条评论