Skip to content

Commit 538c3c9

Browse files
authored
Merge pull request #913 from devlights/update-http-server-shutdown-example
2 parents ab34160 + 32545dc commit 538c3c9

File tree

3 files changed

+84
-24
lines changed

3 files changed

+84
-24
lines changed

examples/http/shutdown/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
server

examples/http/shutdown/Taskfile.yml

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
version: '3'
22

33
tasks:
4-
run:
4+
default:
55
cmds:
6-
- go run -race main.go
6+
- go build -o server main.go
7+
- ./server &
8+
- sleep 1
9+
- curl http://localhost:8888/
10+
- curl http://localhost:8888/something
11+
- kill -INT $(pgrep server)

examples/http/shutdown/main.go

Lines changed: 76 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2,43 +2,97 @@ package main
22

33
import (
44
"context"
5+
"errors"
6+
"fmt"
57
"log"
68
"net/http"
9+
"os"
10+
"os/signal"
11+
"syscall"
712
"time"
813
)
914

1015
func main() {
16+
log.SetFlags(log.Lmicroseconds)
17+
18+
if err := run(); err != nil {
19+
log.Fatalf("Application failed: %v", err)
20+
}
21+
}
22+
23+
func run() error {
1124
var (
12-
mux = http.NewServeMux()
13-
srv = &http.Server{Addr: ":8888", Handler: mux}
14-
mainCtx = context.Background()
15-
procCtx, procCxl = context.WithTimeout(mainCtx, 3*time.Second)
25+
port string
1626
)
17-
defer procCxl()
27+
if port = os.Getenv("PORT"); port == "" {
28+
port = "8888"
29+
}
1830

19-
// ----------- Start ----------- //
31+
var (
32+
mux = http.NewServeMux()
33+
srv = &http.Server{
34+
Addr: ":" + port,
35+
Handler: mux,
36+
MaxHeaderBytes: 1 << 20, // 1MB
37+
ReadTimeout: 10 * time.Second,
38+
WriteTimeout: 10 * time.Second,
39+
IdleTimeout: 120 * time.Second,
40+
}
41+
mainCtx, mainCxl = context.WithCancel(context.Background())
42+
errs = make(chan error, 1)
43+
)
44+
defer mainCxl()
2045

46+
// ----------- Handlers ----------- //
47+
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
48+
fmt.Fprintln(w, "Hello World")
49+
})
50+
51+
// ----------- Start ----------- //
2152
go func() {
22-
log.Println("Server is up.")
23-
srv.ListenAndServe()
53+
log.Printf("Server is listening on %s", srv.Addr)
54+
if err := srv.ListenAndServe(); err != nil {
55+
errs <- err
56+
}
2457
}()
2558

26-
<-procCtx.Done()
27-
28-
// ----------- Shutdown ----------- //
29-
59+
// ----------- Signals ----------- //
3060
var (
31-
shutdownCtx, shutdownCxl = context.WithTimeout(mainCtx, 1*time.Second)
61+
sigs = make(chan os.Signal, 1)
3262
)
33-
defer shutdownCxl()
34-
35-
if err := srv.Shutdown(shutdownCtx); err != nil {
36-
switch err {
37-
case context.DeadlineExceeded:
38-
log.Println("Server shutdown process timed out.")
39-
default:
40-
log.Fatal(err)
63+
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP)
64+
defer signal.Stop(sigs)
65+
66+
// ----------- Selects ----------- //
67+
select {
68+
case err := <-errs:
69+
if !errors.Is(err, http.ErrServerClosed) {
70+
return fmt.Errorf("server error: %w", err)
4171
}
72+
return nil
73+
case sig := <-sigs:
74+
log.Printf("Shutdown started: %v", sig)
75+
76+
// ----------- Shutdown ----------- //
77+
shutCtx, shutCxl := context.WithTimeout(mainCtx, 5*time.Second)
78+
defer shutCxl()
79+
80+
if err := srv.Shutdown(shutCtx); err != nil {
81+
if errors.Is(err, context.DeadlineExceeded) {
82+
log.Println("Server shutdown process timed out.")
83+
} else {
84+
log.Printf("Server shutdown failed: %v", err)
85+
}
86+
87+
if closeErr := srv.Close(); closeErr != nil {
88+
return fmt.Errorf("force close failed: %w", closeErr)
89+
}
90+
91+
return fmt.Errorf("shutdown error: %w", err)
92+
}
93+
94+
log.Println("Server has been gracefully shutdown.")
95+
96+
return nil
4297
}
43-
log.Println("Server has been shutdown.")
4498
}

0 commit comments

Comments
 (0)