1.本次文章主要介绍如何使用Nacos作为项目的配置中心。
研发环境如下:
SpringCloud、SpringBoot和spring-cloud-alibaba本版如下
- spring-cloud:2021.0.6
- spring-cloud-alibaba: 2021.0.5.0
- springBoot: 2.7.14
2. 为什么需要配置中心
标签:架构,spring,配置,Nacos,nacos,利器,user,cloud From: https://blog.51cto.com/maguobin/7414170在微服务架构中,系统被拆分成多个小型、独立的服务,每个服务都运行在自己的容器中。这种服务拆分的方式使得整个系统更加灵活、易于维护和扩展,但同时也增加了配置管理的复杂度。
随着服务数量的增加,系统中的配置文件数量也会呈现指数级增长,如果使用传统的手动配置的方式,将是一项极为耗时和繁琐的工作。而且当服务需要进行修改或者更新时,也需要重新部署整个服务,会导致系统的不可用性,进而影响业务的正常运行。
因此我们需要一个类似注册中心的中间件,配置中心和应用中解耦,应用不再完全读取本地配置(部分不会频繁热更新的参数仍应用自身维护),而是直接拉取配置中心的信息作为应用参数,后续执行自己的业务流程。配置中心实现对于配置文件的版本控制、动态修改及实时推送等功能,有助于提高配置管理的效率和准确性,同时也方便开发人员对于配置信息进行统一管理和维护
3.Nacos配置管理
Nacos除了可以做注册中心,同样可以做配置管理来使用。
3.1. Nacos统一配置管理
Nacos一方面将配置进行集中管理,另一方面可以在配置变更的时候,及时的通知微服务,实现配置热更新
3.2. 在Nacos中添加配置文件
Nacos实现配置中心较为简单,首先在服务端新增一个配置信息(profile指项目运行环节:dev(开发环境)/test(测试环境)/pro(生产环境))
然后在弹出的表单中,填写配置信息:
pattern: dateformat: yyyy-MM-dd HH:mm:ss
注意:项目的核心配置,需要热更新的配置才有放到nacos管理的必要。基本不会变更的一些配置还是保存在微服务本地比较好
3.3. 微服务从Nacos拉取配置信息
微服务要拉取nacos中管理的配置,并且与本地的application.yml配置合并,才能完成项目启动。
思考:如果尚未读取application.yml,又如何得知nacos地址呢?
答案:spring引入了一种新的配置文件:bootstrap.yml文件,会在application.yml之前被读取
流程如下:
3.4.微服务实现拉取Nacos配置中心步骤
【步骤0】:项目准备
在讲解Nacos配置中心的时候,需要创建一个项目进行演示,和之前的项目区别不大,可是为了区分,我还是重新创建了新的项目
day30-cloud-nacos-config
,不具体演示如何创建项目,大家可以去github地址查看https://github.com/bangbangzhou/learn_springboot/tree/main/day30-cloud-nacos-config
【步骤一】:引入nacos-config依赖
在user-service中的pom.xml中添加nacos-config依赖
<dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> </dependency>
【步骤二】:新增boostrap.yaml文件
在user-service服务中的
resuorces
目录下添加bootstrap.yml文件,内容如下:spring: profiles: active: dev application: name: user-service cloud: nacos: discovery: server-addr: 127.0.0.1:8848 config: file-extension: yaml # 文件后缀 server-addr: 127.0.0.1:8848
此时应用就会使用以下规则作为文件的DataId匹配对应的配置文件:
{spring.application.name}-${spring.procile.active}.${spring.cloud.nacos.config.file-extension}
以本例为准读取的即为:user-service-dev.yaml
【步骤三】:应用读取nacos配置
对于nacos的配置其使用也做到了开箱即用,只需借助@Value注解即可,我们在UserController做如下添加
@Value("${pattern.dateformat}") private String dateformat; @GetMapping("/now") public String now(){ return LocalDateTime.now().format(DateTimeFormatter.ofPattern(dateformat)); }
【步骤四】:启动项目发现出错
错误原因解析:
从Spring Boot 2.4版本开始,配置文件加载方式进行了重构。另外也有配置的默认值变化,原来加载bootstrap.yml文件的默认开关由 true 变更为 false
SPringBoot2.4以前加载bootstrap.yml源码展示,默认ture
package org.springframework.cloud.bootstrap; public class BootstrapApplicationListener implements ApplicationListener<ApplicationEnvironmentPreparedEvent>, Ordered { public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) { ConfigurableEnvironment environment = event.getEnvironment(); if (!environment.getProperty("spring.cloud.bootstrap.enabled", Boolean.class, true)) { return; } } }
SpringBoot2.4版本以后,默认false
package org.springframework.cloud.util; import org.springframework.core.env.Environment; import org.springframework.util.ClassUtils; public abstract class PropertyUtils { public static final String BOOTSTRAP_ENABLED_PROPERTY = "spring.cloud.bootstrap.enabled"; public static boolean bootstrapEnabled(Environment environment) { return environment.getProperty(BOOTSTRAP_ENABLED_PROPERTY, Boolean.class, false) || MARKER_CLASS_EXISTS; } }
【步骤五】:解决启动项目错误
官网描述如下:
从官网解释我们发现,两种解决方式:
- 方案一,添加spring-cloud-starter-bootstrap依赖,到pom.xml(推荐使用)
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-bootstrap</artifactId> </dependency>
- 方案二,通过设置环境变量和系统变量的方式显示配置启动加载bootstrap.yml文件,具体配置参数和值如下:
spring.cloud.bootstrap.enabled=true
【步骤六】:启动项目,访问localhost:8088/user/now
在页面访问,可以看到效果:
4. Nacos配置热更新
如果只是做到了配置中心,显然对于Nacos的配置中心的能力是有所低估的,nacos可以做到毫秒级配置热更新,微服务中无需重启即可让配置生效,也就是配置热更新。
4.1. @RefreshScope实现配置热更新
在@Value注入的变量所在类上添加注解@RefreshScope:
修改后我们重启应用并发完一次,发现访问效果没变,此时我们修改配置文件如下,并点击发布.
注意:此时我们做了两件事情:①修改配置 ②发布配置,但是没有重启应用 再次访问,发现配置已及时生效,如下所示,感兴趣的也可以做更多格式的尝试,会发现都是及时生效
4.2. @ConfigurationProperties注解替代@Value实现配置热更新
我们在user-service工程中新增一个配置类,用于读取此例中配置信息,代码如下:
@Data @Component @ConfigurationProperties(prefix = "pattern") public class PatternProperties { private String dateformat; }
在UserController中使用这个类代替@Value:
// @Value("${pattern.dateformat}") // private String dateformat; @Autowired private PatternProperties patternProperties; @GetMapping("now") public String now(){ return LocalDateTime.now().format(DateTimeFormatter.ofPattern(patternProperties.getDateformat())); }
注意:此配置类我们约定死了配置的前缀,即默认:约定大于配置,后续此配置类将只获取pattern开头的配置信息,通过拼接属性值得到完整的配置信息,此例中即:前缀(pattern)+ 属性(dateformat),通过此唯一key去nacos获取配置信息。重启应用后发现同样生效。
5. 配置共享
对于微服务部署情况下,每个应用都会配置多个环境的配置文件,通过不同环境的active标识获取指定环境的配置信息,但也存在部分配置文件没有profile信息,例如:
- [spring.application.name]-[spring.profiles.active].yaml,例如:userservice-dev.yaml
- [spring.application.name].yaml,例如:userservice.yaml 此时[spring.application.name].yaml 因不包含环境信息,将被多个环境所共享。下面我们实例展示
5.1.添加共享文件配置
在nacos服务端新增一个:user-service.yaml文件,此文件没标识任何环境信息
pattern: sharedValue: 共享配置文件
5.2.服务读取共享配置
- 1.首先在配置文件类中增加新增配置内容
@Data @Component @ConfigurationProperties(prefix = "pattern") public class PatternProperties { private String dateformat; private String sharedValue; }
- 新增一个http接口到UserController,用来访问整个配置信息
@GetMapping("prop") public PatternProperties prop(){ return patternProperties; }
5.3. 运行两个UserConfigApplication,使用不同的profile
修改UserConfigApplication2这个启动项,改变其profile值:
这样,UserConfigApplication(8088)使用的profile是dev,UserConfigApplication2(8089)使用的profile是test。启动UserConfigApplication和UserConfigApplication2
访问http://localhost:8088/user/prop,结果
访问http://localhost:8089/user/prop,结果:
可以看出来,不管是dev,还是test环境,都读取到了sharedValue这个属性的值。
6. Nacos配置共享优先级
对于本地配置、共享配置、服务名-profile配置优先级有如下规则关系:
为了验证效果,我们同时在三个文件中增加一组配置信息,并进行验证:
- 本地新增
pattern: name: pattern本地配置
- user-service.yaml新增并发布
pattern: sharedValue: 共享配置文件 name: user-service共享配置信息
- user-service-dev.yaml新增
pattern: dateformat: yyyy年MM月dd HH:mm:ss name: user-service-dev共享配置
- 在PatternProperties类中新增属性值
private String name;
- 重新启动,并验证 访问http://localhost:8088/user/prop,结果
访问http://localhost:8089/user/prop,结果:
7. 总结
在文需要大家重点关注Nacos实现配置中心的步骤、热更新的实现方式,以及多配置文件的优先级顺序(后续生产发现配置信息读取不一致可借助此进行排查)