健康检查接口必须返回200或503状态码,kubernetes等系统仅依赖http状态码判定服务状态;结构化响应体仅用于人工排查;所有i/o检查需加context超时且总耗时≤3s;/metrics与/health须严格分离;go指标需正确打标与聚合。

健康检查接口必须返回200或503状态码,不能只靠JSON字段判断
Kubernetes、Consul、Nginx等系统不解析/health响应体内容,只依赖HTTP状态码做决策。返回{"status":"down"}但状态码是200,K8s readiness probe仍会把流量导过来;反过来,哪怕body写{"status":"up"},只要状态码是503,实例就会被立即摘除。
正确做法是:
- 用
w.WriteHeader(http.StatusOK)表示可服务,w.WriteHeader(http.StatusServiceUnavailable)表示暂时不可用 - 结构化响应体(如
{"status":"UP","timestamp":"..."})仅用于人工排查或日志审计,不是判定依据 - 避免在handler里返回204、302或重定向——这些都会被探针视为失败
依赖检查必须加context超时,且总耗时≤3s
数据库Ping()、Redis PingContext()、下游gRPC Connect()这些操作默认无超时,一旦网络抖动或中间件卡住,健康检查会阻塞数秒甚至更久。Kubernetes默认probe timeout是1s,连续3次失败就重启Pod,没超时控制等于主动触发误杀。
实操要点:
- 所有I/O检查必须套
context.WithTimeout(ctx, 2*time.Second),主handler再包一层3s兜底 - 用
errgroup.Group并发执行多个依赖检查,而不是串行if err := checkDB(); err != nil { ... } if err := checkRedis(); ... - 非关键依赖(如日志上报服务)失败应静默忽略,不参与健康判定,否则单点故障会放大成全链路不可用
指标暴露要区分/metrics和/health,别混在一起
/metrics是Prometheus拉取的时序数据端点,/health是调度器探测的瞬时状态端点,二者语义、协议、调用频率完全不同。把promhttp.Handler()挂到/health路径,或者在/health handler里调用prometheus.MustRegister(),会导致指标注册重复panic,或让探针拿到一堆乱码文本。
正确组织方式:
-
http.Handle("/metrics", promhttp.Handler())—— 暴露标准Prometheus格式指标 -
http.HandleFunc("/health", healthHandler)—— 纯状态检查,不引入任何prometheus/client_golang逻辑 - 自定义业务指标(如
order_created_total)必须在init()或main()开头注册,不能在handler里new或re-register
Grafana查不到Go指标?先盯紧label和聚合层级
常见现象是Prometheus Graph能查出原始时间序列,Grafana图表却显示“No data”或P95恒为0。根本原因几乎全是label匹配错误或聚合漏层。
高频踩坑点:
- 查询
rate(http_requests_total{job="order-service"}[5m])时漏了{job="..."},结果跨服务混算,数值失真 - 查延迟P95写成
histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m])),缺了sum(...) by (le),导致分桶未归一化,返回空 - 给
http_requests_total加了user_id这种高基数label,Prometheus内存暴涨后自动drop掉该指标
真正需要上Grafana的Go指标其实就三类:http_requests_total(带method/path/status)、http_request_duration_seconds_bucket(同标签)、少量关键业务计数器。其他运行时指标(如goroutines、GC次数)除非定位瓶颈,否则不必上大盘。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











