使用Spring Cloud Config统一管理微服务配置
Posted shi_zi_183
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用Spring Cloud Config统一管理微服务配置相关的知识,希望对你有一定的参考价值。
使用Spring Cloud Config统一管理微服务配置
为什么要统一管理微服务配置
对于传统的单体应用,常使用配置文件管理所有配置。例如一个Spring Boot开发的单体应用,可将配置内容放在application.yml文件中。如果需要切换环境,可设置多个Profile,并在启动应用时指定spring.profiles.active={profile}
。当然也可借助Profile实现环境切换。
然而,在微服务架构中,微服务的配置管理一般有以下需求:
- 集中管理配置。一个使用微服务架构的应用系统可能会包含成百上千个微服务,因此集中管理配置是非常有必要的。
- 不同环境,不同配置。例如,数据源配置在不同的环境(开发、测试、预发布、生产等)中是不同的。
- 运行期间可动态调整。例如,可根据各个微服务的负载情况,动态调整数据源连接池大小或熔断阈值,并且在调整配置时不停止微服务。
- 配置修改后可自动更新。如配置内容发生变化,微服务能够自动更新配置。
Spring Cloud Config简介
Spring Cloud Config为分布式系统外部化配置了服务器端和客户端的支持,它包括Config Server和Config Client两部分。由于Config Server和Config Client都实现类对Spring Environment和PropertySource抽象的应映射,因此Spring Cloud Config非常适合Spring应用程序,当然也可与任何其他语言编写的应用程序配合使用。
Config Server是一个可横向拓展、集中式的配置服务器,它用于集中管理应用程序各个环境下的配置,默认使用Git存储配置内容(可以使用Subversion、本地文件系统或Vault存储配置),因此可以很方便地实现对配置的版本控制与内容审计。
Config Client是Config Server的客户端,用于操作存储在Config Server中的配置属性。所有的微服务都指向Config Server。各个微服务在启动时,会请求Config Server以获取所需要的配置属性,然后缓存这些属性以提高性能。
编写Config Server
1)在Git仓库https://gitee.com/persimmon_me/spring-cloud-config-repo.git中新建几个配置文件。
内容分别是
profile=default-1.0
profile=dev-1.0
profile=test-1.0
profile=production-1.0
为了测试版本控制,为该Git仓库创建config-label-v2.0分支,并将各个配置文件中的1.0改为2.0。
2)创建一个Maven工程,ArtifactId是microservice-config-server,并为项目添加以下依赖。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
3)编写启动类,在启动类上添加注解@EnableConfigServer,声明这是一个Config Server。
package com.example;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.config.server.EnableConfigServer;
@SpringBootApplication
@EnableConfigServer
public class ConfigServerApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigServerApplication.class);
}
}
4)编写配置文件application.yml,并在其中添加
server:
port: 8080
spring:
application:
name: microservice-config-server
cloud:
config:
server:
git:
uri: https://gitee.com/********/spring-cloud-config-repo
username: ***********
password: ******
这样,一个Config Server就完成了。
可以使用Config Server的端点获取配置文件的内容。端点与配置文件的映射规则如下
/{application}/{profile}[/{label}]
/{application}-{profile}.yml
/{label}/{application}-{profile}.yml
/{application}-{profile}.properties
/{label}/{application}-{profile}.properties
以上端点都可映射到{application}-{profile}.properties这个配置文件,{application}表示微服务的名称,{label}对应Git仓库的分支,默认是master
按照以上规则,对于本例,可使用以下URL访问到Git仓库master分支的microservice-foo-dev.properties。
http://localhost:8080/microservice-foo/dev
http://localhost:8080/microservice-foo-dev.properties
http://localhost:8080/microservice-foo-dev.yml
测试
1)访问http://localhost:8080/microservice-foo/dev
从结果可以直观地看到应用名称、项目profile、Git label、 Git version、配置文件URL、配置详情等信息。
2)访问http://localhost:8080/microservice-foo-dev.properties,返回配置文件中的属性
3)访问http://localhost:8080/config-labal-v2.0/microservice-foo-dev.properties,可获得
说明获得了Git仓库config-label-v2.0分支中的配置信息。
至此,已成功构建了Config Server,并通过构造URL的方式,获取了Git仓库中的配置信息。
编写Config Client
1)创建一个Maven工程,ArtifactId是microservice-config-client,并为项目添加以下依赖。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
2)创建一个基本的Spring Boot启动类
package com.example;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class ConfigClientApplication {
public static void main(String[] args){
SpringApplication.run(ConfigClientApplication.class,args);
}
}
3)编写配置文件application.yml
server:
port: 8081
4)创建配置文件bootstrap.yml,并在其中添加如下内容
spring:
application:
name: microservice-foo
cloud:
config:
uri: http://localhost:8080/
profile: dev
label: master
其中
- spring.application.name:对应Config Server所获取的配置文件中的{application}。
- spring.cloud.config.uri:指定Config Server的地址,默认是http://localhost:8888。
- spring.cloud.config.profile:profile对应的Config Server所获取的配置文件中的{profile}。
- spring.cloud.config.label:指定Git仓库的分支,对应Config Server所获取配置文件的{label}。
注:以上属性应配置在bootstrap.yml,而不是application.yml中。如果配置在application.yml中,该部分配置就不能正常工作。例如,Config Client会连接spring.cloud.config.uri的默认值http://localhost:8888,而并非配置的http://localhost:8080
Spring Cloud有一个"引导上下文"的概念,这是主应用程序的父上下文。引导上下文负责从配置服务器加载配置属性,以及解密外部配置文件中的属性。和著应用程序加载application.*(yml或properties)中的属性不同,引导上下文加载bootstrap.*中的属性。配置在bootstrap.*中的属性有更高的优先级,因此默认情况下它们不能被本地配置覆盖。
若需禁用引导过程,可设置spring.cloud.bootstrap.enabled=false。
5)编写Controller
package com.example.controller;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class ConfigClientController {
@Value("${profile}")
private String profile;
@GetMapping("/profile")
public String hello() {
return this.profile;
}
}
在Controller中,通过注解@Value("${profile}"),绑定Git仓库配置文件中的profile属性。
测试
1)启动microservice-config-server
2)启动microservice-config-client
3)访问http://localhost:8081/profile
Config Server的Git仓库配置详情
前文使用spring.cloud.config.server.git.uri指定了一个Git仓库,事实上,该属性非常灵活。
占位符支持
Config Server的占位符支持{application}、{profile}和{label}。
示例
server:
port: 8080
spring:
application:
name: microservice-config-server
cloud:
config:
server:
git:
uri: https://gitee.com/*******/{application}
username: 18332104715
password: zbn456123
模式匹配
模式匹配指的是带有通配符的{application}/{profile}名称的列表。如果{application}/{profile}不匹配任何模式,他将使用spring.cloud.config.server.git.uri定义的URI。
spring:
cloud:
config:
server:
git:
uri: https://github.com/spring-cloud-samples/config-repo
repos:
simple: https://github.com/simple/config-repo
special:
pattern: special*/dev*,*special*/dev*
uri: https://github.com/special/config-repo
local:
pattern: local*
uri: file:/home/configsvc/config-repo
该例中,对于simple仓库,它只匹配所有配置文件中名为simple的应用程序。local仓库则匹配所有配置文件中以local开头的所有应用程序的名称。
搜索目录
很多场景下,可能把配置文件放在了Git仓库子目录中,此时可以使用search-path指定,search-path同样支持占位符。
spring:
cloud:
config:
server:
git:
uri: https://github.com/spring-cloud-samples/config-repo
searchPaths: foo,bar*
这样,Config Server就会在Git仓库根目录、foo子目录以及所有以bar开始的子目录中查找配置文件。
启动时加载配置文件
默认情况下,在配置被首次请求时,Config Server才会clone Git仓库。也可让Config Server在启动时就clone Git仓库
spring:
cloud:
config:
server:
git:
uri: https://git/common/config-repo.git
repos:
team-a:
pattern: microservice-*
cloneOnStart: true
uri: http://git/microservice/config-repo.git
将属性spring.cloud.config.server.git.repos.*.clone-on-start
设为true,即可让Config Server启动时clone指定Git仓库。
当然,也可使用spring.cloud.config.git.clone-on-start=true进行全局配置。
配置clone-on-start=true,可帮助Config Server启动时快速识别错误的配置源(例如无效的Git仓库)。
将以下包的日志级别设置为DEBUG,就可打印Config Server请求Git仓库的细节。可通过这种方式,更好地理解Config Server的Git仓库配置,同时也便于快速定位问题。
logging:
level:
org.springframework.cloud: DEBUG
org.springframework.boot: DEBUG
Config Server的健康状态指示器
Config Server自带了一个健康状况指示器,用于检查所配置的EnvironmentRepository是否正常工作。可使用Config Server的/health端点查询当前健康状态。默认情况下,健康指示器向EnvironmentRepository请求的{application}是app,{profile}和{label}是对应EnvironmentRepository实现的默认值。对于Git,{profile}是default,{label}是master。
server:
port: 8080
spring:
application:
name: microservice-config-server
cloud:
config:
server:
git:
uri: https://gitee.com/**********/spring-cloud-config-repo
username: *********
password: *******
health:
repositories:
a-foo:
label: config-label-v2.0
name: microservice-foo
profiles: dev
若需禁用健康状态指示器,可设置spring.cloud.config.server.health.enabled=false
配置内容的加解密
前文是在Git仓库中明文存储配置属性的。很多场景下,对于某些敏感的配置内容(例如数据库账号、密码)应当加密存储。
Config Server为配置内容的加密与解密提供了支持
安装JCE
Config Server的加解密功能依赖Java Cryptography Extension(JCE)
Java 8 JCE的地址:
Config Server的加解密端点
Config Server提供了加密与解密的端点,分别是/encrypt与/decrypt。可使用以下代码来加密明文:
curl $CONFIG_SERVER_URL/encrypt -d 想要加密的密文
使用以下代码来解密密文
curl $CONFIG_SERVER_URL/decrypt -d 想要加密的密文
对称加密
1)复制项目microservice-config-service,将ArtifactId修改为microservice-config-server-encryption。
2)创建bootstrap.yml
encrypt:
key: foo #对称加密密钥
测试
1)输入以下命令。
curl http://localhost:8080/encrypt -d mysecret
可返回8c1f87805fd3cea100e50df3ccd9aad592f3104fa79325edbf16833f79bec29f
,说明mysecret已被加密。
2)输入以下命令
curl http://localhost:8080/decrypt -d 8c1f87805fd3cea100e50df3ccd9aad592f3104fa79325edbf16833f79bec29f
注:encrypt开头的配置,必须放在bootstrap.yml中,否则将无法正常加解密
在早期的Spring Cloud版本中,并无此限制,例如Spring Cloud Camden SR4
存储加密的内容
加密后的内容,可使用{cipher}密文的形式存储。
1)准备一个配置文件,命名为encryption.yml
spring:
datasource:
username: dbuser
password: '{cipher}2********************9c1d53908afbb'
并将其push到Git仓库。此处需要注意spring.datasource.password上的单引号不能少。如果使用properties格式管理配置,则不能使用单引号,否则该值不会被解密。
2)使用http://localhost:8080/encryption-default.yml可获得如下结果。
说明Config Server能自动解密配置内容
一些场景下,想要让Config Server直接返回密文本身,可设置spring.cloud.config.server.encrypt.enable=false,这时可由Config Client自行解密。
非对称加密
1)复制项目microservice-config-server,将ArtifactId修改为microservice-config-server-encryption-rsa。
2)执行以下命名,并按照提示操作,即可创建一个Key Store
keytool -genkeypair -alias mytestkey -keyalg RSA -dname "CN=Web Server,OU=Unit,O=Organization,L=City,S=State,C=US" -keypass changeme -keystore D:/server.jks -storepass letmein
3)将生成的server.jks文件复制到项目的classpath下。
4)创建bootstrap.yml
encrypt:
key-store:
location: classpath:/server.jks #jks文件的路径
password: letmein #storepass
alias: mytestkey #alias
secret: changeme #keypass
这样,使用以下命令
curl http://localhost:8080/encrypt -d mysecret
使用/refresh端点手动刷新配置
很多场景下,需要在运行期间动态调整配置。如果配置发生了修改,微服务要如何实现配置的刷新呢?
要想实现配置刷新,需对之前的代码进行一点改造。
1)复制项目microservice-config-client,将ArtifactId修改为microservice-config-client-refresh。
2)为项目添加spring-boot-starter-actuator的依赖,该依赖包含了/refresh端点,用于配置的刷新。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
3)在Controler上添加注解@RefreshScope。添加@RefreshScope的类会在配置更改时得到特殊的处理
package com.example.controller;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RefreshScope
public class ConfigClientController {
@Value("${profile}")
private String profile;
@GetMapping("/profile")
public String hello() {
return this.profile;
}
}
测试
1)启动microservice-config-server。
2)启动microservice-config-client-refresh。
3)访问http://localhost:8081/profile,获得结果:dev-1.0
4)修改Git仓库中microservice-foo-dev.properties文件内容为profile=dev-1.0-change。
5)重新访问http://localhost:8081/profile,发现结果依然是dev-1.0,说明配置尚未刷新。
6)发送POST请求到http://localhost:8081/refresh
7)再次访问http://localhost:8081/profile,返回dev-1.0-change,说明配置已经刷新。
使用Spring Cloud Bus自动属性配置
前文讨论了使用/refresh端点手动刷新配置,但如果所有微服务节点的配置都需要手动去刷新,工作量可想而知。不仅如此,随着系统的不断扩张,会越来越难以维护。因此实现配置的自动刷新是很有必要的。
Spring Cloud Bus简介
Spring Cloud Bus使用轻量级的消息代理(例如RabbitMQ、Kafka等)连接分布式系统的节点,这样就可以广播传播状态的更改(例如配置的更新)或者其他的管理指令。可将Spring Cloud Bus想象成一个分布式的Spring Boot Actuator。
微服务A的所有实例都通过消息总线连接到了一起,每个实例都会订阅配置更新事件。当其中一个微服务节点的/bus/refresh端点被请求时,该实例就会向消息总线发送一个配置更新事件,其他实例获得该事件后也会更新配置。
实现自动刷新
安装RabbitMQ后,接下来为项目整合Spring Cloud Bus并实现自动刷新。
1)复制项目microservice-config-client-refresh,将ArtifactId修改为microservice-config-client-refresh-cloud-bus。
2)为项目添加spring-cloud-starter-bus-amqp的依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>
3)在bootstrap.yml中添加以下内容
spring:
application:
name: microservice-foo
cloud:
config:
uri: http://localhost:8080/
profile: dev
label: master
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest
测试
1)启动microservice-config-server
2)启动microservice-config-client-refresh-cloud-bus
3)将microservice-config-client-refresh-cloud-bus的端口改为8082,再启动一个节点。
4)访问http://localhost:8081/profile,可获得结果:dev-1.0-changa
5)将Git仓库中的microservice-foo-dev.proerties文件内容修改为
prifile=dev-1.0-bus
6)发送POST请求到其中一个Config Client实例的/bus/refresh端点
curl -X POST http://localhost:8081/bus/refresh
7)访问两个Config Client节点的/profile端点,发现两个节点都会返回dev-1.0-bus,说明配置内容已被刷新。
借助Git仓库的WebHooks,即可轻松实现配置的自动刷新。
局部刷新
某些场景下(例如灰度发布等),若只想刷新部分微服务的配置,可通过/bus/refresh端点的destination参数来定位要刷新的应用程序。
例如/bus/refresh?destination=customers:9000
,这样消息总线上的微服务实例就会根据destination参数的值来判断是否需要刷新。其中,customers:9000指的是各个微服务的ApplicationContextID。
destination参数也可以用来定位特定的微服务。例如/bus/refresh?destination=customers:**,这样就可以触发customers微服务所有实例的配置刷新。
架构改进
在前面的示例中,通过请求某个微服务/bus/refresh端点的方式来实现配置刷新,但这种方式并不优雅。
- 破坏了微服务的职责单一原则。业务微服务只应关注自身业务,不应承担配置配置刷新的职责。
- 破坏了微服务各节点的对等性。
- 有一定的局限性。例如,微服务在迁移时,网络地址常常会发生变化。此时如果想自动刷新配置,就不得不修改WebHook的配置。
将Config Server也加入消息总线中,并使用Config Server的/bus/refresh端点来实现配置的刷新。这样,各个微服务只需要关注自身的业务,而不再承担配置刷新的职责。
跟踪总线事件
在一些场景下,如果希望知道Spring Cloud Bus事件传播的细节,可以跟踪总线事件。
想要跟踪总线事件非常简单,只需设置spring.cloud.bus.trace.enable=true,这样在/bus/refresh端点被请求后,访问/trace端点
Spring Cloud Config与Eureka配合使用
前文在微服务中指定了Config Server地址,这种方式无法利用服务发现组件的优势。
1)将Config Server和ConfigClient都注册到Eureka Server上。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-netflix-eureka-client</artifactId>
</dependency>
Config Client application.yml
server:
port: 8081
management:
security:
enabled: false
spring:
application:
name: microservice-config-client-eureka
Config Server application.yml
server:
port: 8080
spring:
application:
name: microservice-config-server
cloud:
config:
server:
git:
uri: https://gitee.com/persimmon_me/spring-cloud-config-repo
username: *********
password: ******
health:
repositories:
a-foo:
label: config-labal-v2.0
name: microservice-foo
profiles: dev
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
2)Config Client的bootstrap.yml
spring:
application:
name: microservice-foo
cloud:
config:
profile: dev
label: master
discovery:
enabled: true
service-id: microservice-config-server-eureka
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
Spring Cloud Config的用户认证
在前文的示例中,Config Server是允许匿名访问的。为了防止配置内容的外泄,应该保护Config Server的安全。有多种方式做到这一点,例如通过物理网络安全,或者为Config Server添加用户认证等。
本节来为Config Server添加基于HTTP Basic的用户认证
先来构建一个需要用户认证的Config Server
1)复制项目microservice-config-server,将ArtifactId修改为microservice-config-server-authenticating
2)为项目添加以下依赖
<dependency>
以上是关于使用Spring Cloud Config统一管理微服务配置的主要内容,如果未能解决你的问题,请参考以下文章
使用 Spring Cloud Config 统一管理配置,别再到处放配置文件了!
springcloud-spring cloud config统一配置中心