forked from mirror/gin
Better stats in realtime-advanced
This commit is contained in:
parent
286d775de6
commit
eed6d93095
|
@ -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()
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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()
|
||||||
|
|
Loading…
Reference in New Issue