首页 > 其他分享 >为什么要避免在 Go 中使用 io.ReadAll

为什么要避免在 Go 中使用 io.ReadAll

时间:2024-01-17 17:02:44浏览次数:13  
标签:ReadAll 512 err os io Go Copy buf

ioutil包在go1.16版本已弃用。

io.ReadAll()实现:

// src/io/io.go

func ReadAll(r Reader) ([]byte, error) {
    // 创建一个 512 字节的 buf
	b := make([]byte, 0, 512)
	for {
		if len(b) == cap(b) {
			// 如果 buf 满了,则追加一个元素,使其重新分配内存
			b = append(b, 0)[:len(b)]
		}
		// 读取内容到 buf
		n, err := r.Read(b[len(b):cap(b)])
		b = b[:len(b)+n]
		// 遇到结尾或者报错则返回
		if err != nil {
			if err == EOF {
				err = nil
			}
			return b, err
		}
	}
}

我给代码加上了必要的注释,这段代码的执行主要分三个步骤:

  1. 创建一个 512 字节的 buf
  2. 不断读取内容到 buf,当 buf 满的时候,会追加一个元素,促使其重新分配内存;
  3. 直到结尾或报错,则返回;

知道了执行步骤,但想要分析其性能问题,还需要了解 Go 切片的扩容策略,如下:

  1. 如果期望容量大于当前容量的两倍就会使用期望容量;
  2. 如果当前切片的长度小于 1024 就会将容量翻倍;
  3. 如果当前切片的长度大于 1024 就会每次增加 25% 的容量,直到新容量大于期望容量;

也就是说,如果待拷贝数据的容量小于 512 字节的话,性能不受影响。但如果超过 512 字节,就会开始切片扩容。数据量越大,扩容越频繁,性能受影响越大。

如果数据量足够大的话,内存可能就直接撑爆了,这样的话影响就大了。

那有更好的替换方案吗?当然是有的,我们接着往下看。

io.Copy

可以使用 io.Copy 函数来代替

对比之后就会发现,io.Copy 函数不会一次性读取全部数据,也不会频繁进行切片扩容,显然在数据量大时是更好的选择。

实现方式:

package main
import (
	"fmt"
	"io"
	"net/http"
	"os"
)
func main() {
	fmt.Println("嗨客网()")
	for _, url := range os.Args[1:] {
		resp, err := http.Get(url)
		if err != nil {
			fmt.Fprintf(os.Stderr, "fetch: %v\n", err)
			os.Exit(1)
		}
		b, err := io.Copy(os.Stdout, resp.Body)
		resp.Body.Close()
		if err != nil {
			fmt.Fprintf(os.Stderr, "fetch: %v\n", err)
			os.Exit(1)
		}
		fmt.Printf("%s", b)
	}
}

函数 调用 io.Copy(dst, src) 会从 src 中读取内容,并将读到的结果写入到 dst 中,使用这个函数替代掉例子中的 ioutil.ReadAll 来拷贝响应 结构体 到 os.Stdout,避免申请一个缓冲区(例子中的 b)来存储。记得处理 io.Copy 返回结果中的错误。

标签:ReadAll,512,err,os,io,Go,Copy,buf
From: https://blog.51cto.com/lookingdream/9294311

相关文章

  • BurpSuite启动失败--Your JRE appears to be version 20.0.2 from Oracle Corporation
    1.检查java版本,Burpsuite对jdk版本要求,win+Rcmd 如果版本有问题,在环境变量处修改JAVA_HOME和path。修改完后,仍然有问题;2.查看环境变量中path是否有其他优先级较高的设置 尝试下移或者在不影响生产的前提下删除3.查看之前是否安装过其他版本java未卸载干净,在'控制面板'删......
  • Print linked list using recursion【1月17日学习笔记】
    点击查看代码//Printlinkedlistusingrecursion#include<iostream>usingnamespacestd;structnode{ intdata; node*next;};voidprint(node*p){ if(p==NULL)return;//递归中止条件 cout<<p->data<<""; print(p->next)......
  • golang 处理未确定json字符串
    json字符串转golangmap我们知道golang处理json字符串时,需要先转成struct,并且struct必须是确定的。有时候我们传递的json是不固定的,针对每种情况都写一个struct比较麻烦,有没有处理不确定json的方法呢?答案是有的 varastring="{\"a\":1,\"b\":\"xx\"}" varbmap[string]in......
  • `cargo build`报错:`failed to run custom build command for libgit2-sys v0.13.2+1.4
    cargobuild报错:failedtoruncustombuildcommandforlibgit2-sysv0.13.2+1.4.21问题背景在使用cargo编译cargo-cache时出现报错:Thefollowingwarningswereemittedduringcompilation:warning:[email protected]+1.4.2:Infileincludedfromlibgit2/src/pack.......
  • jmeter安装成功后打不开,提示:Cause: CannotResolveClassException: com.blazemeter.jme
    下载安装Jmeter,环境变量也配置完成了,打开Jmeter脚本报错,提示:Cause:CannotResolveClassException:com.blazemeter.jmeter.threads.concurrency.ConcurrencyThreadGroup解决办法:1.可以试着下载安装Jmeter插件管理器,https://jmeter-plugins.org/install/Install/ 2.把步骤1中......
  • dremio nessie 版本持久化配置简单说明
    dremio对于nessie的使用就是一个插件配置,默认基于docker运行的nessie是没有进行版本持久化的,以下是一个简单说明配置参考我基于了pg,实际支持的存储引擎还是很多的docker-composeversion:"3"services:mysql2:image:mysql:5.6command:--......
  • Go gin框架使用 SSEVENT
    我知道的是,是一个http长连接,有着类websocket的api;后端示例代码:packagemainimport( "fmt" "net/http" "time" "github.com/gin-gonic/gin")funcmain(){ router:=gin.Default() r.GET("/events",func(c*gin.Context......
  • 每天一个linux命令(47):iostat命令
      Linux系统中的 iostat是I/O statistics(输入/输出统计)的缩写,iostat工具将对系统的磁盘操作活动进行监视。它的特点是汇报磁盘活动统计情况,同时也会汇报出CPU使用情况。同vmstat一样,iostat也有一个弱点,就是它不能对某个进程进行深入分析,仅对系统的整体情况进行分析。iost......
  • 使用Go语言编写HTTP代理服务器
    在Go语言中,编写一个HTTP代理服务器相对简单且直观。代理服务器的主要职责是接收客户端的请求,然后将请求转发到目标服务器,再将目标服务器的响应返回给客户端。下面是一个简单的示例,展示如何使用Go语言编写一个基本的HTTP代理服务器:go复制代码package mainimport ("io" "log" "......
  • 华为云DTSE助力无锡云数IoT系统:打造超可靠数字化之源
    本文分享自华为云社区《华为云DTSE赋能无锡云数IoT数字化系统建设,助力系统可靠性提升》,作者:HuaweiCloudDeveloper。随着数字化的持续深入,传统的生产模式不断被重塑,各行各业相继掀起了转型升级的新浪潮。作为深耕智能注塑装备、软件开发、互联网平台以及智能制造领域多年的头部企业,......