Spring Cloud Config 是 Spring Cloud 團隊創建的一個全新項目,用來為分布式系統中的基礎設施和微服務應用提供集中化的外部配置支持,它分為服務端與客戶端兩個部分。其中服務端也稱為分布式配置中心,它是一個獨立的微服務應用,用來連接配置倉庫並為客戶端提供獲取配置信息、加密 / 解密信息等訪問接口;而客戶端則是微服務架構中的各個微服務應用或基礎設施,它們通過指定的配置中心來管理應用資源與業務相關的配置內容,並在啟動的時候從配置中心獲取和加載配置信息。Spring Cloud Config 實現了對服務端和客戶端中環境變量和屬性配置的抽象映射,所以它除了適用於 Spring 構建的應用程序之外,也可以在任何其他語言運行的應用程序中使用。由於 Spring Cloud Config 實現的配置中心默認采用 Git 來存儲配置信息,所以使用 Spring Cloud Config 構建的配置服務器,天然就支持對微服務應用配置信息的版本管理,並且可以通過 Git 客戶端工具來方便的管理和訪問配置內容。當然它也提供了對其他存儲方式的支持,比如:GIT倉庫、SVN 倉庫、本地化文件系統。
下面我們將構建一個基於 Git 存儲的分布式配置中心,並對客戶端進行改造,讓其能夠從配置中心獲取配置信息並綁定到代碼中。
准備工作
准備一個 Git 倉庫,在 Github 上面創建了一個文件夾 config-repo 用來存放配置文件,為了模擬生產環境,我們創建以下三個配置文件:
1 // 開發環境 2 config-server-dev.yml 3 // 測試環境 4 config-server-test.yml 5 // 生產環境 6 config-server-prod.yml
每個配置文件中都寫一個屬性 info.profile, 屬性值分別是 dev/test/prod
Server 端
創建一個基礎的 Spring Boot 工程,命名為:service-config-server
POM依賴
1 <dependency> 2 <groupId>org.springframework.cloud</groupId> 3 <artifactId>spring-cloud-config-server</artifactId> 4 </dependency> 5 <dependency> 6 <groupId>org.springframework.cloud</groupId> 7 <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> 8 </dependency>
配置文件
在 application.yml 中添加配置服務的基本信息以及 Git 倉庫的相關信息
server: port: 9300 spring: application: name: service-config-server cloud: config: server: git: uri: https://github.com/carry-chan/spring-cloud # 配置git倉庫的地址 search-paths: config-repo # git倉庫地址下的相對地址,可以配置多個,用,分割。 eureka: client: serviceUrl: defaultZone: http://admin:123456@localhost:8761/eureka/
Spring Cloud Config 也提供本地存儲配置的方式。我們只需要設置屬性spring.profiles.active=native,Config Server會從默認的src/main/resource目錄下檢索配置文件。也可以通過spring.cloud.config.server.native.searchLocations=file:E:/properties/屬性來制定配置文件的位置。雖然 Spring Cloud Config 提供了這樣的功能,但是為了支持更好的管理內容和版本控制的功能,還是推薦使用 Git 的方式。
如果我們的 Git 倉庫需要權限訪問,那么可以通過配置下面的兩個屬性來實現
spring.cloud.config.server.git.username:訪問 Git 倉庫的用戶名
spring.cloud.config.server.git.password:訪問 Git 倉庫的用戶密碼
啟動類
1 package com.carry.springcloud; 2 3 import org.springframework.boot.SpringApplication; 4 import org.springframework.boot.autoconfigure.SpringBootApplication; 5 import org.springframework.cloud.config.server.EnableConfigServer; 6 7 @EnableConfigServer 8 @SpringBootApplication 9 public class ServiceConfigServerApplication { 10 11 public static void main(String[] args) { 12 SpringApplication.run(ServiceConfigServerApplication.class, args); 13 } 14 }
到此 Server 端相關配置已經完成。
測試
啟動eureka-server、service-config-server,瀏覽器直接訪問 http://localhost:9300/config-server/dev 返回信息如下:
{ "name": "config-server", "profiles": [ "dev" ], "label": null, "version": "beb220098b44c60cf99277f064a19d52e7ebeb91", "state": null, "propertySources": [ { "name": "https://github.com/carry-chan/spring-cloud/config-repo/config-server-dev.yml", "source": { "info.profile": "dev" } } ] }
上述的返回的信息包含了配置文件的位置、版本、配置文件的名稱以及配置文件中的具體內容,說明 Server 端已經成功獲取了 Git 倉庫的配置信息。
如果直接查看配置文件中的配置信息可訪問 http://localhost:9300/config-server-dev.yml 返回:
info: profile: dev
修改配置文件config-server-dev.yml中配置信息為dev v0,再次在瀏覽器訪問 http://localhost:9300/config-server-dev.yml 返回:dev v0,說明 Server 端會自動讀取最新提交的內容。
倉庫中的配置文件會被轉換成 Web 接口,訪問可以參照以下的規則:
- /{application}/{profile}[/{label}]
- /{application}-{profile}.yml
- /{label}/{application}-{profile}.yml
- /{application}-{profile}.properties
- /{label}/{application}-{profile}.properties
上面的 URL 會映射 {application}-{profile}.yml 對應的配置文件,其中 {label} 對應 Git 上不同的分支,默認為 master。
Client 端
創建一個基礎的 Spring Boot 應用,命名為 service-config-client。
POM依賴
1 <dependency> 2 <groupId>org.springframework.boot</groupId> 3 <artifactId>spring-boot-starter-webflux</artifactId> 4 </dependency> 5 <dependency> 6 <groupId>org.springframework.cloud</groupId> 7 <artifactId>spring-cloud-starter-config</artifactId> 8 </dependency> 9 <dependency> 10 <groupId>org.springframework.cloud</groupId> 11 <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> 12 </dependency>
配置文件
需要配置兩個配置文件,application.yml 和 bootstrap.yml,配置分別如下:
application.yml
server: port: 9400 spring: application: name: service-config-client
bootstrap.yml
spring: cloud: config: name: config-server # 對應 {application} 部分 profile: dev # 對應 {profile} 部分 label: master # 對應 {label} 部分,即 Git 的分支。如果配置中心使用的是本地存儲,則該參數無用 discovery: enabled: true service-id: service-config-server #springcloud config的服務名 eureka: client: serviceUrl: defaultZone: http://admin:123456@localhost:8761/eureka/
注意:上面這些與 Spring Cloud Config 相關的屬性必須配置在 bootstrap.yml 中,config 部分內容才能被正確加載。因為 config 的相關配置會先於 application.yml,而 bootstrap.yml 的加載也是先於 application.yml。
啟動類
1 package com.carry.springcloud; 2 3 import org.springframework.boot.SpringApplication; 4 import org.springframework.boot.autoconfigure.SpringBootApplication; 5 6 @SpringBootApplication 7 public class ServiceConfigClientApplication { 8 9 public static void main(String[] args) { 10 SpringApplication.run(ServiceConfigClientApplication.class, args); 11 } 12 }
在 Controller 中使用 @Value
注解來獲取 Server 端參數的值
1 package com.carry.springcloud; 2 3 import org.springframework.beans.factory.annotation.Value; 4 import org.springframework.cloud.context.config.annotation.RefreshScope; 5 import org.springframework.web.bind.annotation.GetMapping; 6 import org.springframework.web.bind.annotation.RestController; 7 8 import reactor.core.publisher.Mono; 9 10 @RestController 11 public class ClientController { 12 13 @Value("${info.profile}") 14 private String profile; 15 16 @GetMapping("/info") 17 public Mono<String> hello() { 18 return Mono.justOrEmpty(profile); 19 } 20 }
測試
依次啟動項目eureka-server、service-config-server、service-config-client,訪問 http://localhost:9400/info 返回dev 說明已經正確的從 Server 端獲取到了參數。
手動修改Git倉庫中 config-server-dev.yml 的值,再次訪問 http://localhost:9400/info 依舊返回dev,這是因為 Spring Cloud Config 分服務端和客戶端,服務端負責將 Git 中存儲的配置文件發布成 REST 接口,客戶端可以從服務端 REST 接口獲取配置。但客戶端並不能主動感知到配置的變化,從而主動去獲取新的配置。客戶端如何去主動獲取新的配置信息呢,Spring Cloud 已經給我們提供了解決方案,每個客戶端通過 POST 方法觸發各自的 /actuator/refresh。
Refresh功能
修改客戶端即 service-config-client 項目
添加依賴
1 <dependency> 2 <groupId>org.springframework.boot</groupId> 3 <artifactId>spring-boot-starter-actuator</artifactId> 4 </dependency>
增加了spring-boot-starter-actuator
包,spring-boot-starter-actuator
是一套監控的功能,可以監控程序在運行時狀態,其中就包括/actuator/refresh
的功能。
開啟更新機制
需要給加載變量的類上面加 @RefreshScope
,在客戶端執行/actuator/refresh
的時候就會更新此類下面的變量值。
1 package com.carry.springcloud; 2 3 import org.springframework.beans.factory.annotation.Value; 4 import org.springframework.cloud.context.config.annotation.RefreshScope; 5 import org.springframework.web.bind.annotation.GetMapping; 6 import org.springframework.web.bind.annotation.RestController; 7 8 import reactor.core.publisher.Mono; 9 10 @RestController 11 @RefreshScope 12 public class ClientController { 13 14 @Value("${info.profile}") 15 private String profile; 16 17 @GetMapping("/info") 18 public Mono<String> hello() { 19 return Mono.justOrEmpty(profile); 20 } 21 }
配置
Spring Boot 1.5.X 以上默認開通了安全認證,所以要在配置文件 application.yml 中添加以下配置以將/actuator/refresh
這個 Endpoint 暴露出來
management: endpoints: web: exposure: include: refresh
測試
重啟 service-config-client 項目
訪問 http://localhost:9400/info 返回dev
我將 Git 上對應配置文件里的值改為dev v0
執行 curl -X POST http://localhost:9400/actuator/refresh
,返回["config.client.version","info.profile"]
再次訪問 http://localhost:9400/info 返回dev v0
這就說明客戶端已經得到了最新的值,Refresh 是有效的。
Webhook
現在雖然可以不用重啟服務就更新配置了,但還是需要我們手動操作,這樣還是不可取的。所以,這里就要用到git的webhooks來達到自動更新配置。
打開git上配置倉庫的地址,添加webhooks
上面的Payload URL就填寫我們的配置中心觸發刷新的地址,當然這里不能寫localhost,要外網訪問地址才行。還有這里面有個Secret的秘鑰驗證,如果這里填寫的話,在配置文件上要寫上encrypt.key與之對應。