topfans/backend/services/statisticService/handler/healthz.go
zerosaturation f5ece5e1d2 feat(statistic): T3 service skeleton (config + main + healthz + metrics + 10 SQL)
- 6 config blocks (DB/Redis/Channel/Refresh/Partition/Extension with 4 EnableXxx=false)
- 14 Prometheus metric declarations
- self-impl healthz (/healthz + /metrics) — bypasses pkg/health to keep /metrics
- main.go startup: logger → config → DB → Redis → healthz HTTP :21009
- 10 SQL migrations: events partitioned + 4 MV + 3 pre-agg + refresh_log + 7-day initial

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 17:20:53 +08:00

57 lines
1.2 KiB
Go

package handler
import (
"context"
"database/sql"
"net/http"
"time"
"github.com/gin-gonic/gin"
"github.com/prometheus/client_golang/prometheus/promhttp"
"github.com/redis/go-redis/v9"
)
// Healthz 健康检查 + /metrics 端点
type Healthz struct {
db *sql.DB
redis *redis.Client
}
// NewHealthz 构造 healthz handler
func NewHealthz(db *sql.DB, rdb *redis.Client) *Healthz {
return &Healthz{db: db, redis: rdb}
}
// Register 在 gin engine 上注册 /healthz 和 /metrics 路由
func (h *Healthz) Register(r *gin.Engine) {
r.GET("/healthz", h.handleHealthz)
r.GET("/metrics", gin.WrapH(promhttp.Handler()))
}
func (h *Healthz) handleHealthz(c *gin.Context) {
ctx, cancel := context.WithTimeout(c.Request.Context(), 1*time.Second)
defer cancel()
status := gin.H{"status": "ok"}
if err := h.db.PingContext(ctx); err != nil {
status["db"] = "down"
status["db_error"] = err.Error()
} else {
status["db"] = "up"
}
if err := h.redis.Ping(ctx).Err(); err != nil {
status["redis"] = "down"
status["redis_error"] = err.Error()
} else {
status["redis"] = "up"
}
code := http.StatusOK
if status["db"] == "down" || status["redis"] == "down" {
code = http.StatusServiceUnavailable
}
c.JSON(code, status)
}