117 lines
4.1 KiB
JavaScript
117 lines
4.1 KiB
JavaScript
|
|
// 光湖 push-heartbeat v1.0 — 每台服务器主动上报本机状态
|
|||
|
|
// 部署: 各服务器 PM2 cron模式,每5分钟运行一次
|
|||
|
|
// 原理: 不扫描别人,只报自己。主控台(BS-GZ-006:3920)集中收。
|
|||
|
|
// 冰朔: "不要扫描,让每个服务器自己主动往外发"
|
|||
|
|
|
|||
|
|
'use strict';
|
|||
|
|
|
|||
|
|
const http = require('http');
|
|||
|
|
const os = require('os');
|
|||
|
|
const fs = require('fs');
|
|||
|
|
|
|||
|
|
// ═══════════════════════════════════════
|
|||
|
|
// 配置 — 部署时修改
|
|||
|
|
// ═══════════════════════════════════════
|
|||
|
|
|
|||
|
|
const MASTER_HOST = process.env.MASTER_HOST || '43.139.217.141';
|
|||
|
|
const MASTER_PORT = parseInt(process.env.MASTER_PORT || '3920');
|
|||
|
|
const SERVER_CODE = process.env.SERVER_CODE || 'BS-UNKNOWN';
|
|||
|
|
const SERVER_NAME = process.env.SERVER_NAME || '未命名';
|
|||
|
|
const SERVER_KEY = process.env.SERVER_KEY || '';
|
|||
|
|
const INTERVAL_MS = 5 * 60 * 1000; // 5分钟
|
|||
|
|
|
|||
|
|
// ═══════════════════════════════════════
|
|||
|
|
// 硬件采集(本机,不连外网)
|
|||
|
|
// ═══════════════════════════════════════
|
|||
|
|
|
|||
|
|
function collectHardware() {
|
|||
|
|
const cpuCores = os.cpus().length;
|
|||
|
|
const loadAvg = os.loadavg();
|
|||
|
|
const totalMem = os.totalmem();
|
|||
|
|
const freeMem = os.freemem();
|
|||
|
|
const usedMem = totalMem - freeMem;
|
|||
|
|
const uptime = os.uptime();
|
|||
|
|
|
|||
|
|
return {
|
|||
|
|
cpu_cores: cpuCores,
|
|||
|
|
cpu_load: parseFloat(loadAvg[0].toFixed(2)),
|
|||
|
|
cpu_load5: parseFloat(loadAvg[1].toFixed(2)),
|
|||
|
|
cpu_load15: parseFloat(loadAvg[2].toFixed(2)),
|
|||
|
|
mem_total_mb: Math.floor(totalMem / 1024 / 1024),
|
|||
|
|
mem_used_mb: Math.floor(usedMem / 1024 / 1024),
|
|||
|
|
mem_avail_mb: Math.floor(freeMem / 1024 / 1024),
|
|||
|
|
mem_used_pct: parseFloat((usedMem / totalMem * 100).toFixed(1)),
|
|||
|
|
uptime_h: Math.floor(uptime / 3600),
|
|||
|
|
hostname: os.hostname(),
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ═══════════════════════════════════════
|
|||
|
|
// 上报到主控台(只发POST,不扫描)
|
|||
|
|
// ═══════════════════════════════════════
|
|||
|
|
|
|||
|
|
function reportToMaster(data) {
|
|||
|
|
return new Promise((resolve) => {
|
|||
|
|
const payload = JSON.stringify(data);
|
|||
|
|
const opts = {
|
|||
|
|
hostname: MASTER_HOST,
|
|||
|
|
port: MASTER_PORT,
|
|||
|
|
path: '/api/heartbeat',
|
|||
|
|
method: 'POST',
|
|||
|
|
headers: {
|
|||
|
|
'Content-Type': 'application/json',
|
|||
|
|
'Content-Length': Buffer.byteLength(payload),
|
|||
|
|
'X-Server-Code': SERVER_CODE,
|
|||
|
|
'X-Server-Key': SERVER_KEY,
|
|||
|
|
},
|
|||
|
|
timeout: 8000,
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const req = http.request(opts, (res) => {
|
|||
|
|
let body = '';
|
|||
|
|
res.on('data', (c) => (body += c));
|
|||
|
|
res.on('end', () => resolve({ ok: res.statusCode === 200, status: res.statusCode, body }));
|
|||
|
|
});
|
|||
|
|
req.on('error', (e) => resolve({ ok: false, error: e.message }));
|
|||
|
|
req.on('timeout', () => {
|
|||
|
|
req.destroy();
|
|||
|
|
resolve({ ok: false, error: 'timeout' });
|
|||
|
|
});
|
|||
|
|
req.write(payload);
|
|||
|
|
req.end();
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ═══════════════════════════════════════
|
|||
|
|
// 主逻辑
|
|||
|
|
// ═══════════════════════════════════════
|
|||
|
|
|
|||
|
|
async function push() {
|
|||
|
|
const hw = collectHardware();
|
|||
|
|
const data = {
|
|||
|
|
server_code: SERVER_CODE,
|
|||
|
|
server_name: SERVER_NAME,
|
|||
|
|
timestamp: new Date().toISOString(),
|
|||
|
|
hardware: hw,
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
console.log(`[${data.timestamp}] ${SERVER_CODE} → ${MASTER_HOST}:${MASTER_PORT}`);
|
|||
|
|
|
|||
|
|
const result = await reportToMaster(data);
|
|||
|
|
if (result.ok) {
|
|||
|
|
console.log(` ✅ 上报成功 · CPU ${hw.cpu_load} · 内存 ${hw.mem_used_pct}% · 运行 ${hw.uptime_h}h`);
|
|||
|
|
} else {
|
|||
|
|
console.log(` ❌ 上报失败: ${result.error || result.status}`);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 立即发一次
|
|||
|
|
push();
|
|||
|
|
|
|||
|
|
// 定时发
|
|||
|
|
if (process.argv.includes('--once')) {
|
|||
|
|
// 一次性模式 — PM2 cron 使用
|
|||
|
|
} else {
|
|||
|
|
setInterval(push, INTERVAL_MS);
|
|||
|
|
}
|