HTTP ResponseWriter的write函数在Go中会进行缓冲吗?

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

Will HTTP ResponseWriter's write function buffer in Go?

问题

假设我们有一个处理HTTP请求的函数,类似于:

func handler(w http.ResponseWriter, r *http.Request) {
    w.Write([]byte("first piece of data"))
    // do something
    w.Write([]byte("second piece of data"))
}

我想知道第一次调用w.Write()是否已经刷新到客户端了?

如果已经刷新了,那么我们实际上向客户端发送了两次响应,这很奇怪,因为在第二次调用写入之前,我们如何确定Content-Length

如果没有刷新(假设数据在本地缓冲),那么如果我们在第一次调用时写入了大量数据会怎么样?(会不会导致堆栈溢出?)

对于任何解释,我都会非常感激! HTTP ResponseWriter的write函数在Go中会进行缓冲吗?

英文:

Assume that we have a function handling an HTTP Request, something like:

func handler(w http.ResponseWriter,  r *http.Request) {
    w.Write([]byte("first piece of data"))
    // do something
    w.Write([]byte("second piece of data"))
}  

I'm wondering that if the first call to w.Write() is flushed to client or not?

If it is flushed, then we actually responses to clients twice, this is strange because how can we determine Content-Length before the second call to write?

If it is not flushed (say the data is buffered locally), then what if we write a huge amount of data at the first call? (will that stack overflow?)

Any explanation will be appreciated! HTTP ResponseWriter的write函数在Go中会进行缓冲吗?

答案1

得分: 6

我想知道第一次调用w.Write()是否会立即刷新到客户端?

net/http的默认ResponseWriter在写入到net.Conn时有一个(目前为4KB)的输出缓冲区。此外,操作系统通常会对套接字的写入进行缓冲。因此,在大多数情况下会进行某种形式的缓冲。

如果刷新了,那么我们实际上会向客户端发送两次响应,这很奇怪,因为在第二次调用写入之前,我们如何确定Content-Length?

好吧,HTTP 1.1允许持久连接。这种响应通常不包括Content-Length头。此外,还有HTTP trailers。

如果您的客户端不支持HTTP 1.1和持久连接,它们将具有某种读取超时,在此期间您可以随意多次写入连接;这是一个响应。

这更多与TCP套接字和HTTP实现的性质有关,而不是与Go有关。

如果没有刷新(假设数据在本地缓冲),那么如果我们在第一次调用时写入了大量数据会怎么样?(会导致堆栈溢出吗?)

不会,将缓冲区分配到堆栈上是没有意义的——缓冲区的内容将存储在堆上。如果达到了进程的内存限制,应用程序将会发生“内存不足”的恐慌。

另请参阅:

编辑以回答您在评论中的问题:

分块传输编码是HTTP 1.1规范的一部分,不支持HTTP 1.0。

编辑以澄清:

只要您写入响应的两部分所需的总时间不超过客户端的读取超时时间,并且您没有指定Content-Length头,您只需写入响应然后关闭连接。这是完全可以的,不是“hacky”的。

英文:

> I'm wondering that if the first call to w.Write() is flushed to client or not?

net/http's default ResonseWriter has a (currently 4KB) large output buffer over the net.Conn it writes to. Additionally, the OS normally will buffer writes to a socket. So in most cases some kind of buffering takes place.

> If it is flushed, then we actually responses to clients twice, this is strange because how can we determine Content-Length before the second call to write?

Well there's HTTP 1.1 which allows persistent connections. Such responses usually don't include a Content-Length header. Additionally, there's HTTP trailers.

If your client does not support HTTP 1.1 and persistent connections they will have some sort of read timeout, during this time you can write to the connection as many times as you like; it's one response.

This has more to do with the nature of TCP sockets and HTTP implementations than Go.

> If it is not flushed (say the data is buffered locally), then what if we write a huge amount of data at the first call? (will that stack overflow?)

No, allocating a buffer on the stack makes no sense – the buffer's body will live on the heap. If you hit your per-process memory limit your application will panic "out of memory".

See also:

Edit to answer your question in the comments:

Chunked Transfer Encoding is part of the HTTP 1.1 specification and not supported in HTTP 1.0.

Edit to clarify:

As long as the total time it takes you to write both parts of your response does not exceed your client's read time out, and you don't specify a Content-Length header you just write your response and then close the connection. That's totally OK and not "hacky".

huangapple
  • 本文由 发表于 2014年9月25日 16:25:27
  • 转载请务必保留本文链接:https://go.coder-hub.com/26033853.html
匿名

发表评论

匿名网友

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

确定