前言
對於微服務而言配置本地化是個很大的雞肋,不可能每次需要改個配置都要重新把服務重新啟動一遍,因此最終的解決方案都是將配置外部化,托管在一個平台上達到不用重啟服務即可一次修改多處生效
的目的。
但是對於單體應用的Spring Boot項目而言,動態刷新顯然是有點多余,反正就一個服務,改下重啟不就行了?
然而在某些特殊的場景下還是必須用到動態刷新的,如下:
添加數據源
:對接某個第三方平台的時候,你不可能每次添加一個數據源都要重啟下服務固化的對接
:大量的固定對接方式,只是其中的某個固定的代碼段不同,比如提供視圖中的字段不同,接口服務中字段不同等情況。
當然以上列舉的兩種場景每個公司都有不同的解決方案,這里不做深究。
微服務下有哪幾種主流的方案?
微服務下的動態配置中心有三種主流的方式,如下圖:
上圖中的三種配置中心方案可以說是現在企業中使用率最高的,分別是:
-
Nacos:阿里巴巴的最近開源的項目,這個家伙很牛逼,一個干掉了
Eureka
(停更)和Config+Bus
,既能作為配置中心也能作為注冊中心,並且有自己的獨立的 管理平台,可以說是現在最主流的一種。 -
Config+Bus:早期在用的微服務配置中心,可以依托
GitHub
管理微服務的配置文件,這種現在也是有不少企業在用,但是需要自己獨立部署一個微服務,和Nacos
相比遜色了不少。 -
Apollo:攜程開源項目Apollo,這個也是不少企業在用,陳某了解的不多,有興趣的可以深入研究下。
針對Spring Boot 適用的幾種方案?
其實上述三種都可以在Spring Boot項目中適配,但是作為單體應用有些重了,下面作者簡單的介紹兩種可用的方案。
Spring Boot+Nacos(不推薦)
不得不說阿里巴巴確實挺有野心,阿里要做其實是一個微服務生態,Nacos不僅僅可以作為Spring Cloud的配置和注冊中心,也適配了Dubbo、K8s,官方文檔中對於如何適配都做了詳細的介紹,作者 這里就不再詳細介紹了,如下圖:
當然Nacos對Spring、Spring Boot 項目同樣適用。
如何使用呢?這里作者只提供下思路,不做過多的深究,這篇在作者下個專欄Spring Cloud 進階會詳細介紹:
-
下載對應版本的Nacos,啟動項目,訪問
http://localhost:8848
進入Nacos的管理界面; -
Spring Boot 項目引入Nacos的配置依賴
nacos-config-spring-boot-starter
,配置Nacos管理中心的地址。 -
@NacosPropertySource
、@NacosValue
兩個注解結合完成。
@NacosPropertySource
:指定配置中心的dataId
,和是否自動刷新@NacosValue
替代@Value
注解完成屬性的自動裝配
- 如果公司項目做了后台管理,則可以直接調用Nacos開放的API修改對應配置的值(替代了Nacos管理界面的手動操作),API的地址:https://nacos.io/zh-cn/docs/open-api.html
此種方案雖說可以實現配置的動態刷新,但是還要集成Nacos,啟動一個Nacos的服務,完全是有點大材小用了,實際項目中不推薦使用。
Spring Boot+Config+actuator(推薦)
此種方案實際使用的是Config配置中心,但是不像Nacos那般重,完全適用於單體應用的SpringBoot項目,只需要做小部分的更改即可達到效果。
方案一(不推薦)
- 添加Config的依賴,如下:
<!-- springCloud的依賴-->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR3</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<!-- config的依賴-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<!-- actuator的依賴-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
- 配置文件中暴露Spring Boot的端點,如下:
management.endpoints.web.exposure.include=*
- 配置文件中新增三個屬性配置:
config.version=22
config.app.name=dynamic-project
config.platform=mysql
- 結合
@RefreshScope
注解動態刷新,寫個Controller,如下:
@RestController
//@RefreshScope該注解必須標注,否則無法完成動態更新
@RefreshScope
public class DynamicConfigController {
@Value("${config.version}")
private String version;
@Value("${config.app.name}")
private String appName;
@Value("${config.platform}")
private String platform;
@GetMapping("/show/version")
public String test(){
return "version="+version+"-appName="+appName+"-platform="+platform;
}
- 啟動項目測試,瀏覽器訪問
http://localhost:8080/show/version
,返回信息如下圖:
- 修改
target
目錄下的配置文件,如下:
config.version=33
config.app.name=dynamic-project
config.platform=ORACLE
-
POST請求
http://localhost:8080/actuator/refresh
接口,手動刷新下配置(必須,否則不能自動刷新) -
瀏覽器再次輸入
http://localhost:8080/show/version
,結果如下圖:
可以看到,配置已經自動修改了,結束。
方案二(推薦)
看到了方案一覺得如何?是不是有點雞肋了
第一個問題:為什么還要調用一次手動刷新呢?
第二個問題:只能手動的在配置文件中改嗎?如果想在后台管理系統改怎么辦?
想要解決上述兩個問題還是要看下Config
的源碼,代碼關鍵部分在org.springframework.cloud.context.refresh.ContextRefresher#refresh()
方法中,如下圖:
因此只需要在修改屬性之后調用下ContextRefresher#refresh()
(異步,避免一直阻塞等待)方法即可。
為了方便測試,我們自己手動寫一個refresh接口,如下:
@GetMapping("/show/refresh")
public String refresh(){
//修改配置文件中屬性
HashMap<String, Object> map = new HashMap<>();
map.put("config.version",99);
map.put("config.app.name","appName");
map.put("config.platform","ORACLE");
MapPropertySource propertySource=new MapPropertySource("dynamic",map);
//將修改后的配置設置到environment中
environment.getPropertySources().addFirst(propertySource);
//異步調用refresh方法,避免阻塞一直等待無響應
new Thread(() -> contextRefresher.refresh()).start();
return "success";
}
上述代碼中作者只是手動設置了配置文件中的值,實際項目中可以通過持久化的方式從數據庫中讀取配置刷新。
下面我們測試看看,啟動項目,訪問http://localhost:8080/show/version
,發現是之前配置在application.properties
中的值,如下圖:
調用refresh
接口:http://localhost:8080/show/refresh
重新設置屬性值;
再次調用http://localhost:8080/show/version
查看下配置是否修改了,如下圖:
從上圖可以發現,配置果然修改了,達到了動態刷新的效果。
總結
本文從微服務的配置中心介紹到Spring Boot 搭建簡易的配置中心,詳細介紹了幾種可行性的方案,作者強力推薦最后一種方案,簡化版的Config
,完全適用於單體應用。