在SpringCloud元件:Eureka服務註冊是採用主機名還是IP地址?文章中我們講到了服務註冊
的幾種註冊方式
,那麼這幾種註冊方式
的原始碼是怎麼實現的呢?我們帶著這一個疑問來閱讀本章內容能夠讓你更深入瞭解這塊的知識點!!!
本章目標
分析每一種服務註冊方式
原始碼執行流程。
構建專案
本章以分析原始碼為主,所以不去新建立專案來講解相關內容,我們使用SpringCloud元件:Eureka服務註冊是採用主機名還是IP地址?原始碼作為註冊服務
,SpringCloud元件:搭建Eureka服務註冊中心原始碼作為服務註冊中心
,還是按照之前的執行流程:
- 啟動服務註冊中心
- 啟動本章服務專案
- 檢視服務列表,服務註冊方式
配置資訊獲取執行流程
在開始講解本章註冊方式
之前,我們需要了解整體的配置資訊
獲取的流程資訊,這樣才可以分析指定的註冊方式
執行流程。
第一步:例項化EurekaInstanceConfigBean
配置實體
在專案啟動時由於依賴spring-cloud-starter-netflix-eureka-client
內通過配置spring.factories
檔案來讓專案啟動時自動載入並例項化org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration
配置類,EurekaClientAutoConfiguration
內會自動例項化EurekaInstanceConfigBean
並且自動繫結eureka.instance
開頭的配置資訊(具體為什麼會自動對映可以去了解下@ConfigurationProperties
註解作用),部分原始碼如下所示:
......
public class EurekaClientAutoConfiguration {
//省略部分原始碼
@Bean
@ConditionalOnMissingBean(value = EurekaInstanceConfig.class, search = SearchStrategy.CURRENT)
public EurekaInstanceConfigBean eurekaInstanceConfigBean(InetUtils inetUtils, ManagementMetadataProvider managementMetadataProvider) {
//省略部分原始碼
// 傳遞
EurekaInstanceConfigBean instance = new EurekaInstanceConfigBean(inetUtils);
// 省略部分原始碼
}
//省略部分原始碼
}
複製程式碼
EurekaClientAutoConfiguration#eurekaInstanceConfigBean
方法只有滿足@ConditionalOnMissingBean(value = EurekaInstanceConfig.class, search = SearchStrategy.CURRENT)
表示式後才會去例項化,並且把例項化物件放入到IOC
容器內容,BeanId
為eurekaInstanceConfigBean
,也就是方法的名稱。
在EurekaClientAutoConfiguration#eurekaInstanceConfigBean
方法中有這麼一行程式碼我們可以進行下一步的分析
// 通過有參建構函式例項化EurekaInstanceConfigBean配置實體
EurekaInstanceConfigBean instance = new EurekaInstanceConfigBean(inetUtils);
複製程式碼
通過呼叫EurekaInstanceConfigBean(InetUtils inetUtils)
建構函式來進行例項化EurekaInstanceConfigBean
物件,在這個建構函式內也有一些例項化的工作,原始碼如下:
public EurekaInstanceConfigBean(InetUtils inetUtils) {
this.inetUtils = inetUtils;
this.hostInfo = this.inetUtils.findFirstNonLoopbackHostInfo();
this.ipAddress = this.hostInfo.getIpAddress();
this.hostname = this.hostInfo.getHostname();
}
複製程式碼
第二步:InetUtils#findFirstNonLoopbackHostInfo
獲取主機基本資訊
在建構函式EurekaInstanceConfigBean(InetUtils inetUtils)
原始碼實現內hostInfo
主機資訊通過了InetUtils#findFirstNonLoopbackHostInfo
方法來進行例項化,我們來看看這個方法的具體實現邏輯,它會自動讀取系統網路卡列表
然再進行迴圈遍歷
查詢正在UP
狀態的網路卡資訊,如果沒有查詢到網路卡資訊,則使用預設的HostName
、IpAddress
配置資訊,原始碼如下所示:
public HostInfo findFirstNonLoopbackHostInfo() {
InetAddress address = findFirstNonLoopbackAddress();
if (address != null) {
return convertAddress(address);
}
HostInfo hostInfo = new HostInfo();
hostInfo.setHostname(this.properties.getDefaultHostname());
hostInfo.setIpAddress(this.properties.getDefaultIpAddress());
return hostInfo;
}
public InetAddress findFirstNonLoopbackAddress() {
InetAddress result = null;
try {
int lowest = Integer.MAX_VALUE;
for (Enumeration<NetworkInterface> nics = NetworkInterface
.getNetworkInterfaces(); nics.hasMoreElements();) {
NetworkInterface ifc = nics.nextElement();
if (ifc.isUp()) {
log.trace("Testing interface: " + ifc.getDisplayName());
if (ifc.getIndex() < lowest || result == null) {
lowest = ifc.getIndex();
}
else if (result != null) {
continue;
}
// @formatter:off
if (!ignoreInterface(ifc.getDisplayName())) {
for (Enumeration<InetAddress> addrs = ifc
.getInetAddresses(); addrs.hasMoreElements();) {
InetAddress address = addrs.nextElement();
if (address instanceof Inet4Address
&& !address.isLoopbackAddress()
&& isPreferredAddress(address)) {
log.trace("Found non-loopback interface: "
+ ifc.getDisplayName());
result = address;
}
}
}
// @formatter:on
}
}
}
catch (IOException ex) {
log.error("Cannot get first non-loopback address", ex);
}
if (result != null) {
return result;
}
try {
return InetAddress.getLocalHost();
}
catch (UnknownHostException e) {
log.warn("Unable to retrieve localhost");
}
return null;
}
複製程式碼
預設的HostName
、IpAddress
屬性配置資訊在InetUtilsProperties
配置實體類內,如果不進行設定則直接使用預設值,如果你想更換預設值
,那麼你可以在application.yml
配置檔案內通過設定spring.cloud.inetutils.defaultHostname
、spring.cloud.inetutils.defaultIpAddress
進行修改預設值,原始碼如下所示:
public class InetUtilsProperties {
public static final String PREFIX = "spring.cloud.inetutils";
/**
* The default hostname. Used in case of errors.
*/
private String defaultHostname = "localhost";
/**
* The default ipaddress. Used in case of errors.
*/
private String defaultIpAddress = "127.0.0.1";
}
複製程式碼
第三步:EurekaInstanceConfigBean#getHostName
方法實現
getHostName
是一個Override
的方法,繼承於com.netflix.appinfo.EurekaInstanceConfig
介面,該方法有個boolean
型別的引數refresh
來判斷是否需要重新整理重新獲取主機網路基本資訊,當傳遞refresh=false
並且在application.yml
配置檔案內並沒有進行手動設定eureka.instance.hostname
以及eureka.instance.ip-address
引數則會根據eureka.instance.prefer-ip-address
設定的值進行返回資訊,原始碼如下所示:
@Override
public String getHostName(boolean refresh) {
if (refresh && !this.hostInfo.override) {
this.ipAddress = this.hostInfo.getIpAddress();
this.hostname = this.hostInfo.getHostname();
}
return this.preferIpAddress ? this.ipAddress : this.hostname;
}
複製程式碼
預設註冊方式原始碼分析
由於在例項化EurekaInstanceConfigBean
配置實體類時,建構函式進行了獲取第一個非迴環主機資訊
,預設的hostName
以及ipAddress
引數則是會直接使用InetUtils#findFirstNonLoopbackHostInfo
方法返回的相對應的值。
IP優先註冊方式原始碼分析
EurekaInstanceConfigBean#getHostName
方法直接呼叫本類過載方法getHostName(boolean refresh)
並且傳遞引數為false
,根據第三步原始碼我們就可以看到:
return this.preferIpAddress ? this.ipAddress : this.hostname;
複製程式碼
如果eureka.instance.prefer-ip-address
引數設定了true
就會返回eureka.instance.ip-address
的值,這樣我們就可以從中明白為什麼主動設定eureka.instance.ip-address
引數後需要同時設定eureka.instance.prefer-ip-address
引數才可以生效。
指定IP、HostName原始碼分析
我們通過application.yml
配置檔案進行設定eureka.instance.hostname
以及eureka.instance.ip-address
後會直接替換原預設值,在EurekaInstanceConfigBean#getHostName
中也是返回的this.hostname
、this.ipAddress
所以在這裡設定後會直接生效作為返回的配置值。
總結
我們通過原始碼進行分析服務註冊方式
執行流程,這樣在以後進行配置eureka.instance.hostname
、eureka.instance.prefer.ip-address
、eureka.instance.ip-address
三個配置資訊時就可以根據優先順序順序達到預期的效果,避免沒有必要的錯誤出現。
原始碼位置
- SpringBoot配套原始碼地址:訪問碼雲檢視原始碼、訪問GitHub檢視原始碼
- SpringCloud配套原始碼地址(
本章原始碼在這
):訪問碼雲檢視原始碼
有問題要問?
如果你有技術相關的問題想要諮詢
恆宇少年
,請去部落格首頁左側導航欄,點選知識星球
微信掃碼加入我的星球。
與恆宇少年面對面
如果你喜歡
恆宇少年
的相關文章,那麼就去微信公眾號(恆宇少年
)關注我吧!!!
當然你也可以去 SpringCloud碼雲原始碼 專案底部掃描微信公眾號二維碼關注我,感謝閱讀!!!
學習目錄推薦
- SpringCloud相關係列文章訪問:www.jianshu.com/p/64e4aaada…
- SpringBoot相關係列文章請訪問:www.jianshu.com/p/9a08417e4…
- QueryDSL相關係列文章請訪問:www.jianshu.com/p/99a5ec5c3…
- SpringDataJPA相關係列文章請訪問:www.jianshu.com/p/615ed9c1f…
開源資訊
這段時間一直在編寫開源的相關框架,致力於公司使用的框架升級以及開源計劃,將公司使用到的工具
以及外掛
進行升級重構並且開源。
- 程式碼生成器(Code-Builder)
code-builder
程式碼生成器根據你提供的模板檔案(目前支援freemarker
)自動生成實體類,可以很大很有效的提高開發效率。
Gitee地址
:gitee.com/hengboy/cod…
Github地址
:github.com/hengyuboy/c… - 持久化框架(MyBatis-Enhance)
mybatis-enhance
是一個對mybatis
框架的增強封裝,提供一系列的內部方法來完成單表資料的操作,多表資料提供DSL
方式進行操作。
Gitee地址
:gitee.com/hengboy/myb…
Github地址
:github.com/hengyuboy/m… - 自動分頁外掛
MyBatis-Pageable
是一款自動化分頁的外掛,基於MyBatis
內部的外掛Interceptor
攔截器編寫完成,攔截Executor.query
的兩個過載方法計算出分頁的資訊以及根據配置的資料庫Dialect
自動執行不同的查詢語句完成總數量的統計。
Gitee地址
:gitee.com/hengboy/myb…