Go的http.DefaultClient是否会阻塞,直到响应体的最后一个字节传输完成?

huangapple go评论73阅读模式
英文:

Does Go's http.DefaultClient block until the last byte of the body?

问题

例如,如果我有以下代码:

t := time.Now()
http.Get("google.com")
fmt.Println(time.Now().Sub(t))

打印的持续时间是在响应正文的最后一个字节之后,还是在接收到响应头的最后一个字节之后?

英文:

For example if I had the code

t := time.Now()
http.Get("google.com")
fmt.Println(time.Now().Sub(t))

Would the printed duration be after the last byte of the response's body or after the last byte of the response's headers has been received?

答案1

得分: 1

文档没有明确说明这一点,但它在读取完响应头后立即返回。

一旦你对Go有了一些经验,你就会意识到几乎每次返回一个io.Reader(或在这种情况下,resp.Body是一个io.ReadCloser)时,它都是一个流式读取器,还没有所有的数据。
就像调用os.Open返回一个可以用作io.Reader的东西,但并不读取整个文件。

在许多情况下,如果文档不清楚,你可以查看标准包代码来获得启示。
诚然,在这个特定的情况下(就像encoding/json一样),代码做了很多事情,所以一眼看过去可能很难理解。
大多数标准包要容易理解和学习得多。

如果找不到答案,你可以在你自己的机器上运行一个稍微扩展了你给出的代码的示例(playground不支持建立TCP连接),这样就会很清楚了:

<sub>(注意,我只是随机选择了这些URL,最好是选择一些具有大型有效负载的URL,你知道没有人会介意你为了测试而获取它们)</sub>

package main

import (
	&quot;fmt&quot;
	&quot;io&quot;
	&quot;io/ioutil&quot;
	&quot;net/http&quot;
	&quot;time&quot;
)

func measure(url string) (t1, t2 time.Duration, n int64, err error) {
	start := time.Now()
	resp, err := http.Get(url)
	if err != nil {
		return
	}
	defer resp.Body.Close()
	t1 = time.Since(start)
	n, err = io.Copy(ioutil.Discard, resp.Body)
	t2 = time.Since(start)
	return
}

func main() {
	for _, url := range []string{
		&quot;http://google.com/&quot;,
		&quot;http://en.wikipedia.org/&quot;,
		&quot;http://upload.wikimedia.org/wikipedia/commons/7/76/PIA02863_-_Jupiter_surface_motion_animation.gif&quot;,
	} {
		fmt.Printf(&quot;正在获取 %q &quot;, url)
		t1, t2, n, err := measure(url)
		if err != nil {
			fmt.Println(&quot;错误:&quot;, err)
			continue
		}
		fmt.Printf(&quot;在 %v / %v 中获取到 %d 字节\n&quot;, t1, t2, n)
	}
}

例如,在一个相对较慢的连接上,上面测量的最后一个URL显示为:597.055907ms / 29.269763851s
也就是说,初始的Get调用很快返回,而读取所有数据花费的时间大约是初始时间的60倍。

英文:

The documentation doesn't clearly state that this, but it returns as soon as the response headers have been read.

As soon as you get a little experience with Go you'll realize that almost every time you get returned an io.Reader (or in this case resp.Body is an io.ReadCloser) it's a streaming reader that doesn't have all the data yet.
Just like calling os.Open returns something that can be used as an io.Reader but doesn't read the whole file.

In many cases with Go, if the documentation is not clear,
you can look at the standard package code for enlightenment.
Admittedly, in this specific case (as with encoding/json) the code is doing a lot so can be hard to follow at first glance.
Most of the standard packages are much easier to follow and learn from.

Failing that, you can just run a minor extension of the code you gave (on your own machine, the playground doesn't support making TCP connections) to make it pretty clear:

<sub>(note I just picked these URLs somewhat at random, better would be to pick some URL with a large payload that you know no one will mind you fetching for a test)</sub>

package main

import (
	&quot;fmt&quot;
	&quot;io&quot;
	&quot;io/ioutil&quot;
	&quot;net/http&quot;
	&quot;time&quot;
)

func measure(url string) (t1, t2 time.Duration, n int64, err error) {
	start := time.Now()
	resp, err := http.Get(url)
	if err != nil {
		return
	}
	defer resp.Body.Close()
	t1 = time.Since(start)
	n, err = io.Copy(ioutil.Discard, resp.Body)
	t2 = time.Since(start)
	return
}

func main() {
	for _, url := range []string{
		&quot;http://google.com/&quot;,
		&quot;http://en.wikipedia.org/&quot;,
		&quot;http://upload.wikimedia.org/wikipedia/commons/7/76/PIA02863_-_Jupiter_surface_motion_animation.gif&quot;,
	} {
		fmt.Printf(&quot;fetching %q &quot;, url)
		t1, t2, n, err := measure(url)
		if err != nil {
			fmt.Println(&quot;error:&quot;, err)
			continue
		}
		fmt.Printf(&quot;got %d bytes in %v / %v\n&quot;, n, t1, t2)
	}
}

E.g. on a somewhat slow connection the final URL measured above says: 597.055907ms / 29.269763851s.
(That is, the initial Get call returned quickly whereas reading all the data took ~60 times longer.)

huangapple
  • 本文由 发表于 2015年4月22日 00:48:39
  • 转载请务必保留本文链接:https://go.coder-hub.com/29778461.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定