本文主要实现使用Node-RED实现远程服务器性能监测,监测指标包含运行天数、cpu负载、cpu使用率、内存、交换信息等。
一、实现步骤
- 安装
node-red-contrib-bigssh
节点:
npm install -g node-red-contrib-bigssh
Big SSH 是 Node-RED 的输入节点,用于通过 SSH 协议在远程主机上执行命令
核心功能特性如下:
- 命令配置
- 命令可直接设置为节点的属性值
- 支持通过有效载荷(payload)动态添加命令参数
- 命令可定义为 JavaScript 模板字符串,并以 payload 传递变量值
- 身份验证配置
- 依赖第三方库实现认证模块
- 需注意:首次连接需测试交互式应答机制(SSH 默认要求交互确认)
- 命令执行规范
- 必须指定命令的完整路径(因远程连接后环境变量不生效)
- 需显式定义 payload 的数据处理逻辑(如参数拼接方式)
- 使用top命令获取远程服务器指标:
top -b -n 1
top -b -n 1:是Linux 系统监控命令的常用组合,用于以批处理模式获取系统资源的一次性快照
命令返回结构分为五部分:
- 系统运行时间及负载平均值(1/5/15分钟)
- 任务统计(总进程数、运行/休眠/僵尸进程数)
- CPU使用详情(用户态/内核态/空闲占比等)
- 物理内存和交换分区使用量
- 进程列表(默认按CPU占用排序)
返回数据示例:
top - 16:10:33 up 315 days, 7:24, 2 users, load average: 0.02, 0.07, 0.12
Tasks: 224 total, 1 running, 223 sleeping, 0 stopped, 0 zombie
%Cpu(s): 0.7 us, 2.8 sy, 0.0 ni, 96.5 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem : 16265504 total, 3410928 free, 1766692 used, 11087884 buff/cache
KiB Swap: 8257532 total, 8250032 free, 7500 used. 13368544 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
25329 1000 20 0 3670564 263004 93620 S 5.9 1.6 1138:18 beam.smp
28355 root 20 0 162100 2192 1512 R 5.9 0.0 0:00.03 top
1 root 20 0 54572 6232 3500 S 0.0 0.0 201:38.12 systemd
2 root 20 0 0 0 0 S 0.0 0.0 0:16.42 kthreadd
4 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 kworker/0:+
6 root 20 0 0 0 0 S 0.0 0.0 0:55.03 ksoftirqd/0
7 root rt 0 0 0 0 S 0.0 0.0 1:27.17 migration/0
...
返回数据解析:
function parseTopOutput(output) {
let result = {};
// 提取运行天数
const uptimeMatch = output.match(/up\s+(\d+)\s+days?,\s+[\d:]+/);
result.days = uptimeMatch ? parseInt(uptimeMatch[1]) : 0;
// 提取CPU负载
const loadMatch = output.match(/load average:\s+([\d.]+),\s+([\d.]+),\s+([\d.]+)/);
result.cpuLoad = loadMatch ? `${loadMatch[1]}, ${loadMatch[2]}, ${loadMatch[3]}` : null;
// 提取CPU使用率
const cpuMatch = output.match(/%Cpu\(s\):\s+([\d.]+)\s+us,\s+([\d.]+)\s+sy,\s+([\d.]+)\s+ni,\s+([\d.]+)\s+id/);
result.cpuUsage = cpuMatch ? `用户:${cpuMatch[1]}% 系统:${cpuMatch[2]}% 空闲:${cpuMatch[4]}%` : null;
// 提取内存信息
const memMatch = output.match(/KiB Mem\s*:\s*(\d+)\s+total,\s*(\d+)\s+free,\s*(\d+)\s+used/);
result.memory = memMatch ? getMemInfo(memMatch[1], memMatch[3]): null;
// 提取交换分区信息
const swapMatch = output.match(/KiB Swap\s*:\s*(\d+)\s+total,\s*(\d+)\s+free,\s*(\d+)\s+used/);
result.swap = swapMatch ? getMemInfo(swapMatch[1], swapMatch[3]) : null;
return {
运行天数: `${result.days}天`,
CPU负载: result.cpuLoad,
CPU使用率: result.cpuUsage,
内存: result.memory,
交换分区: result.swap
};
}
/** 设置输出格式:使用百分比% -> 已使用/总量 ,单位由KiB转换为MiB*/
function getMemInfo(total,used){
const per = ((used / total) * 100).toFixed(1);
return per +`% -> ${Math.round(used / 1024)}MB/${Math.round(total / 1024)}MB`
}
输出如下:
二、源码
[
{
"id": "940e18a11fe3beb1",
"type": "bigssh",
"z": "b6f68decfc53e1bc",
"name": "",
"commandLine": "",
"commandArgs": "",
"minError": 1,
"minWarning": 1,
"noStdin": false,
"format": "utf8",
"payloadIsArg": true,
"myssh": "c46911cf241d534d",
"x": 650,
"y": 80,
"wires": [
[
"6104e759a01b23b7"
],
[],
[]
]
},
{
"id": "35009a3fa1d0a288",
"type": "debug",
"z": "b6f68decfc53e1bc",
"name": "debug 8",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "payload",
"targetType": "msg",
"statusVal": "",
"statusType": "auto",
"x": 1140,
"y": 80,
"wires": []
},
{
"id": "6104e759a01b23b7",
"type": "function",
"z": "b6f68decfc53e1bc",
"name": "数据转换",
"func": "msg.flag = false;\nif(msg.payload && msg.payload.startsWith(\"top\")){\n msg.flag = true;\n msg.payload = parseTopOutput(msg.payload);\n}\nreturn msg;\nfunction parseTopOutput(output) {\n let result = {};\n // 提取运行天数\n const uptimeMatch = output.match(/up\\s+(\\d+)\\s+days?,\\s+[\\d:]+/);\n result.days = uptimeMatch ? parseInt(uptimeMatch[1]) : 0;\n\n // 提取CPU负载\n const loadMatch = output.match(/load average:\\s+([\\d.]+),\\s+([\\d.]+),\\s+([\\d.]+)/);\n result.cpuLoad = loadMatch ? `${loadMatch[1]}, ${loadMatch[2]}, ${loadMatch[3]}` : null;\n\n // 提取CPU使用率\n const cpuMatch = output.match(/%Cpu\\(s\\):\\s+([\\d.]+)\\s+us,\\s+([\\d.]+)\\s+sy,\\s+([\\d.]+)\\s+ni,\\s+([\\d.]+)\\s+id/);\n result.cpuUsage = cpuMatch ? `用户:${cpuMatch[1]}% 系统:${cpuMatch[2]}% 空闲:${cpuMatch[4]}%` : null;\n\n // 提取内存信息\n const memMatch = output.match(/KiB Mem\\s*:\\s*(\\d+)\\s+total,\\s*(\\d+)\\s+free,\\s*(\\d+)\\s+used/);\n result.memory = memMatch ? getMemInfo(memMatch[1], memMatch[3]): null;\n\n // 提取交换分区信息\n const swapMatch = output.match(/KiB Swap\\s*:\\s*(\\d+)\\s+total,\\s*(\\d+)\\s+free,\\s*(\\d+)\\s+used/);\n result.swap = swapMatch ? getMemInfo(swapMatch[1], swapMatch[3]) : null;\n\n return {\n 运行天数: `${result.days}天`,\n CPU负载: result.cpuLoad,\n CPU使用率: result.cpuUsage,\n 内存: result.memory,\n 交换分区: result.swap\n };\n}\n/** 设置输出格式:使用百分比% -> 已使用/总量 ,单位由KiB转换为MiB*/\nfunction getMemInfo(total,used){\n const per = ((used / total) * 100).toFixed(1);\n return per +`% -> ${Math.round(used / 1024)}MB/${Math.round(total / 1024)}MB`\n}",
"outputs": 1,
"timeout": 0,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 840,
"y": 80,
"wires": [
[
"03cbef785d9bc275"
]
]
},
{
"id": "9abfcfc3a2cafc21",
"type": "function",
"z": "b6f68decfc53e1bc",
"name": "top指令",
"func": "msg.payload = `top -b -n 1 `;\nreturn msg;",
"outputs": 1,
"timeout": 0,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 440,
"y": 80,
"wires": [
[
"940e18a11fe3beb1"
]
]
},
{
"id": "4b5e31d0def99e0f",
"type": "inject",
"z": "b6f68decfc53e1bc",
"name": "",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"payload": "",
"payloadType": "date",
"x": 290,
"y": 80,
"wires": [
[
"9abfcfc3a2cafc21"
]
]
},
{
"id": "03cbef785d9bc275",
"type": "switch",
"z": "b6f68decfc53e1bc",
"name": "",
"property": "flag",
"propertyType": "msg",
"rules": [
{
"t": "true"
}
],
"checkall": "true",
"repair": false,
"outputs": 1,
"x": 990,
"y": 80,
"wires": [
[
"35009a3fa1d0a288"
]
]
},
{
"id": "db6efc9b136db406",
"type": "debug",
"z": "b6f68decfc53e1bc",
"name": "debug 9",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "payload",
"targetType": "msg",
"statusVal": "",
"statusType": "auto",
"x": 840,
"y": 40,
"wires": []
},
{
"id": "c46911cf241d534d",
"type": "SSH_Credentials",
"host": "10.50.3.101",
"port": "22",
"userlabel": "root@10.50.3.101"
}
]