Java系統線上生產問題排查一把梭 1 環境 2 監控 分析定位問題的最佳實踐 迷茫時的最佳實踐 總結

1 環境

1.1 Dev

可以隨意使用任何熟悉的工具排查。只要問題能重現,排查就不會太難,最多就是把程序調試到各種框架源碼,所以這也是爲何面試都會問源碼,不求都看過,但要有思路知道如何去看能解決問題。

1.2 Test

比開發環境少了debug,不過也可使用jvisualvm或Arthas,附加到遠程JVM進程。

還有測試環境是允許造數據來模擬我們需要的場景的哦,因此這時遇到問題記得主動溝通測試人員造數據讓bug更容易復現。

1.3 Prd

該環境下開發人員的權限最低,所以排查問題時障礙很大:

  • 無法使用調試工具從遠程附加進程
  • 快速恢復爲先,即使在結婚,也得趕緊修復線上問題。而且生產環境流量大、網絡權限嚴格、調用鏈路複雜,因此更容易出問題,也是出問題最多的環境。

2 監控

生產環境出現問題時,因爲要儘快恢復應用,就不可能保留完整現場用於排查和測試。因此,是否有充足的信息(日誌、監控和快照)可以瞭解歷史、還原bug 場景。
最常用的就是 ELK 的日誌了,注意:

  • 確保錯誤、異常信息可被完整記錄到文件日誌
  • 確保生產上程序的日誌級別是INFO以上
    記錄日誌要使用合理的日誌優先級,DEBUG用於開發調試、INFO用於重要流程信息、WARN用於需要關注的問題、ERROR用於阻斷流程的錯誤

生產環境需開發配合運維才能做好完備監控:

主機維度

對CPU、內存、磁盤、網絡等資源做監控。如果應用部署在虛擬機或k8s集羣,那麼除了對物理機做基礎資源監控外,同樣還要對虛擬機或Pod監控。監控層數取決於應用的部署方案,有一層OS就要做一層監控。

網絡維度

監控專線帶寬、交換機基本情況、網絡延遲

所有的中間件和存儲都要做好監控

不僅僅是監控進程對CPU、內存、磁盤IO、網絡使用的基本指標,更重要的是監控組件內部的一些重要指標。比如最常用的Prometheus,就提供了大量exporter對接各種中間件和存儲系統

應用層面

需監控JVM進程的類加載、內存、GC、線程等常見指標(比如使用Micrometer來做應用監控),此外還要確保能夠收集、保存應用日誌、GC日誌

我們再來看看快照。這裏的“快照”是指,應用進程在某一時刻的快照。通常情況下,我們會爲生產環境的Java應用設置-XX:+HeapDumpOnOutOfMemoryError和-XX:HeapDumpPath=…這2個JVM參數,用於在出現OOM時保留堆快照。這個課程中,我們也多次使用MAT工具來分析堆快照。

分析定位問題的最佳實踐

定位問題,首先要定位問題出在哪個層次:Java應用程序自身問題還是外部因素導致。

  • 可以先查看程序是否有異常,異常信息一般比較具體,可以馬上定位到大概的問題方向
  • 如果是一些資源消耗型的問題可能不會有異常,我們可以通過指標監控配合顯性問題點來定位。

一般問題原因可歸類如下:

程序發佈後 Bug

回滾,再慢慢通過版本差異分析根因。

外部因素

比如主機、中間件或DB問題。
這種按主機層面問題、中間件或存儲(統稱組件)的問題分爲:

主機層

可使用工具排查:

CPU相關

使用top、vmstat、pidstat、ps

內存相關

使用free、top、ps、vmstat、cachestat、sar

IO相關

使用lsof、iostat、pidstat、sar、iotop、df、du

網絡相關

使用ifconfig、ip、nslookup、dig、ping、tcpdump、iptables

組件

從如下方面排查:

  • 組件所在主機是否有問題
  • 組件進程基本情況,觀察各種監控指標
  • 組件的日誌輸出,特別是錯誤日誌
  • 進入組件控制檯,使用一些命令查看其運作情況。

系統資源不夠造成系統假死

通常先通過重啓和擴容解決問題,之後再分析,最好能留個快照。

系統資源不夠,一般可能:

CPU使用高

若現場還在,具體分析流程:

  • 在服務器執行top -Hp pid
    查看進程中哪個線程CPU使用高
  • 輸入大寫的P將線程按照 CPU 使用率排序,並把明顯佔用CPU的線程ID轉換爲16進制
  • 在jstack命令輸出的線程棧中搜索這個線程ID,定位出問題的線程當時的調用棧

若無法直接在服務器執行top,可採樣定位:間隔固定時間運行一次jstack,採樣幾次後,對比採樣得出哪些線程始終處於運行狀態,找出問題線程。

若現場沒了,可排除法分析。CPU使用高,一般是由下面的因素引起的:

  • 突發壓力
    可通過應用之前的負載均衡的流量或日誌量確認,諸如Nginx等反向代理都會記錄URL,可依靠代理的Access Log進行細化定位,也可通過監控觀察JVM線程數的情況。壓力問題導致CPU使用高的情況下,如果程序的各資源使用沒有明顯不正常,之後可以通過壓測+Profiler(jvisualvm就有這個功能)進一步定位熱點方法;如果資源使用不正常,比如產生了幾千個線程,就需要考慮調參

  • GC
    可通過JVM監控GC相關指標、GC Log確認。如果確認是GC壓力,那麼內存使用也很可能會不正常,需要按照內存問題分析流程做進步分析。

  • 死循環或不正常處理流程
    可以結合應用日誌分析。一般情況下,應用執行過程中都會產生一些日誌,可以重點關注日誌量異常部分。

內存泄露或OOM

最簡單的就是堆轉儲後使用MAT分析。堆轉儲,包含了堆現場全貌和線程棧信息,一般觀察支配樹圖、直方圖就可以馬上看到佔用大量內存的對象,可以快速定位到內存相關問題
Java進程對內存的使用不僅僅是堆區,還包括線程使用的內存(線程個數*每一個線程的線程棧)和元數據區。每一個內存區都可能產生OOM,可以結合監控觀察線程數、已加載類數量等指標分析
注意看JVM參數的設置是否有明顯不合理的,限制了資源。

IO問題

除非是代碼問題引起的資源不釋放等問題,否則通常都不是由Java進程內部因素引發的。

網絡

一般也是由外部因素引起。對於連通性問題,結合異常信息通常比較容易定位;對於性能或瞬斷問題,可以先嚐試使用ping等工具簡單判斷,如果不行再使用tcpdump或Wireshark。

迷茫時的最佳實踐

偶爾可能分析和定位難題,會迷失自我。如果你也這樣,可參考如下經驗

cause or result?

比如業務執行的很慢,而且線程數增多,那就可能是:

  • 代碼邏輯有問題、依賴的外部服務慢
    使得自己的業務邏輯執行緩慢,在訪問量不變情況下,就需要更多線程處理。比如,10 TPS的併發原先一次請求1s即可完成,10個線程可支撐;現在執行完成需要10s,就需100個線程
  • 請求量增大
    使得線程數增多,應用本身CPU不足,上下文切換問題導致處理變慢

這時就需要多結合監控指標和各服務的入口流量,分析慢是cause or result。

探求規律

如果沒頭緒,那就試試總結規律吧!
比如

  • 有一堆服務器做負載均衡,出問題時可分析監控和日誌看請求是否是均勻分佈的,可能問題都集中在某個機器節點上
  • 應用日誌一般會記錄線程名稱,出問題時可分析日誌是否集中在某類線程
  • 若發現應用開啓大量TCP連接,通過netstat可分析出主要集中連接到哪個服務

探求到了規律,就很容易突破了。

調用拓撲

比如看到Nginx返回502,一般可認爲是下游服務的問題導致網關無法完成請求轉發。
對於下游服務,不能想當然就認爲是我們的Java程序,比如在拓撲上可能Nginx代理的是Kubernetes的Traefik Ingress,鏈路是Nginx->Traefik->應用,如果一味排查Java程序的健康,則始終找不到根因。

有時雖然使用了Feign進行服務調用,出現連接超時也不一定就是服務端問題,有可能是客戶端通過URL調用服務端,並非通過Eureka的服務發現實現的客戶端負載均衡。即客戶端連接的是Nginx代理而非直接連接應用,客戶端連接服務出現的超時,其實是Nginx代理宕機所致。

資源限制

觀察各種監控指標,如果發現曲線慢慢上升然後穩定在一個水平線,一般就是資源達到瓶頸。

觀察網絡帶寬曲線時,如果帶寬上升到120MB左右不動了,很可能就是打滿了1GB的網卡或傳輸帶寬
觀察到數據庫活躍連接數上升到10個不動了,很可能是連接池打滿了

觀察監控一旦看到任何這樣曲線,都要引起重視。

連鎖反應

CPU、內存、IO和網絡相輔相成,一個資源出現瓶頸,很可能同時引起其他資源連鎖反應。

內存泄露後對象無法回收會造成大量Full GC,CPU會大量消耗在GC從而引起CPU使用增加

經常會把數據緩存在內存隊列進行異步IO,網絡或磁盤出現問題時,就很可能會引起內存暴漲。

所以出問題時,要綜合考慮避免誤判

客戶端or服務端or傳輸問題?

比如MySQL訪問慢了,可能:

  • 客戶端原因,連接池不夠導致連接獲取慢、GC停頓、CPU佔滿
  • 傳輸過程問題
    包括光纖可能被挖斷了呀、防火牆、路由表等設置有問題
  • 真的服務端背鍋了

這都需要逐一排查區分。

服務端慢一般可以看到MySQL出慢日誌,傳輸慢一般可以通過ping來簡單定位,排除了這兩個可能,並且僅僅是部分客戶端出現訪問慢的情況,就需要懷疑是客戶端本身的問題。對於第三方系統、服務或存儲訪問出現慢的情況,不能完全假設是服務端的問題。

第七,快照類工具和趨勢類工具需要結合使用。比如,jstat、top、各種監控曲線是趨勢類工具,可以讓我們觀察各個指標的變化情況,定位大概的問題點;而jstack和分析堆快照的MAT是快照類工具,用於詳細分析某一時刻應用程序某一個點的細節。

一般情況下,我們會先使用趨勢類工具來總結規律,再使用快照類工具來分析問題。如果反過來可能就會誤判,因爲快照類工具反映的只是一個瞬間程序的情況,不能僅僅通過分析單一快照得出結論,如果缺少趨勢類工具的幫助,那至少也要提取多個快照來對比。

第八,不要輕易懷疑監控。我曾看過一個空難事故的分析,飛行員在空中發現儀表顯示飛機所有油箱都處於缺油的狀態,他第一時間的懷疑是油表出現故障了,始終不願意相信是真的缺油,結果飛行不久後引擎就斷油熄火了。同樣地,在應用出現問題時,我們會查看各種監控系統,但有些時候我們寧願相信自己的經驗,也不相信監控圖表的顯示。這可能會導致我們完全朝着錯誤的方向來排查問題。

如果你真的懷疑是監控系統有問題,可以看一下這套監控系統對於不出問題的應用顯示是否正常,如果正常那就應該相信監控而不是自己的經驗。

第九,如果因爲監控缺失等原因無法定位到根因的話,相同問題就有再出現的風險,需要做好三項工作:

做好日誌、監控和快照補漏工作,下次遇到問題時可以定位根因;
針對問題的症狀做好實時報警,確保出現問題後可以第一時間發現;
考慮做一套熱備的方案,出現問題後可以第一時間切換到熱備系統快速解決問題,同時又可以保留老系統的現場。

總結

分析問題必須講理

靠猜是猜不出來的,需要提前做好基礎監控的建設。監控的話,需要在基礎運維層、應用層、業務層等多個層次進行。定位問題的時候,我們同樣需要參照多個監控層的指標表現綜合分析。

定位問題要先對原因進行大致分類

比如是內部問題還是外部問題、CPU相關問題還是內存相關問題、僅僅是A接口的問題還是整個應用的問題,然後再去進一步細化探索,一定是從大到小來思考問題;在追查問題遇到瓶頸的時候,我們可以先退出細節,再從大的方面捋一下涉及的點,再重新來看問題。

經驗很重要

遇到重大問題的時候,往往也需要根據直覺來第一時間找到最有可能的點,這裏甚至有運氣成分。我還和你分享了我的九條經驗,建議你在平時解決問題的時候多思考、多總結,提煉出更多自己分析問題的套路和拿手工具。

定位到問題原因後,要做好覆盤回溯。每次故障的解決都是寶貴經驗,覆盤不止是記錄問題,更是爲了架構優化。
覆盤可重點關注如下:

  • 記錄完整的時間線、處理措施、上報流程等信息
  • 分析問題的根本原因
  • 給出短、中、長期改進方案,包括但不限於代碼改動、SOP、流程,並記錄跟蹤每一個方案進行閉環
  • 定期組織團隊回顧過去的故障
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章