python实时显示数据_Tornado实现监控数据实时展示

前言:

It has been a while since I last updated my blogs.

使用Tornado开发一个实时监控信息系统,其中包括 CUP、内存、网卡、磁盘使用率。

涉及技术

编程语言:Python

关系型数据库:MySQL

Web框架:Tornado

数据库连接驱动:mysql-connector-python

数据库ORM:sqlalchemy

服务端websocket通信:sockjs-tornado

客户端websocket通信:sockejs-client

前端:HTML、CSS、JS

图表可视化:pyecharts==0.5.11 #新版本和旧版本差异较大

获取硬件信息工具:psutil模块

pip install -i https://pypi.douban.com/simple --trusted-host pypi.douban.com -r requirements.txt

-i:指定国内安装源 --trusted-host:指定信任主机 -r指定依赖文件

Tornado项目目录结构设计

Monitor

-------->app01

------>models

------>static

------->template

------->tools

------->views

manage.py

1122865-20190902165954964-1011891442.png

ContractedBlock.gif

ExpandedBlockStart.gif

from sqlalchemy.ext.declarative import declarative_base #模型继承的父类

from sqlalchemy.dialects.mssql import BIGINT,DECIMAL,DATE,TIME,DATETIME#导入数据库字段

from sqlalchemy import Column#用于创建字段的类

Base=declarative_base()#调用

metadata=Base.metadata#创建元类

classMem(Base):__tablename__='mem'id=Column(BIGINT,primary_key=True)

precent=Column(DECIMAL(6,2))#小数类型 保留6位数字 2位小数

total=Column(DECIMAL(8,2))#总量

used=Column(DECIMAL(8,2))#使用率

free=Column(DECIMAL(8,2))#剩余率

create_date=Column(DATE)#创建的日期

create_time=Column(TIME)#c创建的时间

create_dt=Column(DATETIME)#创建的日期+时间

classSwap(Base):__tablename__='swap'id= Column(BIGINT,primary_key=True)

precent= Column(DECIMAL(6,2)) #小数类型 保留6位数字 2位小数

total = Column(DECIMAL(8,2)) #总量

used = Column(DECIMAL(8,2)) #使用率

free = Column(DECIMAL(8,2)) #剩余率

create_date = Column(DATE) #创建的日期

create_time = Column(TIME) #c创建的时间

create_dt = Column(DATETIME) #创建的日期+时间

classCPU(Base):__tablename__ = 'cpu'id= Column(BIGINT, primary_key=True)

precent= Column(DECIMAL(6, 2)) #小数类型 保留6位数字 2位小数

create_date = Column(DATE) #创建的日期

create_time = Column(TIME) #c创建的时间

create_dt = Column(DATETIME) #创建的日期+时间

if __name__ == '__main__':from sqlalchemy importcreate_engine

mysql_configs={"db_host":'192.168.56.128',"db_name":"web","db_port":3306,"db_user":"web","db_pwd":"123.com"}

link="mysql+mysqlconnector://{db_user}:{db_pwd}@{db_host}:{db_port}/{db_name}".format(**mysql_configs)

engine=create_engine(link,encoding="utf-8",echo=True)

metadata.create_all(engine)#调用元类

models.py

一、前端(SockJS)与Tornado建立web socket连接

前端使用:sockejs-client插件

ContractedBlock.gif

ExpandedBlockStart.gif

//1.定义长连接

var conn=null;//2.定义连接函数

function connect() {

disconnect();//把之前的连接关闭掉,在创建新的连接//0.定义协议

var transports= ["websocket"];//1.创建连接对象

conn= new SockJS("http://" + window.location.host + "/real/time",transports);//2.建立连接

conn.onopen=function () {

console.log('连接成功')

};//3.建立发送消息

conn.onmessage=function (e) {

console.log(e.data);

};//4.建立关闭连接

conn.onclose=function (e) {

console.log("断开连接");

};

setInterval(function () {

conn.send("system")

},1000)

}

function disconnect() {if (conn !=null) {

conn.close();

conn=null;

}

}if (conn ==null) {

connect();

}else{

disconnect();

}

monitor.js

Tornado使用:sockjs

ContractedBlock.gif

ExpandedBlockStart.gif

from sockjs.tornado import SockJSConnection#专门生成web_socket服务

classRealTimeHandler(SockJSConnection):

waiters=set()#定义1个客户端连接池,所有客户端共用1个集合

#1.建立连接(不存在重复的连接)

defon_open(self, request):try:

self.waiters.add(self)exceptException as e:print(e)#2.发送消息

def on_message(self, message):#接收客户端消息

try:

self.broadcast(self.waiters,message)#把消息广播给所有连接的客户端

exceptException as e:print(e)#3.关闭连接

defon_close(self):try:

self.waiters.remove(self)exceptException as e:print(e)

views_real_time.py

二、前端数据实时更新

1.CPU信息实时更新(水球图)

Tornado调用pyecharts生成前端代码(html+js+css);

ContractedBlock.gif

ExpandedBlockStart.gif

importdatetimefrom pyecharts import Liquid, Gauge, Pie, Line #水球图、

classChart(object):def liquid_html(self,chart_id,title,val):#水球图

liquid =Liquid(

title="{}-{}".format(self.dt, title),

title_pos="center",

width="100%",

title_color="white",

title_text_size=14,

height=300)

liquid.chart_id=chart_id#指定 chart_id

liquid.add("",[round(val/100,4)])#添加数据

return liquid.render_embed()#返回html图表代码

@propertydefdt(self):return datetime.datetime.now().strftime('%Y%m%dT%H:%M:%S')

chart.py

Tornado把pyecharts生成的前端代码(html+js+css),响应给浏览器渲染;

ContractedBlock.gif

ExpandedBlockStart.gif

varmyChart_cpu_avg=echarts.init(document.getElementById('cpu_avg'),'light', {renderer:'canvas'});varoption_cpu_avg={"title": [

{"text":"20190910T15:13:19-cpu\u5e73\u5747\u4f7f\u7528\u7387","left":"center","top":"auto","textStyle": {"color":"white","fontSize":14},"subtextStyle": {"fontSize":12}

}

],"toolbox": {"show":true,"orient":"vertical","left":"95%","top":"center","feature": {"saveAsImage": {"show":true,"title":"save as image"},"restore": {"show":true,"title":"restore"},"dataView": {"show":true,"title":"data view"}

}

},"series_id":8147274,"tooltip": {"trigger":"item","triggerOn":"mousemove|click","axisPointer": {"type":"line"},"textStyle": {"fontSize":14},"backgroundColor":"rgba(50,50,50,0.7)","borderColor":"#333","borderWidth":0},"series": [

{"type":"liquidFill","data": [0.167],"waveAnimation":true,"animationDuration":2000,"animationDurationUpdate":1000,"color": ["#294D99","#156ACF","#1598ED","#45BDFF"],"shape":"circle","outline": {"show":true}

}

],"legend": [

{"data": [],"selectedMode":"multiple","show":true,"left":"center","top":"top","orient":"horizontal","textStyle": {"fontSize":12}

}

],"animation":true,"color": ["#c23531","#2f4554","#61a0a8","#d48265","#749f83","#ca8622","#bda29a","#6e7074","#546570","#c4ccd3","#f05b72","#ef5b9c","#f47920","#905a3d","#fab27b","#2a5caa","#444693","#726930","#b2d235","#6d8346","#ac6767","#1d953f","#6950a1","#918597","#f6f5ec"]

};

myChart_cpu_avg.setOption(option_cpu_avg);

tornado调用pyechart生成的前端代码

浏览器发送web socket请求给Tornado server端

Tornado不断响应浏览器pyecharts生成前端代码(html+js+css)

ContractedBlock.gif

ExpandedBlockStart.gif

importjsonfrom sockjs.tornado import SockJSConnection#专门生成web_socket服务

from app01.tools.monitor_tools importMonitorclassRealTimeHandler(SockJSConnection):

waiters=set()#定义1个客户端连接池,所有客户端共用1个集合

#1.建立连接(不存在重复的连接)

defon_open(self, request):try:

self.waiters.add(self)exceptException as e:print(e)#2.发送消息

def on_message(self, message):#接收客户端消息,根据客户端发送过来的消息返回一些数据!

try:if message =="system":

m=Monitor()

data={"mem":m.mem(),"swap":m.swap(),"cpu":m.cpu(),"disk":m.disk(),"net":m.net(),'dt':m.dt}

self.broadcast(self.waiters,json.dumps(data,ensure_ascii=False))#把消息广播给所有连接的客户端

exceptException as e:print(e)#3.关闭连接

defon_close(self):try:

self.waiters.remove(self)exceptException as e:print(e)

views_real_time.py

浏览器通过JS代码不断修改pyechart生成的前端代码

ContractedBlock.gif

ExpandedBlockStart.gif

//进度条变化

functionprogress_status(val) {var data = "";if (val >= 0 && val < 25) {

data= " bg-success";

}else if (val >= 25 && val < 50) {

data= "";

}else if (val >= 50 && val < 75) {

data= " bg-warning";

}else if (val >= 75 && val <= 100) {

data= " bg-success";

}returndata

}functionupdate_ui(e) {var data=JSON.parse(e.data);//因为pyechart 声明了变量option_cpu_avg所有我们只需要修改现有的变量option_cpu_avg即可

option_cpu_avg.series[0].data[0]=(data['cpu']['percent_avg']/100).toFixed(4); //保留4位小数

option_cpu_avg.title[0].text=data['dt']+'CPU平均使用率';//保证对option_cpu_avg的修改生效

myChart_cpu_avg.setOption(option_cpu_avg);//-------------------------------------------------------

var cpu_per = "";for (var k in data['cpu']['percent_per']) {var num =parseInt(k);

cpu_per+= "

CPU" + num + "

cpu_per+= "

" + data['cpu']['percent_per'][k] + "%
";

}

document.getElementById("tb_cpu_per").innerHTML =cpu_per;

}//1.定义长连接

var conn = null;//2.定义连接函数

functionconnect() {

disconnect();//把之前的连接关闭掉,在创建新的连接

//0.定义协议

var transports = ["websocket"];//1.创建连接对象

conn = new SockJS("http://" + window.location.host + "/real/time",transports);//2.建立连接

conn.onopen = function() {

console.log('连接成功')

};//3.建立接收消息

conn.onmessage = function(e) {//console.log(e.data);

update_ui(e)

};//4.建立关闭连接

conn.onclose = function(e) {

console.log("断开连接");

};

setInterval(function() {//2.1 建立连接发送消息

conn.send("system")

},1000)

}functiondisconnect() {if (conn != null) {

conn.close();

conn= null;

}

}if (conn == null) {

connect();

}else{

disconnect();

}

monitor.js

2.内存+交互分区信息实时展示(仪表图)

ContractedBlock.gif

ExpandedBlockStart.gif

importtornado.webfrom app01.views.views_commen importCommnHardlerfrom app01.tools.monitor_tools importmonitor_objfrom app01.tools.chart importChart#定义1个首页的视图

classIndexHandler(CommnHardler):def get(self,*args,**kwargs):

cpu_info=monitor_obj.cpu()

cpu_percent_avg_info=cpu_info ['percent_avg']#CPU平均使用率

cpu_percent_per=cpu_info ['percent_per'] #每个CPU使用率

mem_info=monitor_obj.mem()

swap_info=monitor_obj.swap()print(swap_info)

c=Chart()

liquid_html=c.liquid_html(chart_id='cpu_avg',title="cpu平均使用率",val=cpu_percent_avg_info)

self.render("index.html",**{"liquid_html":liquid_html,"cpu_percent_per_info":cpu_percent_per,"mem_html":c.guage_html("mem","内存使用率",mem_info['percent']),"swap_html":c.guage_html('swap',"交互分区使用率",swap_info['percent']),"mem_info":mem_info,"swap_info": swap_info

})

view_index.py

ContractedBlock.gif

ExpandedBlockStart.gif

//进度条变化

functionprogress_status(val) {var data = "";if (val >= 0 && val < 25) {

data= " bg-success";

}else if (val >= 25 && val < 50) {

data= "";

}else if (val >= 50 && val < 75) {

data= " bg-warning";

}else if (val >= 75 && val <= 100) {

data= " bg-success";

}returndata

}functionupdate_ui(e) {var data=JSON.parse(e.data);//因为pyechart 声明了变量option_cpu_avg所有我们只需要修改现有的变量option_cpu_avg即可

option_cpu_avg.series[0].data[0]=(data['cpu']['percent_avg']/100).toFixed(4); //保留4位小数

option_cpu_avg.title[0].text=data['dt']+'CPU平均使用率';//保证对option_cpu_avg的修改生效

myChart_cpu_avg.setOption(option_cpu_avg);//-------------------------------------------------------

var cpu_per = "";for (var k in data['cpu']['percent_per']) {var num =parseInt(k);

cpu_per+= "

CPU" + num + "

cpu_per+= "

" + data['cpu']['percent_per'][k] + "%
";

}

document.getElementById("tb_cpu_per").innerHTML =cpu_per;/*内存实时更新*/option_mem.series[0].data[0].value = data['mem']['percent'];

option_mem.title[0].text = data["dt"] + "-内存使用率";

myChart_mem.setOption(option_mem);

document.getElementById("mem_percent").innerText = data['mem']['percent'];

document.getElementById("mem_total").innerText = data['mem']['total'];

document.getElementById("mem_used").innerText = data['mem']['used'];

document.getElementById("mem_free").innerText = data['mem']['free'];/*交换分区实时更新*/option_swap.series[0].data[0].value = data['swap']['percent'];

option_swap.title[0].text = data["dt"] + "-交换分区使用率";

myChart_swap.setOption(option_swap);

document.getElementById("swap_percent").innerText = data['swap']['percent'];

document.getElementById("swap_total").innerText = data['swap']['total'];

document.getElementById("swap_used").innerText = data['swap']['used'];

document.getElementById("swap_free").innerText = data['swap']['free'];

}//1.定义长连接

var conn = null;//2.定义连接函数

functionconnect() {

disconnect();//把之前的连接关闭掉,在创建新的连接

//0.定义协议

var transports = ["websocket"];//1.创建连接对象

conn = new SockJS("http://" + window.location.host + "/real/time",transports);//2.建立连接

conn.onopen = function() {

console.log('连接成功')

};//3.建立接收消息

conn.onmessage = function(e) {//console.log(e.data);

update_ui(e)

};//4.建立关闭连接

conn.onclose = function(e) {

console.log("断开连接");

};

setInterval(function() {//2.1 建立连接发送消息

conn.send("system")

},1000)

}functiondisconnect() {if (conn != null) {

conn.close();

conn= null;

}

}if (conn == null) {

connect();

}else{

disconnect();

}

monitor.js

ContractedBlock.gif

ExpandedBlockStart.gif

{% extends "layout.html" %}{% block head %}{% end %}

{% block content %}

CPU信息

所有CPU使用率

{% for k,v in enumerate(cpu_percent_per_info) %}

CPU{{ k }}

{{ v }}%

{% end %}

{% raw liquid_html%}

内存/交互分区信息

{% raw mem_html %}

使用率(%)

{{ mem_info['percent'] }}

总量(GB)

{{ mem_info['total'] }}

使用量(GB)

{{ mem_info['used'] }}

剩余量(GB)

{{ mem_info['free'] }}

{% raw swap_html %}

使用率(%)

{{swap_info['percent']}}

总量(GB)

{{swap_info['total']}}

使用量(GB)

{{swap_info[ 'used']}}

剩余量(GB)

{{swap_info['free']}}

{% end %}

index.html

--------------支持web socket的协议

ContractedBlock.gif

ExpandedBlockStart.gif

importjsonfrom sockjs.tornado import SockJSConnection#专门生成web_socket服务

from app01.tools.monitor_tools importMonitorclassRealTimeHandler(SockJSConnection):

waiters=set()#定义1个客户端连接池,所有客户端共用1个集合

#1.建立连接(不存在重复的连接)

defon_open(self, request):try:

self.waiters.add(self)exceptException as e:print(e)#2.发送消息

def on_message(self, message):#接收客户端消息,根据客户端发送过来的消息返回一些数据!

try:if message =="system":

m=Monitor()

data={"mem":m.mem(),"swap":m.swap(),"cpu":m.cpu(),"disk":m.disk(),"net":m.net(),'dt':m.dt}

self.broadcast(self.waiters,json.dumps(data,ensure_ascii=False))#把消息广播给所有连接的客户端

exceptException as e:print(e)#3.关闭连接

defon_close(self):try:

self.waiters.remove(self)exceptException as e:print(e)

view_real_time.py

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值