对于一个程序,我正在将此函数作为goroutine在for循环中运行,具体取决于传入的URL数量(没有设置数量)。
func makeRequest(url string, ch chan<- string, errors map[string]error){
res, err := http.Get(url)
if err != nil {
errors[url] = err
close(ch)
return
}
defer res.Body.Close()
body, _ := ioutil.ReadAll(res.Body)
ch <- string(body)
}
必须使用整个响应,因此ioutil.ReadAll看起来非常合适,但对可以传递的url数量没有限制,并且ReadAll的性质是它全部存储在内存中,开始感觉不舒服像金票。我是Go的新手,所以如果您决定回答,如果您可以在解决方案背后给出一些解释,将不胜感激!
最佳答案
当我学习如何使用Go时,我得到的一个见解是,对于所有读者来说,ReadAll通常效率低下,并且像您的情况一样,它受制于任意输入量很大并且可能会泄漏内存。开始时,我曾经像这样进行JSON解析:
data, err := ioutil.ReadAll(r)
if err != nil {
return err
}
json.Unmarshal(data, &v)
然后,我了解了一种解析JSON的更有效的方法,该方法就是简单地使用
Decoder
类型。err := json.NewDecoder(r).Decode(&v)
if err != nil {
return err
}
这不仅更加简洁,而且在内存方面和时间方面都更加高效:
Read
方法以获取所有数据并进行解析。这样可以节省大量的分配时间,并消除了GC 现在,您的问题当然与JSON无关,但是此示例非常有用,它说明了如果您可以直接使用
Read
并一次解析数据块,则可以这样做。特别是对于HTTP请求,解析比读取/下载要快,因此这可能导致解析的数据几乎在请求主体到达时立即准备就绪。就您而言,您目前似乎实际上并未对数据进行任何处理,因此没有太多建议可以帮助您。但是
io.Reader
和io.Writer
接口(interface)与UNIX管道的Go等效,因此您可以在许多不同的地方使用它们:将数据写入文件:
f, err := os.Create("file")
if err != nil {
return err
}
defer f.Close()
// Copy will put all the data from Body into f, without creating a huge buffer in memory
// (moves chunks at a time)
io.Copy(f, resp.Body)
将所有内容打印到标准输出:
io.Copy(os.Stdout, resp.Body)
将响应的主体传递给请求的主体:
resp, err := http.NewRequest("POST", "https://example.com", resp.Body)
关于http - 替代ioutil.ReadAll吗?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/52539695/