Skip to content

Commit

Permalink
nsqd: expose memory stats under /stats
Browse files Browse the repository at this point in the history
  • Loading branch information
sparklxb committed Apr 1, 2017
1 parent ccbb9c3 commit 30c3ef7
Show file tree
Hide file tree
Showing 4 changed files with 74 additions and 31 deletions.
19 changes: 16 additions & 3 deletions nsqd/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -495,19 +495,21 @@ func (s *httpServer) doStats(w http.ResponseWriter, req *http.Request, ps httpro
}
}

ms := getMemStats()
if !jsonFormat {
return s.printStats(stats, health, startTime, uptime), nil
return s.printStats(stats, ms, health, startTime, uptime), nil
}

return struct {
Version string `json:"version"`
Health string `json:"health"`
StartTime int64 `json:"start_time"`
Topics []TopicStats `json:"topics"`
}{version.Binary, health, startTime.Unix(), stats}, nil
Memory *memStats `json:"memory"`
}{version.Binary, health, startTime.Unix(), stats, ms}, nil
}

func (s *httpServer) printStats(stats []TopicStats, health string, startTime time.Time, uptime time.Duration) []byte {
func (s *httpServer) printStats(stats []TopicStats, ms *memStats, health string, startTime time.Time, uptime time.Duration) []byte {
var buf bytes.Buffer
w := &buf
now := time.Now()
Expand All @@ -518,6 +520,17 @@ func (s *httpServer) printStats(stats []TopicStats, health string, startTime tim
io.WriteString(w, "\nNO_TOPICS\n")
return buf.Bytes()
}
fmt.Fprintf(w, "\nMemory:\n")
fmt.Fprintf(w, " %-25s\t%d\n", "heap_objects", ms.HeapObjects)
fmt.Fprintf(w, " %-25s\t%d\n", "heap_idle_bytes", ms.HeapIdleBytes)
fmt.Fprintf(w, " %-25s\t%d\n", "heap_in_use_bytes", ms.HeapInUseBytes)
fmt.Fprintf(w, " %-25s\t%d\n", "heap_released_bytes", ms.HeapReleasedBytes)
fmt.Fprintf(w, " %-25s\t%d\n", "gc_pause_usec_100", ms.GCPauseUsec100)
fmt.Fprintf(w, " %-25s\t%d\n", "gc_pause_usec_99", ms.GCPauseUsec99)
fmt.Fprintf(w, " %-25s\t%d\n", "gc_pause_usec_95", ms.GCPauseUsec95)
fmt.Fprintf(w, " %-25s\t%d\n", "next_gc_bytes", ms.NextGCBytes)
fmt.Fprintf(w, " %-25s\t%d\n", "gc_total_runs", ms.GCTotalRuns)

io.WriteString(w, fmt.Sprintf("\nHealth: %s\n", health))
for _, t := range stats {
var pausedPrefix string
Expand Down
6 changes: 4 additions & 2 deletions nsqd/http_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import (
"testing"
"time"

"strings"

"github.com/nsqio/go-nsq"
"github.com/nsqio/nsq/internal/test"
"github.com/nsqio/nsq/internal/version"
Expand Down Expand Up @@ -478,15 +480,15 @@ func TestHTTPgetStatusJSON(t *testing.T) {
defer nsqd.Exit()

nsqd.startTime = testTime
expectedJSON := fmt.Sprintf(`{"version":"%v","health":"OK","start_time":%v,"topics":[]}`, version.Binary, testTime.Unix())
expectedJSON := fmt.Sprintf(`{"version":"%v","health":"OK","start_time":%v,"topics":[],"memory":{`, version.Binary, testTime.Unix())

url := fmt.Sprintf("http://%s/stats?format=json", httpAddr)
resp, err := http.Get(url)
test.Nil(t, err)
defer resp.Body.Close()
body, _ := ioutil.ReadAll(resp.Body)
test.Equal(t, 200, resp.StatusCode)
test.Equal(t, expectedJSON, string(body))
test.Equal(t, true, strings.HasPrefix(string(body), expectedJSON))
}

func TestHTTPgetStatusText(t *testing.T) {
Expand Down
40 changes: 40 additions & 0 deletions nsqd/stats.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package nsqd

import (
"runtime"
"sort"
"sync/atomic"

Expand Down Expand Up @@ -143,3 +144,42 @@ func (n *NSQD) GetStats() []TopicStats {
}
return topics
}

type memStats struct {
HeapObjects uint64 `json:"heap_objects"`
HeapIdleBytes uint64 `json:"heap_idle_bytes"`
HeapInUseBytes uint64 `json:"heap_in_use_bytes"`
HeapReleasedBytes uint64 `json:"heap_released_bytes"`
GCPauseUsec100 uint64 `json:"gc_pause_usec_100"`
GCPauseUsec99 uint64 `json:"gc_pause_usec_99"`
GCPauseUsec95 uint64 `json:"gc_pause_usec_95"`
NextGCBytes uint64 `json:"next_gc_bytes"`
GCTotalRuns uint32 `json:"gc_total_runs"`
}

func getMemStats() *memStats {
var ms runtime.MemStats
runtime.ReadMemStats(&ms)

// sort the GC pause array
length := len(ms.PauseNs)
if int(ms.NumGC) < length {
length = int(ms.NumGC)
}
gcPauses := make(Uint64Slice, length)
copy(gcPauses, ms.PauseNs[:length])
sort.Sort(gcPauses)

return &memStats{
ms.HeapObjects,
ms.HeapIdle,
ms.HeapInuse,
ms.HeapReleased,
percentile(100.0, gcPauses, len(gcPauses)) / 1000,
percentile(99.0, gcPauses, len(gcPauses)) / 1000,
percentile(95.0, gcPauses, len(gcPauses)) / 1000,
ms.NextGC,
ms.NumGC,
}

}
40 changes: 14 additions & 26 deletions nsqd/statsd.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ package nsqd
import (
"fmt"
"math"
"runtime"
"sort"
"time"

"github.com/nsqio/nsq/internal/statsd"
Expand All @@ -25,7 +23,7 @@ func (s Uint64Slice) Less(i, j int) bool {
}

func (n *NSQD) statsdLoop() {
var lastMemStats runtime.MemStats
var lastMemStats *memStats
var lastStats []TopicStats
ticker := time.NewTicker(n.getOpts().StatsdInterval)
for {
Expand Down Expand Up @@ -115,29 +113,19 @@ func (n *NSQD) statsdLoop() {
lastStats = stats

if n.getOpts().StatsdMemStats {
var memStats runtime.MemStats
runtime.ReadMemStats(&memStats)

// sort the GC pause array
length := len(memStats.PauseNs)
if int(memStats.NumGC) < length {
length = int(memStats.NumGC)
}
gcPauses := make(Uint64Slice, length)
copy(gcPauses, memStats.PauseNs[:length])
sort.Sort(gcPauses)

client.Gauge("mem.heap_objects", int64(memStats.HeapObjects))
client.Gauge("mem.heap_idle_bytes", int64(memStats.HeapIdle))
client.Gauge("mem.heap_in_use_bytes", int64(memStats.HeapInuse))
client.Gauge("mem.heap_released_bytes", int64(memStats.HeapReleased))
client.Gauge("mem.gc_pause_usec_100", int64(percentile(100.0, gcPauses, len(gcPauses))/1000))
client.Gauge("mem.gc_pause_usec_99", int64(percentile(99.0, gcPauses, len(gcPauses))/1000))
client.Gauge("mem.gc_pause_usec_95", int64(percentile(95.0, gcPauses, len(gcPauses))/1000))
client.Gauge("mem.next_gc_bytes", int64(memStats.NextGC))
client.Incr("mem.gc_runs", int64(memStats.NumGC-lastMemStats.NumGC))

lastMemStats = memStats
ms := getMemStats()

client.Gauge("mem.heap_objects", int64(ms.HeapObjects))
client.Gauge("mem.heap_idle_bytes", int64(ms.HeapIdleBytes))
client.Gauge("mem.heap_in_use_bytes", int64(ms.HeapInUseBytes))
client.Gauge("mem.heap_released_bytes", int64(ms.HeapReleasedBytes))
client.Gauge("mem.gc_pause_usec_100", int64(ms.GCPauseUsec100))
client.Gauge("mem.gc_pause_usec_99", int64(ms.GCPauseUsec99))
client.Gauge("mem.gc_pause_usec_95", int64(ms.GCPauseUsec95))
client.Gauge("mem.next_gc_bytes", int64(ms.NextGCBytes))
client.Incr("mem.gc_runs", int64(ms.GCTotalRuns-lastMemStats.GCTotalRuns))

lastMemStats = ms
}

client.Close()
Expand Down

0 comments on commit 30c3ef7

Please sign in to comment.