英文:
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
我一直在尝试使用 netstat
和 tcpdump
来查看它们到底卡在哪里,但目前并没有什么用。在深入源代码或更加细致地监控之前,我想先提出这个问题。这里到底发生了什么?
另外,为什么我的超时设置不起作用?是否还有其他需要设置的超时时间?(如你所见,我尽量设置了所有能找到的超时时间,还有响应头的超时时间,也许我也应该设置吗?不过我认为 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 = &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
}
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("GET", 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)
<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
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
.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论