mirror of
https://github.com/minio/minio.git
synced 2024-12-24 22:25:54 -05:00
stats: Add network and http statisics (#3686)
Network: total bytes of incoming and outgoing server's data by taking advantage of our ConnMux Read/Write wrapping HTTP: total number of different http verbs passed in http requests and different status codes passed in http responses. This is counted in a new http handler.
This commit is contained in:
parent
6717a0b68c
commit
93fd269329
@ -356,3 +356,52 @@ func (h resourceHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
// Serve HTTP.
|
// Serve HTTP.
|
||||||
h.handler.ServeHTTP(w, r)
|
h.handler.ServeHTTP(w, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// httpResponseRecorder wraps http.ResponseWriter
|
||||||
|
// to record some useful http response data.
|
||||||
|
type httpResponseRecorder struct {
|
||||||
|
http.ResponseWriter
|
||||||
|
http.Flusher
|
||||||
|
respStatusCode int
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wraps ResponseWriter's Write()
|
||||||
|
func (rww *httpResponseRecorder) Write(b []byte) (int, error) {
|
||||||
|
return rww.ResponseWriter.Write(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wraps ResponseWriter's Flush()
|
||||||
|
func (rww *httpResponseRecorder) Flush() {
|
||||||
|
f, ok := rww.ResponseWriter.(http.Flusher)
|
||||||
|
if ok {
|
||||||
|
f.Flush()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wraps ResponseWriter's WriteHeader() and record
|
||||||
|
// the response status code
|
||||||
|
func (rww *httpResponseRecorder) WriteHeader(httpCode int) {
|
||||||
|
rww.respStatusCode = httpCode
|
||||||
|
rww.ResponseWriter.WriteHeader(httpCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
// httpStatsHandler definition: gather HTTP statistics
|
||||||
|
type httpStatsHandler struct {
|
||||||
|
handler http.Handler
|
||||||
|
}
|
||||||
|
|
||||||
|
// setHttpStatsHandler sets a http Stats Handler
|
||||||
|
func setHTTPStatsHandler(h http.Handler) http.Handler {
|
||||||
|
return httpStatsHandler{handler: h}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h httpStatsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
|
// Wraps w to record http response information
|
||||||
|
ww := &httpResponseRecorder{ResponseWriter: w}
|
||||||
|
|
||||||
|
// Execute the request
|
||||||
|
h.handler.ServeHTTP(ww, r)
|
||||||
|
|
||||||
|
// Update http statistics
|
||||||
|
globalHTTPStats.updateStats(r, ww)
|
||||||
|
}
|
||||||
|
@ -122,6 +122,11 @@ var (
|
|||||||
// url.URL endpoints of disks that belong to the object storage.
|
// url.URL endpoints of disks that belong to the object storage.
|
||||||
globalEndpoints = []*url.URL{}
|
globalEndpoints = []*url.URL{}
|
||||||
|
|
||||||
|
// Global server's network statistics
|
||||||
|
globalConnStats = newConnStats()
|
||||||
|
// Global HTTP request statisitics
|
||||||
|
globalHTTPStats = newHTTPStats()
|
||||||
|
|
||||||
// Add new variable global values here.
|
// Add new variable global values here.
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -84,6 +84,8 @@ func configureServerHandler(srvCmdConfig serverCmdConfig) (http.Handler, error)
|
|||||||
|
|
||||||
// List of some generic handlers which are applied for all incoming requests.
|
// List of some generic handlers which are applied for all incoming requests.
|
||||||
var handlerFns = []HandlerFunc{
|
var handlerFns = []HandlerFunc{
|
||||||
|
// Network statistics
|
||||||
|
setHTTPStatsHandler,
|
||||||
// Limits all requests size to a maximum fixed limit
|
// Limits all requests size to a maximum fixed limit
|
||||||
setRequestSizeLimitHandler,
|
setRequestSizeLimitHandler,
|
||||||
// Adds 'crossdomain.xml' policy handler to serve legacy flash clients.
|
// Adds 'crossdomain.xml' policy handler to serve legacy flash clients.
|
||||||
|
@ -58,16 +58,16 @@ var defaultHTTP1Methods = []string{
|
|||||||
// connections on the same listeners.
|
// connections on the same listeners.
|
||||||
type ConnMux struct {
|
type ConnMux struct {
|
||||||
net.Conn
|
net.Conn
|
||||||
bufrw *bufio.ReadWriter
|
// To peek net.Conn incoming data
|
||||||
|
peeker *bufio.Reader
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewConnMux - creates a new ConnMux instance
|
// NewConnMux - creates a new ConnMux instance
|
||||||
func NewConnMux(c net.Conn) *ConnMux {
|
func NewConnMux(c net.Conn) *ConnMux {
|
||||||
br := bufio.NewReader(c)
|
br := bufio.NewReader(c)
|
||||||
bw := bufio.NewWriter(c)
|
|
||||||
return &ConnMux{
|
return &ConnMux{
|
||||||
Conn: c,
|
Conn: c,
|
||||||
bufrw: bufio.NewReadWriter(br, bw),
|
peeker: bufio.NewReader(br),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -83,7 +83,7 @@ const (
|
|||||||
// errors in peeking over the connection.
|
// errors in peeking over the connection.
|
||||||
func (c *ConnMux) PeekProtocol() (string, error) {
|
func (c *ConnMux) PeekProtocol() (string, error) {
|
||||||
// Peek for HTTP verbs.
|
// Peek for HTTP verbs.
|
||||||
buf, err := c.bufrw.Peek(maxHTTPVerbLen)
|
buf, err := c.peeker.Peek(maxHTTPVerbLen)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
@ -110,20 +110,31 @@ func (c *ConnMux) PeekProtocol() (string, error) {
|
|||||||
|
|
||||||
// Read - streams the ConnMux buffer when reset flag is activated, otherwise
|
// Read - streams the ConnMux buffer when reset flag is activated, otherwise
|
||||||
// streams from the incoming network connection
|
// streams from the incoming network connection
|
||||||
func (c *ConnMux) Read(b []byte) (int, error) {
|
func (c *ConnMux) Read(b []byte) (n int, e error) {
|
||||||
// Push read deadline
|
// Push read deadline
|
||||||
c.Conn.SetReadDeadline(time.Now().Add(defaultTCPReadTimeout))
|
c.Conn.SetReadDeadline(time.Now().Add(defaultTCPReadTimeout))
|
||||||
return c.bufrw.Read(b)
|
|
||||||
|
// Update server's connection statistics
|
||||||
|
defer func() {
|
||||||
|
globalConnStats.incInputBytes(n)
|
||||||
|
}()
|
||||||
|
|
||||||
|
return c.peeker.Read(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ConnMux) Write(b []byte) (n int, e error) {
|
||||||
|
// Update server's connection statistics
|
||||||
|
defer func() {
|
||||||
|
globalConnStats.incOutputBytes(n)
|
||||||
|
}()
|
||||||
|
// Run the underlying net.Conn Write() func
|
||||||
|
return c.Conn.Write(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close the connection.
|
// Close the connection.
|
||||||
func (c *ConnMux) Close() (err error) {
|
func (c *ConnMux) Close() (err error) {
|
||||||
// Make sure that we always close a connection,
|
// Make sure that we always close a connection,
|
||||||
// even if the bufioWriter flush sends an error.
|
return c.Conn.Close()
|
||||||
defer c.Conn.Close()
|
|
||||||
|
|
||||||
// Flush and write to the connection.
|
|
||||||
return c.bufrw.Flush()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListenerMux wraps the standard net.Listener to inspect
|
// ListenerMux wraps the standard net.Listener to inspect
|
||||||
|
129
cmd/stats.go
Normal file
129
cmd/stats.go
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
/*
|
||||||
|
* Minio Cloud Storage, (C) 2017 Minio, Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"sync/atomic"
|
||||||
|
)
|
||||||
|
|
||||||
|
// counter - simplify atomic counting
|
||||||
|
type counter struct {
|
||||||
|
val uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inc increases counter atomically
|
||||||
|
func (c *counter) Inc(n uint64) {
|
||||||
|
atomic.AddUint64(&c.val, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Value fetches counter's value atomically
|
||||||
|
func (c *counter) Value() uint64 {
|
||||||
|
return atomic.LoadUint64(&c.val)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConnStats - Network statistics
|
||||||
|
// Count total input/output transferred bytes during
|
||||||
|
// the server's life.
|
||||||
|
type ConnStats struct {
|
||||||
|
totalInputBytes counter
|
||||||
|
totalOutputBytes counter
|
||||||
|
}
|
||||||
|
|
||||||
|
// Increase total input bytes
|
||||||
|
func (s *ConnStats) incInputBytes(n int) {
|
||||||
|
s.totalInputBytes.Inc(uint64(n))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Increase total output bytes
|
||||||
|
func (s *ConnStats) incOutputBytes(n int) {
|
||||||
|
s.totalOutputBytes.Inc(uint64(n))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return total input bytes
|
||||||
|
func (s *ConnStats) getTotalInputBytes() uint64 {
|
||||||
|
return s.totalInputBytes.Value()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return total output bytes
|
||||||
|
func (s *ConnStats) getTotalOutputBytes() uint64 {
|
||||||
|
return s.totalOutputBytes.Value()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepare new ConnStats structure
|
||||||
|
func newConnStats() *ConnStats {
|
||||||
|
return &ConnStats{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// httpStats holds statistics information about
|
||||||
|
// HTTP requests made by all clients
|
||||||
|
type httpStats struct {
|
||||||
|
// HEAD request stats
|
||||||
|
totalHEADs counter
|
||||||
|
successHEADs counter
|
||||||
|
// GET request stats
|
||||||
|
totalGETs counter
|
||||||
|
successGETs counter
|
||||||
|
// PUT request
|
||||||
|
totalPUTs counter
|
||||||
|
successPUTs counter
|
||||||
|
// POST request
|
||||||
|
totalPOSTs counter
|
||||||
|
successPOSTs counter
|
||||||
|
// DELETE request
|
||||||
|
totalDELETEs counter
|
||||||
|
successDELETEs counter
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update statistics from http request and response data
|
||||||
|
func (st *httpStats) updateStats(r *http.Request, w *httpResponseRecorder) {
|
||||||
|
// A successful request has a 2xx response code
|
||||||
|
successReq := (w.respStatusCode >= 200 && w.respStatusCode < 300)
|
||||||
|
// Update stats according to method verb
|
||||||
|
switch r.Method {
|
||||||
|
case "HEAD":
|
||||||
|
st.totalHEADs.Inc(1)
|
||||||
|
if successReq {
|
||||||
|
st.successHEADs.Inc(1)
|
||||||
|
}
|
||||||
|
case "GET":
|
||||||
|
st.totalGETs.Inc(1)
|
||||||
|
if successReq {
|
||||||
|
st.successGETs.Inc(1)
|
||||||
|
}
|
||||||
|
case "PUT":
|
||||||
|
st.totalPUTs.Inc(1)
|
||||||
|
if successReq {
|
||||||
|
st.successPUTs.Inc(1)
|
||||||
|
}
|
||||||
|
case "POST":
|
||||||
|
st.totalPOSTs.Inc(1)
|
||||||
|
if successReq {
|
||||||
|
st.successPOSTs.Inc(1)
|
||||||
|
}
|
||||||
|
case "DELETE":
|
||||||
|
st.totalDELETEs.Inc(1)
|
||||||
|
if successReq {
|
||||||
|
st.successDELETEs.Inc(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepare new HttpStats structure
|
||||||
|
func newHTTPStats() *httpStats {
|
||||||
|
return &httpStats{}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user