你所需要掌握的問題排查知識
1. 說之前
由於業務應用 bug(本身或引入第三方庫)、環境原因、硬體問題等原因,線上服務出現故障 / 問題幾乎不可避免。例如,常見的現象包括請求超時、使用者明顯感受到系統發生卡頓等等。
作為一個合格的研發人員(技術人員),不僅要能寫得一手好程式碼,掌握如何排查問題技巧也是研發人進階必須掌握的實戰技能。這裡提到的排查問題不僅僅是在Coding的過程中Debug,還包括測試階段、線上釋出階段問題的排查。特別是在生產環境中,一般是沒辦法或很難進行Debug操作的。 而通過掌握服務線上問題排查思路並能夠熟練排查問題常用工具 / 命令 / 平臺來獲取執行時的具體情況,這些執行時資訊包括但不限於執行日誌、異常堆疊、堆使用情況、GC情況、JVM引數情況、執行緒情況等。
排查出問題並找到根本原因加以解決,其實是一件很成就感的事情。曾經有人問過我:“你是怎麼想到問題出現在xxx的?又是怎麼確認根本原因是xxx的?”,我只能輕描淡寫的回答:“靠經驗”,其實這裡說的“靠經驗”是很模糊的,一直以來大家可能都覺得排查問題要靠經驗,但是又說不出具體通過什麼樣的經驗排查出了問題。而本質上排查定位線上問題是具有一定技巧或者說是經驗規律的,排查者如果對業務系統瞭解得越深入,那麼相對來說定位也會容易一些。排查問題的關鍵是什麼? 一句話總結:給一個系統定位排查問題的時候,知識、經驗是關鍵,資料是依據,工具是運用知識處理資料的手段! 在此,我將結合自身經歷、總結,說關於“問題排查”的方法論,希望能與您產生更多的共鳴。
注:由於針對不同技術問題,所用到的排查工具,命令千差萬別,所以本文將只介紹思路,不涉及具體排查命令的介紹。
2. 有哪些常見問題
那我們經常說遇到這樣那樣的問題,那到底有哪些問題,問題又集中在哪些方面?對於不同技術框架、語言族所可能引發的問題也會存在很大的差異,但基本的套路排查思路都還是一致的,以Java為例。
所有 Java 服務的線上問題從系統表象來看歸結起來總共有四方面:CPU、記憶體、磁碟、網路。例如 CPU 使用率峰值突然飈高、記憶體溢位 (洩露)、磁碟滿了、網路流量異常、FullGC 等等問題。
基於這些現象我們可以將線上問題分成兩大類:系統異常、業務服務異常 。
1. 系統異常
常見的系統異常現象包括: CPU 佔用率過高、CPU 上下文切換頻率次數較高、磁碟滿了、磁碟 I/O 過於頻繁、網路流量異常 (連線數過多)、系統可用記憶體長期處於較低值 (導致 oom killer) 等等。
這些問題如果是在Linux系統下可以通過 top(cpu)、free(記憶體)、df(磁碟)、dstat(網路流量)、pstack、vmstat、strace(底層系統呼叫) 等工具獲取系統異常現象資料。
注:CPU 是系統重要的監控指標,能夠分析系統的整體執行狀況。對CPU的分析或監控指標,一般包括執行佇列、CPU 使用率和上下文切換等,記憶體是排查線上問題的重要參考依據,記憶體問題很多時候是引起 CPU 使用率較高的主要因素。 而經常遇到記憶體佔用飆高它的原因可能有很多。最常見的就是記憶體洩露。可以得到堆dump檔案後,進行物件分析。如果有大量物件在持續被引用,並沒有被釋放掉,那就產生了記憶體洩露,就要結合程式碼,把不用的物件釋放掉。
2. 業務服務異常
常見的業務服務異常現象包括: PV 量過高、服務呼叫耗時異常、執行緒死鎖、多執行緒併發問題、頻繁進行 Full GC、異常安全攻擊掃描等。
頻繁的 GC 將導致應用吞吐量下降、響應時間增加,甚至導致服務不可用。
3.問題排查方法論
一、排查問題猶如破案
排查線上問題猶如警察破案一樣,是一個不停分析線索,推理的過程,但在準備排查問題之前,我們應該明白三個認知:
- 系統出現異常是正常的
時至今日計算機系統已經變得異常複雜,一次使用者請求可能要經過傳送請求,DNS解析,運營商網路,負載均衡,伺服器,虛擬機器(容器),視業務邏輯的複雜程度可能還要呼叫元件,快取,儲存和資料庫等。每個環節都可能出現問題,有的元件又是分散式的,大大增加的排查問題的難度,所以出現問題後不要慌,保持好的心態。
- 首要任務是恢復系統
飛機在發生緊急情況下,飛行員的首要任務是保持飛機飛行,相比保證乘客與飛機安全著陸,故障定位和排除是次要目標”,所以恢復線上系統是首要任務,而不是立馬找到它發生的原因。
- 真相永遠只有一個
計算機是一門科學,而且計算機的世界裡都是由0或1組成,在這個世界裡只有是或否,沒有中間地帶,所以在計算機世界凡事都有根本原因,沒有偶然發生,一切都是必然。正如墨菲定律所提到的“如果事情有變壞的可能,不管這種可能性有多小,它總會發生!”
二、瞭解案情,評估大小
先評估出這個問題的影響範圍,是全網,某些地區,還是某條鏈路不可用的問題,還是很多業務線都出現問題,評估出案情的大小,到底是普通的民事案件,還是刑事案件。
三、理清線索,整理分析
理清手頭已得到的資訊或線索,比如監控上有網路報警,有使用者反饋無法訪問,有開發人員反饋伺服器有問題,同時間段有做變更等等,儘量不要漏掉這些看似無關緊要的線索,把這些線索先整理下來,後面一併分析。
推理的過程,就是根據已知線索,通過合理的想象、推斷得出一個唯一的結果。線索是整個推理過程的起點,線索給出的好有不好、是否有錯誤,直接會影響推理的質量,因此是最基礎、也是最重要的一環。線索的梳理,最常犯錯誤就是資訊不足,主觀臆斷。
其中整理分析過程中,很重要的一點:儘可能搞清楚問題的前因後果!
不要一下子就扎到伺服器前面,你需要先搞明白對這臺伺服器有多少已知的情況,還有故障的具體情況。不然你很可能就是在無的放矢。
必須搞清楚的問題有:
-
故障的表現是什麼?無響應?報錯?
-
故障是什麼時候發現的?
-
故障是否可重現?
-
有沒有出現的規律(比如每小時出現一次)
-
最後一次對整個平臺進行更新的內容是什麼(程式碼、伺服器等)?
-
故障影響的特定使用者群是什麼樣的(已登入的, 退出的, 某個地域的…)?
-
基礎架構(物理的、邏輯的)的文件是否能找到?
-
是否有監控平臺可用? (比如Munin、Zabbix、 Nagios、 New Relic… 什麼都可以)
-
是否有日誌可以檢視?. (比如Loggly、Airbrake、 Graylog…)
另外也可以進一步從應用層、資料庫層、網路層進行檢查:
應用層:
-
應用最近是否有上線?
-
軟硬體環境最近是否有變更?
-
應用日誌是否有異常?
-
重啟是否有效?
資料庫:
-
資料庫系統級配置最近是否有變更?
-
telnet埠是否暢通?
-
tnsping監聽是否正常(連通性、延遲)
-
資料庫是否有異常的等待?
-
遠端、本地SQL執行是否正常?
網路:
-
網路最近是否有變更?
-
ping是否正常?
-
traceroute -l 是否正常?
-
網路是否有丟包、延遲?
儘可能地獲取到更多的已知有效資訊,彙總資訊並從多條排查線去進行分析,這裡推薦思路有:
-
通過變更記錄來諮詢相關人員,大量問題其實都是上線、變更等引起的,所以排查下同時期有過業務相關的變更操作人員,往往就可以把很多問題排除在這道線上了。
-
通過日誌、資料等,把一些已知問題篩選出來。
-
通過影響人群、問題點等資訊嘗試找出復現方法。一般來說,能有方法穩定復現的問題,就比較容易排查到了。
-
到這一步的問題,基本上都屬於一些疑難雜症了,就沒有一些特別通用的方法了。需要開闊思路,找找規律,將平時沒關注到的技術、業務點再瞭解的更細緻,更深入一些,或者求助於團隊的幫助一起來解決。
需要注意一點:通過分析日誌時,業務日誌除了要關注系統異常與業務異常之外,還要關注服務執行耗時情況,耗時過長的服務呼叫如果沒有熔斷等機制,很容易導致應用效能下降或服務不可用,服務不可用很容易導致雪崩。如果沒辦法直接從日誌中發現異常,那就只能看看應用到底在幹嘛了(可分析應用在異常時期的執行緒記憶體堆疊資訊)。
這一步原則很簡單:找出系統正在執行“什麼”,詢問系統“為什麼”執行這些操作,以及系統的資源都被用在了“哪裡”可以幫助你瞭解系統為什麼出錯。
四、擴大你的資訊量
主動擴大資訊的接收面,比如問詢一下開發或其它相關同學,今天有沒有做線上改動,網路組有無重大調整。從中獲取到有價值的資訊點,對於排查問題至關重要。檢視監控,細看某個監控項的變化,追蹤日誌和除錯資訊都是擴大資訊量的手段。
拓展知識面,閒暇時間多些瞭解相關聯絡統,比如架構,部署,邏輯等。一旦故障發生,討論中也可提供你解決辦法的思路,舉一反三,推進問題的排查與解決。
收集問題及環境資訊,需要收集的資訊可能有:
-
問題的已知首次發生時間
-
問題反饋人員所處的環境,例如省、市、ip、ISP、瀏覽器、手機型號、app 版本等
-
問題是全員的還是部分的。
-
問題發生在哪些伺服器上。
-
同期相關的日誌、資料資訊。
-
同時期的上線、配置變更、運維操作、基礎服務變更等記錄。
-
同時期基礎服務提供商的變更、故障公告等。
五、分析證詞,甄別對錯
如果是外部提出的問題,比如業務投訴,使用者反饋等資訊,有時候是可信的,有時候人卻是不可信的,舉個例子之前有開發反饋效果有問題,有些廣告位bias異常,有些正常,讓我們幫查查系統的問題,但是最後是程式碼呼叫一處動態配置造成的。有些時候反饋的資訊,是經過描述者過濾加工過的資訊,他的排查和分析有可能把你帶偏了,在收集資訊同時需要以審視、懷疑的態度,分析每個人的證詞。
六、看清問題本質
“當你聽到蹄子聲響時,應該先想到馬,而不是斑馬”,看到一件現象或一件事情,要看實質而不只是表面的東西,聽到馬蹄聲時候猜是什麼馬,是什麼人的馬,是來幹什麼的而不是猜它是斑馬還是白馬還是黑馬。排查問題也一樣切忌先入為主,有時候你覺得極其簡單,看似非常不可能發生的事情,可能就是原因,不要輕易的排除掉某項原因。例比:之前遇到有個mysql連線異常的問題,查了很久,做了很多調優都沒有解決,最後發現是網絡卡跑滿了。
七、確定方向,開展定位
排查步驟,可以先“從大到小”,先看比如運營商網路,機房狀態等比較巨集觀的地方是否有問題,逐一排除,逐步縮小問題範圍。再“從上到下”,先從現象發生的頂端呼叫鏈逐一排查,逐步向下深入。
但也並不是所有問題都從大到小從上到下,巨集觀問題只有達到一定量級才會引發”質變”,從而引起的注意,在通往質變過程中,你的業務可能已經收到某中影響而表現的很明確,此時需要微觀分析,然後再逐漸到巨集觀來診斷。
八、卷宗記錄,破案歸檔
問題排查解決後,養成事後總結的習慣。好記性不如爛筆頭,然而在一片混亂問題分析當中,心平氣和地記錄下問題與判斷確實有點不切實際。但即使如此,我們仍然可以在事情結束後為保留一份分析資料,總結並記錄處理過程中的執行步驟以及解決途徑,則能幫助自己和團隊積累寶貴的處理經驗。
- 對於個人
一次問題的定位解決往往伴隨著個人的成長,我們不要放棄這樣的機會。在追查過程中瞭解的知識點是比較零碎的,不繫統。事後就需要大家將這些點整體串起來,並且以點帶面,將知識點變更知識面。
- 對於團隊
-
是對這次問題的反思,我們應該在流程、程式碼、工具或者哪些方面做出調整,可以更好的避免同類型問題的出現。
-
是對追查過程的總結,在問題定位的過程中,我們缺少哪些幫助及工具的支援,能否更好的提升排查問題的效率,然後相關人員是否對過程結果存在異議。
4. 長期改進建議
吃一塹長一智出了問題並不可怕,怕的是我們從問題中學不到什麼,怕的是類似的問題重現,提高問題定位的效率,有哪些值得去做,比如:
1、建立長效錯誤碼機制,使用具統計、可視意義的數字來簡短描述錯誤含義和範疇,正所謂濃縮就是精華,這一點在錯誤碼屢試不爽。
2、正常程式中打錯誤日誌主要是為了更好地排查問題和解決問題,提供重要線索和指導。但是在實際中打的錯誤日誌內容和格式變化多樣,錯誤提示上可能殘缺不全、沒有相關背景、不明其義,使得排查解決問題成為非常不方便或者耗時的操作。而實際上只要開發稍加用心,也需就會減少排查問題的很多無用功。如何編寫有效的錯誤日誌,建立日誌標準,也是非常有利於問題分析的。
3、定位問題避免二次損害,當某個看似難以捉摸的難題出現時,本能可能是重啟,儘快讓系統恢復正常。雖然這樣的方式經常能夠解決問題而且起效神速,但同時也很可能把情況推向令人難以置信的惡化深淵。問題排查手段包括重新啟動不穩定系統、嘗試自動記錄資料庫、檔案系統修復等等,這些方式往往確實能搞定難題並讓系統重回生產軌道,但同時也沒準導致資料恢復努力付之東流,毀掉確定問題根本原因的機會甚至大大延長關鍵性系統的停機時間。保留現場也非常重要,跟破案現場要要求現場勘察、樣本採集、排查、鎖定如出一轍,對於難以重現問題,儘量創造條件保留了可以用於故障重現的資料或現場。線上環境複雜多變,雖然這一點並不能馬上解決問題起到直接作用,但堅持這種處理思路,為開發和測試創造條件,降低因難以重現的疑難故障的掛起率,最終有助於業務的長期穩定。
4、建立集中的資料可視平臺,不至於遇到問題才開始著手分析,若是對業務沒有足夠的瞭解又沒有資料依賴,就很可能在解決問題時雪上加霜。
5、建立沙箱影子系統,模擬複雜多變的現網環境,規避線上影響,重現或壓測問題,如tcpcopy、dubbocopy等。
6、搭建開源的日誌可視方案,協助我們去解決最後”一公里”的問題,常見如ELK、Log.io等。
7、善其事必先利其器,常見系統排查工具perf、iptraf、netperf、tcpdump、gdb、pstack、jstack、strace,top、iotop、tsar等。
8、 在升級版本或者替換或修改檔案時,一定要做好備份,要保證隨時可以還原。
9、 程式在使用多執行緒時,儘可能的減少多執行緒競爭鎖,可以將資料分段,各個執行緒分別讀取。
10、 儘量不要線上程中做大量耗時的網路操作,如查詢資料庫(可以的話在一開始就將資料從從 DB 中查出準備好)。
11、 建議對執行緒取一個有意義的名稱,這樣對於在排查問題時很有必要,如當拿到程式執行緒堆疊資訊時,可直接根據執行緒名在日誌中找到相關的堆疊。
12、生產環境進行問題排查時一定要保證不要影響正常的業務執行。