我正在尝试抓取单独存储在文件中的网址列表。我对每个URL访问都使用goroutine,但是我将相同的http.Client传递给所有请求,从而创建了一个连接池,这被认为是最佳实践。我正在使用默认的net / http客户端。我将响应存储到具有唯一名称的html文件中。
我已将MaxIdleConnsPerHost设置为大于URL数量的数字,因此没有请求进入time_wait状态,但是我收到以下错误net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers)
,其中约1000个URL中有400个左右。我想念的是,一次可以发送多少个请求是否受到限制,我是否需要限制正在使用的goroutine的数量?
package main
import (
"fmt"
"io"
"io/ioutil"
"log"
"net/http"
"os"
"os/exec"
"strings"
"sync"
"time"
)
func main() {
start := time.Now()
rows := ReadInput()
tr := &http.Transport{
MaxIdleConns: 1000,
MaxIdleConnsPerHost: 2000,
IdleConnTimeout: 10 * time.Second,
}
client := &http.Client{Timeout: 10 * time.Second,
Transport: tr}
wg := new(sync.WaitGroup)
for _, url := range rows {
wg.Add(1)
go VisitURL(client, url, wg)
}
wg.Wait()
elapsed := time.Since(start)
log.Printf("Scraping took %s", elapsed)
}
func ReadInput() []string {
// Read from file
b, err := ioutil.ReadFile("test.csv")
if err != nil {
fmt.Print(err)
}
str := string(b)
rows := strings.Split(str, "\n")
return rows
}
func VisitURL(client *http.Client, url string, wg *sync.WaitGroup) {
defer wg.Done()
resp, err := client.Get(url)
if err != nil {
out, uuidErr := exec.Command("uuidgen").Output()
if uuidErr != nil {
log.Fatal(uuidErr)
}
name := string(out)
name = name[:30]
f, fileErr := os.Create(name + ".html")
if fileErr != nil {
panic(fileErr)
}
f.WriteString("Error: " + err.Error() + "\n")
f.Close()
} else {
defer resp.Body.Close()
out, uuidErr := exec.Command("uuidgen").Output()
if uuidErr != nil {
log.Fatal(uuidErr)
}
name := string(out)
name = name[:30]
f, fileErr := os.Create(name + ".html")
if fileErr != nil {
panic(fileErr)
}
defer f.Close()
io.Copy(f, resp.Body)
}
}
我也使用gocolly的异步功能尝试了同样的事情,但是当我不限制并行性时,会发生相同的错误。我知道gocolly也在使用net / http并试图为此使用自定义传输,但这也无济于事。