Go — Practical
Go — Practical patterns
Section titled “Go — Practical patterns”HTTP server with graceful shutdown
Section titled “HTTP server with graceful shutdown”package main
import ( "context"; "errors"; "log"; "net/http" "os"; "os/signal"; "syscall"; "time")
func main() { mux := http.NewServeMux() mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { w.Write([]byte("ok")) }) srv := &http.Server{ Addr: ":8080", Handler: mux, ReadHeaderTimeout: 5 * time.Second, }
go func() { if err := srv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) { log.Fatalf("listen: %v", err) } }()
quit := make(chan os.Signal, 1) signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) <-quit
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() if err := srv.Shutdown(ctx); err != nil { log.Fatalf("shutdown: %v", err) }}Worker pool
Section titled “Worker pool”func workerPool[T, R any](ctx context.Context, n int, jobs <-chan T, fn func(T) R) <-chan R { out := make(chan R) var wg sync.WaitGroup for i := 0; i < n; i++ { wg.Add(1) go func() { defer wg.Done() for { select { case <-ctx.Done(): return case j, ok := <-jobs: if !ok { return } out <- fn(j) } } }() } go func() { wg.Wait(); close(out) }() return out}errgroup for parallel work with cancellation
Section titled “errgroup for parallel work with cancellation”import "golang.org/x/sync/errgroup"
g, ctx := errgroup.WithContext(ctx)for _, url := range urls { url := url g.Go(func() error { req, _ := http.NewRequestWithContext(ctx, "GET", url, nil) resp, err := http.DefaultClient.Do(req) if err != nil { return err } defer resp.Body.Close() return nil })}if err := g.Wait(); err != nil { return err}Retry with backoff
Section titled “Retry with backoff”func retry(ctx context.Context, max int, fn func() error) error { var err error for i := 0; i < max; i++ { if err = fn(); err == nil { return nil } wait := time.Duration(math.Pow(2, float64(i))) * 100 * time.Millisecond select { case <-time.After(wait): case <-ctx.Done(): return ctx.Err() } } return err}Singleflight (dedupe concurrent calls)
Section titled “Singleflight (dedupe concurrent calls)”import "golang.org/x/sync/singleflight"
var g singleflight.Groupval, err, _ := g.Do(key, func() (any, error) { return expensiveLookup(key)})Rate limiter
Section titled “Rate limiter”import "golang.org/x/time/rate"
lim := rate.NewLimiter(rate.Every(time.Second), 10) // 10/sec, burst 10if err := lim.Wait(ctx); err != nil { /* ctx canceled */ }Mutex-protected map (or use sync.Map)
Section titled “Mutex-protected map (or use sync.Map)”type Cache struct { mu sync.RWMutex m map[string]string}
func (c *Cache) Get(k string) (string, bool) { c.mu.RLock(); defer c.mu.RUnlock() v, ok := c.m[k] return v, ok}
func (c *Cache) Set(k, v string) { c.mu.Lock(); defer c.mu.Unlock() c.m[k] = v}DB with database/sql
Section titled “DB with database/sql”db, err := sql.Open("postgres", dsn)if err != nil { return err }db.SetMaxOpenConns(25); db.SetMaxIdleConns(5); db.SetConnMaxLifetime(5*time.Minute)
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)defer cancel()
var name stringerr = db.QueryRowContext(ctx, "SELECT name FROM users WHERE id=$1", id).Scan(&name)Profiling
Section titled “Profiling”import _ "net/http/pprof"go http.ListenAndServe(":6060", nil) // exposes /debug/pprof/*go tool pprof http://localhost:6060/debug/pprof/profile?seconds=30go tool pprof http://localhost:6060/debug/pprof/heapgo tool pprof http://localhost:6060/debug/pprof/goroutineBuild & deploy
Section titled “Build & deploy”CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" -o app ./cmd/appdocker build -t app:latest . # use distroless or scratch base for tiny imageTesting patterns
Section titled “Testing patterns”func TestAdd(t *testing.T) { cases := []struct{ a, b, want int }{ {1, 2, 3}, {0, 0, 0}, {-1, 1, 0}, } for _, c := range cases { c := c t.Run(fmt.Sprintf("%d+%d", c.a, c.b), func(t *testing.T) { t.Parallel() if got := Add(c.a, c.b); got != c.want { t.Errorf("got %d want %d", got, c.want) } }) }}Useful libs
Section titled “Useful libs”- HTTP framework:
chi,echo,fiber,gin(or stdlib +chifor middleware). - DB:
pgx,sqlx,sqlc(codegen),gorm. - Logging:
log/slog(stdlib 1.21+),zap,zerolog. - Config:
viper,envconfig. - Validation:
go-playground/validator. - Mocking:
mockery,gomock. - Tracing:
go.opentelemetry.io/otel.