HealthCheck 脚本应使用显式 http.NewServeMux() 启动,避免与 DefaultServeMux 混用;返回结构化 JSON 并设 Content-Type;DB 检查须用带 timeout 的 PingContext;liveness 与 readiness 探针应分离路径且逻辑解耦。

HealthCheck 脚本该用哪个 HTTP handler 启动
Go 的 net/http 自带 http.ServeMux,但直接注册 /health 路由时容易忽略默认路由覆盖问题。比如用 http.HandleFunc("/health", ...) 没问题,但若同时用了 http.Handle("/", ...)(如静态文件服务),它会吞掉所有子路径,导致 /health 404。
推荐用显式 http.NewServeMux() 控制路由优先级:
mux := http.NewServeMux()
mux.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w.Write([]byte("ok"))
})
http.ListenAndServe(":8080", mux)
- 避免和
http.DefaultServeMux混用,尤其在引入第三方中间件时 - 如果项目已用
gorilla/mux或chi,直接复用其路由实例,别再套一层http.ServeMux - 不要在 HealthCheck handler 里调用可能阻塞的
time.Sleep或未设 timeout 的外部 HTTP 请求
如何让 HealthCheck 返回结构化 JSON 而不是纯文本
运维监控系统(如 Prometheus、Kubernetes liveness probe)通常期望 JSON 格式响应,且要求字段明确、可解析。返回 "ok" 字符串虽然简单,但无法携带状态细节或时间戳,排查时缺乏上下文。
标准做法是定义最小结构体并设置 Content-Type:
立即学习“go语言免费学习笔记(深入)”;
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
type HealthResponse struct {
Status string `json:"status"`
Time string `json:"time"`
}
func healthHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(HealthResponse{
Status: "healthy",
Time: time.Now().UTC().Format(time.RFC3339),
})
}
- 务必调用
w.Header().Set("Content-Type", "application/json"),否则某些代理(如 Nginx)可能缓存或修改响应体 - 避免在结构体中嵌入指针字段(如
*string),防止序列化出null引发解析失败 - Kubernetes readiness probe 默认只检查 HTTP 状态码,不校验 JSON 内容,所以
Status: "degraded"仍需配http.StatusServiceUnavailable才生效
数据库连接检查为什么总超时或 panic
HealthCheck 中常见错误是直接在 handler 里执行 db.Ping() 却没设 context timeout,一旦数据库不可达,请求卡死 30 秒以上,拖垮整个服务可用性。
必须用带 cancel 的 context 控制最大等待时间:
func dbHealthCheck(db *sql.DB) error {
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
return db.PingContext(ctx)
}
-
db.Ping()不走连接池,每次新建连接;db.PingContext()才真正受 context 控制 - 不要在 HealthCheck 中执行
SELECT 1这类查询——PingContext已足够验证连通性,额外查询增加 DB 压力 - 如果 DB 初始化失败(如 DSN 错误),
db变量为 nil,调用db.PingContext会 panic,需提前判空
Kubernetes 中 livenessProbe 和 readinessProbe 怎么配才不互相干扰
两个探针共用同一 HealthCheck 接口时,容易因逻辑耦合导致服务反复重启。典型问题是:DB 临时抖动触发 liveness 失败,Pod 被 kill,重启后又因 DB 还没恢复而再次失败,陷入循环。
- livenessProbe 应只检查进程存活(如端口可连、HTTP 200),不检查依赖服务;readinessProbe 才负责 DB、Redis 等依赖项
- readinessProbe 的
initialDelaySeconds必须大于应用启动耗时,否则 Pod 尚未初始化完成就被标记为 ready,流量进来直接 500 - 避免在同一个 handler 里混写 liveness 和 readiness 逻辑——用不同 path(如
/live和/ready)物理隔离更稳妥
最易被忽略的是:Kubernetes 默认用 HTTP GET,但某些 Ingress(如 Traefik)对 HEAD 请求优化更好,如果 HealthCheck handler 没实现 HEAD 方法,probe 可能失败。加一句 if r.Method == "HEAD" { w.WriteHeader(http.StatusOK); return } 就能省掉 body 序列化开销。

















