一、概述
nacos可以作为配置管理使用,为各个微服务之间提供统一的配置中心,方便管理所有服务的配置。
二、什么是配置中心?
配置中心:一般SpringBoot项目都使用在resources下创建类似application.yml之类的配置文件来管理整个项目的一些配置信息。
当微服务部署的实例越来越多时,这时候逐个修改配置效率非常低,也容易出错,那么提供统一的配置中心就可以集中管理各个服务配置了。
三、nacos配置中心原理解析
1.配置中心核心接口ConfigService获取配置
获取配置的主要方法是 NacosConfigService 类的 getConfig 方法,通常情况下该方法直接从本地文件中取得配置的值,如果本地文件不存在或者内容为空,则再通过 HTTP GET 方法从远端拉取配置,并保存到本地快照中。当通过 HTTP 获取远端配置时,Nacos 提供了两种熔断策略,一是超时时间,二是最大重试次数,默认重试三次。
2.nacos客户端获取服务配置
public String getConfig(String dataId, String group, long timeoutMs) throws NacosException {
return this.getConfigInner(this.namespace, dataId, group, timeoutMs);
}
具体实现
private String getConfigInner(String tenant, String dataId, String group, long timeoutMs) throws NacosException {
group = this.blank2defaultGroup(group);
ParamUtils.checkKeyParam(dataId, group);
ConfigResponse cr = new ConfigResponse();
cr.setDataId(dataId);
cr.setTenant(tenant);
cr.setGroup(group);
String content = LocalConfigInfoProcessor.getFailover(this.worker.getAgentName(), dataId, group, tenant);
String encryptedDataKey;
if (content != null) {
LOGGER.warn("[{}] [get-config] get failover ok, dataId={}, group={}, tenant={}, config={}", new Object[]{this.worker.getAgentName(), dataId, group, tenant, ContentUtils.truncateContent(content)});
cr.setContent(content);
encryptedDataKey = LocalEncryptedDataKeyProcessor.getEncryptDataKeyFailover(this.agent.getName(), dataId, group, tenant);
cr.setEncryptedDataKey(encryptedDataKey);
this.configFilterChainManager.doFilter
以下是读取本地配置具体实现。
public static String getFailover(String serverName, String dataId, String group, String tenant) {
File localPath = getFailoverFile(serverName, dataId, group, tenant);
if (localPath.exists() && localPath.isFile()) {
try {
return readFile(localPath);
} catch (IOException var6) {
LOGGER.error("[" + serverName + "] get failover error, " + localPath, var6);
return null;
}
} else {
return null;
}
}
当本地文件为空时,则需要去向这个服务端的配置中心发起http请求,最后会通过这个接口回调来判断这个响应的状态码。
基本实现调getServerConfig方法。
public ConfigResponse getServerConfig(String dataId, String group, String tenant, long readTimeout, boolean notify) throws NacosException {
if (StringUtils.isBlank(group)) {
group = "DEFAULT_GROUP";
}
return this.agent.queryConfig(dataId, group, tenant, readTimeout, notify);
}
内部调queryConfig方法获取远端配置,这里就不具体细说了。
3.nacos的服务配置监听
在整个容器启动完成后,就会去调用这个监听器,nacos在NacosContextRefresher
类下实现监听。其实现了这个ApplicationListener这个接口,就是一个nacos的一个上下文的一个刷新流。
public NacosContextRefresher(NacosConfigManager nacosConfigManager, NacosRefreshHistory refreshHistory) {
this.nacosConfigProperties = nacosConfigManager.getNacosConfigProperties();
this.nacosRefreshHistory = refreshHistory;
this.configService = nacosConfigManager.getConfigService();
this.isRefreshEnabled = this.nacosConfigProperties.isRefreshEnabled();
}
public void onApplicationEvent(ApplicationReadyEvent event) {
if (this.ready.compareAndSet(false, true)) {
this.registerNacosListenersForApplications();
}
}
这个类里面,会调用一个onApplicationEvent的事件方法,里面就会去进行nacos的监听的一个注册。
private void registerNacosListenersForApplications() {
if (this.isRefreshEnabled()) {
Iterator var1 = NacosPropertySourceRepository.getAll().iterator();
while(var1.hasNext()) {
NacosPropertySource propertySource = (NacosPropertySource)var1.next();
if (propertySource.isRefreshable()) {
String dataId = propertySource.getDataId();
this.registerNacosListener(propertySource.getGroup(), dataId);
}
}
}
}
这个方法会去获取nacos的全部的配置文件,然后在获取dataId 之后,通过这个dataId对这个服务进行一个监听。
其中registerNacosListener
方法的具体实现如下,当配置发生变化的时候,这个监听方法就会发起一个调用。
private void registerNacosListener(final String groupKey, final String dataKey) {
String key = NacosPropertySourceRepository.getMapKey(dataKey, groupKey);
Listener listener = (Listener)this.listenerMap.computeIfAbsent(key, (lst) -> {
return new AbstractSharedListener() {
public void innerReceive(String dataId, String group, String configInfo) {
NacosContextRefresher.refreshCountIncrement();
NacosContextRefresher.this.nacosRefreshHistory.addRefreshRecord(dataId, group, configInfo);
NacosContextRefresher.this.applicationContext.publishEvent(new RefreshEvent(this, (Object)null, "Refresh Nacos config"));
if (NacosContextRefresher.log.isDebugEnabled()) {
NacosContextRefresher.log.debug(String.format("Refresh Nacos config group=%s,dataId=%s,configInfo=%s", group, dataId, configInfo));
}
}
};
});
try {
this.configService.addListener(dataKey, groupKey, listener);
log.info("[Nacos Config] Listening config: dataId={}, group={}", dataKey, groupKey);
} catch (NacosException var6) {
log.warn(String.format("register fail for nacos listener ,dataId=[%s],group=[%s]", dataKey, groupKey), var6);
}
}
最后会去调用一个refresh方法,会进行一个环境的刷新,将新的参数和原来的参数进行一个比较,通过发布这个环境变更事件,对做出改变的值进行一个更新操作。
总结:
就是这个客户端进行启动的时候,就会优先拉取本地的配置,如果本地配置不存在,那么就会和这个服务端建立这个http请求,然后去拉取这个服务端的全部配置,就是配置中心的全部配置。在拉取到全部配置之后,会去获取每一个配置文件的dataId,然后通过这个id对服务端的每一个配置文件进行一个监听的操作。当服务端这边的配置文件出现修改的时候,就可以通过这个监听器进行感知,然后这个客户端也会对对应的配置文件进行修改,每一份修改的配置都会存储在这个nacos配置文件里面,会作为一个历史文件保留。