去正常关闭负WaitGroup

时间:2015-10-29 12:04:23

标签: go

我试图实现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
}

Custom Conn:

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()次呼叫?或者我错过了什么?

P.S。我尝试将stopped属性和互斥锁添加到gracefullConn,因此在Close中它会锁定互斥锁并检查stopped以确保我们只停止它一次。但是,我仍然受到同样的恐慌。

2 个答案:

答案 0 :(得分:2)

Close()可以多次调用,因此您肯定需要在func (w gracefulConn) Close() error中检查该内容。

  

P.S。我试图将已停止的属性和互斥锁添加到gracefullConn,因此在关闭时它会锁定互斥锁并检查已停止以确保我们只停止它一次。但是,我仍然受到同样的恐慌。

请记住gracefulConn如果作为值而不是引用传递,那么任何互斥锁/标志都不会按预期工作。因此,请务必将c = gracefulConn{Conn: c}变为c = &gracefulConn{Conn: c}

答案 1 :(得分:1)

我认为这是错误的方法。

你真的不关心连接是否开放 - 持久连接怎么样?

您关心的是您是否正在积极使用这些连接。您最好将WaitGroup放入我认为的ServeMux方法的自定义ServeHTTP中。然后,您可以在函数调用周围使用WaitGroup

相关问题