doupo2157 2017-08-18 08:18
浏览 227
已采纳

立即调用Shutdown时,http.Server Serve方法挂起

I have an issue with Go's http.Server, which I'm embedding in a struct that is supposed to control the server startup and shutdown. The struct looks like this:

type HTTPListen struct {
    Consumers []pipeline.Consumer
    Cfg       HTTPListenConfig
    Srv       *http.Server
    Logger    log.Logger
    wg        *sync.WaitGroup
    mu        sync.Mutex
    state     State
}

The issue is that in my test code, I call my struct's Start() method (which in turn runs the Serve() method on the http.Server), check a few vars, and then call Stop(), whitch Shutdown()s the server and then waits for the http.Server to exit (return err from the Serve() method).

Now, for some reason, the Serve() method seems to just hang on the WaitGroup.Wait(), when I try to shutdown the server immediately after starting. When I add a short pause (tried 100ms), or when running the tests with the race detector, It works just fine.

Not sure if it matters, but there are no incoming requests between calling Serve() and Shutdown().

EDIT: link to a playground minimal example. If you comment out the time.Sleep call, the program hangs.

Here is the relevant code for the two methods:

func (h *HTTPListen) Start() error {

    h.Logger.Log("msg", "starting HTTPListen input")

    addr := h.Cfg.ListenAddr
    ln, err := net.Listen("tcp", addr)
    if err != nil {
        h.Logger.Log("msg", "failed to create listener on tcp/"+addr+": "+err.Error())
        h.setState(StateFailed)
        return err
    }

    h.wg.Add(1)
    go func() {

        defer h.wg.Done()
        err := h.Srv.Serve(ln)
        h.Logger.Log("msg", "HTTP server stopped: "+err.Error())

    }()

    h.setState(StateStarted)
    h.Logger.Log("msg", "HTTPListen input started")

    return nil

}

Stop method:

func (h *HTTPListen) Stop() error {

    h.Logger.Log("msg", "stopping HTTPListen input")

    ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
    defer cancel()

    if err := h.Srv.Shutdown(ctx); err != nil {
        h.Logger.Log("msg", "HTTP server shutdown deadline expired")
    }

    h.wg.Wait()

    h.setState(StateStopped)
    h.Logger.Log("msg", "HTTPListen input stopped")

    return nil
}

Log output:

kwz@cyclone ~/s/stblogd> go test -v ./pkg/pipeline/input/ -run TestHTTPListen_StartStop
=== RUN   TestHTTPListen_StartStop
msg="starting HTTPListen input"
msg="HTTPListen input started"
msg="stopping HTTPListen input"
... hangs indefinitely

Log output when running tests with the race detector:

kwz@cyclone ~/s/stblogd> go test -race -v ./pkg/pipeline/input/ -run TestHTTPListen_StartStop
=== RUN   TestHTTPListen_StartStop
msg="starting HTTPListen input"
msg="HTTPListen input started"
msg="stopping HTTPListen input"
msg="HTTP server stopped: http: Server closed"
msg="HTTPListen input stopped"
--- PASS: TestHTTPListen_StartStop (0.00s)
PASS
ok      stblogd/pkg/pipeline/input      1.007s

I'm tempted to just slap a short delay on the test and call it a day, but I would like to know why it behaves like this.

  • 写回答

1条回答 默认 最新

  • doushu9253 2017-08-18 10:47
    关注

    This is a known issue, see this thread:

    https://github.com/golang/go/issues/20239

    Hopefully they will fix it soon but for now it sounds like adding a short delay in your test is the simplest solution - it probably doesn't come up in real world use much because you won't trigger a shutdown so soon after starting.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 TYPCE母转母,插入认方向
  • ¥15 如何用python向钉钉机器人发送可以放大的图片?
  • ¥15 vue3加ant-design-vue无法渲染出页面
  • ¥15 matlab(相关搜索:紧聚焦)
  • ¥15 基于51单片机的厨房煤气泄露检测报警系统设计
  • ¥15 路易威登官网 里边的参数逆向
  • ¥15 Arduino无法同时连接多个hx711模块,如何解决?
  • ¥50 需求一个up主付费课程
  • ¥20 模型在y分布之外的数据上预测能力不好如何解决
  • ¥15 processing提取音乐节奏