SpringCloud系列(十):分布式配置管理Config之Git实现

star2017 1年前 ⋅ 589 阅读

在微服务架构集群部署的环境中,可能会有十几甚至几十个服务,若要修改项目属性参数,手动的方式是很糟糕的,所以就有了分布式配置管理这个概念。

将服务器的配置外部化,可以存在文件或数据库中,一个专门用于管理应用服务配置的应用,可以随时修改和自动更新到目标服务器上。目前已有开源项目,如,smconfdisconfQConf

Spring Cloud Config 为分布式系统中的外部化配置提供服务器端和客户端支持。使用 Config Server,可以在所有环境中管理应用程序的外部属性。Spring Cloud Config 官方文档spring-cloud-config Github

概述

Spring Cloud Config Server 默认使用 Git 来存储配置,使用 Git 管理配置文件自动支持文件的版本管理。

Config Server 特性

  • 提供基于 HTTP API 的外部配置(key-value 或 yaml 内容)。
  • 加密和解密属性值(对称或非对称)。
  • 使用 @EnableConfigServer 轻松地将 Config Server 嵌入到 Spring Boot 应用中。

Spring Cloud Config Server 默认的Git存储库方式,是将 spring.cloud.config.server.git.uri 指定的Git配置仓库克隆到本地,客户端的配置文件存储在此Git存储库中或子目里,并将之用于客户端初始化。

spring.cloud.config.server.git.uri=https://github.com/spring-cloud-samples/config-repo

Config Server 获取以下形式的配置文件供客户端应用使用:

/{application}/{profile}[/{label}]
/{application}-{profile}.yml
/{label}/{application}-{profile}.yml
/{application}-{profile}.properties
/{label}/{application}-{profile}.properties
  • application:指 spring.config.name 属性设置的 Config Client 名称,不存在则使用 spring.application.name 的值。
  • profile:应用需要使用的 profile,若没有指定,则使用默认 default。
  • label:Git 分支、标签,若没有指定,则使用默认 master。

注意spring.application.name 的值不要使用 application- 作为前缀,会导致无法正确地解析配置文件。

Config Client 特性

  • 绑定到配置服务器,并使用远程属性源初始化 Spring 环境。
  • 加密和解密属性值(对称或非对称)。
  • Spring Bean 上添加 @RefreshScope 注解即可实现在配置更改时重新初始化。
  • 使用和管理端点:
    • /env:用于更新 Environment 和重新绑定 @ConfigurationProperties 及日志级别。
    • /refresh:用于刷新 @RefreshScope beans。
    • /restart:重启 Spring Context(默认情况下禁用)。
    • /pause/resume:用于调用 Lifecycle 方法(在 ApplicationContext 上的 stop() 和 start() )。

Config Git 存储库

Config Server

  1. 创建 Config Server 项目并引入 spring-cloud-config-server 依赖。
    pom.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.1.4.RELEASE</version>
            <relativePath/> <!-- lookup parent from repository -->
        </parent>
        <groupId>com.springcloud.configserver</groupId>
        <artifactId>config-server-jdbc</artifactId>
        <version>v1.0.0</version>
        <name>config-server-git</name>
        <description>Demo project for Spring Boot</description>
    
        <properties>
            <java.version>1.8</java.version>
            <spring-cloud.version>Greenwich.SR1</spring-cloud.version>
        </properties>
    
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-config-server</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-configuration-processor</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
        </dependencies>
    
        <dependencyManagement>
            <dependencies>
                <dependency>
                    <groupId>org.springframework.cloud</groupId>
                    <artifactId>spring-cloud-dependencies</artifactId>
                    <version>${spring-cloud.version}</version>
                    <type>pom</type>
                    <scope>import</scope>
                </dependency>
            </dependencies>
        </dependencyManagement>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
            </plugins>
        </build>
    
    </project>
    
  2. 在启动类上添加 @EnableConfigServer 注解来启用Config Server。

    @SpringBootApplication
    @EnableConfigServer
    public class ConfigServerApplication {
        public static void main(String[] args) {
            SpringApplication.run(ConfigServerApplication.class, args);
        }
    }
    

    Spring Cloud Config Server 为外部配置(key-val 或等效 yaml 内容)提供基于 HTTP 资源的 API。 通过使用 @EnableConfigServer 注解,服务器可嵌入 Spring Boot 应用程序中。

  3. 创建远程 Git 仓库,用于存储客户端应用的配置文件。
    这里在 GitHub 创建配置存储库用于,库名最好与服务应用名相同。实际生产会创建私有仓库,需要授权认证才可访问。
    Config Server Git 配置存储库:https://github.com/gxing19/config-repo

  4. Config Server 配置文件
    application.properties

    # 端口
    server.port=9010
    # 应用名
    spring.application.name=config-repo
    # Git URI 使用占位符
    spring.cloud.config.server.git.uri=https://github.com/gxing19/config-repo
     #spring.cloud.config.server.git.uri=https://github.com/gxing19/${spring.application.name}
    # 设置 HTTP 连接超时时长, 单位:秒
    spring.cloud.config.server.git.timeout=10
    # 删除本地未跟踪的库
    spring.cloud.config.server.git.delete-untracked-branches=true
    

Config Client

  1. 创建 Spring Boot 应用,引入 spring-cloud-starter-config 依赖和 spring-boot-starter-actuator 依赖。
    这里创建一个订单服务的应用(orderService)来演示
    pom.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.1.3.RELEASE</version>
            <relativePath/> <!-- lookup parent from repository -->
        </parent>
        <groupId>com.springcloud</groupId>
        <artifactId>service-order</artifactId>
        <version>v1.0.0</version>
        <name>service-order</name>
        <description>Demo project for Spring Boot</description>
    
        <properties>
            <java.version>1.8</java.version>
            <spring-cloud.version>Greenwich.RELEASE</spring-cloud.version>
        </properties>
    
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-config</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-actuator</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-devtools</artifactId>
                <scope>runtime</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-configuration-processor</artifactId>
                <optional>true</optional>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
        </dependencies>
    
        <dependencyManagement>
            <dependencies>
                <dependency>
                    <groupId>org.springframework.cloud</groupId>
                    <artifactId>spring-cloud-dependencies</artifactId>
                    <version>${spring-cloud.version}</version>
                    <type>pom</type>
                    <scope>import</scope>
                </dependency>
            </dependencies>
        </dependencyManagement>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
            </plugins>
        </build>
    
    </project>
    
  2. 作为 Config Client ,必须使用 bootstrap.properties 或 bootstrap.yml 作为引导配置,在应用上下文的引导阶段起作用。
    bootstrap.properties

    # Config Server 地址
    spring.cloud.config.uri=http://localhost:9010
    # 应用名
    spring.application.name=orderService
    # 使用的 profile
    spring.profiles.active=dev
    # Actuator 演示开放所有端点(生产只开放少数必要的端点)
    management.endpoints.web.exposure.include=*
    
  3. 在远程 Git 仓库创建客户端应用的配置文件
    在 Git 配置存储库:https://github.com/gxing19/config-repo 创建配置文件 orderService-dev.properties
    orderService-dev.properties

    server.port=8010
    common.properties.app-id=QWERTYUIO
    #logging.level.root=debug
    
  4. 启动客户端应用,查看配置客户端和服务端的日志
    客户端应用(Config Client)在启动时需要向配置服务器(Config Server) 发送请求获取配置属性来完成应用初始化。看日志是在打印出 Banner 后就向配置服务器发送请求。

    Config Client 启动日志:

    2019-04-15 18:09:17.622  INFO 8944 --- [  restartedMain] c.c.c.ConfigServicePropertySourceLocator : 
    Fetching config from server at : http://localhost:9010
    
    2019-04-15 18:09:20.669  INFO 8944 --- [  restartedMain] c.c.c.ConfigServicePropertySourceLocator : 
    Located environment: name=orderService, profiles=[dev], label=null, version=5780a5068b7241badfebf68fffdc006c4a185545, state=null
    
    2019-04-15 18:09:20.669  INFO 8944 --- [  restartedMain] b.c.PropertySourceBootstrapConfiguration : 
    Located property source: CompositePropertySource {name='configService', propertySources=[MapPropertySource {name='configClient'}, 
    MapPropertySource {name='https://github.com/gxing19/config-repo/orderService-dev.properties'}]}
    
    2019-04-15 18:09:20.673  INFO 8944 --- [  restartedMain] c.s.s.order.OrderServiceApplication      : The following profiles are active: dev
    

    客户端应用启动成功,使用了远程配置文件中的 8010 端口。

    Config Server 接收到请求日志:
    克隆远程配置仓库到本地,在 Windows 系统默认是克隆到系统临时文件目录。

    2019-04-16 09:17:52.292  INFO 10176 --- [nio-9010-exec-5] .c.s.e.MultipleJGitEnvironmentRepository : 
    Fetched for remote master and found 1 updates
    
    2019-04-16 09:17:52.517  INFO 10176 --- [nio-9010-exec-5] o.s.c.c.s.e.NativeEnvironmentRepository  : 
    Adding property source: file:/C:/Users/gxing/AppData/Local/Temp/config-repo-4123568461296240052/orderService-dev.properties
    

Client 运行环境

客户端添加 spring-boot-starter-actuator 依赖,才可以通过调用端点来更新 Environment 。

根端点:http://localhost:8010/actuator

  1. 环境变量端点:http://localhost:8010/actuator/env
    从该端点可以看到客户端应用完整的运行环境,包括环境变量。bootstrap.properties 在 /env 端点显示为高优先级属性源,端点数据如下:

    {
      "activeProfiles": [
        "dev"
      ],
      "propertySources": [
        {
          "name": "server.ports",
          "properties": {
            "local.server.port": {
              "value": 8010
            }
          }
        },
        {
          "name": "configService:configClient",
          "properties": {
            "config.client.version": {
              "value": "5780a5068b7241badfebf68fffdc006c4a185545"
            }
          }
        },
        {
          "name": "configService:https://github.com/gxing19/config-repo/orderService-dev.properties",
          "properties": {
            "server.port": {
              "value": "8010"
            },
            "common.properties.app-id": {
              "value": "QWERTYUIO"
            }
          }
        },
        {...省略...},
        {
          "name": "applicationConfig: [classpath:/bootstrap.properties]",
          "properties": {
            "spring.cloud.config.uri": {
              "value": "http://localhost:9010",
              "origin": "class path resource [bootstrap.properties]:1:25"
            },
            "spring.application.name": {
              "value": "orderService",
              "origin": "class path resource [bootstrap.properties]:2:25"
            },
            "spring.profiles.active": {
              "value": "dev",
              "origin": "class path resource [bootstrap.properties]:3:24"
            },
            "management.endpoints.web.exposure.include": {
              "value": "*",
              "origin": "class path resource [bootstrap.properties]:4:43"
            }
          }
        },
        {
          "name": "defaultProperties",
          "properties": {
    
          }
        }
       ]
    }
    

动态更新属性

客户端应用的远程配置文件 orderService-dev.properties 有个自定义属性,写个 Controller 从环境中获取该值,然后改变自定义属性的值,调用 /refresh 刷新环境和变量,重新请求该属性。

  1. 创建获取自定义属性的 Controller 方法

    @RestController
    @RequestMapping("/config")
    public class ConfigController {
    
        @Autowired
        private Environment environment;
    
        @GetMapping("/refresh")
        public String refreshProperties(){
            String appId = environment.getProperty("common.properties.app-id");
            return appId;
        }
    }
    

    返回结果:QWERTYUIO

  2. 修改远程配置文件该属性值
    orderService-dev.properties

    common.properties.app-id=abcdefghijk112233
    
  3. 调用刷新环境的端点 /refresh,POST 请求

    # post 请求
    http://localhost:8010/actuator/refresh
    

    返回结果:

    [
        "config.client.version",
        "common.properties.app-id"
    ]
    
  4. 调用执行步骤 1 创建的方法,会重新拉取远程的配置文件。
    返回结果:abcdefghijk112233
    不用重启应用,调用端点刷新环境和变量,即完成了配置文件属性重载。

与 Eureka 集成

在上面示例的基础上整合 Eureka ,通过服务发现来与 Config Server 通信获取远程配置源。

注册 Config Server

  1. Config Server 作为 Eureka Server 的客户端,需要引入 eureka-client 依赖
    pom.xml

    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>
    
  2. Config Server 的配置文件添加注册到 Eureka Server 的属性
    application.properties

    # 添加注册Eureka Server
    eureka.client.service-url.defaultZone=http://admin:123456@eureka.master.com:8761/eureka,http://admin:123456@eureka.slave.com:8762/eureka
    

注册 Config Client

  1. Config Client 作为 Eureka Server 的客户端,需要引入 eureka-client 依赖

    pom.xml

    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>
    
  2. Config Client 的引导启动配置文件 bootstrap.properties 添加开启服务发现和设置 Eureka 服务地址:

    bootstrap.properties

    # 添加开启服务发现 discovery
    spring.cloud.config.discovery.enabled=true
    # 添加注册Eureka Server
    eureka.client.service-url.defaultZone=http://admin:123456@eureka.master.com:8761/eureka,http://admin:123456@eureka.slave.com:8762/eureka
    # 添加设置 Config Server 在 Eureka Server 中的 service ID
    spring.cloud.config.discovery.service-id=config-repo
    # 注释掉显式指定的 Config Server uri
    #spring.cloud.config.uri=http://localhost:9010
    
  3. 重启 Config Server 和 Config Client,查看日志

    在 Eureka Server 的 Web 控制台可以看到 Config Server 和 Config Client 注册实例。

    Config Client 在引导启动阶段,通过服务发现获取远程配置源。

    调用 Config Client 获取环境变量的 Controller 方法成功。

Config Server 高可用

Spring Cloud Config Server 实现高可用主要有两种方式:

  1. 部署多个 Config Server 实例,指向同一个存储库(Git 或 JDBC),在上层通过负载均衡器来反向代理到 Config Server。
  2. 部署多个 Config Server,集成 Eureka Client,注册到 Eureka Server,通过服务治理来实现,即可高可用,也实现了自维护。

Config 详细配置

请参阅 [ Spring Cloud系列(十二):分布式配置管理 Config 之 配置详解]http://www.gxitsky.com/article/23)

其它参考

Spring Coud Config Server 也支持使用 VaultCredHub作为存储后端,还支持复合环境存储(同时使用不同的存储库),支持通过代理访问存储后端。详细使用参考官网。

可以使用 UAA 作为提供者,通过 OAuth2.0 进行身份验证。

更多内容请访问:IT源点

相关文章推荐

全部评论: 0

    我有话说: