2024-02-05|Spring Cloud Config快速入门
今天做了什么:
- 创建项目 spring-cloud-examples,测试 Spring Cloud Config 使用本地文件和 git 仓库作为配置中心
Spring Cloud Config 是一个基于http协议的远程配置实现方式。通过统一的配置管理服务器进行配置管理,客户端通过http协议主动的拉取服务的的配置信息,完成配置获取。
Spring Cloud Config 支持以下几种存储方式:
- Git 仓库
- 本地文件
- Vault
- JDBC 数据库
本文主要分享 Spring Cloud Config 使用本地文件和Git 仓库存储配置文件、配置文件加解密、集成 Spring Cloud Bus 等内容,源码在 github:spring-cloud-examples。
本地文件
服务端应用
1. 创建项目
首先,创建一个目录
mkdir spring-cloud-examples
cd spring-cloud-examples
然后,创建 config 目录,并使用 spring cli 创建一个 maven 项目,项目名称 config-server-file
mkdir config
cd config
spring init \
--boot-version=3.2.2 \
--type=maven-project \
--java-version=8 \
--name=config-server-file \
--package-name=com.chensoul.springcloud \
--groupId=com.chensoul.springcloud \
--dependencies=cloud-config-server,actuator \
config-server-file
项目创建成功之后,将 spring-boot-starter-parent 版本改为 2.7.18,对应的将 spring-cloud.version 改为 2021.0.9。
2. 添加 @EnableConfigServer 注解
@EnableConfigServer
@SpringBootApplication
public class ConfigServerFileApplication {
public static void main(final String[] args) {
SpringApplication.run(ConfigServerFileApplication.class, args);
}
}
3. 修改配置文件
将 application.properties 重命名为 application.yml,并添加如下配置
server:
port: 8888
spring.application.name: config-server-file
spring.profiles.active: native
management.endpoint.health.show-details: ALWAYS
management.endpoints.web.exposure.include: "*"
# default value: classpath:/, classpath:/config, file:./, file:./config
spring.cloud.config.server.native.search-locations: file:${PWD}/config-repo
说明:
- 使用本地配置文件,要求
spring.profiles.active
必须为 native${PWD}
指向的是当前项目的路径
创建 config-repo 目录,用于保存配置文件:
mkdir config-repo
在 config-repo 目录创建一个测试文件 foo.yml:
foo: hello world
4. 启动应用
启动应用之后,可以访问 /actuator 。
$ curl http://localhost:8888/actuator/ -s | jq
{
"_links": {
"self": {
"href": "http://localhost:8888/actuator",
"templated": false
},
...
"refresh": {
"href": "http://localhost:8888/actuator/refresh",
"templated": false
},
"features": {
"href": "http://localhost:8888/actuator/features",
"templated": false
}
}
}
此外,配置服务器还暴露以下几个端点:
/encrypt
和/decrypt
/{application}/{profile}[/{label}]
/{application}-{profile}.yml
/{label}/{application}-{profile}.yml
/{application}-{profile}.properties
/{label}/{application}-{profile}.properties
说明:
application
:应用名称profile
:spring 的 profilelabel
:git 仓库的分支名称
针对当前项目的 foo.yml 配置文件,可以访问的路径有:
http://localhost:8888/foo/native
http://localhost:8888/foo-native.yml
http://localhost:8888/foo-native.yaml
http://localhost:8888/foo-native.properties
http://localhost:8888/foo-native.json
http://localhost:8888/foo/native/master
http://localhost:8888/foo/native,default/master
http://localhost:8888/master/foo-native.properties
http://localhost:8888/master/foo-native.yml
http://localhost:8888/master/foo-native.yaml
http://localhost:8888/master/foo-native.json
例如,访问 localhost:8888/foo/native :
$ curl -s localhost:8888/foo/native | jq
{
"name": "foo",
"profiles": [
"native"
],
"label": null,
"version": null,
"state": null,
"propertySources": [
{
"name": "file:/Users/chensoul/workspace/IdeaProjects/spring-cloud-examples/config-repo/foo.yml",
"source": {
"foo": "hello world"
}
}
]
}
访问 http://localhost:8888/actuator/env 可以查看所有 propertySources 配置:
$ curl -s localhost:8080/actuator/env | jq
{
"activeProfiles": [
"native"
],
"propertySources": [
{
"name": "server.ports",
"properties": {
"local.server.port": {
"value": 8888
}
}
},
{
"name": "servletContextInitParams",
"properties": {}
},
{
"name": "systemProperties",
"properties": {
}
},
{
"name": "systemEnvironment",
"properties": {
}
},
{
"name": "configServerClient",
"properties": {
"spring.cloud.config.enabled": {
"value": "false"
}
}
},
{
"name": "springCloudClientHostInfo",
"properties": {
"spring.cloud.client.hostname": {
"value": "192.168.3.214"
},
"spring.cloud.client.ip-address": {
"value": "192.168.3.214"
}
}
},
{
"name": "Config resource 'class path resource [application.yml]' via location 'optional:classpath:/'",
"properties": {
"server.port": {
"value": 8888,
"origin": "class path resource [application.yml] - 2:9"
},
"spring.profiles.active": {
"value": "native",
"origin": "class path resource [application.yml] - 6:13"
},
"spring.cloud.config.server.native.search-locations": {
"value": "file:/Users/chensoul/workspace/IdeaProjects/spring-cloud-examples/config-repo",
"origin": "class path resource [application.yml] - 9:53"
},
"management.endpoints.web.exposure.include": {
"value": "*",
"origin": "class path resource [application.yml] - 15:18"
},
"management.endpoint.env.post.enabled": {
"value": true,
"origin": "class path resource [application.yml] - 19:18"
}
}
}
]
}
propertySources 包括:
server.ports
servletContextInitParams
systemProperties
systemEnvironment
configServerClient
springCloudClientHostInfo
optional:classpath:/
中的application.yml
查看 foo.yml 配置文件内容:
$ curl http://localhost:8888/foo-native.yml
foo: hello world
5. 开启 Security
添加 security 依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
修改配置文件,设置用户名和密码:
spring.security.user.name: ${SPRING_SECURITY_USER_NAME:root}
spring.security.user.password: ${SPRING_SECURITY_USER_PASSWORD:123456}
或者通过环境变量 SPRING_SECURITY_USER_NAME
、SPRING_SECURITY_USER_PASSWORD
进行设置。
重启应用,再次访问配置服务器时需要指定用户名和密码,例如,查看 foo.yml 配置文件内容:
$ curl http://root:123456@localhost:8888/foo/native -s | jq
test: hello world
6. 加解密信息
支持两种配置方式:
第一种,对称加密。配置加密 key 和 salt
encrypt.key: ${ENCRYPT_KEY:123456} encrypt.salt: deadbeef # 可选,默认值为 deadbeef
或者通过环境变量
ENCRYPT_KEY
进行设置。第二种,分对称加密。
首先,需要生成 keystore 文件:
cd config-server-file/src/main/resources keytool -genkeypair -alias mytestkey -storetype PKCS12 -keyalg RSA \ -dname "CN=Web Server,OU=Unit,O=Organization,L=City,S=State,C=US" \ -keypass changeme -keystore server.jks -storepass changeme
然后,在配置文件中添加:
encrypt: key-store: location: classpath:server.jks password: changeme alias: mytestkey secret: changeme
接下来,使用第一种配置方式进行测试。
重启应用,加密 “hello world”:
$ curl -s http://root:123456@localhost:8888/encrypt --data-urlencode "hello world"
1245fd945d0a14e529a0cafe0b6367206d69cad381d4d733ba21d348a303865e
解密:
$ curl -s http://root:123456@localhost:8888/decrypt -d 1245fd945d0a14e529a0cafe0b6367206d69cad381d4d733ba21d348a303865e
hello world
在配置文件使用加密字符串。修改 foo.yml 中 test 的值为上面生成的加密字符串:
foo: '{cipher}1245fd945d0a14e529a0cafe0b6367206d69cad381d4d733ba21d348a303865e'
重启应用,查看 foo.yml 文件内容:
$ curl http://root:123456@localhost:8888/foo/native -s | jq
foo: hello world
7. 集成 Eureka 注册中心
添加 eureka 客户端依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
添加 eureka 配置:
eureka:
instance:
prefer-ip-address: true
instance-id: ${spring.cloud.client.ip-address}:${server.port}
# 客户端向注册中心发送心跳的时间间隔,默认30秒
leaseRenewalIntervalInSeconds: 5
#注册中心在收到客户端心跳之后,等待下一次心跳的超时时间,如果在这个时间内没有收到下次心跳,则移除该客户端。默认90秒
leaseExpirationDurationInSeconds: 30
client:
service-url:
defaultZone: http://localhost:8761/eureka/
initialInstanceInfoReplicationIntervalSeconds: 5
registryFetchIntervalSeconds: 5
8. 配置高可用
有两种方式:
- 部署多实例,然后使用负载均衡进行代理
- 注册到注册中心,部署多实例
客户端应用
1. 创建项目
使用 spring cli 创建一个 maven 项目 config-client
spring init \
--boot-version=3.2.2 \
--type=maven-project \
--java-version=8 \
--name=config-server-file-client \
--package-name=com.chensoul.springcloud \
--groupId=com.chensoul.springcloud \
--dependencies=cloud-config-client,web,actuator \
config-client
将 spring-boot-starter-parent 版本改为 2.7.18,对应的将 spring-cloud.version 改为 2021.0.9
2. 修改配置文件
将 application.properties 重命名为 application.yml,并添加如下配置
spring.application.name: config-client
management.endpoint.health.show-details: ALWAYS
management.endpoints.web.exposure.include: "*"
# 1. 默认从 http://localhost:8888 获取配置
# 2. 如果没有配置 optional: 则 Config Client 连接不到 Config Server 时就会失败
# 3. import 配置优先于 uri 配置
# 4. 配置了后就不需要 bootstrap 引导文件
spring.config.import: "optional:configserver:"
spring:
cloud:
config:
uri: http://localhost:8888
在 config-repo 目录添加客户端配置文件 config-client.yml
test: hello world
3. 创建一个 controller
@RefreshScope
@RestController
@SpringBootApplication
public class ConfigClientApplication {
@Value("${test}")
private String test;
@GetMapping("/test")
public String test() {
return this.test;
}
public static void main(final String[] args) {
SpringApplication.run(ConfigFileClientApplication.class, args);
}
}
4. 启动应用
查看 test 的值
$ curl http://localhost:8080/test
test: hello world
查看 config-client 应用的配置:
$ curl http://root:123456@localhost:8888/config-client.yml
test: hello world
5. 配置重试
添加 spring-retry 依赖:
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
修改配置添加重试参数:
spring:
cloud:
config:
uri: http://localhost:8888
# 客户端配置快速失败
fail-fast: true
# 配置重试,需要引入 spring-retry
retry:
initialInterval: 1000
multiplier: 1.1
maxInterval: 10000
maxAttempts: 6
注意:重试参数也可以添加到 url 后面:
spring.config.import: configserver:http://configserver.example.com?fail-fast=true&max-attempts=10&max-interval=1500&multiplier=1.2&initial-interval=1100"
6. 配置其他参数
配置客户端快速失败和连接超时:
spring:
cloud:
config:
uri: http://localhost:8888
# 客户端配置快速失败
fail-fast: true
# 配置重试,需要引入 spring-retry
retry:
initialInterval: 1000
multiplier: 1.1
maxInterval: 10000
maxAttempts: 6
request-connect-timeout: 3000
request-read-timeout: 3000
7. 开启 Security
服务端开启了 security 之后,客户端启动时会报错:
org.springframework.cloud.config.client.ConfigClientFailFastException: Could not locate PropertySource and the fail fast property is set, failing
Caused by: org.springframework.web.client.UnknownContentTypeException: Could not extract response: no suitable HttpMessageConverter found for response type [class org.springframework.cloud.config.environment.Environment] and content type [text/html;charset=UTF-8]
这时候,客户端需要设置用户名和密码。修改配置文件:
spring:
cloud:
config:
uri: http://localhost:8888
username: ${SPRING_SECURITY_USER_NAME:root}
password: ${SPRING_SECURITY_USER_PASSWORD:123456}
重启之后,再次查看 test 的值:
$ curl http://localhost:8080/test
hello world
8. 集成 Eureka 注册中心
添加 eureka 客户端依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
修改配置文件:
spring:
cloud:
config:
# uri: http://localhost:8888
discovery:
enabled: true
service-id: config-server-file
Git 仓库
服务端应用
1. 创建项目
使用 spring cli 创建一个 maven 项目 config-server-git
cd config
spring init \
--boot-version=3.2.2 \
--type=maven-project \
--java-version=8 \
--name=config-server-git \
--package-name=com.chensoul.springcloud \
--groupId=com.chensoul.springcloud \
--dependencies=cloud-config-server,actuator \
config-server-git
将 spring-boot-starter-parent 版本改为 2.7.18,对应的将 spring-cloud.version 改为 2021.0.9
2. 添加 @EnableConfigServer 注解
@EnableConfigServer
@SpringBootApplication
public class ConfigServerGitApplication {
public static void main(final String[] args) {
SpringApplication.run(ConfigServerGitApplication.class, args);
}
}
3. 修改配置文件
将 application.properties 重命名为 application.yml,并添加如下配置
server:
port: 8888
spring.application.name: config-server-git
management.endpoint.health.show-details: ALWAYS
management.endpoints.web.exposure.include: "*"
spring.cloud.config.server.git.uri: https://github.com/chensoul/spring-cloud-examples
spring.cloud.config.server.git.search-paths: config-repo
spring.cloud.config.server.git.default-label: config # 使用 config 分支
说明:
- search-paths:搜索路径,可以是多个,使用逗号连接,支持 * 匹配
- default-label:指定默认分支
4. 配置参数
spring:
cloud:
config:
server:
git:
uri: file:${PWD}/config-repo
username: username
password: password
skipSslValidation: true
timeout: 4
force-pull: true
deleteUntrackedBranches: true
refreshRate: 0
defaultLabel: main
tryMasterBranch: false
如果是私有仓库,则需要配置认证,或者使用 ssh 私钥,配置方式,请参考 Git SSH configuration using properties。
5. 启动应用
查看 test 的值
$ curl http://localhost:8080/test
hello world
6. 开启 Security
添加 security 依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
修改配置文件,设置用户名和密码:
spring.security.user.name: ${SPRING_SECURITY_USER_NAME:root}
spring.security.user.password: ${SPRING_SECURITY_USER_PASSWORD:123456}
或者通过环境变量 SPRING_SECURITY_USER_NAME
、SPRING_SECURITY_USER_PASSWORD
进行设置。
重启应用,再次访问配置服务器时需要指定用户名和密码,例如,查看 foo.yml 配置文件内容:
$ curl http://root:123456@localhost:8888/foo-native.yml
foo: hello world
查看 config-client 应用的配置:
$ curl http://root:123456@localhost:8888/config-client.yml
test: hello world
客户端应用
1. 创建应用
在 config-client 项目基础上,修改配置文件
因为服务端配置文件是在 config 分支(spring.cloud.config.server.git.default-label: config
),故客户端也应该设置分支 spring.cloud.config.label: config
:
spring.application.name: config-client
management.endpoint.health.show-details: ALWAYS
management.endpoints.web.exposure.include: "*"
# 1. 默认从 http://localhost:8888 获取配置
# 2. 如果没有配置 optional: 则 Config Client 连接不到 Config Server 时就会失败
# 3. import 配置优先于 uri 配置
# 4. 配置了后就不需要 bootstrap 引导文件
spring.config.import: "optional:configserver:"
spring:
cloud:
config:
uri: http://localhost:8888
label: config
# 客户端配置快速失败
fail-fast: true
# 配置重试,需要引入 spring-retry
retry:
initialInterval: 1000
multiplier: 1.1
maxInterval: 10000
maxAttempts: 6
2. 启动应用
重启应用,查看 test 的值
$ curl http://root:123456@localhost:8080/test
hello world
3. 开启 security
服务端开启 security 之后,客户端需要设置用户名和密码。修改配置文件:
spring:
cloud:
config:
uri: http://localhost:8888
label: config
username: ${SPRING_SECURITY_USER_NAME:root}
password: ${SPRING_SECURITY_USER_PASSWORD:123456}
重启之后,再次查看 test 的值:
$ curl http://localhost:8080/test
hello world
4. 测试手动更新配置
修改 config-client.yml 文件中 test 的值为 “hello world 1111”,并提交到 git 仓库。
通过配置服务,查看 config-client 应用的配置,发现 test 的值已经更新。
$ curl http://root:123456@localhost:8888/config-client.yml
test: hello world
然后,再次查看客户端的 test 的值:
$ curl http://localhost:8080/test
hello world
发现 test 值并没有更新,这时候需要调用客户端的 /actuator/refresh 手动更新。
$ curl -X POST http://localhost:8080/actuator/refresh
["config.client.version","test"]%
返回内容,说明 config.client.version 和 test 的值有更新。如果没有返回,说明执行异常。
再次查看客户端的 test 的值,发现 test 值已修改为 “hello world 1111”:
$ curl http://localhost:8080/test
hello world 1111
说明:
- /actuator/refresh 只能更新单个客户端的配置,如果有多个客户端需要一个个执行刷新接口。
集成 Spring Cloud Bus
服务端应用
1. 创建应用
在 config-server-git 项目基础上,添加依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>
2. 修改配置文件
添加 rabbitmq 配置
spring.rabbitmq:
host: 127.0.0.1
port: 5672
username: guest
password: 123456
3. 重启应用
客户端应用
1. 创建应用
在 config-client 项目基础上,添加依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>
2. 修改配置文件
添加 rabbitmq 配置
spring.rabbitmq:
host: 127.0.0.1
port: 5672
username: guest
password: 123456
3. 重启应用
查看 test 的值
$ curl http://root:123456@localhost:8888/config-client-default.yml
test: hello world 1111
$ curl http://localhost:8080/test
hello world 1111
4. 自动刷新配置
spring cloud bus 提供了两个端点:
- /actuator/busrefresh:通过该接口刷新配置,spring cloud bus 会广播给所有客户端从而刷新配置
- /actuator/busenv
使用 POST 请求访问 /actuator/busenv,修改 config-client.yml 文件中 test 的值为 “hello world 2222”:
curl -X POST -H 'Content-Type: application/json' -d '{"name":"test","value":"hello world 2222"}' http://localhost:8080/actuator/busenv
查看客户端日志:
2024-02-06 11:33:06.003 INFO 11387 --- [nio-8080-exec-7] o.s.c.b.event.EnvironmentChangeListener : Received remote environment change request. Keys/values to update {test=hello world 2222}
这时候查看,返回内容为:
$ curl http://root:123456@localhost:8888/config-client-default.yml
test: hello world 1111
$ curl http://localhost:8080/test
hello world 1111
可以看到 test 属性的值还没有刷新。通过 /actuator/busrefresh 刷新配置:
curl -X POST http://localhost:8080/actuator/busrefresh
再次查看,可以看到 test 属性的值已经刷新。
$ curl http://localhost:8080/test
hello world 2222
5. 通过 git 刷新配置
在 git 仓库中修改 test 的值为 “hello world 3333”,然后执行刷新操作:
curl -X POST http://localhost:8080/actuator/busrefresh
查看客户端日志:
Received remote refresh request.
Fetching config from server at : http://localhost:8888
Located environment: name=config-client, profiles=[default], label=config, version=cfcb38e6d396ce6b85e0aa0e8839f38538b7f221, state=null
Keys refreshed [config.client.version, test]
再次查看,可以看到 test 属性的值已经刷新。
$ curl http://localhost:8080/test
hello world 3333%
另外,如果想实现 git 上修改之后立即推送,则需要配置 git 的 webhook 功能。
Related content
- 2024-11-13|今天我做了什么
- 2024-11-07|今天我做了什么
- 2024-11-06|今天我做了什么
- 2024-11-05|今天我做了什么
- 2024-11-04|SivaLabs博客文章
- 2024-02-20|RateLimitAspect请求限流、调整spring-cloud-examples项目结构
- 2024-02-19|foodie-cloud集成Sharding Sphere实现读写分离
- 2024-02-18|NewRelice应用性能监控、6个Diagrams工具、foodie-food测试
- 2024-02-04|foodie-cloud集成Resilience4j
- 2024-02-02|foodie-cloud集成Spring Cloud Config和SpringDoc