Merge branch 'use-gzip-pool' of github.com:glefloch/client_golang into beorn7/http
This commit is contained in:
commit
752f50d366
|
@ -53,19 +53,16 @@ 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) {
|
var bufPool = sync.Pool{
|
||||||
buf.Reset()
|
New: func() interface{} {
|
||||||
bufPool.Put(buf)
|
return &bytes.Buffer{}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handler returns an http.Handler for the prometheus.DefaultGatherer, using
|
// Handler returns an http.Handler for the prometheus.DefaultGatherer, using
|
||||||
|
@ -112,7 +109,6 @@ func HandlerFor(reg prometheus.Gatherer, opts HandlerOpts) http.Handler {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mfs, err := reg.Gather()
|
mfs, err := reg.Gather()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if opts.ErrorLog != nil {
|
if opts.ErrorLog != nil {
|
||||||
|
@ -133,10 +129,12 @@ func HandlerFor(reg prometheus.Gatherer, opts HandlerOpts) http.Handler {
|
||||||
}
|
}
|
||||||
|
|
||||||
contentType := expfmt.Negotiate(req.Header)
|
contentType := expfmt.Negotiate(req.Header)
|
||||||
buf := getBuf()
|
buf := bufPool.Get().(*bytes.Buffer)
|
||||||
defer giveBuf(buf)
|
buf.Reset()
|
||||||
writer, encoding := decorateWriter(req, buf, opts.DisableCompression)
|
enc := expfmt.NewEncoder(buf, contentType)
|
||||||
enc := expfmt.NewEncoder(writer, contentType)
|
|
||||||
|
defer bufPool.Put(buf)
|
||||||
|
|
||||||
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,9 +153,7 @@ 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
|
||||||
|
@ -165,12 +161,20 @@ func HandlerFor(reg prometheus.Gatherer, opts HandlerOpts) http.Handler {
|
||||||
header := w.Header()
|
header := w.Header()
|
||||||
header.Set(contentTypeHeader, string(contentType))
|
header.Set(contentTypeHeader, string(contentType))
|
||||||
header.Set(contentLengthHeader, fmt.Sprint(buf.Len()))
|
header.Set(contentLengthHeader, fmt.Sprint(buf.Len()))
|
||||||
if encoding != "" {
|
|
||||||
header.Set(contentEncodingHeader, encoding)
|
if !opts.DisableCompression && gzipAccepted(req.Header) {
|
||||||
}
|
header.Set(contentEncodingHeader, "gzip")
|
||||||
if _, err := w.Write(buf.Bytes()); err != nil && opts.ErrorLog != nil {
|
gz := gzipPool.Get().(*gzip.Writer)
|
||||||
opts.ErrorLog.Println("error while sending encoded metrics:", err)
|
defer gzipPool.Put(gz)
|
||||||
|
|
||||||
|
gz.Reset(w)
|
||||||
|
defer gz.Close()
|
||||||
|
|
||||||
|
zipWriter := gzipResponseWriter{gz, w}
|
||||||
|
writeResult(zipWriter, buf, opts)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
writeResult(w, buf, opts)
|
||||||
// TODO(beorn7): Consider streaming serving of metrics.
|
// TODO(beorn7): Consider streaming serving of metrics.
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -292,20 +296,36 @@ 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, ""
|
}
|
||||||
|
|
||||||
|
func (w gzipResponseWriter) Write(b []byte) (int, error) {
|
||||||
|
return w.Writer.Write(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// writeResult to buf using http.ResponseWriter.
|
||||||
|
// If ErrorLog is enabled, err is logged in.
|
||||||
|
func writeResult(w http.ResponseWriter, buf *bytes.Buffer, opts HandlerOpts) {
|
||||||
|
if _, err := w.Write(buf.Bytes()); err != nil && opts.ErrorLog != nil {
|
||||||
|
opts.ErrorLog.Println("error while sending encoded metrics:", err)
|
||||||
}
|
}
|
||||||
header := request.Header.Get(acceptEncodingHeader)
|
}
|
||||||
parts := strings.Split(header, ",")
|
|
||||||
|
// gzipHandler return a http.HandlerFunc in charge of compressing the content
|
||||||
|
// of the given http.HandlerFunc
|
||||||
|
func gzipAccepted(header http.Header) bool {
|
||||||
|
|
||||||
|
a := header.Get(acceptEncodingHeader)
|
||||||
|
parts := strings.Split(a, ",")
|
||||||
for _, part := range parts {
|
for _, part := range parts {
|
||||||
part = strings.TrimSpace(part)
|
part = strings.TrimSpace(part)
|
||||||
if part == "gzip" || strings.HasPrefix(part, "gzip;") {
|
if part == "gzip" || strings.HasPrefix(part, "gzip;") {
|
||||||
return gzip.NewWriter(writer), "gzip"
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return writer, ""
|
return false
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue