本文主要学习和使用 nacos 配置中心,从 ncaos 配置中心原理出发,深入到个人项目中的nacos配置中心使用。
应用程序在启动和运行的时候往往需要读取一些配置信息,配置基本上伴随着应用程序的整个生命周期,比如:数据库连接参数、启动参数等。应用程序根据配置改变自身的行为,但一般程序不会去修改配置。
对于配置,大家在日常开发中应该很熟悉。配置的加载有多种方式,其中hard code在应用中的方式最不可取的,而配置文件、环境变量、启动参数、甚至数据库的方式,在微服务架构中多机器、多应用、多实例的情况下也有很大不足,会使得配置分散,不易于管理。想象一下,如果你启动了一个服务的多个实例,一旦配置需要更改,在没有配置中心的情况,你得将每个实例的配置信息改一遍,这将是非常繁琐且低效的工作。
配置中心就是一种统一管理各种应用配置的基础服务组件,它将将配置从各应用中剥离出来,对配置进行统一管理,应用自身不需要自己去管理配置。
一个合格的配置中心需要满足如下特性:
• 配置项容易读取和修改
• 分布式环境下应用配置的可管理性,即提供远程管理配置的能力
• 支持对配置的修改的检视以把控风险
• 可以查看配置修改的历史记录
不同部署环境下应用配置的隔离性
在这里,我们手动实现对 nacos 配置的相关操作,对后续的配置化理解有很大帮助。
首先要了解几个 Nacos 的关键信息:
nameSpace:命名空间,默认public,标识不同执行环境;如:dev,test,prod …
group :组标识,默认DEFAULT_GROUP,标识不同项目;如:ORDER_GROUP,USER_GROUP …
DataId:文件唯一标识,简单理解为文件名(在同组中唯一)。
如:dev_ORDER_GROUP_application.yaml -> dev环境_订单项目_ application.yaml配置文件
基础依赖 —— nacos 客户端
<dependency>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-client</artifactId>
<version>1.1.3</version>
</dependency>
public static void main(String[] args) throws NacosException, InterruptedException, UnsupportedEncodingException {
//使用nacos open api 服务获取相关配置
//namespace 区分命名空间(dev,test...)
String nameSpacePublic = ""; //命名空间区分配置所属环境(或多用户配置)
String nameSpaceDev = "76c8c145-bdc0-4bfc-965d-80337b21cdbf"; //命名空间区分配置所属环境(或多用户配置)
String nameSpaceTest = "766363ce-8b26-4882-b1d9-e12134d5cad7"; //命名空间区分配置所属环境(或多用户配置)
String nameSpaceProd = "d1888d9a-9911-4b6a-84ac-a689cd4c7c66";//命名空间区分配置所属环境(或多用户配置)
String group = "DEFAULT_GROUP"; //组标识可以区分不同的项目
String dataId = "nacos-config-demo.yaml"; //配置文件唯一标识
String nacosIp = "127.0.0.1"; //nacso 服务地址
String nacosPort = "8848"; //nacos 服务端口
Properties properties = new Properties(); // nacos配置信息
properties.put("serverAddr",nacosIp+":"+nacosPort);
properties.put("namespace",nameSpaceDev);
//链接nacos
ConfigService configService = NacosFactory.createConfigService(properties);
//获取配置 5000 超时时间
String config = configService.getConfig(dataId, group, 5000);
System.out.println(config);
//监听,当服务端配置改变时主动推送到客户端
configService.addListener(dataId, group, new Listener() {
@Override
public Executor getExecutor() {
return null;
}
//当配置变化时获取通知
@Override
public void receiveConfigInfo(String s) {
System.out.println("配置变化");
System.out.println(s);
}
});
// 测试让主线程不退出,因为订阅配置是守护线程,主线程退出守护线程就会退出。 正式代码中无需下面代码
while (true){
Thread.sleep(2000);
}
}
测试结果如下
这样,我们就能实现对 nacos 中配置的获取以及实时监听 相关配置的改变。
在 springboot 配置化实现nacos 基本原理如上。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
<version>2.1.0.RELEASE</version>
</dependency>
注意:nacos 配置需要配置到 bootstrap.yml 或 bootstrap.properties 文件中,而不能是application.yml/properties 文件。
在 SpringBoot 启动时对自定义配置文件有如下加载顺序:
- bootstrap.yml(或bootstrap.properties)优先加载。这个文件用于应用程序的引导阶段,通常包含系统级别的一些参数配置,这些参数一般是不变的。
- bootstrap.yml(或bootstrap.properties)由父Spring ApplicationContext加载,而父ApplicationContext被加载到使用application.yml(或application.properties)的之前。
- application.yml(或application.properties)随后加载。这个文件通常用来定义单个应用级别的配置,如果搭配Spring Cloud Config使用,可以定义动态替换的文件。
- application.yml(或application.properties)由当前的Spring ApplicationContext加载。
需要注意的是,
一旦bootstrap.yml
被加载,其内容不会被覆盖,即便后期加载的application.yml
的内容标签与bootstrap.yml
的标签一致,application.yml
也不会覆盖bootstrap.yml
。而application.yml
里面的内容可以动态替换。
综上所述,加载顺序为:bootstrap.yml(或bootstrap.properties)
优先加载,然后是bootstrap.yml(或bootstrap.properties)
,接着是application.yml(或application.properties),最后是application.yml(或application.properties)。这样的加载顺序允许基础设施配置先于应用程序配置加载,并且允许后面加载的配置覆盖先前加载的配置。
更正(2024.09.10)
加载顺序为:bootstrap.propertie > bootstrap.yml >application.properties > application.yml)
存在以下规律:1、同名配置按优先级覆盖;2、异名配置按后加载的覆盖。(即:application中的配置会覆盖 bootstrap 中的。propertie 中的配置会覆盖 yml 中的。即最终加载的配置以 application.propertie 为准)
补充:在对应 nacos 的使用还是需要配置在 bootstrap 配置文件中,需要在启动时读取 nacos 配置文件,从而获取 nacos 配置中心的配置文件。
综上所述:Nacos 配置文件必须放入到 bootstrap 文件中,如果放在 application 文件中会出现nacos 连接失败等初始化问题,导致系统无法正常启动。(简单理解为系统已经在加载 Application 配置了,但发现这些配置还没有。)
spring:
application:
name: prod-service
profiles:
active: dev
cloud:
nacos:
config:
server-addr: 127.0.0.1:8848,127.0.0.1:18848 #配置中心地址(集群)
file-extension: yaml #文件后缀 项目名+文件后缀 -》指定文件
namespace: d31a3f74-e122-41b3-b36e-0de805587fb3 #配置的环境(dev环境)
ext-config[0]: #额外的配置文件
data-id: testExt.properties
group: TEST_GROUP #所在组 不填则默认
refresh: true #是否支持动态刷新
ext-config[1]:
data-id: nacos-demo.yaml
ext-config[2]:
data-id: application.yaml #将相关配置内容放到 nacos 服务器上,读取就行
group: TEST_GROUP
refresh: true
#配置加载的优先级-命名冲突
# 1 基础的和扩展的存在命名冲突 - 以基础的为准
# 2 扩展的之间存在命名冲突 - ext-config[n] n的值越大优先级越高
配置文件解读:
server-addr :服务地址,可以是多个。nacos 集群启动需要修改nacos 启动方式。
file-extension :文件后缀。在搜索文件时会自动与服务名拼接成一个 dataId;
namespace:命名空间
group:组
ext-config[x]:扩展配置
shared-configs[x]:(本配置未使用) 共享配置
在此配置文件中一共会加载 5 个配置文件,分别是:
配置文件 | 来源 | 说明 |
---|---|---|
prod-service.yaml | {application.name}.{file-extension} | 绝对会获取的配置 |
prod-service-dev.yaml | {application.name}-{profiles.active}.{file-extension} | profiles.active 存在时获取 |
testExt.properties | ext-config[0] | 扩展配置 |
nacos-demo.yaml | ext-config[1] | 扩展配置 |
application.yaml | ext-config[2] | 扩展配置 |
说明:profiles.active
在本文中特意赋值为 dev。在这里需要和 namespace 的dev环境做区分,此处的 dev 仅会与服务名生成 prod-service-dev.yaml
配置。而并不能跨 namespace 去 namespace - dev 中获取。
ext-config[x] 与 shared-configs[x] 使用方法相同
都包含:data-id、group(默认为字符串DEFAULT_GROUP)、refresh(默认为true)
三个属性。
-refresh:是动态刷新,在Nacos修改配置后,服务可以动态感知而无需重启项目。
在不同配置文件中如果存在相同的配置项,优先级高的匹配文件会覆盖优先级低的。
相关配置优先级如下。
- userservice-dev.yml(当前环境配置profiles) > userservice.yml(nacos上的)> application.yml(本地的)
- 主配置 > 扩展配置(extension-configs) > 共享配置(shared-configs)。
- 同为扩展配置,存在如下优先级关系:extension-configs[3] > extension-configs[2] > extension-configs[1] > extension-configs[0]。
- 同为共享配置,存在如下优先级关系:shared-configs[3] > shared-configs[2] > shared-configs[1] > shared-configs[0]。
简单记为:定位越明确优先级越高,远程的优先级大于本地,数组编号越大优先级越高