作者: ruan 最后更新: 2025-11-05
SimpleServerStatus 使用 WebSocket 实现实时双向通信。系统包含两个独立的 WebSocket 通道:
- Agent 通道 (
/ws-report): Agent 上报监控数据到 Dashboard - 前端通道 (
/ws-frontend): Dashboard 推送数据到前端展示
- URL:
ws://dashboard-host:8900/ws-report - 协议: WebSocket
- 认证: HTTP Header 认证
const ws = new WebSocket('ws://localhost:8900/ws-report', {
headers: {
'X-AUTH-SECRET': 'your-secret-key',
'X-SERVER-ID': 'your-server-id'
}
});import "github.com/gorilla/websocket"
headers := http.Header{
"X-AUTH-SECRET": []string{"your-secret-key"},
"X-SERVER-ID": []string{"your-server-id"},
}
conn, _, err := websocket.DefaultDialer.Dial(
"ws://localhost:8900/ws-report",
headers,
)websocat ws://localhost:8900/ws-report \
--header "X-AUTH-SECRET: your-secret-key" \
--header "X-SERVER-ID: your-server-id"Agent 连接时必须在 HTTP Header 中提供认证信息:
| Header 名称 | 必填 | 说明 |
|---|---|---|
| X-AUTH-SECRET | 是 | 认证密钥,必须与 Dashboard 配置匹配 |
| X-SERVER-ID | 是 | 服务器ID,必须与 Dashboard 配置匹配 |
认证失败:
如果认证失败,连接将被立即关闭(Close Code: 1000)。
消息类型: Text (JSON)
完整消息示例:
{
"serverId": "web-1",
"serverName": "Web Server 1",
"group": "production",
"countryCode": "CN",
"location": "Beijing, China",
"ip": "123.45.67.89",
"cpu": {
"percent": 45.2,
"cores": 8,
"modelName": "Intel(R) Xeon(R) CPU E5-2680 v4"
},
"memory": {
"total": 16777216000,
"used": 8388608000,
"available": 8388608000,
"usedPercent": 50.0
},
"disk": [
{
"path": "/",
"total": 107374182400,
"used": 53687091200,
"free": 53687091200,
"usedPercent": 50.0,
"readSpeed": 1048576,
"writeSpeed": 524288
}
],
"network": {
"interfaceName": "eth0",
"bytesSent": 1073741824,
"bytesRecv": 2147483648,
"inSpeed": 1048576,
"outSpeed": 524288
},
"system": {
"hostname": "web-server-1",
"os": "linux",
"platform": "ubuntu",
"arch": "amd64",
"kernel": "5.15.0-58-generic",
"uptime": 864000
},
"timestamp": 1699123456
}字段说明:
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| serverId | string | 是 | 服务器ID |
| serverName | string | 是 | 服务器名称 |
| group | string | 否 | 分组名称 |
| countryCode | string | 否 | 国家代码(ISO 3166-1 alpha-2) |
| location | string | 否 | 地理位置描述 |
| ip | string | 否 | 公网IP地址 |
| cpu | CPUInfo | 是 | CPU信息 |
| memory | MemoryInfo | 是 | 内存信息 |
| disk | DiskInfo[] | 是 | 磁盘信息数组 |
| network | NetworkInfo | 是 | 网络信息 |
| system | SystemInfo | 是 | 系统信息 |
| timestamp | number | 是 | Unix时间戳(秒) |
Dashboard 不主动向 Agent 发送消息,只接收 Agent 上报的数据。
Agent 端:
- 每 30 秒发送一次 Ping 帧
- 如果 45 秒内未收到 Pong 响应,认为连接断开
Dashboard 端:
- 自动响应 Ping 帧(发送 Pong)
- 如果 60 秒内未收到任何消息(包括 Ping),断开连接
指数退避算法:
初始间隔: 3 秒
最大间隔: 10 分钟
增长因子: 1.2
重连序列:
- 第1次: 3秒后重连
- 第2次: 3.6秒后重连
- 第3次: 4.32秒后重连
- ...
- 最大: 600秒(10分钟)后重连
1. 创建连接
↓
2. 发送认证信息(Header)
↓
3. 认证验证
├─ 成功 → 连接建立
└─ 失败 → 连接关闭
↓
4. 数据上报循环
- 每 5 秒上报一次数据
- 每 30 秒发送心跳
↓
5. 连接断开
- 网络错误
- 心跳超时
- 主动关闭
↓
6. 重连(指数退避)
Close Codes:
| Code | 说明 |
|---|---|
| 1000 | 正常关闭 |
| 1001 | 端点离开 |
| 1002 | 协议错误 |
| 1003 | 不支持的数据类型 |
| 1006 | 异常关闭(连接丢失) |
| 1008 | 违反策略(认证失败) |
| 1011 | 内部错误 |
- URL:
ws://dashboard-host:8900/ws-frontend - 协议: WebSocket
- 认证: 无需认证
const ws = new WebSocket('ws://localhost:8900/ws-frontend');
ws.onopen = () => {
console.log('WebSocket 连接成功');
};
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
console.log('收到服务器数据:', data);
// 更新UI
};
ws.onerror = (error) => {
console.error('WebSocket 错误:', error);
};
ws.onclose = () => {
console.log('WebSocket 断开,3秒后重连');
setTimeout(() => connectWebSocket(), 3000);
};interface ServerInfo {
serverId: string;
serverName: string;
cpu: CPUInfo;
memory: MemoryInfo;
// ... 其他字段
}
class WebSocketClient {
private ws: WebSocket | null = null;
private reconnectInterval = 3000;
private url: string;
constructor(url: string) {
this.url = url;
}
connect() {
this.ws = new WebSocket(this.url);
this.ws.onopen = () => {
console.log('WebSocket 连接成功');
};
this.ws.onmessage = (event: MessageEvent) => {
try {
const data: ServerInfo = JSON.parse(event.data);
this.handleMessage(data);
} catch (error) {
console.error('JSON 解析失败:', error);
}
};
this.ws.onerror = (error: Event) => {
console.error('WebSocket 错误:', error);
};
this.ws.onclose = () => {
console.log('WebSocket 断开,尝试重连...');
setTimeout(() => this.connect(), this.reconnectInterval);
};
}
private handleMessage(data: ServerInfo) {
// 更新UI或触发事件
console.log('收到数据:', data);
}
close() {
if (this.ws) {
this.ws.close();
}
}
}
// 使用
const client = new WebSocketClient('ws://localhost:8900/ws-frontend');
client.connect();消息类型: Text (JSON)
消息内容: 与 Agent 上报的 ServerInfo 格式完全相同
{
"serverId": "web-1",
"serverName": "Web Server 1",
"cpu": { ... },
"memory": { ... },
"disk": [ ... ],
"network": { ... },
"system": { ... },
"timestamp": 1699123456
}推送时机:
- Dashboard 收到 Agent 上报数据时,立即广播到所有前端连接
- 实时性: 通常 <100ms 延迟
前端不向 Dashboard 发送消息,只接收数据。
多连接支持:
Dashboard 支持多个前端同时连接,每个连接独立接收所有服务器的数据。
断线重连:
let reconnectAttempts = 0;
const maxReconnectDelay = 30000; // 最大30秒
function connect() {
const ws = new WebSocket('ws://localhost:8900/ws-frontend');
ws.onopen = () => {
reconnectAttempts = 0; // 重置重连计数
console.log('连接成功');
};
ws.onclose = () => {
const delay = Math.min(3000 * Math.pow(1.5, reconnectAttempts), maxReconnectDelay);
reconnectAttempts++;
console.log(`连接断开,${delay/1000}秒后重连(第${reconnectAttempts}次)`);
setTimeout(connect, delay);
};
}// composables/useWebSocket.ts
import { ref, onMounted, onUnmounted } from 'vue'
import type { ServerInfo } from '@/types'
export function useWebSocket(url: string) {
const servers = ref<Map<string, ServerInfo>>(new Map())
const connected = ref(false)
let ws: WebSocket | null = null
function connect() {
ws = new WebSocket(url)
ws.onopen = () => {
connected.value = true
console.log('WebSocket 连接成功')
}
ws.onmessage = (event) => {
const data: ServerInfo = JSON.parse(event.data)
servers.value.set(data.serverId, data)
}
ws.onclose = () => {
connected.value = false
setTimeout(connect, 3000)
}
}
onMounted(() => {
connect()
})
onUnmounted(() => {
if (ws) {
ws.close()
}
})
return {
servers,
connected
}
}
// 使用
<script setup lang="ts">
import { useWebSocket } from '@/composables/useWebSocket'
const { servers, connected } = useWebSocket('ws://localhost:8900/ws-frontend')
</script>
<template>
<div>
<div v-if="connected" class="status online">在线</div>
<div v-else class="status offline">离线</div>
<div v-for="server in servers.values()" :key="server.serverId">
<h3>{{ server.serverName }}</h3>
<p>CPU: {{ server.cpu.percent.toFixed(1) }}%</p>
<p>内存: {{ server.memory.usedPercent.toFixed(1) }}%</p>
</div>
</div>
</template>// hooks/useWebSocket.ts
import { useState, useEffect, useRef } from 'react'
import type { ServerInfo } from './types'
export function useWebSocket(url: string) {
const [servers, setServers] = useState<Map<string, ServerInfo>>(new Map())
const [connected, setConnected] = useState(false)
const wsRef = useRef<WebSocket | null>(null)
useEffect(() => {
function connect() {
const ws = new WebSocket(url)
ws.onopen = () => {
setConnected(true)
}
ws.onmessage = (event) => {
const data: ServerInfo = JSON.parse(event.data)
setServers(prev => new Map(prev).set(data.serverId, data))
}
ws.onclose = () => {
setConnected(false)
setTimeout(connect, 3000)
}
wsRef.current = ws
}
connect()
return () => {
wsRef.current?.close()
}
}, [url])
return { servers, connected }
}- 打开开发者工具(F12)
- 切换到 Network 标签
- 筛选 WS(WebSocket)
- 查看连接状态和消息
# 安装 wscat
pnpm add -g wscat
# 测试前端通道
wscat -c ws://localhost:8900/ws-frontend
# 测试 Agent 通道(需要认证)
wscat -c ws://localhost:8900/ws-report \
-H "X-AUTH-SECRET: your-secret" \
-H "X-SERVER-ID: your-id"# Dashboard 日志
sudo journalctl -u sss-dashboard -f | grep WebSocket
# 过滤连接事件
sudo journalctl -u sss-dashboard -f | grep "连接\|断开"- Agent 上报: 默认 5 秒/次(可配置)
- 前端推送: 收到数据后立即推送(实时)
- 心跳: Agent 30秒/次,Dashboard 自动响应
- 并发连接数: 理论无限制,实际受服务器资源限制
- 单个连接: 支持长时间连接(数小时到数天)
- 重连频率: 建议使用指数退避,避免DDoS
单个 Agent:
- 消息大小: ~2KB
- 频率: 5秒/次
- 带宽: ~0.4 KB/s
100 个 Agent + 10 个前端:
- Agent 上行: 40 KB/s
- 前端下行: 400 KB/s (10个连接)
- 总带宽: ~440 KB/s
检查清单:
- ✅ Dashboard 是否运行
- ✅ 端口是否开放(防火墙)
- ✅ URL 是否正确(ws:// 或 wss://)
- ✅ Agent 认证信息是否正确
常见原因:
- 网络不稳定
- 心跳超时
- Dashboard 重启
- 代理服务器超时
解决方案:
- 实现自动重连(指数退避)
- 调整心跳间隔
- 配置代理超时(Nginx、Caddy)
检查:
- WebSocket 连接状态(浏览器开发者工具)
- Agent 是否正常上报(Dashboard 日志)
- 前端是否正确处理消息(控制台)
- 使用 WSS: 生产环境使用 wss:// (WebSocket Secure)
- 强认证密钥: Agent 使用强随机密钥
- 限流: 实现连接速率限制
- 监控: 监控异常连接和消息模式
- WebSocket 通信设计 - 架构设计详解
- REST API - HTTP API 文档
- 数据流向 - 数据流转
版本: 1.0 作者: ruan 最后更新: 2025-11-05