求助,关于 http body 的传输

2022-08-25 10:01:15 +08:00
 hzjseasea

有一个场景是要把前一个请求 get 到的 body 原模原样的拿下来作为另外一个 post 请求的请求体,然后我就有一个疑问,对于小体积的 body 来说无所谓,哪怕是直接将 body copy 一份都没事,但是对于大体积的 body 来说,如果直接用第一种方式 copy 一份的话,那整个内容都会保存在内存里,高并发的环境下对于内存小的机器会不会直接撑爆了,然后我就想了下面两种方式,第二种用函数返回的方式我觉得也是存在内存里面的,用 pipe 的方式更像是流式的方式传输? 望指教

// 直接 copy 一份
func GetBody() ([]byte, error) {
	resp, err := http.Get("http://www.baidu.com")
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	body, _ := ioutil.ReadAll(resp.Body)
	return body, nil
}

func main() {
	body, _ := GetBody()
	// ... deal with body
	fmt.Println(body)

}
// 直接将 response body 抛出来
func GetBody() (io.ReadCloser, error) {
	resp, err := http.Get("http://www.baidu.com")
	if err != nil {
		return nil, err
	}
	return resp.Body, nil
}

func main() {
	body, err := GetBody()
	if err != nil {
		fmt.Println(err)
	}
	defer body.Close()

	// ... deal with body

}

// 通过 io.pipe 的方式传输
func PipeBody(w *io.PipeWriter) {
	resp, err := http.Get("http://www.baidu.com")
	if err != nil {
		log.Println(err)
	}
	defer resp.Body.Close()
	io.Copy(w, resp.Body)
}

func main() {
	reader, writer := io.Pipe()
	go func() {
		defer writer.Close()
		PipeBody(writer)
	}()

	// ... deal with body, for example
	body, _ := ioutil.ReadAll(reader)
	fmt.Println(string(body))
}
2814 次点击
所在节点    Go 编程语言
24 条回复
ScepterZ
2022-08-25 10:10:03 +08:00
你再发起请求的时候,不还是得把它全都读出来么,感觉没啥用
qianxiaoxiao
2022-08-25 10:17:09 +08:00
只能改源码或者 重写 http client
因为 http.Get() 之后 数据已经存放在 buf 里面了
hzjseasea
2022-08-25 10:20:27 +08:00
@ScepterZ 像 23 这两个例子不就少读写各一次吗
hzjseasea
2022-08-25 10:21:21 +08:00
@qianxiaoxiao 是这样的吗 我还以为这个 io.Pipe 可以边读边写
GogoGo666
2022-08-25 10:22:05 +08:00
赞同 1 楼
kkhaike
2022-08-25 10:33:58 +08:00
直接吧 Get 的 body 作为 io.Reader 传入 Post 的 Request 不就行了。。
c332030
2022-08-25 10:39:31 +08:00
直接用流操作,IOUtils.copy 就行
c332030
2022-08-25 10:40:50 +08:00
哦,你这不是 java😂
KouShuiYu
2022-08-25 10:41:09 +08:00
https://developer.mozilla.org/zh-CN/docs/Web/API/ReadableStream
body 是一个可读取的二进制流,如果不处理 body 的话可以不用全部读到内存中,
hxysnail
2022-08-25 10:56:03 +08:00
直接将 body 取出来,传给 post 不就好了吗? body 是一个读流,数据会预读,数据量大的话是不会全部读到内存里面。post 请求会循环读取数据,并发给服务器。因此,可以认为是不断从读流读取数据,然后写到一个写流上。这个拷贝过程会复制所有数据,不过是分批进行的,不会将全部数据读到内存。
hzjseasea
2022-08-25 11:08:44 +08:00
@kkhaike
@KouShuiYu
@c332030
嗷嗷嗷谢谢哈,这样我就清楚了,本来我也是想着把 get 跟 post 两个东西放在一起的,不过 post 里有些东西不能开放出来所以想要着用上面三种方式,如果和 @hxysnail 老哥说的一样的话,那我就用第二种方式了,不用担心了。 感谢
warcraft1236
2022-08-25 11:24:11 +08:00
这不正好是 zero copy 可以做的事情吗?性能还更好
runningman
2022-08-25 13:31:25 +08:00
body 很大本身就是个错误吧
jjwjiang
2022-08-25 13:55:49 +08:00
body 大到你需要考虑这种问题的时候,你应该先去操心网络吞吐量和网速的问题了,我感觉纯属杞人忧天
proxytoworld
2022-08-25 14:29:02 +08:00
sujin190
2022-08-25 14:35:29 +08:00
@runningman 大概率文件下载上传吧,比如微信这个 jssdk 上传的想变永久自己存就得下下来再上传到七牛、又拍、oss 之类的,这样的 body 大就是自然的了
zjsxwc
2022-08-25 14:53:28 +08:00
你服务器 Get 文件大小确实没有限制,但你把这个文件 Post 到另一个服务器时,
你 Post 发出的请求不可能没有大小限制,
我记得 Nginx 默认对于 Request 大小限制 client_max_body_size 值是 1M 。

1M 的“大文件"算大吗?
hzjseasea
2022-08-25 15:33:30 +08:00
@jjwjiang 技术不大行,只想尽可能把自己想到的可能会出现的问题解决一下
hzjseasea
2022-08-25 15:35:05 +08:00
@sujin190 对的,普遍都是 10G-20G 的
hzjseasea
2022-08-25 15:40:05 +08:00
@zjsxwc emm Nginx 的配置我看不到哈,其实我现在碰到的场景就跟 16 楼说的类似,端到端之间做数据备份,没走线下的物理备份。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/875234

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX