package main import ( "context" "errors" "net/http" "os/signal" "syscall" "time" "github.com/jackc/pgx/v5" stringunpack "git.grachevko.ru/grachevko/h/string-unpack" "github.com/gin-gonic/gin" "github.com/prometheus/client_golang/prometheus" collector "github.com/prometheus/client_golang/prometheus/collectors/version" "github.com/prometheus/client_golang/prometheus/promhttp" "go.uber.org/zap" "golang.org/x/sync/errgroup" ) func main() { logger, _ := zap.NewProduction() defer func(logger *zap.Logger) { _ = logger.Sync() }(logger) // flushes buffer, if any cfg := Config{} if err := cfg.Parse(); err != nil { logger.Fatal("config parse", zap.Error(err)) } ctx := context.Background() ctx, stop := signal.NotifyContext(ctx, syscall.SIGINT, syscall.SIGTERM) defer stop() var metricServer *http.Server { // Metrics prometheus.MustRegister(collector.NewCollector("unpack")) mux := http.NewServeMux() mux.Handle("/metrics", promhttp.Handler()) metricServer = &http.Server{ Handler: mux, Addr: ":8089", } go func() { if err := metricServer.ListenAndServe(); err != nil { logger.Error("metric server failed", zap.Error(err)) } }() } // Metrics pgconn: conn, err := pgx.Connect(ctx, cfg.DSN) if err != nil { logger.Error("pgx", zap.Error(err)) logger.Info("Wait before reconnect", zap.Duration("after", cfg.ReconnectTimeout)) <-time.After(cfg.ReconnectTimeout) logger.Info("Retrying connection") goto pgconn } defer func(conn *pgx.Conn, ctx context.Context) { if err = conn.Close(ctx); err != nil { logger.Error("pgx close", zap.Error(err)) } }(conn, ctx) var httpServer *http.Server { // Http r := gin.Default() stringunpack.Setup(ctx, logger, r.Group("unpack"), conn) httpServer = &http.Server{ Addr: ":8080", Handler: r, } go func() { if err := httpServer.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) { logger.Error("http server failed", zap.Error(err)) } }() } // Http // Listen for the interrupt signal. <-ctx.Done() { // Graceful shutdown // Restore default behavior on the interrupt signal and notify user of shutdown. stop() logger.Info("shutting down gracefully, press Ctrl+C again to force") // The context is used to inform the server it has 5 seconds to finish // the request it is currently handling ctx, cancel := context.WithTimeout(ctx, 5*time.Second) defer cancel() g, ctx := errgroup.WithContext(ctx) g.Go(func() error { return metricServer.Shutdown(ctx) }) g.Go(func() error { return httpServer.Shutdown(ctx) }) if err := g.Wait(); err != nil { logger.Error("graceful shutdown failed", zap.Error(err)) } } // Graceful shutdown logger.Info("Server exiting") }