
學習目標

本文所講內容為上一文的后續(xù),我們在上一文中學習了什么是 Spring Cloud 的配置服務器;如何使用兩種方式搭建 Spring Cloud Config Server 以及客戶端如何連接配置服務器。本文將以問題解答的形式一步步講解如何刷新配置,所用示例項目仍為上文所用代碼:
- spring-cloud-chapter-3-config-server 端口:9090
- spring-cloud-chapter-3-config-client 端口:8080
動態(tài)配置屬性 Bean
當我們的服務發(fā)布到了服務器上后,我們想要修改配置文件中的某些配置,我們該怎么辦呢?有些小伙伴就會說:“把服務停掉,修改配置文件后重新啟動不就解決了”。的確,這是一種解決辦法,但是并不是一種好的解決辦法,停掉服務器會對用戶產生很大的影響,尤其是在單體應用中,你把服務器停掉,別人就無法訪問,客戶就抓狂了,你的客服同事也會恨死你。那么這里我們就講解一下如何動態(tài)配置,讓服務正常運行的同時,修改配置信息。
1)在 spring-cloud-chapter-3-config-client 項目中定義配置屬性 Bean User,此處使用了 @ConfigurationProperties(prefix = "xxx") 注解。
package top.alanshelby.springcloudchapter3configclient.domain;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "as.user")
public class User {
private Long id;
private String name;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
2)創(chuàng)建 UserController,并使用構造器的方式注入 User 對象,使用 @EnableConfigurationProperties(User.class) 注解。
package top.alanshelby.springcloudchapter3configclient.web.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import top.alanshelby.springcloudchapter3configclient.domain.User;
@RestController
@EnableConfigurationProperties(User.class)
public class UserController {
private final User user;
@Autowired
public UserController(User user) {
this.user = user;
}
@GetMapping("/user")
public User user() {
return user;
}
}
3)將 User 關聯(lián)配置項,在 application.properties 中進行配置
as.user.id = 1
as.user.name = AlanShelby
通過瀏覽器訪問:http://localhost:8080/env/as.user.*
{
"as.user.name":"AlanShelby",
"as.user.id":"1"
}
可以看到我們的配置已經(jīng)生效了,準備工作一切就緒,那么我們應該如何動態(tài)修改配置信息呢?這里我們需要借助 PostMan 工具來進行修改。
4)配置完成后,可以使用 PostMan 發(fā)送 POST 請求可動態(tài)修改參數(shù)信息
- 請求:http://localhost:8080/env
- 請求類型:POST
- 參數(shù):
- as.user.name -> spring-cloud
- as.user.id -> 2

修改完成后,重新通過瀏覽器訪問:http://localhost:8080/env/as.user.*,可以看到配置信息已經(jīng)修改過來了,這樣就實現(xiàn)了在不重啟服務的前提下修改配置信息。
{
"as.user.name":"spring-cloud",
"as.user.id":"2"
}
說到這,不知道小伙伴們有沒有什么疑問。我們所講的內容為微服務方面的知識,也就是說不可能僅僅只有一個服務發(fā)布,如果在整個微服務項目中有幾十個或者幾百個服務實例,難道我們要手動挨個進行修改嗎?不存在的,那樣我們程序猿們早就撂挑子不干了,所以我們要想一個辦法,能夠調整所有機器的配置,那我們應該如何做呢?接下來,我們來一起解決這個問題。
問題:如果需要調整所有機器的配置應該如何操做?
解決這個問題前,我們要先搞明白一些事情,在上一文中我們講了如何搭建 Spring Cloud Config Server,我們講解了兩種方式,這里我們要了解的是配置客戶端應用所關聯(lián)的分布式配置信息,優(yōu)先于傳統(tǒng)的配置信息,如 application.properties (application.yml)或者是 bootstrap.properties (bootstrap.yml),由此可知,要解決這個問題,我們可以對配置服務器配置信息進行調整(blogstemp-prod.properties)。
啟動 spring-cloud-chapter-3-config-server 項目,服務端啟動完畢后重啟 spring-cloud-chapter-3-config-client 項目
注意:
blogstemp-prod.properties為上文中基于遠程 git 倉庫使用的配置文件,這里依舊使用它作為例子。
1)在 blogstemp-prod.properties 文件中添加如下信息:
as.user.id = 1000
as.user.name = AlanShelbyTest
修改完成后,將代碼提交到 GitHub,訪問:http://localhost:9090/blogstemp/prod,即可發(fā)現(xiàn)服務端配置信息就改過來了。

但是當我們訪問客戶端時:http://localhost:8080/env,就會發(fā)現(xiàn),客戶端并沒有及時把服務端的配置信息同步過來,那這個應該如何解決呢?

這里我們可以使用 Spring Cloud 給我們提供的 refresh 端點來進行刷新操作,依舊使用 PostMan 進行操作:
- 請求:http://localhost:8080/refresh
- 請求類型:
POST
請求完成后,會返回給我們修改的配置 key:
[
"as.user.id",
"config.client.version",
"as.user.name"
]
重新訪問 http://localhost:8080/env,就會發(fā)現(xiàn)我們剛剛修改的配置已經(jīng)同步過來了。

這樣修改的分布式配置信息就可以應用于所有服務實例了,是不是一舉多得。
客戶端調整所有機器的配置的問題解決了,那么新的問題又出現(xiàn)了。我們剛剛使用了 Spring Cloud 給我們提供的 refresh 端點手動為客戶端刷新了遠程配置信息,這樣很不方便,每次修改完信息后,都要有專門的人員來進行刷新操作,十分不友好,那么我們如何讓服務端更新配置信息后,客戶端進行感知后自動刷新呢?接下來我們就來解決一下這個問題。
問題:如果服務端更新了,客戶端如何感知?
從上面我們已經(jīng)知道了可以使用 Spring Cloud 給我們提供的 refresh 端點手動進行刷新操作,客戶端感知說白了就是客戶端定時刷新,所以我們使用一個簡易的定時任務就可以解決這個問題,這種方式只是一種解決辦法,較為簡單、方便,還有很多其他的解決方式(如官方文檔中提供的整合 Spring Cloud Bus 和 spring-cloud-config-monitor),這里只是提供解決思路,讀者可自行斟酌使用。
1)使用構造器注入的方式將 ContextRefresher 進行注入,使用定時任務每5秒(根據(jù)需求自行設定)對上下文進行一次刷新,這里使用了 contextRefresher.refresh() 進行刷新。
package top.alanshelby.springcloudchapter3configclient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.context.refresh.ContextRefresher;
import org.springframework.scheduling.annotation.Scheduled;
import java.util.Set;
@SpringBootApplication
public class SpringCloudChapter3ConfigClientApplication {
private final ContextRefresher contextRefresher;
// 使用構造器注入的方式將 contextRefresher 進行注入
@Autowired
public SpringCloudChapter3ConfigClientApplication(ContextRefresher contextRefresher) {
this.contextRefresher = contextRefresher;
}
@Scheduled(fixedRate = 5000L)
public void update() {
Set<String> keys = contextRefresher.refresh();
if (!keys.isEmpty()) {
System.out.println("本次更新的配置項:" + keys);
}
}
public static void main(String[] args) {
SpringApplication.run(SpringCloudChapter3ConfigClientApplication.class, args);
}
}
2)修改 blogstemp-prod.properties 配置文件,并提交到遠程 GitHub 倉庫
as.user.id = 1001
as.user.name = Shelby
3)查看日志信息,可以看到輸入如下信息,表明上下文的刷新操作完成:
本次更新的配置項:[as.user.id, config.client.version, as.user.name]
至此,關于Spring Cloud Config Server(配置服務器)所有內容就講解完了,這是我的理解,各位看官如果有不同見解或文章中有錯誤,請不吝指正。
所用代碼碼云地址:https://gitee.com/AlanShelby/spring-cloud-chapter
知乎專欄地址:https://zhuanlan.zhihu.com/c_200981602
個人微信公眾號:AlanShelby(多多關注,感謝~)