健康檢查
健康檢查端點讓負載均衡器、Kubernetes 等編排工具和監控系統能夠驗證你的應用程式是否正在運行並準備好處理流量。典型的設定包含兩個端點:存活探測(程序是否存活?)和就緒探測(是否準備好接受請求?)。
基本健康檢查
一個回傳 200 OK 的簡單健康端點:
package main
import ( "net/http"
"github.com/gin-gonic/gin")
func main() { r := gin.Default()
r.GET("/healthz", func(c *gin.Context) { c.JSON(http.StatusOK, gin.H{"status": "ok"}) })
r.GET("/ping", func(c *gin.Context) { c.String(http.StatusOK, "pong") })
r.Run(":8080")}存活探測 vs 就緒探測
在 Kubernetes 和類似環境中,你通常需要兩種類型的健康檢查:
- 存活探測(Liveness probe) — 檢查應用程式程序是否存活。如果失敗,容器會被重新啟動。
- 就緒探測(Readiness probe) — 檢查應用程式是否準備好處理流量。如果失敗,流量會暫時停止,但容器不會被重新啟動。
package main
import ( "database/sql" "net/http" "sync/atomic"
"github.com/gin-gonic/gin" _ "github.com/lib/pq")
var isReady atomic.Bool
func main() { db, err := sql.Open("postgres", "postgres://user:pass@localhost/dbname?sslmode=disable") if err != nil { panic(err) } defer db.Close()
r := gin.Default()
// Liveness: is the process alive? r.GET("/healthz", func(c *gin.Context) { c.JSON(http.StatusOK, gin.H{"status": "alive"}) })
// Readiness: can we serve traffic? r.GET("/readyz", func(c *gin.Context) { if !isReady.Load() { c.JSON(http.StatusServiceUnavailable, gin.H{"status": "not ready"}) return }
// Check database connectivity if err := db.Ping(); err != nil { c.JSON(http.StatusServiceUnavailable, gin.H{ "status": "not ready", "reason": "database unreachable", }) return }
c.JSON(http.StatusOK, gin.H{"status": "ready"}) })
// Mark as ready after initialization is complete isReady.Store(true)
r.Run(":8080")}Kubernetes 配置
在你的 Kubernetes 部署清單中配置探測:
apiVersion: apps/v1kind: Deploymentmetadata: name: my-gin-appspec: template: spec: containers: - name: app image: my-gin-app:latest ports: - containerPort: 8080 livenessProbe: httpGet: path: /healthz port: 8080 initialDelaySeconds: 5 periodSeconds: 10 readinessProbe: httpGet: path: /readyz port: 8080 initialDelaySeconds: 5 periodSeconds: 5檢查多個依賴項
對於具有多個依賴項的應用程式,逐一檢查每個依賴項並回報詳細狀態:
package main
import ( "context" "database/sql" "net/http" "time"
"github.com/gin-gonic/gin" "github.com/redis/go-redis/v9")
type HealthChecker struct { DB *sql.DB Redis *redis.Client}
func (h *HealthChecker) CheckHealth(c *gin.Context) { ctx, cancel := context.WithTimeout(c.Request.Context(), 2*time.Second) defer cancel()
checks := gin.H{} healthy := true
// Check database if err := h.DB.PingContext(ctx); err != nil { checks["database"] = gin.H{"status": "unhealthy", "error": err.Error()} healthy = false } else { checks["database"] = gin.H{"status": "healthy"} }
// Check Redis if err := h.Redis.Ping(ctx).Err(); err != nil { checks["redis"] = gin.H{"status": "unhealthy", "error": err.Error()} healthy = false } else { checks["redis"] = gin.H{"status": "healthy"} }
status := http.StatusOK if !healthy { status = http.StatusServiceUnavailable }
c.JSON(status, gin.H{ "status": map[bool]string{true: "healthy", false: "unhealthy"}[healthy], "checks": checks, })}測試
# Check livenesscurl -i http://localhost:8080/healthz
# Check readinesscurl -i http://localhost:8080/readyz預期輸出:
HTTP/1.1 200 OKContent-Type: application/json; charset=utf-8
{"status":"ready"}