服務器線上問題排查研究


線上問題諸如:

  1、線上服務器CPU占用率高如何排查?

  2、線上服務器Load飆高如何排查? 

  3、線上服務器頻繁發生Full GC如何排查? 

  4、線上服務器發生死鎖如何排查?


一:線上服務器CPU占用率高如何排查?

問題發現:

 在每次大促之前,我們的測試人員都會對網站進行壓力測試,這個時候會查看服務的cpu、內存、load、rt、qps等指標。

 在一次壓測過程中,測試人員發現我們的某一個接口,在qps上升到500以后,CPU使用率急劇升高

CPU利用率,又稱CPU使用率。顧名思義,CPU利用率是來描述CPU的使用情況的,表明了一段時間內CPU被占用的情況。使用率越高,說明你的機器在這個時間上運行了很多程序,反之較少。

問題定位:

 定位進程:登錄服務器,執行top命令,查看CPU占用情況:

top命令是Linux下常用的性能分析工具,能夠實時顯示系統中各個進程的資源占用狀況,類似於Windows的任務管理器。

 通過以上命令,我們可以看到,進程ID為1893的Java進程的CPU占用率達到了181%,基本可以定位到是我們的Java應用導致整個服務器的CPU占用率飆升。

 定位線程

  我們知道,Java是單進程多線程的,那么,我們接下來看看PID=1893的這個Java進程中的各個線程的CPU使用情況,同樣是用top命令: 

 

  通過top -Hp 1893命令,我們可以發現,當前1893這個進程中,ID為4519的線程占用CPU最高。

  定位代碼 

  通過top命令,我們目前已經定位到導致CPU使用率較高的具體線程, 那么我么接下來就定位下到底是哪一行代碼存在問題。

  首先,我們需要把4519這個線程轉成16進制:

 

  接下來,通過jstack命令,查看棧信息:

 

  通過以上代碼,我們可以清楚的看到,BeanValidator.java的第30行是有可能存在問題的。

 問題解決

  接下來就是通過查看代碼來解決問題了,我們發現,我們自定義了一個BeanValidator,封裝了Hibernate的Validator,然后在validate方法中,通過Validation.buildDefaultValidatorFactory().getValidator()初始化一個Validator實例,通過分析發現這個實例化的過程比較耗時。

  我們重構了一下代碼,把Validator實例的初始化提到方法外,在類初始化的時候創建一次就解決了問題。

 總結 

  以上,展示了一次比較完成的線上問題定位過程。主要用到的命令有:top 、printfjstack

  另外,線上問題排查還可以使用Alibaba開源的工具Arthas進行排查,以上問題,可以使用一下命令定位:

 

  以上,本文介紹了如何排查線上服務器CPU使用率過高的問題,如果大家感興趣,后面可以再介紹一些關於LOAD飆高、頻繁GC等問題的排查手段。

 


 

 二、線上服務器Load飆高如何排查? 

什么是負載

負載(load)是linux機器的一個重要指標,直觀了反應了機器當前的狀態。

來看下負載的定義是怎樣的:

In UNIX computing, the system load is a measure of the amount of computational work that a computer system performs. The load average represents the average system load over a period of time. It conventionally appears in the form of three numbers which represent the system load during the last one-, five-, and fifteen-minute periods.(wikipedia)

  簡單解釋一下:在UNIX系統中,系統負載是對當前CPU工作量的度量,被定義為特定時間間隔內運行隊列中的平均線程數。load average 表示機器一段時間內的平均load。這個值越低越好。負載過高會導致機器無法處理其他請求及操作,甚至導致死機。

  Linux的負載高,主要是由於CPU使用、內存使用、IO消耗三部分構成。任意一項使用過多,都將導致服務器負載的急劇攀升。

查看機器負載。

  在Linux機器上,有多個命令都可以查看機器的負載信息。其中包括uptimetopw等。

uptime命令

  uptime命令能夠打印系統總共運行了多長時間和系統的平均負載。uptime命令可以顯示的信息顯示依次為:現在時間、系統已經運行了多長時間、目前有多少登陸用戶、系統在過去的1分鍾、5分鍾和15分鍾內的平均負載。

  這行信息的后半部分,顯示”load average”,它的意思是”系統的平均負荷”,里面有三個數字,我們可以從中判斷系統負荷是大還是小。

  1.74 1.87 1.97 這三個數字的意思分別是1分鍾、5分鍾、15分鍾內系統的平均負荷。我們一般表示為load1、load5、load15。  

何為系統負載呢?

 系統平均負載被定義為在特定時間間隔內運行隊列中的平均進程數。如果一個進程滿足以下條件則其就會位於運行隊列中:

  • 它沒有在等待I/O操作的結果
  • 它沒有主動進入等待狀態(也就是沒有調用'wait')
  • 沒有被停止(例如:等待終止)

  一般來說,每個CPU內核當前活動進程數不大於3,則系統運行表現良好!當然這里說的是每個cpu內核,也就是如果你的主機是四核cpu的話,那么只要uptime最后輸出的一串字符數值小於12即表示系統負載不是很嚴重.當然如果達到20,那就表示當前系統負載非常嚴重,估計打開執行web腳本非常緩慢.  

cat /proc/loadavg

 最直接查看系統平均負載命令

root@Slyar.com:~# cat /proc/loadavg
0.10 0.06 0.01 1/72 29632

 除了前3個數字表示平均進程數量外,后面的1個分數,分母表示系統進程總數,分子表示正在運行的進程數;最后一個數字表示最近運行的進程ID

 

w命令

  w命令的主要功能其實是顯示目前登入系統的用戶信息。但是與who不同的是,w命令功能更加強大,w命令還可以顯示:當前時間,系統啟動到現在的時間,登錄用戶的數目,系統在最近1分鍾、5分鍾和15分鍾的平均負載。然后是每個用戶的各項數據,項目顯示順序如下:登錄帳號、終端名稱、遠 程主機名、登錄時間、空閑時間、JCPU、PCPU、當前正在運行進程的命令行。

 

  從上面的w命令的結果可以看到,當前系統時間是14:08,系統啟動到現在經歷了23小時41分鍾,共有3個用戶登錄。系統在近1分鍾、5分鍾和15分鍾的平均負載分別是1.74 1.87 1.97。這和uptime得到的結果相同。 下面還打印了一些登錄的用戶的各項數據,不詳細介紹了。

top命令

  top命令是Linux下常用的性能分析工具,能夠實時顯示系統中各個進程的資源占用狀況,類似於Windows的任務管理器。

機器正常負載范圍

  對於機器的Load到底多少算正常的問題,一直都是很有爭議的,不同人有着不同的理解。對於單個CPU,有人認為如果Load超過0.7就算是超出正常范圍了。也有人認為只要不超過1都沒問題。也有人認為,單個CPU的負載在2以下都可以接受。

  為什么會有這么多不同的理解呢,是因為不同的機器除了CPU影響之外還有其他因素的影響,運行的程序、機器內存、甚至是機房溫度等都有可能有區別。

  比如,有些機器用於定時執行大量的跑批任務,這個時間段內,Load可能會飆的比較高。而其他時間可能會比較低。那么這段飆高時間我們要不要去排查問題呢?

  我的建議是,最好根據自己機器的實際情況,建立一個指標的基線(如近一個月的平均值),只要日常的load在基線上下范圍內不太大都可以接收,如果差距太多可能就要人為介入檢查了。

  但是,總要有個建議的閾值吧,關於這個值。阮一峰在自己的博客中有過以下建議:

當系統負荷持續大於0.7,你必須開始調查了,問題出在哪里,防止情況惡化。

當系統負荷持續大於1.0,你必須動手尋找解決辦法,把這個值降下來。

當系統負荷達到5.0,就表明你的系統有很嚴重的問題,長時間沒有響應,或者接近死機了。你不應該讓系統達到這個值。

  以上指標都是基於單CPU的,但是現在很多電腦都是多核的。所以,對一般的系統來說,是根據cpu數量去判斷系統是否已經過載(Over Load)的。如果我們認為0.7算是單核機器負載的安全線的話,那么四核機器的負載最好保持在3(4*0.7 = 2.8)以下。

  還有一點需要提一下,在Load Avg的指標中,有三個值,1分鍾系統負荷、5分鍾系統負荷,15分鍾系統負荷。我們在排查問題的時候也是可以參考這三個值的。

  一般情況下,1分鍾系統負荷表示最近的暫時現象。15分鍾系統負荷表示是持續現象,並非暫時問題。如果load15較高,而load1較低,可以認為情況有所好轉。反之,情況可能在惡化。

如何降低負載

 導致負載高的原因可能很復雜,有可能是硬件問題也可能是軟件問題。

 如果是硬件問題,那么說明機器性能確實就不行了,那么解決起來很簡單,直接換機器就可以了。

 前面我們提過,CPU使用、內存使用、IO消耗都可能導致負載高。如果是軟件問題,有可能由於Java中的某些線程被長時間占用、大量內存持續占用等導致。建議從以下幾個方面排查代碼問題:

  1、是否有內存泄露導致頻繁GC

  2、是否有死鎖發生

  3、是否有大字段的讀寫

  4、會不會是數據庫操作導致的,排查SQL語句問題。

 這里還有個建議,如果發現線上機器Load飆高,可以考慮先把堆棧內存dump下來后,進行重啟,暫時解決問題,然后再考慮回滾和排查問題。

Java Web應用Load飆高排查思路

1、使用uptime查看當前load,發現load飆高。

2、使用top命令,查看占用CPU較高的進程ID。、

  發現PID為1893的進程占用CPU 181%。而且是一個Java進程,基本斷定是軟件問題。

3、使用 top命令 (top -Hp 進程id),查看具體是哪個線程占用率較高

4、使用printf命令查看這個線程的16進制

5、使用jstack命令查看當前線程正在執行的方法 (jstack 進程id | grep 線程id的16進制)。(Java命令學習系列(二)——Jstack)

  從上面的線程的棧日志中,可以發現,當前占用CPU較高的線程正在執行我代碼的com.hollis.test.util.BeanValidator.validate(BeanValidator.java:30)類。那么就可以去排查這個類是否用法有問題了。

 

網上有牛人寫了一個腳本能自動幫我們大致定位到現場導致LOAD飆升的JVM線程,腳本大概如下

#!/bin/ksh
typeset top=${1:-10}
typeset pid=${2:-$(pgrep -u $USER java)}
typeset tmp_file=/tmp/java_${pid}_$$.trace

$JAVA_HOME/bin/jstack $pid > $tmp_file
ps H -eo user,pid,ppid,tid,time,%cpu --sort=%cpu --no-headers\
        | tail -$top\
        | awk -v "pid=$pid" '$2==pid{print $4"\t"$6}'\
        | while read line;
do
        typeset nid=$(echo "$line"|awk '{printf("0x%x",$1)}')
        typeset cpu=$(echo "$line"|awk '{print $2}')
        awk -v "cpu=$cpu" '/nid='"$nid"'/,/^$/{print $0"\t"(isF++?"":"cpu="cpu"%");}' $tmp_file
done

rm -f $tmp_file

現在我們就來拆解其中的原理,以及說明下類似腳本的適用范圍。
  1.使用top命令查看飆高的java進程,記錄pid
  2.通過jstack命令將java的線程棧輸出,保留現場 jstack -l 30142 > 30142.stack
  3.找到當前CPU使用占比高的線程,通過 ps H -eo user,pid,ppid,tid,time,%cpu –sort=%cpu
    USER:進程歸屬用戶,PID:進程號,PPID:父進程號,TID:線程號
    %CPU:線程使用CPU占比(這里要提醒下各位,這個CPU占比是通過/proc計算得到,存在時間差)
  4.合並相關信息,通過PS拿到了TID,可以通過進制換算10-16得到jstack出來的JVM線程號​
    typeset nid=”0x”(echo"(echo"line”|awk ‘{print $1}’|xargs -I{} echo “obase=16;{}”|bc|tr ‘A-Z’ ‘a-z’)
  5.最后再將ps和jstack出來的信息進行一個匹配與合並。終於,得到我們最想要的信息


 


 

三:線上服務器頻繁發生Full GC如何排查

1:查看JVM堆配置參數

2:linux下查看java虛擬機(JVM)GC情況

3:導出堆棧的dump信息分析

jstat命令命令格式:

jstat [Options] vmid [interval] [count]
 
參數說明:
  Options,選項,我們一般使用 -gcutil 查看gc情況
  vmid,VM的進程號,即當前運行的java進程號
  interval,間隔時間,單位為秒或者毫秒
  count,打印次數,如果缺省則打印無數次
 
實例說明

示例

通常運行命令如下:
  jstat -gc 12538 5000
即會每5秒一次顯示進程號為12538的java進成的GC情況,
顯示內容如下圖:
 
結果說明
 顯示內容說明如下(部分結果是通過其他其他參數顯示的,暫不說明):
         S0C:年輕代中第一個survivor(幸存區)的容量 (字節) 
         S1C:年輕代中第二個survivor(幸存區)的容量 (字節) 
         S0U:年輕代中第一個survivor(幸存區)目前已使用空間 (字節) 
         S1U:年輕代中第二個survivor(幸存區)目前已使用空間 (字節) 
         EC:年輕代中Eden(伊甸園)的容量 (字節) 
         EU:年輕代中Eden(伊甸園)目前已使用空間 (字節) 
         OC:Old代的容量 (字節) 
         OU:Old代目前已使用空間 (字節) 
         PC:Perm(持久代)的容量 (字節) 
         PU:Perm(持久代)目前已使用空間 (字節) 
         YGC:從應用程序啟動到采樣時年輕代中gc次數 
         YGCT:從應用程序啟動到采樣時年輕代中gc所用時間(s) 
         FGC:從應用程序啟動到采樣時old代(全gc)gc次數 
         FGCT:從應用程序啟動到采樣時old代(全gc)gc所用時間(s) 
         GCT:從應用程序啟動到采樣時gc用的總時間(s) 
         NGCMN:年輕代(young)中初始化(最小)的大小 (字節) 
         NGCMX:年輕代(young)的最大容量 (字節) 
         NGC:年輕代(young)中當前的容量 (字節) 
         OGCMN:old代中初始化(最小)的大小 (字節) 
         OGCMX:old代的最大容量 (字節) 
         OGC:old代當前新生成的容量 (字節) 
         PGCMN:perm代中初始化(最小)的大小 (字節) 
         PGCMX:perm代的最大容量 (字節)   
         PGC:perm代當前新生成的容量 (字節) 
         S0:年輕代中第一個survivor(幸存區)已使用的占當前容量百分比 
         S1:年輕代中第二個survivor(幸存區)已使用的占當前容量百分比 
         E:年輕代中Eden(伊甸園)已使用的占當前容量百分比 
         O:old代已使用的占當前容量百分比 
         P:perm代已使用的占當前容量百分比 
         S0CMX:年輕代中第一個survivor(幸存區)的最大容量 (字節) 
         S1CMX :年輕代中第二個survivor(幸存區)的最大容量 (字節) 
         ECMX:年輕代中Eden(伊甸園)的最大容量 (字節) 
         DSS:當前需要survivor(幸存區)的容量 (字節)(Eden區已滿) 
         TT: 持有次數限制 
         MTT : 最大持有次數限制

 若發現GC頻繁則可能是內存泄露等情況


四、線上服務器發生死鎖如何排查?

 

 

 出處:

  https://mp.weixin.qq.com/s/aZ2Otci6TntXdsoyMcwBVw  

  https://blog.csdn.net/huangyimo/article/details/80401638

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM