From 3a79513e95483963b9ac326797729f98dc491cb4 Mon Sep 17 00:00:00 2001 From: gaoxq <376340421@qq.com> Date: Mon, 13 Apr 2026 22:49:09 +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 --- .../modules/apps/Module/ContainerInfo.java | 20 ++++- .../com/jeesite/modules/utils/DockerUtil.java | 82 ++++++++----------- 2 files changed, 53 insertions(+), 49 deletions(-) diff --git a/web-api/src/main/java/com/jeesite/modules/apps/Module/ContainerInfo.java b/web-api/src/main/java/com/jeesite/modules/apps/Module/ContainerInfo.java index 6ffc854..dacf4f4 100644 --- a/web-api/src/main/java/com/jeesite/modules/apps/Module/ContainerInfo.java +++ b/web-api/src/main/java/com/jeesite/modules/apps/Module/ContainerInfo.java @@ -8,14 +8,28 @@ import java.io.Serializable; * Docker 容器信息 */ @Data -public class ContainerInfo implements Serializable { +public class ContainerInfo implements Serializable { private String containerId; private String image; private String command; - private String status; private String created; - private String names; + private String status; private String ports; + private String names; private String accountId; + + public ContainerInfo() { + } + + public ContainerInfo(String containerId, String image, String command, String created, String status, String ports, String names, String accountId) { + this.containerId = containerId; + this.image = image; + this.command = command; + this.created = created; + this.status = status; + this.ports = ports; + this.names = names; + this.accountId = accountId; + } } diff --git a/web-api/src/main/java/com/jeesite/modules/utils/DockerUtil.java b/web-api/src/main/java/com/jeesite/modules/utils/DockerUtil.java index c932e55..0d6993b 100644 --- a/web-api/src/main/java/com/jeesite/modules/utils/DockerUtil.java +++ b/web-api/src/main/java/com/jeesite/modules/utils/DockerUtil.java @@ -5,28 +5,31 @@ import com.jeesite.modules.apps.Module.ContainerInfo; import com.jeesite.modules.apps.Module.DockerResult; import com.jeesite.modules.apps.Module.SystemInfo; import com.jeesite.modules.biz.entity.MySftpAccounts; +import io.micrometer.common.util.StringUtils; import java.io.ByteArrayOutputStream; import java.io.InputStream; import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.Hashtable; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; /** * Docker 工具类(连接池版本) */ public class DockerUtil { - private static final int SSH_TIMEOUT = 5000; + private static final int SSH_TIMEOUT = 3000; private static final int MAX_POOL_SIZE = 50; - /** 连接池:accountId -> Session */ + /** + * 连接池:accountId -> Session + */ private static final Map sessionPool = new ConcurrentHashMap<>(); - /** 被池化的 Session,包含创建时间和活跃标记 */ + /** + * 被池化的 Session,包含创建时间和活跃标记 + */ private static class PooledSession { Session session; long createdAt; @@ -44,11 +47,8 @@ public class DockerUtil { */ private static PooledSession getSession(MySftpAccounts account) throws JSchException { String key = account.getAccountId(); - - // 1. 检查现有连接:未使用 + 未断开 → 直接复用 PooledSession pooled = sessionPool.get(key); if (pooled != null && !pooled.inUse && pooled.session.isConnected()) { - // 超过30分钟没用的连接,视为过期,关闭重建 if (System.currentTimeMillis() - pooled.createdAt > 30 * 60 * 1000) { pooled.session.disconnect(); pooled.session = null; @@ -76,7 +76,7 @@ public class DockerUtil { if ("key".equalsIgnoreCase(account.getAuthType()) && account.getPrivateKey() != null) { jsch.addIdentity("key_" + key, - account.getPrivateKey().getBytes(StandardCharsets.UTF_8), null, null); + account.getPrivateKey().getBytes(StandardCharsets.UTF_8), null, null); } else { session.setPassword(account.getPassword()); } @@ -105,9 +105,8 @@ public class DockerUtil { channel = (ChannelExec) session.openChannel("exec"); String command = account.getRootPath() != null && !account.getRootPath().isEmpty() - ? "cd " + account.getRootPath() + " && " + cmd - : cmd; - + ? "cd " + account.getRootPath() + " && " + cmd + : cmd; channel.setCommand(command); InputStream in = channel.getInputStream(); channel.connect(SSH_TIMEOUT); @@ -121,7 +120,6 @@ public class DockerUtil { return out.toString(StandardCharsets.UTF_8).trim(); } catch (Exception e) { - // 连接异常,移除缓存,强制下次重建 if (pooled != null) { sessionPool.remove(account.getAccountId()); if (pooled.session.isConnected()) { @@ -135,36 +133,32 @@ public class DockerUtil { } } - // ────────────────────────────────────────────── - // 对外方法 - // ────────────────────────────────────────────── - /** * 列出容器(一次 SSH 连接获取所有信息) */ public static List listContainers(MySftpAccounts accounts, boolean all) { - List list = new ArrayList<>(); - String format = "\"{{.ID}}|{{.Image}}|{{.Command}}|{{.CreatedAt}}|{{.Status}}|{{.Ports}}|{{.Names}}\""; - String cmd = (all ? "docker ps -a" : "docker ps") + " --format " + format; - + String FORMAT = "{{.ID}}|{{.Image}}|{{.Command}}|{{.CreatedAt}}|{{.Status}}|{{.Ports}}|{{.Names}}"; + String cmd = (all ? "docker ps -a" : "docker ps") + " --format \"" + FORMAT + "\""; String output = runCommand(accounts, cmd); - if (output == null || output.isBlank()) return list; - - for (String line : output.split("\\R")) { - if (line.isBlank()) continue; - String[] arr = line.split("\\|"); - ContainerInfo info = new ContainerInfo(); - info.setContainerId(arr.length > 0 ? arr[0].trim() : ""); - info.setImage(arr.length > 1 ? arr[1].trim() : ""); - info.setCommand(arr.length > 2 ? arr[2].trim() : ""); - info.setCreated(arr.length > 3 ? arr[3].trim() : ""); - info.setStatus(arr.length > 4 ? arr[4].trim() : ""); - info.setPorts(arr.length > 5 ? arr[5].trim() : ""); - info.setNames(arr.length > 6 ? arr[6].trim() : ""); - info.setAccountId(accounts.getAccountId()); - list.add(info); + if (output == null || output.isBlank()) { + return Collections.emptyList(); } - return list; + return Arrays.stream(output.split("\\R")) + .filter(StringUtils::isNotBlank) + .map(line -> { + String[] arr = line.split("\\|", 8); + return new ContainerInfo( + arr.length > 0 ? arr[0].trim() : "", + arr.length > 1 ? arr[1].trim() : "", + arr.length > 2 ? arr[2].trim() : "", + arr.length > 3 ? arr[3].trim() : "", + arr.length > 4 ? arr[4].trim() : "", + arr.length > 5 ? arr[5].trim() : "", + arr.length > 6 ? arr[6].trim() : "", + accounts.getAccountId() + ); + }) + .collect(Collectors.toList()); } public static DockerResult start(MySftpAccounts accounts, String containerId) { @@ -203,9 +197,9 @@ public class DockerUtil { */ public static SystemInfo systemInfo(MySftpAccounts accounts) { String cmd = - "echo CPU:$(top -bn1 2>/dev/null | grep 'Cpu(s)' | awk '{printf \"%.1f\", 100-$8}') && " + - "echo MEM:$(free 2>/dev/null | grep Mem | awk '{printf \"%.1f\", $3/$2*100}') && " + - "echo DISK:$(df -h / 2>/dev/null | grep / | awk '{gsub(/%/,\"\"); print $5}')"; + "echo CPU:$(top -bn1 2>/dev/null | grep 'Cpu(s)' | awk '{printf \"%.1f\", 100-$8}') && " + + "echo MEM:$(free 2>/dev/null | grep Mem | awk '{printf \"%.1f\", $3/$2*100}') && " + + "echo DISK:$(df -h / 2>/dev/null | grep / | awk '{gsub(/%/,\"\"); print $5}')"; String output = runCommand(accounts, cmd); SystemInfo info = new SystemInfo(); @@ -224,10 +218,6 @@ public class DockerUtil { return info; } - // ────────────────────────────────────────────── - // 连接管理 - // ────────────────────────────────────────────── - /** * 关闭指定账号的连接(账号删除或更新密码时调用) */