http.Server的ListenAndServe方法和立即调用Shutdown方法无法正常工作。

huangapple go评论123阅读模式
英文:

http.Server ListenAndServer and then instant Shutdown doesn't work

问题

我正在尝试解决一个较大程序中的问题,并编写了这个小的测试程序来帮助解决它。

如果我启动一个HTTP服务器,然后立即尝试关闭它,它不会关闭并继续提供请求。我可以推理出这种行为,但无法找到解决方法。我猜想这是因为在我尝试关闭它之前,服务器还没有完全启动,所以关闭失败,启动继续进行,然后完成,使其正常提供请求。

如何确保服务器处于可以关闭的状态,然后再调用关闭函数?如果你在启动和停止服务器之间注释掉 sleep 函数,你会发现它按预期工作。

  1. package main
  2. import (
  3. "context"
  4. "fmt"
  5. "io"
  6. "log"
  7. "net/http"
  8. "os"
  9. "os/signal"
  10. "time"
  11. )
  12. func main() {
  13. // stop on ^c
  14. quit := make(chan os.Signal)
  15. signal.Notify(quit, os.Interrupt)
  16. // router
  17. mux := http.NewServeMux()
  18. mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
  19. fmt.Println("serving request")
  20. io.WriteString(w, "Hello, world!\n")
  21. })
  22. // start server
  23. srv := &http.Server{Addr: ":8080", Handler: mux}
  24. go func() {
  25. if err := srv.ListenAndServe(); err != nil {
  26. log.Printf("listenAndServe failed: %v", err)
  27. }
  28. }()
  29. fmt.Println("server started")
  30. // time.Sleep(1 * time.Second)
  31. // gracefully stop server
  32. d := time.Now().Add(60 * time.Second)
  33. ctx, cancel := context.WithDeadline(context.Background(), d)
  34. defer cancel()
  35. if err := srv.Shutdown(ctx); err != nil {
  36. fmt.Println(err)
  37. }
  38. fmt.Println("server stopped")
  39. <-quit
  40. }

只需运行此程序并访问 http://localhost:8080。你会发现即使我们尝试关闭它,它仍然会继续提供请求。

程序的输出应该是:

  1. :; go run main.go
  2. server started
  3. server stopped
  4. 2017/07/22 16:17:11 serve failed: http: Server closed

但实际输出是:

  1. :; go run main.go
  2. server started
  3. server stopped
  4. serving request
  5. serving request
  6. 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.

  1. package main
  2. import (
  3. &quot;context&quot;
  4. &quot;fmt&quot;
  5. &quot;io&quot;
  6. &quot;log&quot;
  7. &quot;net/http&quot;
  8. &quot;os&quot;
  9. &quot;os/signal&quot;
  10. &quot;time&quot;
  11. )
  12. func main() {
  13. // stop on ^c
  14. quit := make(chan os.Signal)
  15. signal.Notify(quit, os.Interrupt)
  16. // router
  17. mux := http.NewServeMux()
  18. mux.HandleFunc(&quot;/&quot;, func(w http.ResponseWriter, r *http.Request) {
  19. fmt.Println(&quot;serving request&quot;)
  20. io.WriteString(w, &quot;Hello, world!\n&quot;)
  21. })
  22. // start server
  23. srv := &amp;http.Server{Addr: &quot;:8080&quot;, Handler: mux}
  24. go func() {
  25. if err := srv.ListenAndServe(); err != nil {
  26. log.Printf(&quot;listenAndServe failed: %v&quot;, err)
  27. }
  28. }()
  29. fmt.Println(&quot;server started&quot;)
  30. // time.Sleep(1 * time.Second)
  31. // gracefully stop server
  32. d := time.Now().Add(60 * time.Second)
  33. ctx, cancel := context.WithDeadline(context.Background(), d)
  34. defer cancel()
  35. if err := srv.Shutdown(ctx); err != nil {
  36. fmt.Println(err)
  37. }
  38. fmt.Println(&quot;server stopped&quot;)
  39. &lt;-quit
  40. }

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:

  1. :; go run main.go
  2. server started
  3. server stopped
  4. 2017/07/22 16:17:11 serve failed: http: Server closed

instead it is:

  1. :; go run main.go
  2. server started
  3. server stopped
  4. serving request
  5. serving request
  6. serving request

答案1

得分: 4

您对srv.ListenAndServe的调用可以在srv.Shutdown之后进行,而且显然是这样做的。您可以通过在调用srv.ListenAndServe之前的goroutine中添加日志语句来验证这一点。要查看在ListenAndServe之后调用关闭是否起作用,您可以取消注释time.Sleep(1 * time.Second)。在您的&lt;-quit之后立即调用ShutdownShutdown的典型用法。

  1. func main() {
  2. // 在^c上停止
  3. quit := make(chan os.Signal)
  4. signal.Notify(quit, os.Interrupt)
  5. // 路由器
  6. mux := http.NewServeMux()
  7. mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
  8. log.Println("正在处理请求")
  9. io.WriteString(w, "Hello, world!\n")
  10. })
  11. // 启动服务器
  12. srv := &http.Server{Addr: ":8080", Handler: mux}
  13. go func() {
  14. log.Println("服务器启动中")
  15. if err := srv.ListenAndServe(); err != nil {
  16. log.Fatalf("listenAndServe 失败: %v", err)
  17. }
  18. }()
  19. fmt.Println("服务器已启动")
  20. <-quit
  21. // 优雅地停止服务器
  22. ctx, cancel := context.WithTimeout(context.Background(), 60 * time.Second)
  23. defer cancel()
  24. if err := srv.Shutdown(ctx); err != nil {
  25. log.Fatal(err)
  26. }
  27. log.Println("服务器已停止")
  28. }
英文:

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 &lt;-quit:

  1. func main() {
  2. // stop on ^c
  3. quit := make(chan os.Signal)
  4. signal.Notify(quit, os.Interrupt)
  5. // router
  6. mux := http.NewServeMux()
  7. mux.HandleFunc(&quot;/&quot;, func(w http.ResponseWriter, r *http.Request) {
  8. log.Println(&quot;serving request&quot;)
  9. io.WriteString(w, &quot;Hello, world!\n&quot;)
  10. })
  11. // start server
  12. srv := &amp;http.Server{Addr: &quot;:8080&quot;, Handler: mux}
  13. go func() {
  14. log.Println(&quot;server starting&quot;)
  15. if err := srv.ListenAndServe(); err != nil {
  16. log.Fatalf(&quot;listenAndServe failed: %v&quot;, err)
  17. }
  18. }()
  19. fmt.Println(&quot;server started&quot;)
  20. &lt;-quit
  21. // gracefully stop server
  22. ctx, cancel := context.WithTimeout(context.Background(), 60 * time.Second)
  23. defer cancel()
  24. if err := srv.Shutdown(ctx); err != nil {
  25. log.Fatal(err)
  26. }
  27. log.Println(&quot;server stopped&quot;)
  28. }

huangapple
  • 本文由 发表于 2017年7月22日 23:23:29
  • 转载请务必保留本文链接:https://go.coder-hub.com/45256166.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定