一、服务配置中心
先我们来看一下,微服务架构下关于配置文件的一些问题:
-
配置文件相对分散。在一个微服务架构下,配置文件会随着微服务的增多变的越来越多,而且分散在各个微服务中,不好统一配置和管理。
-
配置文件无法区分环境,开发环境、测试环境、线上环境。微服务项目可能会有多个环境,例如:测试环境、预发布环境、生产环境。每一个环境所使用的配置理论上都是不同的,一旦需要修改,就需要我们去各个微服务下手动维护,这比较困难。
-
配置文件无法实时更新。我们修改了配置文件之后,必须重新启动微服务才能使配置生效,这对一个正在运行的项目来说是非常不友好的。
基于上面这些问题,我们就需要配置中心的加入来解决这些问题。
配置中心的思路是:
- 首先把项目中各种配置全部都放到一个集中的地方进行统一管理,并提供一套标准的接口。
- 当各个服务需要获取配置的时候,就来配置中心的接口拉取自己的配置。
- 当配置中心中的各种参数有更新的时候,也能通知到各个服务实时的过来同步最新的信息,使之动态更新。
二、Nacos配置中心
Nacos
提供用于存储配置和其他元数据的 key/value
存储,为分布式系统中的外部化配置提供服务器端和客户端支持。
使用 Spring Cloud Alibaba Nacos Config
,您可以在 Nacos Server
集中管理你 Spring Cloud
应用的外部属性配置。
Spring Cloud Alibaba Nacos Config
是 Config Server
和 Client
的替代方案,客户端和服务器上的概念与 Spring Environment
和 PropertySource
有着一致的抽象,在特殊的 bootstrap
阶段,配置被加载到 Spring
环境中。当应用程序通过部署管道从开发到测试再到生产时,您可以管理这些环境之间的配置,并确保应用程序具有迁移时需要运行的所有内容。
三、准备条件
已完成Nacos服务端部署,本章基于上章实现的Nacos集群为基础,详情请见:nacos 单机集群搭建及常用生产环境配置 | Spring Cloud 3
已完成SpringCloud初始项目的手脚架搭建,详情请见:Spring Cloud入门篇 Hello World | Spring Cloud 1
四、项目搭建
4.1 项目目录结构
- 模块
nacos-config
演示如何使用Nacos Config Starter
完成Spring Cloud
应用的配置管理
4.2 pom.xml配置
在
spring cloud alibaba 2021.0.1.0
版本后,spring-cloud-starter-alibaba-nacos-config
模块移除了spring-cloud-starter-bootstrap
依赖,如果你想以旧版的方式使用,你需要手动加上该依赖。
本示例以旧版的方式使用展开,后续补充新版本特性及升级注意事项
本示例版本说明:
<spring-boot.version>2.7.9</spring-boot.version>
<spring-cloud.version>2021.0.5</spring-cloud.version>
<spring-cloud-alibaba.version>2021.0.4.0</spring-cloud-alibaba.version>
nacos-config/pom.xml
完整配置如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>com.gm</groupId>
<artifactId>springboot-cloud-example</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>nacos-config</artifactId>
<dependencies>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!--在`spring cloud alibaba 2021.0.1.0`版本后,`spring-cloud-starter-alibaba-nacos-config` 模块移除了 `spring-cloud-starter-bootstrap` 依赖-->
<!--如使用bootstrap.yml配置需添加以下依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
4.3 配置文件
nacos-config/src/main/resources/bootstrap.yml
server:
port: 3001
spring:
profiles:
active: dev
application:
name: @artifactId@
cloud:
nacos:
username: @nacos.username@
password: @nacos.password@
config:
server-addr: 192.168.0.31:8848
prefix: @artifactId@
file-extension: yaml
config:
name: bootstrap
nacos-config/src/main/resources/bootstrap-dev.yml
config:
name: bootstrap-dev
4.4 常见示例
com/gm/demo/nacos_config/component/ConfigListener.java
监听配置信息的例子
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import javax.annotation.PostConstruct;
import com.alibaba.cloud.nacos.NacosConfigManager;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.config.listener.Listener;
import com.alibaba.nacos.api.exception.NacosException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Slf4j
@Component
public class ConfigListener {
/**
* Nacos group.
*/
public static final String GROUP = "DEFAULT_GROUP";
@Autowired
private NacosConfigManager nacosConfigManager;
@Value("${spring.cloud.nacos.config.prefix}")
private String prefix;
@Value("${spring.cloud.nacos.config.file-extension}")
private String fileExtension;
@PostConstruct
public void init() throws NacosException {
String dataId = prefix + "." + fileExtension;
log.error("add listener [dataId]:" + dataId);
ConfigService configService = nacosConfigManager.getConfigService();
configService.addListener(dataId, GROUP, new Listener() {
@Override
public Executor getExecutor() {
return Executors.newSingleThreadExecutor();
}
@Override
public void receiveConfigInfo(String content) {
log.info("[dataId]:[" + dataId + "],Configuration changed to:");
log.error("\n" + content);
}
});
}
}
com/gm/demo/nacos_config/model/ConfigInfo.java
通过将配置信息配置为bean,支持配置变自动刷新的例子
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Data
@Component
@ConfigurationProperties(prefix = "config")
public class ConfigInfo {
private String name;
}
com/gm/demo/nacos_config/controller/ConfigController.java
对接 nacos
接口通过接口完成对配置信息新增/替换、查询和其他示例补充
一般都是通过
@Value
的形式读取配置信息,但是无法感知修改后的值,需要利用@RefreshScope
动态刷新。
import com.alibaba.cloud.nacos.NacosConfigManager;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.exception.NacosException;
import com.gm.demo.nacos_config.model.ConfigInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RefreshScope
@RestController
@RequestMapping("/config")
public class ConfigController {
@Autowired
private ConfigInfo configInfo;
@Value("${config.name:}")
private String name;
@Autowired
private NacosConfigManager nacosConfigManager;
@Value("${spring.cloud.nacos.config.prefix}")
private String prefix;
@Value("${spring.cloud.nacos.config.file-extension}")
private String fileExtension;
public static final String GROUP = "DEFAULT_GROUP";
/**
* 通过将配置信息配置为bean,支持配置变自动刷新
*
* @return
*/
@RequestMapping("/model")
public ConfigInfo model() {
return configInfo;
}
/**
* 通过 @Value 注解进行配置信息获取
*
* @return
*/
@RequestMapping("/value")
public String value() {
return name;
}
/**
* 对接 nacos 接口,通过接口完成对配置信息新增/替换单独属性
*
* @param name
* @return
* @throws NacosException
*/
@RequestMapping("/publish")
public boolean publishConfig(
@RequestParam("name") String name) throws NacosException {
String dataId = prefix + "." + fileExtension;
String content = "config:\n" +
" name: " + name;
ConfigService configService = nacosConfigManager.getConfigService();
return configService.publishConfig(dataId, GROUP, content);
}
@RequestMapping("/getConfig")
public String getConfig()
throws NacosException {
String dataId = prefix + "." + fileExtension;
ConfigService configService = nacosConfigManager.getConfigService();
return configService.getConfig(dataId, GROUP, 2000);
}
}
com/gm/demo/nacos_config/NacosConfigApplication.java
主方法启动类
支持配置的动态更新,五秒刷新一次
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import java.util.concurrent.TimeUnit;
/**
* nacos 配置获取 启动类
*
* @author 高明
*/
@SpringBootApplication
public class NacosConfigApplication {
public static void main(String[] args) throws InterruptedException {
ConfigurableApplicationContext applicationContext = SpringApplication.run(NacosConfigApplication.class, args);
while (true) {
//当动态配置刷新时,会更新到 Enviroment中,因此这里每隔5秒中从Enviroment中获取配置
String name = applicationContext.getEnvironment().getProperty("config.name");
System.err.println("=========>>>>>>>>>>名字 :" + name);
TimeUnit.SECONDS.sleep(5);
}
}
}
源码地址:https://gitee.com/gm19900510/springboot-cloud-example
更多详细配置请见官网:https://github.com/alibaba/spring-cloud-alibaba/blob/2021.x/spring-cloud-alibaba-examples/nacos-example/nacos-config-example/readme-zh.md
五、测试
5.1 项目启动
此时
nacos
无对应配置,获取的输出name
为本地profile
粒度配置的内容
nacos开启对:
[Nacos Config] Listening config: dataId=nacos-config, group=DEFAULT_GROUP
[Nacos Config] Listening config: dataId=nacos-config.yaml, group=DEFAULT_GROUP
[Nacos Config] Listening config: dataId=nacos-config-dev.yaml, group=DEFAULT_GROUP
此三项的配置监听
5.2 向nacos中新增配置
浏览器访问:http://127.0.0.1:3001/config/publish?name=nacos
将以下内容,通过上述请求新增至nacos
服务端中:
config:
name: nacos
简单实现可见
ConfigController
中的publishConfig
方法
此时发现配置监听和动态配置刷新均感知到数据的变化,此时nacos
服务端中,存在一条新增的记录
5.3 查看@Value刷新
浏览器访问:http://127.0.0.1:3001/config/value
5.4 查看信息配置为bean刷新
浏览器访问:http://127.0.0.1:3001/config/model
5.5 新增nacos内profile粒度配置
在nacos
服务端创建配置nacos-config-dev.yaml
查看控制台数据变化
六、下章预告
下章对nacos
融合springcloud
配置中心高级使用、配置加载优先级、本地配置优先、新特性使用和老版本升级等相关问题进行展开说明。