当我学习如何使用 Go 时,我得到的一个见解是,ReadAll 对于大读者来说通常效率低下,并且就像您的情况一样,它会受到非常大的任意输入的影响,并且可能会泄漏内存。当我开始时,我曾经这样进行 JSON 解析:
data, err := ioutil.ReadAll(r)
if err != nil {
return err
}
json.Unmarshal(data, &v)
然后,我学到了一种更有效的解析 JSON 的方法,那就是简单地使用Decoder
type.
err := json.NewDecoder(r).Decode(&v)
if err != nil {
return err
}
这不仅更简洁,而且在内存和时间方面都更加高效:
- 解码器不必分配一个巨大的字节片来容纳数据读取 - 它可以简单地重用一个微小的缓冲区,该缓冲区将用于
Read
方法获取所有数据并解析它。这节省了大量的分配时间并消除了 GC 的压力
- JSON 解码器可以在第一个数据块进入时立即开始解析数据 - 它不必等待所有内容完成下载。
现在,当然你的问题与 JSON 无关,但这个例子很有用,可以说明如果你可以使用Read
直接一次解析数据块,就可以了。特别是对于 HTTP 请求,解析比读取/下载更快,因此这可能会导致解析的数据在请求正文完成到达时几乎立即准备就绪。
就您而言,您目前似乎并未实际对数据进行任何处理,因此没有太多建议可以专门帮助您。但是io.Reader
和io.Writer
接口在 Go 中相当于 UNIX 管道,因此您可以在许多不同的地方使用它们:
将数据写入文件:
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)