net / http:请求在等待连接时被取消(在等待标头时超过了Client.Timeout)

时间:2019-06-25 12:07:51

标签: go concurrency

我正在尝试抓取单独存储在文件中的网址列表。我对每个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并试图为此使用自定义传输,但这也无济于事。

0 个答案:

没有答案