go - 将 HTTP 响应写入临时 bytes.Buffer

标签 go

我一直在做一些分析和基准测试,以优化写入临时 bytes.Buffer 以捕获来自 template.ExecuteTemplate 的任何错误。

具体来说,我们正在写入缓冲区,检查是否有任何错误,如果没有,则写入我们的 http.ResponseWriter。然而,问题是临时缓冲区的请求开销有点明显:

  • 大约 6.2k req/s - 27.6k -> 21.4k 开启分析,29k -> 24k 关闭分析;
  • 每个请求的延迟增加 9 毫秒(40 毫秒 -> 49 毫秒)。

当然,21k req/s 仍然是很多请求,但是 22% 的性能。 hit也是一个比较大的影响。

func renderTemplate(w http.ResponseWriter, name string, data map[string]interface{}) error {
    // Ensure the template exists in the map.
    tmpl, ok := templates[name]
    if !ok {
        return ErrTemplateDoesNotExist
    }

    // Create a buffer to temporarily write to and check if any errors were encountered.
    buf := bytes.NewBuffer(make([]byte, 0, 10000))
    err := tmpl.ExecuteTemplate(buf, "base", data)
    if err != nil {
        return err
    }

    // Set the header and write the buffer to the http.ResponseWriter
    w.Header().Set("Content-Type", "text/html; charset=utf-8")
        buf.WriteTo(w)

    return nil
}

10K 缓冲区大小是对我的大多数 响应的典型最大页面大小的粗略估计,尽管我还没有测试过超出一小部分页面的范围。大于缓冲区大小的响应通常会导致性能再下降 20%。

有没有更好的方法在每个 请求中写入临时缓冲区? 另一个 gopher 指出了即将到来的 sync.Pool在 Go 1.3 中,但我不确定在写出来时从哪里开始。


添加:使用http://godoc.org/github.com/oxtoacart/bpool目前在每个请求 36 毫秒时产生 33k 请求/秒:

var bufpool *bpool.BufferPool

func renderTemplate(w http.ResponseWriter, name string, data map[string]interface{}) error {
    ...
    buf := bufpool.Get()
    err := tmpl.ExecuteTemplate(buf, "base", data)
    if err != nil {
        return err
    }

    // Set the header and write the buffer to the http.ResponseWriter
    w.Header().Set("Content-Type", "text/html; charset=utf-8")
    buf.WriteTo(w)
    bufpool.Put(buf)

    return nil
}

func init() {
    bufpool = bpool.NewBufferPool(48)

}

最佳答案

[从评论中复制作为答案]

只需使用不是来自标准库的可用池来池化您的缓冲区。这个看起来会起作用(在 godoc 上搜索一些其他的选择):

http://godoc.org/github.com/oxtoacart/bpool

你可能还会看到吞吐量的增加,无论大小如何,只是通过减少垃圾收集器的压力。

关于go - 将 HTTP 响应写入临时 bytes.Buffer,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24120466/

相关文章:

go - 如果它具有相同的 'signature' 为什么不能使用来自不同包的类型? golang

go - 有人可以解释这个使用 channel 的 Go 代码块吗?我不明白它是如何一次执行 500 个 Action 的

go - 如何使用golang从firebase数据库中读取特定记录?

go - 有没有办法在不创建新的 goroutine 的情况下回调函数?

arrays - 戈朗 : array is empty after call a method that adds items to the array

google-app-engine - 戈朗 : Audio to FLAC conversion without running a executable

go - 动态创建结构

logging - 在 Golang 中将每个日志保存在单独的行中

go - 在 serveHTTP 处理程序中调用接口(interface)方法和结构方法有什么区别?

http - 如何拦截错误的http HEAD请求