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
会返回整个网页。
答案 0 :(得分:6)
我怀疑您的问题是,即使出现错误,您也会尝试阅读回复正文:
if err != nil {
fmt.Println(err)
}
此后您应该有else
,或者您应该return
或continue
或其他内容。您的ReadAll()
行是未定义的行为。
(如果您最初从Get()
示例代码中复制了此内容,请注意它在错误分支中包含log.Fatalf()
,从而终止该程序。)
我怀疑,正如您所说,偶尔会出于某种原因导致网络错误。您是否正在检查输出Println()
的结果?你完成它的方式,我可以想象它很容易被埋没在输出中。
正如@twotwotwo所说,此URL返回重定向到带有斜杠的同一URL。 Get()
会自动为您处理此问题,因此不是问题所在。默认情况下,当wget执行时,curl不会遵循重定向。您可以通过将-i
传递给curl。
要验证的其他事项:
确保您的defer
实际被调用。请记住,defer
在函数末尾调用,而不是当前作用域的结尾。因此,如果您处于循环中(如您所述),您只会累积defer
块,并且永远不会实际关闭这些响应。
如果服务器实际上从未关闭连接,则io.ReadAll()
将永远不会返回。这是一个功能。如果您想要超时,则需要handle that yourself。您应该能够使用curl
等工具测试此假设。对于某些解决方案,请参阅:
http.Transport.ResponseHeaderTimeout
答案 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是块。