Spring Cloud 配置中心

开始配置中心前,先模拟一个业务场景:电商系统做一次促销活动,由于无法预估促销商品的需求量,于是设置了库存为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里面的内容要先加载