基于Spring Cloud的应用通常由多个Spring Boot微服务构成。每个微服务均需要一些如数据库地址,用户名/密码,缓存服务地址等通用配置。因此,我们需要统一管理这些配置,实现:

  1. 多个微服务使用统一配置,如不同服务使用同一数据库,和
  2. 同一微服务使用不同配置:如开发、测试、发布,

两种场景下的配置同步与维护。Spring Cloud提供了一个基于Config Server的配置管理方案,通过将所有配置文件放在共享存储中,基于版本管理实现不同场景下的配置文件的管理。

原理

其基本原理如下图所示。

config server 原理

一般实践中会配置多个Config Server实例,以实现高可用。配置以文件形式存储在版本管理工具如:Git,码云,SVN等,或本地文件系统中。支持配置文件中的属性加密。Client 是指单独的微服务实例,通过访问Config Server可分别获取各自的配置文件。运维人员可通过Push本地配置文件修改Config Server上的配置文件,配置通过SpringCloud BUS或版本仓库的WebHooks实现自动刷新。

Config Server构建

  1. 创建Spring Boot项目config-server, 添加spring-cloud-config-server 依赖
//gradle
project("config-server"){
	description="a simple config server example"
	dependencies {
        compile("org.springframework.cloud:spring-cloud-starter-eureka")
		compile("org.springframework.cloud:spring-cloud-starter-eureka-server")
        compile("org.springframework.cloud:spring-cloud-config-server")

		compile("org.springframework.boot:spring-boot-starter-test")
    }
}

2. 创建Spring Boot应用,@EnableConfigServer注解说明此应用为配置服务,使用@EnableDiscoveryClient 将其注册到服务中心。

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.config.server.EnableConfigServer;

@SpringBootApplication
@EnableConfigServer
@EnableDiscoveryClient
public class ConfigApplication {

	public static void main(String[] args) {
		SpringApplication.run(ConfigApplication.class, args);
	}
}

3. 配置config-server, 在resouce 目录下找application.yml 或application.properties文件,如果未找到,就地创建一个。主要配置服务注册信息和版本管理仓库的访问信息。

server:
  port: ${port:CONFIG_SERVER_PORT}

eureka:
  client:
    serviceUrl:
      defaultZone: ${eureka.serviceUrl:http://EUREKA_HOST:EUREKA_PORT/eureka/}

spring:
  application:
    name: config-server
  cloud:
    config:
      label: master
      server:
        git:
          uri: ${git.uri:http://username@GIT_HOST:GIT_PORT/gitRepo/config-server.git}
          username: ${git.username:USERNAME}
          password: ${git.password:PASSWORD}
          search-paths: ${git.paths:*}

4. 启动config server并验证。在浏览器中输入http://CONFIG_SERVER_HOST:PORT/application/profile 看到xml格式的响应则说明config server 部署成功。

Config Client 构建

  1. 在应用中添加依赖
//gradle 
compile("org.springframework.cloud:spring-cloud-starter-config")

2. 在应用中访问配置项,即可在其他地方引用此配置项。

//
@Value("${config-item-name}")
String config_item;

3. 配置config server访问信息,此应用对应的配置文件路径为:http://config-server-host/config-client/dev。

//bootstrap.yml或bootstrap.properties
spring:
  application:
    name: config-client
  cloud:
    config:
      label: master
      profile: dev
      uri: CONFIG_SERVER_HOST
server:
  port: PORT

配置自动更新

至此,spring cloud的配置管理简单示例已经完成,但client 不能自动感知服务端的变化。 比如,我们修改了GIT中的文件内容,但无论如何刷新client端的页面,都不能反映配置的变化。Spring Cloud提供refresh方法来实现客户端配置自动刷新。客户端向外公开一个/refresh接口,外部通过POST请求调用/refresh接口即可启动配置更新过程。为此,需要对客户端进行一些改造。

  1. 添加依赖spring-boot-starter-actuator。spring-boot-starter-actuator是一套监控的功能,可以监控程序在运行时状态,其中就包括/refresh的功能。
//gradle
		compile("org.springframework.boot:spring-boot-starter-actuator")

2. 开启refresh机制,需要给加载配置变量的类上面增加@RefreshScope注解,其他代码不做改变,那么在/refresh接口被调用时,就会更新此类下的配置项的值。

@RefreshScope
public class ClientService {

	@Value("${config-item-name}")
	String config_item;

}

3. 如果有多个服务,可以通过SpringCloud Bus推送配置更新通知,调用refresh命令。SpringCloud Bus需要消息中间件,如RabbitMQ的支持,让每个客户端服务订阅主题消息,当接收到新配置文件push到版本管理仓库的消息后,可通过访问http://client_host/refresh刷新实现多个客户端微服务的配置。如果要实现自动刷新,还需要集成Jenkins 或者git版本仓库的WebHook机制。通过监听版本库的改动,自动调用/refresh命令。基本过程如下图:

基于Webhook机制自动更新配置

参考:

  1. 使用Spring cloud config 来统一管理配置文件
  2. Spring Cloud 入门教程(二): 配置管理
  3. SpringCloud-微服务配置统一管理SpringCloud Config