http - ioutil.ReadAll 的替代方案?

标签 http go io

对于我正在制作的程序,此函数在 for 循环中作为 goroutine 运行,具体取决于传入的 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
}

这不仅更加简洁,而且在内存方面和时间方面都更加高效:

  • 解码器不必分配一个巨大的 byte slice 来容纳读取的数据——它可以简单地重用一个小缓冲区,该缓冲区将用于 Read 方法以获取所有数据和解析它。这节省了大量的分配时间并消除了 GC 的压力
  • JSON 解码器可以在第一个数据 block 进入时立即开始解析数据 - 它不必等待所有内容都完成下载。

当然,您的问题与 JSON 无关,但是这个示例有助于说明,如果您可以直接使用 Read 并一次解析数据 block ,那就去做吧。特别是对于 HTTP 请求,解析比读取/下载更快,因此这会导致解析数据在请求主体完成到达时几乎立即就绪。

在您的情况下,您现在似乎实际上没有对数据进行任何处理,因此没有太多建议可以具体帮助您。但是 io.Readerio.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/

相关文章:

java - 服务器如何检查客户端的可用性?

http - 关于 Wireshark 和 http/tcp 流

google-app-engine - 多对象删除 在谷歌云存储去

docker - 如何在不杀死原来的pod的情况下更改k8s的pod限制?

java - 使用 `Replace()` 方法不会替换 Apache POI 中的文本

clojure - 为什么不with-open force求值

apache - 将 IIS 与 Tomcat 集成是个好主意,还是忘记它并坚持使用 Apache HTTP 和 Tomcat

json - 如何从Golang中具有json对象列表的文件中读取单个json对象

java - `javax.imageio.ImageIO` 是否需要文件锁定?

即使获得适当的权限,Android HTTP 连接仍被拒绝错误