溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

微服務(wù)中如何進行Eureka配置部分源碼分析

發(fā)布時間:2022-01-14 20:41:04 來源:億速云 閱讀:185 作者:柒染 欄目:服務(wù)器

這篇文章將為大家詳細講解有關(guān)微服務(wù)中如何進行Eureka配置部分源碼分析,文章內(nèi)容質(zhì)量較高,因此小編分享給大家做個參考,希望大家閱讀完這篇文章后對相關(guān)知識有一定的了解。

簡介

今天,我們開始來研究 Eureka 的源碼,先從配置部分的源碼開始看,其他部分后面再補充。

補充一點,我更多地會從設(shè)計層面分析源碼,而不會順序地剖析每個過程的代碼。一方面是因為篇幅有限,另一方面是因為我認為這樣做更有意義一些。

項目環(huán)境

  • os:win 10

  • jdk:1.8.0_231

  • eureka:1.10.11

  • maven:3.6.3

從一個例子開始

ConcurrentCompositeConfiguration 這個類是 Eureka 配置體系的核心 。在這個例子中,我們使用它 對 property  進行增刪改查 ,并 注冊了自定義監(jiān)聽器來監(jiān)聽 property 的改變 。

@Test     public void test01() {         // 創(chuàng)建配置對象         final ConcurrentCompositeConfiguration config = new ConcurrentCompositeConfiguration();          // 注冊監(jiān)聽器監(jiān)聽property的改變         config.addConfigurationListener(new ConfigurationListener() {                          public void configurationChanged(ConfigurationEvent event) {                 // 增加property                 if(AbstractConfiguration.EVENT_ADD_PROPERTY == event.getType()                          && !event.isBeforeUpdate()) {                     System.err.println("add property:" + event.getPropertyName() + "=" + event.getPropertyValue());                     return;                 }                 // 刪除property                 if(AbstractConfiguration.EVENT_CLEAR_PROPERTY == event.getType()) {                     System.err.println("clear property:" + event.getPropertyName());                     return;                 }                 // 更新property                 if(AbstractConfiguration.EVENT_SET_PROPERTY == event.getType()                          && event.isBeforeUpdate()                         && !config.getString(event.getPropertyName()).equals(event.getPropertyValue())) {                     System.err.println("update property:"                      + event.getPropertyName()                      + ":"                      + config.getString(event.getPropertyName())                     + "==>"                     + event.getPropertyValue()                     );                     return;                 }             }         });         // 添加property         config.addProperty("author", "zzs");         // 獲取property         System.err.println(config.getString("author"));         // 更改property         config.setProperty("author", "zzf");         // 刪除property         config.clearProperty("author");     } //    運行以上方法,控制臺打印內(nèi)容: //    add property:author=zzs //    zzs //    update property:author:zzs==>zzf //    clear property:author

可以看到,當(dāng)我們更改了 property 時,監(jiān)聽器中的方法被觸發(fā)了,利用這一點,我們可以實現(xiàn)動態(tài)配置。

后面就會發(fā)現(xiàn), Eureka 底層使用 ConcurrentCompositeConfiguration  來對配置參數(shù)進行增刪改查,并基于事件監(jiān)聽的機制來支持動態(tài)配置 。

另一個有意思的地方

我們再來看看一個 UML 圖。上面例子中說到 ConcurrentCompositeConfiguration 的兩個功能,是通過實現(xiàn)  Configuration 和繼承 EventSource 來獲得的,這一點沒什么特別的,之所以深究它,是因為我發(fā)現(xiàn)了其他有趣的地方。

微服務(wù)中如何進行Eureka配置部分源碼分析

我們主要來關(guān)注下它的三個成員屬性(它們都是 AbstractConfiguration 類型):

  1. configList :持有的配置對象集合。 這個集合的配置對象存在優(yōu)先級 ,舉個例子,如果我添加了 Configuration1 和  Configuration2,當(dāng)我們 getProperty(String) 時,會優(yōu)先從 Configuration1 獲取,實在找不到才會去  Configuration2 獲取。

  2. overrideProperties : 最高優(yōu)先級的配置對象 。當(dāng)我們 getProperty(String) 時,會先從這里獲取,實在沒有才會去  configList 里找。

  3. containerConfiguration : 保底的配置對象 。一般是 configList 的最后一個(注意,不一定是最后一個1),我們往  ConcurrentCompositeConfiguration 里增刪改 property,實際操作的就是這個對象。

為了更好理解它們的作用,我寫了個測試例子。

@Test     public void test02() {         // 創(chuàng)建配置對象         ConcurrentCompositeConfiguration config = new ConcurrentCompositeConfiguration();          // 添加配置1         ConcurrentMapConfiguration config1 = new ConcurrentMapConfiguration();         config1.addProperty("author", "zzs");         config.addConfiguration(config1, "CONFIG_01");                  // 添加配置2         ConcurrentMapConfiguration config2 = new ConcurrentMapConfiguration();         config2.addProperty("author", "zzf");         config.addConfiguration(config2, "CONFIG_02");                  // 在默認的containerConfiguration中添加property         config.addProperty("author", "zhw");                  // ============以下測試configList的優(yōu)先級============         System.err.println(config.getString("author"));         // 刪除config1中的property         config1.clearProperty("author");         System.err.println(config.getString("author"));         // 刪除config2中的property         config2.clearProperty("author");         System.err.println(config.getString("author"));                  // ============以下測試overrideProperties的優(yōu)先級============         // 添加overrideProperties的property         config.setOverrideProperty("author", "lt");         System.err.println(config.getString("author"));     } //    運行以上方法,控制臺打印內(nèi)容: //    zzs //    zzf //    zhw //    lt

這里補充一點,當(dāng)我們創(chuàng)建 ConcurrentCompositeConfiguration 時,就會生成一個  containerConfiguration,默認情況下,它會一直在集合最后面,每次添加新的配置對象,都是往 containerConfiguration  前面插入。

誰來加載配置

通過上面的例子可以知道, ConcurrentCompositeConfiguration 并不會主動地去加載配置,所以,Eureka 需要自己往  ConcurrentCompositeConfiguration 里添加配置,而完成這件事的是另外一個類-- ConfigurationManager  。

微服務(wù)中如何進行Eureka配置部分源碼分析

ConfigurationManager 作為一個單例對象使用,用來初始化配置對象,以及提供加載配置文件的方法 (后面的  DefaultEurekaClientConfig 、 DefaultEurekaServerConfig 會來調(diào)用這些方法)。

下面我們看看配置對象的初始化。在 ConfigurationManager  被加載時就會初始化配置對象,進入到它的靜態(tài)代碼塊就可以找到。我截取的是最關(guān)鍵部分的代碼。

private static AbstractConfiguration createDefaultConfigInstance() {         ConcurrentCompositeConfiguration config = new ConcurrentCompositeConfiguration();           try {             // 加載指定url的配置             // 通過archaius.configurationSource.additionalUrls啟動參數(shù)設(shè)置url,多個逗號隔開             DynamicURLConfiguration defaultURLConfig = new DynamicURLConfiguration();             config.addConfiguration(defaultURLConfig, URL_CONFIG_NAME);         } catch (Throwable e) {             logger.warn("Failed to create default dynamic configuration", e);         }         if (!Boolean.getBoolean(DISABLE_DEFAULT_SYS_CONFIG)) {             // 加載System.getProperties()的配置             // 通過archaius.dynamicProperty.disableSystemConfig啟動參數(shù)可以控制是否添加             SystemConfiguration sysConfig = new SystemConfiguration();             config.addConfiguration(sysConfig, SYS_CONFIG_NAME);         }         if (!Boolean.getBoolean(DISABLE_DEFAULT_ENV_CONFIG)) {             // 加載System.getenv()的配置             // 通過archaius.dynamicProperty.disableEnvironmentConfig啟動參數(shù)可以控制是否添加             EnvironmentConfiguration envConfig = new EnvironmentConfiguration();             config.addConfiguration(envConfig, ENV_CONFIG_NAME);         }         // 這個是自定義的保底配置         ConcurrentCompositeConfiguration appOverrideConfig = new ConcurrentCompositeConfiguration();         config.addConfiguration(appOverrideConfig, APPLICATION_PROPERTIES);         config.setContainerConfigurationIndex(config.getIndexOfConfiguration(appOverrideConfig));// 這里可以更改保底配置         return config;     }

可以看到, Eureka 支持通過 url 來指定配置文件,只要指定啟動參數(shù)就行  ,這一點將有利于我們更靈活地對項目進行配置。默認情況下,它還會去加載所有的系統(tǒng)參數(shù)和環(huán)境參數(shù)。

另外,當(dāng)我們設(shè)置以下啟動參數(shù),就可以通過 JMX 的方式來更改配置。

-Darchaius.dynamicPropertyFactory.registerConfigWithJMX=true

配置對象初始化后, ConfigurationManager 提供了方法供我們加載配置文件(本地或遠程),如下。

// 這兩個的區(qū)別在于:前者會生成一個新的配置添加到configList;后者直接將property都加入到appOverrideConfig public static void loadCascadedPropertiesFromResources(String configName) throws IOException; public static void loadAppOverrideProperties(String appConfigName);

怎么拿到最新的參數(shù)

動態(tài)配置的內(nèi)容直接看源碼不大好理解,我們先通過一個再簡單不過的例子開始來一步步實現(xiàn)我們自己的動態(tài)配置。在下面的方法中,我更改了  property,但是拿不到更新的值。原因嘛,我相信大家都知道。

@Test     public void test03() {         // 獲取配置對象         AbstractConfiguration config = ConfigurationManager.getConfigInstance();         // 添加一個property         config.addProperty("author", "zzs");                  String author = config.getString("author", "");                  System.err.println(author);                  // 更改property         config.setProperty("author", "zzf");                  System.err.println(author);     } //    運行以上方法,控制臺打印內(nèi)容: //    zzs //    zzs

為了拿到更新的值,我把代碼改成這樣。我不定義變量來存放 property 的值,每次都重新獲取。顯然,這樣做可以成功。

@Test     public void test04() {         // 獲取配置對象         AbstractConfiguration config = ConfigurationManager.getConfigInstance();         // 添加一個property         config.addProperty("author", "zzs");                  System.err.println(config.getString("author", ""));                  // 更改property         config.setProperty("author", "zzf");                  System.err.println(config.getString("author", ""));     } //    運行以上方法,控制臺打印內(nèi)容: //    zzs //    zzf

但是上面的做法有個問題,我們都知道從 ConcurrentCompositeConfiguration 中獲取 property  是比較麻煩的,因為我需要去遍歷 configList,以及進行參數(shù)的轉(zhuǎn)換等。每次都這樣拿,不大合理。

于是,我增加了緩存來減少這部分的開銷,當(dāng)然,property 更改時我必須刷新緩存。

@Test     public void test05() {         // 緩存         Map<String, String> cache = new ConcurrentHashMap<String, String>();         // 獲取配置對象         AbstractConfiguration config = ConfigurationManager.getConfigInstance();         // 添加一個property         config.addProperty("author", "zzs");                  String value = cache.computeIfAbsent("author", x -> config.getString(x, ""));         System.err.println(value);                  // 添加監(jiān)聽器監(jiān)聽property的更改         config.addConfigurationListener(new ConfigurationListener() {             public void configurationChanged(ConfigurationEvent event) {                 // 刪除property                 if(AbstractConfiguration.EVENT_CLEAR_PROPERTY == event.getType()) {                     cache.remove(event.getPropertyName());                     return;                 }                 // 更新property                 if(AbstractConfiguration.EVENT_SET_PROPERTY == event.getType()                          && !event.isBeforeUpdate()) {                     cache.put(event.getPropertyName(), String.valueOf(event.getPropertyValue()));                     return;                 }             }         });                  // 更改property         config.setProperty("author", "zzf");                  System.err.println(cache.get("author"));     } //    運行以上方法,控制臺打印內(nèi)容: //    zzs //    zzf

通過上面的例子,我們實現(xiàn)了動態(tài)配置。

現(xiàn)在我們再來看看 Eureka 是怎么實現(xiàn)的。這里用到了 DynamicPropertyFactory 和 DynamicStringProperty  兩個類,通過它們,也實現(xiàn)了動態(tài)配置。

@Test     public void test06() {         // 獲取配置對象         AbstractConfiguration config = ConfigurationManager.getConfigInstance();         // 添加一個property         config.addProperty("author", "zzs");                  // 通過DynamicPropertyFactory獲取property         DynamicPropertyFactory dynamicPropertyFactory = DynamicPropertyFactory.getInstance();         DynamicStringProperty stringProperty = dynamicPropertyFactory.getStringProperty("author", "");                  System.err.println(stringProperty.get());                  // 更改property         config.setProperty("author", "zzf");                  System.err.println(stringProperty.get());     } //    運行以上方法,控制臺打印內(nèi)容: //    zzs //    zzf

至于原理,其實和我們上面的例子是差不多的。通過 UML 圖可以知道, DynamicProperty 中就放了一張緩存表,每次獲取 property  時,會優(yōu)先從這里拿。

微服務(wù)中如何進行Eureka配置部分源碼分析

既然有緩存,就應(yīng)該有監(jiān)聽器,沒錯,在 DynamicProperty.initialize(DynamicPropertySupport)  方法中就可以看到。

static synchronized void initialize(DynamicPropertySupport config) {         dynamicPropertySupportImpl = config;         // 注冊監(jiān)聽器         config.addConfigurationListener(new DynamicPropertyListener());         updateAllProperties();     }

Eureka有那幾類配置

在上面的分析中,我們用 ConfigurationManager 來初始化配置對象,并使用 DynamicPropertyFactory  來實現(xiàn)動態(tài)配置,這些東西構(gòu)成了 Eureka 的配置體系的基礎(chǔ),比較通用。基礎(chǔ)之上,是 Eureka 更具體的一些配置對象。

在 Eureka 里,配置分成了三種(理解這一點非常重要):

  1. EurekaInstanceConfig :當(dāng)前實例身份的配置信息,即 我是誰?

  2. EurekaServerConfig :一些影響當(dāng)前Eureka Server和客戶端或?qū)Φ裙?jié)點交互行為的配置信息,即 怎么交互?

  3. EurekaClientConfig :一些影響當(dāng)前實例和Eureka Server交互行為的配置信息,即 和誰交互?怎么交互?

這三個對象都持有了 DynamicPropertyFactory 的引用,所以支持動態(tài)配置,另外,它們還是用 ConfigurationManager  來加載自己想要的配置文件。例如, EurekaInstanceConfig 、 EurekaClientConfig 負責(zé)加載  eureka-client.properties ,而 EurekaServerConfig 則負責(zé)加載 eureka-server.properties  。

微服務(wù)中如何進行Eureka配置部分源碼分析

關(guān)于微服務(wù)中如何進行Eureka配置部分源碼分析就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,可以學(xué)到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。

向AI問一下細節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI