main.go
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162
package main

import (
	"context"
	"fmt"
	"log"
	"net/http"
	"os"
	"os/signal"
	"syscall"
	"time"
)

// handler provides a simple HTTP endpoint
func handler(w http.ResponseWriter, r *http.Request) {
	// Simulate work that takes time to complete
	time.Sleep(2 * time.Second)
	fmt.Fprintf(w, "Request processed successfully!\n")
}

func main() {
	// Create router and register handler
	r := http.NewServeMux()
	r.HandleFunc("/api/process", handler)

	// Configure HTTP server with timeouts (critical for production)
	srv := &http.Server{
		Addr:         ":8080",
		Handler:      r,
		ReadTimeout:  10 * time.Second,
		WriteTimeout: 10 * time.Second,
		IdleTimeout:  15 * time.Second,
	}

	// Start server in a goroutine to avoid blocking
	go func() {
		log.Printf("Server starting on %s", srv.Addr)
		if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
			log.Fatalf("Failed to start server: %v", err)
		}
	}()

	// Set up signal handling for graceful shutdown
	sigChan := make(chan os.Signal, 1)
	// Listen for SIGINT (Ctrl+C) and SIGTERM (system shutdown)
	signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)

	// Block until a signal is received
	sig := <-sigChan
	log.Printf("Received signal: %v. Initiating graceful shutdown...", sig)

	// Create context with timeout for shutdown (allow time for active requests)
	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	defer cancel()

	// Perform graceful shutdown
	if err := srv.Shutdown(ctx); err != nil {
		log.Fatalf("Server shutdown failed: %v", err)
	}

	log.Println("Server shutdown completed successfully")
}

How It Works

Demonstrates how to stop an HTTP server cleanly instead of abruptly killing connections.

Creates ServeMux handlers, starts the server in a goroutine, listens for SIGINT or SIGTERM, calls Shutdown with a timeout context to drain ongoing requests, and logs shutdown progress or errors.

Key Concepts

  • 1Sets read, write, and idle timeouts to protect from slowloris clients.
  • 2Separate goroutine runs ListenAndServe while main waits on signals.
  • 3Shutdown uses context to bound the graceful period and cancels outstanding requests.
  • 4Error handling differentiates normal http.ErrServerClosed from real failures.

When to Use This Pattern

  • Web services that need predictable restarts during deployments.
  • Graceful rollouts behind load balancers.
  • Local development to show how to stop servers without killing processes.
  • Workloads that must flush logs or queues before exit.

Best Practices

  • Always use Shutdown instead of Close for HTTP servers in production.
  • Tune timeout durations to match expected request lengths.
  • Handle multiple signals to allow a second interrupt to force exit if needed.
  • Log shutdown steps so operators know when draining finishes.
Go Version1.8
Difficultyintermediate
Production ReadyYes
Lines of Code62