Skip to content

Commit

Permalink
Adds graceful shutdown for golang -middleware and -http
Browse files Browse the repository at this point in the history
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) <alexellis2@gmail.com>
  • Loading branch information
alexellis committed Feb 6, 2020
1 parent 23db8b3 commit 1c52628
Show file tree
Hide file tree
Showing 12 changed files with 256 additions and 68 deletions.
1 change: 1 addition & 0 deletions template/golang-http-armhf/.gitignore
@@ -0,0 +1 @@
/handler
2 changes: 1 addition & 1 deletion template/golang-http-armhf/Dockerfile
@@ -1,4 +1,4 @@
FROM openfaas/of-watchdog:0.7.3 as watchdog
FROM openfaas/of-watchdog:0.7.6 as watchdog
FROM golang:1.13-alpine3.11 as build

RUN apk --no-cache add git
Expand Down
78 changes: 62 additions & 16 deletions template/golang-http-armhf/main.go
@@ -1,19 +1,80 @@
package main

import (
"context"
"fmt"
"io/ioutil"
"log"
"net/http"
"os"
"os/signal"
"strconv"
"sync/atomic"
"syscall"
"time"

"handler/function"
// "github.com/alexellis/golang-http-template/template/golang-http/function"

handler "github.com/openfaas-incubator/go-function-sdk"
)

var (
acceptingConnections int32
)

const defaultTimeout = 10 * time.Second

func main() {
readTimeout := parseIntOrDurationValue(os.Getenv("read_timeout"), defaultTimeout)
writeTimeout := parseIntOrDurationValue(os.Getenv("write_timeout"), defaultTimeout)

s := &http.Server{
Addr: fmt.Sprintf(":%d", 8082),
ReadTimeout: readTimeout,
WriteTimeout: writeTimeout,
MaxHeaderBytes: 1 << 20, // Max header of 1MB
}

http.HandleFunc("/", makeRequestHandler())
listenUntilShutdown(s, writeTimeout)
}

func listenUntilShutdown(s *http.Server, shutdownTimeout time.Duration) {
idleConnsClosed := make(chan struct{})
go func() {
sig := make(chan os.Signal, 1)
signal.Notify(sig, syscall.SIGTERM)

<-sig

log.Printf("[entrypoint] SIGTERM received.. shutting down server in %s\n", shutdownTimeout.String())

<-time.Tick(shutdownTimeout)

if err := s.Shutdown(context.Background()); err != nil {
log.Printf("[entrypoint] Error in Shutdown: %v", err)
}

log.Printf("[entrypoint] No new connections allowed. Exiting in: %s\n", shutdownTimeout.String())

<-time.Tick(shutdownTimeout)

close(idleConnsClosed)
}()

// Run the HTTP server in a separate go-routine.
go func() {
if err := s.ListenAndServe(); err != http.ErrServerClosed {
log.Printf("[entrypoint] Error ListenAndServe: %v", err)
close(idleConnsClosed)
}
}()

atomic.StoreInt32(&acceptingConnections, 1)

<-idleConnsClosed
}

func makeRequestHandler() func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
var input []byte
Expand Down Expand Up @@ -74,18 +135,3 @@ func parseIntOrDurationValue(val string, fallback time.Duration) time.Duration {
}
return duration
}

func main() {
readTimeout := parseIntOrDurationValue(os.Getenv("read_timeout"), 10*time.Second)
writeTimeout := parseIntOrDurationValue(os.Getenv("write_timeout"), 10*time.Second)

s := &http.Server{
Addr: fmt.Sprintf(":%d", 8082),
ReadTimeout: readTimeout,
WriteTimeout: writeTimeout,
MaxHeaderBytes: 1 << 20, // Max header of 1MB
}

http.HandleFunc("/", makeRequestHandler())
log.Fatal(s.ListenAndServe())
}
1 change: 1 addition & 0 deletions template/golang-http/.gitignore
@@ -0,0 +1 @@
/handler
2 changes: 1 addition & 1 deletion template/golang-http/Dockerfile
@@ -1,4 +1,4 @@
FROM openfaas/of-watchdog:0.7.3 as watchdog
FROM openfaas/of-watchdog:0.7.6 as watchdog
FROM golang:1.13-alpine3.11 as build

RUN apk --no-cache add git
Expand Down
78 changes: 62 additions & 16 deletions template/golang-http/main.go
@@ -1,19 +1,80 @@
package main

import (
"context"
"fmt"
"io/ioutil"
"log"
"net/http"
"os"
"os/signal"
"strconv"
"sync/atomic"
"syscall"
"time"

"handler/function"
// "github.com/alexellis/golang-http-template/template/golang-http/function"

handler "github.com/openfaas-incubator/go-function-sdk"
)

var (
acceptingConnections int32
)

const defaultTimeout = 10 * time.Second

func main() {
readTimeout := parseIntOrDurationValue(os.Getenv("read_timeout"), defaultTimeout)
writeTimeout := parseIntOrDurationValue(os.Getenv("write_timeout"), defaultTimeout)

s := &http.Server{
Addr: fmt.Sprintf(":%d", 8082),
ReadTimeout: readTimeout,
WriteTimeout: writeTimeout,
MaxHeaderBytes: 1 << 20, // Max header of 1MB
}

http.HandleFunc("/", makeRequestHandler())
listenUntilShutdown(s, writeTimeout)
}

func listenUntilShutdown(s *http.Server, shutdownTimeout time.Duration) {
idleConnsClosed := make(chan struct{})
go func() {
sig := make(chan os.Signal, 1)
signal.Notify(sig, syscall.SIGTERM)

<-sig

log.Printf("[entrypoint] SIGTERM received.. shutting down server in %s\n", shutdownTimeout.String())

<-time.Tick(shutdownTimeout)

if err := s.Shutdown(context.Background()); err != nil {
log.Printf("[entrypoint] Error in Shutdown: %v", err)
}

log.Printf("[entrypoint] No new connections allowed. Exiting in: %s\n", shutdownTimeout.String())

<-time.Tick(shutdownTimeout)

close(idleConnsClosed)
}()

// Run the HTTP server in a separate go-routine.
go func() {
if err := s.ListenAndServe(); err != http.ErrServerClosed {
log.Printf("[entrypoint] Error ListenAndServe: %v", err)
close(idleConnsClosed)
}
}()

atomic.StoreInt32(&acceptingConnections, 1)

<-idleConnsClosed
}

func makeRequestHandler() func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
var input []byte
Expand Down Expand Up @@ -74,18 +135,3 @@ func parseIntOrDurationValue(val string, fallback time.Duration) time.Duration {
}
return duration
}

func main() {
readTimeout := parseIntOrDurationValue(os.Getenv("read_timeout"), 10*time.Second)
writeTimeout := parseIntOrDurationValue(os.Getenv("write_timeout"), 10*time.Second)

s := &http.Server{
Addr: fmt.Sprintf(":%d", 8082),
ReadTimeout: readTimeout,
WriteTimeout: writeTimeout,
MaxHeaderBytes: 1 << 20, // Max header of 1MB
}

http.HandleFunc("/", makeRequestHandler())
log.Fatal(s.ListenAndServe())
}
1 change: 1 addition & 0 deletions template/golang-middleware-armhf/.gitignore
@@ -0,0 +1 @@
/handler
2 changes: 1 addition & 1 deletion template/golang-middleware-armhf/Dockerfile
@@ -1,4 +1,4 @@
FROM openfaas/of-watchdog:0.7.3 as watchdog
FROM openfaas/of-watchdog:0.7.6 as watchdog
FROM golang:1.13-alpine3.11 as build

RUN apk --no-cache add git
Expand Down
78 changes: 62 additions & 16 deletions template/golang-middleware-armhf/main.go
@@ -1,17 +1,78 @@
package main

import (
"context"
"fmt"
"log"
"net/http"
"os"
"os/signal"
"strconv"
"sync/atomic"
"syscall"
"time"

"handler/function"
//"github.com/openfaas-incubator/golang-http-template/template/golang-middleware/function"
)

var (
acceptingConnections int32
)

const defaultTimeout = 10 * time.Second

func main() {
readTimeout := parseIntOrDurationValue(os.Getenv("read_timeout"), defaultTimeout)
writeTimeout := parseIntOrDurationValue(os.Getenv("write_timeout"), defaultTimeout)

s := &http.Server{
Addr: fmt.Sprintf(":%d", 8082),
ReadTimeout: readTimeout,
WriteTimeout: writeTimeout,
MaxHeaderBytes: 1 << 20, // Max header of 1MB
}

http.HandleFunc("/", function.Handle)

listenUntilShutdown(s, writeTimeout)
}

func listenUntilShutdown(s *http.Server, shutdownTimeout time.Duration) {
idleConnsClosed := make(chan struct{})
go func() {
sig := make(chan os.Signal, 1)
signal.Notify(sig, syscall.SIGTERM)

<-sig

log.Printf("[entrypoint] SIGTERM received.. shutting down server in %s\n", shutdownTimeout.String())

<-time.Tick(shutdownTimeout)

if err := s.Shutdown(context.Background()); err != nil {
log.Printf("[entrypoint] Error in Shutdown: %v", err)
}

log.Printf("[entrypoint] No new connections allowed. Exiting in: %s\n", shutdownTimeout.String())

<-time.Tick(shutdownTimeout)

close(idleConnsClosed)
}()

// Run the HTTP server in a separate go-routine.
go func() {
if err := s.ListenAndServe(); err != http.ErrServerClosed {
log.Printf("[entrypoint] Error ListenAndServe: %v", err)
close(idleConnsClosed)
}
}()

atomic.StoreInt32(&acceptingConnections, 1)

<-idleConnsClosed
}

func parseIntOrDurationValue(val string, fallback time.Duration) time.Duration {
if len(val) > 0 {
parsedVal, parseErr := strconv.Atoi(val)
Expand All @@ -26,18 +87,3 @@ func parseIntOrDurationValue(val string, fallback time.Duration) time.Duration {
}
return duration
}

func main() {
readTimeout := parseIntOrDurationValue(os.Getenv("read_timeout"), 10*time.Second)
writeTimeout := parseIntOrDurationValue(os.Getenv("write_timeout"), 10*time.Second)

s := &http.Server{
Addr: fmt.Sprintf(":%d", 8082),
ReadTimeout: readTimeout,
WriteTimeout: writeTimeout,
MaxHeaderBytes: 1 << 20, // Max header of 1MB
}

http.HandleFunc("/", function.Handle)
log.Fatal(s.ListenAndServe())
}
1 change: 1 addition & 0 deletions template/golang-middleware/.gitignore
@@ -0,0 +1 @@
/handler
2 changes: 1 addition & 1 deletion template/golang-middleware/Dockerfile
@@ -1,4 +1,4 @@
FROM openfaas/of-watchdog:0.7.3 as watchdog
FROM openfaas/of-watchdog:0.7.6 as watchdog
FROM golang:1.13-alpine3.11 as build

RUN apk --no-cache add git
Expand Down

0 comments on commit 1c52628

Please sign in to comment.