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

View File

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

View File

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

View File

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

View File

@ -8,24 +8,22 @@ import json
from app.base.logger import log from app.base.logger import log
from app.base.utils import tp_timestamp_utc_now 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.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): def __init__(self):
super().__init__(name='sys-status-thread') super().__init__()
import builtins import builtins
if '__tp_sys_status__' in builtins.__dict__: if '__tp_sys_status__' in builtins.__dict__:
raise RuntimeError('TPSysStatus object exists, you can not create more than one instance.') raise RuntimeError('TPSysStatus object exists, you can not create more than one instance.')
# session表session_id为索引每个项为一个字典包括 v(value), t(last access), e(expire seconds) # 实时数据我们在内存中保留最近10分钟的数据每5秒收集一次共 10*60/5 = 120 条记录
self._session_dict = dict() self._history = list()
self._stop_flag = False
self._time_cnt = 0
self._interval = 2
self._disk_read = 0 self._disk_read = 0
self._disk_write = 0 self._disk_write = 0
@ -33,6 +31,19 @@ class TPSysStatus(threading.Thread):
self._net_sent = 0 self._net_sent = 0
def init(self): 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() psutil.cpu_times_percent()
net = psutil.net_io_counters(pernic=False) net = psutil.net_io_counters(pernic=False)
self._net_recv = net.bytes_recv self._net_recv = net.bytes_recv
@ -41,25 +52,12 @@ class TPSysStatus(threading.Thread):
self._disk_read = disk.read_bytes self._disk_read = disk.read_bytes
self._disk_write = disk.write_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 return True
def stop(self): def _check_status(self):
self._stop_flag = True # time.sleep(self._interval)
self.join()
log.v('{} stopped.\n'.format(self.name))
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
time.sleep(self._interval)
val = {'t': tp_timestamp_utc_now()} val = {'t': tp_timestamp_utc_now()}
cpu = psutil.cpu_times_percent() cpu = psutil.cpu_times_percent()
@ -82,7 +80,7 @@ class TPSysStatus(threading.Thread):
_read = 0 _read = 0
if _write < 0: if _write < 0:
_write = 0 _write = 0
val['d'] = {'r': _read, 'w': _write} val['d'] = {'r': int(_read / self._INTERVAL), 'w': int(_write / self._INTERVAL)}
# print(int(_read / self._interval), int(_write / self._interval)) # print(int(_read / self._interval), int(_write / self._interval))
net = psutil.net_io_counters(pernic=False) net = psutil.net_io_counters(pernic=False)
@ -97,14 +95,16 @@ class TPSysStatus(threading.Thread):
_recv = 0 _recv = 0
if _sent < 0: if _sent < 0:
_sent = 0 _sent = 0
val['n'] = {'r': _recv, 's': _sent} val['n'] = {'r': int(_recv / self._INTERVAL), 's': int(_sent / self._INTERVAL)}
# print(int(_recv / self._interval), int(_sent / self._interval)) # print(int(_recv / self._interval), int(_sent / self._interval))
# s = json.dumps(val, separators=(',', ':')) self._history.pop(0)
self._history.append(val)
tp_wss().send_message('sys_real_status', val) tp_wss().send_message('sys_status', val)
# print(s) def get_status(self):
return self._history
def tp_sys_status(): 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.db import get_db
from app.base.logger import log from app.base.logger import log
from app.base.session import tp_session 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 from app.base.status import tp_sys_status
@ -84,6 +84,9 @@ class WebApp:
if not tp_session().init(): if not tp_session().init():
log.e('can not initialize session manager.\n') log.e('can not initialize session manager.\n')
return 0 return 0
if not tp_sys_status().init():
log.e('can not initialize system status collector.\n')
return 0
settings = { 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)) 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 return 0
# 启动定时任务调度器
tp_corn().init()
tp_corn().start()
# 启动session超时管理 # 启动session超时管理
tp_session().start() # tp_session().start()
def job(): # def job():
log.v('---job--\n') # log.v('---job--\n')
# tp_corn().add_job('test', job, first_interval_seconds=None, interval_seconds=10) # tp_corn().add_job('test', job, first_interval_seconds=None, interval_seconds=10)
# tp_corn().init() # tp_sys_status().init()
# tp_corn().start() # 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: try:
tornado.ioloop.IOLoop.instance().start() tornado.ioloop.IOLoop.instance().start()
except: except:
log.e('\n') log.e('\n')
# tp_corn().stop() tp_corn().stop()
tp_sys_status().stop() # tp_sys_status().stop()
tp_session().stop() # tp_session().stop()
return 0 return 0

View File

@ -26,6 +26,11 @@ class TPWebSocketServer(object):
if '__tp_websocket_server__' in builtins.__dict__: if '__tp_websocket_server__' in builtins.__dict__:
raise RuntimeError('TPWebSocketServer object exists, you can not create more than one instance.') 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): def have_callbacker(self, callbacker):
return callbacker in self._clients return callbacker in self._clients
@ -56,25 +61,24 @@ class TPWebSocketServer(object):
for p in req['params']: for p in req['params']:
if p not in self._clients[callbacker]['subscribe']: if p not in self._clients[callbacker]['subscribe']:
self._clients[callbacker]['subscribe'].append(p) 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): def send_message(self, subscribe, message):
msg = {'subscribe': subscribe, 'data': message} s = None
s = json.dumps(msg, separators=(',', ':'))
with self._lock: with self._lock:
for c in self._clients: for c in self._clients:
if subscribe in self._clients[c]['subscribe']: 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) 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(): def tp_wss():
""" """
@ -118,4 +122,3 @@ class WebSocketHandler(tornado.websocket.WebSocketHandler):
self.write_message(json.dumps(ret)) self.write_message(json.dumps(ret))
return return
tp_wss().on_message(self, message) tp_wss().on_message(self, message)