問題起因
“只開一個監(jiān)視器的數(shù)據(jù)落庫侠草,怎么會導致網絡帶寬打滿?這才多少數(shù)據(jù)犁嗅,快查边涕!”客戶說
信息收集
首先咨詢了在客戶現(xiàn)場的支持人員確定了客戶現(xiàn)場部署架構,雙機房四個監(jiān)控節(jié)點褂微,其中只有一個出現(xiàn)問題功蜓,出現(xiàn)問題的節(jié)點的網絡帶寬比較小,只有20M
從客戶現(xiàn)場監(jiān)控系統(tǒng)對網絡的監(jiān)控圖上來看蕊梧,發(fā)送確實到達了20M霞赫,且持續(xù)不下。
理智告訴我不可能肥矢,一個監(jiān)視器的傳輸?shù)絀NFLUXDB的數(shù)據(jù)最多不會超過1M端衰,事出反常必有妖叠洗,讓我們開始這次的“捉妖記”。
排查過程
確定被打滿的具體網卡
本例中因為有SEE的監(jiān)控旅东,可以直接從頁面上獲取到問題網卡em1灭抑。如果沒有頁面支持也可以使用sar命令來監(jiān)控網卡,得到哪塊網卡流量大一些抵代,從而進行下面的邏輯腾节。得到的結論是一致的,我們關注em1這塊網卡即可荤牍。
監(jiān)控em1網卡流量
通過監(jiān)控圖可以發(fā)現(xiàn)是流出數(shù)據(jù)打滿案腺,所以我們需要確定em1這個網卡的具體流量流向是哪里。我們可以使用iftop命令對流量進行實時監(jiān)控
iftop -i em1 -P
-i參數(shù)可以指定具體監(jiān)測的網卡康吵,-P 可以顯示端口號劈榨。
通過上圖可以發(fā)現(xiàn),這20M的寫入數(shù)據(jù)基本上都是寫到172.1.9.14這臺機器的18086接口對應的服務上晦嵌,而這個端口對應的服務就是時序數(shù)據(jù)庫influxdb同辣,所以可以判斷問題是在監(jiān)控節(jié)點往influxdb寫入數(shù)據(jù)出的問題。
確定流量內容
要確定這20M的數(shù)據(jù)是什么惭载,最好的方式就是網絡抓包了旱函。在有問題的監(jiān)控節(jié)點上執(zhí)行如下命令,抓取流向172.1.9.14機器18086端口的流量描滔,具體參數(shù)含義可以從網絡上搜索到棒妨。
tcpdump -i em1 -s 0 -nnA 'dst host 172.1.9.14 and port 18086'
將抓到的內容提取出來對內容進行分析,乍一看沒看出端倪伴挚,都是業(yè)務數(shù)據(jù)靶衍。但是瀏覽過程中發(fā)現(xiàn)好像有些數(shù)據(jù)上面見過,就搜索了一下茎芋,發(fā)現(xiàn)確實存在相同的數(shù)據(jù)颅眶,且多達9處
代碼跟蹤
tcpdump的結果和事實不符。正常情況下田弥,當采集節(jié)點往influxdb寫入數(shù)據(jù)之后涛酗,會清理掉內存中的數(shù)據(jù),防止下次重復插入偷厦,但是現(xiàn)在發(fā)現(xiàn)了很多重復的數(shù)據(jù)商叹,那一定是代碼邏輯出現(xiàn)了問題,于是找到寫入influxdb的代碼進行review只泼,偽代碼如下:
public void writePoints{
????while(true){ ??????????????
???????List<Point> logPoints= logMapPoints.get();
???????try{
???????????infludb.write(logPoints);
???????????logPoints.clear(); ??????
???????}cache(Exception e){
???????}
????}
}
代碼邏輯如下:外層通過一個死循環(huán)剖笙,保證可以一直獲取到Point的數(shù)據(jù),隨后調用Influxdb的API進行寫入请唱,寫入成功后清理數(shù)據(jù)點弥咪。如果調用influxdb寫入數(shù)據(jù)沒問題還好过蹂,一旦發(fā)生異常,是不會清理數(shù)據(jù)點聚至,而是繼續(xù)循環(huán)調用酷勺。結合我們tcpdump拿到的數(shù)據(jù)來講也是能對上的:裝有相同數(shù)據(jù)的請求不只發(fā)送了一次,因為短時間內的多次重復調用導致寫入數(shù)據(jù)量達到網卡閾值觸發(fā)報警扳躬。如果我們推斷是正確的話脆诉,那么調用influxdb.write方法應該是需要報錯的。
問題驗證
沒有打印日志贷币,我們怎么才能證明呢击胜,我想到了arthas。因為想要看到和influxdb交互的最原始的信息役纹,所以監(jiān)控了Okhttp的相關方法潜的,我們需要的是返回值returnObj,另外我們系統(tǒng)有很多和influxdb的交互請求字管,比如ping探活這種,為了更清晰的獲取我們需要的數(shù)據(jù)信不,所以使用了ognl表達式對結果進行了過濾嘲叔,我們只需要write方法的返回值即可。arthas具體參數(shù)含義可以從網絡上搜索到抽活,不再詳述硫戈。
watch retrofit2.OkHttpCall execute 'returnObj' 'returnObj.rawResponse.request.url.url.path=="/write"' -x 2 -n 10
最終拿到了我們需要的結果:果然是寫數(shù)據(jù)時報錯了,報錯的原因是influxdb數(shù)據(jù)庫自我保護下硕,設定每個標簽對應的不同的值的數(shù)量最大為100000丁逝,超過這個值就寫入失敗,經過處理后功能恢復梭姓。
總結
本次事件是因為采集節(jié)點采集的部分數(shù)據(jù)超過influxdb設定的標簽閾值而導致的寫入失敗烙丛,而后又引起了死循環(huán)調用將此錯誤數(shù)據(jù)重復投遞導致帶寬打滿驰凛。留給我們的啟示是做好服務之間的隔離,不要因為第三方服務的不可用導致自身服務出現(xiàn)故障,同時在日志打印和死循環(huán)邏輯的處理上有進一步優(yōu)化的空間狠毯。