您好,登錄后才能下訂單哦!
本文轉載自公眾號kiritomoe,不僅僅學習結果,更多的是學習優秀的人是如何思考。
我曾經寫過一篇和本文標題類似的文章《研究優雅停機時的一點思考》,上文和本文都有一個共同點:網卡地址注冊和優雅停機都是一個很小的知識點,但是背后牽扯到的知識點卻是龐大的體系,我在寫這類文章前基本也和大多數讀者一樣,處于“知道有這么個東西,但不了解細節”的階段,但一旦深挖,會感受到其中的奇妙,并有機會接觸到很多平時不太關注的知識點。
另外,我還想介紹一個叫做”元閱讀“的技巧,可能這個詞是我自己造的,也有人稱之為”超視角閱讀“。其內涵指的是,普通讀者從我的文章中學到的是某個知識點,而元閱讀者從我的文章中可能會額外關注,我是如何掌握某個知識點的,在一個知識點的學習過程中我關注了哪些知識點相關的點,又是如何將他們聯系在一起,最終形成一個體系的。這篇文章就是一個典型的例子,我會對一些點進行發散,大家可以嘗試著跟我一起來思考”網卡地址注冊“這個問題。
可能相當一部分人還不知道我這篇文章到底要講什么,我說個場景,大家應該就明晰了。在分布式服務調用過程中以 Dubbo 為例,服務提供者往往需要將自身的 IP 地址上報給注冊中心,供消費者去發現。在大多數情況下 Dubbo 都可以正常工作,但你如果留意過 Dubbo 的 github issue,其實有不少人反饋:Dubbo Provider 注冊了錯誤的 IP。如果你能立刻聯想到:多網卡、內外網地址共存、VPN、虛擬網卡等關鍵詞,那我建議你一定要繼續將本文看下去,因為我也想到了這些,它們都是本文所要探討的東西!那么“如何選擇合適的網卡地址”呢?Dubbo 現有的邏輯到底算不算完備?是否有改進措施?我們不急著回答它,而是帶著這些問題一起來進行研究,相信到文末,其中答案,各位看官自有評說。
Dubbo 獲取網卡地址的邏輯在各個版本中也是千回百轉,走過彎路,也做過優化,我們用最新的 2.7.2-SNAPSHOT 版本來介紹,在看以下源碼時,大家可以懷著質疑的心態去閱讀,在 dubbo github 的 master 分支可以獲取源碼。獲取 localhost 的邏輯位于 org.apache.dubbo.common.utils.NetUtils#getLocalAddress0()
之中
private static InetAddress getLocalAddress0() {
InetAddress localAddress = null;
// 首先嘗試獲取 /etc/hosts 中 hostname 對應的 IP
localAddress = InetAddress.getLocalHost();
Optional<InetAddress> addressOp = toValidAddress(localAddress);
if (addressOp.isPresent()) {
return addressOp.get();
}
// 沒有找到適合注冊的 IP,則開始輪詢網卡
Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
if (null == interfaces) {
return localAddress;
}
while (interfaces.hasMoreElements()) {
NetworkInterface network = interfaces.nextElement();
Enumeration<InetAddress> addresses = network.getInetAddresses();
while (addresses.hasMoreElements()) {
// 返回第一個匹配的適合注冊的 IP
Optional<InetAddress> addressOp = toValidAddress(addresses.nextElement());
if (addressOp.isPresent()) {
return addressOp.get();
}
}
}
return localAddress;
}
Dubbo 這段獲取 localhost 的邏輯大致分成了兩步
先去 /etc/hosts 文件中找 hostname 對應的 IP 地址,找到則返回;找不到則轉 2
輪詢網卡,尋找合適的 IP 地址,找到則返回;找不到返回 null,再 getLocalAddress0 外側還有一段邏輯,如果返回 null,則注冊 127.0.0.1 這個本地回環地址
首先強調下,這段邏輯并沒有太大的問題,先別急著挑刺,讓我們來分析下其中的一些細節,并進行驗證。
Dubbo 首先選取的是 hostname 對應的 IP,在源碼中對應的 InetAddress.getLocalHost();
在 *nix
系統實際部署 Dubbo 應用時,可以首先使用 hostname
命令獲取主機名
xujingfengdeMacBook-Pro:~ xujingfeng$ hostnamexujingfengdeMacBook-Pro.local
緊接著在 /etc/hosts
配置 IP 映射,為了驗證 Dubbo 的機制,我們隨意為 hostname 配置一個 IP 地址
127.0.0.1 localhost1.2.3.4 xujingfengdeMacBook-Pro.local
接著調用 NetUtils.getLocalAddress0()
進行驗證,控制臺打印如下:
xujingfengdeMacBook-Pro.local/1.2.3.4
在 toValidAddress 邏輯中,Dubbo 存在以下邏輯判定一個 IP 地址是否有效
private static Optional<InetAddress> toValidAddress(InetAddress address) { if (address instanceof Inet6Address) { Inet6Address v6Address = (Inet6Address) address; if (isValidV6Address(v6Address)) { return Optional.ofNullable(normalizeV6Address(v6Address)); } } if (isValidV4Address(address)) { return Optional.of(address); } return Optional.empty();}
依次校驗其符合 Ipv6 或者 Ipv4 的 IP 規范,對于 Ipv6 的地址,見如下代碼:
static boolean isValidV6Address(Inet6Address address) { boolean preferIpv6 = Boolean.getBoolean("java.net.preferIPv6Addresses"); if (!preferIpv6) { return false; } try { return address.isReachable(100); } catch (IOException e) { // ignore } return false;}
首先獲取 java.net.preferIPv6Addresses
參數,其默認值為 false,鑒于大多數應用并沒有使用 Ipv6 地址作為理想的注冊 IP,這問題不大,緊接著通過 isReachable 判斷網卡的連通性。例如一些網卡可能是 VPN/虛擬網卡的地址,如果沒有配置路由表,往往無法連通,可以將之過濾。
對于 Ipv4 的地址,見如下代碼:
static boolean isValidV4Address(InetAddress address) { if (address == null || address.isLoopbackAddress()) { return false; } String name = address.getHostAddress(); boolean result = (name != null && IP_PATTERN.matcher(name).matches() && !Constants.ANYHOST_VALUE.equals(name) && !Constants.LOCALHOST_VALUE.equals(name)); return result;}
對比 Ipv6 的判斷,這里我們已經發現前后不對稱的情況了
Ipv4 相比 Ipv6 的邏輯多了 Ipv4 格式的正則校驗、本地回環地址校驗、ANYHOST 校驗
Ipv4 相比 Ipv6 的邏輯少了網卡連通性的校驗
大家都知道,Ipv4 將 127.0.0.1 定為本地回環地址, Ipv6 也存在回環地址:0:0:0:0:0:0:0:1 或者表示為 ::1。改進建議也很明顯,我們放到文末統一總結。
如果上述地址獲取為 null 則進入輪詢網卡的邏輯(例如 hosts 未指定 hostname 的映射或者 hostname 配置成了 127.0.0.1 之類的地址便會導致獲取到空的網卡地址),輪詢網卡對應的源碼是 NetworkInterface.getNetworkInterfaces()
,這里面涉及的知識點就比較多了,支撐起了我寫這篇文章的素材,Dubbo 的邏輯并不復雜,進行簡單的校驗,返回第一個可用的 IP 即可。
性子急的讀者可能忍不住了,多網卡!合適的網卡可能不止一個,Dubbo 怎么應對呢?按道理說,我們也替 Dubbo 說句公道話,客官要不你自己指定下?我們首先對于多網卡的場景達成一致看法,咱們才能繼續把這篇文章完成下去:我們只能盡可能過濾那些“不對”的網卡。Dubbo 看樣子對所有網卡是一視同仁了,我們是不是可以嘗試優化一下其中的邏輯呢?
許多開源的服務治理框架在 stackoverflow 或者其 issue 中,注冊錯 IP 相關的問題都十分高頻,大多數都是輪詢網卡出了問題。既然事情發展到這兒,勢必需要了解一些網絡、網卡的知識,我們才能過濾掉那些明顯不適合 RPC 服務注冊的 IP 地址了。
我并沒有想要讓大家對后續的內容望而卻步,特地選擇了這個大家最熟悉的 Linux 命令!對于那些吐槽:“天吶,都 2019 年了,你怎么還在用 net-tools/ifconfig,iproute2/ip 了解一下”的言論,請大家視而不見。無論你使用的是 mac,還是 linux,都可以使用它去 CRUD 你的網卡配置。
啟動關閉指定網卡:
ifconfig eth0 upifconfig eth0 down
ifconfig eth0 up
為啟動網卡 eth0, ifconfig eth0 down
為關閉網卡 eth0。ssh 登陸 linux 服務器操作的用戶要小心執行這個操作了,千萬不要蠢哭自己。不然你下一步就需要去 google:“禁用 eth0 網卡后如何遠程連接 Linux 服務器” 了。
為網卡配置和刪除IPv6地址:
ifconfig eth0 add 33ffe:3240:800:1005::2/64 #為網卡eth0配置IPv6地址ifconfig eth0 del 33ffe:3240:800:1005::2/64 #為網卡eth0刪除IPv6地址
用 ifconfig 修改 MAC 地址:
ifconfig eth0 hw ether 00:AA:BB:CC:dd:EE
配置 IP 地址:
[root@localhost ~]# ifconfig eth0 192.168.2.10[root@localhost ~]# ifconfig eth0 192.168.2.10 netmask 255.255.255.0[root@localhost ~]# ifconfig eth0 192.168.2.10 netmask 255.255.255.0 broadcast 192.168.2.255
啟用和關閉arp協議:
ifconfig eth0 arp #開啟網卡eth0 的arp協議ifconfig eth0 -arp #關閉網卡eth0 的arp協議
設置最大傳輸單元:
ifconfig eth0 mtu 1500 #設置能通過的最大數據包大小為 1500 bytes
在一臺 ubuntu 上執行 ifconfig-a
ubuntu@VM-30-130-ubuntu:~$ ifconfig -a
eth0 Link encap:Ethernet HWaddr 52:54:00:a9:5f:ae
inet addr:10.154.30.130 Bcast:10.154.63.255 Mask:255.255.192.0
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:149673 errors:0 dropped:0 overruns:0 frame:0
TX packets:152271 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:15205083 (15.2 MB) TX bytes:21386362 (21.3 MB)
lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
UP LOOPBACK RUNNING MTU:65536 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
docker0 Link encap:Ethernet HWaddr 02:42:58:45:c1:15
inet addr:172.17.0.1 Bcast:172.17.255.255 Mask:255.255.0.0
UP BROADCAST MULTICAST MTU:1500 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
tun0 Link encap:UNSPEC HWaddr 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00
UP POINTOPOINT NOARP MULTICAST MTU:1500 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:100
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
為了防止黑客對我的 Linux 發起攻擊,我還是偷偷對 IP 做了一點“改造”,請不要為難一個趁著打折+組團購買廉價云服務器的小伙子。對于部門網卡的詳細解讀:
eth0 表示第一塊網卡, 其中 HWaddr 表示網卡的物理地址,可以看到目前這個網卡的物理地址(MAC 地址)是 02:42:38:52:70:54
inet addr 用來表示網卡的 IP 地址,此網卡的 IP 地址是 10.154.30.130,廣播地址, Bcast: 172.18.255.255,掩碼地址 Mask:255.255.0.0
lo 是表示主機的回環地址,這個一般是用來測試一個網絡程序,但又不想讓局域網或外網的用戶能夠查看,只能在此臺主機上運行和查看所用的網絡接口。比如把 HTTPD 服務器的指定到回壞地址,在瀏覽器輸入 127.0.0.1 就能看到你所架 WEB 網站了。但只是你能看得到,局域網的其它主機或用戶無從知曉。
第一行:連接類型:Ethernet(以太網)HWaddr(硬件mac地址)
第二行:網卡的IP地址、子網、掩碼
第三行:UP(代表網卡開啟狀態)RUNNING(代表網卡的網線被接上)MULTICAST(支持組播)MTU:1500(最大傳輸單元):1500字節(ipconfig 不加 -a 則無法看到 DOWN 的網卡)
第四、五行:接收、發送數據包情況統計
第七行:接收、發送數據字節數統計信息。
緊接著的兩個網卡 docker0,tun0 是怎么出來的呢?我在我的 ubuntu 上裝了 docker 和 openvpn。這兩個東西應該是日常干擾我們做服務注冊時的罪魁禍首了,當然,也有可能存在 eth2 這樣的第二塊網卡。ifconfig -a 看到的東西就對應了 JDK 的 api : NetworkInterface.getNetworkInterfaces()
。我們簡單做個總結,大致有三個干擾因素
以 docker 網橋為首的虛擬網卡地址,畢竟這東西這么火,怎么也得單獨列出來吧?
以 TUN/TAP 為代表的虛擬網卡地址,多為 VPN 場景
以 eth2 為代表的多網卡場景,有錢就可以裝多網卡了!
我們后續的篇幅將針對這些場景做分別的介紹,力求讓大家沒吃過豬肉,起碼看下豬怎么跑的。
熟悉 docker 的朋友應該知道 docker 會默認創建一個 docker0 的網橋,供容器實例連接。如果嫌默認的網橋不夠直觀,我們可以使用 bridge 模式自定義創建一個新的網橋:
ubuntu@VM-30-130-ubuntu:~$ docker network create kirito-bridgea38696dbbe58aa916894c674052c4aa6ab32266dcf6d8111fb794b8a344aa0d9ubuntu@VM-30-130-ubuntu:~$ ifconfig -abr-a38696dbbe58 Link encap:Ethernet HWaddr 02:42:6e:aa:fd:0c inet addr:172.19.0.1 Bcast:172.19.255.255 Mask:255.255.0.0 UP BROADCAST MULTICAST MTU:1500 Metric:1 RX packets:0 errors:0 dropped:0 overruns:0 frame:0 TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:0 RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
使用 docker network 指令創建網橋之后,自動創建了對應的網卡,我只給出了 ifconfig-a
的增量返回部分,可以看出多了一個 br-a38696dbbe58 的網卡。
我有意區分了“網橋”和“網卡”,可以使用 bridge-utils/brctl 來查看網橋信息:
ubuntu@VM-30-130-ubuntu:~$ sudo brctl showbridge name bridge id STP enabled interfacesbr-a38696dbbe58 8000.02426eaafd0c nodocker0 8000.02425845c215 no
網橋是一個虛擬設備,這個設備只有 brctl show 能看到,網橋創建之后,會自動創建一個同名的網卡,并將這個網卡加入網橋。
平時我們所說的虛擬網卡、虛擬機,大致都跟 TUN/TAP 有關。我的讀者大多數是 Java 從業者,相信我下面的內容并沒有太超綱,不要被陌生的名詞唬住。對于被唬住的讀者,也可以直接跳過 5.1~5.3,直接看 5.4 的實戰。
上圖中的 eth0 表示我們主機已有的真實的網卡接口 (interface)。
網卡接口 eth0 所代表的真實網卡通過網線(wire)和外部網絡相連,該物理網卡收到的數據包會經由接口 eth0 傳遞給內核的網絡協議棧(Network Stack)。然后協議棧對這些數據包進行進一步的處理。
對于一些錯誤的數據包,協議棧可以選擇丟棄;對于不屬于本機的數據包,協議棧可以選擇轉發;而對于確實是傳遞給本機的數據包,而且該數據包確實被上層的應用所需要,協議棧會通過 Socket API 告知上層正在等待的應用程序。
我們知道,普通的網卡是通過網線來收發數據包的話,而 TUN 設備比較特殊,它通過一個文件收發數據包。
如上圖所示,tunX 和上面的 eth0 在邏輯上面是等價的, tunX 也代表了一個網絡接口,雖然這個接口是系統通過軟件所模擬出來的.
網卡接口 tunX 所代表的虛擬網卡通過文件 /dev/tunX 與我們的應用程序(App) 相連,應用程序每次使用 write 之類的系統調用將數據寫入該文件,這些數據會以網絡層數據包的形式,通過該虛擬網卡,經由網絡接口 tunX 傳遞給網絡協議棧,同時該應用程序也可以通過 read 之類的系統調用,經由文件 /dev/tunX 讀取到協議棧向 tunX 傳遞的所有數據包。
此外,協議棧可以像操縱普通網卡一樣來操縱 tunX 所代表的虛擬網卡。比如說,給 tunX 設定 IP 地址,設置路由,總之,在協議棧看來,tunX 所代表的網卡和其他普通的網卡區別不大,當然,硬要說區別,那還是有的,那就是 tunX 設備不存在 MAC 地址,這個很好理解,tunX 只模擬到了網絡層,要 MAC *地址沒有任何意義。當然,如果是 *tapX 的話,在協議棧的眼中,tapX 和真實網卡沒有任何區別。
是不是有些懵了?我是誰,為什么我要在這篇文章里面學習 TUN!因為我們常用的 VPN 基本就是基于 TUN/TAP 搭建的,如果我們使用 TUN 設備搭建一個基于 UDP 的 VPN ,那么整個處理過程可能是這幅樣子:
TAP 設備與 TUN 設備工作方式完全相同,區別在于:
TUN 設備是一個三層設備,它只模擬到了 IP 層,即網絡層 我們可以通過 /dev/tunX 文件收發 IP 層數據包,它無法與物理網卡做 bridge,但是可以通過三層交換(如 ip_forward)與物理網卡連通。可以使用 ifconfig
之類的命令給該設備設定 IP 地址。
TAP 設備是一個二層設備,它比 TUN 更加深入,通過 /dev/tapX 文件可以收發 MAC 層數據包,即數據鏈路層,擁有 MAC 層功能,可以與物理網卡做 bridge,支持 MAC 層廣播。同樣的,我們也可以通過 ifconfig
之類的命令給該設備設定 IP 地址,你如果愿意,我們可以給它設定 MAC 地址。
關于文章中出現的二層,三層,我這里說明一下,第一層是物理層,第二層是數據鏈路層,第三層是網絡層,第四層是傳輸層。
openvpn 是 Linux 上一款開源的 vpn 工具,我們通過它來復現出影響我們做網卡選擇的場景。
安裝 openvpn
sudo apt-get install openvpn
安裝一個 TUN 設備:
ubuntu@VM-30-130-ubuntu:~$ sudo openvpn --mktun --dev tun0Mon Apr 29 22:23:31 2019 TUN/TAP device tun0 openedMon Apr 29 22:23:31 2019 Persist state set to: ON
安裝一個 TAP 設備:
ubuntu@VM-30-130-ubuntu:~$ sudo openvpn --mktun --dev tap0Mon Apr 29 22:24:36 2019 TUN/TAP device tap0 openedMon Apr 29 22:24:36 2019 Persist state set to: ON
執行 ifconfig-a
查看網卡,只給出增量的部分:
tap0 Link encap:Ethernet HWaddr 7a:a2:a8:f1:6b:df
BROADCAST MULTICAST MTU:1500 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:100
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
tun0 Link encap:UNSPEC HWaddr 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00
inet addr:10.154.30.131 P-t-P:10.154.30.131 Mask:255.255.255.255
UP POINTOPOINT NOARP MULTICAST MTU:1500 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:100
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
這樣就解釋了文章一開始為什么會有 tun0 這樣的網卡了。這里讀者可能會有疑惑,使用 ifconfig 不是也可以創建 tap 和 tun 網卡嗎?當然啦,openvpn 是一個 vpn 工具,只能創建名為 tunX/tapX 的網卡,其遵守著一定的規范,ifconfig 可以隨意創建,但沒人認那些隨意創建的網卡。
這個沒有太多好說的,有多張真實的網卡,從普哥那兒搞到如上的 IP 信息。
雖然 ifconfig 等指令是 *nux 通用的,但是其展示信息,網卡相關的屬性和命名都有較大的差異。例如這是我 MAC 下執行 ifconfig-a
的返回:
xujingfengdeMacBook-Pro:dubbo-in-action xujingfeng$ ifconfig -alo0: flags=8049<UP,LOOPBACK,RUNNING,MULTICAST> mtu 16384 options=1203<RXCSUM,TXCSUM,TXSTATUS,SW_TIMESTAMP> inet 127.0.0.1 netmask 0xff000000 inet6 ::1 prefixlen 128 inet6 fe80::1%lo0 prefixlen 64 scopeid 0x1 nd6 options=201<PERFORMNUD,DAD>gif0: flags=8010<POINTOPOINT,MULTICAST> mtu 1280stf0: flags=0<> mtu 1280XHC0: flags=0<> mtu 0XHC20: flags=0<> mtu 0en0: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500 ether 88:e9:fe:88:a0:76 inet6 fe80::1cab:f689:60d1:bacb%en0 prefixlen 64 secured scopeid 0x6 inet 30.130.11.242 netmask 0xffffff80 broadcast 30.130.11.255 nd6 options=201<PERFORMNUD,DAD> media: autoselect status: activep2p0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> mtu 2304 ether 0a:e9:fe:88:a0:76 media: autoselect status: inactiveawdl0: flags=8943<UP,BROADCAST,RUNNING,PROMISC,SIMPLEX,MULTICAST> mtu 1484 ether 66:d2:8c:8c:dd:85 inet6 fe80::64d2:8cff:fe8c:dd85%awdl0 prefixlen 64 scopeid 0x8 nd6 options=201<PERFORMNUD,DAD> media: autoselect status: activeen1: flags=8963<UP,BROADCAST,SMART,RUNNING,PROMISC,SIMPLEX,MULTICAST> mtu 1500 options=60<TSO4,TSO6> ether aa:00:d0:13:0e:01 media: autoselect <full-duplex> status: inactiveen2: flags=8963<UP,BROADCAST,SMART,RUNNING,PROMISC,SIMPLEX,MULTICAST> mtu 1500 options=60<TSO4,TSO6> ether aa:00:d0:13:0e:00 media: autoselect <full-duplex> status: inactivebridge0: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500 options=63<RXCSUM,TXCSUM,TSO4,TSO6> ether aa:00:d0:13:0e:01 Configuration: id 0:0:0:0:0:0 priority 0 hellotime 0 fwddelay 0 maxage 0 holdcnt 0 proto stp maxaddr 100 timeout 1200 root id 0:0:0:0:0:0 priority 0 ifcost 0 port 0 ipfilter disabled flags 0x2 member: en1 flags=3<LEARNING,DISCOVER> ifmaxaddr 0 port 9 priority 0 path cost 0 member: en2 flags=3<LEARNING,DISCOVER> ifmaxaddr 0 port 10 priority 0 path cost 0 nd6 options=201<PERFORMNUD,DAD> media: <unknown type> status: inactiveutun0: flags=8051<UP,POINTOPOINT,RUNNING,MULTICAST> mtu 2000 inet6 fe80::3fe0:3e8b:384:9968%utun0 prefixlen 64 scopeid 0xc nd6 options=201<PERFORMNUD,DAD>utun1: flags=8051<UP,POINTOPOINT,RUNNING,MULTICAST> mtu 1380 inet6 fe80::7894:3abc:5abd:457d%utun1 prefixlen 64 scopeid 0xd nd6 options=201<PERFORMNUD,DAD>
內容很多,我挑幾點差異簡述下:
內容展示形式不一樣,沒有 Linux 下的接收、發送數據字節數等統計信息
真實網卡的命名不一樣:eth0 -> en0
虛擬網卡的命名格式不一樣:tun/tap -> utun
對于這些常見網卡命名的解讀,我摘抄一部分來自 stackoverflow 的回答:
In arbitrary order of my familarity / widespread relevance:
lo0
is loopback.
en0
at one point "ethernet", now is WiFi (and I have no idea what extraen1
oren2
are used for).
fw0
is the FireWire network interface.
stf0
is an IPv6 to IPv4 tunnel interface to support the transition from IPv4 to the IPv6 standard.
gif0
is a more generic tunneling interface [46]-to-[46].
awdl0
is Apple Wireless Direct Link
p2p0
is related to AWDL features. Either as an old version, or virtual interface with different semantics thanawdl
.
the "Network" panel in System Preferences to see what network devices "exist" or "can exist" with current configuration.
many VPNs will add additional devices, often "utun#" or "utap#" following TUN/TAP (L3/L2)virtual networking devices.
use
netstat-nr
to see how traffic is currently routed via network devices according to destination.interface naming conventions started in BSD were retained in OS X / macOS, and now there also additions.
我們進行了以上探索,算是對網卡有了一點了解了。回過頭來看看 Dubbo 的獲取網卡的邏輯,是否可以做出改進呢?
Dubbo Action 1:
保持 Ipv4 和 Ipv6 的一致性校驗。為 Ipv4 增加連通性校驗;為 Ipv6 增加 LoopBack 和 ANYHOST 等校驗。
Dubbo Action 2:
NetworkInterface network = interfaces.nextElement();if (network.isLoopback() || network.isVirtual() || !network.isUp()) { continue;}
JDK 提供了以上的 API,我們可以利用起來,過濾一部分一定不正確的網卡。
Dubbo Action 3:
我們本文花了較多的篇幅介紹了 docker 和 TUN/TAP 兩種場景導致的虛擬網卡的問題,算是較為常見的一個影響因素,雖然他們的命名具有固定性,如 docker0、tunX、tapX,但我覺得通過網卡名稱的判斷方式去過濾注冊 IP 有一些 hack,所以不建議 dubbo contributor 提出相應的 pr 去增加這些 hack 判斷,盡管可能會對判斷有所幫助。
對于真實多網卡、內外網 IP 共存的場景,不能僅僅是框架側在做努力,用戶也需要做一些事,就像愛情一樣,我可以主動一點,但你也得反饋,才能發展出故事。
Dubbo User Action 1:
可以配置 /etc/hosts
文件,將 hostname 對應的 IP 顯示配置進去。
Dubbo User Action 2:
可以使用啟動參數去顯示指定注冊的 IP:
-DDUBBO_IP_TO_REGISTRY=1.2.3.4
也可以指定 Dubbo 服務綁定在哪塊網卡上:
-DDUBBO_IP_TO_BIND=1.2.3.4
TUN/TAP 設備淺析
what-are-en0-en1-p2p-and-so-on-that-are-displayed-after-executing-ifconfig
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。