分布式配置——Spring Cloud Configuration

(git上的源码:https://gitee.com/rain7564/spring_microservices_study/tree/master/third-spring-cloud-configuration)
众所周知,将配置硬编码到业务代码中是一个非常不明智的选择。很多时候,我们都会被强制要求将应用的配置信息从代码中分离出来。一般的做法是定义一个常量类,将所有配置信息都集中在这个类中。但是这种做法有一个很明显的弊端,每当需要修改某个或某些配置,就必须修改这个类,应用程序也必须重新编译然后部署。要避免这种情况,必须把配置信息从应用程序代码中完全分离出来。如果能够实现,其带来的好处是再也不用因为仅仅修改一点配置而重新编译整个应用了,不过,问题也是明显的,必须要有另外一个独立的小应用/微服务来支撑这一功能,而且如何将这个独立出来的小应用与原来的应用关联起来也是一个难题。

一开始,许多开发团队转向使用property文件(如:YAML、JSON、XML)来存储这些配置信息。这种方式,适用于一些小应用,或服务较少的应用;但是如果是应用于基于云的应用程序,由于云应用一般会包含成百上千个微服务,而且每个微服务都会有多个实例,这些数量庞大的配置文件的管理会让人崩溃的。应用配置信息的管理成为一个急需破解的大难题。

需要注意的是,基于云的应用强调:

  1. 配置信息从应用程序主体代码中完全分离出来;
  2. 应用部署到不同环境时不会对应用产生影响,即在不修改任何文件的情况下,应用可以运行在任何环境,如开发环境或生产环境;
  3. 应用的微服务在启动时,可以通过一个运行环境变量从配置信息中央存储库获取相应运行环境的配置信息。

本章节会介绍如何搭建一个适用基于的微服务应用的配置信息服务器(config server)。

管理繁杂的配置

运行在云的众多微服务实例需要快速启动,而且尽可能地减少人为介入,所以管理应用的配置成为关键的一环。接下来通过了解下面四个概念开始讨论如何管理应用的配置信息:

  1. 分离:我们需要把配置信息从应用的主体代码中分离出来。应用的配置信息不应该跟随着服务实例一起发布,而是在服务启动时,通过一个运行环境变量从配置信息中央存储库获取相应运行环境的配置信息。
  2. 抽象:把配置信息的获取抽象出来并封装成一个接口。应用通过一个基于REST JSON的服务从远程获取配置信息数据,而不是写代码直接从文件中读取或使用JDBC从数据库获取。
  3. 集中:因为一个云应用一般都会有成百上千个微服务,若不将配置信息集中起来,那么必须要有大量不同的数据储存库支撑(每个微服务都有自己的域,不能跨域直接访问属于别的服务的数据库表),所以要将应用的配置信息集中起来管理,尽可能减少存配置信息储库数量。
  4. 健壮:因为云应用的所有配置信息与代码中完全分离并集中管理,所以如何实现高可用和备份成为了关键。

另外,需要注意的是,配置信息与应用主体代码完全分离,意味着应用多出了一个额外的依赖,并需要去管理和版本控制。必须强调的是应用的配置信息数据必须是可追踪和版本控制的,因为配置信息管理混乱,会滋生出许多难以调试的bug和未知故障。

配置管理架构

先来看一张图,下图展示了微服务的生命周期,而从配置信息存储库获取配置发生在服务的启动阶段。

服务在启动阶段读取配置信息

下图列出启动阶段获取配置信息更详细的说明:


配置管理架构

至此,我们已经了解配置信息管理在理论上的架构,对其的实现有许多不同优秀的实现方案,具体有:Etc、Eureka、Consul、Zookeeper、Spring Cloud configuration server等。而我们主要学习一个简单易学而且功能强大的实现方案——Spring Cloud configuration server。

Spring Cloud configuration server

选择Spring Cloud configuration server是有原因的,如下:

  1. 简单易学而且功能强大;
  2. 由Spring Boot集成;
  3. 提供了多种后端来存储配置信息数据;
  4. 可直接与Git集成
    而其它的工具(Tecd,Consul,Eureka)不提供版本控制,而如果需要这一功能,开发者只能自己实现。下面开始搭建Spring Cloud configuration server。
搭建Spring Cloud configuration server

Spring Cloud configuration server是一个基于REST的服务,建议将其嵌入到已有的Spring Boot应用中,而不是独立出一个应用。

创建白板微服务

类似之前创建的license服务和organization服务,在之前搭建的应用中继续创建一个名为"config-server"的微服务。

修改pom文件

config-server服务的pom文件:

<?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>

    <groupId>cn.study.microservice</groupId>
    <artifactId>config-server</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>config-server</name>
    <description></description>

    <parent>
        <groupId>cn.study.microservice</groupId>
        <artifactId>third-spring-cloud-configuration</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-config-server</artifactId>
        </dependency>
    </dependencies>

</project>

上面的pom文件配置,引入了三个依赖:

  • spring-cloud-starter-eureka:要将该服务注册到eureka并由它托管,该依赖是必不可少的。
  • spring-cloud-config-server:要让一个服务成为配置管理的服务端,必须引入该启动依赖。该依赖包含了spring-cloud-config-server的核心类库。
启动类

config-server服务的启动类:

@SpringBootApplication
@EnableEurekaClient
@EnableConfigServer
public class Application {

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

观察上面的代码,相比如license服务的启动类,多出了一个注解——@EnableConfigServer,该注解使config-server服务成为一个Spring Cloud Config服务(服务端)。

application.yml文件

该文件最重要的是配置了其他服务的配置信息存放的位置和路径(如git、svn、本地文件系统甚至位于Spring Cloud configuration服务的classpath路径下)。本章节会对如何使用git和classpath作为其他服务配置信息的数据存储库。

另外,本章节会用license服务作为如何使用Spring Cloud Configuration的示范。假设license服务可能会运行在三种运行环境中:default(默认)、dev(开发)、prod(生产),这三种不同的运行环境,有对应不同的配置文件。这些配置文件有一个约定的命名规则:appname-env.yml,如下图所示:

image.png

config-server的application.yml文件配置:

server:
  port: 8888

eureka:
  instance:
    preferIpAddress: true
  client:
    registerWithEureka: true
    fetchRegistry: true
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/

spring:
  profiles:
    active: native
  cloud:
    config:
      server:
        native:
          search-locations: classpath:config/,classpath:config/licenseservice

我们会先使用服务的classpath来作为存储库,端口和eureka配置就不过多赘述,重点关注最后一段配置。spring.cloud.config.server可以配置使用哪种存储方式来存放配置信息数据,目前支持本地(native,包括classpath、filesystem)、版本控制(version control,包括git、svn)。显然,上面的配置使用的是native的classpath方式。而spring.cloud.config.server.native.search-locations则是config-server服务会去扫描的路径/文件夹。另外,当配置信息存储方式为native,必须配置属性spring.profiles.active,而且值只能为native。

存储库

在application.yml文件相同的目录下,创建一个config目录,所有的配置信息文件都放在这个目录下。本章节使用license服务做示范,所以在config目录下创建一个licenseservice目录,该目录存放各种环境对应的配置文件。创建一个application.yml文件,并将原先license服务的application.yml文件中的内容剪切到该文件中,并增加一条属性:example.property,如下:

example.property: I am the default.

server:
  port: 10000

#服务发现客户端
eureka:
  instance:
    #向Eureka注册时,是否使用IP地址+端口号作为服务实例的唯一标识。推荐设置为true
    prefer-ip-address: true

  client:
    #是否将自身的实例信息注册到Eureka服务端
    register-with-eureka: true
    #是否拉取并缓存其他服务注册表副本到本地
    fetch-registry: true
    #注册到哪个Eureka服务实例
    service-url:
      defaultZone: http://localhost:8761/eureka/

#数据源的配置
spring:
  datasource:
    platform: h2
    schema: classpath:schema.sql
    data: classpath:data.sql
    driver-class-name: org.h2.Driver
  jpa:
    show-sql: true
    hibernate:
      ddl-auto: none

至此,最简单的Spring Cloud Config服务搭建完成。下面开始学习如何使用该服务。

Spring Cloud configuration server使用
启动服务

先启动eureka服务,再启动config-server服务,然后使用postman访问http://localhost:8888/licenseservice/default,可以看到如下返回结果:

image.png

其中name='licenseservice"属性代表获取服务licenseservice的配置信息,而propertySource.name则是获取的配置信息来自哪个路径的哪个文件,此处为"classpath:config/licenseservice/licenseservice.yml",很明显获取的是我们刚刚在config-server服务创建的licenseservice.yml文件。

若在config-server的classpath:config/licenseservice路径下再创建两个yml文件,分别为:licenseservice-dev.yml,licenseservice-prod.yml,如下:
licenseservice-dev.yml:

example.property: I am the dev.

server:
  port: 10001

#服务发现客户端
eureka:
  instance:
    #向Eureka注册时,是否使用IP地址+端口号作为服务实例的唯一标识。推荐设置为true
    prefer-ip-address: true

  client:
    #是否将自身的实例信息注册到Eureka服务端
    register-with-eureka: true
    #是否拉取并缓存其他服务注册表副本到本地
    fetch-registry: true
    #注册到哪个Eureka服务实例
    service-url:
      defaultZone: http://localhost:8761/eureka/

#数据源的配置
spring:
  datasource:
    platform: h2
    schema: classpath:schema.sql
    data: classpath:data.sql
    driver-class-name: org.h2.Driver
  jpa:
    show-sql: true
    hibernate:
      ddl-auto: none

licenseservice-prod.yml:

example.property: I am the prod.

server:
  port: 10002

#服务发现客户端
eureka:
  instance:
    #向Eureka注册时,是否使用IP地址+端口号作为服务实例的唯一标识。推荐设置为true
    prefer-ip-address: true

  client:
    #是否将自身的实例信息注册到Eureka服务端
    register-with-eureka: true
    #是否拉取并缓存其他服务注册表副本到本地
    fetch-registry: true
    #注册到哪个Eureka服务实例
    service-url:
      defaultZone: http://localhost:8761/eureka/

#数据源的配置
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/prodDB?useSSL=true&characterEncoding=utf8
    username: root
    password: root
    tomcat:
      validation-query: "select 1"
      max-active: 20
      max-wait: 100000
      test-while-idle: true
      min-idle: 5
      initial-size: 5
  jpa:
    database: MYSQL
    show-sql: true
    hibernate:
      ddl-auto: update
      naming-strategy: org.hibernate.cfg.ImprovedNamingStrategy
      use-new-id-generator-mappings: true
    properties:
      hibernate:
        dialect: org.hibernate.dialect.MySQL5Dialect

重启config-server服务,然后依次访问http://localhost:8888/licenseservice/devhttp://localhost:8888/licenseservice/prod,可以看到如下返回结果:

image.png
image.png

观察上面两个返回结果,可以看出,当请求的不是default,那么返回的结果除了请求配置文件信息外,还会将default对应的配置信息返回。原因是Spring框架对properties的解析采用了"等级分层机制",即当Spring在做property解析时,会优先解析default配置文件,即application.yml,然后当有其他特殊环境的配置文件需要解析时(使用spring.profiles.active属性控制),则会将对应文件解析,然后把解析得到属性集合合并,当存在相同的属性时,从default配置文件解析得到的会被覆盖。所以可以在application.yml文件配置共享的属性,而其它配置文件中则配置那些在不同运行环境中,属性值不同的属性。此处,default配置文件就是"licenseservice.yml",而特殊环境的配置文件则有"licenseservice-dev.yml"、"licenseservice-prod.yml"。

搭建Spring Cloud configuration client

本章节会使用license服务做示范,将license服务修改成Spring Cloud configuration的客户端,即配置信息从Spring Cloud configuration server获取。

pom文件

毋庸置疑,要想让license服务成为config-server的客户端,必须引入类似spring-cloud-config-client的依赖。所以在原来license服务的pom文件中加入如下spring cloud提供的启动依赖:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-config-client</artifactId>
</dependency>
其他修改

然后把license服务的application.yml文件删掉,因为该文件中的所有配置,将会从config-server服务获取。而bootstrap.yml文件保留,该文件是第一个会被加载的文件,该文件一般用来配置属性:spring.application.name,而且还可以配置服务默认的启动环境:spring.profiles.active,还有一个最重要的:spring.cloud.config.uri,该属性用来指向Spring Cloud Config的服务端。当然,还会有其他更多的配置,以后的章节会逐渐完善。此时,bootstrap.yml配置如下:

spring:
  application:
    name: licenseservice
  profiles:
    active:
      default
  cloud:
    config:
      uri: http://localhost:8888

上面配置的属性中,spring.cloud.config.uri=http://localhost:8888,就是讲Spring Cloud Config的服务端指向config-server服务。

最后,新建一个服务配置信息类,路径为:cn.study.microservice.license.config.ServiceConfig,然后将ServiceConfig注入Licenseservice中,涉及代码如下:
ServiceConfig:

@Component
public class ServiceConfig{

    @Value("${example.property}")
    private String exampleProperty;

    public String getExampleProperty(){
        return exampleProperty;
    }
}

Licenseservice:

@Service
public class LicenseService {

    @Autowired
    private ServiceConfig serviceConfig;
    ...
    public License getLicense(String organizationId, String licenseId, String clientType) {
        License license = licenseRepository.findByOrganizationIdAndLicenseId(organizationId, licenseId);

        Organization org = retrieveOrgInfo(organizationId, clientType);

        return license
                .withOrganizationName( org.getName())
                .withContactName( org.getContactName())
                .withContactEmail( org.getContactEmail() )
                .withContactPhone( org.getContactPhone() )
                .withComment(serviceConfig.getExampleProperty());
    }

}

经过上边的几步简单的修改,就可以将license服务变成一个Spring Cloud Config的客户端。现在启动license服务。

Spring Cloud Config客户端验证
spring.profiles.active: default

启动license服务(记得organization服务也要启动)。查看控制台的输出,类似如下:


从config-server获取配置信息,spring.profiles.active: default

可以看到,license服务再启动时,从http://localhost:8888,即config-server获取配置信息,而且profiles=[default],与在boostrap.yml中配置的spring.profiles.active一致。

然后访问(localhost:10000/v1/organizations/e254f8c-c442-4ebe-a82a-e2fc1d1ff78a/licenses/f3831f8c-c338-4ebe-a82a-e2fc1d1ff78a/feign)[localhost:10000/v1/organizations/e254f8c-c442-4ebe-a82a-e2fc1d1ff78a/licenses/f3831f8c-c338-4ebe-a82a-e2fc1d1ff78a/feign],不出意外的话,返回的License对象的comment的值为"I am the default."。得到如下结果:

image.png
spring.profiles.active: dev

你可以选择将bootstrap.yml中的spring.profiles.active设置为:dev,也可以使用更简单的方法,操作如下:
点击下图的"Edit Configurations...",之后会出现一个弹框:

image.png

在出现的弹框中选择license服务对应的启动器,然后在"VM options"选项中输入"-Dspring.profiles.active=dev",最后点击OK即可。

image.png

然后重新启动服务,查看控制台输出,类似如下:

从config-server获取配置信息,spring.profiles.active: dev

接下来使用git作为配置信息的存储库。

使用git作为配置信息的存储库

修改config-server的application.yml文件,如下:

...
spring:
  profiles:
    active: git
  cloud:
    config:
      server:
        git:
          uri: https://gitee.com/spring-cloud-study/spring-cloud-study-config-repo
          search-paths: licenseservice
          username: 3257722804@qq.com
          password: a12345678

其中,spring.cloud.config.server.git.uri为git仓库的地址;spring.cloud.config.server.git.search-paths为Spring Cloud Config server从git仓库加载配置文件的相对路径,一个服务对应一个路径,当有多个时,用逗号隔开,如"licenseservice,organizationservice";spring.cloud.config.server.git.username、.password则是git仓库的账号和密码。

注意,属性spring.profiles.active: native,必须修改成非native,或直接去掉。因为如果还是native,Spring Cloud Config server会因为找不到spring.cloud.config.server.native.search-locations而报错。若该属性不为native,默认会使用git方式,将git版本库作为配置信息存储库。读者可以自行尝试,并查看控制台打印出的错误信息。

若想使用svn配置存储库,需要额外引入一个依赖,如下:

<dependency>
    <groupId>org.tmatesoft.svnkit</groupId>
    <artifactId>svnkit</artifactId>
    <version>1.8.10</version>
</dependency>

然后,修改spring.cloud.config.server.uri、.username、.password三个属性。通过上面的配置修改,就可以使用SVN作为存储库来存储配置文件了,而对于客户端来说,这个过程是透明的,不需要做任何变动。

对于客户端来说,这个过程也是透明的,不需要做任何变动。所以直接重启config-server服务,然后重新访问localhost:10000/v1/organizations/e254f8c-c442-4ebe-a82a-e2fc1d1ff78a/licenses/f3831f8c-c338-4ebe-a82a-e2fc1d1ff78a/feign。可以看到如下返回结果:

image.png

至此,Spring Cloud Config的基础已经学习完毕,大家可能有一个疑问,开发团队如何使用Spring Cloud configuration server实现当服务的配置变更后能够动态刷新。下面开始讲属性配置的刷新的内容。另外读者可以将git上的配置克隆到自己的git仓库,为接下来要讲的属性配置的刷新做准备。

使用Spring Cloud configuration server刷新服务的属性

Spring Cloud config强大的地方就在可以动态刷新服务的配置信息。然而,Spring Boot应用(此处用license服务做例子)只能在服务启动的时候才能加载它们的配置信息,所以在Spring Cloud configuration server对license服务的配置做出的变更,license服务没办法主动去获取新的配置。所以Spring Boot提供一个@RefreshScope注解来解决这一问题,使用该注解后,开发团队可以使用"/refresh"来让应用主动重新读取配置信息。下面是@RefreshScope注解的使用方法:

@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
@RefreshScope
public class Application {
    
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
    ...
}

需要注意的是,@RefreshScope注解虽然让应用拥有重新加载配置信息的能力,但只能加载那些定制的Spring properties,而类似database这样的Spring Data属性,则没办法重载。可以使用http://<yourserver>:port/refresh接口,http方法为POST,来让服务重新加载配置。

但是在用postman访问该接口前,还需要做一些改动。向pom文件和license服务的boostrap.yml文件添加一些东西。如下:
pom文件:

...
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
...

pom文件添加了启动依赖——spring-boot-starter-actuator,实际上添加的是spring-boot-actuator依赖。Spring Boot Actuator的关键特性是在应用程序里提供众多Web端点,通过它们了解应用程序运行时的内部状况。有了Actuator,你可以知道Bean在Spring应用程序上下文里是如何组装在一起的,掌握应用程序可以获取的环境属性信息,获取运行时度量信息的快照。Actuator提供了多个端点,如下表所示:

HTTP方法 路径 描述
GET /autoconfig 提供了一份自动配置报告,记录哪些自动配置条件通过了,哪些没通过
GET /configprops 描述配置属性(包含默认值)如何注入Bean
GET /beans 描述应用程序上下文里全部的Bean,以及它们的关系
GET /dump 获取线程活动的快照
GET /env 获取全部环境属性
GET /env/{name} 根据名称获取特定的环境属性值
GET /health 报告应用程序的健康指标,这些值由 HealthIndicator 的实现类提供
GET /info 获取应用程序的定制信息,这些信息由 info 打头的属性提供
GET /mappings 描述全部的URI路径,以及它们和控制器(包含Actuator端点)的映射关系
GET /metrics 报告各种应用程序度量信息,比如内存用量和HTTP请求计数
GET /metrics/{name} 报告指定名称的应用程序度量值
POST /shutdown 关闭应用程序,要求 endpoints.shutdown.enabled 设置为 true
GET /trace 提供基本的HTTP请求跟踪信息(时间戳、HTTP头等)

如果创建一个只引入"spring-boot-starter-web"、"spring-boot-starter-actuator"两个启动依赖的Spring Boot应用,然后启动,可以看到控制台的输出类似如下:


image.png

拖动滚动条,可以看到如下输出:


image.png

可以看出这些端点都是由spring-boot-actuator这个jar包提供的。打开这个jar包,可以看到类似如下:
image.png

说了这么多,都没有看到与"/refresh"这个端点有关的。这是可以理解的,因为我们刚刚在说的是由Spring Boot提供的Actuator包,而"/refresh"是Spring
Cloud Config客户端服务用来重新刷新配置用的,必须有Spring
Cloud Config的支持,注解@RefreshScope的路径为"org.springframework.cloud.context.config.annotation.RefreshScope",所以"/refresh"自然是由Spring Cloud提供的。但既然是Spring Cloud的提供的,为什么还要花那么多精力去说Spring Boot提供的"spring-boot-starter-actuator",因为只有引入该启动依赖Spring Cloud微服务才会开启监控模式,也可以说是起到一个开关的作用。当然,Spring Cloud还暴露了其他的端点,读者可以在控制台找找。读者也可以试一下,若不引入"spring-boot-starter-actuator"服务还有没有暴露"/refresh"这个端点。
bootstrap.yml文件:

...
management:
  security:
    enabled: false
...

应用的运行信息对于应用本身来说,这些都属于敏感信息。所以如果不加上这个配置,直接访问的话,会报401,即没有通过认证。这个配置是暂时关闭安全认证。

代码全部修改完后,重启服务。可以看到控制台有如下类似输出:

image.png

访问localhost:10000/v1/organizations/e254f8c-c442-4ebe-a82a-e2fc1d1ff78a/licenses/f3831f8c-c338-4ebe-a82a-e2fc1d1ff78a/feign,结果如下:
image.png

然后,修改git上的配置,将/licenseservice/licenseservice.yml中属性example.property的值从"I am the default. I am from git now."变成"I am the default. I am from git now. I am refreshed."。修改完提交,可以看到git上的配置已经改变,如下:
image.png

然后,用postman访问localhost:10000/refresh,注意,访问方法要选择POST。首先,返回的结果是:
image.png

可以看到该接口的返回结果是一个列表,而且返回的是已经被修改的属性,若配置配置没变更,则返回一个空列表。然后,如果查看license服务的控制台,如下:
image.png

这时,你可能会很高兴地跑去访问localhost:10000/v1/organizations/e254f8c-c442-4ebe-a82a-e2fc1d1ff78a/licenses/f3831f8c-c338-4ebe-a82a-e2fc1d1ff78a/feign,然而,看到结果的你眼泪差点掉下来,心中顿时万马奔腾。结果如下:
image.png

为什么会这样呢,明明已经从Spring Cloud Config server拉取最新的配置了,可以结果却没发生任何变化。首先,license服务实例已经从config server获取到最新的配置了,这点毋庸置疑,说到这里,大家应该会想到一个可能性,那就是缓存。能想到这个的童鞋,可以吃点零食奖励下自己,因为你答对了,就是因为缓存导致的。

先来看RefreshScope的解释:

Note that all beans in this scope are only initialized when first accessed, so the scope forces lazy initialization semantics. The implementation involves creating a proxy for every bean in the scope, so there is a flag

If a bean is refreshed then the next time the bean is accessed (i.e. a method is executed) a new instance is created. All lifecycle methods are applied to the bean instances, so any destruction callbacks that were registered in the bean factory are called when it is refreshed, and then the initialization callbacks are invoked as normal when the new instance is created. A new bean instance is created from the original bean definition, so any externalized content (property placeholders or expressions in string literals) is re-evaluated when it is created.

可以看出,加上该注解的Bean会被延迟加载,只有在第一次访问时才初始化。当该Bean被刷新后,即调用了"/refresh"端点,在下次访问时会创建一个新的Bean,而之前引用旧Bean的Bean,会引用新创建的Bean。所以讲@RefreshScope加载启动类上是没效果的,甚至是加在LicenseServiceController、LicenseService也是没效果的,只有加在注入example.property这一属性的Bean——ServiceConfig上才有效果。如下:

package cn.study.microservice.license.config;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Component;

@RefreshScope
@Component
public class ServiceConfig {

    @Value("${example.property}")
    private String exampleProperty;

    public String getExampleProperty(){
        return exampleProperty;
    }
}

有兴趣的话,可以看这篇文章:Spring Cloud 是如何实现热更新的

保护敏感配置信息

默认情况下,Spring Cloud configuration server管理的所有配置文件中的数据都是无格式的文本,但这些数据却包含了许多敏感信息,如数据库认证信息。所以Spring Cloud Config支持使用对称和非对称的加密算法对这些敏感数据进行加密。

由于这个教程只是一个入门教程,不会讲太深入,在这里只提一下Spring Cloud Config还有通过加密的方式保护配置信息数据,具体实现大家可以先自行查阅资料,对于Spring Cloud以后可能会出一个进阶教程,希望大家多多支持,如果有哪些地方讲得不合理,也希望能指出,大家共同进步。

完。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,839评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,543评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,116评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,371评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,384评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,111评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,416评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,053评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,558评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,007评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,117评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,756评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,324评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,315评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,539评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,578评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,877评论 2 345