<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          最牛逼的故障診斷工具!秒級定位線上問題

          共 17100字,需瀏覽 35分鐘

           ·

          2021-05-23 20:27

          作者 | 大數(shù)據(jù)之路

          來源 | my.oschina.net/leejun2005/blog/1524687

          背景

          經(jīng)常做后端服務(wù)開發(fā)的同學(xué),或多或少都遇到過 CPU 負載特別高的問題。

          尤其是在周末或大半夜(有沒有同感,平時不出問題,一到休息或下班時間頻繁出故障,有的文末點個在看示意一下,突然群里有人反饋線上機器負載特別高,不熟悉定位流程和思路的同學(xué)可能登上服務(wù)器一通手忙腳亂,定位過程百轉(zhuǎn)千回。

          圖片

          對此,也有不少同學(xué)曾經(jīng)整理過相關(guān)流程或方法論,類似把大象放進冰箱要幾步

          傳統(tǒng)的方案一般是4步

          1. top oder by with P:1040 // 首先按進程負載排序找到  axLoad(pid)  
          2. top -Hp 進程PID:1073    // 找到相關(guān)負載 線程PID  
          3. printf “0x%x\\n”線程PID: 0x431  // 將線程PID轉(zhuǎn)換為 16進制,為后面查找 jstack 日志做準備  
          4. jstack  進程PID | vim +/十六進制線程PID -        // 例如:jstack 1040|vim +/0x431 -  

          但是對于線上問題定位來說,分秒必爭,上面的 4 步還是太繁瑣耗時了,有沒有可能封裝成為一個工具,在有問題的時候一鍵定位,秒級找到有問題的代碼行呢?

          當然可以!

          工具鏈的成熟與否不僅體現(xiàn)了一個開發(fā)者的運維能力,也體現(xiàn)了開發(fā)者的效率意識。

          淘寶的oldratlee 同學(xué)就將上面的流程封裝為了一個工具:

          show-busy-java-threads.sh

          https://github.com/oldratlee/useful-scripts

          可以很方便的定位線上的這類問題,下面我會舉兩個例子來看實際的效果。

          快速安裝使用:

          source <(curl -fsSL https://raw.githubusercontent.com/oldratlee/useful-scripts/master/test-cases/self-installer.sh)  

          1、java 正則表達式回溯造成 CPU 100%

          import java.util.ArrayList;  
          import java.util.List;  
          import java.util.regex.Matcher;  
          import java.util.regex.Pattern;  
            
            
          public class RegexLoad {  
              public static void main(String\[\] args) {  
                  String\[\] patternMatch = {"(\[\\\w\\\s\]+)+(\[+\\\-/*\])+(\[\\\w\\\s\]+)",  
                          "(\[\\\w\\\s\]+)+(\[+\\\-/*\])+(\[\\\w\\\s\]+)+(\[+\\\-/*\])+(\[\\\w\\\s\]+)"};  
                  List patternList = new ArrayList();  
            
                  patternList.add("Avg Volume Units product A + Volume Units product A");  
                  patternList.add("Avg Volume Units /  Volume Units product A");  
                  patternList.add("Avg retailer On Hand / Volume Units Plan / Store Count");  
                  patternList.add("Avg Hand Volume Units Plan Store Count");  
                  patternList.add("1 - Avg merchant Volume Units");  
                  patternList.add("Total retailer shipment Count");  
            
                  for (String s :patternList ){  
            
                      for(int i=0;i<patternmatch.length;i++){  
                          Pattern pattern = Pattern.compile(patternMatch\[i\]);  
            
                          Matcher matcher = pattern.matcher(s);  
                          System.out.println(s);  
                          if (matcher.matches()) {  
            
                              System.out.println("Passed");  
                          }else  
                              System.out.println("Failed;");  
                      }  
                  }  
              }  
          }  

          編譯、運行上述代碼之后,咱們就能觀察到服務(wù)器多了一個 100% CPU 的 java 進程:

          圖片

          怎么使用呢?

          show-busy-java-threads.sh  
          # 從 所有的 Java進程中找出最消耗CPU的線程(缺省5個),打印出其線程棧。  
            
          show-busy-java-threads.sh -c <要顯示的線程棧數(shù)>  
            
          show-busy-java-threads.sh -c <要顯示的線程棧數(shù)> -p <指定的Java Process>  
          # -F選項:執(zhí)行jstack命令時加上-F選項(強制jstack),一般情況不需要使用  
          show-busy-java-threads.sh -p <指定的Java Process> -F  
            
          show-busy-java-threads.sh -s <指定jstack命令的全路徑>  
          # 對于sudo方式的運行,JAVA_HOME環(huán)境變量不能傳遞給root,  
          # 而root用戶往往沒有配置JAVA_HOME且不方便配置,  
          # 顯式指定jstack命令的路徑就反而顯得更方便了  
            
          show-busy-java-threads.sh -a <輸出記錄到的文件>  
            
          show-busy-java-threads.sh -t <重復(fù)執(zhí)行的次數(shù)> -i <重復(fù)執(zhí)行的間隔秒數(shù)>  
          # 缺省執(zhí)行一次;執(zhí)行間隔缺省是3秒  
            
          ##############################  
          # 注意:  
          ##############################  
          # 如果Java進程的用戶 與 執(zhí)行腳本的當前用戶 不同,則jstack不了這個Java進程。  
          # 為了能切換到Java進程的用戶,需要加sudo來執(zhí)行,即可以解決:  
          sudo show-busy-java-threads.sh  

          示例:

          work@dev\_zz\_Master 10.48.186.32 23:45:50 ~/demo >  
          bash show-busy-java-threads.sh  
          \[1\] Busy(96.2%) thread(8577/0x2181) stack of java process(8576) under user(work):  
          "main" prio=10 tid=0x00007f0c64006800 nid=0x2181 runnable \[0x00007f0c6a64a000\]  
             java.lang.Thread.State: RUNNABLE  
                  at java.util.regex.Pattern$GroupHead.match(Pattern.java:4168)  
                  at java.util.regex.Pattern$Loop.match(Pattern.java:4295)  
                  ...  
                  at java.util.regex.Matcher.match(Matcher.java:1127)  
                  at java.util.regex.Matcher.matches(Matcher.java:502)  
                  at RegexLoad.main(RegexLoad.java:27)  
            
          \[2\] Busy(1.5%) thread(8591/0x218f) stack of java process(8576) under user(work):  
          "C2 CompilerThread1" daemon prio=10 tid=0x00007f0c64095800 nid=0x218f waiting on condition \[0x0000000000000000\]  
             java.lang.Thread.State: RUNNABLE  
            
          \[3\] Busy(0.8%) thread(8590/0x218e) stack of java process(8576) under user(work):  
          "C2 CompilerThread0" daemon prio=10 tid=0x00007f0c64093000 nid=0x218e waiting on condition \[0x0000000000000000\]  
             java.lang.Thread.State: RUNNABLE  
            
          \[4\] Busy(0.2%) thread(8593/0x2191) stack of java process(8576) under user(work):  
          "VM Periodic Task Thread" prio=10 tid=0x00007f0c640a2800 nid=0x2191 waiting on condition   
            
          \[5\] Busy(0.1%) thread(25159/0x6247) stack of java process(25137) under user(work):  
          "VM Periodic Task Thread" prio=10 tid=0x00007f13340b4000 nid=0x6247 waiting on condition   
          work@dev\_zz\_Master 10.48.186.32 23:46:04 ~/demo >  

          可以看到,一鍵直接定位異常代碼行,是不是很方便?

          2、線程死鎖,程序 hang 住

          import java.util.*;  
          public class SimpleDeadLock extends Thread {  
              public static Object l1 = new Object();  
              public static Object l2 = new Object();  
              private int index;  
              public static void main(String\[\] a) {  
                  Thread t1 = new Thread1();  
                  Thread t2 = new Thread2();  
                  t1.start();  
                  t2.start();  
              }  
              private static class Thread1 extends Thread {  
                  public void run() {  
                      synchronized (l1) {  
                          System.out.println("Thread 1: Holding lock 1...");  
                          try { Thread.sleep(10); }  
                          catch (InterruptedException e) {}  
                          System.out.println("Thread 1: Waiting for lock 2...");  
                          synchronized (l2) {  
                              System.out.println("Thread 2: Holding lock 1 & 2...");  
                          }  
                      }  
                  }  
              }  
              private static class Thread2 extends Thread {  
                  public void run() {  
                      synchronized (l2) {  
                          System.out.println("Thread 2: Holding lock 2...");  
                          try { Thread.sleep(10); }  
                          catch (InterruptedException e) {}  
                          System.out.println("Thread 2: Waiting for lock 1...");  
                          synchronized (l1) {  
                              System.out.println("Thread 2: Holding lock 2 & 1...");  
                          }  
                      }  
                  }  
              }  
          }  

          執(zhí)行之后的效果:

          圖片

          如何用工具定位:

          圖片

          一鍵定位:可以清晰的看到線程互相鎖住了對方等待的資源,導(dǎo)致死鎖,直接定位到代碼行和具體原因。

          通過上面兩個例子,我想各位同學(xué)應(yīng)該對這個工具和工具能解決什么問題有了比較深刻的了解了,遇到 CPU 100% 問題可以從此不再慌亂。

          不過更多的還是依賴大家自己去實踐,畢竟實踐出真知嘛~

          3、免費實用的腳本工具大禮包

          除了正文提到的 show-busy-java-threads.sh,oldratlee 同學(xué)還整合和不少常見的開發(fā)、運維過程中涉及到的腳本工具,覺得特別有用的我簡單列下:

          (1)show-duplicate-java-classes

          偶爾會遇到本地開發(fā)、測試都正常,上線后卻莫名其妙的 class 異常,歷經(jīng)千辛萬苦找到的原因竟然是 Jar 沖突!

          這個工具就可以找出Java Lib(Java庫,即Jar文件)或Class目錄(類目錄)中的重復(fù)類。

          Java開發(fā)的一個麻煩的問題是Jar沖突(即多個版本的Jar),或者說重復(fù)類。會出NoSuchMethod等的問題,還不見得當時出問題。找出有重復(fù)類的Jar,可以防患未然。

          # 查找當前目錄下所有Jar中的重復(fù)類  
          show-duplicate-java-classes  
            
          # 查找多個指定目錄下所有Jar中的重復(fù)類  
          show-duplicate-java-classes path/to/lib_dir1 /path/to/lib_dir2  
            
          # 查找多個指定Class目錄下的重復(fù)類。Class目錄 通過 -c 選項指定  
          show-duplicate-java-classes -c path/to/class_dir1 -c /path/to/class_dir2  
            
          # 查找指定Class目錄和指定目錄下所有Jar中的重復(fù)類的Jar  
          show-duplicate-java-classes path/to/lib_dir1 /path/to/lib_dir2 -c path/to/class_dir1 -c path/to/class_dir2  

          例如:

          # 在war模塊目錄下執(zhí)行,生成war文件  
          $ mvn install  
          ...  
          # 解壓war文件,war文件中包含了應(yīng)用的依賴的Jar文件  
          $ unzip target/*.war -d target/war  
          ...  
          # 檢查重復(fù)類  
          $ show-duplicate-java-classes -c target/war/WEB-INF/classes target/war/WEB-INF/lib  
          ...  

          (2)find-in-jars

          在當前目錄下所有jar文件里,查找類或資源文件。

          用法:注意,后面Pattern是grep的 擴展正則表達式。

          find-in-jars 'log4j\\.properties'  
          find-in-jars 'log4j\\.xml$' -d /path/to/find/directory  
          find-in-jars log4j\\\.xml  
          find-in-jars 'log4j\\.properties|log4j\\.xml'  

          示例:

          $ ./find-in-jars 'Service.class$'  
          ./WEB-INF/libs/spring-2.5.6.SEC03.jar!org/springframework/stereotype/Service.class  
          ./rpc-benchmark-0.0.1-SNAPSHOT.jar!com/taobao/rpc/benchmark/service/HelloService.class  

          (3)housemd pid [java_home]

          很早的時候,我們使用BTrace排查問題,在感嘆BTrace的強大之余,也曾好幾次將線上系統(tǒng)折騰掛掉。

          2012年淘寶的聚石寫了HouseMD,將常用的幾個Btrace腳本整合在一起形成一個獨立風格的應(yīng)用,其核心代碼用的是Scala

          HouseMD是基于字節(jié)碼技術(shù)的診斷工具, 因此除了Java以外, 任何最終以字節(jié)碼形式運行于JVM之上的語言, HouseMD都支持對它們進行診斷, 如Clojure(感謝@Killme2008提供了它的使用入門), scala, Groovy, JRuby, Jython, kotlin等.

          使用housemd對java程序進行運行時跟蹤,支持的操作有:

          • 查看加載類
          • 跟蹤方法
          • 查看環(huán)境變量
          • 查看對象屬性值

          詳細信息請參考:

          https://github.com/CSUG/HouseMD/wiki/UserGuideCN

          (4)jvm pid

          執(zhí)行jvm debug工具,包含對java棧、堆、線程、gc等狀態(tài)的查看,支持的功能有:

          ========線程相關(guān)=======  
          1 : 查看占用cpu最高的線程情況  
          2 : 打印所有線程  
          3 : 打印線程數(shù)  
          4 : 按線程狀態(tài)統(tǒng)計線程數(shù)  
          ========GC相關(guān)=======  
          5 : 垃圾收集統(tǒng)計(包含原因)可以指定間隔時間及執(zhí)行次數(shù),默認1秒, 10次  
          6 : 顯示堆中各代的空間可以指定間隔時間及執(zhí)行次數(shù),默認1秒,5次  
          7 : 垃圾收集統(tǒng)計??梢灾付ㄩg隔時間及執(zhí)行次數(shù),默認1秒, 10次  
          8 : 打印perm區(qū)內(nèi)存情況*會使程序暫停響應(yīng)*  
          9 : 查看directbuffer情況  
          ========堆對象相關(guān)=======  
          10 : dump heap到文件*會使程序暫停響應(yīng)*默認保存到\`pwd\`/dump.bin,可指定其它路徑  
          11 : 觸發(fā)full gc。*會使程序暫停響應(yīng)*  
          12 : 打印jvm heap統(tǒng)計*會使程序暫停響應(yīng)*  
          13 : 打印jvm heap中top20的對象。*會使程序暫停響應(yīng)*參數(shù):1:按實例數(shù)量排序,2:按內(nèi)存占用排序,默認為1  
          14 : 觸發(fā)full gc后打印jvm heap中top20的對象。*會使程序暫停響應(yīng)*參數(shù):1:按實例數(shù)量排序,2:按內(nèi)存占用排序,默認為1  
          15 : 輸出所有類裝載器在perm里產(chǎn)生的對象??梢灾付ㄩg隔時間及執(zhí)行次數(shù)  
          ========其它=======  
          16 : 打印finalzer隊列情況  
          17 : 顯示classloader統(tǒng)計  
          18 : 顯示jit編譯統(tǒng)計  
          19 : 死鎖檢測  
          20 : 等待X秒,默認為1  
          q : exit  

          進入jvm工具后可以輸入序號執(zhí)行對應(yīng)命令

          可以一次執(zhí)行多個命令,用分號";"分隔,如:1;3;4;5;6

          每個命令可以帶參數(shù),用冒號":"分隔,同一命令的參數(shù)之間用逗號分隔,如:

          Enter command queue:1;5:1000,100;10:/data1/output.bin  

          (5)greys[@IP:PORT]

          PS:目前Greys僅支持Linux/Unix/Mac上的Java6+,Windows暫時無法支持

          Greys是一個JVM進程執(zhí)行過程中的異常診斷工具,可以在不中斷程序執(zhí)行的情況下輕松完成問題排查工作。

          和HouseMD一樣,Greys-Anatomy取名同名美劇“實習醫(yī)生格蕾”,目的是向前輩致敬。代碼編寫的時候參考了BTrace和HouseMD兩個前輩的思路。

          使用greys對java程序進行運行時跟蹤(不傳參數(shù),需要先greys -C pid,再greys)。支持的操作有:

          • 查看加載類,方法信息
          • 查看JVM當前基礎(chǔ)信息
          • 方法執(zhí)行監(jiān)控(調(diào)用量,失敗率,響應(yīng)時間等)
          • 方法執(zhí)行數(shù)據(jù)觀測、記錄與回放(參數(shù),返回結(jié)果,異常信息等)
          • 方法調(diào)用追蹤渲染

          詳細信息請參考:

          https://github.com/oldmanpushcart/greys-anatomy/wiki

          (6)sjksjk --commands sjk --help

          使用sjk對Java診斷、性能排查、優(yōu)化工具

          • ttop:監(jiān)控指定jvm進程的各個線程的cpu使用情況
          • jps: 強化版
          • hh: jmap -histo強化版
          • gc: 實時報告垃圾回收信息

          更多信息請參考:

          https://github.com/aragozin/jvm-tools

          1、Intellij IDEA這樣 配置注釋模板,讓你瞬間高出一個逼格!
          2、吊炸天的 Docker 圖形化工具 Portainer,必須推薦給你!
          3、最牛逼的 Java 日志框架,性能無敵,橫掃所有對手!
          4、把Redis當作隊列來用,真的合適嗎?
          5、驚呆了,Spring Boot居然這么耗內(nèi)存!你知道嗎?
          6、全網(wǎng)最全 Java 日志框架適配方案!還有誰不會?
          7、Spring中毒太深,離開Spring我居然連最基本的接口都不會寫了

          點分享

          點收藏

          點點贊

          點在看

          瀏覽 62
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  日韩淫乱无码视频播放 | 日韩 人妻 精品 | 大香蕉中文青草 | 啪啪视频官网 | 成人国产精品秘 在线看 |