您好,登錄后才能下訂單哦!
上周四晚剛回到家,就接到了軟負載同學的電話,說是客戶線上出了故障,我一聽”故障“兩個字,立馬追問是什么情況,經過整理,還原出線上問題的原貌:
客戶使用了 Dubbo,注冊中心使用的是 Nacos,在下午開始不斷有調用報錯,查看日志,發現了 Nacos 心跳請求返回 502
2019-11-15 03:02:41.973 [com.alibaba.nacos.client.naming454] -ERROR [com.alibaba.nacos.naming.beat.sender] request xx.xx.xx.xx failed.
com.alibaba.nacos.api.exception.NacosException: failed to req API: xx.xx.xx.xx:8848/nacos/v1/ns/instance/beat. code:502 msg:
此時還沒有大范圍的報錯。隨后,用戶對部分機器進行了重啟,開始出現大規模的 Nacos 連接不上的報錯,并且調用開始出現大量 no provider 的報錯。
Nacos 出現心跳報錯,一般會有兩種可能:
用戶機器出現問題,如網絡不通
Nacos Server 宕機
但由于是大面積報錯,所以很快定位到是 Nacos Server 本身出了問題:由于磁盤老舊導致 IO 效率急劇下降,Nacos Server 無法響應客戶端的請求,客戶端直接接收到 502 錯誤響應。這個事件本身并不復雜,是一起注冊中心磁盤故障引發的血案,但從這起事件,卻可以窺探到很多高可用的問題,下面來跟大家一起聊聊這當中的細節。
Dubbo 版本:2.7.4
Nacos 版本:1.1.4
復現目標:在本地模擬 Nacos Server 宕機,檢查 Dubbo 的調用是否會受到影響。
復現步驟:
本地啟動 Nacos Server、Provider、Consumer,觸發 Consumer 調用 Provider
kill -9 Nacos Server,模擬 Nacos Server 宕機,觸發 Consumer 調用 Provider
重啟 Consumer,觸發 Consumer 調用 Provider
期望:
3 個步驟均可以調用成功
實際結果:
1、2 調用成功,3 調用失敗
問題成功復現,重啟 Consumer 之后,沒有調用成功,客戶恰好遇到了這個問題。大家可能對這其中的細節還是有一些疑問,我設想了一些疑惑點,來和大家一起進行探討。
我們都知道,一般聊到 Dubbo,有三個角色是必須要聊到的:服務提供者、服務消費者、注冊中心。他們的關系不用我贅述,可以從下面的連通性列表得到一個比較全面的認識:
注冊中心負責服務地址的注冊與查找,相當于目錄服務,服務提供者和消費者只在啟動時與注冊中心交互,注冊中心不轉發請求,壓力較小
服務提供者向注冊中心注冊其提供的服務,此時間不包含網絡開銷
服務消費者向注冊中心獲取服務提供者地址列表,并根據負載算法直接調用提供者,此時間包含網絡開銷
注冊中心,服務提供者,服務消費者三者之間均為長連接
注冊中心通過長連接感知服務提供者的存在,服務提供者宕機,注冊中心將立即推送事件通知消費者
注冊中心宕機,不影響已運行的提供者和消費者,消費者在本地緩存了提供者列表
注冊中心可選的,服務消費者可以直連服務提供者
重點關注倒數第二條,Dubbo 其實在內存中緩存了一份提供者列表,這樣可以方便地在每次調用時,直接從本地內存拿地址做負載均衡,而不避免每次調用都訪問注冊中心。只有當服務提供者節點發生上下線時,才會推送到本地,進行更新。所以,Nacos 宕機后,Dubbo 仍然可以調用成功。
宕機期間,已有的服務提供者節點可能突然下線,但由于注冊中心無法通知給消費者,所以客戶端調用到下線的 IP 就會出現報錯。
對于此類問題,Dubbo 也可以進行兜底
Dubbo 會在連接級別進行心跳檢測,當 channel 本身不可用時,即使沒有注冊中心通知,也會對其進行斷連,并設置定時器,當該連接恢復后,再恢復其可用性
在阿里云商業版的 Dubbo -- EDAS 中,提供了「離群摘除」功能,可以在調用層面即時摘除部分有問題的節點,保證服務的可用性。
Nacos Server 宕機后,Consumer 依舊可以調用成功,這個大家應該都比較清楚。但是為什么期望 Consumer 重啟之后,依舊調用成功,有些人可能就會有疑問了,注冊中心都宕機了,重啟之后一定連不上,理應調用失敗,怎么會期望成功呢?這就要涉及到 Nacos 的本地緩存了。
Nacos 本地緩存的作用:當應用與服務注冊中心發生網絡分區或服務注冊中心完全宕機后,應用進行了重啟操作,內存里沒有數據,此時應用可以通過讀取本地緩存文件的數據來獲取到最后一次訂閱到的內容。
例如在 Dubbo 應用中定義了如下服務:
<dubbo:service interface="com.alibaba.edas.xml.DemoService" group="DUBBO" version="1.0.0" ref="demoService" />
可以在本機的 /home/${user}/nacos/naming/
下看到各個命名空間發布的所有服務的信息,其內容格式如下:
{"metadata":{},"dom":"DEFAULT_GROUP@@providers:com.alibaba.edas.xml.DemoService:1.0.0:DUBBO","cacheMillis":10000,"useSpecifiedURL":false,"hosts":[{"valid":true,"marked":false,"metadata":{"side":"provider","methods":"sayHello","release":"2.7.4","deprecated":"false","dubbo":"2.0.2","pid":"5275","interface":"com.alibaba.edas.xml.DemoService","version":"1.0.0","generic":"false","revision":"1.0.0","path":"com.alibaba.edas.xml.DemoService","protocol":"dubbo","dynamic":"true","category":"providers","anyhost":"true","bean.name":"com.alibaba.edas.xml.DemoService","group":"DUBBO","timestamp":"1575355563302"},"instanceId":"30.5.122.3#20880#DEFAULT#DEFAULT_GROUP@@providers:com.alibaba.edas.xml.DemoService:1.0.0:DUBBO","port":20880,"healthy":true,"ip":"30.5.122.3","clusterName":"DEFAULT","weight":1.0,"ephemeral":true,"serviceName":"DEFAULT_GROUP@@providers:com.alibaba.edas.xml.DemoService:1.0.0:DUBBO","enabled":true}],"name":"DEFAULT_GROUP@@providers:com.alibaba.edas.xml.DemoService:1.0.0:DUBBO","checksum":"69c4eb7e03c03d4b18df129829a486a","lastRefTime":1575355563862,"env":"","clusters":""}
為什么期望重啟后調用成功?因為經過檢查,發現線上出現問題的機器上,緩存文件一切正常。雖然 Nacos Server 宕機了,本地的緩存文件依舊可以作為一個兜底,所以期望調用成功。
緩存文件正常,問題只有可能出現在讀取緩存文件的邏輯上。
可能是 nacos-client 出了問題
可能是 Dubbo 的 nacos-registry 出了問題
一番排查,在 Nacos 研發的協助下,找到了 naocs-client 的一個參數: namingLoadCacheAtStart
,該配置參數控制啟動時是否加載緩存文件,默認值為 false。也就是說,使用 nacos-client,默認是不會加載本地緩存文件的。終于定位到線上問題的原因了:需要手動開啟加載本地緩存,才能讓 Nacos 加載本地緩存文件。
該參數設置為 true 和 false 的利弊:
設置為 true,認為可用性 & 穩定性優先,寧愿接受可能出錯的數據,也不能因為沒有數據導致調用完全出錯
設置為 false,則認為 Server 的可用性較高,更能夠接受沒有數據,也不能接受錯誤的數據
無論是 true 還是 false,都是對一些極端情況的兜底,而不是常態。對于注冊發現場景,設置成 true,可能更合適一點,這樣可以利用 Nacos 的本地緩存文件做一個兜底。
Dubbo 中使用統一 URL 模型進行參數的傳遞,當我們需要在配置文件傳遞注冊中心相關的配置參數時,可以通過鍵值對的形式進行拼接,當我們想要在 Dubbo 中開啟加載注冊中心緩存的開關時,可以如下配置:
<dubbo:registry address="nacos://127.0.0.1:8848?namingLoadCacheAtStart=true"/>
遺憾的是,最新版本的 Dubbo 只傳遞了部分參數給 Nacos Server,即使用戶配置了 namingLoadCacheAtStart
也不會被服務端識別,進而無法加載本地緩存。我在本地修改了 Dubbo 2.7.5-SNAPSHOT,傳遞上述參數后,可以使得 1、2、3 三個階段都調用成功,證明了 namingLoadCacheAtStart
的確可以使得 Dubbo 加載本地緩存文件。該問題將會在 Dubbo 2.7.5 得到修復,屆時 Dubbo 中使用 Nacos 的穩定性將會得到提升。
該線上問題反映出了 Nacos 注冊中心可用性對 Dubbo 應用的影響,以及系統在某個組件宕機時,整體系統需要進行的一些兜底邏輯,不至于因為某個組件導致整個系統的癱瘓。
總結下現有代碼的缺陷以及一些最佳實踐:
Dubbo 傳遞注冊中心參數給 Nacos 時,只能夠識別部分參數,這會導致用戶的部分配置失效,在接下來的版本會進行修復。
nacos-client 加載本地緩存文件的開關等影響到系統穩定性的參數最好設計成 -D 啟動參數,或者環境變量參數,這樣方便發現問題,及時止血。例如此次的事件,有缺陷的 Dubbo 代碼僅僅依賴于參數的傳遞,無法加載本地緩存文件,而如果有 -D 參數,可以強行開始加載緩存,大大降低了問題的影響面。
namingLoadCacheAtStart
是否默認開啟,還需要根據場景具體確定,但 nacos-server 宕機等極端場景下,開啟該參數,可以盡可能地降低問題的影響面。順帶一提,Nacos 本身還提供了一個本地災備文件,與本地緩存文件有一些差異,有興趣的朋友也可以去了解一下。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。