英文:
http.Server ListenAndServer and then instant Shutdown doesn't work
问题
我正在尝试解决一个较大程序中的问题,并编写了这个小的测试程序来帮助解决它。
如果我启动一个HTTP服务器,然后立即尝试关闭它,它不会关闭并继续提供请求。我可以推理出这种行为,但无法找到解决方法。我猜想这是因为在我尝试关闭它之前,服务器还没有完全启动,所以关闭失败,启动继续进行,然后完成,使其正常提供请求。
如何确保服务器处于可以关闭的状态,然后再调用关闭函数?如果你在启动和停止服务器之间注释掉 sleep 函数,你会发现它按预期工作。
package main
import (
"context"
"fmt"
"io"
"log"
"net/http"
"os"
"os/signal"
"time"
)
func main() {
// stop on ^c
quit := make(chan os.Signal)
signal.Notify(quit, os.Interrupt)
// router
mux := http.NewServeMux()
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Println("serving request")
io.WriteString(w, "Hello, world!\n")
})
// start server
srv := &http.Server{Addr: ":8080", Handler: mux}
go func() {
if err := srv.ListenAndServe(); err != nil {
log.Printf("listenAndServe failed: %v", err)
}
}()
fmt.Println("server started")
// time.Sleep(1 * time.Second)
// gracefully stop server
d := time.Now().Add(60 * time.Second)
ctx, cancel := context.WithDeadline(context.Background(), d)
defer cancel()
if err := srv.Shutdown(ctx); err != nil {
fmt.Println(err)
}
fmt.Println("server stopped")
<-quit
}
只需运行此程序并访问 http://localhost:8080。你会发现即使我们尝试关闭它,它仍然会继续提供请求。
程序的输出应该是:
:; go run main.go
server started
server stopped
2017/07/22 16:17:11 serve failed: http: Server closed
但实际输出是:
:; go run main.go
server started
server stopped
serving request
serving request
serving request
英文:
I'm trying to track down an issue in a larger program and wrote this small test program to help resolve it.
If I start a http server and then instantly try to shut it down it doesn't shutdown and continues to serve requests. I can reason about the behavior but can't work out a way around it. I'm assuming it's because the server hasn't fully started up before I try to shut it down and so the shutdown fails and the startup continues and then finishes leaving it to serve requests as normal.
How can I ensure the server is in a state that it can be shutdown before I call shutdown? If you comment out the sleep function between starting and stopping the server you will see it works as desired.
package main
import (
"context"
"fmt"
"io"
"log"
"net/http"
"os"
"os/signal"
"time"
)
func main() {
// stop on ^c
quit := make(chan os.Signal)
signal.Notify(quit, os.Interrupt)
// router
mux := http.NewServeMux()
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Println("serving request")
io.WriteString(w, "Hello, world!\n")
})
// start server
srv := &http.Server{Addr: ":8080", Handler: mux}
go func() {
if err := srv.ListenAndServe(); err != nil {
log.Printf("listenAndServe failed: %v", err)
}
}()
fmt.Println("server started")
// time.Sleep(1 * time.Second)
// gracefully stop server
d := time.Now().Add(60 * time.Second)
ctx, cancel := context.WithDeadline(context.Background(), d)
defer cancel()
if err := srv.Shutdown(ctx); err != nil {
fmt.Println(err)
}
fmt.Println("server stopped")
<-quit
}
Simply run this program and access http://localhost:8080. You will see it will keep serving requests even though we tried to shut it down.
The output of the program should be:
:; go run main.go
server started
server stopped
2017/07/22 16:17:11 serve failed: http: Server closed
instead it is:
:; go run main.go
server started
server stopped
serving request
serving request
serving request
答案1
得分: 4
您对srv.ListenAndServe
的调用可以在srv.Shutdown
之后进行,而且显然是这样做的。您可以通过在调用srv.ListenAndServe
之前的goroutine中添加日志语句来验证这一点。要查看在ListenAndServe
之后调用关闭是否起作用,您可以取消注释time.Sleep(1 * time.Second)
。在您的<-quit
之后立即调用Shutdown
是Shutdown
的典型用法。
func main() {
// 在^c上停止
quit := make(chan os.Signal)
signal.Notify(quit, os.Interrupt)
// 路由器
mux := http.NewServeMux()
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
log.Println("正在处理请求")
io.WriteString(w, "Hello, world!\n")
})
// 启动服务器
srv := &http.Server{Addr: ":8080", Handler: mux}
go func() {
log.Println("服务器启动中")
if err := srv.ListenAndServe(); err != nil {
log.Fatalf("listenAndServe 失败: %v", err)
}
}()
fmt.Println("服务器已启动")
<-quit
// 优雅地停止服务器
ctx, cancel := context.WithTimeout(context.Background(), 60 * time.Second)
defer cancel()
if err := srv.Shutdown(ctx); err != nil {
log.Fatal(err)
}
log.Println("服务器已停止")
}
英文:
Your call to srv.ListenAndServe
can be, and apparently is, called after srv.Shutdown
. You can verify this by adding a logging statement the goroutine just before the call to srv.ListenAndServe
. To see that the shutdown works when called after ListenAndServe
, you can uncomment your time.Sleep(1 * time.Second)
. Here a typical use of Shutdown
would be to call it right after your <-quit
:
func main() {
// stop on ^c
quit := make(chan os.Signal)
signal.Notify(quit, os.Interrupt)
// router
mux := http.NewServeMux()
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
log.Println("serving request")
io.WriteString(w, "Hello, world!\n")
})
// start server
srv := &http.Server{Addr: ":8080", Handler: mux}
go func() {
log.Println("server starting")
if err := srv.ListenAndServe(); err != nil {
log.Fatalf("listenAndServe failed: %v", err)
}
}()
fmt.Println("server started")
<-quit
// gracefully stop server
ctx, cancel := context.WithTimeout(context.Background(), 60 * time.Second)
defer cancel()
if err := srv.Shutdown(ctx); err != nil {
log.Fatal(err)
}
log.Println("server stopped")
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论