Go
Beginner
1 min read
Graceful Shutdown and HTTP Client
Example
package main
import (
"context"
"encoding/json"
"fmt"
"log"
"net/http"
"os"
"os/signal"
"syscall"
"time"
)
type Post struct {
ID int `json:"id"`
Title string `json:"title"`
}
// HTTP client with timeout
func fetchPost(ctx context.Context, id int) (*Post, error) {
url := fmt.Sprintf("https://jsonplaceholder.typicode.com/posts/%d", id)
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil {
return nil, err
}
client := &http.Client{Timeout: 5 * time.Second}
resp, err := client.Do(req)
if err != nil {
return nil, fmt.Errorf("fetchPost: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("fetchPost: unexpected status %d", resp.StatusCode)
}
var post Post
if err := json.NewDecoder(resp.Body).Decode(&post); err != nil {
return nil, err
}
return &post, nil
}
func main() {
mux := http.NewServeMux()
mux.HandleFunc("GET /posts/{id}", func(w http.ResponseWriter, r *http.Request) {
post, err := fetchPost(r.Context(), 1)
if err != nil {
http.Error(w, err.Error(), http.StatusBadGateway)
return
}
json.NewEncoder(w).Encode(post)
})
srv := &http.Server{Addr: ":8080", Handler: mux}
// Graceful shutdown
go func() {
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<-quit
log.Println("shutting down...")
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if err := srv.Shutdown(ctx); err != nil {
log.Fatal("forced shutdown:", err)
}
}()
log.Println("listening on :8080")
if err := srv.ListenAndServe(); err != http.ErrServerClosed {
log.Fatal(err)
}
log.Println("server stopped cleanly")
}