Spring Cloud(四)配置中心Config
在分散式系統中,由於服務數量巨多,為了方便服務配置檔案統一管理,實時更新,所以需要分散式配置中心元件。在Spring Cloud中,有分散式配置中心元件spring cloud config ,它支援配置服務放在配置服務的記憶體中(即本地),也支援放在遠端Git倉庫以及資料庫中。在spring cloud config 元件中,分兩個角色,一是config server,二是config client。
在本文中,我們將分別構建基於Git、基於Mysql 資料庫儲存的分散式配置中心,並對客戶端進行改造,並讓其能夠從配置中心獲取配置資訊並繫結到程式碼中的整個過程。
基於GIT儲存
理解配置中心搜尋路徑
配置資訊的URL與配置檔案的對映關係如下:
- /{application}/{profile}[/{label}]
- /{application}-{profile}.yml
- /{label}/{application}-{profile}.yml
- /{application}-{profile}.properties
- /{label}/{application}-{profile}.properties
準備配置倉庫
在這裡,我在 Gitee 上建立了一個公開的 repo, config-repo-demo 裡面建立 mz-eureka-client-one
的資料夾,存放 mz-eureka-client-one
構建 Server 端
建立一個 SpringBoot 的基礎專案 mz-config-server-git
,同時引入 config 的依賴 spring-cloud-config-server
POM 檔案如下:
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId >spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
</dependencies>
建立專案配置檔案 bootstarp.yml
, 配置如下:
eureka:
client:
serviceUrl:
defaultZone: http://localhost:1001/eureka/
server:
port: 1003
spring:
application:
name: mz-config-server-git
cloud:
config:
server:
git:
searchPaths: '{application}'
uri: https://gitee.com/mrzhouy/config-repo-demo
spring clour config git 屬性解釋:
uri: 倉庫地址
username:訪問賬號(私庫才需要輸入)
password:賬號密碼(私庫才需要輸入)
searchPaths:git倉庫子目錄
當然,在某些時候我們希望對不同的子專案訪問不同的git倉目錄,因此我們可以在使用如下配置:
searchPaths: '{application}'
注:config 客戶端在沒有 spring.cloud.config.name屬性的時候,服務端{application} 獲取的是客戶端
spring.application.name的值,否則,獲取的是 spring.cloud.config.name的值。
1)、當沒有spring.cloud.config.name時,客戶端獲取的是spring.application.name 所對應的git庫中的檔案,並且只能獲取一個檔案,
2)、當一個專案中有需求要獲取多個檔案時,就需要用到spring.cloud.config.name這個屬性,以逗號分割
開啟服務
啟動類上新增 @EnableConfigServer 註解開啟 config server 服務。程式碼如下:
@EnableDiscoveryClient
@EnableConfigServer
@SpringBootApplication
public class ConfigServerApplication extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(ConfigServerApplication.class, args);
}
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return builder.sources(ConfigServerApplication.class);
}
}
升級客戶端(client)
客戶端這裡,我們直接在之前的 mz-eureka-client-one
專案中進行升級。
新增config依賴:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
修改配置檔案 bootstarp.yml
新增 config 配置:
spring:
cloud:
config:
discovery:
enabled: true
service-id: mz-config-server-git
#uri: http://localhost:8888/
label: master
profile: test
#注:如果config server 沒有注入到註冊中心,我們在這裡可以通過 uri 來指定配置中心地址。但是生產、測試、開發環境中,我的配置中心是多個,因此我是將配置中心註冊到了註冊中心,通過 service-id 來訪問註冊中心的。
改造 HelloController:
@RestController
public class HelloController {
@Value("${server.port}")
private String serverPort;
@Value("${info.message}")
private String infoMessage;
@RequestMapping("hi")
public String sayHi() {
return "Hi Spring Cloud, running in port :" + serverPort + " info.message is : " + infoMessage;
}
}
至此,配置中心已經構建完成,按順序啟動 註冊中心、配置中心 、客戶端。 呼叫 http://localhost:2001/hi ,我們可以看到通過配置中心拿到的 info.message 這個值。
基於 MySql 儲存
前面我們已經完成了基於 GIT 儲存的配置中心,其實升級為mysql儲存很簡單。只需要修改 POM 依賴(引入資料庫依賴),以及 bootstarp.yml 屬性檔案即可。在這裡,我們建立新的專案 mz-config-server-mysql
,POM 檔案如下:
<parent>
<artifactId>SpringCloud-Learning</artifactId>
<groupId>com.mz</groupId>
<version>1.0.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>mz-config-server-mysql</artifactId>
<description>Spring Cloud 配置中心——MySql</description>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
<!-- 引入資料庫依賴-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.21</version>
</dependency>
</dependencies>
修改 bootstarp.yml
:
指定 spring.profiles.active = jdbc,然後增加 jdbc配置
eureka:
client:
serviceUrl:
defaultZone: http://localhost:1001/eureka/
server:
port: 1003
spring:
application:
name: mz-config-server-mysql
profiles:
active: jdbc
cloud:
config:
label: master
server:
jdbc:
sql: SELECT `KEY`, `VALUE` from PROPERTIES where APPLICATION=? and PROFILE=? and LABEL=?
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/db_microserver_config
username: root
password: TW123
建立資料庫的sql 檔案在專案中:ConfigMySql.sql
至此,修改完成,按順序啟動 註冊中心、配置中心 、客戶端(記得修改配置中心的serverId哦)。 呼叫 http://localhost:2001/hi ,我們可以看到通過配置中心拿到的 info.message 這個值。
Refresh
上面我們成功的完成了配置中心,但是這個時候就產生了疑問,client是在專案啟動的時候就請求了配置中心,獲取到了配置,如圖:
那再我們修改完配置中心的配置後,如何重新整理配置呢。如果需要重啟專案的話,配置中心就會顯得比較雞肋。
別急,spring cloud 當然為我們提供了重新整理的辦法。僅修改客戶端即 mz-eureka-client-one 專案,就可以實現 refresh 的功能。
新增依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
增加了spring-boot-starter-actuator
包,spring-boot-starter-actuator
是一套監控的功能,可以監控程式在執行時狀態,其中就包括/actuator/refresh
的功能。
開啟更新機制
需要給載入變數的類上面載入@RefreshScope
,在客戶端執行/actuator/refresh
的時候就會更新此類下面的變數值。
@RestController
@RefreshScope
public class HelloController {
@Value("${server.port}")
private String serverPort;
@Value("${info.message}")
private String infoMessage;
@RequestMapping("hi")
public String sayHi() {
return "Hi Spring Cloud, running in port :" + serverPort + " info.message is : " + infoMessage;
}
}
配置
Spring Boot 1.5.X 以上預設開通了安全認證,所以要在配置檔案 application.yml 中新增以下配置以將/actuator/refresh
這個 Endpoint 暴露出來
management:
endpoints:
web:
exposure:
include: refresh
測試
改造完之後,我們重啟 mz-eureka-consumer-one,我們以 POST 請求的方式來訪問 http://localhost:2001/actuator/refresh 就會更新配置檔案至最新版本。
測試流程:
- 訪問 http://localhost:2001/hi 返回
Hi Spring Cloud, running in port :2001 info.message is : i am config by jdbc
- 我將 Git 上對應配置檔案裡的值改為
i am config by jdbc update
- 傳送 Post 請求到
http://localhost:2001/actuator/refresh
- 訪問 http://localhost:2001/hi 返回
Hi Spring Cloud, running in port :2001 info.message is : i am config by jdbc update
不過,每次手動重新整理客戶端也很麻煩,有沒有什麼辦法只要提交程式碼就自動呼叫客戶端來更新呢,Github 的 Webhook 是一個辦法。
Webhook
Webhook 是當某個事件發生時,通過傳送 HTTP POST 請求的方式來通知資訊接收方。Webhook 來監測你在 Github.com 上的各種事件,最常見的莫過於 push 事件。如果你設定了一個監測 push 事件的 Webhook,那麼每當你的這個專案有了任何提交,這個 Webhook 都會被觸發,這時 Github 就會發送一個 HTTP POST 請求到你配置好的地址。
如此一來,你就可以通過這種方式去自動完成一些重複性工作,比如,你可以用 Webhook 來自動觸發一些持續整合(CI)工具的運作,比如 Travis CI;又或者是通過 Webhook 去部署你的線上伺服器。下圖就是 Github 上面的 Webhook 配置。
Payload URL
:觸發後回撥的 URLContent type
:資料格式,兩種一般使用 jsonSecret
:用作給 POST 的 body 加密的字串。採用 HMAC 演算法events
:觸發的事件列表。
events 事件型別 | 描述 |
---|---|
push | 倉庫有 push 時觸發。預設事件 |
create | 當有分支或標籤被建立時觸發 |
delete | 當有分支或標籤被刪除時觸發 |
這樣我們就可以利用 hook 的機制去觸發客戶端的更新,但是當客戶端越來越多的時候,hook 機制也不夠優雅了,另外每次增加客戶端都需要改動 hook 也是不現實的。其實,Spring Cloud 給了我們更好解決方案——Spring Cloud Bus。後續我們將繼續學習如何通過 Spring Cloud Bus 來實現以訊息匯流排的方式進行通知配置資訊的變化,完成叢集上的自動化更新。