Scrierea unui microserviciu Go de nivel enterprise care să funcționeze impecabil în Kubernetes sau Docker presupune respectarea unor convenții arhitecturale clare: separarea logică a codului conform ghidului Standard Go Project Layout, oprirea fără întreruperea cererilor utilizatorilor (Graceful Shutdown) și emiterea de jurnale structurate compatibile cu sistemele centralizate de monitorizare (Datadog, Grafana Loki, Elasticsearch).
1. Structura Recomandată de Foldere
# Structura proiectului de producție:
# ├── cmd/
# │ └── api/
# │ └── main.go # Punctul de intrare: leagă dependențele și pornește serverul
# ├── internal/ # Cod privat (nu poate fi importat de alte proiecte externe!)
# │ ├── handler/ # Rutare HTTP, validare JSON, răspunsuri REST
# │ ├── service/ # Logica pură de business a aplicației
# │ └── repository/ # Acces la baza de date (SQL, Redis, Mongo)
# ├── pkg/ # Biblioteci utilitare publice
# ├── go.mod
# └── compose.yaml
2. Implementarea Graceful Shutdown cu signal.NotifyContext
Când un pod Kubernetes este oprit sau re-planificat, acesta primește semnalul SIGTERM. Aplicația are la dispoziție o perioadă de grație (de regulă 30 de secunde) pentru a termina cererile HTTP în curs, a finaliza tranzacțiile SQL și a închide conexiunile fără erori 502:
package main
import (
"context"
"errors"
"log/slog"
"net/http"
"os"
"os/signal"
"syscall"
"time"
)
func main() {
// 1. Inițializăm loggerul structurat ca JSON (standard cloud):
logger := slog.New(slog.NewJSONHandler(os.Stdout, nil))
slog.SetDefault(logger)
mux := http.NewServeMux()
mux.HandleFunc("GET /healthz", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
})
server := &http.Server{
Addr: ":8080",
Handler: mux,
ReadTimeout: 5 * time.Second,
WriteTimeout: 10 * time.Second,
IdleTimeout: 120 * time.Second,
}
// 2. Creăm un context care este anulat automat la primirea semnalelor SIGINT/SIGTERM:
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
defer stop()
// 3. Pornim serverul HTTP într-o goroutine de fundal:
go func() {
slog.Info("Serverul HTTP a pornit", "port", 8080)
if err := server.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
slog.Error("Eroare fatala la pornirea serverului", "err", err)
os.Exit(1)
}
}()
// 4. Blocăm execuția principală până la primirea semnalului de oprire:
<-ctx.Done()
slog.Info("Semnal de oprire primit! Se initiaza Graceful Shutdown...")
// 5. Alocăm o fereastră de grație de 15 secunde pentru finalizarea cererilor active:
shutdownCtx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
defer cancel()
if err := server.Shutdown(shutdownCtx); err != nil {
slog.Error("Eroare la inchiderea fortata a serverului", "err", err)
} else {
slog.Info("Serverul a fost oprit curat fara pierderi de conexiuni.")
}
}