Http ioutil.ReadAll(response.Body)永久块-Golang
上面是我的代码,用于从驻留在循环中的网页中读取内容。我发现有时行Http ioutil.ReadAll(response.Body)永久块-Golang,http,go,Http,Go,上面是我的代码,用于从驻留在循环中的网页中读取内容。我发现有时行ioutil.ReadAll(response.Body)会永远阻塞。这是随机发生的,但几乎总是发生在这个网页上:http://xkcd.com/55。非常有趣的是,当我做curl时http://xkcd.com/55,它不返回任何内容,但是,wgethttp://xkcd.com/55返回整个网页。您的代码应按预期工作。我猜,这是一个网络问题。尝试设置更高的超时 tr := &http.Transport{ TLS
ioutil.ReadAll(response.Body)
会永远阻塞。这是随机发生的,但几乎总是发生在这个网页上:http://xkcd.com/55
。非常有趣的是,当我做curl时http://xkcd.com/55
,它不返回任何内容,但是,wgethttp://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)
我怀疑您的问题在于,即使出现错误,您也会尝试读取响应正文:
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))
}
在此之后,您应该有一个else
,或者您应该return
或continue
或其他内容。您的ReadAll()
行是未定义的行为
(如果您最初是从Get()
示例代码复制的,请注意,它在错误段中包含了一个log.Fatalf()
,该错误段将终止程序。)
我怀疑,正如你所说,偶尔你会因为这样或那样的原因出现网络错误。您是否正在检查Println()
的输出结果?你这样做的话,我可以想象它很容易被隐藏在输出中
作为@twoonotes,这个URL返回一个重定向到同一个URL并带有一个尾随斜杠Get()
将自动为您处理此问题,因此这不是问题所在。默认情况下,curl不遵循重定向,而wget遵循重定向。您可以通过将-i
传递给curl来查看标题信息
其他需要核实的事项:
- 确保您的
确实被调用。请记住,延迟
是在函数末尾调用的,而不是在当前范围的末尾调用的。因此,如果您处于循环中(正如您所提到的),您只会累积defer
块,而不会真正关闭这些响应defer
- 如果服务器实际上从未关闭连接,则
将永远不会返回。这是一个特点。如果你想要一个超时,你需要。您应该能够使用io.ReadAll()
等工具来检验这一假设。有关某些解决方案,请参阅:curl
http.Transport.ResponseHeaderTimeout
DisableKeepAlives:true,
,如下所示:
if err != nil {
fmt.Println(err)
}
自从我做了这个更改,我还没有遇到任何长阻塞。但我不是100%确定这是解决方案。我会让新代码运行一两天。如果没有阻塞,我想这个问题已经解决了。此外,避免在没有内存/缓冲区限制控制的ReadAll中读取响应体,例如:
tr := &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
DisableKeepAlives: true,
}
在好的博客文章中阅读更多关于它的信息:也许它们是重定向的:请查看我的下载功能以了解这些内容,并附带一个JarCookie:我刚刚测试了下载,它工作正常(使用我的http代码版本)。谢谢@VonC,我将尝试一下。您可能应该为下载添加一个超时,这样,如果服务器确实运行缓慢,您就不会被卡住,问题不在你的代码中。Re:VonC的评论,请注意,重定向到带有斜杠,因此如果此代码不适用于重定向(我实际上不知道),是的,你会在这里被套住。谢谢@Rob,是的,这是一个很好的观点。我放了很多日志,发现这里没有发生错误。然而,它确实在ioutil.ReadAll(response.Body)上被阻塞了。有趣的是,它从不在第一次请求时阻塞。它几乎总是在第二次或第三次请求时阻塞。当服务器在短时间内检测到多个请求时,服务器处理第二个或第三个传入连接的方式与处理第一个不同。我完全理解服务器可能会使用一些策略来防止欺诈。但是我希望它不会永远阻塞。到目前为止它还没有阻塞。然而,最长的等待时间约为15分钟。我想知道如何将超时设置为
ioutil.ReadAll(response.Body)
。结果证明这不是解决方案。它又永远阻塞了。看起来我仍然需要找到一种方法来设置此行的超时ioutil.ReadAll(response.Body)
。
googleResponse := GoogleResponse{}
err = json.NewDecoder(io.LimitReader(resp.Body, MAX_MEMORY)).Decode(&googleResponse)
if err != nil {
return nil, err
}