SpringCloud 詳解配置重新整理的原理
首先先介紹下實現後的效果:
1、在需要動態配置屬性的類上添加註解@RefreshScope表示此類Scope為refresh型別的
2、啟動工程,修改config-server對應的配置檔案,這裡修改的是system.order.serverName
3、以post的方式呼叫refresh介面,返回修改後的key值
4、訪問infoTest介面,可以看到修改後的值
詳細流程:
依次啟動config-server,eureka-server後,再啟動訂單服務order-service,首先訪問http://localhost:8100/infoTest 檢視serverName的值:
然後修改config-server工程下的order-service-dev.properties中的system.order.serverName為Order Service modified,通過postman使用POST的方式呼叫refresh介面,可以看到返回了修改的屬性的key,繼續訪問
可以看到對應的屬性值已經變化了。
基本使用演示完了,下面該對屬性重新整理原理進行詳細探究:
1、先從基本入口refresh介面入手,在專案啟動時可以看到log
Mapped "{[/refresh || /refresh.json],methods=[POST]}" onto public java.lang.Object org.springframework.cloud.endpoint.GenericPostableMvcEndpoint.invoke()
可以知道refresh對應的handler是GenericPostableMvcEndpoint的invoke方法
2、繼續進入GenericPostableMvcEndpoint看invoke原始碼:
@RequestMapping(method = RequestMethod.POST)
@ResponseBody
@Override
public Object invoke() {
if (!getDelegate().isEnabled()) {
return new ResponseEntity<>(Collections.singletonMap(
"message", "This endpoint is disabled"), HttpStatus.NOT_FOUND);
}
return super.invoke();
}
非常簡單,直接呼叫父類的invoke方法,繼續跟進到AbstractEndpointMvcAdapter類,發現最後呼叫的是delegate的invoke方法,而且delegate是從構造方法傳入的。
protected Object invoke() {
if (!this.delegate.isEnabled()) {
// Shouldn't happen - shouldn't be registered when delegate's disabled
return getDisabledResponse();
}
return this.delegate.invoke();
}
3、步驟2可以看出最終呼叫的是對應泛型的invoke方法,那麼找到注入refresh介面的地方,通過查詢哪裡使用到此類,查詢到LifecycleMvcEndpointAutoConfiguration,通過refreshMvcEndpoint方法注入了refresh介面
@Bean
@ConditionalOnBean(RefreshEndpoint.class)
public MvcEndpoint refreshMvcEndpoint(RefreshEndpoint endpoint) {
return new GenericPostableMvcEndpoint(endpoint);
}
4、可以那麼GenericPostableMvcEndpoint中的delegate就是RefreshEndpoint,轉至研究RefreshEndpoint
@ManagedOperation
public String[] refresh() {
Set<String> keys = contextRefresher.refresh();
return keys.toArray(new String[keys.size()]);
}
@Override
public Collection<String> invoke() {
return Arrays.asList(refresh());
}
發現invoke方法很簡單,只是返回一個修改過的屬性key的集合物件。核心方法contextRefresher.refresh()
5、跟進到contextRefresher.refresh()方法,這裡就是核心了
public synchronized Set<String> refresh() {
//獲取目前系統的配置
Map<String, Object> before = extract(
this.context.getEnvironment().getPropertySources());
//獲取最新配置
addConfigFilesToEnvironment();
//對比目前系統配置和最新配置,返回修改後的屬性
Set<String> keys = changes(before,
extract(this.context.getEnvironment().getPropertySources())).keySet();
//通知系統配置變更
this.context.publishEvent(new EnvironmentChangeEvent(keys));
//對應的bean重新整理
this.scope.refreshAll();
return keys;
}
6、核心就是獲取最新的配置,那麼是如何獲取的呢?之前
還以為是通過直接呼叫config配置載入呢,那麼繼續看addConfigFilesToEnvironment原始碼:
private void addConfigFilesToEnvironment() {
ConfigurableApplicationContext capture = null;
try {
StandardEnvironment environment = copyEnvironment(
this.context.getEnvironment());
//這裡就是核心了,啟動SpringBoot環境
SpringApplicationBuilder builder = new SpringApplicationBuilder(Empty.class)
.bannerMode(Mode.OFF).web(false).environment(environment);
// Just the listeners that affect the environment (e.g. excluding logging
// listener because it has side effects)
builder.application()
.setListeners(Arrays.asList(new BootstrapApplicationListener(),
new ConfigFileApplicationListener()));
capture = builder.run();
if (environment.getPropertySources().contains(REFRESH_ARGS_PROPERTY_SOURCE)) {
environment.getPropertySources().remove(REFRESH_ARGS_PROPERTY_SOURCE);
}
MutablePropertySources target = this.context.getEnvironment()
.getPropertySources();
String targetName = null;
for (PropertySource<?> source : environment.getPropertySources()) {
String name = source.getName();
if (target.contains(name)) {
targetName = name;
}
if (!this.standardSources.contains(name)) {
if (target.contains(name)) {
target.replace(name, source);
}
else {
if (targetName != null) {
target.addAfter(targetName, source);
}
else {
if (target.contains("defaultProperties")) {
target.addBefore("defaultProperties", source);
}
else {
target.addLast(source);
}
}
}
}
}
}
finally {
ConfigurableApplicationContext closeable = capture;
closeable.close();
}
}
通過以上程式碼可知,重新整理不是我之前想象的直接呼叫config獲取最新配置的,而是通過重新建立一個SpringBoot環境(非WEB),等到SpringBoot環境啟動時就相當於重新啟動了一個非web版的伺服器。此時config會自動載入到最新的配置。這個過程類似於啟動伺服器。
等到伺服器啟動成功後,獲取到最新的配置,然後跟原來的配置進行對比,返回修改過的key值。
7、獲取到修改後的配置後,發出EnvironmentChangeEvent事件,ConfigurationPropertiesRebinder監聽了此事件,呼叫rebind方法進行配置重新載入
8、this.scope.refreshAll();首先銷燬scope為refresh的bean。然後發出RefreshScopeRefreshedEvent事件,通知bean生命週期已經變更,已知兩個類EurekaDiscoveryClientConfiguration.EurekaClientConfigurationRefresher接收了此事件,EurekaClientConfigurationRefresher接收到此事件後,進行對eureka伺服器重連的操作。
總結:通過以上步驟,配置重新整理基本流程就是再起一個SpringBoot環境,載入最新配置,與目前環境配置對應,篩選出變化後的屬性,將scope型別為refresh的bean銷燬。等到下一次獲取時bean時重新裝配bean,這樣最新配置就注入ok了。具體其他細節自己Debug就行了。
相關推薦
SpringCloud 詳解配置重新整理的原理
首先先介紹下實現後的效果: 1、在需要動態配置屬性的類上添加註解@RefreshScope表示此類Scope為refresh型別的 2、啟動工程,修改config-server對應的配置檔案,這裡修改的是system.order.serverName 3、
Filebeat 原理詳解/配置檔案分析
配置檔案位置 對於rpm和deb,您將在以下位置找到配置檔案/etc/filebeat/filebeat.yml。在Docker下,它位於/usr/share/filebeat/filebeat
(轉)Java 詳解 JVM 工作原理和流程
移植 獲得 代碼 適配 調用 tac 階段 main方法 等待 作為一名Java使用者,掌握JVM的體系結構也是必須的。說起Java,人們首先想到的是Java編程語言,然而事實上,Java是一種技術,它由四方面組成:Java編程語言、Java類文件格式、Java虛擬機和Ja
Java 詳解 JVM 工作原理和流程
str literal 狀態 應用 流程 href ctu 局部變量 自定義 作為一名Java使用者,掌握JVM的體系結構也是必須的。說起Java,人們首先想到的是Java編程語言,然而事實上,Java是一種技術,它由四方面組成:Java編程語言、Java類文件格式、Jav
mongo 3.4分片集群系列之六:詳解配置數據庫
初始化 kpi 更新 並且 color tag 成員 gin sha 這個系列大致想跟大家分享以下篇章(我會持續更新的↖(^ω^)↗): 1、mongo 3.4分片集群系列之一:淺談分片集群 2、mongo 3.4分片集群系列之二:搭建分片集群--哈希分片 3、mongo
Ansible 小手冊系列 四(詳解配置文件)
method random vbo lec 執行 handle test 語言 rand [root@host-172-20-6-120 ansible]# ansible --version ansible 2.2.0.0 config file = /etc/an
純幹貨詳解iptables工作原理以及使用方法
rip -a sports 公網 寫法 內網ip 行處理 外部 是否 簡介 網絡中的防火墻,是一種將內部和外部網絡分開的方法,是一種隔離技術。防火墻在內網與外網通信時進行訪問控制,依據所設置的規則對數據包作出判斷,最大限度地阻止網絡中不法分子破壞企業網絡,從而加強了企業網絡
Hadoop偽分佈安裝詳解+MapReduce執行原理+基於MapReduce的KNN演算法實現
本篇部落格將圍繞Hadoop偽分佈安裝+MapReduce執行原理+基於MapReduce的KNN演算法實現這三個方面進行敘述。 (一)Hadoop偽分佈安裝 1、簡述Hadoop的安裝模式中–偽分佈模式與叢集模式的區別與聯絡. Hadoop的安裝方式有三種:本地模式,偽分佈模式
HTTP詳解 1 -工作原理
分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!  
BadVPN詳解之--組網原理剖析
分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!  
JavaScript 詳解預編譯原理(和其他語言很不一樣)
JavaScript 預編譯原理 今天用了大量時間複習了作用域、預編譯等等知識 看了很多博文,翻開了以前看過的書(好像好多書都不會講預編譯) 發現當初覺得自己學的很明白,其實還是存在一些思維誤區 (很多博文具有誤導性) 今晚就整理了一下凌亂的思路 先整理一下預編譯的知識吧,日後有時間再把作用
Linux : select()詳解 和 實現原理【轉】
https://www.cnblogs.com/sky-heaven/p/7205491.html#4119169 轉自:http://blog.csdn.net/huntinux/article/details/39289317 原文:http://blog.csdn.n
JVM原理相關 (轉)Java 詳解 JVM 工作原理和流程
(轉)Java 詳解 JVM 工作原理和流程 作為一名Java使用者,掌握JVM的體系結構也是必須的。說起Java,人們首先想到的是Java程式語言,然而事實上,Java是一種技術,它由四方面組成:Java程式語言、Java類檔案格式、Java虛擬機器和Java應用程式介
java代理機制詳解(動態代理原理解析,簡單易懂!)
一.代理機制概念 1.代理機制是一種設計模式,分為靜態代理 與動態代理. 2.特徵:代理類與委託類有同樣的介面,代理類主要負責為委託類預處理訊息、過濾訊息、把訊息轉發給委託類,以及事後處理訊息等。 代理類的物件本身並不真正實現服務,而是通過呼叫委託類的物件的相關方法,來提供特
webpack核心概念詳解及其執行原理
* Entry: 入口, webpack執行構建的第一步將從Entry開始,可抽象成輸入 * Module: 模組,在webpcak中一切皆模組,一個模組對應一個檔案。webpack會從配置的Entry開始遞迴找出所有依賴的模組。 * Chunk: 程式碼塊,一個Chunk由多個模組組合
IIC詳解,包括原理、過程,最後一步步教你實現IIC
IIC詳解 1、I2C匯流排具有兩根雙向訊號線,一根是資料線SDA,另一根是時鐘線SCL 2、IIC總線上可以掛很多裝置:多個主裝置,多個從裝置(外圍 裝置)。上圖中主裝置是兩個微控制器,剩下的都是從裝置。 3、多主機會產生匯流排裁決問題。當多個主機同時想佔用匯
【深度學習系列】卷積神經網路CNN原理詳解(一)——基本原理
轉自:https://www.cnblogs.com/charlotte77/p/7759802.html 上篇文章我們給出了用paddlepaddle來做手寫數字識別的示例,並對網路結構進行到了調整,提高了識別的精度。有的同學表示不是很理解原理,為什麼傳統的機
Java詳解JVM工作原理和流程
作為一名Java使用者,掌握JVM的體系結構也是必須的。 說起Java,人們首先想到的是Java程式語言,然而事實上,Java是一種技術,它由四方面組成:Java程式語言、Java類檔案格式、Java虛擬機器和Java應用程式介面(Java API)。它們的關係如下圖所示:
詳解鴿巢原理【組合數學】
鴿巢原理的簡單形式: 如果要把n+1個物體,放進n個盒子,那麼至少有一個盒子包含兩個或更多的物體。 證明:用反證法。如果這n個盒子中的每一個都至多含有一個物體,那麼物體的最多數量是n。這與我們有n+1個物體的實際情況相矛盾,故不成立。 當然,對於鴿巢原理的
redis配置檔案詳解配置檔案redis.conf
配置檔案redis.conf daemonize yes #---預設值no,該引數用於定製redis服務是否以守護模式執行。--- pidfile /var/run/redis.pid #預設值/var/run/redis.pid,指定redis服務的程序號檔案路徑