Use sync.Pool for gzipWriter
Signed-off-by: glefloch <glfloch@gmail.com>
This commit is contained in:
parent
0a8115f42e
commit
fdf4cbc87b
|
@ -53,19 +53,10 @@ const (
|
||||||
acceptEncodingHeader = "Accept-Encoding"
|
acceptEncodingHeader = "Accept-Encoding"
|
||||||
)
|
)
|
||||||
|
|
||||||
var bufPool sync.Pool
|
var gzipPool = sync.Pool{
|
||||||
|
New: func() interface{} {
|
||||||
func getBuf() *bytes.Buffer {
|
return gzip.NewWriter(nil)
|
||||||
buf := bufPool.Get()
|
},
|
||||||
if buf == nil {
|
|
||||||
return &bytes.Buffer{}
|
|
||||||
}
|
|
||||||
return buf.(*bytes.Buffer)
|
|
||||||
}
|
|
||||||
|
|
||||||
func giveBuf(buf *bytes.Buffer) {
|
|
||||||
buf.Reset()
|
|
||||||
bufPool.Put(buf)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handler returns an http.Handler for the prometheus.DefaultGatherer, using
|
// Handler returns an http.Handler for the prometheus.DefaultGatherer, using
|
||||||
|
@ -133,10 +124,9 @@ func HandlerFor(reg prometheus.Gatherer, opts HandlerOpts) http.Handler {
|
||||||
}
|
}
|
||||||
|
|
||||||
contentType := expfmt.Negotiate(req.Header)
|
contentType := expfmt.Negotiate(req.Header)
|
||||||
buf := getBuf()
|
buf := &bytes.Buffer{}
|
||||||
defer giveBuf(buf)
|
enc := expfmt.NewEncoder(buf, contentType)
|
||||||
writer, encoding := decorateWriter(req, buf, opts.DisableCompression)
|
|
||||||
enc := expfmt.NewEncoder(writer, contentType)
|
|
||||||
var lastErr error
|
var lastErr error
|
||||||
for _, mf := range mfs {
|
for _, mf := range mfs {
|
||||||
if err := enc.Encode(mf); err != nil {
|
if err := enc.Encode(mf); err != nil {
|
||||||
|
@ -155,29 +145,50 @@ func HandlerFor(reg prometheus.Gatherer, opts HandlerOpts) http.Handler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if closer, ok := writer.(io.Closer); ok {
|
|
||||||
closer.Close()
|
|
||||||
}
|
|
||||||
if lastErr != nil && buf.Len() == 0 {
|
if lastErr != nil && buf.Len() == 0 {
|
||||||
http.Error(w, "No metrics encoded, last error:\n\n"+lastErr.Error(), http.StatusInternalServerError)
|
http.Error(w, "No metrics encoded, last error:\n\n"+lastErr.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
header := w.Header()
|
|
||||||
header.Set(contentTypeHeader, string(contentType))
|
w.Header().Set(contentTypeHeader, string(contentType))
|
||||||
header.Set(contentLengthHeader, fmt.Sprint(buf.Len()))
|
|
||||||
if encoding != "" {
|
|
||||||
header.Set(contentEncodingHeader, encoding)
|
|
||||||
}
|
|
||||||
if _, err := w.Write(buf.Bytes()); err != nil && opts.ErrorLog != nil {
|
if _, err := w.Write(buf.Bytes()); err != nil && opts.ErrorLog != nil {
|
||||||
opts.ErrorLog.Println("error while sending encoded metrics:", err)
|
opts.ErrorLog.Println("error while sending encoded metrics:", err)
|
||||||
}
|
}
|
||||||
// TODO(beorn7): Consider streaming serving of metrics.
|
// TODO(beorn7): Consider streaming serving of metrics.
|
||||||
})
|
})
|
||||||
|
|
||||||
|
gzipHandler := http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||||
|
if opts.DisableCompression {
|
||||||
|
h.ServeHTTP(w, req)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
header := req.Header.Get(acceptEncodingHeader)
|
||||||
|
parts := strings.Split(header, ",")
|
||||||
|
for _, part := range parts {
|
||||||
|
part = strings.TrimSpace(part)
|
||||||
|
if part == "gzip" || strings.HasPrefix(part, "gzip;") {
|
||||||
|
|
||||||
|
w.Header().Set(contentEncodingHeader, "gzip")
|
||||||
|
gz := gzipPool.Get().(*gzip.Writer)
|
||||||
|
defer gzipPool.Put(gz)
|
||||||
|
|
||||||
|
gz.Reset(w)
|
||||||
|
defer gz.Close()
|
||||||
|
|
||||||
|
h.ServeHTTP(gzipResponseWriter{gz, w}, req)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
h.ServeHTTP(w, req)
|
||||||
|
return
|
||||||
|
})
|
||||||
|
|
||||||
if opts.Timeout <= 0 {
|
if opts.Timeout <= 0 {
|
||||||
return h
|
return gzipHandler
|
||||||
}
|
}
|
||||||
return http.TimeoutHandler(h, opts.Timeout, fmt.Sprintf(
|
return http.TimeoutHandler(gzipHandler, opts.Timeout, fmt.Sprintf(
|
||||||
"Exceeded configured timeout of %v.\n",
|
"Exceeded configured timeout of %v.\n",
|
||||||
opts.Timeout,
|
opts.Timeout,
|
||||||
))
|
))
|
||||||
|
@ -292,20 +303,13 @@ type HandlerOpts struct {
|
||||||
Timeout time.Duration
|
Timeout time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
// decorateWriter wraps a writer to handle gzip compression if requested. It
|
// gzipResponseWriter in charge of wrapping io.Writer and http.ReponseWriter
|
||||||
// returns the decorated writer and the appropriate "Content-Encoding" header
|
// together, allowing to get a single struct which implements both interface.
|
||||||
// (which is empty if no compression is enabled).
|
type gzipResponseWriter struct {
|
||||||
func decorateWriter(request *http.Request, writer io.Writer, compressionDisabled bool) (io.Writer, string) {
|
io.Writer
|
||||||
if compressionDisabled {
|
http.ResponseWriter
|
||||||
return writer, ""
|
}
|
||||||
}
|
|
||||||
header := request.Header.Get(acceptEncodingHeader)
|
func (w gzipResponseWriter) Write(b []byte) (int, error) {
|
||||||
parts := strings.Split(header, ",")
|
return w.Writer.Write(b)
|
||||||
for _, part := range parts {
|
|
||||||
part = strings.TrimSpace(part)
|
|
||||||
if part == "gzip" || strings.HasPrefix(part, "gzip;") {
|
|
||||||
return gzip.NewWriter(writer), "gzip"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return writer, ""
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue