記一次APP和DB間流量異常問題的排查

情景
經過zabbix監控發現有一個應用和數據庫之間存在不定時的流量異常(也不頻繁),具體爲應用server的入向流量和數據庫server的出向流量會有短期(一般在一分鐘左右)的激增,甚至快達到千兆網卡的傳輸上限。
###分析過程
經過症狀,幾乎能夠判定是因爲某些sql語句須要返回大量數據致使。但此次問題的分析結果確是很不順利(其中有我前期方法不當的緣由)。mysql

初次分析:由於在那以前作了一個針對於故障/異常分析的日誌分析系統(詳見),而且基於此完成了兩次異常uri的定位爲題。因此慣性思惟仍是用這套東西來對流量異常時間段內出現的請求,按照出現次數或響應時間或者響應大小排序,觀察。折騰了一通,然這個方法就不是分析這個問題最直接的方法,天然分析不出啥結果。
在這期間也想到了tcpdump抓包,可是有兩個問題:由於異常時間不固定;不合適的方法會致使抓包產生的文件體積很是大。當時也沒相處好方法來抓包,因此就沒再深究這個想法。sql

再次分析:仔細想仍是抓包纔是最直接的方法。因而寫了一個腳本,在應用server上每隔5s計算一下以前5s的入向流量的帶寬,根據應用和數據庫之間正常的流量設置一個閾值,超過次閾值則觸發抓包動做。此次是能看到一部分異常的流量了,可是因爲這個腳本的機制是先判斷流量再觸發抓包,因此先天缺陷是沒法抓到引起流量異常的sql的。數據庫

最終分析,大腦通過一晚上的後臺運行,終於想到了一個「完美」的抓包方法,大體思路以下:網絡

開始抓包,經過tcpdump的-w 參數將結果寫到文件。而後在死循環裏每隔5s計算一下前5s的平均流量,跟閾值相比:
若是小於閾值,則kill掉以前的tcpdump進行,從新開始一個tcpdump,抓包數據還寫到先前的文件中(會覆蓋掉以前內容,解決了抓包文件不斷變大的問題);
若是大於閾值,則繼續抓包,下一次循環再檢測流量,若下一個5s週期流量降到了閾值以內,則kill掉tcpdump進程,同時將抓包文件重命名(這個是能夠用來分析的有效抓包數據),而後再開啓新的tcpdump重複以前的動做。tcp

來看一下具體腳本(這個腳本是整個過程的關鍵)ide

#!/bin/sh
#by ljk 2017/03/18
#對於不按期出現的網絡流量異常進行合理抓包

file=/proc/net/dev
i=0    #用來標記是否出現了流量異常以及異常持續了幾個檢測週期
cd /usr/local/src/mysql-sniffer/logs/query/
alias capture='tcpdump -nnvv port 3306 -w tcpdump.txt &>/dev/null'

(capture) &    #放到後臺執行,不至於阻塞主進程的邏輯

while true;do
    RX_bytes=`cat $file|grep eth0|sed 's/^ *//g'|awk -F'[ :]+' '{print $2}'`
    sleep 10    #間隔10s
    RX_bytes_later=`cat $file|grep eth0|sed 's/^ *//g'|awk -F'[ :]+' '{print $2}'`
    speed_RX=`echo "scale=0;($RX_bytes_later - $RX_bytes)*8/1024/1024/10"|bc`

    tcpdump_pid=`ps -ef|grep tcpdump|grep -v grep|awk '{print $2}'`

    if [ $speed_RX -lt 15 ];then    #個人閾值是15Mb
        kill -9 $tcpdump_pid
        if [ $i -gt 0 ];then
            mv tcpdump.txt "$i"_tcpdump_`date  +"%F_%H:%M:%S"`
        fi

        (capture) &
        i=0
    else
        i=$(($i+1))
    fi
done

把該腳本放到後臺運行,而後我很輕鬆的抓取到了一次異常週期內的數據包,而後經過wireshark進行分析,也很快的找到了問題sql,不出所料,就是由於對用戶輸入的驗證不夠嚴謹,致使where條件裏出現了空字串進而須要返回將近全表的數據(真坑)。.net

相關文章
相關標籤/搜索