Bu örnekte, aktif olarak çalışan istekleri bir süre bekleyip (5 saniye), eğer daha uzun sürerlerse onları öldürmeden önce signal.NotifyContext'i kullanacağız.


package main

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

type app struct {
server *http.Server
shutdown time.Duration
}

func (a app) start() error {
if err := a.server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
return err
}

return nil
}

func (a app) stop(ctx context.Context) error {
ctxNotify, stop := signal.NotifyContext(ctx, os.Interrupt, syscall.SIGTERM)
<-ctxNotify.Done()
stop()

ctxTimeout, cancel := context.WithTimeout(ctx, a.shutdown)
defer cancel()

if err := a.server.Shutdown(ctxTimeout); err != nil {
return err
}

return nil
}

func main() {
handler := http.DefaultServeMux
handler.HandleFunc("/", home)

server := &http.Server{
Addr: ":1234",
Handler: handler,
}

app := app{
server: server,
shutdown: time.Second * 5,
}

go func() {
log.Println("app is running")

if err := app.start(); err != nil {
log.Println("app start:", err)

return
}
}()

if err := app.stop(context.Background()); err != nil {
log.Println("dirty app stop with possible interruptions:", err)
} else {
log.Println("clean app stop without any interruption")
}
}

func home(w http.ResponseWriter, r *http.Request) {
log.Println("in")
time.Sleep(time.Second * 10) <- Adjust this in tests
log.Println("out")
}

Testler


$ curl http://0.0.0.0:1234/ çağrıldığında ctrl+c tuşlarını kullanın.


2 saniyelik bekleme


time.Sleep(time.Second * 2)

$ go run -race main.go
2024/01/21 14:02:14 app is running

2024/01/21 14:02:35 in
^C
2024/01/21 14:02:37 out
2024/01/21 14:02:37 clean app stop without any interruption

8 saniyelik bekleme


time.Sleep(time.Second * 8)

$ go run -race main.go
2024/01/21 14:03:28 app is running

2024/01/21 14:03:32 in
^C
2024/01/21 14:03:37 dirty app stop with possible interruptions: context deadline exceeded