SpringCloud組件 & 源碼剖析:Eureka服務註冊方式流程全面分析

SpringCloud組件:Eureka服務註冊是採用主機名仍是IP地址?文章中咱們講到了服務註冊的幾種註冊方式,那麼這幾種註冊方式的源碼是怎麼實現的呢?咱們帶着這一個疑問來閱讀本章內容可以讓你更深刻了解這塊的知識點!!!git

本章目標

分析每一種服務註冊方式源碼執行流程。github

構建項目

本章以分析源碼爲主,因此不去新建立項目來說解相關內容,咱們使用SpringCloud組件:Eureka服務註冊是採用主機名仍是IP地址?源碼做爲註冊服務SpringCloud組件:搭建Eureka服務註冊中心源碼做爲服務註冊中心,仍是按照以前的運行流程:spring

  1. 啓動服務註冊中心
  2. 啓動本章服務項目
  3. 查看服務列表,服務註冊方式

配置信息獲取執行流程

在開始講解本章註冊方式以前,咱們須要瞭解總體的配置信息獲取的流程信息,這樣才能夠分析指定的註冊方式執行流程。數據庫

第一步:實例化EurekaInstanceConfigBean配置實體

在項目啓動時因爲依賴spring-cloud-starter-netflix-eureka-client內經過配置spring.factories文件來讓項目啓動時自動加載並實例化org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration配置類,EurekaClientAutoConfiguration內會自動實例化EurekaInstanceConfigBean而且自動綁定eureka.instance開頭的配置信息(具體爲何會自動映射能夠去了解下@ConfigurationProperties註解做用),部分源碼以下所示:bash

......
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容器內容,BeanIdeurekaInstanceConfigBean,也就是方法的名稱。 在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狀態的網卡信息,若是沒有查詢到網卡信息,則使用默認的HostNameIpAddress配置信息,源碼以下所示:mybatis

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;
}
複製代碼

默認的HostNameIpAddress屬性配置信息在InetUtilsProperties配置實體類內,若是不進行設置則直接使用默認值,若是你想更換默認值,那麼你能夠在application.yml配置文件內經過設置spring.cloud.inetutils.defaultHostnamespring.cloud.inetutils.defaultIpAddress進行修改默認值,源碼以下所示:app

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.hostnamethis.ipAddress因此在這裏設置後會直接生效做爲返回的配置值。

總結

咱們經過源碼進行分析服務註冊方式執行流程,這樣在之後進行配置eureka.instance.hostnameeureka.instance.prefer.ip-addresseureka.instance.ip-address三個配置信息時就能夠根據優先級順序達到預期的效果,避免沒有必要的錯誤出現。

源碼位置

有問題要問?

若是你有技術相關的問題想要諮詢恆宇少年,請去博客首頁左側導航欄,點擊知識星球微信掃碼加入個人星球。

與恆宇少年面對面

若是你喜歡恆宇少年的相關文章,那麼就去微信公衆號(恆宇少年)關注我吧!!! 固然你也能夠去 SpringCloud碼雲源碼 項目底部掃描微信公衆號二維碼關注我,感謝閱讀!!!

學習目錄推薦

開源信息

這段時間一直在編寫開源的相關框架,致力於公司使用的框架升級以及開源計劃,將公司使用到的工具以及插件進行升級重構而且開源。

  • 代碼生成器(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…
相關文章
相關標籤/搜索