ioutil.ReadAll(response.Body)永远封锁 - Golang

时间:2014-05-31 07:35:58

标签: http go

tr := &http.Transport{
    TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
client := &http.Client{Transport: tr}
response, err := client.Get(link)
if err != nil {
    fmt.Println(err)
}
defer response.Body.Close()

//block forever at the next line
content, _ = ioutil.ReadAll(response.Body)

以上是我的代码,用于从位于循环中的网页中读取内容。我发现有时行ioutil.ReadAll(response.Body)将永远阻止。这是随机发生的,但是,它几乎总是发生在这个网页上:http://xkcd.com/55。非常有趣的是,当我执行curl http://xkcd.com/55时,它不会返回任何内容,但是,wget http://xkcd.com/55会返回整个网页。

5 个答案:

答案 0 :(得分:6)

我怀疑您的问题是,即使出现错误,您也会尝试阅读回复正文:

if err != nil {
    fmt.Println(err)
}

此后您应该有else,或者您应该returncontinue或其他内容。您的ReadAll()行是未定义的行为。

(如果您最初从Get()示例代码中复制了此内容,请注意它在错误分支中包含log.Fatalf(),从而终止该程序。)

我怀疑,正如您所说,偶尔会出于某种原因导致网络错误。您是否正在检查输出Println()的结果?你完成它的方式,我可以想象它很容易被埋没在输出中。

正如@twotwotwo所说,此URL返回重定向到带有斜杠的同一URL。 Get()会自动为您处理此问题,因此不是问题所在。默认情况下,当wget执行时,curl不会遵循重定向。您可以通过将-i传递给curl。

来查看标题信息

要验证的其他事项:

  • 确保您的defer实际被调用。请记住,defer在函数末尾调用,而不是当前作用域的结尾。因此,如果您处于循环中(如您所述),您只会累积defer块,并且永远不会实际关闭这些响应。

  • 如果服务器实际上从未关闭连接,则io.ReadAll()将永远不会返回。这是一个功能。如果您想要超时,则需要handle that yourself。您应该能够使用curl等工具测试此假设。对于某些解决方案,请参阅:

答案 1 :(得分:4)

此外,避免在没有内存/缓冲区限制控制的ReadAll中读取响应Body,例如:

googleResponse := GoogleResponse{}
err = json.NewDecoder(io.LimitReader(resp.Body, MAX_MEMORY)).Decode(&googleResponse)
if err != nil {
    return nil, err
}

在好的博文中了解更多相关信息:
Crossing Streams: a Love Letter to io.Reader by Jason Moiron
ioutil.ReadAll(httpResponse.Body) memory consumption
Golang Slices And The Case Of The Missing Memory

答案 2 :(得分:1)

您的代码应该按预期工作。我猜,这是一个网络问题。尝试设置更高的超时。

package main

import (
    "crypto/tls"
    "fmt"
    "io/ioutil"
    "net/http"
)

func main() {

    link := "http://xkcd.com/55"

    tr := &http.Transport{
        TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
    }
    client := &http.Client{Transport: tr}
    response, err := client.Get(link)
    if err != nil {
        fmt.Println(err)
    }
    defer response.Body.Close()

    //block forever at the next line
    content, _ := ioutil.ReadAll(response.Body)

    fmt.Println(string(content))

}

答案 3 :(得分:0)

我可能通过将DisableKeepAlives: true,添加到`& http.Transport来找到解决方案,如下所示:

tr := &http.Transport{
    TLSClientConfig:   &tls.Config{InsecureSkipVerify: true},
    DisableKeepAlives: true,
}

由于我做了这个改动,我还没有遇到任何长时间阻塞。但我不是百分百肯定这是解决方案。我会让新代码运行一两天。如果没有阻止,我认为这个问题已经解决了。

答案 4 :(得分:0)

我今天遇到这个问题。原因是我写了一个死循环。死循环会导致较高的cpu。所以ioutil.ReadAll是块。