httpclient 并发 导致 goroutine 泄露 报错 socket too many files

2019-12-23 15:02:03 +08:00
 tim0991

代码背景

使用 golang 验证代理 Ip,代码主要作用如下

问题

ip 文件内容一般是 100W 行以上,程序运行一段时间之后会出现socket: too many files open

我的尝试

最开始以为是持久连接的问题,就设置了keep-alive: false,设置之后发现还是有问题 使用 pprof 调试发现很多 goroutine 卡在这里,但是此时 channel 长度是比设定值要小的,代表是可以接收数据,等于是老的 goroutine 没有释放,新的 goroutine 一直在创建

internal/poll.runtime_pollWait(0x7f004f1ca2f8, 0x72, 0xffffffffffffffff)
	/usr/local/go/src/runtime/netpoll.go:184 +0x55
internal/poll.(*pollDesc).wait(0xc0029e6f18, 0x72, 0x1000, 0x1000, 0xffffffffffffffff)
	/usr/local/go/src/internal/poll/fd_poll_runtime.go:87 +0x45
internal/poll.(*pollDesc).waitRead(...)
	/usr/local/go/src/internal/poll/fd_poll_runtime.go:92
internal/poll.(*FD).Read(0xc0029e6f00, 0xc002938000, 0x1000, 0x1000, 0x0, 0x0, 0x0)
	/usr/local/go/src/internal/poll/fd_unix.go:169 +0x1cf
net.(*netFD).Read(0xc0029e6f00, 0xc002938000, 0x1000, 0x1000, 0x0, 0x0, 0xc001f21f18)
	/usr/local/go/src/net/fd_unix.go:202 +0x4f
net.(*conn).Read(0xc0017ae198, 0xc002938000, 0x1000, 0x1000, 0x0, 0x0, 0x0)
	/usr/local/go/src/net/net.go:184 +0x68
bufio.(*Reader).fill(0xc00180ca20)
	/usr/local/go/src/bufio/bufio.go:100 +0x103
bufio.(*Reader).ReadSlice(0xc00180ca20, 0xa, 0xc001f21840, 0xc001f21888, 0x40c0c6, 0xc00087e120, 0x90)
	/usr/local/go/src/bufio/bufio.go:359 +0x3d
bufio.(*Reader).ReadLine(0xc00180ca20, 0x8, 0xc0006c6a80, 0x7f0051656460, 0x0, 0x2, 0xc329f8)
	/usr/local/go/src/bufio/bufio.go:388 +0x34
net/textproto.(*Reader).readLineSlice(0xc001f21960, 0xc00087e120, 0xc002938000, 0x7f004f3698c8, 0xc0027bdd01, 0x101000000950280)
	/usr/local/go/src/net/textproto/reader.go:57 +0x6c
net/textproto.(*Reader).ReadLine(...)
	/usr/local/go/src/net/textproto/reader.go:38
net/http.ReadResponse(0xc00180ca20, 0xc00106b400, 0x1000, 0xc002938000, 0xc0017ae198)
	/usr/local/go/src/net/http/response.go:161 +0xd1
net/http.(*Transport).dialConn(0xc002945a40, 0x94cd60, 0xc000024100, 0xc0029e6d80, 0x8b4508, 0x5, 0xc002685940, 0x11, 0x0, 0xc000288fa8, ...)
	/usr/local/go/src/net/http/transport.go:1544 +0x85a
net/http.(*Transport).dialConnFor(0xc002945a40, 0xc000ec1ce0)
	/usr/local/go/src/net/http/transport.go:1308 +0xdc
created by net/http.(*Transport).queueForDial
	/usr/local/go/src/net/http/transport.go:1277 +0x41d

因为阅读 golang http 源码太过于吃力,所以只大概跟了一下代码,我理解这段代码是创建 connection 请求并返回, 想请教一下各位这个 connection 不释放的 具体原因到底是为什么

代码和测试文件

测试文件 golang 代码

7573 次点击
所在节点    Go 编程语言
48 条回复
tim0991
2019-12-24 09:36:54 +08:00
@icexin 感谢你的回复 尝试了你的代码之后暂时没发现报错了,但是有个疑问,transport 内部管理的是 tcp conn,同一个 client 和 transport 可以复用不同的 host 的 conn 吗?
index90
2019-12-24 09:47:53 +08:00
@tim0991 #41 transport 内部有连接池

话说我在 A 城市网络,即使 size 减半也遇到 too many open file,换到 B 城市网络,即使不减半也不会有问题。
tim0991
2019-12-24 09:54:29 +08:00
@SunRunAway 同问
tim0991
2019-12-24 10:07:54 +08:00
@index90 @icexin 感谢你们的耐心解答 我昨天尝试了使用 size/2 然后同一个 client 和 transport 之后 ulimit 调整到 2W 尝试跑了 8000W 数据没出现问题。

内部连接池的问题 我也有疑问 我理解是 host 不通 连接池不复用,既然不复用的话 那为什么之前的问题就好了
同时附上我昨天修改之后的代码 https://goplay.space/#L1HS0igSwwc

不知道 MaxIdleConnsPerHost 在 tcp conn 复用中起到怎么样的作用,我看代码发现其作用是用来控制 transport.tryPutIdleConn 方法中是否把 conn 加入连接池,所以我把 MaxIdleConnsPerHost 关了 但是这样的话不就不能复用了吗?
那这样使用同一个 transport 和 client 意义何在?
aliipay
2019-12-24 10:35:01 +08:00
@EthanDon 所以,仔细想想, 为什么 get/post 需要无条件 close,do 却不用。
EthanDon
2019-12-24 11:57:09 +08:00
@aliipay 实际情况中我也用的是 do,而且我也是无条件 close 的
EthanDon
2019-12-24 12:05:28 +08:00
@aliipay
func Get(url string) (resp *Response, err error) {
return DefaultClient.Get(url)
}
func (c *Client) Get(url string) (resp *Response, err error) {
req, err := NewRequest("GET", url, nil)
if err != nil {
return nil, err
}
return c.Do(req)
}
func (c *Client) Do(req *Request) (*Response, error) {
return c.do(req)
}
有什么区别吗。。。
aliipay
2019-12-24 13:01:43 +08:00
@EthanDon
看了下文档,get 方法也是判断是否 err 后再 close 的,和 do 一样。 之前说法是有问题。
在 do/get 返回 error 时候,resp 是个 nil,不能调用 body.close 的。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/631536

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX