Go 要传递什么上下文来等待某件事情完成?

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

出于各种原因,我觉得http.listenandservice不适合我的需要

我需要能够确定绑定的地址和端口(即使用
“: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())