xxxxxxxxxx
package helpers
import (
"context"
"fmt"
"net"
"net/http"
"os"
"os/signal"
"syscall"
"time"
"github.com/sirupsen/logrus"
"google.golang.org/grpc"
)
const (
TCP = "tcp"
UDPConn = "udp"
UNIXConn = "unix"
UNIXPACKET = "unixpacket"
)
type GracefulOptions struct {
Ctx context.Context
Server *grpc.Server
Address string
Port string
}
func Graceful(options *GracefulOptions) {
var (
nlc net.ListenConfig = net.ListenConfig{}
signalChan chan os.Signal = make(chan os.Signal, 1)
)
ctx, cancel := context.WithTimeout(options.Ctx, time.Duration(time.Second*120))
defer cancel()
signal.Notify(signalChan, syscall.SIGTERM, syscall.SIGABRT, syscall.SIGKILL, syscall.SIGINT, syscall.SIGALRM)
go func() {
sig := <-signalChan
logrus.Infof("Signal received %s", sig.String())
os.Exit(0)
}()
nls, err := nlc.Listen(ctx, "tcp", fmt.Sprintf("%s:%s", options.Address, options.Port))
if err != nil {
logrus.Fatal(err)
return
}
logrus.Print("\n")
logrus.Infof("Server listening on port %s", options.Port)
if err := options.Server.Serve(nls); err != nil && err != http.ErrServerClosed {
logrus.Fatal(err)
return
}
}
xxxxxxxxxx
func main() {
srv := &http.Server{
Addr: ":8080",
Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
time.Sleep(5 * time.Second)
_, _ = w.Write([]byte("hello"))
}),
}
if err := gracefulShutdown(srv, 10*time.Second); err != nil {
log.Println(err)
}
}
// gracefulShutdown stops the given HTTP server on
// receiving a stop signal and waits for the active connections
// to be closed for {timeout} period of time.
func gracefulShutdown(srv *http.Server, timeout time.Duration) error {
done := make(chan error, 1)
go func() {
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
<-c
ctx := context.Background()
var cancel context.CancelFunc
if timeout > 0 {
ctx, cancel = context.WithTimeout(ctx, timeout)
defer cancel()
}
done <- srv.Shutdown(ctx)
}()
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
return err
}
return <-done
}
xxxxxxxxxx
package main
import (
"context"
"flag"
"fmt"
"log"
"net/http"
"os"
"os/signal"
"time"
)
const (
service = "fooapi"
)
var (
port = flag.Int("port", 3333, "http port to listen on")
shutdownTimeout = flag.Duration("shutdown-timeout", 10*time.Second,
"shutdown timeout (5s,5m,5h) before connections are cancelled")
)
func main() {
flag.Parse()
mux := http.NewServeMux()
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(200)
})
srv := &http.Server{
Addr: fmt.Sprintf(":%d", *port),
Handler: mux,
}
stop := make(chan os.Signal)
signal.Notify(stop, os.Interrupt)
go func() {
log.Printf("%s listening on 0.0.0.0:%d with %v timeout", service, *port, *shutdownTimeout)
if err := srv.ListenAndServe(); err != nil {
if err != http.ErrServerClosed {
log.Fatal(err)
}
}
}()
<-stop
log.Printf("%s shutting down ...\n", service)
ctx, cancel := context.WithTimeout(context.Background(), *shutdownTimeout)
defer cancel()
if err := srv.Shutdown(ctx); err != nil {
log.Fatal(err)
}
log.Printf("%s down\n", service)
}
xxxxxxxxxx
package main
import (
"context"
"errors"
"log"
"net/http"
"os"
"os/signal"
"syscall"
"time"
)
func createChannel() (chan os.Signal, func()) {
stopCh := make(chan os.Signal, 1)
signal.Notify(stopCh, os.Interrupt, syscall.SIGTERM, syscall.SIGINT)
return stopCh, func() {
close(stopCh)
}
}
func start(server *http.Server) {
log.Println("application started")
if err := server.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
panic(err)
} else {
log.Println("application stopped gracefully")
}
}
func shutdown(ctx context.Context, server *http.Server) {
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()
if err := server.Shutdown(ctx); err != nil {
panic(err)
} else {
log.Println("application shutdowned")
}
}
func main() {
log.SetFlags(log.Lshortfile)
s := &http.Server{}
go start(s)
stopCh, closeCh := createChannel()
defer closeCh()
log.Println("notified:", <-stopCh)
shutdown(context.Background(), s)
}
xxxxxxxxxx
package main
import (
"context"
"errors"
"log"
"net/http"
"os"
"os/signal"
"syscall"
"time"
)
func createChannel() (chan os.Signal, func()) {
stopCh := make(chan os.Signal, 1)
signal.Notify(stopCh, os.Interrupt, syscall.SIGTERM, syscall.SIGINT)
return stopCh, func() {
close(stopCh)
}
}
func start(server *http.Server) {
log.Println("application started")
if err := server.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
panic(err)
} else {
log.Println("application stopped gracefully")
}
}
func shutdown(ctx context.Context, server *http.Server) {
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()
if err := server.Shutdown(ctx); err != nil {
panic(err)
} else {
log.Println("application shutdowned")
}
}
func main() {
log.SetFlags(log.Lshortfile)
s := &http.Server{}
go start(s)
stopCh, closeCh := createChannel()
defer closeCh()
log.Println("notified:", <-stopCh)
shutdown(context.Background(), s)
}
xxxxxxxxxx
func SetupGraceFullShutdown(handler *http.ServeMux) {
var wg sync.WaitGroup
httpServer := http.Server{
Addr: fmt.Sprintf(":%s", port),
ReadTimeout: time.Duration(time.Second) * 60,
WriteTimeout: time.Duration(time.Second) * 30,
IdleTimeout: time.Duration(time.Second) * 15,
MaxHeaderBytes: http.DefaultMaxHeaderBytes,
Handler: handler,
}
runtime.GOMAXPROCS(runtime.NumCPU())
wg.Add(1)
go func() {
if err := httpServer.ListenAndServe(); errors.Is(err, http.ErrServerClosed) {
log.Printf("Server not runnings: %s", err.Error())
}
log.Printf("Server running on port: %s", port)
stop := make(chan os.Signal, 1)
signal.Notify(stop, os.Interrupt, syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP)
if sig, ok := <-stop; ok {
log.Printf("Signal received: %v \n", sig)
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(time.Second*10))
defer cancel()
if _, ok := <-ctx.Done(); !ok {
log.Println("Waiting to HTTP server shutdown...")
if err := db.Close(); err != nil {
log.Printf("Database shutdown error: %s", err.Error())
}
if err := httpServer.Shutdown(ctx); err != nil {
log.Printf("HTTP server shutdown error: %s", err.Error())
}
defer close(stop)
log.Println("HTTP server shutdown success")
}
}
wg.Done()
}()
wg.Wait()
}