分布式配置——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閱讀 227,967評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,273評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,870評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,742評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,527評論 6 407
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,010評論 1 322
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,108評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,250評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,769評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,656評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,853評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,371評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,103評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,472評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,717評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,487評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,815評論 2 372