配置⽂件想必⼤家都不陌⽣。在Spring Boot项⽬中,默认会提供⼀个application.properties或者 application.yml⽂件,我们可以把⼀些全局性的配置或者需要动态维护的配置写⼊改⽂件,不如数据库连接,功能开关,限流阈值,服务地址等。为了解决不同环境下服务连接配置等信息的差异,Spring Boot还提供了基于spring.profiles.active={profile}的机制来实现不同的环境的切换。
随着单体架构向微服务架构的演进,各个应⽤⾃⼰独⽴维护本地配置⽂件的⽅式开始显露出它的不足之处。主要有下⾯⼏点。
配置的动态更新:在实际应⽤会有动态更新位置的需求,⽐如修改服务连接地址、限流配置等。在传统模式下,需要⼿动修改配置⽂件并且重启应⽤才能⽣效,这种⽅式效率太低,重启也会导致服务暂时不可⽤。
配置多节点维护:在微服务架构中某些核⼼服务为了保证⾼性能会部署上百个节点,如果在每个节点中都维护⼀个配置⽂件,⼀旦配置⽂件中的某个属性需要修改,可想⽽知,⼯作量是巨⼤的。
不同部署环境下配置的管理:前⾯提到通过profile机制来管理不同环境下的配置,这种⽅式对于⽇常维护来说也⽐较繁琐。
统⼀配置管理就是弥补上述不⾜的⽅法,简单说,最近本的⽅法是把各个应⽤系统中的某些配置放在⼀个第三⽅中间件上进⾏统⼀维护。然后,对于统⼀配置中⼼上的数据的变更需要推送到相应的服务节点实现动态跟新,所以微服务架构中,配置中⼼也是⼀个核⼼组件,⽽Spring Cloud Config就是⼀个配置中⼼组件,并且可以Git,SVN,本地⽂件等作为存储。
6.2 Spring Cloud Config 实践实现最简单的配置中⼼,创建06_cloud_config微服务⼯程,作为配置中⼼的Config Server,同时使⽤git作为配置存储⽅式,改造之前⽀付微服务⼯程,作为客户端从Config Server获取配置。具体实现如下。
6.2.1 配置中⼼服务端创建06_cloud_config配置中⼼服务端⼯程,同时使⽤默认git作为存储⽅式。
1. 添加依赖
pom.xml完整代码如下。
4.0.0 org.springframework.boot spring-boot-starter-parent2.6.3 com.gpd.demo 06_cloud_config0.0.1-SNAPSHOT 06_cloud_config 06_cloud_config 11 2021.0.1 org.springframework.cloud spring-cloud-config-serverorg.springframework.cloud spring-cloud-starter-bus-amqp org.springframework.boot spring-boot-starter-actuator org.springframework.cloud spring-cloud-starter-netflix-eureka-clientorg.springframework.boot spring-boot-devtoolsruntime true org.projectlombok lomboktrue org.springframework.boot spring-boot-starter-testtest org.springframework.cloud spring-cloud-dependencies${spring-cloud.version} pom import org.springframework.boot spring-boot-maven-pluginorg.projectlombok lombok
2. 启动器
在启动器中使⽤@EnableConfigServer启⽤ConfigServer
@SpringBootApplication
@EnableConfigServer
public class ConfigApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigApplication.class, args);
}
}
3. 配置⽂件
配置⽂件application.yml代码如下。
server: port: 9006 spring: application: name: cloud-config cloud: config: server: native: search-locations: classpath:/config_repo git: uri: https://gitee.com/lxsong77/my-config.git search-paths: repo #目录 default-label: master #分支 eureka: client: service-url: defaultZone: http://127.0.0.1:9004/eureka
Config Server默认存储配置的⽅式是git,如果git仓库是公开仓库,username和password属性可以省略不配置,具体配置属性解释如下。
spring.cloud.config.server.git.uri:配置⽂件所在的git仓库
spring.cloud.config.server.git.search-paths:配置⽂件所在⽬录
spring.cloud.config.server.git.default-label:配置⽂件分⽀
4. 配置仓库
在git仓库my-config: 配置中心微服务中,创建config⽬录,在config⽬录中创建app-dev.yml配置⽂件,代码如下。
key1: v1 key2: v2 key3: v3
5. 启动并测试
Spring Cloud Config 有它的⼀套访问规则,我们通过这套规则在浏览器上直接访问就可以。
/{application}-{profile}.yml
/{label}/{application}-{profile}.yml
{application} 就是应⽤名称,对应到配置⽂件上来,就是配置⽂件的名称部分,例如我上⾯创建的配置⽂件。
{profile} 就是配置⽂件的版本,我们的项⽬有开发版本、测试环境版本、⽣产环境版本,对应到配置⽂件上来就是以 application-{profile}.yml 加以区分,例如application-dev.yml、application-test.yml、application-prod.yml。
{label} 表示 git 分⽀,默认是 master 分⽀,如果项⽬是以分⽀做区分也是可以的,那就可以通过不同的 label 来控制访问不同的配置⽂件了。
git仓库配置⽂件缓存本地⽬录 c:UsersAdministratorAppDataLocalTempconfig-repo-4882682414831344447 ,可以通过basedir属性改变。
浏览器访问http://localhost:9006/app-dev.yml
6.2.2 配置中⼼客户端改造⽀付微服务⼯程,作为配置中⼼客户端,从上述Config Server中获取application.yml的配置。
1. 添加依赖
org.springframework.cloud spring-cloud-starter-config
2. 配置⽂件
⽀付微服务项⽬中application.yml代码如下所示。
spring:
application:
name: cloud-payment-service
cloud:
config:
uri: http://localhost:9006
profile: default
label: master
config:
import: optional:configserver:http://localhost:9006
spring.config.import=optional:configserver:http://localhost:9006,指定Spring Boot项⽬从Config Server导⼊配置
spring.cloud.config.url:Config Server地址,默认localhost:8888
spring.cloud.config.profile:为git配置⽂件的后缀
spring.cloud.config.label:为访问git的分⽀。
案例中的配置服务名为cloud-payment-service(spring.application.name=cloud-payment-service),那么我们访问的就是my-config: 配置中心微服务这个git仓库下config⽬录下的application.yml(所有服务重⽤)、cloud-payment-service.yml、cloud-payment-service-default.yml,这三个配置⽂件的内容,在这三个⽂件具有相同配置的情况下,后⾯的配置会覆盖前⾯的配置。
application.yml代码如下所示。
key1: v1 key2: v2 key3: v3
cloud-payment-service.yml代码如下所示。
key2: value2
cloud-payment-service-default.yml代码如下所示。
key3: value3
此时启动Config Server访问http://localhost:9006/cloud-payment-service-default.yml,根据上⾯的分析,得到的结果是application.yml、cloud-payment-service.yml、cloud-payment-service-default.yml,这三个⽂件的内容的整合,如果有相同的配置项,后⾯的⽂件覆盖前⾯的⽂件,效果如图所示。
3. PaymentController
在PaymentController中访问配置⽂件,代码如下。
@RestController
@RequestMapping("payment")
@Slf4j
public class PaymentController {
@Value("${key1}")
private String key1;
@Value("${key2}")
private String key2;
@Value("${key3}")
private String key3;
@Value("${spring.application.name}")
private String name;
@Value("${server.port}")
private String serverPort;
@GetMapping("/{id}")
public ResponseEntity payment(@PathVariable("id") Integer id) {
log.info("key1={}, key2={}, key3={}", key1, key2, key3);
Payment payment = new Payment(id,"支付成功!服务端口:" + serverPort);
return ResponseEntity.ok(payment);
}
}
4.启动并测试
分别启动Erueka,Config Server和⽀付服务,访问http://localhost:9000/payment/123,控制台打印配置项内容key1=v1, key2=value2, key3=value3
6.2.3 本地存储配置数据虽然git存储配置数据⾮常⽅便,但是在项⽬开发阶段,使⽤git存储还是很不⽅便,Spring Cloud Config⽀持多种配置存储⽅式,⽐如默认的git,还有本地⽂件存储,JDBC,Redis等存储⽅式,这⾥介绍下本地⽂件存储,其他存储⽅式,参考官⽅⽂档。
1. 配置⽂件
在Config Server配置⽂件application.yml中,配置如下。
spring:
profiles:
active: native #使用本地配置
cloud:
config:
server:
native:
search-locations: classpath:/config_repo
spring.profiles.active=native:表示使⽤本地配置存储
spring.cloud.config.server.native.searchLocations:指定配置⽂件所在路径,可以使⽤相对路径⽐如classpath
2. 本地配置⽂件
在项⽬classpath下添加配置⽂件结构如图所示。
分别启动Erueka,Config Server和⽀付服务,访问http://localhost:9000/payment/123,控制台打印配置项内容为本地配置⽂件的内容。
6.3 配置⾃动刷新Spring Cloud Config在项⽬启动时⾃动加载配置内容这⼀机制,导致了他的⼀个缺陷,配置不能⾃动刷新,在上述案例中,修改git仓库中的key1的值"key1=v11",发现⽀付服务得到的配置项key1的值还是旧的配置内容,新的内容不会⾃动刷新过来,在微服务架构中,动辄上百个节点如果都需要重启,这个问题⾮常麻烦。
我们可以使⽤Spring Cloud Bus和Spring Boot Actuator实现⾃动刷新,实现原理如图所示。
6.3.1 启动RabbitMQSpring Cloud Bus需要发送消息给消息队列,⽀持Kafka和RabbitMQ,这⾥我们使⽤RabbitMQ,启动我们之前准备好的RabbitMQ服务器(192.168.56.110)。
6.3.2 配置中心服务端1. 添加依赖
添加spring-cloud-starter-bus-amqp和spring-boot-starter-actuator依赖
org.springframework.cloud spring-cloud-starter-bus-amqporg.springframework.boot spring-boot-starter-actuator
2. 配置⽂件
在application.yml中配置连接RabbitMQ,同时配置暴露/actuator/bus-refresh端点,代码如下。
spring:
rabbitmq:
host: 127.0.0.1
port: 5672
username: guest
password: guest
management:
endpoints:
web:
exposure:
include: bus-refresh
endpoint:
busrefresh:
enabled: true
6.3.3 配置中心客户端
1. 添加依赖
org.springframework.cloud spring-cloud-starter-bus-amqp
2. 配置⽂件
在application.yml中配置连接RabbitMQ
spring: application: name: cloud-payment-service rabbitmq: host: 192.168.56.110 port: 5672 username: guest password: guest
3. PaymentController
使⽤@RefreshScope注解刷新更改的配置,代码如下。
@RestController
@RequestMapping("payment")
@Slf4j
@RefreshScope
public class PaymentController {
@Value("${key1}")
private String key1;
@Value("${key2}")
private String key2;
@Value("${key3}")
private String key3;
@Value("${spring.application.name}")
private String name;
@Value("${server.port}")
private String serverPort;
@GetMapping("/{id}")
public ResponseEntity payment(@PathVariable("id") Integer id) {
log.info("key1={}, key2={}, key3={}", key1, key2, key3);
Payment payment = new Payment(id,"支付成功!服务端口:" + serverPort);
return ResponseEntity.ok(payment);
}
}
6.3.4 启动并测试
启动Eureka、Config Server、⽀付微服务(Config Client),修改git仓库中的配置项内容后,使⽤Postman发送POST请求给 /actuator/busrefresh(注意是是POST类型),再次访问⽀付服务,发现配置项已经自动刷新。



