91 lines
2.1 KiB
Go
91 lines
2.1 KiB
Go
package httpserver
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"log/slog"
|
|
"net/http"
|
|
"sync/atomic"
|
|
"time"
|
|
)
|
|
|
|
// Server 提供 /health 和 /ready HTTP 探针。
|
|
type Server struct {
|
|
appName string
|
|
addr string
|
|
shutdownTimeout time.Duration
|
|
logger *slog.Logger
|
|
ready atomic.Bool
|
|
}
|
|
|
|
// New 创建探针 HTTP 服务。
|
|
func New(appName string, addr string, shutdownTimeout time.Duration, logger *slog.Logger) *Server {
|
|
server := &Server{
|
|
appName: appName,
|
|
addr: addr,
|
|
shutdownTimeout: shutdownTimeout,
|
|
logger: logger,
|
|
}
|
|
server.ready.Store(true)
|
|
return server
|
|
}
|
|
|
|
// Run 启动 HTTP 服务并在退出时优雅关闭。
|
|
func (s *Server) Run(ctx context.Context) error {
|
|
mux := http.NewServeMux()
|
|
mux.HandleFunc("/health", s.handleHealth)
|
|
mux.HandleFunc("/heath", s.handleHealth)
|
|
mux.HandleFunc("/ready", s.handleReady)
|
|
|
|
httpServer := &http.Server{
|
|
Addr: s.addr,
|
|
Handler: mux,
|
|
ReadHeaderTimeout: 5 * time.Second,
|
|
}
|
|
|
|
go func() {
|
|
<-ctx.Done()
|
|
s.ready.Store(false)
|
|
shutdownCtx, cancel := context.WithTimeout(context.Background(), s.shutdownTimeout)
|
|
defer cancel()
|
|
_ = httpServer.Shutdown(shutdownCtx)
|
|
}()
|
|
|
|
s.logger.Info("pay http server started", "addr", s.addr)
|
|
|
|
err := httpServer.ListenAndServe()
|
|
if errors.Is(err, http.ErrServerClosed) {
|
|
return nil
|
|
}
|
|
return err
|
|
}
|
|
|
|
func (s *Server) handleHealth(w http.ResponseWriter, _ *http.Request) {
|
|
writeJSON(w, http.StatusOK, map[string]string{
|
|
"status": "ok",
|
|
"service": s.appName,
|
|
})
|
|
}
|
|
|
|
func (s *Server) handleReady(w http.ResponseWriter, _ *http.Request) {
|
|
if !s.ready.Load() {
|
|
writeJSON(w, http.StatusServiceUnavailable, map[string]string{
|
|
"code": "not_ready",
|
|
"message": "service is shutting down",
|
|
})
|
|
return
|
|
}
|
|
|
|
writeJSON(w, http.StatusOK, map[string]string{
|
|
"status": "ready",
|
|
"service": s.appName,
|
|
})
|
|
}
|
|
|
|
func writeJSON(w http.ResponseWriter, statusCode int, payload any) {
|
|
w.Header().Set("Content-Type", "application/json")
|
|
w.WriteHeader(statusCode)
|
|
_ = json.NewEncoder(w).Encode(payload)
|
|
}
|