1. 程式人生 > >SpringCloud 詳解配置重新整理的原理

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,繼續訪問

http://localhost:8100/infoTest 顯示新的資料如下:
這裡寫圖片描述
可以看到對應的屬性值已經變化了。

基本使用演示完了,下面該對屬性重新整理原理進行詳細探究:
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來做手寫數字識別的示例,並對網路結構進行到了調整,提高了識別的精度。有的同學表示不是很理解原理,為什麼傳統的機

JavaJVM工作原理和流程

作為一名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服務的程序號檔案路徑