Linux 分布式监控系统 是一个专业级、模块化的系统监控解决方案,采用现代 C++ 开发,集成了 实时数据采集、高性能 RPC 通信和 Qt 图形界面。系统通过 gRPC + Protocol Buffers 实现分布式架构,能够实时监控 CPU、内存、网络、软中断等关键系统指标,并基于 Qt 表格模型 提供专业的数据可视化展示。
- 系统管理员: 实时监控多台服务器状态,快速定位性能瓶颈
- 开发运维: 分析应用程序的系统资源占用,优化性能
- 性能工程师: 深入理解 Linux 内核各项指标含义和计算方法
- C++学习者: 学习现代 C++ 项目架构、设计模式和构建系统
- 教育研究: 理解操作系统监控原理和分布式系统设计
Linux 分布式监控系统架构
├── 应用层 (Application Layer)
│ ├── Qt 图形界面 (Display Monitor)
│ │ ├── CPU 负载/状态监控
│ │ ├── 内存详细统计
│ │ ├── 网络流量监控
│ │ └── 软中断分析
│ └── 用户交互接口
│
├── 服务层 (Service Layer)
│ ├── gRPC 服务器 (RPC Server)
│ │ ├── 数据接收与缓存
│ │ ├── 连接管理
│ │ └── 协议转换
│ ├── gRPC 客户端 (RPC Client)
│ │ ├── 连接池管理
│ │ ├── 异步通信
│ │ └── 错误重试
│ └── 数据协议层 (Protocol Buffers)
│ ├── CPU 负载消息
│ ├── 内存统计消息
│ ├── 网络流量消息
│ └── 监控聚合消息
│
├── 数据采集层 (Data Collection Layer)
│ ├── 监控器框架 (Monitor Framework)
│ │ ├── 策略模式基类
│ │ ├── 差分计算引擎
│ │ └── 定时采集调度
│ ├── 具体监控器实现
│ │ ├── CPU 负载监控器 (读取 /proc/loadavg)
│ │ ├── CPU 状态监控器 (读取 /proc/stat, 差分计算)
│ │ ├── 内存监控器 (读取 /proc/meminfo, 19个指标)
│ │ ├── 网络监控器 (读取 /proc/net/dev, 速率计算)
│ │ └── 软中断监控器 (读取 /proc/softirqs, 10类中断)
│ └── 工具支持
│ ├── 文件读取工具 (RAII 封装)
│ ├── 时间计算工具
│ └── 单位转换工具
│
├── 操作系统接口层 (OS Interface Layer)
│ ├── Linux /proc 文件系统
│ │ ├── loadavg (系统负载)
│ │ ├── stat (CPU 时间片)
│ │ ├── meminfo (内存统计)
│ │ ├── net/dev (网络接口)
│ │ └── softirqs (软中断)
│ └── 系统调用封装
│
└── 部署与运维层 (Deployment Layer)
├── Docker 容器化支持
├── CMake 跨平台构建
└── Shell 自动化脚本
功能: 提供专业的监控数据可视化界面
- 多页面导航: 使用
QStackedLayout实现 CPU、内存、网络、软中断四个监控页面 - 实时刷新: 每 2 秒自动从服务器获取最新数据并更新显示
- 表格展示: 基于
QAbstractTableModel的自定义表格模型,支持不同颜色和字体样式 - 数据转换: 将 Protobuf 格式的监控数据转换为 Qt 可显示的格式
功能: 采集系统各项性能指标
- 策略模式: 统一的监控器接口,支持多种监控指标
- 差分计算: 对 CPU、网络、软中断等累计值进行速率计算
- 定时采集: 每 3 秒采集一次数据,避免系统过载
- RAII 管理: 自动管理文件句柄等系统资源
功能: 定义数据交换格式和 RPC 接口
- Protobuf 定义: 二进制序列化,高效传输监控数据
- gRPC 服务: 定义
SetMonitorInfo和GetMonitorInfo两个 RPC 方法 - 结构化消息: CPU、内存、网络等消息的详细字段定义
功能: 实现监控数据的远程传输
- 客户端封装: 简化的 gRPC 调用接口,支持错误处理
- 服务器实现: 内存缓存最新的监控数据,供界面查询
- 非安全连接: 适合内网环境,低开销通信
功能: 提供一致的开发和运行环境
- CMake 构建: 模块化配置,支持接口库依赖传递
- Docker 容器: 预配置的开发环境,避免依赖问题
- 自动化脚本: 一键构建、运行和测试
- 数据源:
/proc/loadavg - 指标: 1分钟、3分钟、15分钟平均负载
- 显示: 单行三列的简单表格
- 计算公式: 直接读取,无需计算
- 数据源:
/proc/stat - 指标: 8个状态的使用率百分比(用户态、系统态、空闲、I/O等待等)
- 算法: 差分计算,基于两次采样的时间片差值
- 显示: 每个 CPU 核心一行,共 4 列
// 差分计算核心算法
float total_diff = new_total - old_total; // 总时间片变化
float busy_diff = new_busy - old_busy; // 繁忙时间片变化
float cpu_percent = busy_diff / total_diff * 100.0; // 使用率百分比- 数据源:
/proc/meminfo - 指标: 19 个详细内存指标
- 基础指标: 总量、空闲、可用
- 缓存指标: 缓冲区、页面缓存
- 状态指标: 活跃、非活跃、脏页
- 特殊类型: 匿名页、映射页、Slab 分配
- 单位转换: 从 KB 转换为 GB (1000进制)
- 使用率计算:
(总内存 - 可用内存) / 总内存 × 100%
- 数据源:
/proc/net/dev - 指标: 每个网络接口的 4 个速率
- 发送/接收速率 (KB/s)
- 发送/接收包速率 (packets/s)
- 算法: 差分计算,基于累计字节数和时间间隔
- 显示: 每个网络接口一行,共 5 列
- 数据源:
/proc/softirqs - 指标: 10 类软中断在每个 CPU 核心上的速率
- HI、TIMER、NET_TX、NET_RX、BLOCK 等
- 算法: 差分计算,统计每秒中断次数
- 显示: 每个 CPU 核心一行,共 11 列,支持排序
| 指标类别 | 数据源 | 计算方式 | 显示格式 | 更新频率 |
|---|---|---|---|---|
| 负载平均 | /proc/loadavg |
直接读取 | 3个浮点数 | 3秒 |
| CPU 使用率 | /proc/stat |
差分计算 | 8个百分比 | 3秒 |
| 软中断 | /proc/softirqs |
差分计算 | 10×核心数 | 3秒 |
| 指标分组 | 包含指标 | 单位 | 意义 |
|---|---|---|---|
| 基础信息 | Total, Free, Available | GB | 内存总量和可用性 |
| 缓存信息 | Buffers, Cached | GB | 系统缓存使用情况 |
| 活跃状态 | Active, Inactive | GB | 内存活跃程度 |
| 页面类型 | AnonPages, Mapped | GB | 内存分配类型 |
| Slab 分配 | SReclaimable, SUnreclaim | GB | 内核对象缓存 |
| 指标 | 计算方式 | 单位 | 说明 |
|---|---|---|---|
| 发送速率 | (新字节-旧字节)/时间间隔 | KB/s | 网络出口流量 |
| 接收速率 | (新字节-旧字节)/时间间隔 | KB/s | 网络入口流量 |
| 发送包速率 | (新包数-旧包数)/时间间隔 | packets/s | 包处理频率 |
| 接收包速率 | (新包数-旧包数)/时间间隔 | packets/s | 包接收频率 |
- 内存: 监控客户端 ~15MB,显示界面 ~50MB
- CPU: 监控采集 <2%,界面渲染 <5%
- 网络: 数据传输 ~0.4KB/s (压缩后)
- 数据采集: 3 秒间隔,10ms 内完成
- 数据传输: gRPC 同步调用,<1ms 延迟
- 界面刷新: 2 秒间隔,无感知延迟
- 时间基准: 使用
std::chrono::steady_clock,不受系统时间调整影响 - 差分计算: 避免累计值溢出,处理长时间运行
- 边界检查: 防止除零错误和负时间间隔
- C++编译器: GCC 11+
- 构建系统: CMake 3.15+
- Qt 库: Qt Core 和 Widgets 模块
- Protobuf/gRPC: libprotobuf-dev, libgrpc++-dev
- Docker: 容器化部署
# 1. 启动开发容器
./docker/scripts/monitor_docker_run.sh
# 2. 构建项目
./docker/scripts/build_in_docker.sh
# 3. 进入容器
./docker/scripts/monitor_docker_into.sh
# 4. 在容器内运行,开3个窗口
# 第1个窗口
./docker/scripts/monitor_docker_into.sh
cd /work/build && ./bin/server # 启动 RPC 服务器
# 第2个窗口
./docker/scripts/monitor_docker_into.sh
cd /work/build && ./linux_monitor/src/monitor # 启动监控客户端
# 第3个窗口
./docker/scripts/monitor_docker_into.sh
cd /work/build && ./display_monitor/display # 启动显示界面- 适用: 个人开发机、小型服务器
- 部署: 所有组件在同一机器运行
- 优势: 简单直接,无需网络配置
- 适用: 多台服务器集群
- 部署:
- 每台服务器运行监控客户端
- 集中式 RPC 服务器收集数据
- 管理节点运行显示界面
- 优势: 集中管理,统一视图
- 适用: C++ 应用性能分析
- 使用: 监控应用程序的系统资源占用
- 优势: 量化分析,定位性能瓶颈
- CPU 页面: 显示负载和状态监控
- 软中断页面: 显示各 CPU 核心的中断统计,支持排序
- 内存页面: 显示 19 个详细内存指标
- 网络页面: 显示各网络接口的流量统计
// 修改监控客户端采集间隔
// 在 linux_monitor/src/main.cpp 中
std::this_thread::sleep_for(std::chrono::seconds(3)); // 改为 1、5、10 等
// 修改显示界面刷新间隔
// 在 display_monitor/main.cpp 中
std::this_thread::sleep_for(std::chrono::seconds(2)); // 改为 1、3、5 等// 1. 在 proto/ 中添加 Protobuf 定义
// disk_usage.proto
message DiskUsage {
string device = 1;
float used_percent = 2;
int64 read_rate = 3; // KB/s
int64 write_rate = 4; // KB/s
}
// 2. 实现监控器类
class DiskMonitor : public MonitorInter {
void UpdateOnce(proto::MonitorInfo* info) override {
// 读取 /proc/diskstats 或使用 statfs
}
};
// 3. 添加显示模型
class DiskModel : public MonitorInterModel {
// 实现 Qt 表格模型
};
// 4. 更新界面
void MonitorWidget::InitDiskMonitorWidget() {
// 添加新的页面
}基于典型系统运行的实际数据:
| 组件 | CPU 占用 | 内存占用 | 网络流量 | 启动时间 |
|---|---|---|---|---|
| 监控客户端 | 1-3% | 15-20MB | 0.4KB/s | <100ms |
| RPC 服务器 | <1% | 10-15MB | 双向 0.4KB/s | <50ms |
| 显示界面 | 3-8% | 45-60MB | 0.4KB/s | 500-800ms |
监控数据量分析 (单次采集):
- CPU 负载: 12 字节 (3个 float)
- CPU 状态: 256 字节 (8核×8个 float)
- 软中断: 640 字节 (8核×10个 int64)
- 内存: 152 字节 (19个 int64)
- 网络: 80 字节 (4接口×5个 float)
- 总计: ~1.1KB 原始数据