可觀測性:三態健康檢查與 tracing 抽象層
可觀測性:三態健康檢查與 tracing 抽象層
三態健康檢查(Healthy / Degraded / Unhealthy)比 up/down 更貼近 K8s probe。tracing 則只依賴一個 Tracer 介面,OTel 當 adapter 接上。這篇講介面驅動的可觀測性。
前置:A3 interface 與組合。
三態健康檢查
傳統健康檢查只有 up/down,但真實世界常是「還能跑但快不行了」。gortex 用三態,貼近 K8s 的 readiness / liveness:
const (
HealthStatusHealthy HealthStatus = "healthy"
HealthStatusDegraded HealthStatus = "degraded" // 還能服務,但接近極限
HealthStatusUnhealthy HealthStatus = "unhealthy"
)
聚合採 worst-wins:任一 check 是 Unhealthy,整體就 Unhealthy;否則有 Degraded 就 Degraded;全好才 Healthy。內建的 MemoryHealthCheck 示範得很清楚:超過上限是 Unhealthy,到 80% 就先報 Degraded 預警。
並行檢查與逾時
HealthChecker 背景開一個 goroutine 按 interval 定時跑(runChecks + ticker),結果快取起來,/healthz 那類端點直接讀快取、不每次現跑。
實際跑的時候所有 check 並行,各自帶 timeout,一個慢 check 不會拖垮其他:
for name, check := range checks {
wg.Add(1)
go func(n string, c HealthCheck) {
defer wg.Done()
checkCtx, cancel := context.WithTimeout(ctx, hc.timeout)
defer cancel()
result := c(checkCtx) // 每個 check 各自的 deadline
// ...存結果
}(name, check)
}
wg.Wait()
sync.WaitGroup 等全部跑完。這是 A4 的並行加 A5 的同步原語,落在維運面的用法。
Tracer 介面 + OTel adapter
這節是 A3 介面驅動的活教材。框架只依賴一個小介面:
type Tracer interface {
StartSpan(ctx context.Context, operation string) (context.Context, *Span)
FinishSpan(span *Span)
AddTags(span *Span, tags map[string]string)
SetStatus(span *Span, status SpanStatus)
}
type EnhancedTracer interface { // 介面嵌入
Tracer
StartEnhancedSpan(ctx context.Context, operation string) (context.Context, *EnhancedSpan)
}
附三種實作:NoOpTracer(關掉 tracing)、SimpleTracer(in-memory,開發用)、OTelTracerAdapter(接 OpenTelemetry)。要從 in-memory 換到 Jaeger / OTel,只是換一個實作,業務碼一行不改,這就是 A3 的 adapter。adapter 內部用一張 severityMap,把 gortex 的 8 級嚴重性(DEBUG=10 … EMERGENCY=80)對映到 OTel 的屬性。
Context propagation
span 怎麼跟著請求鏈路走?用 context,而且用私有的 contextKey 型別避免 key 撞車:
type contextKey string
const spanContextKey contextKey = "span"
func ContextWithSpan(ctx context.Context, span *Span) context.Context {
return context.WithValue(ctx, spanContextKey, span)
}
func SpanFromContext(ctx context.Context) *Span { /* ctx.Value(spanContextKey) */ }
StartSpan 先用 SpanFromContext 取出父 span、建子 span、再把子 span 放回 ctx 往下傳。整條鏈路就靠 context 串起來,不用全域變數、也不用手動到處傳。
重點整理
- 三態健康(Healthy / Degraded / Unhealthy)比 up/down 貼近 K8s probe;聚合 worst-wins。
- check 背景定時跑 + 快取;執行時
WaitGroup並行、各自 timeout,慢的不拖垮快的。 - tracing 只依賴
Tracer/EnhancedTracer介面(介面嵌入);NoOp / Simple / OTel 三種實作可抽換,正是 A3 的 adapter。 - span 用私有
contextKey+context.WithValue沿鏈路傳遞;8 級 severity 對映 OTel。 - 可觀測性建在介面上,換後端不動業務碼。
原始碼:yshengliao/gortex。
大綱 Sheng,內文 Claude 協助 · 環境 Go 1.25(gortex go.mod)· 本系列為事後回填整理 · 列入 20260613 blog 翻新計劃,新漆未乾。