記一次線上服務CPU 100%的處理過程

告警

正在開會,忽然釘釘告警聲響個不停,同時市場人員反饋客戶在投訴系統登不進了,報504錯誤。查看釘釘上的告警信息,幾臺業務服務器節點所有報CPU超過告警閾值,達100%。php

趕忙從會上下來,SSH登陸服務器,使用 top 命令查看,幾個Java進程CPU佔用達到180%,190%,這幾個Java進程對應同一個業務服務的幾個Pod(或容器)。程序員

定位

  1. 使用 docker stats 命令查看本節點容器資源使用狀況,對佔用CPU很高的容器使用 docker exec -it <容器ID> bash 進入。
  2. 在容器內部執行 top 命令查看,定位到佔用CPU高的進程ID,使用 top -Hp <進程ID> 定位到佔用CPU高的線程ID。
  3. 使用 jstack <進程ID> > jstack.txt 將進程的線程棧打印輸出。
  4. 退出容器, 使用 docker cp <容器ID>:/usr/local/tomcat/jstack.txt ./ 命令將jstack文件複製到宿主機,便於查看。獲取到jstack信息後,趕忙重啓服務讓服務恢復可用。
  5. 將2中佔用CPU高的線程ID使用 pringf '%x\n' <線程ID> 命令將線程ID轉換爲十六進制形式。假設線程ID爲133,則獲得十六進制85。在jstack.txt文件中定位到 nid=0x85的位置,該位置即爲佔用CPU高線程的執行棧信息。以下圖所示,

jstack

  1. 與同事確認,該處爲使用一個框架的excel導出功能,而且,導出excel時沒有分頁,沒有限制!!!查看SQL查詢記錄,該導出功能一次導出50w條數據,而且每條數據都須要作轉換計算,更爲糟糕的是,操做者由於導出時久久沒有響應,因而連續點擊,幾分鐘內發起了10屢次的導出請求。。。因而,CPU被打滿,服務崩潰了,我也崩潰了。。

解決

對於此類耗資源的操做,必定要作好相應的限制。好比能夠限制請求量,控制最大分頁大小,同時能夠限制訪問頻率,好比同一用戶一分鐘內最多請求多少次。docker

再發

服務重啓後恢復。到了下午,又一臺服務器節點CPU告警,依前面步驟定位到佔用CPU高的線程,以下shell

"GC task thread#0 (ParallelGC)" os_prio=0 tid=0x00007fa114020800 nid=0x10 runnable 

"GC task thread#1 (ParallelGC)" os_prio=0 tid=0x00007fa114022000 nid=0x11 runnable

使用命令 jstat -gcutil <進程ID> 2000 10 查看GC狀況,如圖tomcat

jstat

發現Full GC次數達到1000屢次,且還在不斷增加,同時Eden區,Old區已經被佔滿(也可以使用jmap -heap <進程ID>查看堆內存各區的佔用狀況),使用jmap將內存使用狀況dump出來,bash

jmap -dump:format=b,file=./jmap.dump 13

退出容器,使用 docker cp <容器ID>:/usr/local/tomcat/jmap.dump ./ 將dump文件複製到宿主機目錄,下載到本地,使用 MemoryAnalyzer(下載地址:https://www.eclipse.org/mat/d... )打開,如圖服務器

jmap-mat

若是dump文件比較大,須要增大MemoryAnalyzer.ini配置文件中的-Xmx值

發現佔用內存最多的是char[], String對象,經過右鍵能夠查看引用對象,但點開貌似也看不出因此然來,進入內存泄露報告頁面,如圖框架

mat-leak-suspects

該頁面統計了堆內存的佔用狀況,而且給出疑似泄露點,在上圖中點開「see stacktrace」連接,進入線程棧頁面,eclipse

mat-thread-stack

似曾熟悉的畫面,仍是跟excel導出有關,數據太多,致使內存溢出。。。因而GC頻繁,因而CPU爆了。根源仍是同一個。spa

總結

本文以處理一次線上服務CPU 100%的實戰過程示例了在遇到Java服務形成服務器CPU消耗太高或內存溢出的通常處理方法,但願對你們定位線上相似問題提供參考。同時,開發實現功能時須要考慮的更深遠一些,不能停留在解決當前的場景,須要考慮數據量不斷增大時,你的實現是否還能適用。俗話說,初級程序員解決當前問題,中級程序員解決兩年後的問題,高級程序員解決五年後的問題,^_^。


[轉載請註明出處]
做者:雨歌
歡迎關注做者公衆號:半路雨歌,查看更多技術乾貨文章
qrcode

相關文章
相關標籤/搜索