可觀測性與 SLO ch08 接到 Spring 上,並算一次完整的帳
CH 08 落地與收攏

接到 Spring 上,並算一次完整的帳

Micrometer 與 ActuatorURI 正規化JVM 與連線池指標trace_id 進日誌exemplarRED 與 USE 儀表板保留策略與成本三條主線收攏

前面七章講了方法論、資料模型、查詢、三本柱、SLO 與告警。這一章要做兩件事:

第一,把它們真正接到一個 Spring Boot 專案上。好消息是這件事比想像中簡單——Micrometer 與 OpenTelemetry 已經把大部分工作做完了,你需要做的決定其實只有幾個。而那幾個決定裡,有一個做錯就會重演 ch02 那場事故。

第二,算一次完整的帳。可觀測性很容易變成整個架構裡最貴的一套系統——它的成本會隨著服務數量、流量、與保留期同時成長,而它自己沒有 SLO 來約束自己。很多團隊是在收到帳單的那一天才第一次認真看這件事。

這一章的內容:

  • Spring Boot 的預設能給你什麼:不寫任何程式碼就有的那些指標。
  • URI 正規化:ch02 那個 cardinality 事故,在 Micrometer 裡最常見的形態。
  • 把 backend 那一軌的東西接出來:JVM、GC、連線池——backend ch03、ch04 講的機制,這裡講怎麼讓它們變成可觀測的。
  • 儀表板:RED 與 USE 的實際版面,以及儀表板腐化怎麼防。
  • 成本:三本柱各自的降本手段,以及該先砍哪一個。
  • 最後一節:把這一軌的三條主線收攏,並接回你的 SRE 路線。

這一章的最後一張助記卡是整軌的總結。

在 Spring Boot 3 專案上接可觀測性,第一步的投報比高到有點不合理:加兩個依賴、寫幾行設定,前面七章講的東西大部分就開始有資料了。

<!-- metrics -->
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
  <groupId>io.micrometer</groupId>
  <artifactId>micrometer-registry-prometheus</artifactId>
</dependency>

<!-- tracing(ch05)-->
<dependency>
  <groupId>io.micrometer</groupId>
  <artifactId>micrometer-tracing-bridge-otel</artifactId>
</dependency>
management:
  endpoints.web.exposure.include: health,prometheus
  metrics.tags:
    application: ${spring.application.name}   # 對應 OTel 的 service.name(ch05)
  tracing.sampling.probability: 1.0            # 全收,取樣交給 Collector 做
  otlp.tracing.endpoint: http://otel-collector:4318/v1/traces

不用寫程式就有的指標

指標對應到
http_server_requests_secondsRED 三個指標一次到齊(count/錯誤 status/histogram)
jvm_memory_used_bytes、jvm_gc_pause_secondsbackend ch04 的 GC 與記憶體
hikaricp_connections_*(active/pending/timeout)backend ch03 的連線池——USE 的飽和度
executor_*(queued/active/pool_size)執行緒池的飽和度
jdbc / cache / kafka 等視依賴自動註冊
hikaricp_connections_pending 是這份清單裡最該立刻放上儀表板的一個。ch01 的 USE 方法說過:「用滿」不可怕,「有人在排隊」才可怕;而 db ch07 也說過,連線池排隊會偽裝成「資料庫很慢」。這個指標是分辨兩者的關鍵。

三個要立刻改的預設值

management:
  metrics.distribution:
    percentiles-histogram:
      http.server.requests: true      # ① 產生 bucket,才能正確聚合分位數(ch03)
    slo:
      http.server.requests: 100ms,300ms,1s   # ② bucket 邊界對齊 SLO 門檻(ch03/ch06)
  observations.annotations.enabled: true      # ③ 啟用 @Observed(ch05 的手動 span)
不要開 percentiles(客戶端算好的分位數)——那是 ch02 說的 summary,它的分位數無法跨實例聚合。要開的是 percentiles-histogram(產生 bucket,由 PromQL 在查詢時算)。兩個設定名稱只差一個字,效果完全相反。

ch02 那場「一個 label 把 Prometheus 從 4GB 吃到 30GB」的事故,在 Spring 專案裡有一個非常具體的形態。

好消息:預設是安全的

Spring MVC 的自動埋點會用路徑模板當 uri tag:

GET /api/orders/8821  →  uri="/api/orders/{id}"   ✅ 有界

壞消息:三種情況會破功

1
手動埋點取了實際路徑——request.getRequestURI()。這是 ch02 那場事故的原文。
2
404 與非 Spring MVC 管理的路徑:找不到對應 handler 時,Spring 會把 uri 標成 NOT_FOUND(安全),但某些過濾器層的埋點、或 WebFlux 的特定寫法可能漏掉。而爬蟲與掃描器會製造大量隨機路徑——這是最常見的觸發來源。
3
自訂 tag 帶了高基數的值:
// ❌ 每個租戶一條時序,租戶數無上限
Counter.builder("orders.created").tag("tenant_id", tenantId)

// ✅ 分級:把無界的維度收成有界的
.tag("tenant_tier", tier)   // free / pro / enterprise
需要單一租戶的細節?那屬於 trace 與 log(ch01 的規則)。

兩道防線

// ① 應用端:設定 tag 上限(超過就合併成 "other")
@Bean
MeterFilter uriCardinalityLimit() {
    return MeterFilter.maximumAllowableTags(
        "http.server.requests", "uri", 100, MeterFilter.deny());
}

// ② 也可以直接丟掉不需要的指標
@Bean
MeterFilter denyActuatorMetrics() {
    return MeterFilter.denyNameStartsWith("http.server.requests.actuator");
}

加上 ch02 說的第三道防線:Prometheus 端的 sample_limit 與 metric_relabel_configs。三道防線裡至少要有兩道。

一個實用的上線前檢查:curl localhost:8080/actuator/prometheus | wc -l。
正常的 Spring Boot 服務大約在數百到兩三千行。如果它是幾萬行,你已經有 cardinality 問題了——而這個檢查只要五秒鐘,可以直接放進 CI。

ch01 說三本柱之間的橋比每一樣本身做得多好都重要。這一節是那些橋在 Spring 專案裡的具體接法。

橋一:trace_id 進日誌(ch04)

Spring Boot 3 + Micrometer Tracing 會自動把 traceId/spanId 放進 MDC,你只要在 pattern 裡取用:

<pattern>%d{HH:mm:ss} %-5level [%X{traceId:-},%X{spanId:-}] %logger{36} - %msg%n</pattern>

<!-- JSON encoder 則會自動把整個 MDC 展開成欄位(ch04 建議的做法) -->
<encoder class="net.logstash.logback.encoder.LogstashEncoder"/>

橋二:exemplar 把 metrics 接到 trace(ch05)

management:
  prometheus.metrics.export.properties:
    io.prometheus.exemplars.sampleIntervalMilliseconds: 1000

接好之後,Grafana 的延遲圖上會出現可點的小點,直接跳到那一次的 trace。Prometheus 端要啟用 exemplar 儲存(--enable-feature=exemplar-storage)。

橋三:三邊的名字要一致(ch05 的 semantic conventions)

這是最容易在建置初期做錯、事後最難改的一件事。
spring.application.name: order-api

→ metrics 的 tag:  application="order-api"
→ trace 的屬性:    service.name="order-api"
→ 日誌的欄位:      service="order-api"
三個名字必須完全相同,Grafana 才能自動從一條 trace 跳到對應的日誌。不一致的話你會退回複製貼上的年代——而且要改就是改所有服務。

橋四:非同步邊界(ch05 的失敗場景)

// 執行緒池:讓 context 跟著任務走
@Bean
TaskDecorator otelTaskDecorator() {
    return runnable -> {
        Context ctx = Context.current();
        return () -> { try (Scope s = ctx.makeCurrent()) { runnable.run(); } };
    };
}

// @Async 的 executor 也要套用同一個 decorator
// Kafka:producer inject / consumer extract(ch05)

驗證這四座橋(一次走完)

1
發一個會經過 MQ 的請求。
2
在 Grafana 的延遲圖上找到它,點 exemplar。
3
看 trace 有沒有跨過 MQ(ch05 的斷點檢查)。
4
從 trace 點到日誌,確認 traceId 撈得到兩邊服務的紀錄。

走不通的那一段,就是你還沒接好的那座橋。這個檢查應該在每次新服務上線時做一次。

指標有了,接下來的問題是怎麼擺。而這件事有一個很實際的目標:讓一個沒看過這個服務的人,在 30 秒內判斷出「它現在有沒有事」。

每個服務一張標準版面(由上而下)

第一列  SLO 與錯誤預算:目前達成率、預算剩餘、burn rate      ← 唯一該先看的
第二列  RED:QPS / 錯誤率 / 延遲分位數(含 SLO 門檻的參考線)
第三列  依賴:對下游服務、DB、快取的 RED
第四列  USE:連線池 pending、執行緒池佇列、JVM 堆、GC pause
第五列  最近部署的標記(annotation)
三個讓儀表板好用的細節:
① 在延遲圖上畫一條 SLO 門檻的參考線——沒有基準的數字沒有意義(ch06)。
② 把部署事件疊在圖上:「什麼時候開始變壞」與「什麼時候部署」對齊,是最快的因果線索。
③ 每個服務的版面完全一致(用 Grafana 的變數與同一份模板):換服務不用重新學怎麼看。

PromQL 一律引用 recording rule(ch03)

儀表板不要直接寫 histogram_quantile(...)——ch03 那場事故就是儀表板裡的查詢寫錯而沒人發現。正確寫法固化成 recording rule,儀表板只引用名字。

儀表板腐化:一個沒有告警的失效模式

服務改名、指標改名、標籤變更之後,儀表板不會報錯——它只會顯示「No data」或一條空線。而一張顯示錯誤資料的儀表板比沒有儀表板更危險,因為它會讓人做出有信心的錯誤判斷(ch01 講過)。

三個防護:
  • 每張儀表板要有 owner,沒有人認領的就刪掉。
  • 定期檢視「No data」的面板(Grafana 可以掃描)。
  • 事故檢討時固定問一句:「排查過程中,有沒有哪張圖是壞的、或誤導了我們?」

不要做的三件事

  • 不要一張圖放 20 條線:per-pod 的線放在下鑽的圖裡,總覽只放聚合值與 max by (pod)(後者能抓出 ch03 那個「單一實例異常被平均掉」的情況)。
  • 不要把所有服務塞進一張儀表板:一張總覽(每個服務一列 SLO 狀態)+ 每個服務一張詳細,是比較耐用的結構。
  • 不要複製貼上就生一張新的:那是儀表板數量失控的主要來源,而且複製的同時也複製了裡面寫錯的查詢。

徵狀

年度成本檢視時,財務部門標出了一筆異常:

  • 可觀測性平台的年度費用,超過了所有應用服務運算成本的總和。
  • 而同期的服務數量只成長了 40%,流量成長 60%——可觀測性成本卻成長了 380%。
  • 更尷尬的是:Grafana 上有 214 張儀表板,過去 90 天有被開啟過的只有 23 張。

診斷

把帳單拆開來看(這件事本身就該每季做一次):

項目佔比問題
日誌 ingest 與索引61%全部 INFO 以上都收、全欄位索引、一律保留 90 天
Trace 儲存22%head-based 取樣 100%,全部保留 30 天
Metrics14%430 萬條活躍時序,其中大量來自已下線的服務
其他3%—

根因:三個「預設值從來沒被檢視過」

1
日誌:從第一天起就是「全收、全索引、留 90 天」。當時只有 3 個服務,這個設定很合理;現在有 40 個服務,而沒有人回頭改過它。
2
Trace:取樣率 100% 是為了「開發階段方便」設的,上正式環境時沒有調整,也沒有部署 Collector 做 tail 取樣(ch05)。
3
Metrics:沒有人清理。已下線服務的時序、實驗性的指標、複製貼上產生的重複指標,全部還在保留期內佔位置(ch02 說過「時序只要在保留窗內出現過就佔著索引」)。
而最根本的原因是:可觀測性系統自己沒有被觀測。沒有人對「每個服務的日誌量」「時序成長率」「儀表板使用率」設任何指標或告警——於是它是整個架構裡唯一一個「只會長大、不會被檢視」的部分。

修法(照投報比排序)

1
日誌分級保留與取樣(ch04):存取日誌取樣 1%、DEBUG 不進平台、INFO 留 14 天、ERROR 留 90 天、冷資料轉物件儲存。降 65%,而排查能力幾乎沒有損失。
2
Trace 改 tail-based(ch05):錯誤與慢請求 100% 保留、正常的 1%。降 85%,而且「有問題的都留著」——排查能力反而變強。
3
Metrics 清理(ch02):用 TSDB Status 找出前 10 名,丟掉沒人查詢的高基數指標,對關鍵查詢建 recording rule 後縮短原始資料保留期。
4
刪掉 191 張沒人看的儀表板,剩下的指定 owner。
5
把「觀測可觀測性」變成常態:每個服務的日誌量、時序數成長率、trace 量、儀表板使用率——設指標、設成長率告警、每季檢視一次。

教訓

可觀測性系統的成本會隨服務數、流量、保留期三者相乘成長,而它自己沒有 SLO 來約束自己。
它需要的是與錯誤預算同一種思路的東西:一個明確的成本預算,以及「超出時要砍什麼」的事先約定。否則它會一直長,直到有一天財務部門來問。

降本的優先順序(投報比由高到低)

順序做什麼典型降幅排查能力的損失
①Trace 改 tail-based 取樣80~90%負的——有問題的全留,反而更好用
②存取日誌取樣、DEBUG 不進平台50~70%幾乎沒有
③日誌分級保留 + 冷儲存30~50%舊資料查詢變慢(但很少查)
④清理高基數與無人查詢的 metrics視情況幾乎沒有
⑤縮短 metrics 保留期 + recording rule中等長期趨勢的解析度降低
第一項幾乎總是最划算:head-based 100% 取樣是「花最多錢買最沒有針對性的資料」——因為 99% 的 trace 是正常請求,而你永遠不會去看它們(ch05)。

三樣不能砍的東西

  • SLI 相關的 metrics:它們是錯誤預算的原料,砍了等於失去 ch06 整套決策機制。而且 metrics 本來就是三者裡最便宜的。
  • 錯誤與慢請求的 trace 與日誌:那正是你會去看的部分。降本要砍的永遠是「正常」的資料。
  • 稽核與合規紀錄:它們有法定保留期,而且通常不能取樣。

把成本變成可管理的東西

① 每個服務設 ingest 配額(ch04)——同時防成本與防單一服務拖垮平台
② 對「日誌量」「時序數」的成長率設告警——不是絕對值,是斜率
③ 每季檢視:帳單拆解、TSDB top 10、儀表板使用率
④ 新服務上線的檢查表裡加一項:預估它會產生多少可觀測性資料

一個判斷保留期的簡單問題

「上一次我查超過 X 天前的日誌,是什麼時候?」
多數團隊的答案是「想不起來」——那 X 就可以砍。而真正需要長期保留的,通常是 metrics(趨勢與容量規劃)而不是日誌,偏偏日誌才是最貴的那一個。這個錯配是最常見的成本問題。

把整軌收成一份可以直接跟著做的清單。新服務上線時照這個順序走,一天之內可以完成大部分。

第一天:能看見

□ actuator + micrometer-registry-prometheus,暴露 /actuator/prometheus
□ spring.application.name 設好,且與 trace、log 的服務名一致
□ percentiles-histogram: true(不是 percentiles)
□ SLO 門檻加進 bucket 邊界(例如 100ms,300ms,1s)
□ curl /actuator/prometheus | wc -l 檢查行數(幾萬行 = 有 cardinality 問題)
□ Prometheus 端加 sample_limit

第二天:能追蹤、能關聯

□ micrometer-tracing-bridge-otel,指向 Collector
□ logback JSON encoder,pattern 帶 traceId/spanId
□ 執行緒池套 TaskDecorator、MQ 兩端做 inject/extract
□ 開 exemplar
□ 走一次驗證:metrics → 點 exemplar → trace → 用 trace_id 查 log

第一週:能判斷、能通知

□ 定義 1~2 條關鍵旅程的 SLI(好事件/有效事件形式)
□ 觀察 4 週後再訂 SLO(先別急著訂數字)
□ 正確查詢固化成 recording rule
□ 一張標準版面的儀表板(SLO / RED / 依賴 / USE / 部署標記)
□ burn rate 多視窗告警 + runbook
□ dead man's switch
□ 每條 page 級告警都問過那七個檢查(ch07)

常見的順序錯誤

  • 先做 trace 再做 metrics:連「有沒有事」都不知道時,trace 沒有入口(ch05)。
  • 先訂 SLO 再看資料:拍腦袋訂的數字會變成 ch06 那場「永遠紅但沒人管」的事故。
  • 先做儀表板再做告警:儀表板需要有人主動去看,而告警不用。如果只能做一個,做告警。
  • 一次幫所有服務做:先在一個服務上把整條動線走通,再複製。模板做對了,後面 39 個服務是複製貼上;模板做錯了,那就是複製 39 份錯誤。

這一軌從「儀表板全綠但使用者在客訴」開始,走到 Spring 專案的設定檔。八章底下其實只有三件事。

① 測量本身有成本

可觀測性不是「收集越多越好」——每一種資料都在某個維度上付錢:

  • ch02:metrics 的成本是維度的乘法,一個 label 就能讓時序數翻幾百倍。
  • ch04:日誌的成本是流量的加法,而故障時流量最大。
  • ch05:trace 的成本是 span 數 × 取樣率,而 head-based 取樣會丟掉你最需要的那些。
  • ch07:告警的成本是人的注意力——最貴、最容易耗損、而且不會出現在帳單上。
  • 本章:這些成本會隨服務數、流量、保留期相乘成長,而它自己沒有 SLO 約束自己。

② 症狀優先於原因

觀測點要站在使用者的位置,而不是機器的位置:

  • ch01:白箱說一切正常時,要有一個站在使用者位置的觀測點來反駁它(TLS 那場事故)。
  • ch06:SLI 從使用者旅程推導,不從機器指標推導;SLO 的驗證方式是「上次事故它有掉下來嗎」。
  • ch07:對徵狀告警不對原因告警——使用者有沒有受影響,是唯一該叫醒人的理由。
  • ch03:儀表板與使用者體驗矛盾時,先懷疑查詢,不要先懷疑使用者。

③ 三本柱是一條路徑,不是三個工具

metrics 發現異常 → trace 定位到哪一段 → log 確認細節。而三者之間的橋(trace_id、exemplar、統一的 service.name),比每一樣本身做得多好都重要——這條動線走不通的那一段,就是還沒接好的地方。

還有一條沒明說的線

可觀測性天生看不到「沒有發生的事」。
ch01 的請求根本沒進來、ch03 的排程沒被觸發、ch05 的 trace 斷在非同步邊界、ch07 的監控系統自己掛掉——它們的共同形狀都是「什麼都沒有」,而「什麼都沒有」與「一切正常」在儀表板上長得一模一樣。

對付它只有一種方法:對「應該出現卻沒出現」設告警(absent_over_time、流量異常下降、dead man's switch、孤兒 span 佔比)。這一類防護幾乎都很便宜,而且幾乎都被遺漏。

這一軌在你的 SRE 路線上的位置

這是 SRE 補強計畫的第一軌(見 MIGRATION.md Step 11),而它被排在第一位是因為後面每一軌都會用到它的語言:

  • Kubernetes:資源模型與驅逐要看飽和度指標、Pod 排查要看事件與探針——都是這一軌的方法在容器編排層的應用。
  • Linux 效能診斷:USE 方法(ch01)就是那一軌的核心框架。
  • CI/CD:金絲雀發布要靠 SLI 判斷「這批要不要繼續放量」,自動回滾的依據就是 burn rate(ch06)。
而你既有的優勢——從 DB 到 JVM 到連線池的往下鑽診斷能力——在這一軌之後有了出口:
以前你能查出「這句 SQL 為什麼慢」,現在你能回答「使用者受到多少影響、值不值得現在處理、以及該不該為此停下發版」。

那個轉換,就是工程師與 SRE 的差別。
Spring Boot 的可觀測性設定:預設值與該改的地方
設定預設該改成理由
percentiles-histogramfalsetrue產生 bucket 才能跨實例正確聚合分位數(ch03)
percentiles未設不要開那是客戶端算好的分位數(summary),無法跨實例聚合(ch02)
distribution.slo未設填入 SLO 門檻值bucket 邊界對齊門檻,「≤門檻的比例」變精確值(ch03/ch06)
tracing.sampling.probability0.11.0 + Collector 做 tail 取樣head 取樣會丟掉罕見錯誤(ch05)
spring.application.name未設必設,且三邊一致metrics/trace/log 的服務名不一致就無法自動關聯(ch05)
open-in-viewtruefalse與可觀測性無關但同樣是預設值陷阱(db ch08)
降本順序:投報比與排查能力的損失
順序做什麼典型降幅排查能力損失
①Trace 改 tail-based 取樣80~90%負的——有問題的全留,更好用
②存取日誌取樣、DEBUG 不進平台50~70%幾乎沒有
③日誌分級保留 + 冷儲存30~50%舊資料查詢變慢(但很少查)
④清理高基數與無人查詢的 metrics視情況幾乎沒有
不能砍SLI 指標、錯誤與慢請求的 trace/log、稽核紀錄—砍了等於失去決策機制與排查對象
三條主線在八章裡的分布
主線在哪幾章展開一句話
測量本身有成本ch02 乘法 / ch04 加法 / ch05 取樣 / ch07 注意力 / ch08 相乘每一種資料都在某個維度上付錢,而告警付的是最貴的那一種
症狀優先於原因ch01 黑箱 / ch03 先懷疑查詢 / ch06 SLI / ch07 徵狀告警觀測點要站在使用者的位置,不是機器的位置
三本柱是一條路徑ch01 動線 / ch04 trace_id / ch05 exemplar / ch08 四座橋橋比每一樣本身做得多好都重要
(未明說)看不到沒發生的事ch01 請求沒進來 / ch03 absent / ch05 斷掉的 trace / ch07 watchdog「什麼都沒有」與「一切正常」長得一模一樣

練習題 點選選項查看解析

0 / 10
01 / 10
Spring Boot 的 metrics 設定中,percentiles 與 percentiles-histogram 該開哪一個?
A 兩個都開,資訊比較完整
B 只開 percentiles-histogram——percentiles 是客戶端算好的分位數(summary),無法跨實例聚合
C 只開 percentiles,因為它比較精確
D 都不用開,Prometheus 會自動處理
解析
兩個設定名稱只差一個字,效果完全相反。percentiles 對應 ch02 說的 summary——10 個 pod 各自的 p99 合不出整個服務的 p99,而且事後補不回來。要開的是 percentiles-histogram,讓 PromQL 在查詢時用 histogram_quantile 正確聚合。
02 / 10
Spring MVC 的 uri tag 預設是安全的(用路徑模板),什麼情況會破功?
A 流量太大時
B 手動埋點取了 request.getRequestURI()、爬蟲製造大量隨機路徑、或自訂 tag 帶了 tenant_id 這類無界的值
C 使用 WebFlux 時一定會
D 路徑超過三層時
解析
這是 ch02 那場事故在 Spring 專案裡的具體形態。防線有三道:應用端用 MeterFilter.maximumAllowableTags 限制、Prometheus 端的 sample_limit、以及 metric_relabel_configs。至少要有兩道。
03 / 10
一個五秒鐘就能做、且可以放進 CI 的 cardinality 檢查是什麼?
A 檢查 Prometheus 的記憶體用量
B curl /actuator/prometheus | wc -l——正常服務數百到兩三千行,如果是幾萬行就已經有問題了
C 查詢 prometheus_tsdb_head_series
D 檢查 Grafana 的查詢延遲
解析
它的價值在於「在上線之前就發現」——ch02 那場事故是上線兩週後才被發現的,而那時已經累積了 380 萬條時序。這種便宜的前置檢查比事後的告警更有效。
04 / 10
為什麼 spring.application.name 與 trace 的 service.name、日誌的 service 欄位必須完全一致?
A 為了符合命名規範
B 因為那是 Grafana 能不能自動從一條 trace 跳到對應日誌的關鍵——不一致就退回複製貼上,而事後要改就是改所有服務
C 為了讓指標名稱更短
D 因為 Prometheus 會拒絕不一致的資料
解析
這是 ch05 semantic conventions 的核心。它在建置初期定好幾乎沒有成本,事後統一則要動所有服務——屬於典型的「早做很便宜、晚做很貴」的決定。
05 / 10
在 Spring 專案裡,哪一個指標最該立刻放上儀表板,用來分辨「資料庫慢」與「連線池排隊」?
A jvm_memory_used_bytes
B hikaricp_connections_pending——USE 方法說「用滿」不可怕,「有人在排隊」才可怕
C http_server_requests_seconds_count
D jvm_gc_pause_seconds
解析
這串起了三條線:ch01 的 USE 方法(使用率與飽和度要拆開)、backend ch03 的連線池機制、db ch07 的分流(應用端延遲高但 DB 的 Threads_running 低=問題不在 DB)。
06 / 10
驗證「三本柱的橋」有沒有接好,該怎麼做?
A 檢查三個系統各自是否有資料
B 發一個會經過 MQ 的請求,然後從 metrics 點 exemplar → 看 trace 有沒有跨過 MQ → 從 trace 用 trace_id 查到兩邊服務的日誌
C 檢查設定檔是否正確
D 查看 Collector 的日誌
解析
走不通的那一段就是還沒接好的那座橋。這個檢查應該在每次新服務上線時做一次——因為斷點(尤其非同步邊界)不會有任何錯誤訊息,只會安靜地少一段。
07 / 10
為什麼「顯示錯誤資料的儀表板比沒有儀表板更危險」?
A 因為它浪費儲存空間
B 因為它會讓人做出有信心的錯誤判斷——服務改名、指標改名後儀表板不會報錯,只會顯示 No data 或空線
C 因為它會拖慢 Grafana
D 因為它會產生額外的查詢成本
解析
儀表板腐化是一個沒有告警的失效模式。三個防護:每張圖有 owner(沒人認領就刪)、定期掃描 No data 的面板、事故檢討時固定問「排查過程中有沒有哪張圖是壞的或誤導了我們」。
08 / 10
可觀測性降本,投報比最高的第一步是什麼?
A 縮短所有資料的保留期
B Trace 改成 tail-based 取樣——降 80~90%,而且排查能力是提升的(有問題的全留)
C 關閉 DEBUG 日誌
D 減少 metrics 的抓取頻率
解析
head-based 100% 取樣是「花最多錢買最沒有針對性的資料」——99% 的 trace 是正常請求,你永遠不會去看它們。降本要砍的永遠是「正常」的資料,而不能砍的是 SLI 指標、錯誤與慢請求的 trace/log、以及稽核紀錄。
09 / 10
本章那場「可觀測性成本超過運算成本」的事故,最根本的原因是什麼?
A 選錯了平台供應商
B 可觀測性系統自己沒有被觀測——沒有人對每個服務的日誌量、時序成長率、儀表板使用率設任何指標或告警
C 服務數量成長太快
D 工程師寫了太多日誌
解析
於是它是整個架構裡唯一一個「只會長大、不會被檢視」的部分。三個預設值(日誌全收留 90 天、trace 100% 取樣、metrics 從不清理)都是在只有 3 個服務時很合理、到 40 個服務時沒人回頭改的設定。
10 / 10
導入可觀測性時,最常見的順序錯誤是什麼?
A 先做 metrics 再做日誌
B 一次幫所有服務做——應該先在一個服務上把整條動線走通再複製,否則模板做錯就是複製 39 份錯誤
C 先接 Collector 再接應用程式
D 先設定告警再做儀表板
解析
其他常見錯誤:先做 trace 再做 metrics(連有沒有事都不知道時 trace 沒有入口)、先訂 SLO 再看資料(拍腦袋的數字會變成 ch06 那場事故)、先做儀表板再做告警(儀表板需要人主動看,告警不用——只能做一個就做告警)。

點擊卡片翻面查看答案,共 13 張。

QUESTION
Spring Boot 專案接可觀測性,三個要立刻改的預設值是什麼?
點擊翻面
ANSWER
①percentiles-histogram 開成 true(不是 percentiles,那是無法跨實例聚合的 summary)②distribution.slo 填入 SLO 門檻讓 bucket 邊界對齊 ③tracing 取樣設 1.0 並交給 Collector 做 tail 取樣。
點擊翻回
QUESTION
Spring Boot 不用寫程式就有的四類指標是什麼?
點擊翻面
ANSWER
http_server_requests_seconds(RED 一次到齊)、jvm_*(記憶體與 GC,backend ch04)、hikaricp_connections_*(連線池,backend ch03)、executor_*(執行緒池佇列)。
點擊翻回
QUESTION
哪一個 Spring 指標最該用來分辨「DB 慢」與「連線池排隊」?
點擊翻面
ANSWER
hikaricp_connections_pending。USE 方法說「用滿」不可怕、「有人排隊」才可怕;而 db ch07 的分流也是同一件事——應用端延遲高但 DB 的 Threads_running 低,代表問題不在 DB。
點擊翻回
QUESTION
Micrometer 裡 cardinality 爆炸的三種來源?
點擊翻面
ANSWER
①手動埋點取 request.getRequestURI() 而非路徑模板 ②爬蟲/掃描器製造大量隨機路徑 ③自訂 tag 帶無界的值(tenant_id、order_id)——後者要改成分級(tenant_tier)。
點擊翻回
QUESTION
一個五秒就能做、可放進 CI 的 cardinality 檢查是什麼?
點擊翻面
ANSWER
curl /actuator/prometheus | wc -l。正常服務數百到兩三千行,幾萬行就代表已經有問題——價值在於上線前就發現,而不是像 ch02 那樣兩週後才爆。
點擊翻回
QUESTION
三本柱之間的四座橋是什麼?
點擊翻面
ANSWER
①trace_id 進日誌(MDC + logback pattern)②exemplar 把 metrics 接到 trace ③三邊的 service.name 完全一致 ④非同步邊界的 context 傳播(TaskDecorator、MQ inject/extract)。
點擊翻回
QUESTION
怎麼驗證那四座橋真的通了?
點擊翻面
ANSWER
發一個會經過 MQ 的請求 → 在延遲圖上點 exemplar → 看 trace 有沒有跨過 MQ → 從 trace 用 trace_id 查到兩邊服務的日誌。走不通的那一段就是還沒接好的橋。
點擊翻回
QUESTION
服務儀表板的標準版面由上而下是什麼?
點擊翻面
ANSWER
①SLO 與錯誤預算(唯一該先看的)②RED(含 SLO 門檻參考線)③依賴的 RED ④USE(連線池 pending、佇列、堆、GC)⑤部署標記。每個服務版面完全一致,換服務不用重新學。
點擊翻回
QUESTION
為什麼「顯示錯誤資料的儀表板比沒有更危險」?怎麼防?
點擊翻面
ANSWER
它讓人做出有信心的錯誤判斷,而且腐化時不會報錯(只顯示 No data)。防護:每張圖有 owner、定期掃描 No data 面板、事故檢討固定問「有沒有哪張圖誤導了我們」。
點擊翻回
QUESTION
可觀測性降本的第一順位是什麼?為什麼?
點擊翻面
ANSWER
Trace 改 tail-based 取樣,降 80~90% 而排查能力反而提升。因為 head-based 100% 是「花最多錢買最沒針對性的資料」——99% 的 trace 是正常請求,你永遠不會看它們。
點擊翻回
QUESTION
降本時哪三樣不能砍?
點擊翻面
ANSWER
①SLI 相關的 metrics(錯誤預算的原料,而且 metrics 本來就最便宜)②錯誤與慢請求的 trace 與日誌(那正是你會看的)③稽核與合規紀錄(有法定保留期且通常不能取樣)。
點擊翻回
QUESTION
「可觀測性成本超過運算成本」的根本原因是什麼?
點擊翻面
ANSWER
可觀測性系統自己沒有被觀測——沒人對日誌量、時序成長率、儀表板使用率設指標。於是它是整個架構裡唯一「只會長大、不會被檢視」的部分。它需要跟錯誤預算同一種思路的成本預算與事先約定。
點擊翻回
QUESTION
這一軌的三條主線是什麼?還有哪一條沒明說?
點擊翻面
ANSWER
①測量本身有成本(metrics 乘法/日誌加法/trace 取樣/告警吃注意力,且會相乘成長)②症狀優先於原因(觀測點站在使用者位置,儀表板與體驗矛盾時先懷疑查詢)③三本柱是一條路徑(metrics 發現→trace 定位→log 確認,橋比每樣本身更重要)。沒明說的第四條:可觀測性天生看不到「沒發生的事」——只能靠對「應該出現卻沒出現」設告警(absent_over_time、流量下降、dead man's switch、孤兒 span)。
點擊翻回