2026-04-25 01:18:30 +08:00

82 lines
2.7 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Package healthcheck 提供 im-service 的 HTTP 和 gRPC 健康检查统一状态模型。
package healthcheck
import (
"encoding/json"
"net/http"
"time"
)
// Response 是 HTTP health endpoint 的稳定 JSON 契约。
type Response struct {
// Status 是整体状态,只有全部检查成功才是 ok。
Status string `json:"status"`
// Service 是服务名,便于多服务健康检查统一解析。
Service string `json:"service"`
// NodeID 是当前实例标识,用于定位具体节点。
NodeID string `json:"node_id"`
// TimeUnixMS 是响应生成时间,便于判断探针结果新鲜度。
TimeUnixMS int64 `json:"time_unix_ms"`
// Checks 只在失败时返回,成功响应保持短小。
Checks []Check `json:"checks,omitempty"`
}
// HTTPHandler 暴露 /healthz/live 和 /healthz/ready 的只读探测入口。
type HTTPHandler struct {
// state 是健康检查唯一数据源handler 不缓存探测结果。
state *State
}
// NewHTTPHandler 创建 health HTTP handler。
func NewHTTPHandler(state *State) *HTTPHandler {
return &HTTPHandler{state: state}
}
// Live 返回进程自身是否仍可运行;下游抖动不能影响 live。
func (h *HTTPHandler) Live(writer http.ResponseWriter, request *http.Request) {
checks := h.state.Live()
statusCode := http.StatusOK
if !Healthy(checks) {
// live 失败代表进程自身不可靠,返回 500 让 supervisor 可以重启。
statusCode = http.StatusInternalServerError
}
h.write(writer, statusCode, checks)
}
// Ready 返回当前实例是否可以接新流量。
func (h *HTTPHandler) Ready(writer http.ResponseWriter, request *http.Request) {
checks := h.state.Ready(request.Context())
statusCode := http.StatusOK
if !Healthy(checks) {
// ready 失败只表示不接新流量,不一定需要重启进程。
statusCode = http.StatusServiceUnavailable
}
h.write(writer, statusCode, checks)
}
func (h *HTTPHandler) write(writer http.ResponseWriter, statusCode int, checks []Check) {
status := componentStatus
if !Healthy(checks) {
// 整体状态和 HTTP status code 使用同一个 Healthy 聚合逻辑,避免语义分叉。
status = failStatus
}
response := Response{
Status: status,
Service: h.state.ServiceName(),
NodeID: h.state.NodeID(),
TimeUnixMS: time.Now().UnixMilli(),
}
if status != componentStatus {
// 只有失败时返回 checks成功路径减少响应体和敏感运行信息暴露。
response.Checks = checks
}
// health endpoint 固定 JSON 输出,部署系统和本地调试都消费同一契约。
writer.Header().Set("Content-Type", "application/json")
writer.WriteHeader(statusCode)
_ = json.NewEncoder(writer).Encode(response)
}