net/http.(*Transport).dialConn readLoop goroutine buildup over time

1,544 views
Skip to first unread message

Artur Sapek

unread,
Nov 16, 2014, 8:32:47 PM11/16/14
to golan...@googlegroups.com
My application does a lot of HTTP/HTTPS polling, and it has a slow memory leak. I have pinpointed the problem to two goroutines spawned by net/http.(*Transport).dialConn, which accumulate over time despite my app doing a consistent load of HTTP GET requests.

I have figured this out via runtime.Stack. They are the readLoop and writeLoop calls here: http://golang.org/src/pkg/net/http/transport.go#L600.

An example of the stacks for them both is below the signature of this message.

This leads me to think that I have somehow misconfigured my Transport, specifically its timeout logic. Admittedly I stole most of this from a Stackoverflow post (http://stackoverflow.com/questions/16895294/how-to-set-timeout-for-http-get-requests-in-golang), so I am not sure I am using this feature properly. I would really appreciate some extra eyes on this, hoping to learn from it.

Here is the code where I am defining my own Dial func and running an net/http.Client.Get: http://play.golang.org/p/av-DOouaYG

Any help is appreciated. Thanks,

Artur Sapek



Stacks in question:

goroutine 25265 [select]:
net/http.(*persistConn).writeLoop(0xc208890d10)
	/usr/local/Cellar/go/1.3.1/libexec/src/pkg/net/http/transport.go:885 +0x38f
created by net/http.(*Transport).dialConn
	/usr/local/Cellar/go/1.3.1/libexec/src/pkg/net/http/transport.go:601 +0x957

goroutine 25251 [IO wait]:
net.runtime_pollWait(0x7a1288, 0x72, 0x0)
	/usr/local/Cellar/go/1.3.1/libexec/src/pkg/runtime/netpoll.goc:146 +0x66
net.(*pollDesc).Wait(0xc2083b5950, 0x72, 0x0, 0x0)
	/usr/local/Cellar/go/1.3.1/libexec/src/pkg/net/fd_poll_runtime.go:84 +0x46
net.(*pollDesc).WaitRead(0xc2083b5950, 0x0, 0x0)
	/usr/local/Cellar/go/1.3.1/libexec/src/pkg/net/fd_poll_runtime.go:89 +0x42
net.(*netFD).Read(0xc2083b58f0, 0xc208db4000, 0x1000, 0x1000, 0x0, 0x796290, 0x23)
	/usr/local/Cellar/go/1.3.1/libexec/src/pkg/net/fd_unix.go:242 +0x34c
net.(*conn).Read(0xc20803e740, 0xc208db4000, 0x1000, 0x1000, 0x0, 0x0, 0x0)
	/usr/local/Cellar/go/1.3.1/libexec/src/pkg/net/net.go:122 +0xe7
crypto/tls.(*block).readFromUntil(0xc208a1c2a0, 0x79a548, 0xc20803e740, 0x5, 0x0, 0x0)
	/usr/local/Cellar/go/1.3.1/libexec/src/pkg/crypto/tls/conn.go:451 +0xd9
crypto/tls.(*Conn).readRecord(0xc208c75340, 0x17, 0x0, 0x0)
	/usr/local/Cellar/go/1.3.1/libexec/src/pkg/crypto/tls/conn.go:536 +0x1ff
crypto/tls.(*Conn).Read(0xc208c75340, 0xc20836a000, 0x1000, 0x1000, 0x0, 0x0, 0x0)
	/usr/local/Cellar/go/1.3.1/libexec/src/pkg/crypto/tls/conn.go:901 +0x16a
net/http.noteEOFReader.Read(0x79f2d8, 0xc208c75340, 0xc208890528, 0xc20836a000, 0x1000, 0x1000, 0x6d6ba0, 0x0, 0x0)
	/usr/local/Cellar/go/1.3.1/libexec/src/pkg/net/http/transport.go:1203 +0x72
net/http.(*noteEOFReader).Read(0xc20854c540, 0xc20836a000, 0x1000, 0x1000, 0xc2081969e8, 0x0, 0x0)
	<autogenerated>:124 +0xca
bufio.(*Reader).fill(0xc2081a2f00)
	/usr/local/Cellar/go/1.3.1/libexec/src/pkg/bufio/bufio.go:97 +0x1b3
bufio.(*Reader).Peek(0xc2081a2f00, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0)
	/usr/local/Cellar/go/1.3.1/libexec/src/pkg/bufio/bufio.go:132 +0x101
net/http.(*persistConn).readLoop(0xc2088904d0)
	/usr/local/Cellar/go/1.3.1/libexec/src/pkg/net/http/transport.go:782 +0x95
created by net/http.(*Transport).dialConn
	/usr/local/Cellar/go/1.3.1/libexec/src/pkg/net/http/transport.go:600 +0x93f

Artur Sapek

unread,
Nov 17, 2014, 5:36:04 PM11/17/14
to golan...@googlegroups.com
Nevermind, I managed to solve the issue by using the new Timeout field in the net/http.Client struct (http://stackoverflow.com/a/25344458/827559). I am still very curious what it was about my previous code that caused this issue...
Reply all
Reply to author
Forward
0 new messages