前言
旁白: 有一個人前來採購系統
(尖銳的機車剎車聲)
你老闆:哥們,你這系統能承受多少RPS啊?
你: 5000RPS一天
你老闆: What’s up…你這系統是API是金子做的、還是外部模組是金子做的?
經他這麼一問,你無言以對,你想說這是大棚的系統,但你老闆也並非華強這般好唬弄,你需要多一點資訊。若你不知道你的系統運作,毫無疑問是致命的。除了系統架構,當我們今天要進行效能優化時,必須要有方法測量系統當前的運行狀況,而使用的工具,便是我這次要提到的Spring Actuator。
Spring Actuator 是什麼?
Spring Boot Actuator是Spring 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 | 應用程式健康狀態(DB、Redis、磁碟空間等),常被拿來當作Kubernetes的存活探針(Liveness/Readiness Probe) |
/actuator/metrics | 各種效能指標(JVM Memory、HTTP Request次數、GC次數等),也就是上一篇那些指標的實際來源 |
/actuator/env | 目前環境變數與設定值,排查問題很好用 |
/actuator/beans | 所有 Spring Bean 清單,確認某個 Bean 有沒有被正確載入 |
/actuator/loggers | 動態調整 log level,不用重啟服務就能臨時打開某個套件的 DEBUG |
/actuator/threaddump | Thread Dump,排查Deadlock、抓「某支 Thread 卡在哪」時很有用 |
/actuator/prometheus | 輸出 Prometheus 抓取用的格式(需搭配 Micrometer),下一篇做視覺化會用到 |
提醒一件事:Actuator不是開得越多越好。這些endpoint幾乎都是機密性質,一旦外洩攻擊者就能對你的系統瞭若指掌。
所以正式環境的做法通常是——用獨立的 management.server.port 把Actuator跟業務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延遲、某支API的QPS、JVM記憶體用量趨勢等等。
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,透過這三者一起使用,我們就可以找出系統到底哪裡是瓶頸。下一個單元我們就要帶入實際場景,示範幾個實際的案例,那麼本篇文章就到此為止了。