目录
- [ServWatch:基于 Vue3 + Node.js 的实时可视化监控系统实现](#ServWatch:基于 Vue3 + Node.js 的实时可视化监控系统实现)
-
- 前言
- 一、系统架构
-
- [1.1 整体架构图](#1.1 整体架构图)
- [1.2 技术栈选型](#1.2 技术栈选型)
- 二、核心功能实现
-
- [2.1 实时监控仪表板](#2.1 实时监控仪表板)
- [2.2 监控数据可视化](#2.2 监控数据可视化)
- [2.3 告警规则引擎](#2.3 告警规则引擎)
- [2.4 指标采集 Agent](#2.4 指标采集 Agent)
- [三、API 设计](#三、API 设计)
-
- [3.1 RESTful API](#3.1 RESTful API)
- [3.2 认证机制](#3.2 认证机制)
- 四、快速部署
-
- [4.1 模拟模式(最快体验)](#4.1 模拟模式(最快体验))
- [4.2 Docker Compose 部署](#4.2 Docker Compose 部署)
- [4.3 本地开发部署](#4.3 本地开发部署)
- 五、项目结构
- 六、界面预览
-
- [6.1 登录页面](#6.1 登录页面)
- [6.2 GPU 监控](#6.2 GPU 监控)
- [6.3 监控目标管理](#6.3 监控目标管理)
- 七、后续规划
- 八、总结
ServWatch:基于 Vue3 + Node.js 的实时可视化监控系统实现
前言
在日常运维和开发工作中,服务器监控是必不可少的环节。市面上有不少优秀的监控方案(如 Prometheus、Grafana、Zabbix 等),但对于中小型团队或个人开发者来说,这些工具往往过于复杂,学习成本较高。
本文将介绍我自己开发的 ServWatch 监控系统------一个轻量级、易部署、界面美观的实时监控解决方案。
一、系统架构
1.1 整体架构图
┌─────────────────────────────────────────────────────────────────┐
│ ServWatch 系统架构 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ │ │ │ │ │ │
│ │ Browser │◄────►│ Frontend │◄────►│ Backend │ │
│ │ │ WS │ Vue 3 + │ HTTP │ Node.js │ │
│ │ │ │ ECharts │ │ Express │ │
│ └──────────────┘ └──────────────┘ └──────┬───────┘ │
│ │ │
│ ┌───────▼───────┐ │
│ ┌──────────────┐ ┌──────────────┐ │ Postgres │ │
│ │ │ │ │ │ TimescaleDB │ │
│ │ Agent │─────►│ Redis │─────►│ │ │
│ │ 采集器 │ HTTP │ 缓存 │ │ 时序数据 │ │
│ │ │ │ │ │ │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
1.2 技术栈选型
| 层级 | 技术选型 | 理由 |
|---|---|---|
| 前端框架 | Vue 3 | Composition API 开发体验好 |
| 构建工具 | Vite | 开发速度快,HMR 体验优秀 |
| 状态管理 | Pinia | Vue 3 官方推荐,API 简洁 |
| UI 组件 | Element Plus | 组件丰富,文档完善 |
| 图表库 | Apache ECharts | 图表功能强大,交互性好 |
| 后端框架 | Express | 轻量灵活,中间件丰富 |
| 实时通信 | Socket.IO | WebSocket 封装,兼容性好 |
| 数据库 | PostgreSQL + TimescaleDB | 关系型 + 时序数据扩展 |
| 缓存 | Redis | 高性能 KV 存储 |
二、核心功能实现
2.1 实时监控仪表板

仪表板是监控系统的核心界面,展示所有监控目标的整体状态。
WebSocket 实时推送实现:
javascript
// 前端:建立 WebSocket 连接
import { io } from 'socket.io-client'
const socket = io('http://localhost:3001', {
auth: { token: localStorage.getItem('token') }
})
// 注册为仪表板客户端
socket.emit('dashboard:connect')
// 订阅实时指标
socket.emit('metrics:subscribe', { targetIds: ['1', '2', '3'] })
// 接收实时更新
socket.on('metrics:update', (data) => {
console.log('实时指标:', data)
// 更新图表数据
})
javascript
// 后端:WebSocket 服务
class WebSocketService {
constructor(io) {
this.io = io
this.dashboardClients = new Set()
this.setupEventHandlers()
}
setupEventHandlers() {
this.io.on('connection', (socket) => {
socket.on('dashboard:connect', () => {
this.dashboardClients.add(socket.id)
socket.emit('dashboard:connected', { clientId: socket.id })
})
socket.on('metrics:subscribe', ({ targetIds }) => {
socket.join(`metrics:${targetIds.join(',')}`)
})
socket.on('disconnect', () => {
this.dashboardClients.delete(socket.id)
})
})
}
// 推送实时指标到所有仪表板客户端
broadcastMetrics(targetId, metrics) {
this.io.emit('metrics:update', {
targetId,
metrics,
timestamp: new Date().toISOString()
})
}
// 推送告警通知
broadcastAlert(alert, value) {
this.io.emit('alert:triggered', {
alert,
value,
timestamp: new Date().toISOString()
})
}
}
2.2 监控数据可视化

使用 ECharts 实现实时折线图:
vue
<template>
<div ref="chartRef" style="width: 100%; height: 300px"></div>
</template>
<script setup>
import { ref, onMounted, watch } from 'vue'
import * as echarts from 'echarts'
const chartRef = ref(null)
let chart = null
const initChart = () => {
chart = echarts.init(chartRef.value)
chart.setOption({
title: { text: 'CPU 使用率' },
tooltip: { trigger: 'axis' },
xAxis: {
type: 'category',
data: []
},
yAxis: {
type: 'value',
max: 100,
axisLabel: { formatter: '{value}%' }
},
series: [{
name: 'CPU',
type: 'line',
smooth: true,
data: [],
areaStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: 'rgba(64, 158, 255, 0.5)' },
{ offset: 1, color: 'rgba(64, 158, 255, 0.1)' }
])
}
}]
})
}
// 实时更新图表数据
const updateChart = (timestamp, value) => {
const option = chart.getOption()
option.xAxis[0].data.push(timestamp)
option.series[0].data.push(value)
// 保持最近 60 个数据点
if (option.xAxis[0].data.length > 60) {
option.xAxis[0].data.shift()
option.series[0].data.shift()
}
chart.setOption(option)
}
onMounted(() => {
initChart()
// 监听 WebSocket 数据更新
socket.on('metrics:update', ({ metrics }) => {
updateChart(new Date().toLocaleTimeString(), metrics.cpu)
})
})
</script>
2.3 告警规则引擎

告警评估服务实现:
javascript
class AlertService {
constructor() {
this.alertRules = new Map()
this.alertHistory = []
this.cooldowns = new Map()
}
// 添加告警规则
addRule(rule) {
this.alertRules.set(rule.id, rule)
}
// 评估指标是否触发告警
async evaluate(targetId, metricType, value) {
const rules = Array.from(this.alertRules.values())
.filter(r => r.targetId === targetId && r.metricType === metricType && r.enabled)
for (const rule of rules) {
const shouldAlert = this.checkCondition(value, rule)
if (shouldAlert && !this.isInCooldown(rule.id)) {
await this.triggerAlert(rule, value)
this.setCooldown(rule.id, rule.cooldown || 300)
}
}
}
// 检查条件
checkCondition(value, rule) {
switch (rule.condition) {
case 'greater_than':
return value > rule.threshold
case 'less_than':
return value < rule.threshold
case 'equals':
return value === rule.threshold
default:
return false
}
}
// 触发告警
async triggerAlert(rule, value) {
const alertHistory = {
id: generateId(),
alertId: rule.id,
alertName: rule.name,
severity: rule.severity,
status: 'active',
message: `${rule.name}触发: 当前值 ${value}, 阈值 ${rule.threshold}`,
value,
threshold: rule.threshold,
createdAt: new Date().toISOString()
}
this.alertHistory.push(alertHistory)
rule.triggerCount++
// 通知 WebSocket 客户端
websocketService.broadcastAlert(rule, value)
// TODO: 发送邮件/钉钉通知
}
isInCooldown(ruleId) {
const cooldownEnd = this.cooldowns.get(ruleId)
return cooldownEnd && Date.now() < cooldownEnd
}
setCooldown(ruleId, seconds) {
this.cooldowns.set(ruleId, Date.now() + seconds * 1000)
}
}
2.4 指标采集 Agent
Agent 负责在被监控服务器上采集系统指标:
javascript
const os = require('os')
const axios = require('axios')
class SystemCollector {
constructor(config) {
this.interval = config.collectInterval || 1000
}
// 采集 CPU 指标
collectCPU() {
const cpus = os.cpus()
const totalIdle = cpus.reduce((acc, cpu) => acc + cpu.times.idle, 0)
const totalTick = cpus.reduce((acc, cpu) => {
return acc + Object.values(cpu.times).reduce((a, b) => a + b, 0)
}, 0)
return {
usage: ((1 - totalIdle / totalTick) * 100).toFixed(2),
cores: cpus.map(cpu => ({
usage: ((1 - cpu.times.idle / Object.values(cpu.times).reduce((a, b) => a + b, 0)) * 100).toFixed(2)
}))
}
}
// 采集内存指标
collectMemory() {
const total = os.totalmem()
const free = os.freemem()
const used = total - free
return {
total: (total / 1024 / 1024 / 1024).toFixed(2), // GB
used: (used / 1024 / 1024 / 1024).toFixed(2),
free: (free / 1024 / 1024 / 1024).toFixed(2),
usage: ((used / total) * 100).toFixed(2)
}
}
// 采集网络指标
async collectNetwork() {
const stats = await getNetworkStats()
return {
rx: stats.rx, // bytes/s
tx: stats.tx
}
}
// 采集所有指标
async collectAll() {
return {
cpu: this.collectCPU(),
memory: this.collectMemory(),
network: await this.collectNetwork(),
disk: await this.collectDisk(),
timestamp: new Date().toISOString()
}
}
}
// 定时采集并发送
const collector = new SystemCollector(config)
const transmitter = new HttpTransmitter(config.serverUrl)
setInterval(async () => {
const metrics = await collector.collectAll()
await transmitter.send(metrics)
}, collector.interval)
三、API 设计
3.1 RESTful API
| 分类 | 端点 | 方法 | 说明 |
|---|---|---|---|
| 认证 | /auth/login |
POST | 用户登录 |
| 认证 | /auth/register |
POST | 用户注册 |
| 认证 | /auth/refresh |
POST | 刷新 Token |
| 目标 | /targets |
GET | 获取所有监控目标 |
| 目标 | /targets |
POST | 创建监控目标 |
| 目标 | /targets/:id |
PUT | 更新监控目标 |
| 告警 | /alerts |
GET | 获取告警规则 |
| 告警 | /alerts |
POST | 创建告警规则 |
| 指标 | /metrics/realtime |
GET | 获取实时指标 |
| 指标 | /metrics/aggregated |
GET | 获取聚合指标 |
| 统计 | /stats/overview |
GET | 系统概览统计 |
3.2 认证机制
使用 JWT Bearer Token 认证:
javascript
// 登录获取 Token
POST /auth/login
{
"identifier": "admin",
"password": "admin123"
}
// 响应
{
"user": { "id": "1", "username": "admin", ... },
"accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
// 使用 Token 访问 API
GET /targets
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
四、快速部署
4.1 模拟模式(最快体验)
无需后端,直接体验前端界面:
bash
cd frontend
npm install
npm run dev -- --port 5175
# 访问 http://localhost:5175
# 登录账号: admin / admin123
4.2 Docker Compose 部署
一键启动所有服务:
bash
docker-compose -f docker/docker-compose.yml up -d
# 服务列表:
# - Frontend: http://localhost:5173
# - Backend: http://localhost:3001
# - PostgreSQL: localhost:5432
# - Redis: localhost:6379
4.3 本地开发部署
bash
# 1. 启动数据库
docker run -d --name servwatch-postgres \
-e POSTGRES_DB=servwatch \
-e POSTGRES_USER=postgres \
-e POSTGRES_PASSWORD=postgres \
-p 5432:5432 \
timescale/timescaledb:latest-pg16
# 2. 启动后端
cd backend
npm install
npm run dev
# 3. 启动前端
cd frontend
cp .env.production .env
npm install
npm run dev -- --port 5175
# 4. (可选) 启动 Agent
cd agent
npm install
npm start
五、项目结构
ServWatch/
├── backend/ # Node.js 后端服务
│ ├── src/
│ │ ├── config/ # 配置管理
│ │ ├── controllers/ # 请求处理器
│ │ ├── services/ # 业务逻辑
│ │ │ ├── websocketService.js # WebSocket连接管理
│ │ │ ├── alertService.js # 告警评估通知
│ │ │ └── metricsService.js # 指标聚合处理
│ │ ├── models/ # 数据模型
│ │ ├── routes/ # API路由
│ │ ├── websocket/ # WebSocket处理器
│ │ └── app.js
│ └── package.json
│
├── agent/ # 指标采集代理
│ ├── src/
│ │ ├── collectors/ # 采集器
│ │ │ ├── systemCollector.js # 系统指标
│ │ │ ├── appCollector.js # 应用指标
│ │ │ └── apiCollector.js # API性能
│ │ ├── transmitters/ # 数据传输
│ │ └── agent.js
│ └── package.json
│
├── frontend/ # Vue.js 前端
│ ├── src/
│ │ ├── components/ # Vue组件
│ │ ├── composables/ # 组合式函数
│ │ ├── stores/ # Pinia状态管理
│ │ ├── services/ # API服务
│ │ └── views/ # 页面视图
│ └── package.json
│
└── docker/ # Docker 配置
└── docker-compose.yml
六、界面预览
6.1 登录页面

6.2 GPU 监控

6.3 监控目标管理

七、后续规划
- 基础框架搭建
- 系统指标采集
- 应用性能监控
- 告警系统
- WebSocket 实时通信
- Docker 容器化
- 邮件/钉钉通知功能
- 自定义仪表板
- 数据导出报表
- 多租户支持
- 移动端适配
八、总结
ServWatch 是一个功能完整、易于部署的监控系统。相比 Prometheus+Grafana 组合,它更加轻量,学习成本更低,适合中小型团队和个人开发者使用。
如果你正在寻找一个简洁、美观的服务器监控方案,不妨试试 ServWatch!
GitHub项目地址1 : https://github.com/Anthony-981/ServWatch
Gitee项目地址2 : https://gitee.com/an-xuhui231/serv-watch
欢迎 Star 和 Fork,有问题欢迎提 Issue!
版权声明:本文为原创文章,转载请注明出处。