构建实时网络速度监控面板:Python Flask + SSE 技术详解

构建实时网络速度监控面板:Python Flask + SSE 技术详解

本文将带你一步步构建一个实时网络速度监控面板,使用 Python Flask 后端和 Server-Sent Events (SSE) 前端技术。

项目概述

这个网络监控工具可以实时显示每个网络接口的下载和上传速度,类似于 Web 版本的 iftopnload 命令。

功能特点

  • 实时监控所有活跃网络接口
  • 自动过滤回环接口和无效接口
  • 按下载速度排序显示
  • 同时显示字节每秒和比特每秒
  • 简洁美观的 Web 界面

技术栈

  • 后端: Python + Flask + psutil
  • 前端: HTML + CSS + JavaScript (SSE)
  • 通信: Server-Sent Events (SSE)

完整代码实现

后端代码 (app.py)

python 复制代码
import time
import json
import psutil
from flask import Flask, Response, render_template_string

app = Flask(__name__)

def collect(interval: float = 1.0):
    """
    收集网络接口统计信息
    interval: 采样间隔时间(秒)
    """
    # 获取第一次网络IO计数
    prev = psutil.net_io_counters(pernic=True)
    time.sleep(interval)
    # 获取第二次网络IO计数
    curr = psutil.net_io_counters(pernic=True)
    stats = psutil.net_if_stats()

    def is_active(name: str) -> bool:
        """检查网络接口是否活跃且不是回环接口"""
        st = stats.get(name)
        if not st or not st.isup:
            return False
        # 过滤回环接口
        if name.lower() in ("lo", "loopback", "loopback pseudo-interface 1"):
            return False
        return True

    summary = {"down_bps": 0.0, "up_bps": 0.0, "ifaces": []}
    names = set(prev.keys()) & set(curr.keys())

    for name in sorted(names):
        if not is_active(name):
            continue
        
        # 计算速度(字节/秒)
        p = prev[name]
        c = curr[name]
        down = max(c.bytes_recv - p.bytes_recv, 0) / interval
        up = max(c.bytes_sent - p.bytes_sent, 0) / interval
        
        summary["down_bps"] += down
        summary["up_bps"] += up
        summary["ifaces"].append({"name": name, "down_bps": down, "up_bps": up})

    # 按下载速度降序排序
    summary["ifaces"].sort(key=lambda x: x["down_bps"], reverse=True)
    return summary

def format_rate(bytes_per_sec: float) -> str:
    """格式化速度显示"""
    bits_per_sec = bytes_per_sec * 8
    
    def human(n, unit):
        for s in ["", "K", "M", "G", "T"]:
            if n < 1024:
                return f"{n:,.1f} {s}{unit}"
            n /= 1024
        return f"{n:,.1f} P{unit}"
    
    return f"{human(bytes_per_sec, 'B/s')} ({human(bits_per_sec, 'bps')})"

@app.route("/metrics")
def metrics():
    """SSE 流端点,每秒推送一次网络数据"""
    def stream():
        while True:
            data = collect(1.0)
            yield f"data: {json.dumps(data)}\n\n"
    
    return Response(stream(), mimetype="text/event-stream")

# HTML 模板
INDEX_HTML = """
<!doctype html>
<meta charset="utf-8" />
<title>Net Speed Panel</title>
<style>
  body { font-family: system-ui, sans-serif; max-width: 860px; margin: 24px auto; }
  header { display: flex; align-items: baseline; justify-content: space-between; }
  .total { font-size: 18px; margin: 12px 0; }
  table { width: 100%; border-collapse: collapse; }
  th, td { padding: 8px; border-bottom: 1px solid #eee; text-align: left; }
  th { background: #fafafa; }
  .down { color: #0b6; }
  .up { color: #06b; }
  .muted { color: #888; }
</style>
<header>
  <h1>网络速度监控面板</h1>
  <span class="muted">每秒刷新</span>
</header>
<div class="total" id="total">总计 加载中...</div>
<table>
  <thead>
    <tr><th>网卡</th><th>下行</th><th>上行</th></tr>
  </thead>
  <tbody id="tbody"></tbody>
</table>
<script>
  function human(n, unit) {
    const units = ['', 'K', 'M', 'G', 'T'];
    let i = 0;
    while (n >= 1024 && i < units.length - 1) { n /= 1024; i++; }
    return `${n.toFixed(1)} ${units[i]}${unit}`;
  }
  
  function formatRate(bytesPerSec) {
    const bps = bytesPerSec * 8;
    return `${human(bytesPerSec, 'B/s')} (${human(bps, 'bps')})`;
  }
  
  const total = document.getElementById('total');
  const tbody = document.getElementById('tbody');
  const es = new EventSource('/metrics');
  
  es.onmessage = (e) => {
    const data = JSON.parse(e.data);
    total.textContent = `总计  ↓ ${formatRate(data.down_bps)}    ↑ ${formatRate(data.up_bps)}`;
    tbody.innerHTML = '';
    
    data.ifaces.forEach(({name, down_bps, up_bps}) => {
      const tr = document.createElement('tr');
      tr.innerHTML = `<td>${name}</td><td class="down">↓ ${formatRate(down_bps)}</td><td class="up">↑ ${formatRate(up_bps)}</td>`;
      tbody.appendChild(tr);
    });
  };
</script>
"""

@app.route("/")
def index():
    return render_template_string(INDEX_HTML)

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=5000, debug=False)

核心原理详解

1. 网络速度计算原理

python 复制代码
def collect(interval: float = 1.0):
    prev = psutil.net_io_counters(pernic=True)  # 第一次采样
    time.sleep(interval)                         # 等待指定间隔
    curr = psutil.net_io_counters(pernic=True)  # 第二次采样
    
    # 计算速度 = (第二次计数 - 第一次计数) / 时间间隔
    down = (c.bytes_recv - p.bytes_recv) / interval
    up = (c.bytes_sent - p.bytes_sent) / interval

这种方法通过两次采样之间的差值来计算瞬时速度,比直接读取瞬时值更准确。

2. Server-Sent Events (SSE) 技术

SSE 允许服务器主动向客户端推送数据,非常适合实时监控场景:

python 复制代码
@app.route("/metrics")
def metrics():
    def stream():
        while True:
            data = collect(1.0)  # 每秒收集一次数据
            yield f"data: {json.dumps(data)}\n\n"  # SSE 格式
    
    return Response(stream(), mimetype="text/event-stream")

前端通过 EventSource API 接收数据:

javascript 复制代码
const es = new EventSource('/metrics');
es.onmessage = (e) => {
    const data = JSON.parse(e.data);
    // 更新界面
};

3. 速度单位换算

python 复制代码
def format_rate(bytes_per_sec: float) -> str:
    bits_per_sec = bytes_per_sec * 8  # 转换为比特
    
    def human(n, unit):
        # 自动选择合适的单位 (B, KB, MB, GB, TB)
        for s in ["", "K", "M", "G", "T"]:
            if n < 1024:
                return f"{n:,.1f} {s}{unit}"
            n /= 1024
        return f"{n:,.1f} P{unit}"
    
    return f"{human(bytes_per_sec, 'B/s')} ({human(bits_per_sec, 'bps')})"

部署和运行

安装依赖

bash 复制代码
pip install flask psutil

运行应用

bash 复制代码
python app.py

访问 http://localhost:5000 即可看到监控面板。

总结

通过这个项目,我们学习了:

  • 使用 psutil 库获取系统网络信息
  • SSE 技术的原理和实现方式
  • 实时数据计算的差分方法
  • 前后端分离的 Web 应用架构

这个网络监控工具不仅实用,也是学习实时 Web 应用开发的优秀示例。你可以基于这个基础继续添加更多功能,打造属于自己的系统监控工具。

希望这个教程对你有所帮助!

相关推荐
chinesegf2 小时前
conda虚拟环境直接复制依赖包可能会报错
python·conda
开心-开心急了2 小时前
PySide6 打印(QPrinter)文本编辑器(QPlaintextEdit)内容
python·ui·pyqt
坐吃山猪3 小时前
Python-UV多环境管理
人工智能·python·uv
带娃的IT创业者3 小时前
从零开始掌握 uv:新一代超快 Python 项目与包管理器(含 Windows 支持)
windows·python·uv
浔川python社3 小时前
浔川 AI 翻译 v7.0正式上线公告
python
Wiktok3 小时前
【WIT】ttkbootstrap全组件中文本地化解决方案
python·ttkbootstrap
熊猫钓鱼>_>3 小时前
PySpark全面解析:大数据处理的Python利器
开发语言·python
王嘉俊9253 小时前
Django 入门:快速构建 Python Web 应用的强大框架
前端·后端·python·django·web·开发·入门
lisw053 小时前
大模型的第一性原理考量:基于物理本质与数学基础的范式重构
网络·人工智能·机器学习