← 文档库
📄 文档learn/2026-09-13-网络流量监控/README.md
🌱 创建于2026-09-13 23:31
✏️ 修改于2026-09-13 23:31
👁️ 浏览量—
🔗 短链接/~learn/s/19694d0f
🔐 可见性

网络流量监控(系统状态监控 · 流量板块)

> 目标:回答「今天这个网站一共跑了多少流量?都是谁跑的?」——尤其在用手机热点时, > 能看清各模块(数据抓取 / 游戏交易 / 隧道)每天各自消耗多少。

---

1. 一期方案:进程级总账

macOS 没有现成的「按进程/按模块按天」流量账本,因此一期采用采样 + 差分的方式自建总账:

每 60s:
  nettop -P -x -l 1 -J bytes_in,bytes_out   # 每个进程的「累计」收发字节
  netstat -ib                               # 每张网卡的「累计」收发字节
        │
        ▼
  与上一次采样做差分 → 得到这 60s 的增量
        │
        ▼
  PID → 模块 归因(PID 文件 + 进程树父链)
        │
        ▼
  写入 traffic_history(SQLite),按 module 聚合

1.1 为什么是「差分」

nettop 只给进程启动至今的累计值,不给区间增量。所以必须自己记住上一次的累计值, 两次相减才是这一分钟的真实流量:

1.2 归属到「模块」

流量是挂在 PID 上的,而用户关心的是模块(抓取 / 游戏交易 / 隧道)。归因两条链路:

文件名映射到模块 key:

PID 文件 模块 key 展示名
--------------------------
.run_fetch.pid fetch 🔄 ETF数据拉取
.run_stock_fetchd.pid stock_fetchd 📊 股票选股
.run_pushd.pid pushd 📨 推送调度
.run_market_observe.pid market_observe 📈 行情观察
.web_server.pid web_daemon 🌐 Web服务+游戏交易
.web_tunnel.pid tunnel 🔒 Cloudflare隧道
.tushare_probe.pid tushare_probe 🔍 Tushare探测

Playwright 的浏览器进程)本身没有 PID 文件。通过 ps -axo pid,ppid 构建父子关系, 沿父链向上找到最近的「已知 PID」,把子进程流量归到同一模块(带环检测,防死循环)。

两者都匹配不上的 → 归入 other(🖥️ 系统/其他进程),属正常兜底。

1.3 落库与清理

---

2. 接口 /api/system/traffic 返回结构

{
  "ok": true,
  "traffic": {
    "collected_at": 1789313411.7,
    "interval_s": 86.5,          // 距上次采样间隔(正常≈60,重启后偏大)
    "iface": "en0",
    "realtime": { "in_bps": 745408.2, "out_bps": 61559.9 },
    "today": {
      "total_bytes": 339540573,
      "modules": [ { "module": "web_daemon", "display_name": "🌐 Web服务+游戏交易",
                     "in_bytes": 59142024, "out_bytes": 1256580, "total_bytes": 60398604 }, ... ]
    },
    "daily": [ { "day": "2026-09-13", "total_bytes": 339540573 } ],
    "modules_window": [ ... ],   // 最近一个采样窗口内各模块增量
    "top_processes": [ { "pid": 66645, "module": "web_daemon", ... } ],
    "iface_total": { "total_bytes": 229301208376 },  // 网卡自开机累计
    "level": "normal"
  }
}

失败时返回 {"ok": false, "error": "..."} 或 500,前端会显示原因,不静默。

---

3. 前端渲染

/system 页面新增卡片 #sysTraffic(unified_server.py 内联 HTML/JS):

按日趋势(有历史后出现)、最近窗口 Top 进程。

> ⚠️ 内联模板经 Python .format() 渲染,JS/CSS 里的花括号必须写成 {{ }}。

---

4. 关键踩坑(防复发)

traffic_monitor 用 .web_server.pid 归因 web_daemon 模块,但 restart.sh 启动 unified_server.py 时没有写这个 PID 文件(只有 run_web_daemon.sh 写)。 结果:快速重启后 PID 文件是陈旧值,正在跑的服务器永远归不到 web_daemon, 全被算进 other。 → 修复:restart.sh 启动成功后 echo "$PID" > "$BASE_DIR/.web_server.pid"; 并加回归测试 TestPidFileContract 锁死「PID_FILE_MAP 指向 .web_server.pid」+ 「restart.sh 必须写 .web_server.pid」两条契约。

不能按空格简单切分,需 line.rsplit(None, 2),再用正则 \.(\d+)$ 取末尾 PID。

en0 有 MAC 地址列,lo0 没有 → 必须定位 <Link#> 列后再按是否有 MAC 条件跳过, 否则字段全部左移。

lo0 上的本机回环流量不代表真实外网消耗,统计时过滤,只认物理网卡(en0 等)。

_scheduler_loop 内采样失败只 print 日志、不阻塞主循环;但写库路径不得 except: pass。

---

5. 验证方式

# 单测(含解析/归因/差分/落库/接线/PID 契约)
.venv/bin/python -m unittest tests.adata_test.utils.test_traffic_monitor -v

# 接口
curl -s http://127.0.0.1:8765/api/system/traffic | head -c 1200

# 前端 DOM 实测(接口 200 ≠ 渲染正确)
# 在 /system 页面控制台执行,断言 card===1、bodyChildren>0、subtitle 含「今日」、badge 非 "--"

---

6. 一期限制

web_daemon,无法拆出「游戏交易占多少」。

7. 二期方向(应用层精确埋点)

要回答「19:03 全量抓取 40 页 = 12.4MB」,需在应用层直接累计响应体大小:

汇总写入同一张 traffic_history,module 细化到 web:game_trade / fetch:etf_full, 从而把一期合并的 web_daemon 拆开。

▶ 📜 变更历史(1 次提交)

8dbcb912026-09-13 23:31docs(traffic): 流量监控专题文档 + 模块索引更新
🍃quant 知识库
by 伊甸园和枫

🔊 播放设置

勾选音源后调节配比,总和自动归一化
已选 0 个 · 合计:0%
合成中...
00:00/00:00
1.0x

分享图片

生成中...