Skip to content

Instantly share code, notes, and snippets.

@vituchon
Created May 16, 2024 17:36
Show Gist options
  • Save vituchon/0b3d75286be5ce075c01f894f0bbc8bf to your computer and use it in GitHub Desktop.
Save vituchon/0b3d75286be5ce075c01f894f0bbc8bf to your computer and use it in GitHub Desktop.
Golang mux webserver - Testing graceful shutdown behavior
package main
import (
"context"
"flag"
"fmt"
"log"
"net/http"
"os"
"os/signal"
"time"
"github.com/gorilla/mux"
)
func QuickEndpoint(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Endpoint hit!\n"))
}
func LongRunningTaskForegroundEndpoint(w http.ResponseWriter, r *http.Request) {
for i := 0; i < 10; i++ {
time.Sleep(1 * time.Second)
fmt.Print("=")
}
fmt.Print(".")
w.Write([]byte("Long Running Task Foreground Endpoint 1 hit!\n"))
}
func LongRunningTaskForegroundEndpoint2(w http.ResponseWriter, r *http.Request) {
fmt.Println("10 segundos de pausa")
time.Sleep(10 * time.Second)
w.Write([]byte("Long Running Task Foreground Endpoint 2 hit!\n"))
}
func LongRunningTaskBackgroundEndpoint(w http.ResponseWriter, r *http.Request) {
go func() {
for i := 0; i < 10; i++ {
time.Sleep(1 * time.Second)
fmt.Print("=")
}
fmt.Print(".")
}()
w.Write([]byte("Long Running Task Background Endpoint hit!\n"))
}
func main() {
var wait time.Duration
flag.DurationVar(&wait, "graceful-timeout", time.Second*15, "the duration for which the server gracefully wait for existing connections to finish - e.g. 15s or 1m")
flag.Parse()
r := mux.NewRouter()
// Add your routes as needed
r.HandleFunc("/quick", QuickEndpoint)
r.HandleFunc("/long-fore", LongRunningTaskForegroundEndpoint)
r.HandleFunc("/long-fore-2", LongRunningTaskForegroundEndpoint2)
r.HandleFunc("/long-back", LongRunningTaskBackgroundEndpoint)
srv := &http.Server{
Addr: "0.0.0.0:7070",
// Good practice to set timeouts to avoid Slowloris attacks.
WriteTimeout: time.Second * 15,
ReadTimeout: time.Second * 15,
IdleTimeout: time.Second * 60,
Handler: r, // Pass our instance of gorilla/mux in.
}
// Run our server in a goroutine so that it doesn't block.
go func() {
if err := srv.ListenAndServe(); err != nil {
log.Println(err)
}
}()
c := make(chan os.Signal, 1)
// We'll accept graceful shutdowns when quit via SIGINT (Ctrl+C)
// SIGKILL, SIGQUIT or SIGTERM (Ctrl+/) will not be caught.
signal.Notify(c, os.Interrupt)
// Block until we receive our signal.
<-c
// Create a deadline to wait for.
ctx, cancel := context.WithTimeout(context.Background(), wait)
defer cancel()
// Doesn't block if no connections, but will otherwise wait
// until the timeout deadline.
srv.Shutdown(ctx)
// Optionally, you could run srv.Shutdown in a goroutine and block on
// <-ctx.Done() if your application should wait for other services
// to finalize based on context cancellation.
log.Println("shutting down")
os.Exit(0)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment