【发布时间】:2016-01-29 13:35:18
【问题描述】:
我已尝试实现 go 服务器的正常关闭,如这篇博文 http://grisha.org/blog/2014/06/03/graceful-restart-in-golang/ 中所述。主要内容如下。
自定义监听器:
var httpWg sync.WaitGroup // initialised in the other part
type gracefulListener struct {
net.Listener
stop chan error
stopped bool
}
func newGracefulListener(l net.Listener) (gl *gracefulListener) {
gl = &gracefulListener{Listener: l, stop: make(chan error)}
go func() {
_ = <-gl.stop
gl.stopped = true
gl.stop <- gl.Listener.Close()
}()
return
}
func (gl *gracefulListener) Accept() (c net.Conn, err error) {
c, err = gl.Listener.Accept()
if err != nil {
return
}
c = gracefulConn{Conn: c} // wrap using our custom connection
httpWg.Add(1) // increase the counter
return
}
func (gl *gracefulListener) Close() error {
if gl.stopped {
return syscall.EINVAL
}
gl.stop <- nil
return <-gl.stop
}
func (gl *gracefulListener) File() *os.File {
tl := gl.Listener.(*net.TCPListener)
fl, _ := tl.File()
return fl
}
自定义连接:
type gracefulConn struct {
net.Conn
}
func (w gracefulConn) Close() error {
httpWg.Done() // <- panics sometimes
return w.Conn.Close()
}
这个想法是当程序收到 SIGTERM 时,它会停止提供新的连接,并等待 httpWg.Wait() 完成现有连接。
这种方法在本地有效,但是当我部署它时,有时我会在gracefulConn.Close()httpWg.Done() 行收到恐慌:
panic: sync: negative WaitGroup counter
恐慌不是在我停止服务器时发生,而是在例行服务期间发生。
怎么可能有更多的Close() 电话然后Accept() 电话?还是我错过了什么?
附:我尝试将stopped 属性和互斥锁添加到gracefullConn,因此在Close 中它会锁定互斥锁并检查stopped 以确保我们只停止它一次。但是,我仍然感到同样的恐慌。
【问题讨论】:
-
很可能
Close()在同一个gracefulConn 实例上被调用了两次。如果不查看其余代码,很难说出这是如何发生的。
标签: go