Better stats in realtime-advanced

This commit is contained in:
Manu Mtz-Almeida 2015-05-14 18:16:00 +02:00
parent 286d775de6
commit eed6d93095
5 changed files with 61 additions and 45 deletions

View File

@ -5,11 +5,8 @@ import (
"runtime" "runtime"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/manucorporat/stats"
) )
var messages = stats.New()
func main() { func main() {
ConfigRuntime() ConfigRuntime()
StartWorkers() StartWorkers()

View File

@ -62,7 +62,7 @@
<p>The chat and the charts data is provided in realtime using the SSE implemention of <a href="https://github.com/gin-gonic/gin/blob/15b0c49da556d58a3d934b86e3aa552ff224026d/examples/realtime-chat/main.go#L23-L32">Gin Framework</a>.</p> <p>The chat and the charts data is provided in realtime using the SSE implemention of <a href="https://github.com/gin-gonic/gin/blob/15b0c49da556d58a3d934b86e3aa552ff224026d/examples/realtime-chat/main.go#L23-L32">Gin Framework</a>.</p>
<div class="row"> <div class="row">
<div class="col-md-8"> <div class="col-md-8">
<div id="chat-scroll" style="overflow-y:scroll; overflow-x:scroll; height:270px"> <div id="chat-scroll" style="overflow-y:scroll; overflow-x:scroll; height:290px">
<table id="table-style" class="table" data-show-header="false"> <table id="table-style" class="table" data-show-header="false">
<thead> <thead>
<tr> <tr>
@ -97,8 +97,12 @@
{{end}} {{end}}
</div> </div>
<div class="col-md-4"> <div class="col-md-4">
<h3>Inbound/Outbound</h3> <div id="messagesChart" class="epoch category10"></div>
<div id="messagesChart" class="epoch category20c"></div> <p>
<span style="font-size:20px; color:#1f77b4">◼︎</span> Users<br>
<span style="font-size:20px; color:#ff7f0e">◼︎</span> Inbound messages / sec<br>
<span style="font-size:20px; color:#2ca02c">◼︎</span> Outbound messages / sec<br>
</p>
</div> </div>
</div> </div>
</div> </div>
@ -106,22 +110,24 @@
<div class="container"> <div class="container">
<div class="row"> <div class="row">
<h2>Realtime server Go stats</h2> <h2>Realtime server Go stats</h2>
<div class="col-md-4"> <div class="col-md-6">
<h3>Number of Goroutines</h3> <h3>Memory usage</h3>
<p> <p>
<div id="goroutinesChart" class="epoch category20c"></div> <div id="heapChart" class="epoch category20c"></div>
</p>
<p>
<span style="font-size:20px; color:#1f77b4">◼︎</span> Heap bytes<br>
<span style="font-size:20px; color:#aec7e8">◼︎</span> Stack bytes<br>
</p> </p>
</div> </div>
<div class="col-md-4"> <div class="col-md-6">
<h3>HEAP/Stack bytes</h3> <h3>Allocations per second</h3>
<p> <p>
<div id="heapChart" class="epoch category20b"></div> <div id="mallocsChart" class="epoch category20b"></div>
</p> </p>
</div>
<div class="col-md-4">
<h3>Mallocs/Frees</h3>
<p> <p>
<div id="mallocsChart" class="epoch category10"></div> <span style="font-size:20px; color:#393b79">◼︎</span> Mallocs / sec<br>
<span style="font-size:20px; color:#5254a3">◼︎</span> Frees / sec<br>
</p> </p>
</div> </div>
</div> </div>

View File

@ -18,19 +18,12 @@ function StartEpoch(timestamp) {
var windowSize = 60; var windowSize = 60;
var height = 200; var height = 200;
var defaultData = histogram(windowSize, timestamp); var defaultData = histogram(windowSize, timestamp);
window.goroutinesChart = $('#goroutinesChart').epoch({
type: 'time.bar',
axes: ['bottom', 'left'],
height: height,
data: [
{values: defaultData}
]
});
window.heapChart = $('#heapChart').epoch({ window.heapChart = $('#heapChart').epoch({
type: 'time.area', type: 'time.area',
axes: ['bottom', 'left'], axes: ['bottom', 'left'],
height: height, height: height,
historySize: 10,
data: [ data: [
{values: defaultData}, {values: defaultData},
{values: defaultData} {values: defaultData}
@ -41,6 +34,7 @@ function StartEpoch(timestamp) {
type: 'time.area', type: 'time.area',
axes: ['bottom', 'left'], axes: ['bottom', 'left'],
height: height, height: height,
historySize: 10,
data: [ data: [
{values: defaultData}, {values: defaultData},
{values: defaultData} {values: defaultData}
@ -48,10 +42,12 @@ function StartEpoch(timestamp) {
}); });
window.messagesChart = $('#messagesChart').epoch({ window.messagesChart = $('#messagesChart').epoch({
type: 'time.area', type: 'time.line',
axes: ['bottom', 'left'], axes: ['bottom', 'left'],
height: 250, height: 240,
historySize: 10,
data: [ data: [
{values: defaultData},
{values: defaultData}, {values: defaultData},
{values: defaultData} {values: defaultData}
] ]
@ -69,11 +65,10 @@ function StartSSE(roomid) {
} }
function stats(e) { function stats(e) {
var data = parseJSONStats(e.data) var data = parseJSONStats(e.data);
heapChart.push(data.heap) heapChart.push(data.heap);
mallocsChart.push(data.mallocs) mallocsChart.push(data.mallocs);
goroutinesChart.push(data.goroutines) messagesChart.push(data.messages);
messagesChart.push(data.messages)
} }
function parseJSONStats(e) { function parseJSONStats(e) {
@ -90,16 +85,14 @@ function parseJSONStats(e) {
{time: timestamp, y: data.Frees} {time: timestamp, y: data.Frees}
]; ];
var messages = [ var messages = [
{time: timestamp, y: data.Connected},
{time: timestamp, y: data.Inbound}, {time: timestamp, y: data.Inbound},
{time: timestamp, y: data.Outbound} {time: timestamp, y: data.Outbound}
]; ];
var goroutines = [
{time: timestamp, y: data.NuGoroutines},
]
return { return {
heap: heap, heap: heap,
mallocs: mallocs, mallocs: mallocs,
goroutines: goroutines,
messages: messages messages: messages
} }
} }

View File

@ -59,8 +59,12 @@ func streamRoom(c *gin.Context) {
roomid := c.ParamValue("roomid") roomid := c.ParamValue("roomid")
listener := openListener(roomid) listener := openListener(roomid)
ticker := time.NewTicker(1 * time.Second) ticker := time.NewTicker(1 * time.Second)
defer closeListener(roomid, listener) users.Add("connected", 1)
defer ticker.Stop() defer func() {
closeListener(roomid, listener)
ticker.Stop()
users.Add("disconnected", 1)
}()
c.Stream(func(w io.Writer) bool { c.Stream(func(w io.Writer) bool {
select { select {

View File

@ -4,33 +4,49 @@ import (
"runtime" "runtime"
"sync" "sync"
"time" "time"
"github.com/manucorporat/stats"
) )
var messages = stats.New()
var users = stats.New()
var mutexStats sync.RWMutex var mutexStats sync.RWMutex
var savedStats map[string]uint64 var savedStats map[string]uint64
func statsWorker() { func statsWorker() {
c := time.Tick(1 * time.Second) c := time.Tick(1 * time.Second)
var lastMallocs uint64 = 0
var lastFrees uint64 = 0
for range c { for range c {
var stats runtime.MemStats var stats runtime.MemStats
runtime.ReadMemStats(&stats) runtime.ReadMemStats(&stats)
mutexStats.Lock() mutexStats.Lock()
savedStats = map[string]uint64{ savedStats = map[string]uint64{
"timestamp": uint64(time.Now().Unix()), "timestamp": uint64(time.Now().Unix()),
"HeapInuse": stats.HeapInuse, "HeapInuse": stats.HeapInuse,
"StackInuse": stats.StackInuse, "StackInuse": stats.StackInuse,
"NuGoroutines": uint64(runtime.NumGoroutine()), "Mallocs": (stats.Mallocs - lastMallocs),
"Mallocs": stats.Mallocs, "Frees": (stats.Frees - lastFrees),
"Frees": stats.Mallocs, "Inbound": uint64(messages.Get("inbound")),
"Inbound": uint64(messages.Get("inbound")), "Outbound": uint64(messages.Get("outbound")),
"Outbound": uint64(messages.Get("outbound")), "Connected": connectedUsers(),
} }
lastMallocs = stats.Mallocs
lastFrees = stats.Frees
messages.Reset() messages.Reset()
mutexStats.Unlock() mutexStats.Unlock()
} }
} }
func connectedUsers() uint64 {
connected := users.Get("connected") - users.Get("disconnected")
if connected < 0 {
return 0
}
return uint64(connected)
}
func Stats() map[string]uint64 { func Stats() map[string]uint64 {
mutexStats.RLock() mutexStats.RLock()
defer mutexStats.RUnlock() defer mutexStats.RUnlock()