Skip to content

Commit 752f50d

Browse files
author
beorn7
committed
Merge branch 'use-gzip-pool' of github.com:glefloch/client_golang into beorn7/http
2 parents 1cafe34 + c2c6fd2 commit 752f50d

File tree

1 file changed

+54
-34
lines changed

1 file changed

+54
-34
lines changed

prometheus/promhttp/http.go

Lines changed: 54 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -53,19 +53,16 @@ const (
5353
acceptEncodingHeader = "Accept-Encoding"
5454
)
5555

56-
var bufPool sync.Pool
57-
58-
func getBuf() *bytes.Buffer {
59-
buf := bufPool.Get()
60-
if buf == nil {
61-
return &bytes.Buffer{}
62-
}
63-
return buf.(*bytes.Buffer)
56+
var gzipPool = sync.Pool{
57+
New: func() interface{} {
58+
return gzip.NewWriter(nil)
59+
},
6460
}
6561

66-
func giveBuf(buf *bytes.Buffer) {
67-
buf.Reset()
68-
bufPool.Put(buf)
62+
var bufPool = sync.Pool{
63+
New: func() interface{} {
64+
return &bytes.Buffer{}
65+
},
6966
}
7067

7168
// Handler returns an http.Handler for the prometheus.DefaultGatherer, using
@@ -112,7 +109,6 @@ func HandlerFor(reg prometheus.Gatherer, opts HandlerOpts) http.Handler {
112109
return
113110
}
114111
}
115-
116112
mfs, err := reg.Gather()
117113
if err != nil {
118114
if opts.ErrorLog != nil {
@@ -133,10 +129,12 @@ func HandlerFor(reg prometheus.Gatherer, opts HandlerOpts) http.Handler {
133129
}
134130

135131
contentType := expfmt.Negotiate(req.Header)
136-
buf := getBuf()
137-
defer giveBuf(buf)
138-
writer, encoding := decorateWriter(req, buf, opts.DisableCompression)
139-
enc := expfmt.NewEncoder(writer, contentType)
132+
buf := bufPool.Get().(*bytes.Buffer)
133+
buf.Reset()
134+
enc := expfmt.NewEncoder(buf, contentType)
135+
136+
defer bufPool.Put(buf)
137+
140138
var lastErr error
141139
for _, mf := range mfs {
142140
if err := enc.Encode(mf); err != nil {
@@ -155,22 +153,28 @@ func HandlerFor(reg prometheus.Gatherer, opts HandlerOpts) http.Handler {
155153
}
156154
}
157155
}
158-
if closer, ok := writer.(io.Closer); ok {
159-
closer.Close()
160-
}
156+
161157
if lastErr != nil && buf.Len() == 0 {
162158
http.Error(w, "No metrics encoded, last error:\n\n"+lastErr.Error(), http.StatusInternalServerError)
163159
return
164160
}
165161
header := w.Header()
166162
header.Set(contentTypeHeader, string(contentType))
167163
header.Set(contentLengthHeader, fmt.Sprint(buf.Len()))
168-
if encoding != "" {
169-
header.Set(contentEncodingHeader, encoding)
170-
}
171-
if _, err := w.Write(buf.Bytes()); err != nil && opts.ErrorLog != nil {
172-
opts.ErrorLog.Println("error while sending encoded metrics:", err)
164+
165+
if !opts.DisableCompression && gzipAccepted(req.Header) {
166+
header.Set(contentEncodingHeader, "gzip")
167+
gz := gzipPool.Get().(*gzip.Writer)
168+
defer gzipPool.Put(gz)
169+
170+
gz.Reset(w)
171+
defer gz.Close()
172+
173+
zipWriter := gzipResponseWriter{gz, w}
174+
writeResult(zipWriter, buf, opts)
175+
return
173176
}
177+
writeResult(w, buf, opts)
174178
// TODO(beorn7): Consider streaming serving of metrics.
175179
})
176180

@@ -292,20 +296,36 @@ type HandlerOpts struct {
292296
Timeout time.Duration
293297
}
294298

295-
// decorateWriter wraps a writer to handle gzip compression if requested. It
296-
// returns the decorated writer and the appropriate "Content-Encoding" header
297-
// (which is empty if no compression is enabled).
298-
func decorateWriter(request *http.Request, writer io.Writer, compressionDisabled bool) (io.Writer, string) {
299-
if compressionDisabled {
300-
return writer, ""
299+
// gzipResponseWriter in charge of wrapping io.Writer and http.ReponseWriter
300+
// together, allowing to get a single struct which implements both interface.
301+
type gzipResponseWriter struct {
302+
io.Writer
303+
http.ResponseWriter
304+
}
305+
306+
func (w gzipResponseWriter) Write(b []byte) (int, error) {
307+
return w.Writer.Write(b)
308+
}
309+
310+
// writeResult to buf using http.ResponseWriter.
311+
// If ErrorLog is enabled, err is logged in.
312+
func writeResult(w http.ResponseWriter, buf *bytes.Buffer, opts HandlerOpts) {
313+
if _, err := w.Write(buf.Bytes()); err != nil && opts.ErrorLog != nil {
314+
opts.ErrorLog.Println("error while sending encoded metrics:", err)
301315
}
302-
header := request.Header.Get(acceptEncodingHeader)
303-
parts := strings.Split(header, ",")
316+
}
317+
318+
// gzipHandler return a http.HandlerFunc in charge of compressing the content
319+
// of the given http.HandlerFunc
320+
func gzipAccepted(header http.Header) bool {
321+
322+
a := header.Get(acceptEncodingHeader)
323+
parts := strings.Split(a, ",")
304324
for _, part := range parts {
305325
part = strings.TrimSpace(part)
306326
if part == "gzip" || strings.HasPrefix(part, "gzip;") {
307-
return gzip.NewWriter(writer), "gzip"
327+
return true
308328
}
309329
}
310-
return writer, ""
330+
return false
311331
}

0 commit comments

Comments
 (0)