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