健康檢查與診斷 endpoint 的核心差異是使用者與風險不同。/health 給監控或負載平衡器判斷 process 是否活著,/ready 判斷是否應接流量,/debug/... 則給工程師排查問題且必須限制存取。

本章目標

學完本章後,你將能夠:

  1. 分辨 health、readiness、diagnostics 的語意
  2. 設計快速穩定的 /health
  3. /ready 控制是否接新流量
  4. 條件啟用 pprof、runtime stats 等診斷入口
  5. 測試 status code 與 JSON response 合約

【觀察】所有狀態都塞進 health 會讓監控失真

Health endpoint 的核心風險是語意混亂。若 /health 同時檢查 process、databasequeue、外部 API、cache、背景同步,任何依賴短暫波動都可能讓服務被判定死亡。

問題範例:

1/health
2  ├── process alive?
3  ├── database reachable?
4  ├── queue lag small?
5  ├── external API reachable?
6  └── background sync fresh?

這些問題不應全部塞進同一個 endpoint。Process 活著、可接流量、依賴降級、工程診斷,是不同操作訊號。

【判讀】health、ready、diagnostics 回答不同問題

操作 endpoint 的核心設計是每個 endpoint 只回答一個問題。

Endpoint使用者回答的問題失敗影響
/healthprocess monitorprocess 是否基本活著可能重啟 process
/readyload balancer是否應接新流量暫停導流
/debug/...工程師服務內部狀態如何不應公開
/metricsmetrics collector可聚合監控資料監控缺資料

這樣切分後,某個外部依賴故障不一定要讓 process 被重啟;服務可能只是不 ready,或處於 degraded 狀態。

【執行】health endpoint 應簡單快速

Health endpoint 的核心責任是快速回答 process 是否能處理基本 HTTP request。它應該簡單、快速、穩定。

 1func HandleHealth(w http.ResponseWriter, r *http.Request) {
 2    if r.Method != http.MethodGet {
 3        http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
 4        return
 5    }
 6
 7    w.Header().Set("Content-Type", "application/json")
 8    w.WriteHeader(http.StatusOK)
 9    _, _ = w.Write([]byte(`{"status":"ok"}`))
10}

/health 不應執行昂貴查詢,也不應依賴大量下游服務。若健康檢查本身很慢,監控會把診斷工具變成新問題。

【執行】readiness 控制是否接流量

Readiness 的核心責任是回答「服務現在是否應該接新流量」。它可以檢查啟動狀態、必要依賴、shutdown 狀態。

 1type Readiness struct {
 2    ready        atomic.Bool
 3    shuttingDown atomic.Bool
 4}
 5
 6func (r *Readiness) Ready() bool {
 7    return r.ready.Load() && !r.shuttingDown.Load()
 8}
 9
10func HandleReady(readiness *Readiness) http.HandlerFunc {
11    return func(w http.ResponseWriter, r *http.Request) {
12        w.Header().Set("Content-Type", "application/json")
13
14        if !readiness.Ready() {
15            w.WriteHeader(http.StatusServiceUnavailable)
16            _, _ = w.Write([]byte(`{"status":"not_ready"}`))
17            return
18        }
19
20        w.WriteHeader(http.StatusOK)
21        _, _ = w.Write([]byte(`{"status":"ready"}`))
22    }
23}

服務啟動尚未完成、必要背景同步尚未就緒、或 graceful shutdown 已開始時,readiness 應回 503。Process 仍然活著,但不應接新流量。

【策略】dependency check 依照監控語意分層

依賴檢查的核心判斷是故障是否代表 process 應重啟。Database 暫時不可用不一定代表 process 壞掉;重啟可能無法修復,反而造成更多負載。

建議分層:

  • /health:只確認 process alive。
  • /ready:確認必要依賴是否足以接新流量。
  • /diagnostics/dependencies:提供工程師查看細節。

診斷 response 可以包含穩定欄位:

1{
2  "status": "degraded",
3  "dependencies": {
4    "database": "ok",
5    "queue": "lagging"
6  }
7}

監控應依賴 status code 與穩定欄位,工程師再用 body 細節診斷問題。自由文字可以輔助閱讀,但不應成為監控規則的依據。

【執行】diagnostics endpoint 要條件啟用

Diagnostics endpoint 的核心用途是提供工程師排查問題的資料。pprof、runtime metrics、internal queue length、goroutine count 都屬於這類。

 1func RegisterDiagnostics(mux *http.ServeMux, enabled bool) {
 2    if !enabled {
 3        return
 4    }
 5
 6    mux.HandleFunc("/debug/runtime", HandleRuntimeStats)
 7}
 8
 9func HandleRuntimeStats(w http.ResponseWriter, r *http.Request) {
10    var stats runtime.MemStats
11    runtime.ReadMemStats(&stats)
12
13    response := map[string]any{
14        "heap_alloc":  stats.HeapAlloc,
15        "num_gc":      stats.NumGC,
16        "goroutines":  runtime.NumGoroutine(),
17    }
18
19    _ = json.NewEncoder(w).Encode(response)
20}

Diagnostics 可能揭露內部狀態、記憶體資訊、goroutine 數量、路徑與部署細節,不應公開給一般使用者。若需要長期保留,至少應限制在內網、管理 port、認證或防火牆後。

【判讀】status code 是監控合約

健康檢查的核心合約是 status code。監控系統通常先看 HTTP code 與 timeout,不會理解複雜 body。

狀態意義
200 OK符合該 endpoint 的健康條件
503 Service Unavailable暫時不可用或不應接流量
405 Method Not Allowed呼叫方式錯誤
timeoutendpoint 無法在預期時間內回應

Body 可以提供人類可讀資訊,但不應讓監控依賴自由文字。若要機器讀取,使用穩定 JSON 欄位,例如 statusreasondependencies

【測試】endpoint 測試要鎖定 status code

Endpoint 測試的核心是驗證 status code 與穩定 JSON 欄位,而不是完整自由文字。

 1func TestReadyReturnsUnavailableWhenShuttingDown(t *testing.T) {
 2    readiness := &Readiness{}
 3    readiness.ready.Store(true)
 4    readiness.shuttingDown.Store(true)
 5
 6    req := httptest.NewRequest(http.MethodGet, "/ready", nil)
 7    rec := httptest.NewRecorder()
 8
 9    HandleReady(readiness).ServeHTTP(rec, req)
10
11    if rec.Code != http.StatusServiceUnavailable {
12        t.Fatalf("status = %d, want %d", rec.Code, http.StatusServiceUnavailable)
13    }
14}

Diagnostics endpoint 也應測 gate 關閉時不註冊或回 404,避免診斷入口不小心暴露。

本章不處理

本章先處理 health、readiness 與 diagnostics 的語意切分;Prometheus、OpenTelemetry 與平台設定,會在下列章節再往外延伸:

和 Go 教材的關係

這一章承接的是 pprof、runtime metrics 與 deploy readiness;如果你要先回看語言教材,可以讀:

小結

/health/ready、diagnostics endpoint 解決不同問題。Health 檢查 process 基本可用性,readiness 控制是否接新流量,diagnostics 支援工程排查且應限制存取。Status code 是監控合約,JSON body 是補充細節;把這些訊號混在一起會讓操作判斷與安全邊界都變模糊。