記錄一次解決了CPU飆升的問題

負責的項目預定今天凌晨2點上進行版本更新。前幾天測試對網站進行壓力測試,觀察服務的CPU、內存、load、RT、QPS等各種指標。

在壓測的過程中,測試發現我們其中一個接口,在QPS上升到400以後,CPU利用率急劇升高。

在這裏我不再對CPU內存loadRTQPS等做過多贅述,畢竟這幾個點中的任何一個拿出來探討,一篇文章都不一定寫的完。有興趣深究自己動手查一下吧。

這裏我僅對QPSCPU利用率做簡單的概述。

QPS每秒查詢率,QPS是對一個特定的查詢服務器在規定時間內所處理流量多少的衡量標準。QPS越高說明,在特定時間內服務器所處理的流量越大。

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

瞭解了這兩個基本的點,接下來就來一起看看我具體是怎麼定位的吧。

問題定位

測試反饋這個問題後,我登錄到服務器,做了以下操作:

1.定位進程

登錄服務器,執行top命令,查看CPU佔用情況:

$top
   PID  USER      PR   NI  VIRT   RES    SHR   S  %CPU   %MEM   TIME+  COMMAND
  44529 root     20   0  4289m   874m  13312  S  123.0   10.9   10:39.73  java複製代碼

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

我們來看一下這些指示符代表什麼:

    PID — 進程id
    USER — 進程所有者
    PR — 進程優先級
    NI — nice值。負值表示高優先級,正值表示低優先級
    VIRT — 進程使用的虛擬內存總量,單位kb。VIRT=SWAP+RES
    RES — 進程使用的、未被換出的物理內存大小,單位kb。RES=CODE+DATA
    SHR — 共享內存大小,單位kb
    S — 進程狀態。D=不可中斷的睡眠狀態 R=運行 S=睡眠 T=跟蹤/停止 Z=殭屍進程
    %CPU — 上次更新到現在的CPU時間佔用百分比
    %MEM — 進程使用的物理內存百分比
    TIME+ — 進程使用的CPU時間總計,單位1/100秒
    COMMAND — 進程名稱(命令名/命令行)
複製代碼

通過執行top命令,我看到,進程ID爲44529的Java進程的CPU佔用率達到了123%,基本可以定位到是我們的Java應用導致整個服務器的CPU佔用率飆升。

2.定位線程

當然,Java是單進程多線程的,那麼,接下來我就看了這個PID=44529的這個Java進程中的各個線程的CPU使用情況,同樣是用top命令:

$top -Hp 44529
   PID  USER     PR   NI  VIRT  RES     SHR   S  %CPU   %MEM    TIME+  COMMAND
  44580 root     20   0   4289m 874m   13312  S  16.2   10.9    3:02.96   java複製代碼

通過執行top -Hp 44529命令,我發現,在44529這個進程中,ID爲44580的線程佔用CPU最高。

3.定位代碼

通過top命令,我定位到了導致CPU使用率較高的具體線程, 接下來就要看一下具體是哪一行代碼存在問題。

首先,我把44580這個線程轉成16進制:

$printf %x 44580
ae24
複製代碼

接下來,根據該16進制值去打印的堆棧日誌內查詢,查看該線程所駐留的方法位置。並通過jstack命令,查看棧信息:

$jstack 44529 |grep -A 200 ae24
"System Clock" #28 daemon prio=5 os_prio=0 tid=0x00007efc19e8e800 nid=0xae24 waiting on condition [0x00007efbe0d91000]
   java.lang.Thread.State: TIMED_WAITING (sleeping)
    at java.lang.Thread.sleep(Native Method)
    at java.lang.Thread.sleep(Thread.java:340)
    at java.util.concurrentC.TimeUnit.sleep(TimeUnit.java:386)
    at com.*.order.Controller.OrderController.detail(OrderController.java:37)  //業務代碼阻塞點
複製代碼

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

然後我找到項目中OrderController.java的第37行,仔細分析發現由於自己的粗心,代碼存在邏輯上的漏洞。修改重構之後,問題解決。

總結

以上就是本次問題排查的整個過程。主要用到的有:topprintfjstack等指令。

  1. 使用top指令查看當前佔用CPU較高的進程PID;

  2. 查看當前進程消耗資源的線程PID:    top -Hp PID

  3. 將線程PID轉爲16進制,根據該16進制值去打印的堆棧日誌內查詢,查看該線程所駐留的方法位置。

  4. 通過jstack命令,查看棧信息,這步基本上已經能夠定位大多數問題了。

 

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