feat: string-unpack api

This commit is contained in:
2024-05-08 21:25:02 +03:00
parent 0f8d6b39fb
commit 070934d66a
18 changed files with 572 additions and 4 deletions

24
cmd/api/config.go Normal file
View File

@ -0,0 +1,24 @@
package main
import (
"flag"
"time"
"github.com/kelseyhightower/envconfig"
)
type Config struct {
DSN string `envconfig:"DSN" default:"postgresql://postgres:postgres@postgres:5432/postgres?sslmode=disable"`
ReconnectTimeout time.Duration `envconfig:"RECONNECT_TIMEOUT" default:"2s"`
}
func (c *Config) Parse() error {
flag.StringVar(&c.DSN, "DSN", "", "postgresql DSN")
flag.Parse()
if err := envconfig.Process("", c); err != nil {
return err
}
return nil
}

113
cmd/api/main.go Normal file
View File

@ -0,0 +1,113 @@
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")
}