82 lines
2.7 KiB
Go
82 lines
2.7 KiB
Go
// 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)
|
||
}
|