2 回答
TA贡献1824条经验 获得超5个赞
要在主 goroutine 中等待 Shutdown 完成,请从该 goroutine 调用 Shutdown。消除通道和额外的 goroutine。
listener, _ := net.Listen("tcp", "localhost:0")
fmt.Printf("Listening on http://%v\n", listener.Addr())
mux := http.NewServeMux()
mux.HandleFunc("/", handleIndex)
server := &http.Server{Handler: mux}
go func() {
fmt.Println("Starting server...")
err := server.Serve(listener)
if err != nil && err != http.ErrServerClosed {
log.Fatal(err)
}
}()
time.Sleep(5 * time.Second) // delay here just for example of "long-running" server
// Shutdown and wait for server to complete.
server.Shutdown(context.Background())
如果要限制 Shutdown 等待服务器关闭的时间,请替换context.Background()为使用截止日期创建的上下文。
TA贡献1934条经验 获得超2个赞
理论上,我可以通过调用 server.Shutdown(ctx) 来阻止它,但现在 import "context" 中的任何可用上下文似乎都不能提供我想要的。我希望能够等到干净关闭完成,然后继续我的代码。
这意味着您应该传递一个不会超时的上下文。既不context.Background()也不context.TODO()超时,因此是合适的(真的,见下文)。您是否使用其中一个取决于您是否计划超时关闭(您应该防止粗糙的客户端阻止您关闭您的服务器)。
我的理解是我应该能够 <- ctx.Done() 来实现这一点,但我已经尝试了 context.Background() 和 context.TODO() 并且似乎都没有“触发”ctx.Done(),我最终永远阻塞了。其他上下文选项似乎是基于时间的。
嗯,这个是错的。也context.Background()不会context.TODO()关闭它们Done,因为它们不会超时。但是不需要 等待 done: 是一个正常的函数,一旦服务器实际正确关闭(这就是你想要的)或上下文超时,它就会返回。无论如何只是返回。server.Shutdownserver.Shutdown
一个简单的
server.Shutdown(context.TODO())
是你想要的。(现在,从长远来看:传递一个在很长但有限的时间后超时的上下文。)
但是无论如何,您的代码看起来都很可疑:您的 func startHTTPServer 没有正确处理错误:不是启动错误,也不是停止错误。如果您的服务器没有启动,您将无法停止它,您的代码只会吞下错误。犯错也很活泼。您的问题可能不是来自传递给 server.Shutdown 的上下文,而是来自其他地方。
问题是你的代码没有等待 server.Shutdown 返回,因为这个函数是在一个 goroutine 中启动的,没有任何同步回 startHTTPServer 的调用者:不要那样做。真的: server.Shutdown 的上下文不是问题。
以下是未经测试的代码,它稍微克服了这些问题。正如您在所有 TOOD 中看到的那样,它还没有准备好生产。
// startHTTPServer is a helper function to start a server and returns
// a channel to stop the server and a channel reporting errors during
// starting/stopping the server or nil if the server was shut down
// properly
func startHTTPServer(listener net.Listener, handler http.Handler) (stop chan bool, problems chan error) {
stop, problems = make(chan bool), make(chan error)
server := &http.Server{Handler: handler} // TODO: set timeouts
go func() {
fmt.Println("Starting server...")
err := server.Serve(listener)
if err != http.ErrServerClosed {
problems <- err // TODO: tag/classify as startup error
}
}()
go func() {
select {
case <-stop:
fmt.Println("Stop channel closed; stopping server...")
err := server.Shutdown(context.TODO())
fmt.Println("Stopped.")
problems <- err // TODO: tag/classify as shutdown error
case e := <-problems:
problems <- e // resend, this error is not for us
return // stop waiting for stop as server did not start anyway.
}
}()
return stop, problems
}
其他解决方案也是可能的,例如返回单独的启动和关闭错误通道等。
- 2 回答
- 0 关注
- 117 浏览
添加回答
举报