凌晨四點,線上CPU告警,績效沒了!

👉 這是一個或許對你有用的社群
🐱 一對一交流/面試小冊/簡歷最佳化/求職解惑,歡迎加入芋道快速開發平臺知識星球。下面是星球提供的部分資料:
👉這是一個或許對你有用的開源專案
國產 Star 破 10w+ 的開源專案,前端包括管理後臺 + 微信小程式,後端支援單體和微服務架構。
功能涵蓋 RBAC 許可權、SaaS 多租戶、資料許可權、商城、支付、工作流、大屏報表、微信公眾號、ERPCRMAI 大模型等等功能:
  • Boot 多模組架構:https://gitee.com/zhijiantianya/ruoyi-vue-pro
  • Cloud 微服務架構:https://gitee.com/zhijiantianya/yudao-cloud
  • 影片教程:https://doc.iocoder.cn
【國內首批】支援 JDK 17/21 + SpringBoot 3.3、JDK 8/11 + Spring Boot 2.7 雙版本 

前言

凌晨4點,我被一陣刺耳的手機鈴聲驚醒。迷迷糊糊地摸索著手機,螢幕上赫然顯示著"線上CPU告警"的字樣。瞬間,我的睏意全無,取而代之的是一陣冷汗和心跳加速。作為公司核心系統的負責人,我深知這意味著什麼——使用者體驗受損、可能的資料丟失,更糟糕的是,我的年終績效可能就此化為泡影。
我迅速起身,開始了一場與時間賽跑的故障排查之旅。
基於 Spring Boot + MyBatis Plus + Vue & Element 實現的後臺管理系統 + 使用者小程式,支援 RBAC 動態許可權、多租戶、資料許可權、工作流、三方登入、支付、簡訊、商城等功能
  • 專案地址:https://github.com/YunaiV/ruoyi-vue-pro
  • 影片教程:https://doc.iocoder.cn/video/

1. 初步診斷:快速定位問題

首先,我登入了伺服器,使用top命令檢視系統資源使用情況:

$ top

輸出顯示CPU使用率接近100%,load average遠超伺服器核心數。這確實是一個嚴重的問題。
接下來,我使用htop命令獲取更詳細的程序資訊:

$ htop

我發現有幾個Java程序佔用了大量CPU資源。這些程序正是我們的核心服務。
基於 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 實現的後臺管理系統 + 使用者小程式,支援 RBAC 動態許可權、多租戶、資料許可權、工作流、三方登入、支付、簡訊、商城等功能
  • 專案地址:https://github.com/YunaiV/yudao-cloud
  • 影片教程:https://doc.iocoder.cn/video/

2. JVM層面分析:尋找熱點方法

確定了問題出在Java應用上,我開始進行JVM層面的分析。首先使用jstat命令檢視GC情況:

$ jstat -gcutil [PID] 1000 10

輸出顯示Full GC頻繁發生,這可能是導致CPU使用率高的原因之一。
接著,我使用jstack命令生成執行緒轉儲,檢視執行緒狀態:

$ jstack [PID] > thread_dump.txt

分析thread dump檔案,我發現大量執行緒處於RUNNABLE狀態,執行著相似的方法呼叫。
為了進一步定位熱點方法,我使用了async-profiler工具:

$ ./profiler.sh -d 30 -f cpu_profile.svg [PID]

生成的火焰圖清晰地顯示了一個自定義的排序算法佔用了大量CPU時間。

3. 應用層面最佳化:重構演算法

找到了罪魁禍首,我立即查看了相關程式碼。這是一個用於大量資料的自定義排序演算法,原本設計用於小規模資料,但隨著業務增長,它的效能問題暴露無遺。
我迅速重構了演算法,使用Java 8的並行流進行最佳化:

List<Data> sortedData = data.parallelStream()

    .sorted(Comparator.comparing(Data::getKey))

    .collect(Collectors.toList());

同時,我添加了快取機制,避免重複計算:
@Cacheable

(

"sortedData"

)

public List<Data> getSortedData()

{

// 最佳化後的排序邏輯

}

4. 資料庫最佳化:索引與查詢改進

在排查過程中,我還發現了一些低效的資料庫查詢。使用explain命令分析SQL語句:
EXPLAINSELECT

 * 

FROM

 large_table 

WHEREstatus

 = 

'ACTIVE'

;

結果顯示這個查詢導致了全表掃描。我立即添加了合適的索引:
CREATEINDEX

 idx_status 

ON

 large_table(

status

);

並重寫了部分ORM查詢,使用更高效的原生SQL:
@Query

(value = 

"SELECT * FROM large_table WHERE status = :status"

, nativeQuery = 

true

)

List<LargeTable> findByStatus(@Param("status") String status)

;

5. 部署最佳化:資源隔離

為了防止單個服務影響整個系統,我決定使用Docker進行資源隔離。建立瞭如下的Dockerfile:

FROM openjdk:11-jre-slim

COPY target/myapp.jar app.jar

ENTRYPOINT [

"java"

"-Xmx2g"

"-jar"

"/app.jar"

]

並使用Docker Compose進行服務編排,限制了CPU和記憶體使用:
version:'3'
services:
myapp:
build:.
deploy:
resources:
limits:
cpus:'0.50'
memory:512M

6. 監控告警:防患未然

最後,為了避免類似問題再次發生,我升級了監控系統。使用Prometheus和Grafana搭建了全面的監控平臺,並設定了更加智慧的告警規則:
-alert:HighCPUUsage
expr:100-(avgby(instance)(rate(node_cpu_seconds_total{mode="idle"}[5m]))*100)>80
for:5m
labels:
severity:warning
annotations:
summary:"High CPU usage detected"
description:"CPU usage is above 80% for more than 5 minutes"

結語:危機與成長

經過近4小時的奮戰,系統終於恢復了正常。CPU使用率降到了30%以下,服務響應時間也恢復到了毫秒級。
這次經歷讓我深刻意識到,在追求業務快速發展的同時,我們不能忽視技術債務的累積。定期的程式碼審查、效能測試和壓力測試是必不可少的。同時,建立完善的監控和告警機制,能夠幫助我們更快地發現和解決問題。
雖然這次事件可能會影響我的年終績效,但它帶給我的經驗和教訓是無價的。持續學習和改進永遠是我們的必修課。
凌晨的陽臺上,我望著漸亮的天空,心中暗自慶幸:又一次化險為夷。但我知道,明天將是新的挑戰,我們還有很長的路要走。

歡迎加入我的知識星球,全面提升技術能力。
👉 加入方式,長按”或“掃描”下方二維碼噢
星球的內容包括:專案實戰、面試招聘、原始碼解析、學習路線。
文章有幫助的話,在看,轉發吧。
謝謝支援喲 (*^__^*)

相關文章