在微服务架构集群部署的环境中,可能会有十几甚至几十个服务,若要修改项目属性参数,手动的方式是很糟糕的,所以就有了分布式配置管理这个概念。
将服务器的配置外部化,可以存在文件或数据库中,一个专门用于管理应用服务配置的应用,可以随时修改和自动更新到目标服务器上。目前已有开源项目,如,smconf、disconf、QConf。
Spring Cloud Config 为分布式系统中的外部化配置提供服务器端和客户端支持。使用 Config Server,可以在所有环境中管理应用程序的外部属性。Spring Cloud Config 官方文档,spring-cloud-config Github。
概述
Spring Cloud Config Server 默认使用 Git 来存储配置,使用 Git 管理配置文件自动支持文件的版本管理。
Config Server 特性
- 提供基于 HTTP API 的外部配置(key-value 或 yaml 内容)。
- 加密和解密属性值(对称或非对称)。
- 使用 @EnableConfigServer 轻松地将 Config Server 嵌入到 Spring Boot 应用中。
Spring Cloud Config Server 默认的Git存储库方式,是将 spring.cloud.config.server.git.uri 指定的Git配置仓库克隆到本地,客户端的配置文件存储在此Git存储库中或子目里,并将之用于客户端初始化。
spring.cloud.config.server.git.uri=https://github.com/spring-cloud-samples/config-repo
Config Server 获取以下形式的配置文件供客户端应用使用:
/{application}/{profile}[/{label}]
/{application}-{profile}.yml
/{label}/{application}-{profile}.yml
/{application}-{profile}.properties
/{label}/{application}-{profile}.properties
- application:指 spring.config.name 属性设置的 Config Client 名称,不存在则使用 spring.application.name 的值。
- profile:应用需要使用的 profile,若没有指定,则使用默认 default。
- label:Git 分支、标签,若没有指定,则使用默认 master。
注意:spring.application.name 的值不要使用 application- 作为前缀,会导致无法正确地解析配置文件。
Config Client 特性
- 绑定到配置服务器,并使用远程属性源初始化 Spring 环境。
- 加密和解密属性值(对称或非对称)。
- Spring Bean 上添加 @RefreshScope 注解即可实现在配置更改时重新初始化。
- 使用和管理端点:
- /env:用于更新 Environment 和重新绑定 @ConfigurationProperties 及日志级别。
- /refresh:用于刷新 @RefreshScope beans。
- /restart:重启 Spring Context(默认情况下禁用)。
- /pause 和 /resume:用于调用 Lifecycle 方法(在 ApplicationContext 上的 stop() 和 start() )。
Config Git 存储库
Config Server
创建 Config Server 项目并引入 spring-cloud-config-server 依赖。
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"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.4.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.springcloud.configserver</groupId> <artifactId>config-server-jdbc</artifactId> <version>v1.0.0</version> <name>config-server-git</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> <spring-cloud.version>Greenwich.SR1</spring-cloud.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-config-server</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
在启动类上添加 @EnableConfigServer 注解来启用Config Server。
@SpringBootApplication @EnableConfigServer public class ConfigServerApplication { public static void main(String[] args) { SpringApplication.run(ConfigServerApplication.class, args); } }
Spring Cloud Config Server 为外部配置(key-val 或等效 yaml 内容)提供基于 HTTP 资源的 API。 通过使用 @EnableConfigServer 注解,服务器可嵌入 Spring Boot 应用程序中。
创建远程 Git 仓库,用于存储客户端应用的配置文件。
这里在 GitHub 创建配置存储库用于,库名最好与服务应用名相同。实际生产会创建私有仓库,需要授权认证才可访问。
Config Server Git 配置存储库:https://github.com/gxing19/config-repoConfig Server 配置文件
application.properties# 端口 server.port=9010 # 应用名 spring.application.name=config-repo # Git URI 使用占位符 spring.cloud.config.server.git.uri=https://github.com/gxing19/config-repo #spring.cloud.config.server.git.uri=https://github.com/gxing19/${spring.application.name} # 设置 HTTP 连接超时时长, 单位:秒 spring.cloud.config.server.git.timeout=10 # 删除本地未跟踪的库 spring.cloud.config.server.git.delete-untracked-branches=true
Config Client
创建 Spring Boot 应用,引入 spring-cloud-starter-config 依赖和 spring-boot-starter-actuator 依赖。
这里创建一个订单服务的应用(orderService)来演示
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"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.3.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.springcloud</groupId> <artifactId>service-order</artifactId> <version>v1.0.0</version> <name>service-order</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> <spring-cloud.version>Greenwich.RELEASE</spring-cloud.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
作为 Config Client ,必须使用 bootstrap.properties 或 bootstrap.yml 作为引导配置,在应用上下文的引导阶段起作用。
bootstrap.properties# Config Server 地址 spring.cloud.config.uri=http://localhost:9010 # 应用名 spring.application.name=orderService # 使用的 profile spring.profiles.active=dev # Actuator 演示开放所有端点(生产只开放少数必要的端点) management.endpoints.web.exposure.include=*
在远程 Git 仓库创建客户端应用的配置文件
在 Git 配置存储库:https://github.com/gxing19/config-repo 创建配置文件 orderService-dev.properties
orderService-dev.propertiesserver.port=8010 common.properties.app-id=QWERTYUIO #logging.level.root=debug
启动客户端应用,查看配置客户端和服务端的日志
客户端应用(Config Client)在启动时需要向配置服务器(Config Server) 发送请求获取配置属性来完成应用初始化。看日志是在打印出 Banner 后就向配置服务器发送请求。Config Client 启动日志:
2019-04-15 18:09:17.622 INFO 8944 --- [ restartedMain] c.c.c.ConfigServicePropertySourceLocator : Fetching config from server at : http://localhost:9010 2019-04-15 18:09:20.669 INFO 8944 --- [ restartedMain] c.c.c.ConfigServicePropertySourceLocator : Located environment: name=orderService, profiles=[dev], label=null, version=5780a5068b7241badfebf68fffdc006c4a185545, state=null 2019-04-15 18:09:20.669 INFO 8944 --- [ restartedMain] b.c.PropertySourceBootstrapConfiguration : Located property source: CompositePropertySource {name='configService', propertySources=[MapPropertySource {name='configClient'}, MapPropertySource {name='https://github.com/gxing19/config-repo/orderService-dev.properties'}]} 2019-04-15 18:09:20.673 INFO 8944 --- [ restartedMain] c.s.s.order.OrderServiceApplication : The following profiles are active: dev
客户端应用启动成功,使用了远程配置文件中的 8010 端口。
Config Server 接收到请求日志:
克隆远程配置仓库到本地,在 Windows 系统默认是克隆到系统临时文件目录。2019-04-16 09:17:52.292 INFO 10176 --- [nio-9010-exec-5] .c.s.e.MultipleJGitEnvironmentRepository : Fetched for remote master and found 1 updates 2019-04-16 09:17:52.517 INFO 10176 --- [nio-9010-exec-5] o.s.c.c.s.e.NativeEnvironmentRepository : Adding property source: file:/C:/Users/gxing/AppData/Local/Temp/config-repo-4123568461296240052/orderService-dev.properties
Client 运行环境
客户端添加 spring-boot-starter-actuator 依赖,才可以通过调用端点来更新 Environment 。
根端点:http://localhost:8010/actuator
环境变量端点:http://localhost:8010/actuator/env
从该端点可以看到客户端应用完整的运行环境,包括环境变量。bootstrap.properties 在 /env 端点显示为高优先级属性源,端点数据如下:{ "activeProfiles": [ "dev" ], "propertySources": [ { "name": "server.ports", "properties": { "local.server.port": { "value": 8010 } } }, { "name": "configService:configClient", "properties": { "config.client.version": { "value": "5780a5068b7241badfebf68fffdc006c4a185545" } } }, { "name": "configService:https://github.com/gxing19/config-repo/orderService-dev.properties", "properties": { "server.port": { "value": "8010" }, "common.properties.app-id": { "value": "QWERTYUIO" } } }, {...省略...}, { "name": "applicationConfig: [classpath:/bootstrap.properties]", "properties": { "spring.cloud.config.uri": { "value": "http://localhost:9010", "origin": "class path resource [bootstrap.properties]:1:25" }, "spring.application.name": { "value": "orderService", "origin": "class path resource [bootstrap.properties]:2:25" }, "spring.profiles.active": { "value": "dev", "origin": "class path resource [bootstrap.properties]:3:24" }, "management.endpoints.web.exposure.include": { "value": "*", "origin": "class path resource [bootstrap.properties]:4:43" } } }, { "name": "defaultProperties", "properties": { } } ] }
动态更新属性
客户端应用的远程配置文件 orderService-dev.properties 有个自定义属性,写个 Controller 从环境中获取该值,然后改变自定义属性的值,调用 /refresh 刷新环境和变量,重新请求该属性。
创建获取自定义属性的 Controller 方法
@RestController @RequestMapping("/config") public class ConfigController { @Autowired private Environment environment; @GetMapping("/refresh") public String refreshProperties(){ String appId = environment.getProperty("common.properties.app-id"); return appId; } }
返回结果:QWERTYUIO
修改远程配置文件该属性值
orderService-dev.propertiescommon.properties.app-id=abcdefghijk112233
调用刷新环境的端点 /refresh,POST 请求
# post 请求 http://localhost:8010/actuator/refresh
返回结果:
[ "config.client.version", "common.properties.app-id" ]
调用执行步骤 1 创建的方法,会重新拉取远程的配置文件。
返回结果:abcdefghijk112233
不用重启应用,调用端点刷新环境和变量,即完成了配置文件属性重载。
与 Eureka 集成
在上面示例的基础上整合 Eureka ,通过服务发现来与 Config Server 通信获取远程配置源。
注册 Config Server
Config Server 作为 Eureka Server 的客户端,需要引入 eureka-client 依赖
pom.xml<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency>
Config Server 的配置文件添加注册到 Eureka Server 的属性
application.properties# 添加注册Eureka Server eureka.client.service-url.defaultZone=http://admin:123456@eureka.master.com:8761/eureka,http://admin:123456@eureka.slave.com:8762/eureka
注册 Config Client
Config Client 作为 Eureka Server 的客户端,需要引入 eureka-client 依赖
pom.xml
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency>
Config Client 的引导启动配置文件 bootstrap.properties 添加开启服务发现和设置 Eureka 服务地址:
bootstrap.properties
# 添加开启服务发现 discovery spring.cloud.config.discovery.enabled=true # 添加注册Eureka Server eureka.client.service-url.defaultZone=http://admin:123456@eureka.master.com:8761/eureka,http://admin:123456@eureka.slave.com:8762/eureka # 添加设置 Config Server 在 Eureka Server 中的 service ID spring.cloud.config.discovery.service-id=config-repo # 注释掉显式指定的 Config Server uri #spring.cloud.config.uri=http://localhost:9010
重启 Config Server 和 Config Client,查看日志
在 Eureka Server 的 Web 控制台可以看到 Config Server 和 Config Client 注册实例。
Config Client 在引导启动阶段,通过服务发现获取远程配置源。
调用 Config Client 获取环境变量的 Controller 方法成功。
Config Server 高可用
Spring Cloud Config Server 实现高可用主要有两种方式:
- 部署多个 Config Server 实例,指向同一个存储库(Git 或 JDBC),在上层通过负载均衡器来反向代理到 Config Server。
- 部署多个 Config Server,集成 Eureka Client,注册到 Eureka Server,通过服务治理来实现,即可高可用,也实现了自维护。
Config 详细配置
请参阅 [ Spring Cloud系列(十二):分布式配置管理 Config 之 配置详解]http://www.gxitsky.com/article/23)
其它参考
Spring Coud Config Server 也支持使用 Vault 、CredHub作为存储后端,还支持复合环境存储(同时使用不同的存储库),支持通过代理访问存储后端。详细使用参考官网。
注意:本文归作者所有,未经作者允许,不得转载