Eureka注冊不上或注冊后IP不對(多網(wǎng)卡的坑及解決)
一、問題發(fā)現(xiàn)
使用SpringCloud一套的微服務(wù)項目在開發(fā)測試環(huán)境都再正常不過了,到生產(chǎn)部署的時候啟動服務(wù)就死活無法啟動,去看啟動日志發(fā)現(xiàn),在獲取配置中心配置時連接不到配置中心了,報了一個Host Unreachable
的錯。
按道理來說這個錯很簡單,就是網(wǎng)絡(luò)不通導(dǎo)致的。
但是問題就出現(xiàn)在這兒,我直接ping注冊中心和配置中心的IP是通的,沒有問題。
再仔細一看才發(fā)現(xiàn)事情并不簡單,我們生產(chǎn)環(huán)境開放的是一套10.21.xx.xx的網(wǎng)段IP,但是日志中卻去尋找29.192.xx.xx去了,打開eureka控制臺發(fā)現(xiàn)注冊到注冊中心上的配置中心確實是29.192.xx.xx,并且注冊中心顯示的自己的IP也是29.192.xx.xx。
配置中心啟動沒問題是因為他和注冊中心在一臺機器上,所以用什么樣的ip都無關(guān)緊要,其他服務(wù)想要拉取配置中心所在機器的配置就拉不到了。
問題是我們其他服務(wù)在配置eureka client的時候填寫的eureka server地址確實都是10.21.xx.xx,為什么注冊中心會自動改成29.192.xx.xx。
咨詢了客戶方之后才知道,29.192.xx.xx這個IP是用來監(jiān)控服務(wù)器用的,各服務(wù)器之間都通過這個IP發(fā)送心跳來保持在線狀態(tài),不能做業(yè)務(wù)使用。
二、撥云見日
知道這個IP從哪兒來下一步就要分析解決了,先查看網(wǎng)絡(luò)配置
確實從網(wǎng)卡配置順序上來說eureka client選取了第一塊網(wǎng)卡配置的IP向注冊中心注冊,這就導(dǎo)致了無法連接的問題。
那么問題就指向了eureka client是如何選取網(wǎng)卡IP進行注冊的以及如何能讓eureka client根據(jù)我們的意愿選擇我們想要的IP進行注冊。
去官網(wǎng)查找并沒有找到關(guān)于eureka client是如何選取網(wǎng)卡IP的描述,那么就只能去扒源碼了。
最終找到是在com.netflix.appinfo包下的InstanceInfo類封裝了本機信息,其中就包括了IP地址的獲取方法。
在 Spring Cloud 環(huán)境下,Eureka Client并沒有自己實現(xiàn)探測本機IP的邏輯,而是交給Spring的InetUtils工具類的findFirstNonLoopbackAddress()方法完成的,下邊貼出這個方法的源碼:
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; }
這個方法中通過NetworkInterface接口獲取到網(wǎng)卡的列表信息進行循環(huán)獲取,首先判斷是否啟用(如果網(wǎng)卡禁用再獲取IP自然就沒意義),在啟用狀態(tài)下拿到網(wǎng)卡的索引值(目的是為了獲取網(wǎng)卡的最小索引值),最后還要判斷是否在忽略列表中,如果不在忽略列表才能選用。在這一系列的操作過后如果沒能獲取到最終結(jié)果,那么最后就會調(diào)用jdk的getLocalHost()方法來獲取IP地址并返回。
總體來說,這個工具類會獲取所有網(wǎng)卡,依次進行遍歷,取ip地址合理、索引值最小、已經(jīng)啟動且不在忽略列表的網(wǎng)卡的ip地址作為結(jié)果。如果仍然沒有找到合適的IP, 那么就將InetAddress.getLocalHost()做為最后的fallback方案。
三、開刀治病
? 有了源碼的加持,想要達到我們最終獲取指定IP的目的就條條大路通羅馬了。
1、忽略指定網(wǎng)卡
? 在bootstrap.yml中添加忽略屬性
spring.cloud.inetutils.gnored-interfaces[0]=ens161 # 忽略ens161, 支持正則表達式
注意,不能在application.yml中添加,玩過SpringCloud的應(yīng)該都懂。
2、禁用無關(guān)網(wǎng)卡
? 如上面網(wǎng)卡信息圖即禁用掉ens161和ens256,最終只保留ens224網(wǎng)卡生效,這樣一來獲取到啟用的網(wǎng)卡也就只有一塊了。
查看網(wǎng)卡信息(生產(chǎn)環(huán)境最終沒敢禁用,以下拿我本地環(huán)境測試)
[melonrind@melonrind ~]$ ifconfig
enp0s3: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 10.0.2.15 netmask 255.255.255.0 broadcast 10.0.2.255
inet6 fe80::9ee2:1871:6417:19a9 prefixlen 64 scopeid 0x20<link>
ether 08:00:27:75:08:94 txqueuelen 1000 (Ethernet)
RX packets 44098 bytes 59104330 (56.3 MiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 21246 bytes 1370966 (1.3 MiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0enp0s8: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 192.168.56.200 netmask 255.255.255.0 broadcast 192.168.56.255
inet6 fe80::1c58:28df:b483:fb7a prefixlen 64 scopeid 0x20<link>
ether 08:00:27:f3:69:ec txqueuelen 1000 (Ethernet)
RX packets 333237 bytes 148991996 (142.0 MiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 396257 bytes 138439800 (132.0 MiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
inet 127.0.0.1 netmask 255.0.0.0
inet6 ::1 prefixlen 128 scopeid 0x10<host>
loop txqueuelen 1000 (Local Loopback)
RX packets 224153 bytes 56176690 (53.5 MiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 224153 bytes 56176690 (53.5 MiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
[melonrind@melonrind ~]$ nmcli con sh
NAME UUID TYPE DEVICE
enp0s3 e0a2f8ed-112e-41ed-9f35-89502e325c18 ethernet enp0s3
enp0s8 221018b1-6e99-48fc-8c3d-fe7dee581328 ethernet enp0s8
這里就可以禁用掉enp0s3網(wǎng)卡,保留enp0s8
[root@melonrind ~]# ifdown enp0s3 Device 'enp0s3' successfully disconnected.
啟用可以用如下命令
[root@melonrind ~]# ifup enp0s3
3、配置host
? 當網(wǎng)查遍歷邏輯都沒有找到合適ip時會走JDK的InetAddress.getLocalHost()。該方法會返回當前主機的hostname, 然后會根據(jù)hostname解析出對應(yīng)的ip。
因此如果確認沒有找到合適的IP的情況下,可以配置本機的hostname和/etc/hosts文件,直接將本機的主機名映射到指定IP地址。
4、手工指定實例IP
eureka client在啟動時可以對該eureka client的實例進行配置,因此這里也可以自己指定IP地址。
可以添加如下配置:
# 指定此實例的ip eureka.instance.ip-address=${你指定的ip地址} # 注冊時使用ip而不是主機名 eureka.instance.prefer-ip-address=true
不過該配置需要添加在eureka client配置之上,形如:
eureka: instance: ip-address: 192.168.56.1 prefer-ip-address: true client: register-with-eureka: true fetch-registry: true service-url: defaultZone: http://192.168.56.1:9130/eureka registry-fetch-interval-seconds: 30 eureka-server-connect-timeout-seconds: 5 eureka-server-read-timeout-seconds: 5 filter-only-up-instances: true eureka-connection-idle-timeout-seconds: 30 eureka-server-total-connections: 200 eureka-server-total-connections-per-host: 50
5、服務(wù)啟動時指定IP
在不方便修改配置文件時可以選用此方式(我就是用此方式解決),在服務(wù)啟動時添加參數(shù):
java -jar -Dspring.cloud.inetutils.preferred-networks=192.168.56.1 ...
總結(jié)
至此,該問題得到解決,又是驚心動魄的一天。
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
ThreadPoolExecutor參數(shù)含義及源碼執(zhí)行流程詳解
這篇文章主要為大家介紹了ThreadPoolExecutor參數(shù)含義及源碼執(zhí)行流程詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-11-11Java實現(xiàn)根據(jù)sql動態(tài)查詢并下載數(shù)據(jù)到excel
這篇文章主要為大家詳細介紹了如何使用Java實現(xiàn)根據(jù)sql動態(tài)查詢并下載數(shù)據(jù)到excel的功能,文中的示例代碼講解詳細,有需要的可以參考下2024-04-04Java異常處理操作 Throwable、Exception、Error
這篇文章主要介紹了Java異常處理操作 Throwable、Exception、Error,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-06-06在Java的Spring框架的程序中使用JDBC API操作數(shù)據(jù)庫
這篇文章主要介紹了在Java的Spring框架的程序中使用JDBC API操作數(shù)據(jù)庫的方法,并通過示例展示了其存儲過程以及基本SQL語句的應(yīng)用,需要的朋友可以參考下2015-12-12