增加主机信息功能
This commit is contained in:
220
web-api/src/main/java/com/jeesite/modules/utils/MonitorUtil.java
Normal file
220
web-api/src/main/java/com/jeesite/modules/utils/MonitorUtil.java
Normal file
@@ -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<String, Object> parse(String output) {
|
||||
Map<String, Object> 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<String, String> parseCpu(String block) {
|
||||
Map<String, String> 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<String, String> parseMemory(String block) {
|
||||
Map<String, String> 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<Map<String, String>> parseDisk(String block) {
|
||||
List<Map<String, String>> 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<String, String> 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());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user