Graceful shutdown of TCP server
robertgates55 opened this issue · 1 comments
Thanks for this - all sorts of great stuff in here. Obviously this isn't an issue, per se, but it felt like a decent way to ask the question!
I've been looking at implementing graceful shutdown for a project we're working on, but struggled to get your demo to run in the way I expected. I've created:
shutdown.go
:
// This version expects all clients to close their connections before it
// successfully returns from Stop().
//
// Eli Bendersky [https://eli.thegreenplace.net]
// This code is in the public domain.
package main
import (
"io"
"log"
"net"
"sync"
)
type Server struct {
listener net.Listener
quit chan interface{}
wg sync.WaitGroup
}
func NewServer(addr string) *Server {
s := &Server{
quit: make(chan interface{}),
}
l, err := net.Listen("tcp", addr)
if err != nil {
log.Fatal(err)
}
s.listener = l
s.wg.Add(1)
go s.serve()
return s
}
func (s *Server) Stop() {
close(s.quit)
s.listener.Close()
s.wg.Wait()
}
func (s *Server) serve() {
defer s.wg.Done()
for {
conn, err := s.listener.Accept()
if err != nil {
select {
case <-s.quit:
return
default:
log.Println("accept error", err)
}
} else {
s.wg.Add(1)
go func() {
s.handleConection(conn)
s.wg.Done()
}()
}
}
}
func (s *Server) handleConection(conn net.Conn) {
defer conn.Close()
buf := make([]byte, 2048)
for {
n, err := conn.Read(buf)
if err != nil && err != io.EOF {
log.Println("read error", err)
return
}
if n == 0 {
return
}
log.Printf("received from %v: %s", conn.RemoteAddr(), string(buf[:n]))
}
}
func init() {
log.SetFlags(log.Ltime | log.Lmicroseconds)
}
func main() {
log.Println("Starting")
s := NewServer("0.0.0.0:1234")
// do whatever here...
log.Println("Whatever")
log.Println("Stopping")
s.Stop()
log.Println("Stopped")
}
It's a direct copy/paste from this repo, with a main()
function invoking it (And a couple of extra bits of logging).
>> go run shutdown.go
What I expected - I expected a blocking server, listening on :1234
What happens - it completes immediately:
➜ go run shutdown.go
19:34:44.492644 Starting
19:34:44.493001 Whatever
19:34:44.493002 Stopping
19:34:44.493036 Stopped
➜
What am I missing? What should the // do whatever here...
actually contain if I just want a server listening until I SIGKILL
the process? I presume I need something to block - but based on your description I thought that's what the code was doing?
You invoke s.Stop()
, which tells the server to stop listening and shut down. I'm not sure what you expect to happen.