From 1821d266af59c40b24520b50829281a38921e01e Mon Sep 17 00:00:00 2001 From: gaoxq <376340421@qq.com> Date: Sat, 18 Apr 2026 11:07:29 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=B8=BB=E6=9C=BA=E4=BF=A1?= =?UTF-8?q?=E6=81=AF=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../jeesite/modules/utils/MonitorUtil.java | 220 ++++++++++++++++++ 1 file changed, 220 insertions(+) create mode 100644 web-api/src/main/java/com/jeesite/modules/utils/MonitorUtil.java diff --git a/web-api/src/main/java/com/jeesite/modules/utils/MonitorUtil.java b/web-api/src/main/java/com/jeesite/modules/utils/MonitorUtil.java new file mode 100644 index 0000000..5bfaf90 --- /dev/null +++ b/web-api/src/main/java/com/jeesite/modules/utils/MonitorUtil.java @@ -0,0 +1,220 @@ +package com.jeesite.modules.utils; + +import com.jcraft.jsch.*; +import com.jeesite.modules.apps.Module.SftpResult; +import com.jeesite.modules.biz.entity.MySftpAccounts; +import io.micrometer.common.util.StringUtils; + +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.util.*; + +/** + * 远程主机系统监控工具类 + * 通过 SSH exec 采集 CPU、内存、磁盘、负载、在线时长等指标 + * + * @author gaoxq + */ +public class MonitorUtil { + + private static final int SSH_TIMEOUT = 5000; + + // ------------------------------------------------------------------ 主入口 + + /** + * 采集远程主机系统信息 + * + * @param account SSH账号(复用 MySftpAccounts 的连接信息) + * @return MonitorResult + */ + public static SftpResult monitor(MySftpAccounts account) { + Session session = null; + ChannelExec exec = null; + try { + session = openSession(account); + exec = (ChannelExec) session.openChannel("exec"); + exec.setCommand(buildScript()); + InputStream in = exec.getInputStream(); + InputStream errIn = exec.getErrStream(); + exec.connect(SSH_TIMEOUT); + + String output = readStream(in); + String err = readStream(errIn); + if (!err.isEmpty() && output.isEmpty()) { + return SftpResult.fail("采集失败: " + err); + } + + return SftpResult.ok("采集成功", parse(output)); + } catch (Exception e) { + return SftpResult.fail("采集失败: " + e.getMessage()); + } finally { + if (exec != null && exec.isConnected()) exec.disconnect(); + if (session != null && session.isConnected()) session.disconnect(); + } + } + + // ------------------------------------------------------------------ 采集脚本 + + private static String buildScript() { + return "echo '==MARK_HOSTNAME==' && hostname && " + + "echo '==MARK_OS==' && uname -o 2>/dev/null || echo Linux && " + + "echo '==MARK_UPTIME==' && uptime -p 2>/dev/null || uptime && " + + "echo '==MARK_CPU==' && top -bn1 | head -5 && " + + "echo '==MARK_MEM==' && free -m && " + + "echo '==MARK_DISK==' && df -h"; + } + + private static Map parse(String output) { + Map info = new LinkedHashMap<>(); + + info.put("hostname", extract(output, "MARK_HOSTNAME", true)); + info.put("os", extract(output, "MARK_OS", true)); + info.put("uptime", extract(output, "MARK_UPTIME", true)); + info.put("cpu", parseCpu(extract(output, "MARK_CPU", false))); + info.put("memory", parseMemory(extract(output, "MARK_MEM", false))); + info.put("disk", parseDisk(extract(output, "MARK_DISK", false))); + + return info; + } + + // ---- CPU ---- + + /** + * 解析 top -bn1 输出 + * Cpu(s): 1.2 us, 0.3 sy, 0.0 ni, 98.3 id, 0.1 wa, 0.1 si + */ + private static Map parseCpu(String block) { + Map cpu = new LinkedHashMap<>(); + for (String line : block.split("\n")) { + if (line.startsWith("Cpu") || line.startsWith("%Cpu")) { + String data = line.replaceFirst(".*?:\\s*", "").replaceFirst("^%Cpu\\(s?\\):\\s*", ""); + for (String part : data.split(",")) { + part = part.trim(); + String[] kv = part.split("\\s+", 2); + if (kv.length >= 2) { + cpu.put(kv[1].trim(), kv[0].trim()); + } + } + break; + } + } + return cpu; + } + + /** + * 解析 free -m 输出 + * Mem: total used free shared buff/cache available + */ + private static Map parseMemory(String block) { + Map mem = new LinkedHashMap<>(); + for (String line : block.split("\n")) { + if (line.startsWith("Mem:")) { + String[] cols = line.trim().split("\\s+"); + if (cols.length >= 7) { + mem.put("total", cols[1] + " MB"); + mem.put("used", cols[2] + " MB"); + mem.put("free", cols[3] + " MB"); + mem.put("available", cols[6] + " MB"); + try { + double total = Double.parseDouble(cols[1]); + double used = Double.parseDouble(cols[2]); + double pct = total > 0 ? used / total * 100 : 0; + mem.put("usagePercent", String.format("%.1f%%", pct)); + } catch (NumberFormatException ignored) { + } + } + break; + } + } + return mem; + } + + // ---- 磁盘 ---- + + /** + * 解析 df -h 输出 + * Filesystem Size Used Avail Use% Mounted on + */ + private static List> parseDisk(String block) { + List> disks = new ArrayList<>(); + String[] lines = block.split("\n"); + for (int i = 0; i < lines.length; i++) { + String line = lines[i].trim(); + if (i == 0 && (line.startsWith("Filesystem") || line.startsWith("文件系统"))) continue; + if (line.isEmpty()) continue; + String[] cols = line.split("\\s+"); + if (cols.length >= 6) { + Map d = new LinkedHashMap<>(); + d.put("filesystem", cols[0]); + d.put("size", cols[1]); + d.put("used", cols[2]); + d.put("avail", cols[3]); + d.put("usePercent", cols[4]); + d.put("mounted", cols[5]); + disks.add(d); + } + } + return disks; + } + + // ---- 段落提取 ---- + + private static String extract(String output, String mark, boolean singleLine) { + String start = "==" + mark + "=="; + int s = output.indexOf(start); + if (s < 0) return ""; + s += start.length(); + if (s >= output.length()) return ""; + if (output.charAt(s) == '\n') s++; + if (singleLine) { + int e = output.indexOf('\n', s); + return (e < 0 ? output.substring(s) : output.substring(s, e)).trim(); + } + int e = output.indexOf("\n==", s); + return (e < 0 ? output.substring(s) : output.substring(s, e)).trim(); + } + + private static Session openSession(MySftpAccounts account) throws JSchException { + JSch jsch = new JSch(); + int port = Optional.ofNullable(account.getHostPort()).orElse(22); + if ("key".equalsIgnoreCase(account.getAuthType()) + && StringUtils.isNotBlank(account.getPrivateKey())) { + jsch.addIdentity("tempKey", + account.getPrivateKey().getBytes(StandardCharsets.UTF_8), + null, null); + } + Session session = jsch.getSession(account.getUsername(), account.getHostIp(), port); + session.setTimeout(SSH_TIMEOUT); + if (!"key".equalsIgnoreCase(account.getAuthType())) { + session.setPassword(account.getPassword()); + } + Properties config = new Properties(); + config.put("StrictHostKeyChecking", "no"); + session.setConfig(config); + session.connect(SSH_TIMEOUT); + return session; + } + + private static String readStream(InputStream in) throws IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + byte[] buf = new byte[4096]; + int len; + while ((len = in.read(buf)) != -1) { + out.write(buf, 0, len); + } + return out.toString(StandardCharsets.UTF_8); + } + + + public static void main(String[] args) { + MySftpAccounts sftpAccounts = new MySftpAccounts(); + sftpAccounts.setHostIp("192.168.31.194"); + sftpAccounts.setHostPort(22); + sftpAccounts.setUsername("ogsapp"); + sftpAccounts.setPassword("Sys@2026#me"); + sftpAccounts.setRootPath("/ogsapp"); + sftpAccounts.setAuthType("password"); + SftpResult sftpResult = MonitorUtil.monitor(sftpAccounts); + System.out.println(sftpResult.getData()); + } +}