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

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

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 (
	&quot;context&quot;
	&quot;fmt&quot;
	&quot;io&quot;
	&quot;log&quot;
	&quot;net/http&quot;
	&quot;os&quot;
	&quot;os/signal&quot;
	&quot;time&quot;
)

func main() {
	// stop on ^c
	quit := make(chan os.Signal)
	signal.Notify(quit, os.Interrupt)

	// router
	mux := http.NewServeMux()
	mux.HandleFunc(&quot;/&quot;, func(w http.ResponseWriter, r *http.Request) {
		fmt.Println(&quot;serving request&quot;)
		io.WriteString(w, &quot;Hello, world!\n&quot;)
	})

	// start server
	srv := &amp;http.Server{Addr: &quot;:8080&quot;, Handler: mux}
	go func() {
		if err := srv.ListenAndServe(); err != nil {
			log.Printf(&quot;listenAndServe failed: %v&quot;, err)
		}
	}()
	fmt.Println(&quot;server started&quot;)

	// 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(&quot;server stopped&quot;)

	&lt;-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)。在您的&lt;-quit之后立即调用ShutdownShutdown的典型用法。

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

func main() {
    // stop on ^c
    quit := make(chan os.Signal)
    signal.Notify(quit, os.Interrupt)

    // router
    mux := http.NewServeMux()
    mux.HandleFunc(&quot;/&quot;, func(w http.ResponseWriter, r *http.Request) {
        log.Println(&quot;serving request&quot;)
        io.WriteString(w, &quot;Hello, world!\n&quot;)
    })

    // start server
    srv := &amp;http.Server{Addr: &quot;:8080&quot;, Handler: mux}
    go func() {
        log.Println(&quot;server starting&quot;)
        if err := srv.ListenAndServe(); err != nil {
            log.Fatalf(&quot;listenAndServe failed: %v&quot;, err)
        }
    }()
    fmt.Println(&quot;server started&quot;)

    &lt;-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(&quot;server stopped&quot;)
}

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:

确定