DEV Community

loading...

graceful shutdown server with syg

#go
nasa9084 profile image nasa9084 Originally published at blog.web-apps.tech on ・2 min read

originally posted to blob.web-apps.tech

Using github.com/nasa9084/syg, you can map signal to callback function easily.(c.f. syg: simply signal to callback mapping in Go)

Now, let's implement a HTTP server which shuts down gracefully when catch SIGINT.

package app

import (
    "context"
    "net/http"
    "os"
    "time"

    "github.com/nasa9084/syg"
)

type Server struct {
    server *http.Server
    closed chan struct{}
}

func NewServer() *Server {
    http.HandleFunc("/", longlongHandler)
    return &Server{
        server: &http.Server{
            Addr: ":8080",
        },
        closed: make(chan struct{}),
    }
}

func (s *Server) Run() error {
    // os.Interrupt = syscall.SIGINT
    cancel := syg.Listen(s.shutdown, os.Interrupt)
    defer cancel()

    err := s.server.ListenAndServe()
    <-s.closed
    return err
}

func (s *Server) shutdown(os.Signal) {
    s.Shutdown(context.Background())
    close(s.closed)
}

func longlongHandler(w http.ResponseWriter, r *http.Request) {
    // a very long process!
    time.Sleep(10 * time.Second)
    w.Write([]byte("hello"))
}

call from main:

package main

import (
    "log"

    "foo/bar/app" // assume the app package above is in $GOPATH/foo/bar/app
)

func main() {
    s := app.NewServer()
    if err := s.Run(); err != http.ErrServerClosed {
        log.Print(err)
    }
}

In main, you don't need to think goroutine, graceful shutdown, or signal listening.

Check the server is running well.

At first, build and run the server:

$ go build main.go
$ ./main

Then launch one more Terminal, and send a request:

$ curl localhost:8080
hello

after 10 seconds, message "hello" is returned.

Now, send request once again, type Ctrl-C(this means SIGINT) in the first terminal before returned the response.
The server will shut down after return the response, not suddenly.

We did it!

Discussion

pic
Editor guide