dashboard的CPU负载和内存用量展示可用了。

pull/105/head
Apex Liu 2017-12-12 18:38:30 +08:00
parent 145e3a0d5c
commit 5b8f882f61
7 changed files with 326 additions and 225 deletions

View File

@ -1,7 +1,7 @@
"use strict";
$app.on_init = function (cb_stack) {
$app.MAX_OVERLOAD_DATA = 20;
$app.MAX_OVERLOAD_DATA = 10 * 60 / 5;
$app.dom = {
count_user: $('#count-user')
, count_host: $('#count-host')
@ -12,7 +12,7 @@ $app.on_init = function (cb_stack) {
// refresh basic info every 1m.
$app.load_basic_info();
// refresh overload info every 5m.
$app.load_overload_info();
//$app.load_overload_info();
$app.ws = null;
$app.init_ws();
@ -42,50 +42,40 @@ $app.load_basic_info = function () {
setTimeout($app.load_basic_info, 60 * 1000);
};
$app.load_overload_info = function () {
$app.init_sys_status_info = function (data) {
var i = 0;
// var bar_x = [];
// for (i = 0; i < $app.MAX_OVERLOAD_DATA; i++) {
// bar_x.push(i);
// }
// var t = (Math.floor(Date.now() / 1000) - $app.MAX_OVERLOAD_DATA - 1) * 1000;
console.log(data);
var now = Math.floor(Date.now() / 1000);
console.log('now', now);
//=====================================
// CPU
//=====================================
$app.bar_cpu_user = [];
$app.bar_cpu_sys = [];
var t = tp_local2utc(now - $app.MAX_OVERLOAD_DATA - 1);
console.log(t);
for (i = 0; i < $app.MAX_OVERLOAD_DATA; i++) {
var x = t + i;
console.log(x, t);
$app.bar_cpu_user.push([
{
name: x.toString()
, value: [tp_format_datetime(tp_utc2local(x)), 0]
}
]);
$app.bar_cpu_sys.push([
{
name: x.toString()
, value: [tp_format_datetime(tp_utc2local(x)), 0]
}
]);
for (i = 0; i < data.length; i++) {
// var x = t + i * 1000;
$app.bar_cpu_user.push({name: tp_format_datetime(tp_utc2local(data[i].t), 'HH:mm:ss'), value: [tp_utc2local(data[i].t)*1000, data[i].c.u]});
$app.bar_cpu_sys.push({name: tp_format_datetime(tp_utc2local(data[i].t), 'HH:mm:ss'), value: [tp_utc2local(data[i].t)*1000, data[i].c.s]});
}
//console.log('--', $app.bar_cpu_data);
var clr_user = '#e2524c';
var clr_user_area = '#f7827a';
var clr_sys = '#558c5a';
var clr_sys_area = '#3dc34a';
$app.bar_cpu = echarts.init(document.getElementById('bar-cpu'));
$app.bar_cpu.setOption({
title: {
// show: false
text: 'CPU负载'
, top: 0
, left: 50
, textStyle: {
color: 'rgba(0,0,0,0.5)'
, fontSize: 14
text: 'CPU负载',
top: 0,
textStyle: {
color: 'rgba(0,0,0,0.5)',
fontSize: 14
}
},
color: [clr_sys, clr_user],
grid: {
show: true
, left: 30
@ -94,54 +84,151 @@ $app.load_overload_info = function () {
, bottom: 20
},
tooltip: {
trigger: 'axis'
, formatter: function (params) {
console.log(params);
//params = params[0];
var t = parseInt(params[0].name);
return tp_format_datetime(tp_utc2local(t), 'HH:mm:ss') + '<br/>' + params[0].value[1] + '%, ' + params[1].value[1] + '%';
}
, axisPointer: {
trigger: 'axis',
formatter: function (params) {
return params[0].name + '<br/>'+ params[1].seriesName + ': ' + params[1].value[1] + '%<br/>' + params[0].seriesName + ': ' + params[0].value[1] + '%';
},
axisPointer: {
animation: false
}
},
// legend: {
// // show: false
// },
xAxis: {
type: 'time'
, boundaryGap: false
, axisLine: {show: false}
legend: {
right: 20,
data: [
{name: '系统', icon: 'rect'},
{name: '用户', icon: 'rect'}
]
},
xAxis: {
type: 'time',
boundaryGap: false,
splitNumber: 10,
axisLine: {show: false}
},
// yAxis: {type: 'value', min: 'dataMin', axisLine: {show: false}, splitLine: {show: false}},
yAxis: {
type: 'value'
, axisLine: {
show: false
}
, min: 0
, max: 100
, boundaryGap: [0, '50%']
type: 'value',
axisLine: {show: false},
min: 0,
max: 100,
containLabel: true
},
series: [
{
name: 'cpu-sys'
, type: 'line'
, smooth: true
, symbol: 'none'
, stack: 'a'
, showSymbol: false
, data: $app.bar_cpu_sys
name: '系统',
type: 'line', smooth: true, symbol: 'none', stack: 'a', showSymbol: false,
lineStyle: {
normal: {
width: 1
}
},
areaStyle: {
normal: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
offset: 0,
color: clr_sys
}, {
offset: 1,
color: clr_sys_area
}])
}
},
data: $app.bar_cpu_sys
},
{
name: '用户', type: 'line', smooth: true, symbol: 'none', stack: 'a', showSymbol: false,
lineStyle: {
normal: {width: 1}
},
areaStyle: {
normal: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
offset: 0,
color: clr_user
}, {
offset: 1,
color: clr_user_area
}])
}
},
data: $app.bar_cpu_user
}
, {
name: 'cpu-user'
, type: 'line'
, smooth: true
, symbol: 'none'
, stack: 'a'
, showSymbol: false
, data: $app.bar_cpu_user
]
});
//=====================================
// Memory
//=====================================
$app.bar_mem_used = [];
for (i = 0; i < data.length; i++) {
$app.bar_mem_used.push({name: tp_format_datetime(tp_utc2local(data[i].t), 'HH:mm:ss'), value: [tp_utc2local(data[i].t)*1000, tp_digital_precision(data[i].m.u * 100 / data[i].m.t, 1)]});
}
var clr_mem = '#5671e2';
var clr_mem_area = '#8da4f9';
$app.bar_mem = echarts.init(document.getElementById('bar-mem'));
$app.bar_mem.setOption({
title: {
text: '内存用量',
top: 0,
textStyle: {
color: 'rgba(0,0,0,0.5)',
fontSize: 14
}
},
color: [clr_mem],
grid: {
show: true
, left: 30
, right: 20
, top: 30
, bottom: 20
},
tooltip: {
trigger: 'axis',
formatter: function (params) {
return params[0].name + ': '+ params[0].value[1] + '%';
},
axisPointer: {
animation: false
}
},
xAxis: {
type: 'time',
boundaryGap: false,
splitNumber: 10,
axisLine: {show: false}
},
yAxis: {
type: 'value',
axisLine: {show: false},
min: 0,
max: 100,
containLabel: true
},
series: [
{
//name: '系统',
type: 'line', smooth: true, symbol: 'none', stack: 'a', showSymbol: false,
lineStyle: {
normal: {
width: 1
}
},
areaStyle: {
normal: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
offset: 0,
color: clr_mem
}, {
offset: 1,
color: clr_mem_area
}])
}
},
data: $app.bar_mem_used
}
]
});
@ -155,8 +242,9 @@ $app.init_ws = function () {
$app.ws = new WebSocket('ws://' + location.host + '/ws/' + _sid);
$app.ws.onopen = function (e) {
$app.ws.send('{"method": "request", "param": "sys_status"}');
// 订阅:
$app.ws.send('{"method": "subscribe", "params": ["sys_real_status"]}');
// $app.ws.send('{"method": "subscribe", "params": ["sys_status"]}');
};
$app.ws.onclose = function (e) {
// console.log('[ws] ws-on-close', e);
@ -164,36 +252,28 @@ $app.init_ws = function () {
};
$app.ws.onmessage = function (e) {
var t = JSON.parse(e.data);
// console.log('[ws] ws-on-message', t);
// console.log(t);
if (t.subscribe === 'sys_real_status') {
if (t.method === 'request' && t.param === 'sys_status') {
$app.init_sys_status_info(t.data);
// 订阅系统状态信息
$app.ws.send('{"method": "subscribe", "params": ["sys_status"]}');
return;
}
if (t.method === 'subscribe' && t.param === 'sys_status') {
$app.bar_cpu_user.shift();
$app.bar_cpu_user.push({
'name': t.data.t.toString(),
'value': [tp_format_datetime(tp_utc2local(t.data.t)), t.data.c.u]
});
$app.bar_cpu_user.push({name: tp_format_datetime(tp_utc2local(t.data.t), 'HH:mm:ss'), value: [tp_utc2local(t.data.t) * 1000, t.data.c.u]});
$app.bar_cpu_sys.shift();
$app.bar_cpu_sys.push({
'name': t.data.t.toString(),
'value': [tp_format_datetime(tp_utc2local(t.data.t)), t.data.c.s]
});
//console.log($app.bar_cpu_data);
console.log('--', t.data.t);
$app.bar_cpu_sys.push({name: tp_format_datetime(tp_utc2local(t.data.t), 'HH:mm:ss'), value: [tp_utc2local(t.data.t) * 1000, t.data.c.s]});
$app.bar_cpu.setOption(
{
// xAxis: {data: 1},
series: [
{
name: 'cpu-user'
, data: $app.bar_cpu_user
}
, {
name: 'cpu-sys'
, data: $app.bar_cpu_sys
}
]
}
{series: [{data: $app.bar_cpu_sys}, {data: $app.bar_cpu_user}]}
);
$app.bar_mem_used.shift();
$app.bar_mem_used.push({name: tp_format_datetime(tp_utc2local(t.data.t), 'HH:mm:ss'), value: [tp_utc2local(t.data.t) * 1000, Math.round(t.data.m.u / t.data.m.t * 100, 2)]});
$app.bar_mem.setOption(
{series: [{data: $app.bar_mem_used}]}
);
}

View File

@ -54,13 +54,13 @@ function tp_check_ip(ip) {
// useful functions.
//===================================================
function digital_precision(num, keep) {
function tp_digital_precision(num, keep) {
return Math.round(num * Math.pow(10, keep)) / Math.pow(10, keep);
}
function prefixInteger(num, length) {
return (num / Math.pow(10, length)).toFixed(length).substr(2);
}
// function prefixInteger(num, length) {
// return (num / Math.pow(10, length)).toFixed(length).substr(2);
// }
function tp_size2str(size, precision) {
precision = precision || 0;
@ -71,23 +71,23 @@ function tp_size2str(size, precision) {
k = 'B';
}
else if (size < MB) {
s = digital_precision(size / KB, precision);
s = tp_digital_precision(size / KB, precision);
k = 'KB'
}
else if (size < GB) {
s = digital_precision(size / MB, precision);
s = tp_digital_precision(size / MB, precision);
k = 'MB'
}
else if (size < TB) {
s = digital_precision(size / GB, precision);
s = tp_digital_precision(size / GB, precision);
k = 'GB'
}
else if (size < PB) {
s = digital_precision(size / TB, precision);
s = tp_digital_precision(size / TB, precision);
k = 'TB'
}
else {
s = digital_precision(size / PB, precision);
s = tp_digital_precision(size / PB, precision);
k = 'PB'
}

View File

@ -70,15 +70,13 @@
<div class="row">
<div class="col-sm-6">
<div class="stats stats-bar">
## <div class="stats-name">CPU负载</div>
<div class="stats-value" id="bar-cpu" style="height:260px;">
<div class="stats-value" id="bar-cpu" style="height:180px;">
</div>
</div>
</div>
<div class="col-sm-6">
<div class="stats stats-bar">
<div class="stats-name">内存使用</div>
<div class="stats-value">
<div class="stats-value" id="bar-mem" style="height:180px;">
</div>
</div>
</div>

View File

@ -7,12 +7,15 @@ from app.base.logger import log
from app.base.configs import tp_cfg
class SessionManager(threading.Thread):
# class SessionManager(threading.Thread):
class SessionManager(object):
# SESSION_EXPIRE = 3600 # 60*60 默认超时时间为1小时
_expire = 3600
def __init__(self):
super().__init__(name='session-manager-thread')
# super().__init__(name='session-manager-thread')
super().__init__()
import builtins
if '__session_manager__' in builtins.__dict__:
@ -26,39 +29,48 @@ class SessionManager(threading.Thread):
self._stop_flag = False
self._timer_cond = threading.Condition()
self.update_default_expire()
def init(self):
self.update_default_expire()
return True
def update_default_expire(self):
self._expire = tp_cfg().sys.login.session_timeout * 60
def stop(self):
self._stop_flag = True
self._timer_cond.acquire()
self._timer_cond.notify()
self._timer_cond.release()
self.join()
log.v('{} stopped.\n'.format(self.name))
# def stop(self):
# self._stop_flag = True
# self._timer_cond.acquire()
# self._timer_cond.notify()
# self._timer_cond.release()
# self.join()
# log.v('{} stopped.\n'.format(self.name))
def run(self):
while True:
self._timer_cond.acquire()
# 每隔一分钟醒来检查一次超时的会话
self._timer_cond.wait(60)
self._timer_cond.release()
if self._stop_flag:
break
# def run(self):
# while True:
# self._timer_cond.acquire()
# # 每隔一分钟醒来检查一次超时的会话
# self._timer_cond.wait(60)
# self._timer_cond.release()
# if self._stop_flag:
# break
#
# _now = int(datetime.datetime.utcnow().timestamp())
# with self._lock:
# _keys = [k for k in self._session_dict]
# for k in _keys:
# if self._session_dict[k]['e'] == 0:
# continue
# if _now - self._session_dict[k]['t'] > self._session_dict[k]['e']:
# del self._session_dict[k]
_now = int(datetime.datetime.utcnow().timestamp())
with self._lock:
_keys = [k for k in self._session_dict]
for k in _keys:
if self._session_dict[k]['e'] == 0:
continue
if _now - self._session_dict[k]['t'] > self._session_dict[k]['e']:
del self._session_dict[k]
def check_expire(self):
_now = int(datetime.datetime.utcnow().timestamp())
with self._lock:
_keys = [k for k in self._session_dict]
for k in _keys:
if self._session_dict[k]['e'] == 0:
continue
if _now - self._session_dict[k]['t'] > self._session_dict[k]['e']:
del self._session_dict[k]
def set(self, s_id, value, expire=None):
"""

View File

@ -8,24 +8,22 @@ import json
from app.base.logger import log
from app.base.utils import tp_timestamp_utc_now
from app.base.configs import tp_cfg
from app.controller.ws import tp_wss
from app.base.cron import tp_corn
class TPSysStatus(threading.Thread):
class TPSysStatus(object):
_INTERVAL = 5 # seconds
def __init__(self):
super().__init__(name='sys-status-thread')
super().__init__()
import builtins
if '__tp_sys_status__' in builtins.__dict__:
raise RuntimeError('TPSysStatus object exists, you can not create more than one instance.')
# session表session_id为索引每个项为一个字典包括 v(value), t(last access), e(expire seconds)
self._session_dict = dict()
self._stop_flag = False
self._time_cnt = 0
self._interval = 2
# 实时数据我们在内存中保留最近10分钟的数据每5秒收集一次共 10*60/5 = 120 条记录
self._history = list()
self._disk_read = 0
self._disk_write = 0
@ -33,6 +31,19 @@ class TPSysStatus(threading.Thread):
self._net_sent = 0
def init(self):
t = tp_timestamp_utc_now() - 10 * 60
cnt = int((10 * 60 + self._INTERVAL - 1) / self._INTERVAL)
for i in range(cnt):
val = {
't': t,
'c': {'u': 0, 's': 0},
'm': {'u': 1, 't': 100},
'd': {'r': 0, 'w': 0},
'n': {'r': 0, 's': 0}
}
self._history.append(val)
t += self._INTERVAL
psutil.cpu_times_percent()
net = psutil.net_io_counters(pernic=False)
self._net_recv = net.bytes_recv
@ -41,70 +52,59 @@ class TPSysStatus(threading.Thread):
self._disk_read = disk.read_bytes
self._disk_write = disk.write_bytes
tp_corn().add_job('sys_status', self._check_status, first_interval_seconds=self._INTERVAL, interval_seconds=self._INTERVAL)
tp_wss().register_get_sys_status_callback(self.get_status)
return True
def stop(self):
self._stop_flag = True
self.join()
log.v('{} stopped.\n'.format(self.name))
def _check_status(self):
# time.sleep(self._interval)
val = {'t': tp_timestamp_utc_now()}
def run(self):
while not self._stop_flag:
# time.sleep(1)
# if self._stop_flag:
# break
# self._time_cnt += 1
# if self._time_cnt < 5:
# continue
#
# self._time_cnt = 0
cpu = psutil.cpu_times_percent()
# print(int(cpu.user * 100), int(cpu.system * 100))
val['c'] = {'u': cpu.user, 's': cpu.system}
#
mem = psutil.virtual_memory()
val['m'] = {'u': mem.used, 't': mem.total}
# print(mem.total, mem.used, int(mem.used * 100 / mem.total))
time.sleep(self._interval)
val = {'t': tp_timestamp_utc_now()}
disk = psutil.disk_io_counters(perdisk=False)
# val['d'] = {'r': disk.read_byes, 'w': disk.write_bytes}
# print(disk.read_bytes, disk.write_bytes)
_read = disk.read_bytes - self._disk_read
_write = disk.write_bytes - self._disk_write
self._disk_read = disk.read_bytes
self._disk_write = disk.write_bytes
cpu = psutil.cpu_times_percent()
# print(int(cpu.user * 100), int(cpu.system * 100))
val['c'] = {'u': cpu.user, 's': cpu.system}
#
mem = psutil.virtual_memory()
val['m'] = {'u': mem.used, 't': mem.total}
# print(mem.total, mem.used, int(mem.used * 100 / mem.total))
if _read < 0:
_read = 0
if _write < 0:
_write = 0
val['d'] = {'r': int(_read / self._INTERVAL), 'w': int(_write / self._INTERVAL)}
# print(int(_read / self._interval), int(_write / self._interval))
disk = psutil.disk_io_counters(perdisk=False)
# val['d'] = {'r': disk.read_byes, 'w': disk.write_bytes}
# print(disk.read_bytes, disk.write_bytes)
_read = disk.read_bytes - self._disk_read
_write = disk.write_bytes - self._disk_write
self._disk_read = disk.read_bytes
self._disk_write = disk.write_bytes
net = psutil.net_io_counters(pernic=False)
_recv = net.bytes_recv - self._net_recv
_sent = net.bytes_sent - self._net_sent
self._net_recv = net.bytes_recv
self._net_sent = net.bytes_sent
if _read < 0:
_read = 0
if _write < 0:
_write = 0
val['d'] = {'r': _read, 'w': _write}
# print(int(_read / self._interval), int(_write / self._interval))
# On some systems such as Linux, on a very busy or long-lived system, the numbers
# returned by the kernel may overflow and wrap (restart from zero)
if _recv < 0:
_recv = 0
if _sent < 0:
_sent = 0
val['n'] = {'r': int(_recv / self._INTERVAL), 's': int(_sent / self._INTERVAL)}
# print(int(_recv / self._interval), int(_sent / self._interval))
net = psutil.net_io_counters(pernic=False)
_recv = net.bytes_recv - self._net_recv
_sent = net.bytes_sent - self._net_sent
self._net_recv = net.bytes_recv
self._net_sent = net.bytes_sent
self._history.pop(0)
self._history.append(val)
# On some systems such as Linux, on a very busy or long-lived system, the numbers
# returned by the kernel may overflow and wrap (restart from zero)
if _recv < 0:
_recv = 0
if _sent < 0:
_sent = 0
val['n'] = {'r': _recv, 's': _sent}
# print(int(_recv / self._interval), int(_sent / self._interval))
tp_wss().send_message('sys_status', val)
# s = json.dumps(val, separators=(',', ':'))
tp_wss().send_message('sys_real_status', val)
# print(s)
def get_status(self):
return self._history
def tp_sys_status():

View File

@ -15,7 +15,7 @@ from app.base.configs import tp_cfg
from app.base.db import get_db
from app.base.logger import log
from app.base.session import tp_session
# from app.base.cron import tp_corn
from app.base.cron import tp_corn
from app.base.status import tp_sys_status
@ -84,6 +84,9 @@ class WebApp:
if not tp_session().init():
log.e('can not initialize session manager.\n')
return 0
if not tp_sys_status().init():
log.e('can not initialize system status collector.\n')
return 0
settings = {
#
@ -130,25 +133,30 @@ class WebApp:
log.e('can not listen on port {}:{}, make sure it not been used by another application.\n'.format(cfg.common.ip, cfg.common.port))
return 0
# 启动定时任务调度器
tp_corn().init()
tp_corn().start()
# 启动session超时管理
tp_session().start()
# tp_session().start()
def job():
log.v('---job--\n')
# def job():
# log.v('---job--\n')
# tp_corn().add_job('test', job, first_interval_seconds=None, interval_seconds=10)
# tp_corn().init()
# tp_corn().start()
tp_sys_status().init()
tp_sys_status().start()
# tp_sys_status().init()
# tp_sys_status().start()
tp_corn().add_job('session_expire', tp_session().check_expire, first_interval_seconds=None, interval_seconds=60)
# tp_corn().add_job('sys_status', tp_sys_status().check_status, first_interval_seconds=5, interval_seconds=5)
try:
tornado.ioloop.IOLoop.instance().start()
except:
log.e('\n')
# tp_corn().stop()
tp_sys_status().stop()
tp_session().stop()
tp_corn().stop()
# tp_sys_status().stop()
# tp_session().stop()
return 0

View File

@ -26,6 +26,11 @@ class TPWebSocketServer(object):
if '__tp_websocket_server__' in builtins.__dict__:
raise RuntimeError('TPWebSocketServer object exists, you can not create more than one instance.')
self._cb_get_sys_status = None
def register_get_sys_status_callback(self, cb):
self._cb_get_sys_status = cb
def have_callbacker(self, callbacker):
return callbacker in self._clients
@ -56,25 +61,24 @@ class TPWebSocketServer(object):
for p in req['params']:
if p not in self._clients[callbacker]['subscribe']:
self._clients[callbacker]['subscribe'].append(p)
elif req['method'] == 'request':
if req['param'] == 'sys_status':
if self._cb_get_sys_status is not None:
message = self._cb_get_sys_status()
msg = {'method': 'request', 'param': 'sys_status', 'data': message}
s = json.dumps(msg, separators=(',', ':'))
callbacker.write_message(s)
def send_message(self, subscribe, message):
msg = {'subscribe': subscribe, 'data': message}
s = json.dumps(msg, separators=(',', ':'))
s = None
with self._lock:
for c in self._clients:
if subscribe in self._clients[c]['subscribe']:
if s is None:
msg = {'method': 'subscribe', 'param': subscribe, 'data': message}
s = json.dumps(msg, separators=(',', ':'))
c.write_message(s)
# def response(self, _id, data):
# # print('send to client:', url, data)
# for callbacker in self.clients:
# if self.clients[callbacker].get_id() == _id:
# print('[ws] response', _id, data)
# callbacker.write_message(data)
# return
# print('## [ws] response no client.', _id)
def tp_wss():
"""
@ -118,4 +122,3 @@ class WebSocketHandler(tornado.websocket.WebSocketHandler):
self.write_message(json.dumps(ret))
return
tp_wss().on_message(self, message)