Skip to content

Commit 1c52628

Browse files
committed
Adds graceful shutdown for golang -middleware and -http
Closes: #35 Tested outside of a container, using kill -s TERM <PID> - it worked as expected by waiting for N seconds. Signed-off-by: Alex Ellis (OpenFaaS Ltd) <[email protected]>
1 parent 23db8b3 commit 1c52628

File tree

12 files changed

+256
-68
lines changed

12 files changed

+256
-68
lines changed

template/golang-http-armhf/.gitignore

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

template/golang-http-armhf/Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM openfaas/of-watchdog:0.7.3 as watchdog
1+
FROM openfaas/of-watchdog:0.7.6 as watchdog
22
FROM golang:1.13-alpine3.11 as build
33

44
RUN apk --no-cache add git

template/golang-http-armhf/main.go

Lines changed: 62 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,80 @@
11
package main
22

33
import (
4+
"context"
45
"fmt"
56
"io/ioutil"
67
"log"
78
"net/http"
89
"os"
10+
"os/signal"
911
"strconv"
12+
"sync/atomic"
13+
"syscall"
1014
"time"
1115

1216
"handler/function"
13-
// "github.com/alexellis/golang-http-template/template/golang-http/function"
17+
1418
handler "github.com/openfaas-incubator/go-function-sdk"
1519
)
1620

21+
var (
22+
acceptingConnections int32
23+
)
24+
25+
const defaultTimeout = 10 * time.Second
26+
27+
func main() {
28+
readTimeout := parseIntOrDurationValue(os.Getenv("read_timeout"), defaultTimeout)
29+
writeTimeout := parseIntOrDurationValue(os.Getenv("write_timeout"), defaultTimeout)
30+
31+
s := &http.Server{
32+
Addr: fmt.Sprintf(":%d", 8082),
33+
ReadTimeout: readTimeout,
34+
WriteTimeout: writeTimeout,
35+
MaxHeaderBytes: 1 << 20, // Max header of 1MB
36+
}
37+
38+
http.HandleFunc("/", makeRequestHandler())
39+
listenUntilShutdown(s, writeTimeout)
40+
}
41+
42+
func listenUntilShutdown(s *http.Server, shutdownTimeout time.Duration) {
43+
idleConnsClosed := make(chan struct{})
44+
go func() {
45+
sig := make(chan os.Signal, 1)
46+
signal.Notify(sig, syscall.SIGTERM)
47+
48+
<-sig
49+
50+
log.Printf("[entrypoint] SIGTERM received.. shutting down server in %s\n", shutdownTimeout.String())
51+
52+
<-time.Tick(shutdownTimeout)
53+
54+
if err := s.Shutdown(context.Background()); err != nil {
55+
log.Printf("[entrypoint] Error in Shutdown: %v", err)
56+
}
57+
58+
log.Printf("[entrypoint] No new connections allowed. Exiting in: %s\n", shutdownTimeout.String())
59+
60+
<-time.Tick(shutdownTimeout)
61+
62+
close(idleConnsClosed)
63+
}()
64+
65+
// Run the HTTP server in a separate go-routine.
66+
go func() {
67+
if err := s.ListenAndServe(); err != http.ErrServerClosed {
68+
log.Printf("[entrypoint] Error ListenAndServe: %v", err)
69+
close(idleConnsClosed)
70+
}
71+
}()
72+
73+
atomic.StoreInt32(&acceptingConnections, 1)
74+
75+
<-idleConnsClosed
76+
}
77+
1778
func makeRequestHandler() func(http.ResponseWriter, *http.Request) {
1879
return func(w http.ResponseWriter, r *http.Request) {
1980
var input []byte
@@ -74,18 +135,3 @@ func parseIntOrDurationValue(val string, fallback time.Duration) time.Duration {
74135
}
75136
return duration
76137
}
77-
78-
func main() {
79-
readTimeout := parseIntOrDurationValue(os.Getenv("read_timeout"), 10*time.Second)
80-
writeTimeout := parseIntOrDurationValue(os.Getenv("write_timeout"), 10*time.Second)
81-
82-
s := &http.Server{
83-
Addr: fmt.Sprintf(":%d", 8082),
84-
ReadTimeout: readTimeout,
85-
WriteTimeout: writeTimeout,
86-
MaxHeaderBytes: 1 << 20, // Max header of 1MB
87-
}
88-
89-
http.HandleFunc("/", makeRequestHandler())
90-
log.Fatal(s.ListenAndServe())
91-
}

template/golang-http/.gitignore

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

template/golang-http/Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM openfaas/of-watchdog:0.7.3 as watchdog
1+
FROM openfaas/of-watchdog:0.7.6 as watchdog
22
FROM golang:1.13-alpine3.11 as build
33

44
RUN apk --no-cache add git

template/golang-http/main.go

Lines changed: 62 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,80 @@
11
package main
22

33
import (
4+
"context"
45
"fmt"
56
"io/ioutil"
67
"log"
78
"net/http"
89
"os"
10+
"os/signal"
911
"strconv"
12+
"sync/atomic"
13+
"syscall"
1014
"time"
1115

1216
"handler/function"
13-
// "github.com/alexellis/golang-http-template/template/golang-http/function"
17+
1418
handler "github.com/openfaas-incubator/go-function-sdk"
1519
)
1620

21+
var (
22+
acceptingConnections int32
23+
)
24+
25+
const defaultTimeout = 10 * time.Second
26+
27+
func main() {
28+
readTimeout := parseIntOrDurationValue(os.Getenv("read_timeout"), defaultTimeout)
29+
writeTimeout := parseIntOrDurationValue(os.Getenv("write_timeout"), defaultTimeout)
30+
31+
s := &http.Server{
32+
Addr: fmt.Sprintf(":%d", 8082),
33+
ReadTimeout: readTimeout,
34+
WriteTimeout: writeTimeout,
35+
MaxHeaderBytes: 1 << 20, // Max header of 1MB
36+
}
37+
38+
http.HandleFunc("/", makeRequestHandler())
39+
listenUntilShutdown(s, writeTimeout)
40+
}
41+
42+
func listenUntilShutdown(s *http.Server, shutdownTimeout time.Duration) {
43+
idleConnsClosed := make(chan struct{})
44+
go func() {
45+
sig := make(chan os.Signal, 1)
46+
signal.Notify(sig, syscall.SIGTERM)
47+
48+
<-sig
49+
50+
log.Printf("[entrypoint] SIGTERM received.. shutting down server in %s\n", shutdownTimeout.String())
51+
52+
<-time.Tick(shutdownTimeout)
53+
54+
if err := s.Shutdown(context.Background()); err != nil {
55+
log.Printf("[entrypoint] Error in Shutdown: %v", err)
56+
}
57+
58+
log.Printf("[entrypoint] No new connections allowed. Exiting in: %s\n", shutdownTimeout.String())
59+
60+
<-time.Tick(shutdownTimeout)
61+
62+
close(idleConnsClosed)
63+
}()
64+
65+
// Run the HTTP server in a separate go-routine.
66+
go func() {
67+
if err := s.ListenAndServe(); err != http.ErrServerClosed {
68+
log.Printf("[entrypoint] Error ListenAndServe: %v", err)
69+
close(idleConnsClosed)
70+
}
71+
}()
72+
73+
atomic.StoreInt32(&acceptingConnections, 1)
74+
75+
<-idleConnsClosed
76+
}
77+
1778
func makeRequestHandler() func(http.ResponseWriter, *http.Request) {
1879
return func(w http.ResponseWriter, r *http.Request) {
1980
var input []byte
@@ -74,18 +135,3 @@ func parseIntOrDurationValue(val string, fallback time.Duration) time.Duration {
74135
}
75136
return duration
76137
}
77-
78-
func main() {
79-
readTimeout := parseIntOrDurationValue(os.Getenv("read_timeout"), 10*time.Second)
80-
writeTimeout := parseIntOrDurationValue(os.Getenv("write_timeout"), 10*time.Second)
81-
82-
s := &http.Server{
83-
Addr: fmt.Sprintf(":%d", 8082),
84-
ReadTimeout: readTimeout,
85-
WriteTimeout: writeTimeout,
86-
MaxHeaderBytes: 1 << 20, // Max header of 1MB
87-
}
88-
89-
http.HandleFunc("/", makeRequestHandler())
90-
log.Fatal(s.ListenAndServe())
91-
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/handler

template/golang-middleware-armhf/Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM openfaas/of-watchdog:0.7.3 as watchdog
1+
FROM openfaas/of-watchdog:0.7.6 as watchdog
22
FROM golang:1.13-alpine3.11 as build
33

44
RUN apk --no-cache add git

template/golang-middleware-armhf/main.go

Lines changed: 62 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,78 @@
11
package main
22

33
import (
4+
"context"
45
"fmt"
56
"log"
67
"net/http"
78
"os"
9+
"os/signal"
810
"strconv"
11+
"sync/atomic"
12+
"syscall"
913
"time"
1014

1115
"handler/function"
12-
//"github.com/openfaas-incubator/golang-http-template/template/golang-middleware/function"
1316
)
1417

18+
var (
19+
acceptingConnections int32
20+
)
21+
22+
const defaultTimeout = 10 * time.Second
23+
24+
func main() {
25+
readTimeout := parseIntOrDurationValue(os.Getenv("read_timeout"), defaultTimeout)
26+
writeTimeout := parseIntOrDurationValue(os.Getenv("write_timeout"), defaultTimeout)
27+
28+
s := &http.Server{
29+
Addr: fmt.Sprintf(":%d", 8082),
30+
ReadTimeout: readTimeout,
31+
WriteTimeout: writeTimeout,
32+
MaxHeaderBytes: 1 << 20, // Max header of 1MB
33+
}
34+
35+
http.HandleFunc("/", function.Handle)
36+
37+
listenUntilShutdown(s, writeTimeout)
38+
}
39+
40+
func listenUntilShutdown(s *http.Server, shutdownTimeout time.Duration) {
41+
idleConnsClosed := make(chan struct{})
42+
go func() {
43+
sig := make(chan os.Signal, 1)
44+
signal.Notify(sig, syscall.SIGTERM)
45+
46+
<-sig
47+
48+
log.Printf("[entrypoint] SIGTERM received.. shutting down server in %s\n", shutdownTimeout.String())
49+
50+
<-time.Tick(shutdownTimeout)
51+
52+
if err := s.Shutdown(context.Background()); err != nil {
53+
log.Printf("[entrypoint] Error in Shutdown: %v", err)
54+
}
55+
56+
log.Printf("[entrypoint] No new connections allowed. Exiting in: %s\n", shutdownTimeout.String())
57+
58+
<-time.Tick(shutdownTimeout)
59+
60+
close(idleConnsClosed)
61+
}()
62+
63+
// Run the HTTP server in a separate go-routine.
64+
go func() {
65+
if err := s.ListenAndServe(); err != http.ErrServerClosed {
66+
log.Printf("[entrypoint] Error ListenAndServe: %v", err)
67+
close(idleConnsClosed)
68+
}
69+
}()
70+
71+
atomic.StoreInt32(&acceptingConnections, 1)
72+
73+
<-idleConnsClosed
74+
}
75+
1576
func parseIntOrDurationValue(val string, fallback time.Duration) time.Duration {
1677
if len(val) > 0 {
1778
parsedVal, parseErr := strconv.Atoi(val)
@@ -26,18 +87,3 @@ func parseIntOrDurationValue(val string, fallback time.Duration) time.Duration {
2687
}
2788
return duration
2889
}
29-
30-
func main() {
31-
readTimeout := parseIntOrDurationValue(os.Getenv("read_timeout"), 10*time.Second)
32-
writeTimeout := parseIntOrDurationValue(os.Getenv("write_timeout"), 10*time.Second)
33-
34-
s := &http.Server{
35-
Addr: fmt.Sprintf(":%d", 8082),
36-
ReadTimeout: readTimeout,
37-
WriteTimeout: writeTimeout,
38-
MaxHeaderBytes: 1 << 20, // Max header of 1MB
39-
}
40-
41-
http.HandleFunc("/", function.Handle)
42-
log.Fatal(s.ListenAndServe())
43-
}

template/golang-middleware/.gitignore

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

template/golang-middleware/Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM openfaas/of-watchdog:0.7.3 as watchdog
1+
FROM openfaas/of-watchdog:0.7.6 as watchdog
22
FROM golang:1.13-alpine3.11 as build
33

44
RUN apk --no-cache add git

0 commit comments

Comments
 (0)