Skip to content

Instantly share code, notes, and snippets.

@marcofranssen
Last active June 13, 2019 18:25
Show Gist options
  • Save marcofranssen/699c1aa97c8a33ab20b5eccada275b08 to your computer and use it in GitHub Desktop.
Save marcofranssen/699c1aa97c8a33ab20b5eccada275b08 to your computer and use it in GitHub Desktop.
Code not executing line 29 until interrupt channel is received
package main
import (
"flag"
)
var (
listenAddr string
)
func main() {
flag.StringVar(&listenAddr, "listen-addr", ":5000", "server listen address")
flag.Parse()
server, err := NewServer(listenAddr)
if err != nil {
panic(err)
}
server.Start()
}
package main
import (
"context"
"log"
"net/http"
"os"
"os/signal"
"time"
)
// Server provides an http.Server
type Server struct {
log *log.Logger
*http.Server
}
// NewServer creates and configures an APIServer serving all application routes.
func NewServer(listenAddr string) (*Server, error) {
logger := log.New(os.Stdout, "http: ", log.LstdFlags)
api := http.NewServeMux()
if err != nil {
return nil, err
}
srv := http.Server{
Addr: listenAddr,
Handler: api,
ErrorLog: logger,
ReadTimeout: 5 * time.Second,
WriteTimeout: 10 * time.Second,
IdleTimeout: 15 * time.Second,
}
return &Server{logger, &srv}, nil
}
// Start runs ListenAndServe on the http.Server with graceful shutdown
func (srv *Server) Start() {
srv.log.Println("Starting server...")
done := make(chan bool, 1)
go srv.gracefullShutdown(done)
srv.log.Println("Gracefull shutdown interupt signal registered")
// code below will not run until interupt signal is given using ctrl+c
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
srv.log.Fatalf("Could not listen on %s: %v\n", srv.Addr, err)
}
srv.log.Println("Server is ready to handle requests at", srv.Addr)
<-done
src.log.Println("Server stopped")
}
func (srv *Server) gracefullShutdown(done chan<- bool) {
quit := make(chan os.Signal)
signal.Notify(quit, os.Interrupt)
sig := <-quit
srv.log.Println("Server is shutting down... Reason:", sig)
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
srv.SetKeepAlivesEnabled(false)
if err := srv.Shutdown(ctx); err != nil {
srv.log.Fatalf("Could not gracefully shutdown the server: %v\n", err)
}
close(done)
}
@marcofranssen
Copy link
Author

The result of running the webserver currently behaves as following.

$ go build && ./my-api.exe
http: 2019/06/13 19:51:40 Starting server...
http: 2019/06/13 19:51:40 after grace routine
http: 2019/06/13 19:51:45 Server is shutting down... Reason: interrupt
http: 2019/06/13 19:51:45 Server is ready to handle requests at :5000
http: 2019/06/13 19:51:45 Server stopped

Note the server is only started after the interrupt was received.

I would expect it to behave like this.

$ go build && ./my-api.exe
http: 2019/06/13 19:51:40 Starting server...
http: 2019/06/13 19:51:40 after grace routine
http: 2019/06/13 19:51:40 Server is ready to handle requests at :5000
http: 2019/06/13 19:51:45 Server is shutting down... Reason: interrupt
http: 2019/06/13 19:51:45 Server stopped

Note the server starts immediately and five seconds later the interrupt is given and the server is shutdown.

This more simpler less structured example I blogged about earlier works perfectly as expected.
https://marcofranssen.nl/go-webserver-with-gracefull-shutdown/#TLDR

I just don't understand why my new code with a bit more structure doesn't work. Could anyone explain this?

@marcofranssen
Copy link
Author

I found the solution with some help of a fellow gopher.
It appears I swapped my logline with the location of that logline in my original implementation.

Before I was logging before the line with ListenAndServer now I moved it after. ListenAndServe ofcourse is blocking operation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment