Files
h/cmd/api/main.go

114 lines
2.7 KiB
Go

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")
}