Eureka服務註冊過程詳解之IpAddress
閱讀本文你將瞭解
- 微服務註冊到Eureka Server上的粗粒度過程
eureka.instance.prefer-ip-address = true
時,發生的一些事- 深度理解
eureka.instance.ip-address
和eureka.instance.prefer-ip-address = true
。 - 雜談
注:本篇較長、枯燥、燒腦,並且涉及的範圍相對廣,建議選擇一個舒服的姿勢閱讀。
分析,eureka.instance.prefer-ip-address
- 本節解釋為什麼配置eureka.instance.prefer-ip-address = true時,註冊到Eureka Server上的是IP,以及是什麼IP
老套路,定位問題從配置開始。
(1) 我們通過eureka.instance.prefer-ip-address
配置項,可以找到原始碼
1 | org.springframework.cloud.netflix.eureka.EurekaInstanceConfigBean.preferIpAddress |
(2) preferIpAddress被哪裡呼叫,可以找到
1 | org.springframework.cloud.netflix.eureka.EurekaInstanceConfigBean.getHostName(boolean) |
程式碼如下:
12345678 | public String getHostName |
從這裡我們可以知道,為什麼配置eureka.instance.prefer-ip-address = true
就可以將IP註冊到Eureka Server上,而如果不配置就是機器的主機名。
我們看到以上程式碼有個hostInfo,這是在哪裡例項化的呢?
(3) hostInfo在
1 | org.springframework.cloud.netflix.eureka.EurekaInstanceConfigBean |
的構造方法中例項化!我們來閱讀該類的構造方法:
123456 | public EurekaInstanceConfigBean(InetUtils inetUtils) { this.inetUtils = inetUtils; this.hostInfo = this.inetUtils.findFirstNonLoopbackHostInfo(); this.ipAddress = this.hostInfo.getIpAddress(); this.hostname = this.hostInfo.getHostname();} |
從中 可以看到,hostInfo是呼叫了
1 | this.inetUtils.findFirstNonLoopbackHostInfo(); |
從中可以看到,原來hostInfo是呼叫了
1 | org.springframework.cloud.commons.util.InetUtils.findFirstNonLoopbackHostInfo() |
(4) 閱讀
1 | org.springframework.cloud.commons.util.InetUtils.findFirstNonLoopbackHostInfo() |
可以看到以下程式碼:
12345678910 | 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;} |
我們再看一下該類的
1 | org.springframework.cloud.commons.util.InetUtils.findFirstNonLoopbackAddress() |
方法:
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051 | 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() && !ignoreAddress(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;} |
至此,終於找到了獲得IP的詳細方法,原來只需要配置eureka.instance.prefer-ip-address = true,Spring就會自動為我們獲取第一個非迴環IP地址(這只是簡單的說法,事實上這段程式碼有些容錯的處理)。程式碼雖然長,但是很清晰。不做贅述。
eureka.instance.ip-address和eureka.instance.prefer-ip-address = true同時設定,會用自動獲取的ip還是手動設定的?
上文是討論設定eureka.instance.prefer-ip-address = true
,但沒有指定eureka.instance.ip-address
的情況。那麼如果兩者都被指定了,Spring會怎麼處理呢?是使用eureka.instance.ip-address手動設定的IP,還是用上面自動獲取的IP呢?
答案是聽eureka.instance.ip-address的。
原因是:在
1 | org.springframework.cloud.netflix.eureka.EurekaInstanceConfigBean.setIpAddress(String) |
中:
1234 | public void setIpAddress(String ipAddress) { this.ipAddress = ipAddress; this.hostInfo.override = true;} |
這邊設定了this.hostInfo.override,因此會導致getHostName方法不會進if語句,直接返回this.ipAddress。
12345678 | 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;} |
B.T.W
回到上文的
1 | org.springframework.cloud.commons.util.InetUtils.findFirstNonLoopbackAddress() |
方法,上面有很多ignoreXXX的程式碼。那麼,如何配置想要忽略的網絡卡或者IP地址呢?
答案非常簡單,詳見Spring Cloud官方文件:
當然了,這些配置的本意並不是用來註冊到Eureka上的,而是用作其他用途,只不過如果沒有設定eureka.instance.ip-address時,這個IP就是註冊到Eureka Server上的IP。
我們可以在應用的/env端點看到Spring為我們挑選的IP:
1234 | springCloudClientHostInfo: { spring.cloud.client.hostname: "itmuch", spring.cloud.client.ipAddress: "192.168.0.59"}, |