HTTP客户端挂起

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

HTTP Clients Hanging

问题

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

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

type client struct {
	*http.Client
	// 其他内容
}

func NewClient() client {
	var c client
	c.Client = &http.Client{
		CheckRedirect: func(req *http.Request, via []*http.Request) error {
			req.Header.Set("User-Agent", c.userAgent)
			return nil
		},
		Transport: &http.Transport{
			Dial: func(network, addr string) (net.Conn, error) {
				return net.DialTimeout(network, addr, 2*time.Second)
			},
			TLSHandshakeTimeout: 2 * time.Second,
			TLSClientConfig: &tls.Config{
				InsecureSkipVerify: true,
			},
		},
		Timeout: 2 * time.Second,
	}
	return c
}

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

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

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

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

goroutine 418 [IO wait, 4 minutes]:
net.(*pollDesc).Wait(0xc2083c7870, 0x72, 0x0, 0x0)
	/usr/local/go/src/net/fd_poll_runtime.go:84 +0x47
net.(*pollDesc).WaitRead(0xc2083c7870, 0x0, 0x0)
	/usr/local/go/src/net/fd_poll_runtime.go:89 +0x43
net.(*netFD).Read(0xc2083c7810, 0xc20857e000, 0x1000, 0x1000, 0x0, 0x7fbb634c3bb0, 0xc2084d87a0)
	/usr/local/go/src/net/fd_unix.go:242 +0x40f
net.(*conn).Read(0xc208116020, 0xc20857e000, 0x1000, 0x1000, 0x0, 0x0, 0x0)
	/usr/local/go/src/net/net.go:121 +0xdc
net/http.noteEOFReader.Read(0x7fbb634c7288, 0xc208116020, 0xc208075b28, 0xc20857e000, 0x1000, 0x1000, 0x678100, 0x0, 0x0)
	/usr/local/go/src/net/http/transport.go:1270 +0x6e
net/http.(*noteEOFReader).Read(0xc2083d84c0, 0xc20857e000, 0x1000, 0x1000, 0xc208017600, 0x0, 0x0)
	<autogenerated>:125 +0xd4
bufio.(*Reader).fill(0xc20858c0c0)
	/usr/local/go/src/bufio/bufio.go:97 +0x1ce
bufio.(*Reader).Peek(0xc20858c0c0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0)
	/usr/local/go/src/bufio/bufio.go:132 +0xf0
net/http.(*persistConn).readLoop(0xc208075ad0)
	/usr/local/go/src/net/http/transport.go:842 +0xa4
created by net/http.(*Transport).dialConn
	/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:

type client struct {
	*http.Client
    // other stuff in here
}

func NewClient() client {
	var c client
	c.Client = &amp;http.Client{
		CheckRedirect: func(req *http.Request, via []*http.Request) error {
			req.Header.Set(&quot;User-Agent&quot;, c.userAgent)
			return nil
		},
		Transport: &amp;http.Transport{
			Dial: func(network, addr string) (net.Conn, error) {
				return net.DialTimeout(network, addr, 2*time.Second)
			},
			TLSHandshakeTimeout: 2 * time.Second,
			TLSClientConfig: &amp;tls.Config{
				InsecureSkipVerify: true,
			},
		},
		Timeout: 2 * time.Second,
	}
	return c
}

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

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:

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

and

goroutine 418 [IO wait, 4 minutes]:
net.(*pollDesc).Wait(0xc2083c7870, 0x72, 0x0, 0x0)
	/usr/local/go/src/net/fd_poll_runtime.go:84 +0x47
net.(*pollDesc).WaitRead(0xc2083c7870, 0x0, 0x0)
	/usr/local/go/src/net/fd_poll_runtime.go:89 +0x43
net.(*netFD).Read(0xc2083c7810, 0xc20857e000, 0x1000, 0x1000, 0x0, 0x7fbb634c3bb0, 0xc2084d87a0)
	/usr/local/go/src/net/fd_unix.go:242 +0x40f
net.(*conn).Read(0xc208116020, 0xc20857e000, 0x1000, 0x1000, 0x0, 0x0, 0x0)
	/usr/local/go/src/net/net.go:121 +0xdc
net/http.noteEOFReader.Read(0x7fbb634c7288, 0xc208116020, 0xc208075b28, 0xc20857e000, 0x1000, 0x1000, 0x678100, 0x0, 0x0)
	/usr/local/go/src/net/http/transport.go:1270 +0x6e
net/http.(*noteEOFReader).Read(0xc2083d84c0, 0xc20857e000, 0x1000, 0x1000, 0xc208017600, 0x0, 0x0)
	&lt;autogenerated&gt;:125 +0xd4
bufio.(*Reader).fill(0xc20858c0c0)
	/usr/local/go/src/bufio/bufio.go:97 +0x1ce
bufio.(*Reader).Peek(0xc20858c0c0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0)
	/usr/local/go/src/bufio/bufio.go:132 +0xf0
net/http.(*persistConn).readLoop(0xc208075ad0)
	/usr/local/go/src/net/http/transport.go:842 +0xa4
created by net/http.(*Transport).dialConn
	/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:

确定