線上問題排查手冊

一  線上常見問題定位

 

常見問題 1:CPU 利用率高

 

CPU 使用率是衡量系統繁忙程度的重要指標,一般情況下單純的 CPU 高並沒有問題,它代表系統正在不斷的處理我們的任務,但是如果 CPU 過高,導致任務處理不過來,從而引起 load 高,這個是非常危險需要關注的。 CPU 使用率的安全值沒有一個標準值,取決於你的系統是計算密集型還是 IO 密集型,一般計算密集型應用 CPU 使用率偏高 load 偏低,IO 密集型相反。

 

問題原因及定位:

 

1  頻繁 FullGC/YongGC

 

  • 查看 gc 日誌

 

  • jstat -gcutil pid  查看內存使用和 gc 情況

 

2  代碼消耗,如死循環,md5 等內存態操作

 

1)arthas (已開源:https://github.com/alibaba/arthas)

 

  • thread -n 5  查看 CPU 使用率最高的前 5 個線程(包含堆棧,第二部分有詳解)

 

2)jstack 查找

 

  • ps -ef | grep java  找到 Java 進程 id

 

  • top -Hp pid  找到使用 CPU 最高的線程

 

  • printf ‘0x%x’  tid  線程 id 轉化 16 進制

 

  • jstack pid | grep tid  找到線程堆棧

                   

 

ps:輸入“1”可查看每個 CPU 的情況,之前有團隊遇到單個 CPU 被中間件綁定導致 CPU 飈高的 case。

 

常見問題 2:load 高

 

load 指單位時間內活躍進程數,包含運行態(runnable 和 running)和不可中斷態( IO、內核態鎖)。關鍵字是運行態和不可中斷態,運行態可以聯想到 Java 線程的 6 種狀態,如下,線程 new 之後處於 NEW 狀態,執行 start 進入 runnable 等待 CPU 調度,因此如果 CPU 很忙會導致 runnable 進程數增加;不可中斷態主要包含網絡 IO、磁盤 IO 以及內核態的鎖,如 synchronized 等。

 

 

問題原因及定位:

 

1  CPU 利用率高,可運行態進程數多

 

  • 排查方法見常見問題一

 

2  iowait,等待 IO

 

  • vmstat  查看 blocked 進程狀況

 

  • jstack -l pid | grep BLOCKED  查看阻塞態線程堆棧

 

3  等待內核態鎖,如 synchronized

 

  • jstack -l pid | grep BLOCKED  查看阻塞態線程堆棧

 

  • profiler  dump 線程棧,分析線程持鎖情況

 

常見問題 3:持續 FullGC

 

在瞭解 FullGC 原因之前,先花一點時間回顧下 jvm 的內存相關知識:

 

內存模型

 

新 new 的對象放在 Eden 區,當 Eden 區滿之後進行一次 MinorGC,並將存活的對象放入 S0;

 

當下一次 Eden 區滿的時候,再次進行 MinorGC,並將存活的對象和 S0 的對象放入S1(S0 和 S1 始終有一個是空的);

 

依次循環直到 S0 或者 S1 快滿的時候將對象放入 old 區,依次,直到 old 區滿進行 FullGC。

 

jdk1.7 之前 Java 類信息、常量池、靜態變量存儲在 Perm 永久代,類的原數據和靜態變量在類加載的時候放入 Perm 區,類卸載的時候清理;在 1.8 中,MetaSpace 代替 Perm 區,使用本地內存,常量池和靜態變量放入堆區,一定程度上解決了在運行時生成或加載大量類造成的 FullGC,如反射、代理、groovy 等。

 

回收器

 

年輕代常用 ParNew,複製算法,多線程並行;

 

老年代常用 CMS,標記清除算法(會產生內存碎片),併發收集(收集過程中有用戶線程產生對象)。

 

關鍵常用參數

 

  • CMSInitiatingOccupancyFraction 表示老年代使用率達到多少時進行 FullGC;

 

  • UseCMSCompactAtFullCollection 表示在進行 FullGC 之後進行老年代內存整理,避免產生內存碎片。

 

問題原因及定位:

 

1  prommotion failed

 

從S區晉升的對象在老年代也放不下導致 FullGC(fgc 回收無效則拋 OOM)。

 

1)survivor 區太小,對象過早進入老年代。

 

  • jstat -gcutil pid 1000  觀察內存運行情況;

 

  • jinfo pid  查看 SurvivorRatio 參數;


2)大對象分配,沒有足夠的內存。

 

  • 日誌查找關鍵字 “allocating large”;

 

  • profiler  查看內存概況大對象分佈;

 

3)old 區存在大量對象。

 

  • 實例數量前十的類:jmap -histo pid | sort -n -r -k 2 | head -10

 

  • 實例容量前十的類:jmap -histo pid | sort -n -r -k 3 | head -10

 

  • dump 堆,profiler 分析對象佔用情況

 

2  concurrent mode failed

 

在 CMS GC 過程中業務線程將對象放入老年代(併發收集的特點)內存不足。詳細原因:

 

1)fgc 觸發比例過大,導致老年代佔用過多,併發收集時用戶線程持續產生對象導致達到觸發 FGC 比例。

 

  • jinfo  查看 CMSInitiatingOccupancyFraction 參數,一般 70~80 即可

 

2)老年代存在內存碎片。

 

  • jinfo  查看 UseCMSCompactAtFullCollection 參數,在 FullGC 後整理內存

 

常見問題 4:線程池滿

 

Java 線程池以有界隊列的線程池爲例,當新任務提交時,如果運行的線程少於 corePoolSize,則創建新線程來處理請求。如果正在運行的線程數等於 corePoolSize 時,則新任務被添加到隊列中,直到隊列滿。當隊列滿了後,會繼續開闢新線程來處理任務,但不超過 maximumPoolSize。當任務隊列滿了並且已開闢了最大線程數,此時又來了新任務,ThreadPoolExecutor 會拒絕服務。

 

問題原因及定位:

 

1  下游 RT 高,超時時間不合理

 

  • 業務監控

 

  • sunfire

 

  • eagleeye

 

2  數據庫慢 sql 或者數據庫死鎖

 

  • 日誌關鍵字 “Deadlock found when trying to get lock”

 

  • Jstack 或 zprofiler 查看阻塞態線程

 

3  Java 代碼死鎖

 

  • jstack –l pid | grep -i –E 'BLOCKED | deadlock'

 

  • dump thread 通過 zprofiler 分析阻塞線程和持鎖情況

 

常見問題 5:NoSuchMethodException

 

問題原因及定位:

 

1  jar 包衝突

 

 java 在裝載一個目錄下所有 jar 包時,它加載的順序完全取決於操作系統。

 

  • mvn dependency:tree  分析報錯方法所在的 jar 包版本,留下新的

 

  • arthas:sc -d ClassName

 

  • XX:+TraceClassLoading

 

2  同類問題

 

  • ClassNotFoundException

 

  • NoClassDefFoundError

 

  • ClassCastException

 

二  常用工具介紹

 

常用命令

 

1  tail

 

  • -f   跟蹤文件

 

2  grep

 

  • -i   忽略大小寫

 

  • -v  反轉查找

 

  • -E  擴展正則表達式 :grep -E 'pattern1|pattern2' filename

 

3  pgm

 

  • -b  開啓併發

 

  • -p  指定併發數

 

  • -A  開啓 askpass

 

4  awk

 

  • -F  指定分隔符:awk -F “|”  '{print $1}‘ | sort -r | uniq -c

 

5  sed

 

  • 時間段匹配:sed '/2020-03-02 10:00:00/,/2020-03-02 11:00:00/p' filename

 

arthas

 

阿里巴巴開源 Java 診斷工具(開源地址:https://github.com/alibaba/arthas),基於 javaAgent 方式,使用 Instrumentation 方式修改字節碼方式進行 Java 應用診斷。

 

基礎功能介紹

 

  • dashboard:系統實時數據面板, 可查看線程,內存,gc 等信息

 

  • thread:jvm 線程堆棧信息,如查看最繁忙的前 n 線程

 

  • getstatic:獲取靜態屬性值,如 getstatic className attrName 可用於查看線上開關真實值

 

  • sc:查看 jvm 已加載類信息,可用於排查 jar 包衝突

 

  • sm:查看 jvm 已加載類的方法信息

 

  • jad:反編譯 jvm 加載類信息,排查代碼邏輯沒執行原因

 

  • watch:觀測方法執行數據,包含出入參,異常等;

 

watch xxxClass xxxMethod " {params, throwExp} "  -e -x 2

 

watch xxxClass xxxMethod "{params,returnObj}" "params[0].sellerId.equals('189')" -x 2

 

watch xxxClass xxxMethod sendMsg '@com.taobao.eagleeye.EagleEye@getTraceId()'

 

  • trace:方法內部調用時長,並輸出每個節點的耗時,用於性能分析

 

  • tt:用於記錄方法,並做回放

 

三  常見問題恢復

 

1  線程池滿

 

  • rpc 框架線程池滿

 

高 RT 接口進行線程數限流

 

  • 應用內線程池滿

 

重啓可短暫緩解,具體還得看問題原因

 

2  CPU 高,load 高

 

  • 單機置換或重啓,可短暫緩解,恢復看具體原因

 

  • 集羣高且流量大幅增加,擴容,恢復看具體原因

 

3  下游 RT 高

 

  • 限流

 

  • 降級

 

4  數據庫

 

  • 死鎖

 

kill 進程

 

  • 慢 sql

 

sql 限流

 

線上問題的排查是一個積累的過程,只有瞭解問題背後的原理才能更快速的定位和恢復,除此之外更需要有一些趁手的工具來輔助排查,從而降低整個團隊問題定位和快恢的門檻。

來自:阿里技術

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章