跳至內容
返回

後端仔的效能優化實戰(2)、Spring Boot Actuator

發佈於:  at  08:00 上午

前言

旁白: 有一個人前來採購系統

(尖銳的機車剎車聲)

你老闆:哥們,你這系統能承受多少RPS啊?

你: 5000RPS一天

你老闆: What’s up…你這系統是API是金子做的、還是外部模組是金子做的?

經他這麼一問,你無言以對,你想說這是大棚的系統,但你老闆也並非華強這般好唬弄,你需要多一點資訊。若你不知道你的系統運作,毫無疑問是致命的。除了系統架構,當我們今天要進行效能優化時,必須要有方法測量系統當前的運行狀況,而使用的工具,便是我這次要提到的Spring Actuator。

Spring Actuator 是什麼?

Spring Boot ActuatorSpring Boot官方提供的生產環境(Production)監控與管理模組。它的核心價值是只要加入一個依賴、做點設定,Spring Boot就會自動幫你把應用程式的內部狀態曝露成一系列可以直接存取的HTTP endpoints(或透過JMX)。

要用它,第一步先引入 spring-boot-starter-actuator 這個依賴:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

光是加入依賴什麼都不設定的話,Actuator預設只會曝露 /actuator/health/actuator/info 這兩個最無害的endpoint。 但我們需要詳細觀測的話,就必須要一一手動設定我們想要開放哪些指標。

management:
  endpoints:
    web:
      exposure:
        include: health, metrics, env, beans, loggers, threaddump, prometheus
      base-path: /actuator # 預設就是 /actuator,可省略

  endpoint:
    health:
      show-details: always # 預設是 never,不然 /health 只回 {"status":"UP"} 看不到細節
    shutdown:
      enabled: false # 這個很危險,不要輕易打開

  server:
    port: 9090 # 建議獨立 port,跟業務 API 分開,方便做網路隔離

  metrics:
    tags:
      application: ${spring.application.name} # 讓 Prometheus 抓到的 metrics 有辨識度

設定好之後,你就有了一整排可以查詢系統狀態的endpoint。以下是幾個最常用的:

Endpoint用途
/actuator/health應用程式健康狀態(DBRedis、磁碟空間等),常被拿來當作Kubernetes的存活探針(Liveness/Readiness Probe)
/actuator/metrics各種效能指標(JVM MemoryHTTP Request次數、GC次數等),也就是上一篇那些指標的實際來源
/actuator/env目前環境變數與設定值,排查問題很好用
/actuator/beans所有 Spring Bean 清單,確認某個 Bean 有沒有被正確載入
/actuator/loggers動態調整 log level,不用重啟服務就能臨時打開某個套件的 DEBUG
/actuator/threaddumpThread Dump,排查Deadlock、抓「某支 Thread 卡在哪」時很有用
/actuator/prometheus輸出 Prometheus 抓取用的格式(需搭配 Micrometer),下一篇做視覺化會用到

提醒一件事:Actuator不是開得越多越好。這些endpoint幾乎都是機密性質,一旦外洩攻擊者就能對你的系統瞭若指掌。 所以正式環境的做法通常是——用獨立的 management.server.portActuator跟業務API分開,再搭配防火牆或Spring Security限制只有內網、或特定身分才能存取(這部分我們後面實戰會實際做一次)。

除了用來看的指標,Actuator也有少部分endpoint是可以直接修改系統行為的。最經典的例子就是 /actuator/loggers——你可以在服務不重啟的情況下,動態把某個套件的 log level 臨時調高,排查完再調回去:

# 查詢目前 com.example.demo 這個 package 的 log level
curl http://localhost:9090/actuator/loggers/com.example.demo
# 查詢目前 com.example.demo.DemoController 這個 class 的 log level
curl http://localhost:9090/actuator/loggers/com.example.demo.DemoController

# 動態調成 DEBUG,馬上生效,不用重啟服務
curl -X POST http://localhost:9090/actuator/loggers/com.example.demo \
  -H "Content-Type: application/json" \
  -d '{"configuredLevel": "DEBUG"}'

這種「線上急救」的能力,在生產環境噴錯、又不能重啟服務時特別實用——先開 DEBUG 看清楚問題,確認好之後再改回 INFO,全程不用重新部署。

這些指標對於第一次接觸的人來說可以說錯綜複雜,我們需要一個更簡易的方式來檢視這些指標,也就是透過Prometheus與Grafana。

Prometheus 是什麼?

Prometheus是一套開源的時間序列資料庫(Time Series Database),同時也是監控系統的事實標準之一。它的運作模式是Pull(拉取)方式,Prometheus每隔固定時間(例如15秒),就會自己跑去每個服務的某個endpoint把當下的指標抓回來,存成一筆帶時間戳記的資料。

而這個「某個endpoint」,剛好就是我們前面表格裡提到的 /actuator/prometheus。不過光有 spring-boot-starter-actuator 還不夠,這個endpoint預設是不存在的,你還要額外加一個依賴,讓Micrometer(Spring Boot內建的指標門面)把資料轉成Prometheus看得懂的格式:

<dependency>
    <groupId>io.micrometer</groupId>
    <artifactId>micrometer-registry-prometheus</artifactId>
</dependency>

加完之後,打 /actuator/prometheus 就會看到一大串像這樣的純文字格式資料:

http_server_requests_seconds_bucket{uri="/api/v1/ticket",le="0.1",} 823.0
http_server_requests_seconds_bucket{uri="/api/v1/ticket",le="0.5",} 910.0
http_server_requests_seconds_count{uri="/api/v1/ticket",} 915.0
http_server_requests_seconds_sum{uri="/api/v1/ticket",} 42.183

接著你要告訴Prometheus去哪裡抓這份資料,這是透過它自己的設定檔 prometheus.yml

scrape_configs:
  - job_name: "ticket-service"
    metrics_path: "/actuator/prometheus"
    scrape_interval: 15s
    static_configs:
      - targets: ["localhost:9090"] # 你的 management.server.port

設定好、Prometheus跑起來之後,它就會每15秒去敲一次 /actuator/prometheus,把當下的數字存進自己的時間序列資料庫,讓你可以用PromQL(Prometheus專屬的查詢語言)回頭做各種分析——例如算出過去5分鐘的P95延遲、某支APIQPSJVM記憶體用量趨勢等等。

Grafana 是什麼?

如果說Prometheus負責「收集資料、存起來、讓你查」,那Grafana負責的就是「把查出來的資料畫成人看得懂的圖」。它本身不儲存資料,而是接上Prometheus當作Data Source,讓你用PromQL寫查詢,再把結果畫成折線圖、儀表板、告警規則。

其他常用指標

這部分很多,以下列舉一些常見的:

# 同時段 QPS,排除純粹是流量上升造成的
sum(rate(http_server_requests_seconds_count{uri="/api/v1/ticket"}[5m]))

# HikariCP 連線池使用率,看是否連線耗盡
hikaricp_connections_active / hikaricp_connections_max

# JVM GC 暫停時間,看是否 GC 造成延遲飆升
rate(jvm_gc_pause_seconds_sum[5m])

# CPU 使用率(這支 JVM process 自己吃了多少 CPU)
process_cpu_usage * 100

# CPU 使用率(整台主機層級),跟上面那條要分清楚是「誰在吃」
system_cpu_usage * 100

# JVM Heap Memory 使用率,看是不是快被吃滿、要不要調 -Xmx
sum(jvm_memory_used_bytes{area="heap"}) / sum(jvm_memory_max_bytes{area="heap"}) * 100

# 依 GC 世代分別看 Memory 用量(Eden、Old Gen 等),排查記憶體洩漏很有用
jvm_memory_used_bytes{area="heap"}

# JVM 存活執行緒數,暴增通常代表 Thread Pool 設定有問題或有 Thread 洩漏
jvm_threads_live_threads

# 磁碟剩餘空間比例,太低會導致 /actuator/health 直接回 DOWN
disk_free_bytes / disk_total_bytes * 100

# HTTP 5xx 錯誤率,跟延遲一起看,才知道系統是「慢」還是「壞」
sum(rate(http_server_requests_seconds_count{status=~"5.."}[5m]))
  / sum(rate(http_server_requests_seconds_count[5m])) * 100

如果QPS沒有明顯變化,但延遲卻持續飆高,八成問題出在連線池或快取,而不是單純流量太大——這也是Metrics視覺化真正的價值:讓你在半夜噴錯之前,就先看到警訊。這些指標非常多,我建議就是存個小抄,等到需要的時候拿來查就好了,不用都背起來。

總結就是──Spring Actuator+Grafana+Prometheus,透過這三者一起使用,我們就可以找出系統到底哪裡是瓶頸。下一個單元我們就要帶入實際場景,示範幾個實際的案例,那麼本篇文章就到此為止了。


建議修改
在以下平台分享此文章:

上一篇
後端仔的效能優化實戰(3)、Grafana與Prometheus查詢案例
下一篇
後端仔的效能優化實戰(1)、觀測問題