Spring Cloud Config 是 Spring Cloud 家族中最早的配置中心,雖然后來又發布了 Consul 可以代替配置中心功能,但是 Config 依然適用於 Spring Cloud 項目,通過簡單的配置即可實現功能。
配置文件是我們再熟悉不過的了,尤其是 Spring Boot 項目,除了引入相應的 maven 包之外,剩下的工作就是完善配置文件了,例如 mysql、redis 、security 相關的配置。除了項目運行的基礎配置之外,還有一些配置是與我們業務有關系的,比如說七牛存儲、短信相關、郵件相關,或者一些業務上的開關。
對於一些簡單的項目來說,我們一般都是直接把相關配置放在單獨的配置文件中,以 properties 或者 yml 的格式出現,更省事兒的方式是直接放到 application.properties 或 application.yml 中。但是這樣的方式有個明顯的問題,那就是,當修改了配置之后,必須重啟服務,否則配置無法生效。
目前有一些用的比較多的開源的配置中心,比如攜程的 Apollo、螞蟻金服的 disconf 等,對比 Spring Cloud Config,這些配置中心功能更加強大。有興趣的可以拿來試一試。
接下來,我們開始在 Spring Boot 項目中集成 Spring Cloud Config,並以 github 作為配置存儲。除了 git 外,還可以用數據庫、svn、本地文件等作為存儲。主要從以下三塊來說一下 Config 的使用。
1.基礎版的配置中心(不集成 Eureka);
2.結合 Eureka 版的配置中心;
3.實現配置的自動刷新;
實現最簡單的配置中心
最簡單的配置中心,就是啟動一個服務作為服務方,之后各個需要獲取配置的服務作為客戶端來這個服務方獲取配置。
先在 github 中建立配置文件
我創建的倉庫地址為:配置中心倉庫
目錄結構如下:
配置文件的內容大致如下,用於區分,略有不同。
data:
env: config-eureka-dev
user:
username: eureka-client-user
password: 1291029102
注意文件的名稱不是亂起的,例如上面的 config-single-client-dev.yml 和 config-single-client-prod.yml 這兩個是同一個項目的不同版本,項目名稱為 config-single-client, 一個對應開發版,一個對應正式版。config-eureka-client-dev.yml 和 config-eureka-client-prod.yml 則是另外一個項目的,項目的名稱就是 config-eureka-client 。
創建配置中心服務端
1、新建 Spring Boot 項目,引入 config-server 和 starter-web
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- spring cloud config 服務端包 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
2、配置 config 相關的配置項
bootstrap.yml 文件
spring:
application:
name: config-single-server # 應用名稱
cloud:
config:
server:
git:
uri: https://github.com/huzhicheng/config-only-a-demo #配置文件所在倉庫
username: github 登錄賬號
password: github 登錄密碼
default-label: master #配置文件分支
search-paths: config #配置文件所在根目錄
application.yml
server:
port: 3301
3、在 Application 啟動類上增加相關注解 @EnableConfigServer
@SpringBootApplication
@EnableConfigServer
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
啟動服務,接下來測試一下。
Spring Cloud Config 有它的一套訪問規則,我們通過這套規則在瀏覽器上直接訪問就可以。
/{application}/{profile}[/{label}]
/{application}-{profile}.yml
/{label}/{application}-{profile}.yml
/{application}-{profile}.properties
/{label}/{application}-{profile}.properties
{application} 就是應用名稱,對應到配置文件上來,就是配置文件的名稱部分,例如我上面創建的配置文件。
{profile} 就是配置文件的版本,我們的項目有開發版本、測試環境版本、生產環境版本,對應到配置文件上來就是以 application-{profile}.yml 加以區分,例如application-dev.yml、application-sit.yml、application-prod.yml。
{label} 表示 git 分支,默認是 master 分支,如果項目是以分支做區分也是可以的,那就可以通過不同的 label 來控制訪問不同的配置文件了。
上面的 5 條規則中,我們只看前三條,因為我這里的配置文件都是 yml 格式的。根據這三條規則,我們可以通過以下地址查看配置文件內容:
http://localhost:3301/config-single-client/dev/master
http://localhost:3301/config-single-client/prod
http://localhost:3301/config-single-client-dev.yml
http://localhost:3301/config-single-client-prod.yml
http://localhost:3301/master/config-single-client-prod.yml
通過訪問以上地址,如果可以正常返回數據,則說明配置中心服務端一切正常。
創建配置中心客戶端,使用配置
配置中心服務端好了,配置數據准備好了,接下來,就要在我們的項目中使用它了。
1、引用相關的 maven 包。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- spring cloud config 客戶端包 -->
<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>
2、初始化配置文件
bootstrap.yml
spring:
profiles:
active: dev
---
spring:
profiles: prod
application:
name: config-single-client
cloud:
config:
uri: http://localhost:3301
label: master
profile: prod
---
spring:
profiles: dev
application:
name: config-single-client
cloud:
config:
uri: http://localhost:3301
label: master
profile: dev
配置了兩個版本的配置,並通過 spring.profiles.active 設置當前使用的版本,例如本例中使用的 dev 版本。
application.yml
server:
port: 3302
management:
endpoint:
shutdown:
enabled: false
endpoints:
web:
exposure:
include: "*"
data:
env: NaN
user:
username: NaN
password: NaN
其中 management 是關於 actuator 相關的,接下來自動刷新配置的時候需要使用。
data 部分是當無法讀取配置中心的配置時,使用此配置,以免項目無法啟動。
3、要讀取配置中心的內容,需要增加相關的配置類,Spring Cloud Config 讀取配置中心內容的方式和讀取本地配置文件中的配置是一模一樣的。可以通過 @Value 或 @ConfigurationProperties 來獲取。
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
4、要讀取配置中心的內容,需要增加相關的配置類,Spring Cloud Config 讀取配置中心內容的方式和讀取本地配置文件中的配置是一模一樣的。可以通過 @Value 或 @ConfigurationProperties 來獲取。
使用 @Value 的方式:
@Data
@Component
public class GitConfig {
@Value("${data.env}")
private String env;
@Value("${data.user.username}")
private String username;
@Value("${data.user.password}")
private String password;
}
使用 @ConfigurationProperties 的方式:
@Component
@Data
@ConfigurationProperties(prefix = "data")
public class GitAutoRefreshConfig {
public static class UserInfo {
private String username;
private String password;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "UserInfo{" +
"username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
}
private String env;
private UserInfo user;
}
4、增加一個 RESTController 來測試使用配置
@RestController
public class GitController {
@Autowired
private GitConfig gitConfig;
@Autowired
private GitAutoRefreshConfig gitAutoRefreshConfig;
@GetMapping(value = "show")
public Object show(){
return gitConfig;
}
@GetMapping(value = "autoShow")
public Object autoShow(){
return gitAutoRefreshConfig;
}
}
5、項目啟動類
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
啟動項目,訪問 RESTful 接口
http://localhost:3302/show,結果如下:
{
"env": "localhost-dev-edit",
"username": "fengzheng-dev",
"password": "password-dev"
}
http://localhost:3302/autoShow,結果如下:
{
"env": "localhost-dev-edit",
"user": {
"username": "fengzheng-dev",
"password": "password-dev"
}
}
實現自動刷新
Spring Cloud Config 在項目啟動時加載配置內容這一機制,導致了它存在一個缺陷,修改配置文件內容后,不會自動刷新。例如我們上面的項目,當服務已經啟動的時候,去修改 github 上的配置文件內容,這時候,再次刷新頁面,對不起,還是舊的配置內容,新內容不會主動刷新過來。
但是,總不能每次修改了配置后重啟服務吧。如果是那樣的話,還是不要用它了為好,直接用本地配置文件豈不是更快。
它提供了一個刷新機制,但是需要我們主動觸發。那就是 @RefreshScope 注解並結合 actuator ,注意要引入 spring-boot-starter-actuator 包。
1、在 config client 端配置中增加 actuator 配置,上面大家可能就注意到了。
management:
endpoint:
shutdown:
enabled: false
endpoints:
web:
exposure:
include: "*"
其實這里主要用到的是 refresh 這個接口
2、在需要讀取配置的類上增加 @RefreshScope 注解,我們是 controller 中使用配置,所以加在 controller 中。
@RestController
@RefreshScope
public class GitController {
@Autowired
private GitConfig gitConfig;
@Autowired
private GitAutoRefreshConfig gitAutoRefreshConfig;
@GetMapping(value = "show")
public Object show(){
return gitConfig;
}
@GetMapping(value = "autoShow")
public Object autoShow(){
return gitAutoRefreshConfig;
}
}
注意,以上都是在 client 端做的修改。
之后,重啟 client 端,重啟后,我們修改 github 上的配置文件內容,並提交更改,再次刷新頁面,沒有反應。沒有問題。
接下來,我們發送 POST 請求到 http://localhost:3302/actuator/refresh 這個接口,用 postman 之類的工具即可,此接口就是用來觸發加載新配置的,返回內容如下:
[
"config.client.version",
"data.env"
]
之后,再次訪問 RESTful 接口,http://localhost:3302/autoShow 這個接口獲取的數據已經是最新的了,說明 refresh 機制起作用了。
而 http://localhost:3302/show 獲取的還是舊數據,這與 @Value 注解的實現有關,所以,我們在項目中就不要使用這種方式加載配置了。
在 github 中配置 Webhook
這就結束了嗎,並沒有,總不能每次改了配置后,就用 postman 訪問一下 refresh 接口吧,還是不夠方便呀。 github 提供了一種 webhook 的方式,當有代碼變更的時候,會調用我們設置的地址,來實現我們想達到的目的。
1、進入 github 倉庫配置頁面,選擇 Webhooks ,並點擊 add webhook;
2、之后填上回調的地址,也就是上面提到的 actuator/refresh 這個地址,但是必須保證這個地址是可以被 github 訪問到的。如果是內網就沒辦法了。這也僅僅是個演示,一般公司內的項目都會有自己的代碼管理工具,例如自建的 gitlab,gitlab 也有 webhook 的功能,這樣就可以調用到內網的地址了。
使用 Spring Cloud Bus 來自動刷新多個端
Spring Cloud Bus 將分布式系統的節點與輕量級消息代理鏈接。這可以用於廣播狀態更改(例如配置更改)或其他管理指令。一個關鍵的想法是,Bus 就像一個擴展的 Spring Boot 應用程序的分布式執行器,但也可以用作應用程序之間的通信渠道。
—— Spring Cloud Bus 官方解釋
如果只有一個 client 端的話,那我們用 webhook ,設置手動刷新都不算太費事,但是如果端比較多的話呢,一個一個去手動刷新未免有點復雜。這樣的話,我們可以借助 Spring Cloud Bus 的廣播功能,讓 client 端都訂閱配置更新事件,當配置更新時,觸發其中一個端的更新事件,Spring Cloud Bus 就把此事件廣播到其他訂閱端,以此來達到批量更新。
1、Spring Cloud Bus 核心原理其實就是利用消息隊列做廣播,所以要先有個消息隊列,目前官方支持 RabbitMQ 和 kafka。
這里用的是 RabbitMQ, 所以先要搭一套 RabbitMQ 環境。請自行准備環境,這里不再贅述。我是用 docker 直接創建的,然后安裝了 rabbitmq-management 插件,這樣就可以在瀏覽器訪問 15672 查看 UI 管理界面了。
2、在 client 端增加相關的包,注意,只在 client 端引入就可以。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>
3、在配置文件中增加 RabbitMQ 相關配置,默認的端口應該是 5672 ,因為我是用 docker 創建的,所以有所不同。
spring:
rabbitmq:
host: localhost
port: 32775
username: guest
password: guest
4、啟動兩個或多個 client 端,准備來做個測試
在啟動的時候分別加上 vm option:-Dserver.port=3302 和 -Dserver.port=3303 ,然后分別啟動就可以了。
5、分別打開 http://localhost:3302/autoShow 和 http://localhost:3303/autoShow,查看內容,然后修改 github 上配置文件的內容並提交。再次訪問這兩個地址,數據沒有變化。
6、訪問其中一個的 actuator/bus-refresh 地址,注意還是要用 POST 方式訪問。之后查看控制台輸出,會看到這兩個端都有一條這樣的日志輸出
o.s.cloud.bus.event.RefreshListener: Received remote refresh request. Keys refreshed
7、再次訪問第 5 步的兩個地址,會看到內容都已經更新為修改后的數據了。
綜上所述,當我們修改配置后,使用 webhook ,或者手動觸發的方式 POST 請求一個 client 端的 actuator/bus-refresh 接口,就可以更新給所有端了。
結合 Eureka 使用 Spring Cloud Config
以上講了 Spring Cloud Config 最基礎的用法,但是如果我們的系統中使用了 Eureka 作為服務注冊發現中心,那么 Spring Cloud Config 也應該注冊到 Eureka 之上,方便其他服務消費者使用,並且可以注冊多個配置中心服務端,以實現高可用。
好的,接下來就來集成 Spring Cloud Config 到 Eureka 上。
在 github 倉庫中增加配置文件
啟動 Eureka Server
首先啟動一個 Eureka Server,之前的文章有講過 Eureka ,可以回過頭去看看。Spring Cloud Eureka 實現服務注冊發現,為了清楚,這里還是把配置列出來
1、pom 中引入相關包
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
2、設置配置文件內容
bootstrap.yml
spring:
application:
name: kite-eureka-center
security:
user:
name: test # 用戶名
password: 123456 # 密碼
cloud:
inetutils: ## 網卡設置
ignoredInterfaces: ## 忽略的網卡
- docker0
- veth.*
- VM.*
preferredNetworks: ## 優先的網段
- 192.168
application.yml
server:
port: 3000
eureka:
instance:
hostname: eureka-center
appname: 注冊中心
client:
registerWithEureka: false # 單點的時候設置為 false 禁止注冊自身
fetchRegistry: false
serviceUrl:
defaultZone: http://test:123456@localhost:3000/eureka
server:
enableSelfPreservation: false
evictionIntervalTimerInMs: 4000
3、Application 啟動類
@EnableEurekaServer
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
4、啟動服務,在瀏覽器訪問 3000 端口,並輸出用戶名 test,密碼 123456 即可進入 Eureka UI
配置 Spring Cloud Config 服務端
服務端和前面的相比也就是多了注冊到 Eureka 的配置,其他地方都是一樣的。
1、在 pom 中引入相關的包
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- spring cloud config 服務端包 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
<!-- eureka client 端包 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
2、配置文件做配置
application.yml
server:
port: 3012
eureka:
client:
serviceUrl:
register-with-eureka: true
fetch-registry: true
defaultZone: http://test:123456@localhost:3000/eureka/
instance:
preferIpAddress: true
spring:
application:
name: config-eureka-server
cloud:
config:
server:
git:
uri: https://github.com/huzhicheng/config-only-a-demo
username: github 用戶名
password: github 密碼
default-label: master
search-paths: config
相比於不加 Eureka 的版本,這里僅僅是增加了 Eureka 的配置,將配置中心注冊到 Eureka ,作為服務提供者對外提供服務。
3、啟動類,在 @EnableConfigServer 的基礎上增加了 @EnableEurekaClient,另外也可以用 @EnableDiscoveryClient 代替 @EnableEurekaClient
@SpringBootApplication
@EnableConfigServer
@EnableEurekaClient
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
4、啟動服務,之后訪問 Eureka ,可以看到服務已注冊成功
配置 Spring Cloud Config 客戶端
客戶端的配置相對來說變動大一點,加入了 Eureka 之后,就不用再直接和配置中心服務端打交道了,要通過 Eureka 來訪問。另外,還是要注意客戶端的 application 名稱要和 github 中配置文件的名稱一致。
1、pom 中引入相關的包
<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.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
2、配置文件
bootstrap.yml
eureka:
client:
serviceUrl:
register-with-eureka: true
fetch-registry: true
defaultZone: http://test:123456@localhost:3000/eureka/
instance:
preferIpAddress: true
spring:
profiles:
active: dev
---
spring:
profiles: prod
application:
name: config-eureka-client
cloud:
config:
label: master
profile: prod
discovery:
enabled: true
service-id: config-eureka-server
---
spring:
profiles: dev
application:
name: config-eureka-client
cloud:
config:
label: master #指定倉庫分支
profile: dev #指定版本 本例中建立了dev 和 prod兩個版本
discovery:
enabled: true # 開啟
service-id: config-eureka-server # 指定配置中心服務端的server-id
除了注冊到 Eureka 的配置外,就是配置和配置中心服務端建立關系。
其中 service-id 也就是服務端的application name。
application.yml
server:
port: 3011
management:
endpoint:
shutdown:
enabled: false
endpoints:
web:
exposure:
include: "*"
data:
env: NaN
user:
username: NaN
password: NaN
3、啟動類,增加了 @EnableEurekaClient 注解,可以用 @EnableDiscoveryClient 代替
@SpringBootApplication
@EnableEurekaClient
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
4、另外的配置實體類和 RESTController 和上面的一樣,沒有任何更改,直接參考即可。
5、啟動 client 端,訪問 http://localhost:3011/autoShow,即可看到配置文件內容。
這個例子只是介紹了和 Eureka 結合的最基礎的情況,還可以注冊到高可用的 Eureka 注冊中心,另外,配置中心服務端還可以注冊多個實例,同時保證注冊中心的高可用。
注意事項
1. 在 git 上的配置文件的名字要和 config 的 client 端的 application name 對應;
2. 在結合 eureka 的場景中,關於 eureka 和 git config 相關的配置要放在 bootstrap.yml 中,否則會請求默認的 config server 配置,這是因為當你加了配置中心,服務就要先去配置中心獲取配置,而這個時候,application.yml 配置文件還沒有開始加載,而 bootstrap.yml 是最先加載的。
如果你覺得寫的還可以的話,請點個「推薦」吧
歡迎關注,不定期更新本系列和其他文章
古時的風箏
,進入公眾號可以加入交流群