为什么当响应超过8KB时,Golang的HTTP服务器会出现“broken pipe”错误?

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

why is golang http server failing with "broken pipe" when response exceeds 8kb?

问题

我有一个示例的Web服务器,如果你调用curl localhost:3000 -v,然后立即(在1秒之前)按下^C(取消),它会报告write tcp 127.0.0.1:3000->127.0.0.1:XXXXX: write: broken pipe

根据我的调试,我已经得出结论,只有当整个响应写入超过8192字节(或8KB)时,才会发生这种情况。如果我的整个响应写入少于8192字节,就不会返回broken pipe错误。

我的问题是**这个8192字节(或8KB)的缓冲区限制是在哪里设置的?**这是Golang的HTTP写缓冲区的限制吗?这与响应分块有关吗?这只与curl客户端或浏览器客户端有关吗?我如何更改这个限制,以便在关闭连接之前可以写入更大的缓冲区(用于调试目的)?

谢谢!

英文:

I have a example web server below where if you call curl localhost:3000 -v then ^C (cancel) it immediately (before 1 second), it will report write tcp 127.0.0.1:3000->127.0.0.1:XXXXX: write: broken pipe.

package main

import (
    "fmt"
    "net/http"
    "time"
)

func main() {
    log.Fatal(http.ListenAndServe(":3000", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
            time.Sleep(1 * time.Second)

            // Why 8061 bytes? Because the response header on my computer
            // is 132 bytes, adding up the entire response to 8193 (1 byte 
            // over 8kb)
            if _, err := w.Write(make([]byte, 8061)); err != nil {
                    fmt.Println(err)
                    return
            }   
    })))
}

Based on my debugging, I have been able to conclude that this will only happen if the entire response is writing more than 8192 bytes (or 8kb). If my entire response write less than 8192, the broken pipe error is not returned.

My question is where is this 8192 bytes (or 8kb) buffer limit set? Is this a limit in Golang's HTTP write buffer? Is this related to the response being chunked? Is this only related to the curl client or the browser client? How can I change this limit so I can have a bigger buffer written before the connection is closed (for debugging purposes)?

Thanks!

答案1

得分: 22

net/http/server.go中,输出缓冲区被设置为4<<10,即4KB。

你看到在8KB处出现错误的原因是,至少需要两次向套接字写入操作才能检测到关闭的远程连接。第一次写入成功,但远程主机发送了一个RST数据包。第二次写入将是对一个关闭的套接字进行操作,这就是返回broken pipe错误的原因。

根据套接字写入缓冲区和连接延迟的情况,可能会在第一个RST数据包被注册之前成功进行更多的写入操作。

英文:

In net/http/server.go the output buffer is set to 4&lt;&lt;10, i.e. 4KB.

The reason you see the error at 8KB, is that it takes at least 2 writes to a socket to detect a closed remote connection. The first write succeeds, but the remote host sends an RST packet. The second write will be to a closed socket, which is what returns the broken pipe error.

Depending on the socket write buffer, and the connection latency, it's possible that even more writes could succeed before the first RST packet is registered.

答案2

得分: -2

这是翻译好的内容:

这是一个破损的管道,但是对于响应数据较小的情况,你应该使用ioutil.ReadAll,对于响应数据较大的情况,你应该使用io.copy

  1. 对于ioutil.ReadAll
	defer response.Body.Close()
	body, err := ioutil.ReadAll(response.Body)
	if err != nil {
		logger.Errorf(ctx, "err is %+v", err)
		return nil, err
	}
  1. 对于io.copy
	// 10MB
	var wb = make([]byte, 0, 10485760)
	buf := bytes.NewBuffer(wb)
	written, err := io.Copy(buf, response.Body)
	body := wb[:written]
英文:

It is broken pipe, but you should use ioutil.ReadAll for small data size of response or io.copy for large data size of response.

  1. For ioutil.ReadAll
	defer response.Body.Close()
	body, err := ioutil.ReadAll(response.Body)
	if err != nil {
		logger.Errorf(ctx, &quot;err is %+v&quot;, err)
		return nil, err
	}
  1. For io.copy
	// 10MB
	var wb = make([]byte, 0, 10485760)
	buf := bytes.NewBuffer(wb)
	written, err := io.Copy(buf, response.Body)
	body := wb[:written]

huangapple
  • 本文由 发表于 2017年4月4日 00:06:50
  • 转载请务必保留本文链接:https://go.coder-hub.com/43189375.html
匿名

发表评论

匿名网友

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

确定