前言

It has been while since last updated my blogs.

使用Tornado开发1个实时监控信息系统,其中包括 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

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#创建元类

class Mem(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)#创建的日期+时间

class Swap(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)  # 创建的日期+时间



class CPU(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 import create_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 

一、WebSocket协议

WebSocket 协议是1种全双工通信协议,基于TCP 连接,旨在通过单1的长连接在客户端和服务器之间实现实时、低延迟的数据传输。

WebSocket协议最初由HTML5标准定义,用于解决传统 HTTP 请求无法高效处理实时通信的问题。

1.特点

  1. 全双工通信(Full-Duplex)
    客户端和服务器可以同时发送和接收消息,无需等待对方完成操作。

  2. 单一持久连接
    WebSocket 使用一个长期保持的 TCP 连接,避免了 HTTP 协议中每次通信都需要重新建立连接的开销。

  3. 实时性
    由于不需要每次发送消息都创建新的连接,WebSocket 非常适合实时性要求较高的场景,如聊天应用、在线游戏、股票行情等。

  4. 轻量化协议头
    相比 HTTP 的冗长头部,WebSocket 的头部较小,数据传输效率更高。

2.工作流程

2.1.握手阶段

WebSocket 使用 HTTP 协议完成初始握手:

  • 客户端发起 HTTP请求,包含Upgrade: websocketConnection: Upgrade等头信息,表明希望升级到WebSocket协议。
  • 服务器如果支持WebSocket,则返回状态码101 Switching Protocols,升级成功后切换到WebSocket协议。

握手示例

2.1.1.客户端发送HTTP请求

客户端在握手请求中包含1个随机的Sec-WebSocket-Key,Sec-WebSocket-Ket是由客户端生成的Base64编码的随机值。

GET /chat HTTP/1.1
Host: example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13

2.1.2.服务端响应HTTP请求

服务器接收到客户的Sec-WebSocket-Key后,将其与服务端的MagicString拼接

dGhlIHNhbXBsZSBub25jZQ==258EAFA5-E914-47DA-95CA-C5AB0DC85B11

对该拼接字符串进行SHA-1哈希计算,然后将结果进行Base64 编码

得到的编码值就是 Sec-WebSocket-Accept

s3pPLMBiTxaQ9kYGzzhZRbK+xOo=

服务器将生成的Sec-WebSocket-Accept返回给客户端:

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=

客户端验证Sec-WebSocket-Accept是否正确?

客户端使用它发送的Sec-WebSocket-Key和MagicString,按照相同的方式计算出Sec-WebSocket-Accept。

如果计算结果与服务器返回的匹配,握手成功,进入全双工通信。

2.1.3.Magic String

MagicString是WebSocket协议的客户端和服务端,双方约定的固定字符串,固定值为258EAFA5-E914-47DA-95CA-C5AB0DC85B11;

  • 防伪造:MagicString确保客户端和服务器的握手具有唯一性,即便有人拦截了握手请求,也无法伪造有效的响应。
  • 协议验证:服务器根据MagicString生成响应值,客户端验证后确保连接双方都遵循WebSocket协议。
  • 安全性:MagicString的固定值和随机的Sec-WebSocket-Key 结合,形成了类似于 “挑战-响应” 的认证机制,防止简单重放攻击

2.2.连接关闭

双方可以通过发送Close帧来关闭连接,TCP 连接随之释放。

3.WebSocket 的数据帧格式

WebSocket通过帧(Frame)传输数据,帧格式如下:

  • 固定部分(2字节):
    • FIN(1 bit):表示消息是否结束。
    • Opcode(4 bits):定义帧类型(如文本、二进制、关闭等)。
    • Mask(1 bit):客户端发送的帧必须是掩码加密的。
    • Payload Length(7 bits/16 bits/64 bits):表示数据长度。
  • 可选部分:
    • Masking Key(4 bytes):用于解码消息内容(仅客户端帧有)。
  • 数据部分:
    • Payload Data:实际传输的数据。

4.WebSocket的应用场景

    1. 实时聊天
      比如即时通讯(如微信、Slack)、客服系统等,要求消息快速交互。

    2. 在线游戏
      游戏客户端与服务器之间需要实时更新状态。

    3. 股票行情
      实时更新股票价格和交易信息。

    4. 协同编辑
      多用户实时协作编辑文档或绘图。

    5. 物联网(IoT)
      设备与服务器之间需要低延迟数据传输。

二、使用WebSocket协议

前端使用Sockjs-client插件与Tornado的建立WebSocket连接;

1.前端使用:sockejs-client插件

//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

2.Tornado使用:sockjs

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

class RealTimeHandler(SockJSConnection):
    waiters=set()#定义1个客户端连接池,所有客户端共用1个集合
    #1.建立连接(不存在重复的连接)
    def on_open(self, request):
        try:
            self.waiters.add(self)
        except Exception as e:
            print(e)
   #2.发送消息
    def on_message(self, message):#接收客户端消息
        try:
            self.broadcast(self.waiters,message)#把消息广播给所有连接的客户端
        except Exception as e:
            print(e)
    #3.关闭连接
    def on_close(self):
        try:
            self.waiters.remove(self)
        except Exception as e:
            print(e)
views_real_time.py

 

三、前端数据实时更新

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

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

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


class Chart(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图表代码

    @property
    def dt(self):
        return datetime.datetime.now().strftime('%Y%m%dT%H:%M:%S')
chart.py

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

<div id="cpu_avg" style="width:100%;height:300px;"></div>
        <script type="text/javascript">

var myChart_cpu_avg = echarts.init(document.getElementById('cpu_avg'), 'light', {renderer: 'canvas'});

var option_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);

</script>
        </div>
tornado调用pyechart生成的前端代码

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

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

import json
from sockjs.tornado import SockJSConnection#专门生成web_socket服务
from app01.tools.monitor_tools import Monitor
class RealTimeHandler(SockJSConnection):
    waiters=set()#定义1个客户端连接池,所有客户端共用1个集合
    #1.建立连接(不存在重复的连接)
    def on_open(self, request):
        try:
            self.waiters.add(self)
        except Exception 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))#把消息广播给所有连接的客户端
        except Exception as e:
            print(e)
    #3.关闭连接
    def on_close(self):
        try:
            self.waiters.remove(self)
        except Exception as e:
            print(e)
views_real_time.py

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

//进度条变化
function progress_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";
    }
    return data
}

function update_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 += "<tr><td class='text-primary' style='width: 30%'>CPU" + num + "</td>";
        cpu_per += "<td><div class='progress'><div class='progress-bar progress-bar-striped progress-bar-animated" + progress_status(data['cpu']['percent_per'][k]) + "' role='progressbar' aria-valuenow='" + data['cpu']['percent_per'][k] + "' aria-valuemin='0' aria-valuemax='100' style='width: " + data['cpu']['percent_per'][k] + "%'>" + data['cpu']['percent_per'][k] + "%</div></div></td></tr>";
    }
    document.getElementById("tb_cpu_per").innerHTML = cpu_per;

    }

//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);
        update_ui(e)
    };
    //4.建立关闭连接
    conn.onclose = function (e) {
        console.log("断开连接");
    };
    setInterval(function () {
        //2.1 建立连接发送消息
        conn.send("system")
    },1000)
}

function disconnect() {
    if (conn != null) {
        conn.close();
        conn = null;
    }

}


if (conn == null) {
    connect();

} else {
    disconnect();
}
monitor.js

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

import tornado.web
from app01.views.views_commen import CommnHardler
from app01.tools.monitor_tools import monitor_obj
from app01.tools.chart import Chart
#定义1个首页的视图

class IndexHandler(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
//进度条变化
function progress_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";
    }
    return data
}

function update_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 += "<tr><td class='text-primary' style='width: 30%'>CPU" + num + "</td>";
        cpu_per += "<td><div class='progress'><div class='progress-bar progress-bar-striped progress-bar-animated" + progress_status(data['cpu']['percent_per'][k]) + "' role='progressbar' aria-valuenow='" + data['cpu']['percent_per'][k] + "' aria-valuemin='0' aria-valuemax='100' style='width: " + data['cpu']['percent_per'][k] + "%'>" + data['cpu']['percent_per'][k] + "%</div></div></td></tr>";
    }
    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.定义连接函数

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);
        update_ui(e)
    };
    //4.建立关闭连接
    conn.onclose = function (e) {
        console.log("断开连接");
    };
    setInterval(function () {
        //2.1 建立连接发送消息
        conn.send("system")
    },1000)
}

function disconnect() {
    if (conn != null) {
        conn.close();
        conn = null;
    }

}


if (conn == null) {
    connect();

} else {
    disconnect();
}
monitor.js
{% extends "layout.html" %}<!--继承布局文件-->
{% block head %}
<script src="{{ static_url('echarts-liquidfill/echarts-liquidfill.min.js') }}"></script>
{% end %}
{% block content %}
<div class="row">
    <div class="col-md-12">
        <div class="card text-white bg-dark mb-3">
            <div class="card-header">CPU信息</div>
            <div class="card-body">
                <div class="row">
                    <div class="col-md-6 pad-left">
                        <table class="table table-responsive-sm table-bordered">
                            <thead>
                            <th colspan="2">所有CPU使用率</th>
                            </thead>
                            <tbody id="tb_cpu_per">
                            {% for k,v in enumerate(cpu_percent_per_info) %}
                            <tr>
                                <td class="text-primary" style="width: 30%">
                                    CPU{{ k }}
                                </td>
                                <td>
                                    <div class="progress"> <!---进度条!-->
                                        <div class="progress-bar progress-bar-striped progress-bar-animated {{ handler.progress_status(v)}}"
                                             role="progressbar" aria-valuenow="{{ v }}" aria-valuemin="0"
                                             aria-valuemax="100" style="width:{{ v }}%">{{ v }}%
                                        </div>
                                    </div>
                                </td>
                            </tr>
                            {% end %}
                            </tbody>
                        </table>
                    </div>

                    <div class="col-md-6 pad-right">
                        <div class="border border-white">{% raw liquid_html%}</div>
                    </div>
                </div>
            </div>
        </div>
    </div>
    <div class="col-md-12">
        <div class="card text-white bg-dark mb-3">
            <div class="card-header">内存/交互分区信息</div>
            <div class="card-body">
                <div class="row">
                    <div class="col-md-6 pad-left">
                        <div class="border border-white">{% raw mem_html %}</div>
                        <table class="table table-sm table-bordered">
                            <tr>
                                <td class="text-primary" style="width: 30%">使用率(%)</td>
                                 <td id="mem_percent" class="text-danger">{{ mem_info['percent'] }}</td>
                            </tr>
                            <tr>
                                <td class="text-primary" style="width: 30%">总量(GB)</td>
                                <td id="mem_total" class="text-danger">{{ mem_info['total'] }}</td>
                            </tr>

                            <tr>
                                <td class="text-primary" style="width: 30%">使用量(GB)</td>
                                 <td id="mem_used" class="text-danger">{{ mem_info['used'] }}</td>
                            <tr>
                            <tr>
                                <td class="text-primary" style="width: 30%">剩余量(GB)</td>
                                 <td id="mem_free" class="text-danger">{{ mem_info['free'] }}</td>
                            </tr>
                        </table>
                    </div>
                    <div class="col-md-6 pad-right">
                        {% raw swap_html %}
                        <table class="table table-sm table-bordered">
                            <tr>
                                <td class="text-primary" style="width: 30%">使用率(%)</td>
                                <td id="swap_percent" class="text-danger">{{swap_info['percent']}}</td>
                            </tr>
                            <tr>
                                <td class="text-primary" style="width: 30%">总量(GB)</td>
                                <td id="swap_total" class="text-danger" >{{swap_info['total']}}</td>
                            </tr>

                            <tr>
                                <td class="text-primary" style="width: 30%">使用量(GB)</td>
                                <td id="swap_used" class="text-danger">{{swap_info[ 'used']}}</td>
                            <tr>
                            <tr>
                                <td class="text-primary" style="width: 30%">剩余量(GB)</td>
                                <td id="swap_free" class="text-danger">{{swap_info['free']}}</td>
                            </tr>
                        </table>
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>
{% end %}
index.html

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

import json
from sockjs.tornado import SockJSConnection#专门生成web_socket服务
from app01.tools.monitor_tools import Monitor
class RealTimeHandler(SockJSConnection):
    waiters=set()#定义1个客户端连接池,所有客户端共用1个集合
    #1.建立连接(不存在重复的连接)
    def on_open(self, request):
        try:
            self.waiters.add(self)
        except Exception 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))#把消息广播给所有连接的客户端
        except Exception as e:
            print(e)
    #3.关闭连接
    def on_close(self):
        try:
            self.waiters.remove(self)
        except Exception as e:
            print(e)
view_real_time.py

 

 

 

 

 

 

web socket协议

 参考

pyecharts

 

258EAFA5-E914-47DA-95CA-C5AB0DC85B11
posted on 2019-08-27 11:00  Martin8866  阅读(3219)  评论(1编辑  收藏  举报