Nacos配置中心组件学习
- 1. Nacos简介
- 1.1 Nacos是啥
- 1.2 作用
- 2. springCloud项目集成
- 2.1 maven依赖
- 2.2 Nacos配置相关参数
- 2.3 配置信息
- 2.5 配置使用
- 2.6 获取实时配置
- 3. nacos自动装配
- 3.1 配置加载原理
- 3.2 配置实时刷新原理
- 4. nacos配置中心原理
- 3.1. 动态配置管理
- 3.2. 配置存储与一致性
- 3.3. 长轮询与推送机制
- 3.4. 本地缓存与版本控制
- 3.5. 安全性与权限管理
1. Nacos简介
1.1 Nacos是啥
Nacos(Dynamic Naming and Configuration Service)是一个开源的服务发现和配置管理平台,主要用于微服务架构中。Nacos的名字源自于“Naming and Configuration Service”的缩写,它提供了两大核心功能:服务发现和服务配置。上篇博文Naocos注册中心。下面我们一起来学习有关Nacos另外一个重要特性Nacos配置中心。
1.2 作用
- 服务发现:
- 允许服务之间相互发现和通信,支持基于DNS和基于RPC的服务发现。
- 服务提供者在启动时注册自己,服务消费者通过服务名查找服务,实现服务之间的解耦。
- 服务配置:
- 动态配置服务是Nacos的另一个重要功能,允许在所有环境中以集中和动态的方式管理所有服务的配置。
- 配置的更改可以自动推送到使用该配置的服务,实现配置的热更新,无需重启服务。
- 集中管理:
- 提供一个中央配置管理服务来统一管理所有服务的配置,避免了配置文件分散在各个项目里导致的不便维护问题。
- 动态更新:
- 能够在不重启服务的情况下,动态地更新配置,提高了系统的灵活性和可维护性。
- 版本控制&:
- 配置的变更被版本化,以便追踪变更历史和回滚到之前的配置,增强了配置的管理能力。
- 权限控制:
- 对配置的访问进行权限控制,确保只有授权的用户可以修改配置,提高了配置的安全性。
- 高可用性和故障转移:
- Nacos服务具有高可用性,能够在部分故障的情况下继续工作,并且能够从故障中恢复,保证了系统的稳定运行。
- 多环境配置:
- 支持多环境配置,如开发、测试和生产环境,可以实现环境隔离和配置加密,确保配置的正确性和安全性。
- 简化服务治理:
- Nacos无缝支持一些主流的开源生态,如Kubernetes Service、gRPC&Dubbo RPC Service、Spring Cloud RESTful Service等,使得服务治理更加容易和高效。
2. springCloud项目集成
前期准备:部署nacos服务,参考:Naocos注册中心
2.1 maven依赖
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
2.2 Nacos配置相关参数
通常使用bootstrap.properties
或bootstrap.yml
文件来配置Nacos的相关参数
spring:
application:
name: order-server
cloud:
nacos:
#nacos配置中心配置信息
config:
prefix: customer-server
namespace: nacos-xiu-dev
server-addr: 127.0.0.0:8848
file-extension: yml
# username: name
# password: pwd!
#nacos服务中心配置
discovery:
server-addr: 127.0.0.0:8848
namespace: nacos-xiu-dev
# username: name
# password: pwd!
在 Nacos Spring Cloud 中,dataId
的完整格式如下:
${prefix}-${spring.profiles.active}.${file-extension}
prefix
默认为spring.application.name
的值,也可以通过配置项spring.cloud.nacos.config.prefix
来配置。spring.profiles.active
即为当前环境对应的 profile,注意:当spring.profiles.active
为空时,对应的连接符-
也将不存在,dataId 的拼接格式变成${prefix}.${file-extension}
file-exetension
为配置内容的数据格式,可以通过配置项spring.cloud.nacos.config.file-extension
来配置。目前只支持properties
和yaml
类型。
按照上述逻辑我们环境为dev在nacos服务里的文件为 命名空间:nacos-xiu-dev
文件名称: customer-server-dev.yml
。
2.3 配置信息
在nacos服务上添加配置信息如下:
nacos:
olympic:
games:
pingpangball: 马龙,樊振东,孙颖莎
dive: 全红婵,陈芋汐
shoot: 黄雨婷,盛李豪
配置项 | 说明 | 使用场景 |
---|---|---|
命名空间(Namespace) | 用于进行不同环境的配置隔离,如开发环境、测试环境和生产环境等。 不同的命名空间下,可以存在相同名称的配置分组(Group)或配置集(Data ID)。 | 1.隔离不同环境的配置,防止环境间的配置相互干扰。 2.支持多租户配置隔离,不同租户可以使用相同的Nacos集群,但通过命名空间进行隔离 |
配置分组(Group) | 用于对配置集进行分组,不同的配置分组下可以有相同的配置集(Data ID)。 | 根据功能模块或组件进行配置分组,以便于管理。 |
配置集 (Data ID) | 一个配置文件就是一个配置集,包含了系统各个方面的配置信息。配置集通过Data ID来唯一标识。 | 配置文件,最细粒度的配置信息,开发最长接触的 |
多格式支持 | Nacos支持多种文件格式的配置项,包括JSON、XML、YAML、Properties、Text等。 | 常用的为 yaml文件、properties文件 |
2.5 配置使用
在你的Spring Boot应用中,你可以通过@Value
注解或@ConfigurationProperties
来注入这些配置。
@Value注入配置
@Value("${nacos.olympic.games.pingpangball}")
private String pingpangBall;
@GetMapping("/getOlympicName")
public String test() {
log.info("pingpangBall:{}",pingpangBall);
return "伦敦奥运会";
}
@ConfigurationProperties注入配置
//注意必须要有set/get方法
@Data
@Component
//设置配置信息前缀
@ConfigurationProperties(prefix = "nacos.olympic.games")
public class OlympicConfig implements Serializable {
/**
* 乒乓球
*/
private String pingpangBall;
/**
* 跳水
*/
private String dive;
/**
* 射击
*/
private String shoot;
}
使用
@Resource
private OlympicConfig olympicConfig;
@GetMapping("/getOlympicName")
public String test() {
log.info("config:{}", JsonUtil.toJson(olympicConfig));
return "伦敦奥运会";
}
2.6 获取实时配置
-
启用配置刷新
- 在
application.yml
或application.properties
文件中添加配置,启用Nacos配置的刷新功能。例如,在application.properties
中添加spring.cloud.nacos.config.refresh.enabled=true
。默认配置刷新已启用
- 在
-
使用@RefreshScope注解
- 在需要实时刷新配置的Bean上添加
@RefreshScope
注解。这个注解会告诉Spring Cloud在配置发生变化时,重新创建这个Bean,并注入最新的配置信息。
@Data @Component @ConfigurationProperties(prefix = "nacos.olympic.games") //启用实时刷新 @RefreshScope public class OlympicConfig implements Serializable { /** * 乒乓球 */ private String pingpangBall; /** * 跳水 */ private String dive; /** * 射击 */ private String shoot; }
- 在需要实时刷新配置的Bean上添加
Nacos实时刷新的原理:
Nacos实时刷新的原理主要依赖于客户端与Nacos服务器之间的长轮询机制。当Nacos客户端启动时,它会向Nacos服务器发起请求,订阅自己关心的配置信息。Nacos服务器会保持这个连接,并在配置信息发生变化时,主动将最新的配置信息推送给客户端。客户端在接收到新的配置信息后,会更新本地缓存,并通知应用程序使用最新的配置。
@RefreshScope 注解的工作原理
作用在类上 | 作用在方法上 |
---|---|
当@RefreshScope 注解被应用在一个类上时,Spring Cloud会为这个类创建一个代理Bean。这个代理Bean会在每次被调用时检查配置是否已更改。如果配置已更改,则Spring Cloud会重新创建实际的Bean实例,并将其注入到应用程序中。 | 被用在方法上(尽管在实际应用中这种用法并不常见)。然而,需要注意的是,当@RefreshScope 作用在方法上时,它可能并不会像作用在类上时那样直接重新创建Bean实例,而是可能影响到方法调用时的某些行为或结果。但具体的行为可能会根据Spring Cloud的版本和配置而有所不同。 |
3. nacos自动装配
查看spring-cloud-starter-alibaba-nacos-config
maven依赖中的spring.factories中的自动装配类(如下配置类中去掉了重复的配置)
自动装配类 | 创建的bean实例 | bean实例的作用 |
---|---|---|
NacosConfigBootstrapConfiguration | NacosConfigProperties | 用于封装Nacos配置中心的配置信息,如服务器地址、命名空间、组名等。 加载方式:通过 @ConfigurationProperties 注解从spring.cloud.nacos.config 路径下的配置文件中读取配置项。 |
NacosConfigManager | 作为Nacos配置中心的顶层接口ConfigService 的封装,提供配置获取、监听等功能。 | |
NacosPropertySourceLocator | 负责从Nacos配置中心加载配置信息,并将这些配置信息封装成PropertySource对象,进而添加到Spring的Environment中,供应用程序使用 | |
NacosConfigAutoConfiguration | NacosRefreshProperties | 控制Nacos配置中心的配置刷新行为,包括是否启用自动刷新、刷新策略的配置 |
NacosRefreshHistory | 它负责记录并存储配置刷新的历史信息。通过提供详细的历史记录查询功能 | |
NacosContextRefresher | nacosContextRefresher类在Nacos配置中心中起到了桥梁和纽带的作用,它通过监听配置变化、触发刷新操作以及发布刷新事件等方式,实现了配置的动态更新和应用程序的灵活调整。 | |
NacosConfigEndpointAutoConfiguration | NacosConfigEndpoint | NacosConfigEndpoint类通过提供Actuator端点、配置信息查看、动态刷新支持等功能,为Spring Cloud Alibaba项目中的Nacos配置中心提供了丰富的监控和管理手段 |
NacosConfigHealthIndicator | 它主要用于监测Nacos配置中心的健康状态 | |
NacosConnectionFailureAnalyzer | 无 | nacos客户端(应用程序)连接nacos服务端失败信息的分析(日志打印) |
3.1 配置加载原理
从上面的配置可以知道,nacos配置中心中配置加载的入口是
- 应用启动时,Spring Cloud Alibaba通过
BootstrapApplicationContext
来加载外部配置。在这个过程中,NacosPropertySourceLocator
会被触发,从Nacos Server拉取配置。 NacosPropertySourceLocator
通过ConfigServiceProxy
与Nacos Server进行通信,获取配置信息,并将这些信息封装成PropertySource
对象,然后添加到Spring的Environment
中。- 接下来我们就可以像在spring中使用普通配置方式一样了。比如
@Value
注解或@ConfigurationProperties
来注入这些配置。
下面我们来看一看NacosPropertySourceLocator具体实现。
locate方法
locate
方法是在Spring Cloud Alibaba的Nacos配置管理中使用的,用于将Nacos中的配置加载到Spring环境中
@Override
public PropertySource<?> locate(Environment env) {
//设置环境变量(方便后续使用不同的环境(profiles)加载配置)
nacosConfigProperties.setEnvironment(env);
//基于nacos配置信息 通过nacosConfigManager获取ConfigService实例,这是与Nacos服务器进行交互的关键组件
ConfigService configService = nacosConfigManager.getConfigService();
if (null == configService) {
log.warn("no instance of config service found, can't load config from nacos");
return null;
}
//设置获取配置的超时时间
long timeout = nacosConfigProperties.getTimeout();
nacosPropertySourceBuilder = new NacosPropertySourceBuilder(configService,
timeout);
//获取配置的dataId的前缀 正确地定位到Nacos中的配置。
//一般为:`prefix` 默认为 `spring.application.name` 的值,也可以通过配置项 `spring.cloud.nacos.config.prefix`来配置
String name = nacosConfigProperties.getName();
String dataIdPrefix = nacosConfigProperties.getPrefix();
if (StringUtils.isEmpty(dataIdPrefix)) {
dataIdPrefix = name;
}
if (StringUtils.isEmpty(dataIdPrefix)) {
dataIdPrefix = env.getProperty("spring.application.name");
}
//创建CompositePropertySource 这个对象将用于存储从Nacos加载的所有配置属性源。
CompositePropertySource composite = new CompositePropertySource(
NACOS_PROPERTY_SOURCE_NAME);
//加载共享配置 其配置信息跨多个应用或服务的通用配置。
//spring.cloud.nacos.config.shared-configs 指定
loadSharedConfiguration(composite);
//加载扩展配置 其配置信息包含一些特定于某个服务或功能的配置
loadExtConfiguration(composite);
//加载应用配置 常见的配置信息
loadApplicationConfiguration(composite, dataIdPrefix, nacosConfigProperties, env);
//返回加载完成后的配置对象
return composite;
}
共享配置、扩展配置
加载配置信息
其实不管是loadSharedConfiguration、loadExtConfiguration、loadApplicationConfiguration最终都是调用loadNacosDataIfPresent方法加载配置。这里我们以最常见的设置配置的方式loadApplicationConfiguration方法进行分析。
//加载常用配置
private void loadApplicationConfiguration(
CompositePropertySource compositePropertySource, String dataIdPrefix,
NacosConfigProperties properties, Environment environment) {
//获取配置文件的文件类型 比如yaml、properties等 用于读取到数据的格式化
String fileExtension = properties.getFileExtension();
//获取配置分组
String nacosGroup = properties.getGroup();
//加载默认配置文件 dataId= dataIdPrefix
loadNacosDataIfPresent(compositePropertySource, dataIdPrefix, nacosGroup,
fileExtension, true);
// load with suffix, which have a higher priority than the default
//加载默认配置文件 dataId= dataIdPrefix
loadNacosDataIfPresent(compositePropertySource,
dataIdPrefix + DOT + fileExtension, nacosGroup, fileExtension, true);
// Loaded with profile, which have a higher priority than the suffix
for (String profile : environment.getActiveProfiles()) {
String dataId = dataIdPrefix + SEP1 + profile + DOT + fileExtension;
loadNacosDataIfPresent(compositePropertySource, dataId, nacosGroup,
fileExtension, true);
}
dataIdPrefix: 默认为 spring.application.name
的值,也可以通过配置项 spring.cloud.nacos.config.prefix
来配置。
以上述的示例代码为例子:spring.application.name=customer-server,profiles=dev
上述代码加载三种类型的配置信息(dataId) customer-server、customer-server.yaml、customer-server-dev.yaml,配置优先级从高到低
为customer-server-dev.yaml —> customer-server.yaml —> customer-server.
具体加载实现
1. loadNacosDataIfPresent方法核心实现
//加载配置为NacosPropertySource(包含配置信息)
NacosPropertySource propertySource = this.loadNacosPropertySource(dataId, group,
fileExtension, isRefreshable);
//放入composite的配置列表的头部 这表明后加载的配置文件优先级更高
this.addFirstPropertySource(composite, propertySource, false);
2.loadNacosPropertySource方法核心实现
NacosPropertySource build(String dataId, String group, String fileExtension,
boolean isRefreshable) {
//从nacos服务端加载配置
Map<String, Object> p = loadNacosData(dataId, group, fileExtension);
//将配置构造成NacosPropertySource
NacosPropertySource nacosPropertySource = new NacosPropertySource(group, dataId,
p, new Date(), isRefreshable);
//存储到map中
NacosPropertySourceRepository.collectNacosPropertySource(nacosPropertySource);
return nacosPropertySource;
}
3. loadNacosData核心实现
private Map<String, Object> loadNacosData(String dataId, String group,String fileExtension) {
String data = null;
try {
//从nacos服务端获取配置信息(字符串形式)
data = configService.getConfig(dataId, group, timeout);
//省略日志打印
//根据配置文件类型将字符串类型的配置信息转换成map
Map<String, Object> dataMap = NacosDataParserHandler.getInstance()
.parseNacosData(data, fileExtension);
return dataMap == null ? EMPTY_MAP : dataMap;
}
catch (NacosException e) {
//省略异常处理
}
return EMPTY_MAP;
}
3.2 配置实时刷新原理
Nacos 服务器端负责存储配置数据,并提供配置监听和推送的功能。该功能是nacos实时刷新得以实现的基础。
- 长轮询支持:Nacos 服务器端支持客户端的长轮询请求,即客户端发起一个请求后,服务器会保持这个连接打开,直到有配置更新或超时。这种方式可以减少无效的轮询请求,提高性能。
- 数据变更通知:当配置数据发生变化时,Nacos 服务器端会记录这些变化,并通知所有正在等待的客户端。这通常通过维护一个客户端连接列表,并在数据变化时遍历这个列表来实现。
- nacos与spring cloud整合,nacos基于spring的事件发布和监听机制,会在spring启动后注册一个配置变化的事件监听。其启作用的为NacosContextRefresher。
NacosContextRefresher
spring启动时候注册配置变更事件
@Override
public void onApplicationEvent(ApplicationReadyEvent event) {
// many Spring context
if (this.ready.compareAndSet(false, true)) {
//注册nacos配置变更事件
this.registerNacosListenersForApplications();
}
}
private void registerNacosListenersForApplications() {
//是否开启自动刷新 默认为true
if (isRefreshEnabled()) {
//针对需要加载的所有配置文件都分别进行事件监听
for (NacosPropertySource propertySource : NacosPropertySourceRepository
.getAll()) {
if (!propertySource.isRefreshable()) {
continue;
}
//以上面示例代码为例 分别对customer-server、customer-server.yaml、customer-server-dev.yaml、
String dataId = propertySource.getDataId();
//添加事件监听
registerNacosListener(propertySource.getGroup(), dataId);
}
}
}
private void registerNacosListener(final String groupKey, final String dataKey) {
String key = NacosPropertySourceRepository.getMapKey(dataKey, groupKey);
//创建监听配置变化的监听器,其方法innerReceive即为配置变更处发的逻辑
Listener listener = listenerMap.computeIfAbsent(key,
lst -> new AbstractSharedListener() {
@Override
public void innerReceive(String dataId, String group,
String configInfo) {
refreshCountIncrement();
nacosRefreshHistory.addRefreshRecord(dataId, group, configInfo);
// todo feature: support single refresh for listening
applicationContext.publishEvent(
new RefreshEvent(this, null, "Refresh Nacos config"));
if (log.isDebugEnabled()) {
log.debug(String.format(
"Refresh Nacos config group=%s,dataId=%s,configInfo=%s",
group, dataId, configInfo));
}
}
});
try {
//为nacos服务添加事件监听器
configService.addListener(dataKey, groupKey, listener);
}
catch (NacosException e) {
log.warn(String.format(
"register fail for nacos listener ,dataId=[%s],group=[%s]", dataKey,
groupKey), e);
}
}
这段代码展示了如何在Spring Cloud Alibaba的Nacos配置中心中注册一个监听器,以便在配置发生变化时能够接收到通知并执行相应的更新逻辑
- 构建配置变更事件监听器
- 为nacos服务添加事件监听器
innerReceive方法
该方法三个参数, 当某个分组group
下的某个配置dataId
发生变化则会将最新的配置信息configInfo
传递到该方法中
public void innerReceive(String dataId, String group,
String configInfo) {
//累加配置变更次数
refreshCountIncrement();
//记录此次变化到历史中,便于追溯历史变更记录
nacosRefreshHistory.addRefreshRecord(dataId, group, configInfo);
//发布一个刷新事件,接受到该事件的组件即可获取到最新配置
//比如@RefreshScope底层逻辑肯定是消费了该RefreshEvent事件 从而获取到最新变化配置
applicationContext.publishEvent(
new RefreshEvent(this, null, "Refresh Nacos config"));
if (log.isDebugEnabled()) {
log.debug(String.format(
"Refresh Nacos config group=%s,dataId=%s,configInfo=%s",
group, dataId, configInfo));
}
}
4. nacos配置中心原理
Nacos配置中心的工作原理主要围绕动态配置管理、配置存储与一致性、长轮询与推送机制以及本地缓存与版本控制等核心原理展开。以下是详细的工作原理介绍:
3.1. 动态配置管理
- 功能概述:Nacos配置中心提供了一种集中式的动态配置管理功能,允许开发者实现配置的集中管理、动态更新和实时推送。这意味着当配置信息发生变化时,Nacos能够自动将更新推送给所有相关的服务实例,无需重启服务即可使新配置生效。
- 技术实现:Nacos通过集中存储配置信息(如使用MySQL等数据库)来确保配置数据的安全性和持久性。同时,它支持多种配置格式(如Properties、YAML等),以满足不同应用场景下的配置需求。
3.2. 配置存储与一致性
- 配置存储:Nacos采用集中式的存储方式,将配置信息存储在可靠的存储介质中。这种集中存储方式使得配置信息的管理更加便捷和统一。
- 一致性保证:在集群部署模式下,Nacos通过一致性协议(如Raft协议)来保证配置数据在不同节点之间的一致性。即使在部分节点发生故障的情况下,也能保证配置数据的完整性和准确性。
3.3. 长轮询与推送机制
- 长轮询机制:Nacos客户端通过长轮询机制与服务器保持连接,以实时获取配置更新。当配置发生变化时,服务器会主动将更新推送给客户端,而不是让客户端定时轮询查询。这种推送机制大大减少了客户端与服务器之间的通信次数,提高了配置更新的实时性和效率。
- 推送机制:推送可以通过HTTP长轮询或WebSocket等方式实现。使用WebSocket可以更加实时地推送配置变更,但需要确保网络环境支持WebSocket。
3.4. 本地缓存与版本控制
- 本地缓存:为了提高性能并减少对服务器的访问压力,Nacos客户端会将获取到的配置信息缓存在本地。当本地缓存中的配置信息与服务器上的配置信息不一致时,客户端会重新从服务器拉取最新的配置信息。
- 版本控制:Nacos为每个配置项分配一个唯一的版本号。当配置发生变化时,版本号也会随之更新。客户端在发起请求时可以携带自己的配置版本号,以便服务器判断是否需要推送新的配置信息给客户端。
3.5. 安全性与权限管理
- 安全性:Nacos提供了多种安全措施来保护配置信息的安全性,如数据加密、访问控制等。
- 权限管理:Nacos实现了权限管理功能,允许管理员在控制台创建不同的账户,并分配不同的权限(如读写、只读)。这样可以确保只有具有相应权限的用户才能访问或修改配置信息。