Go 要传递什么上下文来等待某件事情完成?
出于各种原因,我觉得http.listenandservice不适合我的需要 我需要能够确定绑定的地址和端口(即使用Go 要传递什么上下文来等待某件事情完成?,go,Go,出于各种原因,我觉得http.listenandservice不适合我的需要 我需要能够确定绑定的地址和端口(即使用“:0”),因此我引入了net.Listener,读取Listener.Addr(),然后传递到http.service(Listener,nil) 然后我需要能够使用不同的URL处理程序运行两个HTTP服务器,因此我引入了一个HTTP.NewServeMux(),添加了必要的mux.HandleFunc(“/path”,fn)处理程序,并作为HTTP.service(listen
“:0”
),因此我引入了net.Listener
,读取Listener.Addr()
,然后传递到http.service(Listener,nil)
然后我需要能够使用不同的URL处理程序运行两个HTTP服务器,因此我引入了一个HTTP.NewServeMux()
,添加了必要的mux.HandleFunc(“/path”,fn)
处理程序,并作为HTTP.service(listener,mux)
传递
然后我需要能够干净地停止这些服务器,并关闭任何连接,独立于主程序本身,所以现在我引入了&http.Server{Handler:mux}
,我可以go func(){Server.service(listener)}()
理论上,我可以通过调用server.Shutdown(ctx)
来停止此操作,但现在import“context”
中的任何可用上下文似乎都不能提供我想要的内容。我希望能够等到完全关闭完成,然后继续我的代码
我的理解是我应该能够
理论上,我可以通过调用server.Shutdown(ctx)来停止这一过程,但现在导入“context”中的所有可用上下文似乎都不能提供我想要的内容。我希望能够等到完全关闭完成,然后继续我的代码
这意味着您应该传递一个不会超时的上下文。无论是context.Background()
还是context.TODO()
都不超时,因此都不合适(实际上,请参见下文)。您是否使用一个或另一个取决于您是否计划超时关闭(您应该防止粗糙的客户端阻止您关闭服务器)
我的理解是我应该能够
理论上,我可以通过调用server.Shutdown(ctx)来停止这一过程,但现在导入“context”中的所有可用上下文似乎都不能提供我想要的内容。我希望能够等到完全关闭完成,然后继续我的代码
这意味着您应该传递一个不会超时的上下文。无论是context.Background()
还是context.TODO()
都不超时,因此都不合适(实际上,请参见下文)。您是否使用一个或另一个取决于您是否计划超时关闭(您应该防止粗糙的客户端阻止您关闭服务器)
我的理解是,我应该能够在主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())
如果要限制关机等待服务器关机的时间,请将context.Background()
替换为使用截止日期创建的上下文。要在主goroutine中等待关机完成,请从该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())
如果要限制关机等待服务器关机的时间,请将context.Background()
替换为使用截止日期创建的上下文。谢谢,我相信您是对的。但是,在这种情况下,问题是我真的不明白我应该把同步放在哪里,以及我应该如何调用它。您是否能够提供一个经过修改的startHTTPServer
,以表明您的立场?我肯定有很多错误我没有处理,但我似乎找不到一种方法来优雅地处理它们,并实现一个ListenAndServe
等价物,它返回一些模糊可关闭的东西,同时保留一个简单的调用签名。对于没有超时的东西,context.Background()
是正确的,而不是context.TODO()
。根据文档:“代码应该使用context.TODO,当不清楚使用哪个上下文或者它还不可用时(因为周围的函数还没有扩展到接受上下文参数)。@Adrian我的论点是:您可能不应该使用没有超时的上下文,尤其是当您的服务器没有超时时。我建议这样做是因为我认为无限期的等待是危险的:应该加上一个——可能很长,但有限的——超时。谢谢你——我相信你是对的。但是,在这种情况下,问题是我真的不明白我应该把同步放在哪里,以及我应该如何调用它。您是否能够提供一个经过修改的startHTTPServer
,以表明您的立场?我肯定有很多错误我没有处理,但我似乎找不到一种方法来优雅地处理它们,并实现一个ListenAndServe
等价物,它返回一些模糊可关闭的东西,同时保留一个简单的调用签名。对于没有超时的东西,context.Background()
是正确的,而不是context.TODO()
。根据文档:“代码应该使用context.TODO,当不清楚使用哪个上下文或者它还不可用时(因为周围的函数还没有扩展到接受上下文参数)。@Adrian我的论点是:您可能不应该使用没有超时的上下文,尤其是当您的服务器没有超时时。我建议TODO,因为我认为无限期等待是危险的:应该添加一个——可能很长,但有限的——超时。匿名goroutine必须优雅地处理http.ErrServerClosed,以便完全关闭。对于没有超时的内容,context.Background()
是正确的,而不是context.TODO()
。根据文档:“代码应该使用context.TODO,当不清楚要使用哪个上下文或它还不可用时(因为周围的函数还没有扩展到接受上下文参数)。@Peter I更新了答案,以干净地处理ErrServerClosed。不需要停止通道和该通道上的goroutine接收。匿名gorou
// 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
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())