diff --git a/examples/realtime-advanced/main.go b/examples/realtime-advanced/main.go index 65808da4..d209d28c 100644 --- a/examples/realtime-advanced/main.go +++ b/examples/realtime-advanced/main.go @@ -5,11 +5,8 @@ import ( "runtime" "github.com/gin-gonic/gin" - "github.com/manucorporat/stats" ) -var messages = stats.New() - func main() { ConfigRuntime() StartWorkers() diff --git a/examples/realtime-advanced/resources/room_login.templ.html b/examples/realtime-advanced/resources/room_login.templ.html index 0e55aaea..27dac387 100644 --- a/examples/realtime-advanced/resources/room_login.templ.html +++ b/examples/realtime-advanced/resources/room_login.templ.html @@ -62,7 +62,7 @@

The chat and the charts data is provided in realtime using the SSE implemention of Gin Framework.

-
+
@@ -97,8 +97,12 @@ {{end}}
-

Inbound/Outbound

-
+
+

+ ◼︎ Users
+ ◼︎ Inbound messages / sec
+ ◼︎ Outbound messages / sec
+

@@ -106,22 +110,24 @@

Realtime server Go stats

-
-

Number of Goroutines

+
+

Memory usage

-

+
+

+

+ ◼︎ Heap bytes
+ ◼︎ Stack bytes

-
-

HEAP/Stack bytes

+
+

Allocations per second

-

+

-
-
-

Mallocs/Frees

-

+ ◼︎ Mallocs / sec
+ ◼︎ Frees / sec

diff --git a/examples/realtime-advanced/resources/static/realtime.js b/examples/realtime-advanced/resources/static/realtime.js index da4ae88c..919dae26 100644 --- a/examples/realtime-advanced/resources/static/realtime.js +++ b/examples/realtime-advanced/resources/static/realtime.js @@ -18,19 +18,12 @@ function StartEpoch(timestamp) { var windowSize = 60; var height = 200; var defaultData = histogram(windowSize, timestamp); - window.goroutinesChart = $('#goroutinesChart').epoch({ - type: 'time.bar', - axes: ['bottom', 'left'], - height: height, - data: [ - {values: defaultData} - ] - }); window.heapChart = $('#heapChart').epoch({ type: 'time.area', axes: ['bottom', 'left'], height: height, + historySize: 10, data: [ {values: defaultData}, {values: defaultData} @@ -41,6 +34,7 @@ function StartEpoch(timestamp) { type: 'time.area', axes: ['bottom', 'left'], height: height, + historySize: 10, data: [ {values: defaultData}, {values: defaultData} @@ -48,10 +42,12 @@ function StartEpoch(timestamp) { }); window.messagesChart = $('#messagesChart').epoch({ - type: 'time.area', + type: 'time.line', axes: ['bottom', 'left'], - height: 250, + height: 240, + historySize: 10, data: [ + {values: defaultData}, {values: defaultData}, {values: defaultData} ] @@ -69,11 +65,10 @@ function StartSSE(roomid) { } function stats(e) { - var data = parseJSONStats(e.data) - heapChart.push(data.heap) - mallocsChart.push(data.mallocs) - goroutinesChart.push(data.goroutines) - messagesChart.push(data.messages) + var data = parseJSONStats(e.data); + heapChart.push(data.heap); + mallocsChart.push(data.mallocs); + messagesChart.push(data.messages); } function parseJSONStats(e) { @@ -90,16 +85,14 @@ function parseJSONStats(e) { {time: timestamp, y: data.Frees} ]; var messages = [ + {time: timestamp, y: data.Connected}, {time: timestamp, y: data.Inbound}, {time: timestamp, y: data.Outbound} ]; - var goroutines = [ - {time: timestamp, y: data.NuGoroutines}, - ] + return { heap: heap, mallocs: mallocs, - goroutines: goroutines, messages: messages } } diff --git a/examples/realtime-advanced/routes.go b/examples/realtime-advanced/routes.go index 9762f52c..fb1f5b6e 100644 --- a/examples/realtime-advanced/routes.go +++ b/examples/realtime-advanced/routes.go @@ -59,8 +59,12 @@ func streamRoom(c *gin.Context) { roomid := c.ParamValue("roomid") listener := openListener(roomid) ticker := time.NewTicker(1 * time.Second) - defer closeListener(roomid, listener) - defer ticker.Stop() + users.Add("connected", 1) + defer func() { + closeListener(roomid, listener) + ticker.Stop() + users.Add("disconnected", 1) + }() c.Stream(func(w io.Writer) bool { select { diff --git a/examples/realtime-advanced/stats.go b/examples/realtime-advanced/stats.go index f3ccab38..896787f9 100644 --- a/examples/realtime-advanced/stats.go +++ b/examples/realtime-advanced/stats.go @@ -4,33 +4,49 @@ import ( "runtime" "sync" "time" + + "github.com/manucorporat/stats" ) +var messages = stats.New() +var users = stats.New() var mutexStats sync.RWMutex var savedStats map[string]uint64 func statsWorker() { c := time.Tick(1 * time.Second) + var lastMallocs uint64 = 0 + var lastFrees uint64 = 0 for range c { var stats runtime.MemStats runtime.ReadMemStats(&stats) mutexStats.Lock() savedStats = map[string]uint64{ - "timestamp": uint64(time.Now().Unix()), - "HeapInuse": stats.HeapInuse, - "StackInuse": stats.StackInuse, - "NuGoroutines": uint64(runtime.NumGoroutine()), - "Mallocs": stats.Mallocs, - "Frees": stats.Mallocs, - "Inbound": uint64(messages.Get("inbound")), - "Outbound": uint64(messages.Get("outbound")), + "timestamp": uint64(time.Now().Unix()), + "HeapInuse": stats.HeapInuse, + "StackInuse": stats.StackInuse, + "Mallocs": (stats.Mallocs - lastMallocs), + "Frees": (stats.Frees - lastFrees), + "Inbound": uint64(messages.Get("inbound")), + "Outbound": uint64(messages.Get("outbound")), + "Connected": connectedUsers(), } + lastMallocs = stats.Mallocs + lastFrees = stats.Frees messages.Reset() 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 { mutexStats.RLock() defer mutexStats.RUnlock()