[SpringCloud-Eureka] zookeeper和Eureka使用IP註冊
阿新 • • 發佈:2018-12-28
一.註冊IP而不是服務名稱
1.1 問題 : 在使用Eureka和Zookeeper作為註冊中心的時候,均是預設註冊主機名稱,但是很多時候主機名稱解析會有問題,通過配置使用IP註冊:
Eureka:
eureka.instance.prefer-ip-address = true
Zookeeper:
zookeeper中的配置很隱蔽,查了很久,配置形式如下: spring.cloud.zookeeper.discovery.instance-host=${spring.cloud.client.ipAddress} 這裡右邊可以寫成IP,但是寫成IP不好,比如使用配置中心多例項共享一份配置就有問題,因此寫成這樣,會 動態獲取例項執行的主機的ip。配置後,就可以將IP註冊到zookeeper,其他服務使用Feign呼叫就不會有問題了。
二.Eureka註冊IP解讀
2.1 配置註冊IP
通過eureka.instance.prefer-ip-address = true配置,將微服務的IP註冊到Eureka,而不是主機名稱 程式碼解析如下: 在org.springframework.cloud.netflix.eureka.EurekaInstanceConfigBean類的getHostName方法中: @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; } 這裡可以看到最後返回時通過preferIpAddress配置項來決定返回的是IP還是主機名稱,如下所示,預設是false, 也就是預設註冊的是this.hostname主機名稱: /** * Flag to say that, when guessing a hostname, the IP address of the server should be * used in prference to the hostname reported by the OS. */ private boolean preferIpAddress = false;
- 總言之:eureka.instance.prefer-ip-address = true通過該配置項可以往Eureka註冊IP而不是主機名(預設是主機名稱);
2.2 配置自定義IP
配置項spring.cloud.client.ipAddress可指定一個IP,如果指定了IP同時開啟eureka.instance.prefer-ip-address=true 的話,那麼會註冊哪一個資訊呢?我們跟進if (refresh && !this.hostInfo.override) 這個判斷條件,refresh如果為false則 直接走下面的return返回不會再去執行兩個get方法(可以理解為不去重新獲取,即不refresh重新整理),那麼條件取決於 HostInfo的override屬性,跟進:根據setIpAddress方法我們看到,當設定了eureka.instance.ipAddress時,override 會設定為true,,那麼在執行上面的邏輯的時候,就不會再執行hostInfo的getIpAddress和getHostname方法了,因此 就會直接返回this.ipAddress,而這個ipAddress就是spring.cloud.client.ipAddress指定的。 public void setIpAddress(String ipAddress) { this.ipAddress = ipAddress; this.hostInfo.override = true; }
- 總言之:配置了eureka.instance.prefer-ip-address = true和spring.cloud.client.ipAddress的情況下,會註冊spring.cloud.client.ipAddress指定的IP,而不會通過findFirstNonLoopbackHostInfo方法去尋找本機的非迴環地址;
3.findFirstNonLoopbackAddress方法
下面是簡單改造過的findFirstNonLoopbackAddress方法,通過該方法可以獲取本機的非迴環IP地址
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
for (Enumeration<InetAddress> addrs = ifc
.getInetAddresses(); addrs.hasMoreElements();) {
InetAddress address = addrs.nextElement();
if (address instanceof Inet4Address
&& !address.isLoopbackAddress()
) {
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;
}