Skip to content

Commit fdf4cbc

Browse files
committed
Use sync.Pool for gzipWriter
Signed-off-by: glefloch <[email protected]>
1 parent 0a8115f commit fdf4cbc

File tree

1 file changed

+48
-44
lines changed

1 file changed

+48
-44
lines changed

prometheus/promhttp/http.go

Lines changed: 48 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -53,19 +53,10 @@ 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)
64-
}
65-
66-
func giveBuf(buf *bytes.Buffer) {
67-
buf.Reset()
68-
bufPool.Put(buf)
56+
var gzipPool = sync.Pool{
57+
New: func() interface{} {
58+
return gzip.NewWriter(nil)
59+
},
6960
}
7061

7162
// Handler returns an http.Handler for the prometheus.DefaultGatherer, using
@@ -133,10 +124,9 @@ func HandlerFor(reg prometheus.Gatherer, opts HandlerOpts) http.Handler {
133124
}
134125

135126
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)
127+
buf := &bytes.Buffer{}
128+
enc := expfmt.NewEncoder(buf, contentType)
129+
140130
var lastErr error
141131
for _, mf := range mfs {
142132
if err := enc.Encode(mf); err != nil {
@@ -155,29 +145,50 @@ func HandlerFor(reg prometheus.Gatherer, opts HandlerOpts) http.Handler {
155145
}
156146
}
157147
}
158-
if closer, ok := writer.(io.Closer); ok {
159-
closer.Close()
160-
}
148+
161149
if lastErr != nil && buf.Len() == 0 {
162150
http.Error(w, "No metrics encoded, last error:\n\n"+lastErr.Error(), http.StatusInternalServerError)
163151
return
164152
}
165-
header := w.Header()
166-
header.Set(contentTypeHeader, string(contentType))
167-
header.Set(contentLengthHeader, fmt.Sprint(buf.Len()))
168-
if encoding != "" {
169-
header.Set(contentEncodingHeader, encoding)
170-
}
153+
154+
w.Header().Set(contentTypeHeader, string(contentType))
155+
171156
if _, err := w.Write(buf.Bytes()); err != nil && opts.ErrorLog != nil {
172157
opts.ErrorLog.Println("error while sending encoded metrics:", err)
173158
}
174159
// TODO(beorn7): Consider streaming serving of metrics.
175160
})
176161

162+
gzipHandler := http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
163+
if opts.DisableCompression {
164+
h.ServeHTTP(w, req)
165+
return
166+
}
167+
header := req.Header.Get(acceptEncodingHeader)
168+
parts := strings.Split(header, ",")
169+
for _, part := range parts {
170+
part = strings.TrimSpace(part)
171+
if part == "gzip" || strings.HasPrefix(part, "gzip;") {
172+
173+
w.Header().Set(contentEncodingHeader, "gzip")
174+
gz := gzipPool.Get().(*gzip.Writer)
175+
defer gzipPool.Put(gz)
176+
177+
gz.Reset(w)
178+
defer gz.Close()
179+
180+
h.ServeHTTP(gzipResponseWriter{gz, w}, req)
181+
return
182+
}
183+
}
184+
h.ServeHTTP(w, req)
185+
return
186+
})
187+
177188
if opts.Timeout <= 0 {
178-
return h
189+
return gzipHandler
179190
}
180-
return http.TimeoutHandler(h, opts.Timeout, fmt.Sprintf(
191+
return http.TimeoutHandler(gzipHandler, opts.Timeout, fmt.Sprintf(
181192
"Exceeded configured timeout of %v.\n",
182193
opts.Timeout,
183194
))
@@ -292,20 +303,13 @@ type HandlerOpts struct {
292303
Timeout time.Duration
293304
}
294305

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, ""
301-
}
302-
header := request.Header.Get(acceptEncodingHeader)
303-
parts := strings.Split(header, ",")
304-
for _, part := range parts {
305-
part = strings.TrimSpace(part)
306-
if part == "gzip" || strings.HasPrefix(part, "gzip;") {
307-
return gzip.NewWriter(writer), "gzip"
308-
}
309-
}
310-
return writer, ""
306+
// gzipResponseWriter in charge of wrapping io.Writer and http.ReponseWriter
307+
// together, allowing to get a single struct which implements both interface.
308+
type gzipResponseWriter struct {
309+
io.Writer
310+
http.ResponseWriter
311+
}
312+
313+
func (w gzipResponseWriter) Write(b []byte) (int, error) {
314+
return w.Writer.Write(b)
311315
}

0 commit comments

Comments
 (0)