开始配置中心前,先模拟一个业务场景:电商系统做一次促销活动,由于无法预估促销商品的需求量,于是设置了库存为100个,研发工程师可能在代码中写死库存总数为100,由于销售火爆,100个库存无法满足人们的需求,想加大库存,那么研发工程师需要修改代码,走测试流程,再发布到正式环境,一个小参数的改动却要花费大量重复时间。于是通过代码重构,可以把这个库存参数写到远程配置中(本地静态配置运行时无法动态修改生效),定时获取配置信息,下次做促销活动时,快速调整库存,这是配置中心能提供的功能。
配置中心的真实场景还能做很多事情,如:根据网站的实时流量调整限流参数,扩大线程池大小,开启-关闭新功能,线上功能调式等
市面上开源的配置中心有很多,BAT每家都出过,360的QConf、淘宝的diamond、百度的disconf都是解决这类问题,Spring Cloud全家桶中就有 Spring Cloud Config组件,利用 Git 服务与 rabbitmq ,通过消息总线实现动态配置,它有以下特性:
- 提供服务端和客户端支持(服务端把配置文件拉取后存储在本地,客户端通过接口获取服务端拉下来的数据)
- 集中管理配置文件
- 配置文件修改之后,可快速生效
- 支持较大并发查询
本篇要分享的内容:Spring Cloud Config + Spring Cloud Bus + RabbitMQ 实现配置中心配置动态更新,当在 Git 仓库中某个应用配置文件中的参数更新后,只需要通过Git的 WebHook 发送 POST请求到 config Server 的 /actuator/bus-refresh 接口,通过消息总线通知rabbitmq,让所有的客户端服务实例更新配置
1.项目依赖环境
- 需要 Git 仓库,且创建个spring-config-repo的项目,并添加配置文件(对应开发、测试、正式环境),内容如图,这是我的示例仓库与文件内容
- rabbitmq环境
- eureka 注册中心
- 两个项目,config-server服务端、config-client客户端,当Git上的配置文件修改时,server端会拉取配置信息到本地,client端会取server端的信息
- 要达到目标:外部配置文件集中放置在一个Git仓库里,用来管理所有的配置文件,维护的时候需要更改配置时,只需要在本地更改后,推送到远程仓库,所有服务实例都可以通过config server来获取配置文件,当有配置更新时通过 Git 的 WebHook 发送 Post 请求,调用 config-server 暴露的 /actuator/bus-refresh 刷新端点,通过消息总线去动态刷新 config-client 的配置
2.代码实践
- 启动 eureka 注册中心
- 新建 config-server 项目,添加依赖:
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-config-server</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-bus-amqp</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-netflix-eureka-client</artifactId> </dependency>
- 在config-server的启动类上添加注解,启用配置服务与服务注册发现
@EnableConfigServer @EnableDiscoveryClient @SpringBootApplication public class ConfigServerApplication { public static void main(String[] args) { SpringApplication.run(ConfigServerApplication.class, args); } }
- 给config-server添加配置两个文件,bootstrap.yml与application.yml,注意:bootstrap.yml的配置先加载
# bootstrap.yml # 暴露监控端点 management: endpoints: web: exposure: include: '*' endpoint: health: show-details: always
# application.yml # config-server会在本地的临时目录下克隆远程仓库中的信息,默认保存在本机:C:\Users\zhuyu\AppData\Local\Temp\config-repo-315688083391782199\config-info # 可更改本地仓库clone的配置文件信息的路径 spring.cloud.config.server.git.basedir=D:\\localGitRepo\\ server: port: 2000 spring: application: name: config-server cloud: bus: trace: enabled: true config: server: git: uri: https://gitee.com/zhuyu1991/spring-config-repo.git # git仓库的地址 searchPaths: config-info # git仓库地址下的相对地址,可以配置多个,用,分割 username: 用户名 # git仓库的账号 password: 密码 # git仓库的密码 rabbitmq: host: 192.168.1.101 port: 5672 username: 用户名 password: 密码 virtual-host: zy_vhosts # 注册中心配置 eureka: instance: prefer-ip-address: true client: service-url: defaultZone: http://zy:zy123@localhost:10025/eureka/
- 访问 http://localhost:2000/config-info/dev ,看到以下内容,说明config-server端搭建成功了,红色框框内容的是远程 Git 仓库中的配置信息
- 新建 config-client 项目,添加依赖:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-config-client</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-bus-amqp</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency>
- 在config-client的启动类上添加注解,启用服务注册发现@EnableDiscoveryClient
- 给config-client添加配置两个文件,bootstrap.yml与application.yml,注意:bootstrap.yml的配置先加载,一定要在bootstrap.yml中先配置 config-server的地址,不然无法获取server端的配置信息
# bootstrap.yml spring: cloud: bus: trace: enabled: true config: label: master # label代表要请求那个git分支 #uri: http://localhost:2000 # uri代表config-server的地址,可指定地址 name: config-info # name代表请求那个名称的远程文件,与confif-server的searchPaths对应,可以写多个,逗号分隔 profile: dev # profile代表那个分支文件,如:dev 、 test 、 prod # 这里基于服务发现,不指定uri discovery: enabled: true service-id: config-server # 注册中心配置 eureka: instance: prefer-ip-address: true client: service-url: defaultZone: http://zy:zy123@localhost:10025/eureka/
# application.yml server: port: 2002 spring: application: name: config-client rabbitmq: host: 192.168.1.101 port: 5672 username: 用户名 password: 密码 virtual-host: zy_vhosts
- 添加一个 IndexController 控制器,用来返回配置信息,注意@RefreshScope注解,只有添加这个注解才能运行时刷新,内容如下:
@RestController @RefreshScope public class IndexController { // 配置远程 git 仓库中配置文件的 key,且设置个默认值 @Value("${springcloud.book.config:123}") private String book; @RequestMapping("/getBookConfig") public String getBookConfig(){ return book; } }
- 远程Git仓库中的配置文件内容
- 启动 config-client 项目,访问 http://localhost:2002/getBookConfig ,说明 config-client 取到 config-server端的内容了
- 修改Git仓库中配置文件的内容,访问 config-server端,能看到配置信息更新了,但 config-client 端的内容依旧是之前的,此时需要通过发送 Post 请求 http://localhost:2000/actuator/bus-refresh,来触发刷新配置
- 再访问 http://localhost:2002/getBookConfig ,可看到配置已经刷新了
好了,Spring Cloud Config 做配置中心的功能分享到这里,它需要依赖 spring-cloud-starter-bus-amqp 消息队列总线 和 spring-boot-starter-actuator 暴露刷新接口,以及 @RefreshScope 注解共同完成刷新操作
需要注意的是有两个配置文件,配置文件加载的先后顺序要注意,bootstrap.yml里面的内容要先加载