HTTP客户端挂起

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

HTTP Clients Hanging

问题

我有一些代码,用于进行大量简单的HTTP GET请求。

我正在使用的客户端设置如下:

  1. type client struct {
  2. *http.Client
  3. // 其他内容
  4. }
  5. func NewClient() client {
  6. var c client
  7. c.Client = &http.Client{
  8. CheckRedirect: func(req *http.Request, via []*http.Request) error {
  9. req.Header.Set("User-Agent", c.userAgent)
  10. return nil
  11. },
  12. Transport: &http.Transport{
  13. Dial: func(network, addr string) (net.Conn, error) {
  14. return net.DialTimeout(network, addr, 2*time.Second)
  15. },
  16. TLSHandshakeTimeout: 2 * time.Second,
  17. TLSClientConfig: &tls.Config{
  18. InsecureSkipVerify: true,
  19. },
  20. },
  21. Timeout: 2 * time.Second,
  22. }
  23. return c
  24. }

如你所见,我确实在努力确保在连接出现问题时获得超时。我使用以下方式进行请求:

  1. req, err := http.NewRequest("GET", url, nil)

没有什么特别的地方。但是经过一段时间后,我发现这些 goroutine 不断增加并且阻塞,以下是一个在 panic 后获取跟踪信息的示例:

  1. goroutine 325 [select, 4 minutes]:
  2. net/http.(*persistConn).writeLoop(0xc208075130)
  3. /usr/local/go/src/net/http/transport.go:945 +0x41d
  4. created by net/http.(*Transport).dialConn
  5. /usr/local/go/src/net/http/transport.go:661 +0xcbc
  6. goroutine 418 [IO wait, 4 minutes]:
  7. net.(*pollDesc).Wait(0xc2083c7870, 0x72, 0x0, 0x0)
  8. /usr/local/go/src/net/fd_poll_runtime.go:84 +0x47
  9. net.(*pollDesc).WaitRead(0xc2083c7870, 0x0, 0x0)
  10. /usr/local/go/src/net/fd_poll_runtime.go:89 +0x43
  11. net.(*netFD).Read(0xc2083c7810, 0xc20857e000, 0x1000, 0x1000, 0x0, 0x7fbb634c3bb0, 0xc2084d87a0)
  12. /usr/local/go/src/net/fd_unix.go:242 +0x40f
  13. net.(*conn).Read(0xc208116020, 0xc20857e000, 0x1000, 0x1000, 0x0, 0x0, 0x0)
  14. /usr/local/go/src/net/net.go:121 +0xdc
  15. net/http.noteEOFReader.Read(0x7fbb634c7288, 0xc208116020, 0xc208075b28, 0xc20857e000, 0x1000, 0x1000, 0x678100, 0x0, 0x0)
  16. /usr/local/go/src/net/http/transport.go:1270 +0x6e
  17. net/http.(*noteEOFReader).Read(0xc2083d84c0, 0xc20857e000, 0x1000, 0x1000, 0xc208017600, 0x0, 0x0)
  18. <autogenerated>:125 +0xd4
  19. bufio.(*Reader).fill(0xc20858c0c0)
  20. /usr/local/go/src/bufio/bufio.go:97 +0x1ce
  21. bufio.(*Reader).Peek(0xc20858c0c0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0)
  22. /usr/local/go/src/bufio/bufio.go:132 +0xf0
  23. net/http.(*persistConn).readLoop(0xc208075ad0)
  24. /usr/local/go/src/net/http/transport.go:842 +0xa4
  25. created by net/http.(*Transport).dialConn
  26. /usr/local/go/src/net/http/transport.go:660 +0xc9f

我一直在尝试使用 netstattcpdump 来查看它们到底卡在哪里,但目前并没有什么用。在深入源代码或更加细致地监控之前,我想先提出这个问题。这里到底发生了什么?

另外,为什么我的超时设置不起作用?是否还有其他需要设置的超时时间?(如你所见,我尽量设置了所有能找到的超时时间,还有响应头的超时时间,也许我也应该设置吗?不过我认为 http.Client 结构中的 Timeout 应该是相当可靠的,会对任何请求都设置超时时间)

最后,有没有一种方法可以设置客户端端口,以便我可以更好地监控哪些连接出现问题?

编辑:我非常确定我对每个请求都进行了读取和关闭响应体。除非有一些请求由于超时或其他原因而挂起,我不知道,但如果这是唯一的解决方案,我会再次检查。

英文:

I have some code that makes tons of simple HTTP GET requests

The client that I am using is set up like this:

  1. type client struct {
  2. *http.Client
  3. // other stuff in here
  4. }
  5. func NewClient() client {
  6. var c client
  7. c.Client = &amp;http.Client{
  8. CheckRedirect: func(req *http.Request, via []*http.Request) error {
  9. req.Header.Set(&quot;User-Agent&quot;, c.userAgent)
  10. return nil
  11. },
  12. Transport: &amp;http.Transport{
  13. Dial: func(network, addr string) (net.Conn, error) {
  14. return net.DialTimeout(network, addr, 2*time.Second)
  15. },
  16. TLSHandshakeTimeout: 2 * time.Second,
  17. TLSClientConfig: &amp;tls.Config{
  18. InsecureSkipVerify: true,
  19. },
  20. },
  21. Timeout: 2 * time.Second,
  22. }
  23. return c
  24. }

As you can see I am really trying to make sure I get timeouts on bad connections. I make the requests like this:

  1. req, err := http.NewRequest(&quot;GET&quot;, url, nil)

Nothing out of the ordinary there. But after some time I get these goroutines building up and just blocking, an example after a panic to get the trace:

  1. goroutine 325 [select, 4 minutes]:
  2. net/http.(*persistConn).writeLoop(0xc208075130)
  3. /usr/local/go/src/net/http/transport.go:945 +0x41d
  4. created by net/http.(*Transport).dialConn
  5. /usr/local/go/src/net/http/transport.go:661 +0xcbc

and

  1. goroutine 418 [IO wait, 4 minutes]:
  2. net.(*pollDesc).Wait(0xc2083c7870, 0x72, 0x0, 0x0)
  3. /usr/local/go/src/net/fd_poll_runtime.go:84 +0x47
  4. net.(*pollDesc).WaitRead(0xc2083c7870, 0x0, 0x0)
  5. /usr/local/go/src/net/fd_poll_runtime.go:89 +0x43
  6. net.(*netFD).Read(0xc2083c7810, 0xc20857e000, 0x1000, 0x1000, 0x0, 0x7fbb634c3bb0, 0xc2084d87a0)
  7. /usr/local/go/src/net/fd_unix.go:242 +0x40f
  8. net.(*conn).Read(0xc208116020, 0xc20857e000, 0x1000, 0x1000, 0x0, 0x0, 0x0)
  9. /usr/local/go/src/net/net.go:121 +0xdc
  10. net/http.noteEOFReader.Read(0x7fbb634c7288, 0xc208116020, 0xc208075b28, 0xc20857e000, 0x1000, 0x1000, 0x678100, 0x0, 0x0)
  11. /usr/local/go/src/net/http/transport.go:1270 +0x6e
  12. net/http.(*noteEOFReader).Read(0xc2083d84c0, 0xc20857e000, 0x1000, 0x1000, 0xc208017600, 0x0, 0x0)
  13. &lt;autogenerated&gt;:125 +0xd4
  14. bufio.(*Reader).fill(0xc20858c0c0)
  15. /usr/local/go/src/bufio/bufio.go:97 +0x1ce
  16. bufio.(*Reader).Peek(0xc20858c0c0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0)
  17. /usr/local/go/src/bufio/bufio.go:132 +0xf0
  18. net/http.(*persistConn).readLoop(0xc208075ad0)
  19. /usr/local/go/src/net/http/transport.go:842 +0xa4
  20. created by net/http.(*Transport).dialConn
  21. /usr/local/go/src/net/http/transport.go:660 +0xc9f

I've been trying to watch netstat and tcpdump to see what they're actually getting stuck on, but it isn't proving very useful right now. Before jumping into the source or getting more diligent with my monitoring I figured I'd toss this question out there. What is going on here?..

Also why are my timeouts not working? Is there another timeout that I need to set? (As you can see I just keep setting every one I can find, there is also the Response Header timeout which maybe I should set too? I thought the Timeout in the http.Client struct was pretty solid though and would timeout anything)

Lastly, is there a way to set the client port that I am missing so I can better monitor which connections are having an issue?

EDIT: For the record I am also pretty certain I am reading/closing the response body for every request. Unless there are some that are somehow hanging from the timeout or something I don't know of, but if that is the only solution someone sees I will look again.

答案1

得分: 0

原文翻译如下:

原来这些不是问题,它们似乎只是保持连接的消息(尽管奇怪的是我设置了KeepAlive: 0?)。在sync.WaitGroup上放错了Done()是真正的罪魁祸首。我猜我开始把每个Wait()都当作是由http.Client而不是sync.WaitGroup调用的。

英文:

Turns out these weren't the problem, they appear to be just keep alive messages (although strangely I had KeepAlive: 0 set?). Misplaced Done() on a sync.WaitGroup was the real culprit. I guess I started to look at every Wait() as being called by the http.Client instead of the sync.WaitGroup.

huangapple
  • 本文由 发表于 2015年8月4日 14:40:14
  • 转载请务必保留本文链接:https://go.coder-hub.com/31802351.html
匿名

发表评论

匿名网友

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

确定