摘要: 原創出處 http://www.iocoder.cn/Eureka/eureka-client-init-first/ 「芋道源碼」歡迎轉載,保留摘要,謝謝!
本文主要基於 Eureka 1.8.X 版本
- 1. 概述
- 2. EurekaInstanceConfig
- 3. InstanceInfo
- 4. ApplicationInfoManager
1. 概述
本文主要分享 Eureka-Client 自身初始化的過程,不包含 Eureka-Client 向 Eureka-Server 的註冊過程( ��後面會另外文章分享 )。
Eureka-Client 自身初始化過程中,涉及到主要對象如下圖:
- 創建 EurekaInstanceConfig對象
- 使用 EurekaInstanceConfig對象 創建 InstanceInfo對象
- 使用 EurekaInstanceConfig對象 + InstanceInfo對象 創建 ApplicationInfoManager對象
- 創建 EurekaClientConfig對象
- 使用 ApplicationInfoManager對象 + EurekaClientConfig對象 創建 EurekaClient對象
考慮到整個初始化的過程中涉及的配置特別多,拆分成三篇文章:
- 【本文】(一)EurekaInstanceConfig
- (二)EurekaClientConfig
- (三)EurekaClient
下面我們來看看每個類的實現。
推薦 Spring Cloud 書籍:
- 請支持正版。下載盜版,等於主動編寫低級 BUG 。
- 程序猿DD —— 《Spring Cloud微服務實戰》
- 周立 —— 《Spring Cloud與Docker微服務架構實戰》
- 兩書齊買,京東包郵。
2. EurekaInstanceConfig
com.netflix.appinfo.EurekaInstanceConfig
,Eureka 應用實例配置接口。在下文你會看到 EurekaClientConfig 接口,兩者的區別如下:
- EurekaInstanceConfig,重在應用實例,例如,應用名、應用的端口等等。此處應用指的是,Application Consumer 和 Application Provider。
- EurekaClientConfig,重在 Eureka-Client,例如, 連接的 Eureka-Server 的地址、獲取服務提供者列表的頻率、註冊自身爲服務提供者的頻率等等。
2.1 類關係圖
EurekaInstanceConfig 整體類關係如下圖:
- 本文只解析紅圈部分類。
- EurekaArchaius2ClientConfig 基於 Netflix Archaius 2.x 實現,目前還在開發中,因此暫不解析。
- CloudInstanceConfig、Ec2EurekaArchaius2InstanceConfig 基於亞馬遜 AWS,大多數讀者和我對 AWS 都不瞭解,因此暫不解析。
2.2 配置屬性
點擊 EurekaInstanceConfig 查看配置屬性簡介,已經添加中文註釋,可以對照着英文註釋一起理解。這裏筆者摘出部分較爲重要的屬性:
#getLeaseRenewalIntervalInSeconds()
:租約續約頻率,單位:秒。應用不斷通過按照該頻率發送心跳給 Eureka-Server 以達到續約的作用。當 Eureka-Server 超過最大頻率未收到續約(心跳),契約失效,進行應用移除。應用移除後,其他應用無法從 Eureka-Server 獲取該應用。#getLeaseExpirationDurationInSeconds()
:契約過期時間,單位:秒。#getDataCenterInfo()
:數據中心信息。com.netflix.appinfo.DataCenterInfo
,數據中心信息接口,目前較爲簡單,標記所屬數據中心名。一般情況下,我們使用Name.MyOwn
。接口實現代碼如下:public interface DataCenterInfo {/*** 數據中心名枚舉*/enum Name {Netflix,Amazon,MyOwn}/*** @return 歸屬的數據中心名*/Name getName();}#getNamespace()
:配置命名空間,默認使用eureka
。以eureka-client.properties
舉個例子:eureka.name=eurekaeureka.port=8080eureka.vipAddress=eureka.mydomain.net- 每個屬性最前面的
eureka
即是配置命名空間,一般情況無需修改。
- 每個屬性最前面的
TODO[0004]:健康檢查
#isInstanceEnabledOnit()
:應用初始化後是否開啓。在「3. InstanceInfo」詳細解析。
2.3 AbstractInstanceConfig
com.netflix.appinfo.AbstractInstanceConfig
,Eureka 應用實例配置抽象基類,主要實現一些相對通用的配置,實現代碼如下:
|
#getHostInfo()
方法,獲取本地服務器的主機名和主機 IP 地址。如果主機有多網卡或者虛擬機網卡,這塊要小心,解決方式如下:- 手動配置本機的
hostname
+etc/hosts
文件,從而映射主機名和 IP 地址。 - 使用 Spring-Cloud-Eureka-Client 的話,參考周立 —— 《Eureka服務註冊過程詳解之IpAddress》解決。
- 手動配置本機的
2.4 PropertiesInstanceConfig
com.netflix.appinfo.PropertiesInstanceConfig
,基於配置文件的 Eureka 應用實例配置抽象基類,實現代碼如下:
|
configInstance
屬性,配置文件對象,基於 Netflix Archaius 1.x 實現配置文件的讀取。在com.netflix.appinfo.PropertyBasedInstanceConfigConstants
可以看到配置文件的每個屬性 KEY 。appGrpNameFromEnv
屬性,應用分組,從環境變量中獲取。從#getAppGroupName()
方法中,可以看到優先還是從配置文件讀取。設置方法如下:System.setProperty(FALLBACK_APP_GROUP_KEY, "app_gropu_name");FALLBACK_APP_GROUP_KEY
,私有靜態變量,實際得使用NETFLIX_APP_GROUP
。com.netflix.config.ConfigurationManager
可以從環境變量獲取到值。
調用
Archaius1Utils#initConfig(...)
方法,初始化讀取的配置文件對象,實現代碼如下:
public final class Archaius1Utils {private static final Logger logger = LoggerFactory.getLogger(Archaius1Utils.class);private static final String ARCHAIUS_DEPLOYMENT_ENVIRONMENT = "archaius.deployment.environment";private static final String EUREKA_ENVIRONMENT = "eureka.environment";public static DynamicPropertyFactory initConfig(String configName) {// 配置文件對象DynamicPropertyFactory configInstance = DynamicPropertyFactory.getInstance();// 配置文件名DynamicStringProperty EUREKA_PROPS_FILE = configInstance.getStringProperty("eureka.client.props", configName);// 配置文件環境String env = ConfigurationManager.getConfigInstance().getString(EUREKA_ENVIRONMENT, "test");ConfigurationManager.getConfigInstance().setProperty(ARCHAIUS_DEPLOYMENT_ENVIRONMENT, env);// 將配置文件加載到環境變量String eurekaPropsFile = EUREKA_PROPS_FILE.get();try {ConfigurationManager.loadCascadedPropertiesFromResources(eurekaPropsFile);} catch (IOException e) {logger.warn("Cannot find the properties specified : {}. This may be okay if there are other environment "+ "specific properties or the configuration is installed with a different mechanism.",eurekaPropsFile);}return configInstance;}}- 從環境變量
eureka.client.props
,獲取配置文件名。如果未配置,使用參數configName
,即CommonConstants.CONFIG_FILE_NAME
("eureka-client"
)。 - 從環境變量
eureka.environment
( EUREKA_ENVIRONMENT ),獲取配置文件環境。 - 調用
ConfigurationManager#loadCascadedPropertiesFromResources(...)
方法,讀取配置文件到環境變量,首先讀取${eureka.client.props}</code> 對應的配置文件;然後讀取 <code>${eureka.client.props}-${eureka.environment}
對應的配置文件。若有相同屬性,進行覆蓋。
- 從環境變量
2.5 MyDataCenterInstanceConfig
com.netflix.appinfo.MyDataCenterInstanceConfig
,非 AWS 數據中心的 Eureka 應用實例配置實現類,實現代碼如下:
|
2.6 小結
一般情況下,使用 MyDataCenterInstanceConfig 配置 Eureka 應用實例。
在 Spring-Cloud-Eureka 裏,直接基於 EurekaInstanceConfig 接口重新實現了配置類,實際邏輯差別不大,在TODO[0007] :《Spring-Cloud-Eureka-Client》詳細解析。
3. InstanceInfo
com.netflix.appinfo.InstanceInfo
,應用實例信息。Eureka-Client 向 Eureka-Server 註冊該對象信息。註冊成功後,可以被其他 Eureka-Client 發現。
本文僅分享 InstanceInfo 的初始化。InstanceInfo 裏和註冊發現相關的屬性和方法,暫時跳過。
com.netflix.appinfo.providers.EurekaConfigBasedInstanceInfoProvider
,基於 EurekaInstanceConfig 創建 InstanceInfo 的工廠,實現代碼如下:
|
- 該類實現
javax.inject.Provider
接口,設置 InstanceInfo 的生成工廠。感興趣的同學,可以點擊《Google-Guice入門介紹》搜索 Provider 關鍵字。目前處於試驗階段,未完成。 EurekaConfigBasedInstanceInfoProvider(config)
構造方法,設置生成 InstanceInfo 的 EurekaInstanceConfig 配置。調用
#get()
方法,根據 EurekaInstanceConfig 創建 InstanceInfo。InstanceInfo 的絕大數屬性和 EurekaInstanceConfig 是一致的 。實現代碼如下:- 第 21 至 24 行 :創建租約信息構建器(
com.netflix.appinfo.LeaseInfo.Builder
),並設置renewalIntervalInSecs
/durationInSecs
屬性。 第 26 至 29 行 :創建 VIP地址解析器(
com.netflix.appinfo.providers.VipAddressResolver
)。實現代碼如下:// VipAddressResolver.javapublic interface VipAddressResolver {String resolveDeploymentContextBasedVipAddresses(String vipAddressMacro);}public class Archaius1VipAddressResolver implements VipAddressResolver {private static final Pattern VIP_ATTRIBUTES_PATTERN = Pattern.compile("\\$\\{(.*?)\\}"</span>);</div><div class="line"></div><div class="line"> <span class="meta">@Override</span></div><div class="line"> <span class="function"><span class="keyword">public</span> String <span class="title">resolveDeploymentContextBasedVipAddresses</span><span class="params">(String vipAddressMacro)</span> </span>{</div><div class="line"> <span class="keyword">if</span> (vipAddressMacro == <span class="keyword">null</span>) {</div><div class="line"> <span class="keyword">return</span> <span class="keyword">null</span>;</div><div class="line"> }</div><div class="line"> String result = vipAddressMacro;</div><div class="line"> <span class="comment">// 替換表達式</span></div><div class="line"> Matcher matcher = VIP_ATTRIBUTES_PATTERN.matcher(result);</div><div class="line"> <span class="keyword">while</span> (matcher.find()) {</div><div class="line"> String key = matcher.group(<span class="number">1</span>);</div><div class="line"> String value = DynamicPropertyFactory.getInstance().getStringProperty(key, <span class="string">""</span>).get();</div><div class="line"></div><div class="line"> logger.debug(<span class="string">"att:"</span> + matcher.group());</div><div class="line"> logger.debug(<span class="string">", att key:"</span> + key);</div><div class="line"> logger.debug(<span class="string">", att value:"</span> + value);</div><div class="line"> logger.debug(<span class="string">""</span>);</div><div class="line"></div><div class="line"> result = result.replaceAll(<span class="string">"\\$\\{" + key + "\\}", value);matcher = VIP_ATTRIBUTES_PATTERN.matcher(result);}return result;}}- 使用
#resolveDeploymentContextBasedVipAddresses()
方法,將 VIP地址 裏的${(.*?)}</code> 查找配置文件裏的鍵值進行替換。例如,<code>${eureka.env}.domain.com
,查找配置文件裏的鍵${eureka.env}
對應值進行替換。TODO[0005]:調試下來,發現 Archaius 已經替換,等到找到答案修改此處。
- 使用
第 32 至 33 行 :創建應用實例信息構建器(
com.netflix.appinfo.InstanceInfo.Builder
)。
- 第 35 至 45 行 :獲得應用實例編號(
instanceId
)。 - 第 47 至 58 行 :獲得主機名。
- 第 60 至 78 行 :設置應用實例信息構建器的屬性。
- 第 80 至 90 行 :應用初始化後是否開啓。
- 第 82 至 85 行 :應用不開啓,應用實例處於 STARTING 狀態。
- 第 86 至 90 行 :應用開啓,應用實例處於 UP 狀態。
- 使用應用初始化後不開啓,可以通過調用
ApplicationInfoManager#setInstanceStatus(...)
方法改變應用實例狀態,在《Eureka 源碼解析 —— 應用實例註冊發現 (一)之註冊》「2.1 應用實例信息複製器」有詳細解析。
- 第 92 至 98 行 :設置應用實例信息構建器的元數據( Metadata )集合。
- 第 100 至 101 行 :創建應用實例信息(
com.netflix.appinfo.InstanceInfo
)。 - 第 103 至 104 行 :設置應用實例信息的租約信息(
com.netflix.appinfo.InstanceInfo
)。
- 第 21 至 24 行 :創建租約信息構建器(
4. ApplicationInfoManager
com.netflix.appinfo.ApplicationInfoManager
,應用信息管理器。實現代碼如下:
|
listeners
屬性,狀態變更監聽器集合。在《Eureka 源碼解析 —— 應用實例註冊發現 (一)之註冊》「2.1 應用實例信息複製器」有詳細解析。instanceStatusMapper
屬性,應用實例狀態匹配。實現代碼如下:// ApplicationInfoManager.javapublic static interface InstanceStatusMapper {}private static final InstanceStatusMapper NO_OP_MAPPER = new InstanceStatusMapper() {public InstanceStatus map(InstanceStatus prev) {return prev;}};#map
方法,根據傳入pre
參數,轉換成對應的應用實例狀態。- 默認情況下,使用 NO_OP_MAPPER 。一般情況下,不需要關注該類。