// 一个最小的 Go HTTP 服务,用来演示多阶段构建、健康检查和优雅退出。 package main import ( "context" "errors" "log" "net/http" "os" "os/signal" "syscall" "time" ) func newMux() *http.ServeMux { mux := http.NewServeMux() mux.HandleFunc("GET /healthz", func(w http.ResponseWriter, r *http.Request) { w.Write([]byte("ok")) }) mux.HandleFunc("GET /api/hello", func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") w.Write([]byte(`{"message":"hello from container"}`)) }) return mux } func main() { addr := ":" + getenv("PORT", "8080") srv := &http.Server{Addr: addr, Handler: newMux(), ReadHeaderTimeout: 5 * time.Second} // 容器停止时 Docker 先发 SIGTERM,等待一段时间(默认 10 秒)后才 SIGKILL ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM) defer stop() go func() { log.Printf("listening on %s", addr) if err := srv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) { log.Fatal(err) } }() <-ctx.Done() shutdownCtx, cancel := context.WithTimeout(context.Background(), 8*time.Second) defer cancel() if err := srv.Shutdown(shutdownCtx); err != nil { // 不再接新请求,等正在处理的请求完成 log.Printf("shutdown: %v", err) } log.Println("bye") } func getenv(k, def string) string { if v := os.Getenv(k); v != "" { return v } return def }