背景:
需要做一个小文件下载工具,需要做到并发,使用了beego的httplib作为http资源的下载工具,
在服务器上没什么问题(有问题没暴露),但是在本地机器上出现too many open files
查因
这是一个unix上一个比较常见的错误,问题是怎么出现的呢,明明已经限制的协程的数量。怎么会出现过多的socket连接导致文件数超限。
- 通过获取进程打开文件数 明显看到socket的数量明显多于预期。什么原因导致没有关闭连接
- 检查代码发现自己也是简单的用了httplib.Get函数然后使用ToFile写入文件,response也已经关闭,理论上不应该出现问题。
继续查因
- 打印协程数发现协程数明显增加,已知http读写会开启协程,那么明显是tcp链接问题。
- 检查httplib代码
func Get(url string) *BeegoHTTPRequest {
return NewBeegoRequest(url, "GET")
}
发现每次每次都新建一个Request
,然后因为没有设置Transport
,每一次都设置了一个Transport
导致每次都新建一个链接。
为了解决这个问题修改代码如下:
func downOneFile(ctx context.Context, dstDir string, jobChannel chan Entry, index int) {
tp := &http.Transport{
DialContext: (&net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
DualStack: true,
}).DialContext,
MaxIdleConns: 100,
IdleConnTimeout: 90 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
}
for {
select {
case v, ok := <-jobChannel:
if ok {
atomic.AddInt64(&jobCount, 1)
logs.Debug("NumGoroutine:", index, runtime.NumGoroutine(), jobCount)
rawUrl := BaseUrl + v.FullPath
req := httplib.NewBeegoRequest(rawUrl, "GET")
req.SetTransport(tp) // 复用同一个transport
err := req.ToFile(filepath.Join(dstDir, v.SavePath))
if err != nil {
logs.Error(`save %v fail:%v`, v.FullPath, err)
} else {
logs.Debug(v.FullPath, `save to`, filepath.Join(dstDir, v.SavePath))
}
atomic.AddInt64(&downloadCount, 1)
atomic.AddInt64(&jobCount, -1)
wg.Done() //下载结束
} else {
return
}
case <-ctx.Done():
return
}
}
}
修改完成,再次测试发现一切正常,终于解决问题了。
有疑问加站长微信联系(非本文作者)