Nacos Client 1.4.1 版本踩坑記錄
本文轉載自微信公眾號「Kirito的技術分享」,作者kiritomoe。轉載本文請聯系Kirito的技術分享公眾號。
問題發現
就在這周,我接到 MSE Nacos 用戶的反饋,說線上 Nacos 不可用,服務都下線了,日志里面也是一堆報錯,我下意識以為線上炸了,趕緊上線排查。本文主要記錄這次問題的排查過程,以及解決方案。
首先看用戶反饋的報錯,日志如下:
并且用戶反饋業務日志也出現了大量的服務地址找不到的報錯,說明 Nacos 服務都下線了。
我立刻查看了服務端的監控,發現用戶的 MSE Nacos 集群并無異常,cpu/內存等指標有下降,并沒有異常行為,排除了服務端異常的可能性。
隨即將視線聚焦在了客戶端。老實說,這個報錯我第一次見,看異常堆棧,字面意思便是域名解析出問題了。這個報錯大概持續了 10 分鐘,立刻讓用戶在業務節點上使用 ping、dig 等工具確認域名解析是否正常,測試發現均無異常。繼續讓用戶 telnet mse-xx.com 8848,發現也能夠 telnet 通。
根據這些現象,大概能得出結論:用戶的機器上出現了短暫的域名解析問題,導致短時間訪問不通 MSE Nacos。但用戶繼續反饋說,一部分重啟以后的機器已經恢復了,但沒有重啟的機器,竟然還會出現調用報錯。不然怎么說重啟大法好呢,但也加深了問題的詭異性。
正當一籌莫展時,另一用戶也找上來了,竟然也是一樣的問題,并且由于第二個用戶還同時使用了 redis,報錯日志中除了出現 nacos 的域名解析問題,還報了 redis 的域名解析報錯。至此,更加堅定了我之前推測,根因肯定是域名解析出現了故障,導致這兩個用戶收到了影響。但問題在于,為什么短暫的域名解析失敗(大概 10 分鐘),會導致持續性的 Nacos 問題呢?并且只有重啟才能恢復。
分析兩個用戶的共性,最終我和同事將可疑點鎖定在了 Nacos 客戶端版本上,對比發現,用戶都是同一個報錯,并且竟然都是 nacos-client 1.4.1 版本。
Nacos 1.4.1 版本引入的 bug
在問題發生時,Nacos 1.x 最新的版本已經是 Nacos 1.4.2 了,將源碼 checkout 到 1.4.1 版本,追蹤堆棧附近的問題,
上述這段代碼是 Nacos 訪問服務端的一段代碼,進入 595 行,一探究竟。
我們成功找到了堆棧中的直接報錯,就是這段 IsIPv4 的判斷觸發。splitIPPortStr 這個方法的主要邏輯是從 Nacos 的連接串篩選出連接地址,主要是為了做默認端口號的判斷,如果用戶沒有攜帶 8848,會默認帶上 8848。
但問題恰恰便是出現在這兒:
InetAddress.getByName(addr) 是一個內置的方法,描述如下:
- Given the name of a host, returns an array of its IP addresses, based on the configured name service on the system.
意思是把一個域名傳給操作系統,返回一串 IP,這不就是域名解析嗎!我當時就很好奇,你說你判斷 IPv4 格式,為啥要這么判斷呢?直接判斷 IPv4 的 pattern 不行嗎?而這段代碼,恰恰是導致問題的兇手之一。
我們看看 1.4.2,已經修復了這個邏輯了,直接改成了正則判斷。
但疑問還是存在的,域名解析短暫失敗了,為啥會導致服務全都下線了,并且解析恢復后,服務依舊沒有上線呢?
繼續追蹤這段代碼,發現 callServer 這段代碼會被 com.alibaba.nacos.client.naming.beat.BeatReactor 持有,用于維持自身和 Nacos 的心跳。
而由于上述域名解析失敗,拋出的異常是 IllegalArgumentException,并沒有被里層方法轉換成 NacosException,從而導致心跳線程沒有 catch 住異常,徹底停止發送心跳了!
這也就成功解釋了,為什么短暫的域名解析失敗,會導致服務全部下線了。(Nacos 是利用心跳維護和 server 端的存活狀態的)
改進建議
修改 isIPv6 和 isIPv4 的判斷方式,改為正則匹配。上文提及,這點已經在 1.4.2 修復了。
心跳線程要保證不被異常中斷下一次心跳的提交。
第二點,也已經被修復了。
總結
nacos-client 1.4.1 存在嚴重的 bug,客戶端與 Nacos Server 如果發生短暫的域名解析問題,會導致心跳永久丟失,進而引發服務全量下線,即使網絡恢復,也不會自動恢復心跳。
域名解析失敗常見于網絡抖動或者 K8s 環境下的 coreDNS 訪問超時等場景,為避免域名解析對 Nacos 造成的重大影響,請務必自查應用代碼中使用的 nacos-client 的版本。
該問題僅存在于 1.4.1 版本,低于此版本不受此問題的影響,使用 1.4.1 的用戶建議升級至 1.4.2 以避免此問題。
使用 SpringCloud/Dubbo 的用戶,需要確認實際框架使用的 nacos-client 版本,可以通過顯式指定 nacos-client 的版本以覆蓋框架默認的版本。其中 Dubbo 用戶要格外小心,Dubbo 的 2.7.11 版本默認使用了 nacos-client 1.4.1,務必顯式指定 nacos-client 的版本到 1.4.2,Dubbo 也將在下個 release 版本替換 Nacos 的默認版本。