-
-
Save peterhellberg/38117e546c217960747aacf689af3dc2 to your computer and use it in GitHub Desktop.
package main | |
import ( | |
"context" | |
"log" | |
"net/http" | |
"os" | |
"os/signal" | |
"time" | |
) | |
type Server struct { | |
logger *log.Logger | |
mux *http.ServeMux | |
} | |
func NewServer(options ...func(*Server)) *Server { | |
s := &Server{ | |
logger: log.New(os.Stdout, "", 0), | |
mux: http.NewServeMux(), | |
} | |
for _, f := range options { | |
f(s) | |
} | |
s.mux.HandleFunc("/", s.index) | |
return s | |
} | |
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { | |
s.mux.ServeHTTP(w, r) | |
} | |
func (s *Server) index(w http.ResponseWriter, r *http.Request) { | |
s.logger.Println("GET /") | |
w.Write([]byte("Hello, World!")) | |
} | |
func main() { | |
stop := make(chan os.Signal, 1) | |
signal.Notify(stop, os.Interrupt) | |
logger := log.New(os.Stdout, "", 0) | |
addr := ":" + os.Getenv("PORT") | |
if addr == ":" { | |
addr = ":2017" | |
} | |
s := NewServer(func(s *Server) { s.logger = logger }) | |
h := &http.Server{Addr: addr, Handler: s} | |
go func() { | |
logger.Printf("Listening on http://0.0.0.0%s\n", addr) | |
if err := h.ListenAndServe(); err != nil { | |
logger.Fatal(err) | |
} | |
}() | |
<-stop | |
logger.Println("\nShutting down the server...") | |
ctx, _ := context.WithTimeout(context.Background(), 5*time.Second) | |
h.Shutdown(ctx) | |
logger.Println("Server gracefully stopped") | |
} |
@peterhellberg Nice additions 👍
hi~~
The line 62 of first demo "graceful.go" "logger.Fatal(err)" maybe lead to last print (" Server gracefully stopped" )not display
@wangjinbei: Yes, this gist mainly contains improvements to the original code in the comments. I didn't want to change it too much in order to not confuse people :)
@peterhellberg Great examples!
Go vet would indicate a leak on the cancel function for the first one on context.WithTimeout.
I got Error: context deadline exceeded
when I first to request http://0.0.0.0:2017
and then shutdown
in Example with graceful function
There are more below lol. Great job!
hey,
I created a package which does all the work for you with a simple api:
https://github.com/pseidemann/finish
let me know what you think!
@peterhellberg Thanks for posting these examples. Really helpful.
@peterhellberg Thanks for sharing this!
Hi, great contribution.
There is one thing that could make it more complete in my opinion.
Everywhere this code is, it will always print "Listening on http://...." in the console, even if ListenAndServe()
was unable to start actually listening.
log.Printf("Listening on http://0.0.0.0%s\n", hs.Addr)
if err := hs.ListenAndServe(); err != http.ErrServerClosed {
log.Fatal(err)
}
If you start 1 server, you'll see this in the console:
Listening on http://0.0.0.0:2017
If you start a 2nd server without shutting down the 1st one, you will see this in the console:
Listening on http://0.0.0.0:2017
panic... listen tcp :2017: bind: address already in use
So it first shows "Listening on http://..." however this never actually happened.
I'm not a go expert, is there a way to implement this?
-- UPDATE
Apparently using http.Listen
and http.Serve
individually, it is possible to catch a port in use error (or any other error while trying to listen to a tcp network) before assuming the server is listening.
Source: https://stackoverflow.com/a/48250354/194630
listener, err := net.Listen("tcp", ":"+config.Port)
if err != nil {
log.Fatal(err)
}
done := make(chan bool)
go server.Serve(listener)
// Log server started
log.Printf("Server started at port %v", config.Port)
<-done
@pci Since I do logging on the listening URL in almost every service I write I've now also added
LogListenAndServe
: