将HTTP响应写入临时bytes.Buffer

时间:2014-06-09 12:37:35

标签: go

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

具体来说,我们正在写缓冲区,检查是否有错误,如果没有,请写出我们的http.ResponseWriter。但问题是临时缓冲区的请求开销有些明显:

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

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

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指出Go 1.3中即将发布的sync.Pool,但是我&# 39;我不知道在写出来的时候从哪里开始。


已添加:目前使用http://godoc.org/github.com/oxtoacart/bpool会产生33k req / s,每次请求36ms:

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)

}

1 个答案:

答案 0 :(得分:3)

[从评论中复制为答案]

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

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

Yyou也应该看到吞吐量的增加,无论大小,只是减少垃圾收集器的压力。