为什么golang http服务器在响应超过8kb时出现“管道中断”故障?

bvpmtnay  于 2022-12-07  发布在  Go
关注(0)|答案(2)|浏览(220)

我下面有一个示例Web服务器,如果您调用curl localhost:3000 -v,然后立即(在1秒之前)取消^C,它将报告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
            }   
    })))
}

根据我的调试,我已经能够得出结论,只有当整个响应写入超过8192字节(或8 kb)时才会发生这种情况。如果我的整个响应写入小于8192字节,则不会返回broken pipe错误。
我的问题是**这个8192字节(或8 kb)的缓冲区限制是在哪里设置的?**这是Golang的HTTP写缓冲区的限制吗?这与响应被分块有关吗?这只与curl客户端或浏览器客户端有关吗?我如何改变这个限制,以便在连接关闭之前写入更大的缓冲区(用于调试目的)?
谢谢你!

guicsvcw

guicsvcw1#

net/http/server.go中,输出缓冲区设置为4<<10,即4KB。
您看到8KB错误的原因是,它至少需要向套接字写入2次才能检测到关闭的远程连接。第一次写入成功,但远程主机发送了一个RST数据包。第二次写入将指向一个关闭的套接字,这就是返回broken pipe错误的原因。
根据套接字写入缓冲区和连接延迟的不同,在注册第一个RST数据包之前,可能会有更多的写入成功。

nzk0hqpo

nzk0hqpo2#

这是一个中断的管道,但是对于响应的小数据大小,应该使用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]

相关问题