优化SSH线程池
This commit is contained in:
parent
81f1c6407b
commit
1a135d26cf
@ -62,6 +62,28 @@ public class ThreadPoolConfig {
|
||||
return executor;
|
||||
}
|
||||
|
||||
/**
|
||||
* 服务器监控线程池 - 使用虚拟线程(Java 21+)
|
||||
*
|
||||
* ⚠️ 为什么使用虚拟线程?
|
||||
* 1. SSH连接 + 远程命令执行是**网络I/O密集型**任务
|
||||
* 2. 等待SSH响应时线程会长时间阻塞
|
||||
* 3. 虚拟线程在阻塞时不占用OS线程,资源消耗极低
|
||||
* 4. 支持数百台服务器并发监控,无需担心线程池耗尽
|
||||
*
|
||||
* 💡 场景:
|
||||
* - 定时采集服务器CPU、内存、磁盘、网络指标
|
||||
* - 并发检测服务器在线状态
|
||||
* - SSH命令执行(top、free、df等)
|
||||
*/
|
||||
@Bean("serverMonitorExecutor")
|
||||
public SimpleAsyncTaskExecutor serverMonitorExecutor() {
|
||||
SimpleAsyncTaskExecutor executor = new SimpleAsyncTaskExecutor("server-monitor-virtual-");
|
||||
executor.setVirtualThreads(true);
|
||||
executor.setConcurrencyLimit(-1); // 无限制,支持大量并发
|
||||
return executor;
|
||||
}
|
||||
|
||||
/**
|
||||
* 通用应用任务线程池 - 保留平台线程(不使用虚拟线程)
|
||||
*
|
||||
|
||||
@ -36,8 +36,11 @@ public class ServerMonitorDataDTO {
|
||||
@Schema(description = "各分区磁盘使用率")
|
||||
private List<DiskUsageInfo> diskUsage;
|
||||
|
||||
@Schema(description = "网络使用率(MB/s)")
|
||||
private BigDecimal networkUsage;
|
||||
@Schema(description = "网络接收流量(KB/s)")
|
||||
private Long networkRx;
|
||||
|
||||
@Schema(description = "网络发送流量(KB/s)")
|
||||
private Long networkTx;
|
||||
|
||||
@Schema(description = "采集时间")
|
||||
private LocalDateTime collectTime;
|
||||
|
||||
@ -11,7 +11,7 @@ import com.qqchen.deploy.backend.deploy.repository.IServerRepository;
|
||||
import com.qqchen.deploy.backend.deploy.service.IServerAlertService;
|
||||
import com.qqchen.deploy.backend.deploy.service.IServerMonitorService;
|
||||
import com.qqchen.deploy.backend.deploy.service.IServerService;
|
||||
import com.qqchen.deploy.backend.framework.dto.DiskUsageInfo;
|
||||
import com.qqchen.deploy.backend.framework.dto.MonitorData;
|
||||
import com.qqchen.deploy.backend.framework.ssh.ISSHCommandService;
|
||||
import com.qqchen.deploy.backend.framework.ssh.SSHCommandServiceFactory;
|
||||
import com.qqchen.deploy.backend.notification.dto.SendNotificationRequest;
|
||||
@ -19,14 +19,15 @@ import com.qqchen.deploy.backend.notification.service.INotificationService;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.schmizz.sshj.SSHClient;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.core.task.AsyncTaskExecutor;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
@ -58,6 +59,10 @@ public class ServerMonitorScheduler {
|
||||
@Resource
|
||||
private IServerService serverService;
|
||||
|
||||
@Resource
|
||||
@Qualifier("serverMonitorExecutor")
|
||||
private Executor serverMonitorExecutor;
|
||||
|
||||
/**
|
||||
* 采集所有在线服务器的监控数据
|
||||
* 此方法由定时任务管理系统调用
|
||||
@ -101,7 +106,8 @@ public class ServerMonitorScheduler {
|
||||
final ServerMonitorNotificationConfig finalConfig = config;
|
||||
List<CompletableFuture<ServerMonitorDataDTO>> futures = allServers.stream()
|
||||
.map(server -> CompletableFuture.supplyAsync(() ->
|
||||
collectSingleServerWithStatusCheck(server, finalConfig)))
|
||||
collectSingleServerWithStatusCheck(server, finalConfig),
|
||||
serverMonitorExecutor)) // ✅ 使用专用虚拟线程池
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// 3. 等待所有任务完成
|
||||
@ -155,39 +161,65 @@ public class ServerMonitorScheduler {
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测服务器连接状态并采集监控数据
|
||||
* 统一使用 ServerService.testConnection() 方法进行连接测试和状态更新
|
||||
* 检测服务器连接状态并采集监控数据(一次SSH连接完成所有任务)
|
||||
*
|
||||
* 优化点:
|
||||
* 1. 只创建一次SSH连接(避免之前的双重连接问题)
|
||||
* 2. 不采集硬件信息(hostname、CPU核心数等),减少不必要的开销
|
||||
* 3. 只更新状态和最后连接时间
|
||||
*/
|
||||
private ServerMonitorDataDTO collectSingleServerWithStatusCheck(Server server, ServerMonitorNotificationConfig config) {
|
||||
SSHClient sshClient = null;
|
||||
ISSHCommandService sshService = null;
|
||||
|
||||
try {
|
||||
// 1. 调用统一的连接测试方法(会自动更新服务器状态、硬件信息等)
|
||||
ServerInfoDTO info = serverService.testConnection(server.getId());
|
||||
// 1. 获取对应OS的SSH服务
|
||||
sshService = sshCommandServiceFactory.getService(server.getOsType());
|
||||
|
||||
// 2. 检查连接状态
|
||||
if (!info.getConnected()) {
|
||||
// 连接失败(离线),发送离线通知
|
||||
log.error("服务器连接失败(离线): serverId={}, name={}, ip={}, error={}",
|
||||
server.getId(), server.getServerName(), server.getHostIp(), info.getErrorMessage());
|
||||
// 2. 创建SSH连接
|
||||
String password = null;
|
||||
String privateKey = null;
|
||||
String passphrase = null;
|
||||
|
||||
if (config != null && config.getNotificationChannelId() != null && config.getServerOfflineTemplateId() != null) {
|
||||
try {
|
||||
sendServerOfflineNotification(server, config);
|
||||
} catch (Exception notifyError) {
|
||||
log.error("发送服务器离线通知失败: serverId={}", server.getId(), notifyError);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
switch (server.getAuthType()) {
|
||||
case PASSWORD:
|
||||
password = server.getSshPassword();
|
||||
break;
|
||||
case KEY:
|
||||
privateKey = server.getSshPrivateKey();
|
||||
passphrase = server.getSshPassphrase();
|
||||
break;
|
||||
}
|
||||
|
||||
// 3. 连接成功,采集监控数据
|
||||
return collectServerMonitorData(server);
|
||||
sshClient = sshService.createConnection(
|
||||
server.getHostIp(),
|
||||
server.getSshPort(),
|
||||
server.getSshUser(),
|
||||
password,
|
||||
privateKey,
|
||||
passphrase
|
||||
);
|
||||
|
||||
// 3. 连接成功,更新服务器状态
|
||||
server.setStatus(ServerStatusEnum.ONLINE);
|
||||
server.setLastConnectTime(LocalDateTime.now());
|
||||
serverRepository.save(server);
|
||||
|
||||
log.debug("服务器连接成功: serverId={}, name={}, ip={}",
|
||||
server.getId(), server.getServerName(), server.getHostIp());
|
||||
|
||||
// 4. 采集监控数据(复用同一个SSH连接)
|
||||
return collectServerMonitorData(server, sshClient, sshService);
|
||||
|
||||
} catch (Exception e) {
|
||||
// 异常情况,发送离线通知
|
||||
log.error("服务器连接测试异常: serverId={}, name={}, ip={}, error={}",
|
||||
// 连接失败,更新服务器状态为离线
|
||||
log.error("服务器连接失败: serverId={}, name={}, ip={}, error={}",
|
||||
server.getId(), server.getServerName(), server.getHostIp(), e.getMessage());
|
||||
|
||||
server.setStatus(ServerStatusEnum.OFFLINE);
|
||||
serverRepository.save(server);
|
||||
|
||||
// 发送离线通知
|
||||
if (config != null && config.getNotificationChannelId() != null && config.getServerOfflineTemplateId() != null) {
|
||||
try {
|
||||
sendServerOfflineNotification(server, config);
|
||||
@ -197,6 +229,12 @@ public class ServerMonitorScheduler {
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
} finally {
|
||||
// 5. 关闭SSH连接
|
||||
if (sshService != null && sshClient != null) {
|
||||
sshService.closeConnection(sshClient);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -230,79 +268,61 @@ public class ServerMonitorScheduler {
|
||||
}
|
||||
|
||||
/**
|
||||
* 采集服务器监控数据(CPU、内存、磁盘使用率)
|
||||
* 注意:此方法仅负责采集监控数据,不负责连接测试和状态更新
|
||||
* 采集服务器监控数据(CPU、内存、磁盘、网络使用率)
|
||||
*
|
||||
* 性能优化:
|
||||
* 1. 每个OS实现类内部使用 executeCommands 批量执行命令(1次网络往返代替5次)
|
||||
* 2. 自动适配不同操作系统(Linux用bash,Windows用PowerShell)
|
||||
*
|
||||
* @param server 服务器实体
|
||||
* @param sshClient 已建立的SSH连接(由调用方管理生命周期)
|
||||
* @param sshService SSH命令服务
|
||||
* @return 监控数据
|
||||
*/
|
||||
private ServerMonitorDataDTO collectServerMonitorData(Server server) throws Exception {
|
||||
SSHClient sshClient = null;
|
||||
ISSHCommandService sshService = null;
|
||||
|
||||
private ServerMonitorDataDTO collectServerMonitorData(Server server, SSHClient sshClient, ISSHCommandService sshService) {
|
||||
try {
|
||||
// 1. 获取对应OS的SSH服务
|
||||
sshService = sshCommandServiceFactory.getService(server.getOsType());
|
||||
// 1. 调用框架层的统一采集方法(内部已批量执行命令 ✅)
|
||||
MonitorData monitorData = sshService.collectMonitorData(sshClient);
|
||||
|
||||
// 2. 创建SSH连接
|
||||
String password = null;
|
||||
String privateKey = null;
|
||||
String passphrase = null;
|
||||
|
||||
switch (server.getAuthType()) {
|
||||
case PASSWORD:
|
||||
password = server.getSshPassword();
|
||||
break;
|
||||
case KEY:
|
||||
privateKey = server.getSshPrivateKey();
|
||||
passphrase = server.getSshPassphrase();
|
||||
break;
|
||||
if (monitorData == null) {
|
||||
log.warn("监控数据采集失败: serverId={}, 返回null", server.getId());
|
||||
return null;
|
||||
}
|
||||
|
||||
sshClient = sshService.createConnection(
|
||||
server.getHostIp(),
|
||||
server.getSshPort(),
|
||||
server.getSshUser(),
|
||||
password,
|
||||
privateKey,
|
||||
passphrase
|
||||
);
|
||||
|
||||
// 3. 采集监控数据
|
||||
BigDecimal cpuUsage = sshService.getCpuUsage(sshClient);
|
||||
BigDecimal memoryUsage = sshService.getMemoryUsage(sshClient);
|
||||
List<DiskUsageInfo> diskUsage = sshService.getDiskUsage(sshClient);
|
||||
|
||||
// 4. 计算已用内存(基于内存使用率和总内存)
|
||||
// 2. 计算已用内存(基于内存使用率和总内存)
|
||||
Integer memoryUsed = null;
|
||||
if (memoryUsage != null && server.getMemorySize() != null) {
|
||||
memoryUsed = memoryUsage.multiply(new BigDecimal(server.getMemorySize()))
|
||||
if (monitorData.getMemoryUsage() != null && server.getMemorySize() != null) {
|
||||
memoryUsed = monitorData.getMemoryUsage().multiply(new BigDecimal(server.getMemorySize()))
|
||||
.divide(new BigDecimal(100), 0, BigDecimal.ROUND_HALF_UP)
|
||||
.intValue();
|
||||
}
|
||||
|
||||
// 5. 构建监控数据
|
||||
// 3. 转换为业务层DTO
|
||||
ServerMonitorDataDTO data = ServerMonitorDataDTO.builder()
|
||||
.serverId(server.getId())
|
||||
.cpuUsage(cpuUsage)
|
||||
.memoryUsage(memoryUsage)
|
||||
.cpuUsage(monitorData.getCpuUsage())
|
||||
.memoryUsage(monitorData.getMemoryUsage())
|
||||
.memoryUsed(memoryUsed)
|
||||
.diskUsage(diskUsage)
|
||||
.diskUsage(monitorData.getDiskUsage())
|
||||
.networkRx(monitorData.getNetworkRx())
|
||||
.networkTx(monitorData.getNetworkTx())
|
||||
.collectTime(LocalDateTime.now())
|
||||
.build();
|
||||
|
||||
log.debug("服务器监控数据采集成功: serverId={}, cpu={}%, mem={}%, diskCount={}",
|
||||
server.getId(), cpuUsage, memoryUsage,
|
||||
diskUsage != null ? diskUsage.size() : 0);
|
||||
log.debug("服务器监控数据采集成功: serverId={}, cpu={}%, mem={}%, diskCount={}, netRx={}KB/s, netTx={}KB/s",
|
||||
server.getId(),
|
||||
monitorData.getCpuUsage(),
|
||||
monitorData.getMemoryUsage(),
|
||||
monitorData.getDiskUsage() != null ? monitorData.getDiskUsage().size() : 0,
|
||||
monitorData.getNetworkRx(),
|
||||
monitorData.getNetworkTx());
|
||||
|
||||
return data;
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("采集服务器监控数据失败: serverId={}, serverName={}, error={}",
|
||||
server.getId(), server.getServerName(), e.getMessage());
|
||||
throw e; // 抛出异常让上层处理
|
||||
} finally {
|
||||
// 6. 关闭SSH连接
|
||||
if (sshService != null && sshClient != null) {
|
||||
sshService.closeConnection(sshClient);
|
||||
}
|
||||
return null; // 返回null,由调用方处理
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -70,6 +70,8 @@ public class ServerMonitorServiceImpl implements IServerMonitorService {
|
||||
.memoryUsage(dto.getMemoryUsage())
|
||||
.memoryUsed(dto.getMemoryUsed())
|
||||
.diskUsage(diskUsageJson)
|
||||
.networkRx(dto.getNetworkRx())
|
||||
.networkTx(dto.getNetworkTx())
|
||||
.collectTime(dto.getCollectTime())
|
||||
.build();
|
||||
}
|
||||
|
||||
@ -0,0 +1,53 @@
|
||||
package com.qqchen.deploy.backend.framework.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* SSH命令执行结果
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class CommandResult {
|
||||
|
||||
/**
|
||||
* 命令是否执行成功
|
||||
*/
|
||||
private boolean success;
|
||||
|
||||
/**
|
||||
* 命令输出(成功时为标准输出,失败时为错误输出)
|
||||
*/
|
||||
private String output;
|
||||
|
||||
/**
|
||||
* 退出码(0表示成功)
|
||||
*/
|
||||
private Integer exitCode;
|
||||
|
||||
/**
|
||||
* 创建成功结果
|
||||
*/
|
||||
public static CommandResult success(String output) {
|
||||
return CommandResult.builder()
|
||||
.success(true)
|
||||
.output(output)
|
||||
.exitCode(0)
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建失败结果
|
||||
*/
|
||||
public static CommandResult failure(String error, Integer exitCode) {
|
||||
return CommandResult.builder()
|
||||
.success(false)
|
||||
.output(error)
|
||||
.exitCode(exitCode)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,45 @@
|
||||
package com.qqchen.deploy.backend.framework.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 监控数据(Framework层)
|
||||
* 用于一次性返回所有监控指标
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class MonitorData {
|
||||
|
||||
/**
|
||||
* CPU使用率(%)
|
||||
*/
|
||||
private BigDecimal cpuUsage;
|
||||
|
||||
/**
|
||||
* 内存使用率(%)
|
||||
*/
|
||||
private BigDecimal memoryUsage;
|
||||
|
||||
/**
|
||||
* 磁盘使用情况列表
|
||||
*/
|
||||
private List<DiskUsageInfo> diskUsage;
|
||||
|
||||
/**
|
||||
* 网络接收流量(KB/s)
|
||||
*/
|
||||
private Long networkRx;
|
||||
|
||||
/**
|
||||
* 网络发送流量(KB/s)
|
||||
*/
|
||||
private Long networkTx;
|
||||
}
|
||||
@ -1,5 +1,6 @@
|
||||
package com.qqchen.deploy.backend.framework.ssh;
|
||||
|
||||
import com.qqchen.deploy.backend.framework.dto.CommandResult;
|
||||
import com.qqchen.deploy.backend.framework.enums.ResponseCode;
|
||||
import com.qqchen.deploy.backend.framework.exception.BusinessException;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@ -10,6 +11,8 @@ import net.schmizz.sshj.userauth.keyprovider.KeyProvider;
|
||||
import net.schmizz.sshj.userauth.password.PasswordUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
@ -99,20 +102,23 @@ public abstract class AbstractSSHCommandService implements ISSHCommandService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String executeCommand(SSHClient sshClient, String command) throws Exception {
|
||||
public CommandResult executeCommand(SSHClient sshClient, String command) throws Exception {
|
||||
try (Session session = sshClient.startSession()) {
|
||||
Session.Command cmd = session.exec(command);
|
||||
cmd.join(COMMAND_TIMEOUT_SECONDS, TimeUnit.SECONDS);
|
||||
|
||||
if (cmd.getExitStatus() == 0) {
|
||||
Integer exitStatus = cmd.getExitStatus();
|
||||
|
||||
if (exitStatus == 0) {
|
||||
// 命令执行成功
|
||||
String output = new String(cmd.getInputStream().readAllBytes()).trim();
|
||||
log.debug("命令执行成功: {} -> {}", command, output);
|
||||
return output;
|
||||
return CommandResult.success(output);
|
||||
} else {
|
||||
// 命令执行失败
|
||||
String error = new String(cmd.getErrorStream().readAllBytes()).trim();
|
||||
log.warn("命令执行失败: {} -> {}", command, error);
|
||||
throw new BusinessException(ResponseCode.ERROR,
|
||||
new Object[]{"命令执行失败: " + error});
|
||||
log.warn("命令执行失败: exitCode={}, command={}, error={}", exitStatus, command, error);
|
||||
return CommandResult.failure(error, exitStatus);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
log.error("执行SSH命令异常: {}", command, e);
|
||||
@ -121,6 +127,44 @@ public abstract class AbstractSSHCommandService implements ISSHCommandService {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CommandResult> executeCommands(SSHClient sshClient, List<String> commands) throws Exception {
|
||||
if (commands == null || commands.isEmpty()) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
log.debug("批量执行{}个命令", commands.size());
|
||||
|
||||
List<CommandResult> results = new ArrayList<>();
|
||||
|
||||
// 循环执行每个命令,每个命令独立执行(一个失败不影响其他)
|
||||
for (int i = 0; i < commands.size(); i++) {
|
||||
String command = commands.get(i);
|
||||
try {
|
||||
CommandResult result = executeCommand(sshClient, command);
|
||||
results.add(result);
|
||||
|
||||
if (result.isSuccess()) {
|
||||
log.debug("命令[{}/{}]执行成功: {}", i + 1, commands.size(), command);
|
||||
} else {
|
||||
log.warn("命令[{}/{}]执行失败: exitCode={}, command={}",
|
||||
i + 1, commands.size(), result.getExitCode(), command);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// 命令执行异常(连接问题等),记录失败结果
|
||||
log.error("命令[{}/{}]执行异常: {}", i + 1, commands.size(), command, e);
|
||||
results.add(CommandResult.failure(e.getMessage(), -1));
|
||||
}
|
||||
}
|
||||
|
||||
log.debug("批量命令执行完成: 总数={}, 成功={}, 失败={}",
|
||||
commands.size(),
|
||||
results.stream().filter(CommandResult::isSuccess).count(),
|
||||
results.stream().filter(r -> !r.isSuccess()).count());
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closeConnection(SSHClient sshClient) {
|
||||
if (sshClient != null) {
|
||||
@ -137,11 +181,12 @@ public abstract class AbstractSSHCommandService implements ISSHCommandService {
|
||||
|
||||
/**
|
||||
* 安全执行命令(子类使用)
|
||||
* 出错时返回null而不是抛异常
|
||||
* 返回命令输出,失败时返回null
|
||||
*/
|
||||
protected String safeExecute(SSHClient sshClient, String command) {
|
||||
try {
|
||||
return executeCommand(sshClient, command);
|
||||
CommandResult result = executeCommand(sshClient, command);
|
||||
return result.isSuccess() ? result.getOutput() : null;
|
||||
} catch (Exception e) {
|
||||
log.warn("执行命令失败: {}", command, e);
|
||||
return null;
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
package com.qqchen.deploy.backend.framework.ssh;
|
||||
|
||||
import com.qqchen.deploy.backend.framework.dto.CommandResult;
|
||||
import com.qqchen.deploy.backend.framework.dto.DiskInfo;
|
||||
import com.qqchen.deploy.backend.framework.dto.DiskUsageInfo;
|
||||
import com.qqchen.deploy.backend.framework.dto.MonitorData;
|
||||
import com.qqchen.deploy.backend.framework.enums.OsTypeEnum;
|
||||
import net.schmizz.sshj.SSHClient;
|
||||
|
||||
@ -30,14 +32,25 @@ public interface ISSHCommandService {
|
||||
String password, String privateKey, String passphrase) throws Exception;
|
||||
|
||||
/**
|
||||
* 执行命令并返回结果
|
||||
* 执行单个命令并返回结果(包含成功/失败状态)
|
||||
*
|
||||
* @param sshClient SSH客户端
|
||||
* @param command 要执行的命令
|
||||
* @return 命令输出结果
|
||||
* @throws Exception 执行失败时抛出
|
||||
* @return 命令执行结果,包含成功/失败状态、输出和退出码
|
||||
* @throws Exception 连接异常时抛出
|
||||
*/
|
||||
String executeCommand(SSHClient sshClient, String command) throws Exception;
|
||||
CommandResult executeCommand(SSHClient sshClient, String command) throws Exception;
|
||||
|
||||
/**
|
||||
* 批量执行多个命令(性能优化:一次性执行多个命令,减少网络往返)
|
||||
* 每个命令独立执行,一个命令失败不影响其他命令
|
||||
*
|
||||
* @param sshClient SSH客户端
|
||||
* @param commands 要执行的命令列表
|
||||
* @return 命令执行结果列表,顺序与输入commands对应,包含成功/失败状态和输出
|
||||
* @throws Exception 连接异常时抛出
|
||||
*/
|
||||
List<CommandResult> executeCommands(SSHClient sshClient, List<String> commands) throws Exception;
|
||||
|
||||
/**
|
||||
* 关闭SSH连接
|
||||
@ -111,9 +124,35 @@ public interface ISSHCommandService {
|
||||
List<DiskUsageInfo> getDiskUsage(SSHClient sshClient);
|
||||
|
||||
/**
|
||||
* 获取支持的操作系统类型
|
||||
* 获取网络接收流量(KB/s)
|
||||
*
|
||||
* @return OS类型枚举
|
||||
* @param sshClient SSH客户端
|
||||
* @return 网络接收流量,如果无法获取则返回null
|
||||
*/
|
||||
Long getNetworkRx(SSHClient sshClient);
|
||||
|
||||
/**
|
||||
* 获取网络发送流量(KB/s)
|
||||
*
|
||||
* @param sshClient SSH客户端
|
||||
* @return 网络发送流量,如果无法获取则返回null
|
||||
*/
|
||||
Long getNetworkTx(SSHClient sshClient);
|
||||
|
||||
/**
|
||||
* 一次性采集所有监控数据(性能优化:批量执行命令,减少网络往返)
|
||||
* 每个OS实现类内部会使用 executeCommands 批量执行所有命令
|
||||
* 推荐在定时监控场景使用此方法,而不是分别调用单个方法
|
||||
*
|
||||
* @param sshClient SSH客户端
|
||||
* @return 监控数据(包含CPU、内存、磁盘、网络)
|
||||
*/
|
||||
MonitorData collectMonitorData(SSHClient sshClient);
|
||||
|
||||
/**
|
||||
* 获取当前服务支持的操作系统类型
|
||||
*
|
||||
* @return 操作系统类型
|
||||
*/
|
||||
OsTypeEnum getSupportedOsType();
|
||||
}
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
package com.qqchen.deploy.backend.framework.ssh.impl;
|
||||
|
||||
import com.qqchen.deploy.backend.framework.dto.CommandResult;
|
||||
import com.qqchen.deploy.backend.framework.dto.DiskInfo;
|
||||
import com.qqchen.deploy.backend.framework.dto.DiskUsageInfo;
|
||||
import com.qqchen.deploy.backend.framework.dto.MonitorData;
|
||||
import com.qqchen.deploy.backend.framework.enums.OsTypeEnum;
|
||||
import com.qqchen.deploy.backend.framework.ssh.AbstractSSHCommandService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@ -9,10 +11,7 @@ import net.schmizz.sshj.SSHClient;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Linux SSH命令服务实现(Framework层)
|
||||
@ -21,6 +20,42 @@ import java.util.Set;
|
||||
@Service
|
||||
public class LinuxSSHCommandServiceImpl extends AbstractSSHCommandService {
|
||||
|
||||
// ==================== 命令常量 ====================
|
||||
|
||||
/**
|
||||
* CPU使用率命令
|
||||
*/
|
||||
private static final String CMD_CPU_USAGE =
|
||||
"top -bn1 | grep 'Cpu(s)' | awk '{print $2}' | sed 's/%us,//'";
|
||||
|
||||
/**
|
||||
* 内存使用率命令
|
||||
*/
|
||||
private static final String CMD_MEMORY_USAGE =
|
||||
"free | grep Mem | awk '{printf \"%.2f\", ($2-$7)/$2 * 100}'";
|
||||
|
||||
/**
|
||||
* 磁盘使用率命令(格式:设备|挂载点|文件系统|总容量|已用|使用率)
|
||||
*/
|
||||
private static final String CMD_DISK_USAGE =
|
||||
"df -BG -T | grep -E '^/dev/(sd|vd|nvme|mapper|xvd)' | " +
|
||||
"awk '{print $1 \"|\" $7 \"|\" $2 \"|\" $3 \"|\" $4 \"|\" $6}' | " +
|
||||
"sed 's/G//g; s/%//'";
|
||||
|
||||
/**
|
||||
* 网络接收流量命令(KB/s)
|
||||
*/
|
||||
private static final String CMD_NETWORK_RX =
|
||||
"cat /proc/net/dev | grep -vE 'lo:|face' | awk '{sum+=$2} END {print int(sum/1024)}'";
|
||||
|
||||
/**
|
||||
* 网络发送流量命令(KB/s)
|
||||
*/
|
||||
private static final String CMD_NETWORK_TX =
|
||||
"cat /proc/net/dev | grep -vE 'lo:|face' | awk '{sum+=$10} END {print int(sum/1024)}'";
|
||||
|
||||
// ==================== 实现方法 ====================
|
||||
|
||||
@Override
|
||||
public String getHostname(SSHClient sshClient) {
|
||||
return safeExecute(sshClient, "hostname");
|
||||
@ -95,28 +130,19 @@ public class LinuxSSHCommandServiceImpl extends AbstractSSHCommandService {
|
||||
|
||||
@Override
|
||||
public BigDecimal getCpuUsage(SSHClient sshClient) {
|
||||
// 使用 top 命令获取CPU使用率
|
||||
String command = "top -bn1 | grep 'Cpu(s)' | awk '{print $2}' | sed 's/%us,//'";
|
||||
String result = safeExecute(sshClient, command);
|
||||
String result = safeExecute(sshClient, CMD_CPU_USAGE);
|
||||
return parseBigDecimal(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BigDecimal getMemoryUsage(SSHClient sshClient) {
|
||||
// 计算内存使用率:(总内存 - 可用内存) / 总内存 * 100
|
||||
String command = "free | grep Mem | awk '{printf \"%.2f\", ($2-$7)/$2 * 100}'";
|
||||
String result = safeExecute(sshClient, command);
|
||||
String result = safeExecute(sshClient, CMD_MEMORY_USAGE);
|
||||
return parseBigDecimal(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<DiskUsageInfo> getDiskUsage(SSHClient sshClient) {
|
||||
// 获取磁盘使用率
|
||||
// 格式: 设备|挂载点|文件系统|总容量|已用|使用率
|
||||
String command = "df -BG -T | grep -E '^/dev/(sd|vd|nvme|mapper|xvd)' | " +
|
||||
"awk '{print $1 \"|\" $7 \"|\" $2 \"|\" $3 \"|\" $4 \"|\" $6}' | " +
|
||||
"sed 's/G//g; s/%//'";
|
||||
String result = safeExecute(sshClient, command);
|
||||
String result = safeExecute(sshClient, CMD_DISK_USAGE);
|
||||
|
||||
List<DiskUsageInfo> diskUsageList = new ArrayList<>();
|
||||
Set<String> seenDevices = new HashSet<>();
|
||||
@ -157,6 +183,137 @@ public class LinuxSSHCommandServiceImpl extends AbstractSSHCommandService {
|
||||
return diskUsageList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getNetworkRx(SSHClient sshClient) {
|
||||
String result = safeExecute(sshClient, CMD_NETWORK_RX);
|
||||
return parseLong(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getNetworkTx(SSHClient sshClient) {
|
||||
String result = safeExecute(sshClient, CMD_NETWORK_TX);
|
||||
return parseLong(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析 Long 类型结果
|
||||
*/
|
||||
private Long parseLong(String result) {
|
||||
if (result == null || result.trim().isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return Long.parseLong(result.trim());
|
||||
} catch (NumberFormatException e) {
|
||||
log.warn("解析Long失败: {}", result, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public MonitorData collectMonitorData(SSHClient sshClient) {
|
||||
try {
|
||||
// 1. 准备5个监控命令(使用统一的命令常量 ✅)
|
||||
List<String> commands = Arrays.asList(
|
||||
CMD_CPU_USAGE,
|
||||
CMD_MEMORY_USAGE,
|
||||
CMD_DISK_USAGE,
|
||||
CMD_NETWORK_RX,
|
||||
CMD_NETWORK_TX
|
||||
);
|
||||
|
||||
// 2. 批量执行命令(只需1次网络往返 ✅)
|
||||
List<CommandResult> results = executeCommands(sshClient, commands);
|
||||
|
||||
// 3. 解析结果
|
||||
BigDecimal cpuUsage = parseCommandResultToBigDecimal(results.get(0));
|
||||
BigDecimal memoryUsage = parseCommandResultToBigDecimal(results.get(1));
|
||||
List<DiskUsageInfo> diskUsage = parseDiskUsageFromCommand(results.get(2));
|
||||
Long networkRx = parseCommandResultToLong(results.get(3));
|
||||
Long networkTx = parseCommandResultToLong(results.get(4));
|
||||
|
||||
// 4. 构建监控数据
|
||||
return MonitorData.builder()
|
||||
.cpuUsage(cpuUsage)
|
||||
.memoryUsage(memoryUsage)
|
||||
.diskUsage(diskUsage)
|
||||
.networkRx(networkRx)
|
||||
.networkTx(networkTx)
|
||||
.build();
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("批量采集Linux监控数据失败", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从CommandResult解析BigDecimal
|
||||
*/
|
||||
private BigDecimal parseCommandResultToBigDecimal(CommandResult result) {
|
||||
if (result == null || !result.isSuccess() || result.getOutput() == null || result.getOutput().trim().isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return parseBigDecimal(result.getOutput());
|
||||
}
|
||||
|
||||
/**
|
||||
* 从CommandResult解析Long
|
||||
*/
|
||||
private Long parseCommandResultToLong(CommandResult result) {
|
||||
if (result == null || !result.isSuccess() || result.getOutput() == null || result.getOutput().trim().isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return parseLong(result.getOutput());
|
||||
}
|
||||
|
||||
/**
|
||||
* 从CommandResult解析磁盘使用率(格式:设备|挂载点|文件系统|总容量|已用|使用率)
|
||||
*/
|
||||
private List<DiskUsageInfo> parseDiskUsageFromCommand(CommandResult result) {
|
||||
List<DiskUsageInfo> diskUsageList = new ArrayList<>();
|
||||
|
||||
if (result == null || !result.isSuccess() || result.getOutput() == null || result.getOutput().trim().isEmpty()) {
|
||||
return diskUsageList;
|
||||
}
|
||||
|
||||
Set<String> seenDevices = new HashSet<>();
|
||||
String[] lines = result.getOutput().trim().split("\\n");
|
||||
|
||||
for (String line : lines) {
|
||||
String[] parts = line.split("\\|");
|
||||
if (parts.length >= 6) {
|
||||
try {
|
||||
String device = parts[0].trim();
|
||||
String mountPoint = parts[1].trim();
|
||||
String fileSystem = parts[2].trim();
|
||||
String totalStr = parts[3].trim();
|
||||
String usedStr = parts[4].trim();
|
||||
String usageStr = parts[5].trim();
|
||||
|
||||
// 去重
|
||||
if (seenDevices.contains(device)) {
|
||||
continue;
|
||||
}
|
||||
seenDevices.add(device);
|
||||
|
||||
DiskUsageInfo diskUsage = DiskUsageInfo.builder()
|
||||
.mountPoint(mountPoint)
|
||||
.fileSystem(fileSystem)
|
||||
.totalSize(new BigDecimal(totalStr))
|
||||
.usedSize(new BigDecimal(usedStr))
|
||||
.usagePercent(new BigDecimal(usageStr))
|
||||
.build();
|
||||
diskUsageList.add(diskUsage);
|
||||
} catch (NumberFormatException e) {
|
||||
log.warn("解析磁盘使用率失败: {}", line, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return diskUsageList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OsTypeEnum getSupportedOsType() {
|
||||
return OsTypeEnum.LINUX;
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
package com.qqchen.deploy.backend.framework.ssh.impl;
|
||||
|
||||
import com.qqchen.deploy.backend.framework.dto.CommandResult;
|
||||
import com.qqchen.deploy.backend.framework.dto.DiskInfo;
|
||||
import com.qqchen.deploy.backend.framework.dto.DiskUsageInfo;
|
||||
import com.qqchen.deploy.backend.framework.dto.MonitorData;
|
||||
import com.qqchen.deploy.backend.framework.enums.OsTypeEnum;
|
||||
import com.qqchen.deploy.backend.framework.ssh.AbstractSSHCommandService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@ -9,10 +11,7 @@ import net.schmizz.sshj.SSHClient;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* MacOS SSH命令服务实现(Framework层)
|
||||
@ -21,6 +20,40 @@ import java.util.Set;
|
||||
@Service
|
||||
public class MacOSSSHCommandServiceImpl extends AbstractSSHCommandService {
|
||||
|
||||
// ==================== 命令常量 ====================
|
||||
|
||||
/**
|
||||
* CPU使用率命令
|
||||
*/
|
||||
private static final String CMD_CPU_USAGE =
|
||||
"top -l 1 -s 0 | grep 'CPU usage' | awk '{print $3}' | sed 's/%//'";
|
||||
|
||||
/**
|
||||
* 内存使用率命令
|
||||
*/
|
||||
private static final String CMD_MEMORY_USAGE =
|
||||
"vm_stat | awk '/Pages active/ {active=$3} /Pages wired/ {wired=$4} /Pages free/ {free=$3} END {used=active+wired; total=used+free; printf \"%.2f\", (used/total)*100}'";
|
||||
|
||||
/**
|
||||
* 磁盘使用率命令(格式:设备|挂载点|容量|已用|可用|使用率)
|
||||
*/
|
||||
private static final String CMD_DISK_USAGE =
|
||||
"df -h | grep -E '^/dev/disk' | awk '{print $1 \"|\" $9 \"|\" $2 \"|\" $3 \"|\" $4 \"|\" $5}' | sed 's/Gi//g; s/%//'";
|
||||
|
||||
/**
|
||||
* 网络接收流量命令(KB/s) - 使用netstat统计
|
||||
*/
|
||||
private static final String CMD_NETWORK_RX =
|
||||
"netstat -ib | awk '/en[0-9]/ && !/Link/ {sum+=$7} END {print int(sum/1024)}'";
|
||||
|
||||
/**
|
||||
* 网络发送流量命令(KB/s) - 使用netstat统计
|
||||
*/
|
||||
private static final String CMD_NETWORK_TX =
|
||||
"netstat -ib | awk '/en[0-9]/ && !/Link/ {sum+=$10} END {print int(sum/1024)}'";
|
||||
|
||||
// ==================== 实现方法 ====================
|
||||
|
||||
@Override
|
||||
public String getHostname(SSHClient sshClient) {
|
||||
return safeExecute(sshClient, "hostname");
|
||||
@ -96,27 +129,19 @@ public class MacOSSSHCommandServiceImpl extends AbstractSSHCommandService {
|
||||
|
||||
@Override
|
||||
public BigDecimal getCpuUsage(SSHClient sshClient) {
|
||||
// MacOS CPU使用率:使用top命令获取
|
||||
String command = "top -l 1 -s 0 | grep 'CPU usage' | awk '{print $3}' | sed 's/%//'";
|
||||
String result = safeExecute(sshClient, command);
|
||||
String result = safeExecute(sshClient, CMD_CPU_USAGE);
|
||||
return parseBigDecimal(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BigDecimal getMemoryUsage(SSHClient sshClient) {
|
||||
// MacOS内存使用率:使用vm_stat命令计算
|
||||
// vm_stat输出的是页数,需要计算实际使用率
|
||||
String command = "vm_stat | awk '/Pages active/ {active=$3} /Pages wired/ {wired=$4} /Pages free/ {free=$3} END {used=active+wired; total=used+free; printf \"%.2f\", (used/total)*100}'";
|
||||
String result = safeExecute(sshClient, command);
|
||||
String result = safeExecute(sshClient, CMD_MEMORY_USAGE);
|
||||
return parseBigDecimal(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<DiskUsageInfo> getDiskUsage(SSHClient sshClient) {
|
||||
// MacOS磁盘使用率:使用df命令
|
||||
// 格式: 设备|挂载点|容量|已用|可用|使用率
|
||||
String command = "df -h | grep -E '^/dev/disk' | awk '{print $1 \"|\" $9 \"|\" $2 \"|\" $3 \"|\" $4 \"|\" $5}' | sed 's/Gi//g; s/%//'";
|
||||
String result = safeExecute(sshClient, command);
|
||||
String result = safeExecute(sshClient, CMD_DISK_USAGE);
|
||||
|
||||
List<DiskUsageInfo> diskUsageList = new ArrayList<>();
|
||||
Set<String> seenDevices = new HashSet<>();
|
||||
@ -159,6 +184,139 @@ public class MacOSSSHCommandServiceImpl extends AbstractSSHCommandService {
|
||||
return diskUsageList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getNetworkRx(SSHClient sshClient) {
|
||||
String result = safeExecute(sshClient, CMD_NETWORK_RX);
|
||||
return parseLong(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getNetworkTx(SSHClient sshClient) {
|
||||
String result = safeExecute(sshClient, CMD_NETWORK_TX);
|
||||
return parseLong(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析 Long 类型结果
|
||||
*/
|
||||
private Long parseLong(String result) {
|
||||
if (result == null || result.trim().isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return Long.parseLong(result.trim());
|
||||
} catch (NumberFormatException e) {
|
||||
log.warn("解析Long失败: {}", result, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public MonitorData collectMonitorData(SSHClient sshClient) {
|
||||
try {
|
||||
// 1. 准备5个监控命令(使用统一的命令常量 ✅)
|
||||
List<String> commands = Arrays.asList(
|
||||
CMD_CPU_USAGE,
|
||||
CMD_MEMORY_USAGE,
|
||||
CMD_DISK_USAGE,
|
||||
CMD_NETWORK_RX,
|
||||
CMD_NETWORK_TX
|
||||
);
|
||||
|
||||
// 2. 批量执行命令(只需1次网络往返 ✅)
|
||||
List<CommandResult> results = executeCommands(sshClient, commands);
|
||||
|
||||
// 3. 解析结果
|
||||
BigDecimal cpuUsage = parseCommandResultToBigDecimal(results.get(0));
|
||||
BigDecimal memoryUsage = parseCommandResultToBigDecimal(results.get(1));
|
||||
List<DiskUsageInfo> diskUsage = parseDiskUsageFromCommand(results.get(2));
|
||||
Long networkRx = parseCommandResultToLong(results.get(3));
|
||||
Long networkTx = parseCommandResultToLong(results.get(4));
|
||||
|
||||
// 4. 构建监控数据
|
||||
return MonitorData.builder()
|
||||
.cpuUsage(cpuUsage)
|
||||
.memoryUsage(memoryUsage)
|
||||
.diskUsage(diskUsage)
|
||||
.networkRx(networkRx)
|
||||
.networkTx(networkTx)
|
||||
.build();
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("批量采集macOS监控数据失败", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从CommandResult解析BigDecimal
|
||||
*/
|
||||
private BigDecimal parseCommandResultToBigDecimal(CommandResult result) {
|
||||
if (result == null || !result.isSuccess() || result.getOutput() == null || result.getOutput().trim().isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return parseBigDecimal(result.getOutput());
|
||||
}
|
||||
|
||||
/**
|
||||
* 从CommandResult解析Long
|
||||
*/
|
||||
private Long parseCommandResultToLong(CommandResult result) {
|
||||
if (result == null || !result.isSuccess() || result.getOutput() == null || result.getOutput().trim().isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return parseLong(result.getOutput());
|
||||
}
|
||||
|
||||
/**
|
||||
* 从CommandResult解析macOS磁盘使用率(格式:设备|挂载点|容量|已用|可用|使用率)
|
||||
*/
|
||||
private List<DiskUsageInfo> parseDiskUsageFromCommand(CommandResult result) {
|
||||
List<DiskUsageInfo> diskUsageList = new ArrayList<>();
|
||||
|
||||
if (result == null || !result.isSuccess() || result.getOutput() == null || result.getOutput().trim().isEmpty()) {
|
||||
return diskUsageList;
|
||||
}
|
||||
|
||||
Set<String> seenDevices = new HashSet<>();
|
||||
String[] lines = result.getOutput().trim().split("\\n");
|
||||
|
||||
for (String line : lines) {
|
||||
String[] parts = line.split("\\|");
|
||||
if (parts.length >= 6) {
|
||||
try {
|
||||
String device = parts[0].trim();
|
||||
String mountPoint = parts[1].trim();
|
||||
String totalStr = parts[2].trim();
|
||||
String usedStr = parts[3].trim();
|
||||
String usageStr = parts[5].trim();
|
||||
|
||||
// 去重
|
||||
if (seenDevices.contains(device)) {
|
||||
continue;
|
||||
}
|
||||
seenDevices.add(device);
|
||||
|
||||
// 简化的文件系统类型
|
||||
String fileSystem = "APFS";
|
||||
|
||||
DiskUsageInfo diskUsage = DiskUsageInfo.builder()
|
||||
.mountPoint(mountPoint)
|
||||
.fileSystem(fileSystem)
|
||||
.totalSize(parseBigDecimal(totalStr))
|
||||
.usedSize(parseBigDecimal(usedStr))
|
||||
.usagePercent(parseBigDecimal(usageStr))
|
||||
.build();
|
||||
diskUsageList.add(diskUsage);
|
||||
} catch (Exception e) {
|
||||
log.warn("解析macOS磁盘使用率失败: {}", line, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return diskUsageList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OsTypeEnum getSupportedOsType() {
|
||||
return OsTypeEnum.MACOS;
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
package com.qqchen.deploy.backend.framework.ssh.impl;
|
||||
|
||||
import com.qqchen.deploy.backend.framework.dto.CommandResult;
|
||||
import com.qqchen.deploy.backend.framework.dto.DiskInfo;
|
||||
import com.qqchen.deploy.backend.framework.dto.DiskUsageInfo;
|
||||
import com.qqchen.deploy.backend.framework.dto.MonitorData;
|
||||
import com.qqchen.deploy.backend.framework.enums.OsTypeEnum;
|
||||
import com.qqchen.deploy.backend.framework.ssh.AbstractSSHCommandService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@ -9,10 +11,7 @@ import net.schmizz.sshj.SSHClient;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Windows SSH命令服务实现(Framework层)
|
||||
@ -22,6 +21,61 @@ import java.util.Set;
|
||||
@Service
|
||||
public class WindowsSSHCommandServiceImpl extends AbstractSSHCommandService {
|
||||
|
||||
// ==================== 命令常量 ====================
|
||||
|
||||
/**
|
||||
* CPU使用率命令
|
||||
*/
|
||||
private static final String CMD_CPU_USAGE =
|
||||
"powershell \"Get-Counter '\\Processor(_Total)\\% Processor Time' | " +
|
||||
"Select-Object -ExpandProperty CounterSamples | " +
|
||||
"Select-Object -ExpandProperty CookedValue\"";
|
||||
|
||||
/**
|
||||
* 内存使用率命令
|
||||
*/
|
||||
private static final String CMD_MEMORY_USAGE =
|
||||
"powershell \"$os = Get-CimInstance Win32_OperatingSystem; " +
|
||||
"$total = $os.TotalVisibleMemorySize; " +
|
||||
"$free = $os.FreePhysicalMemory; " +
|
||||
"$used = $total - $free; " +
|
||||
"[math]::Round(($used / $total) * 100, 2)\"";
|
||||
|
||||
/**
|
||||
* 磁盘使用率命令(格式:驱动器号|文件系统|总容量|已用|使用率)
|
||||
*/
|
||||
private static final String CMD_DISK_USAGE =
|
||||
"powershell \"Get-Volume | " +
|
||||
"Where-Object {$_.DriveLetter -ne $null -and $_.Size -gt 0} | " +
|
||||
"ForEach-Object {" +
|
||||
" $used = [math]::Round(($_.Size - $_.SizeRemaining)/1GB, 2); " +
|
||||
" $total = [math]::Round($_.Size/1GB, 2); " +
|
||||
" $percent = [math]::Round((($_.Size - $_.SizeRemaining)/$_.Size)*100, 2); " +
|
||||
" $_.DriveLetter + '|' + $_.FileSystemType + '|' + $total + '|' + $used + '|' + $percent" +
|
||||
"}\"";
|
||||
|
||||
/**
|
||||
* 网络接收流量命令(KB)
|
||||
*/
|
||||
private static final String CMD_NETWORK_RX =
|
||||
"powershell \"Get-NetAdapterStatistics | " +
|
||||
"Where-Object {$_.Name -notlike '*Loopback*' -and $_.Name -notlike '*Virtual*'} | " +
|
||||
"Measure-Object -Property ReceivedBytes -Sum | " +
|
||||
"Select-Object -ExpandProperty Sum | " +
|
||||
"ForEach-Object {[math]::Round($_ / 1KB)}\"";
|
||||
|
||||
/**
|
||||
* 网络发送流量命令(KB)
|
||||
*/
|
||||
private static final String CMD_NETWORK_TX =
|
||||
"powershell \"Get-NetAdapterStatistics | " +
|
||||
"Where-Object {$_.Name -notlike '*Loopback*' -and $_.Name -notlike '*Virtual*'} | " +
|
||||
"Measure-Object -Property SentBytes -Sum | " +
|
||||
"Select-Object -ExpandProperty Sum | " +
|
||||
"ForEach-Object {[math]::Round($_ / 1KB)}\"";
|
||||
|
||||
// ==================== 实现方法 ====================
|
||||
|
||||
@Override
|
||||
public String getHostname(SSHClient sshClient) {
|
||||
return safeExecute(sshClient, "hostname");
|
||||
@ -101,39 +155,19 @@ public class WindowsSSHCommandServiceImpl extends AbstractSSHCommandService {
|
||||
|
||||
@Override
|
||||
public BigDecimal getCpuUsage(SSHClient sshClient) {
|
||||
// Windows CPU使用率:使用PowerShell获取
|
||||
String command = "powershell \"Get-Counter '\\Processor(_Total)\\% Processor Time' | " +
|
||||
"Select-Object -ExpandProperty CounterSamples | " +
|
||||
"Select-Object -ExpandProperty CookedValue\"";
|
||||
String result = safeExecute(sshClient, command);
|
||||
String result = safeExecute(sshClient, CMD_CPU_USAGE);
|
||||
return parseBigDecimal(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BigDecimal getMemoryUsage(SSHClient sshClient) {
|
||||
// Windows内存使用率:使用PowerShell计算
|
||||
String command = "powershell \"$os = Get-CimInstance Win32_OperatingSystem; " +
|
||||
"$total = $os.TotalVisibleMemorySize; " +
|
||||
"$free = $os.FreePhysicalMemory; " +
|
||||
"$used = $total - $free; " +
|
||||
"[math]::Round(($used / $total) * 100, 2)\"";
|
||||
String result = safeExecute(sshClient, command);
|
||||
String result = safeExecute(sshClient, CMD_MEMORY_USAGE);
|
||||
return parseBigDecimal(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<DiskUsageInfo> getDiskUsage(SSHClient sshClient) {
|
||||
// Windows磁盘使用率:使用PowerShell获取
|
||||
// 输出格式: 驱动器号|文件系统|总容量|已用|使用率
|
||||
String command = "powershell \"Get-Volume | " +
|
||||
"Where-Object {$_.DriveLetter -ne $null -and $_.Size -gt 0} | " +
|
||||
"ForEach-Object {" +
|
||||
" $used = [math]::Round(($_.Size - $_.SizeRemaining)/1GB, 2); " +
|
||||
" $total = [math]::Round($_.Size/1GB, 2); " +
|
||||
" $percent = [math]::Round((($_.Size - $_.SizeRemaining)/$_.Size)*100, 2); " +
|
||||
" $_.DriveLetter + '|' + $_.FileSystemType + '|' + $total + '|' + $used + '|' + $percent" +
|
||||
"}\"";
|
||||
String result = safeExecute(sshClient, command);
|
||||
String result = safeExecute(sshClient, CMD_DISK_USAGE);
|
||||
|
||||
List<DiskUsageInfo> diskUsageList = new ArrayList<>();
|
||||
Set<String> seenDrives = new HashSet<>();
|
||||
@ -178,6 +212,141 @@ public class WindowsSSHCommandServiceImpl extends AbstractSSHCommandService {
|
||||
return diskUsageList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getNetworkRx(SSHClient sshClient) {
|
||||
String result = safeExecute(sshClient, CMD_NETWORK_RX);
|
||||
return parseLong(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getNetworkTx(SSHClient sshClient) {
|
||||
String result = safeExecute(sshClient, CMD_NETWORK_TX);
|
||||
return parseLong(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析 Long 类型结果
|
||||
*/
|
||||
private Long parseLong(String value) {
|
||||
if (value == null || value.trim().isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return Long.parseLong(value.trim());
|
||||
} catch (NumberFormatException e) {
|
||||
log.warn("解析Long失败: {}", value, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public MonitorData collectMonitorData(SSHClient sshClient) {
|
||||
try {
|
||||
// 1. 准备5个PowerShell监控命令(使用统一的命令常量 ✅)
|
||||
List<String> commands = Arrays.asList(
|
||||
CMD_CPU_USAGE,
|
||||
CMD_MEMORY_USAGE,
|
||||
CMD_DISK_USAGE,
|
||||
CMD_NETWORK_RX,
|
||||
CMD_NETWORK_TX
|
||||
);
|
||||
|
||||
// 2. 批量执行PowerShell命令(只需1次网络往返 ✅)
|
||||
List<CommandResult> results = executeCommands(sshClient, commands);
|
||||
|
||||
// 3. 解析结果
|
||||
BigDecimal cpuUsage = parseCommandResultToBigDecimal(results.get(0));
|
||||
BigDecimal memoryUsage = parseCommandResultToBigDecimal(results.get(1));
|
||||
List<DiskUsageInfo> diskUsage = parseDiskUsageFromCommand(results.get(2));
|
||||
Long networkRx = parseCommandResultToLong(results.get(3));
|
||||
Long networkTx = parseCommandResultToLong(results.get(4));
|
||||
|
||||
// 4. 构建监控数据
|
||||
return MonitorData.builder()
|
||||
.cpuUsage(cpuUsage)
|
||||
.memoryUsage(memoryUsage)
|
||||
.diskUsage(diskUsage)
|
||||
.networkRx(networkRx)
|
||||
.networkTx(networkTx)
|
||||
.build();
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("批量采集Windows监控数据失败", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从CommandResult解析BigDecimal
|
||||
*/
|
||||
private BigDecimal parseCommandResultToBigDecimal(CommandResult result) {
|
||||
if (result == null || !result.isSuccess() || result.getOutput() == null || result.getOutput().trim().isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return parseBigDecimal(result.getOutput());
|
||||
}
|
||||
|
||||
/**
|
||||
* 从CommandResult解析Long
|
||||
*/
|
||||
private Long parseCommandResultToLong(CommandResult result) {
|
||||
if (result == null || !result.isSuccess() || result.getOutput() == null || result.getOutput().trim().isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return parseLong(result.getOutput());
|
||||
}
|
||||
|
||||
/**
|
||||
* 从CommandResult解析Windows磁盘使用率(格式:驱动器号|文件系统|总容量|已用|使用率)
|
||||
*/
|
||||
private List<DiskUsageInfo> parseDiskUsageFromCommand(CommandResult result) {
|
||||
List<DiskUsageInfo> diskUsageList = new ArrayList<>();
|
||||
|
||||
if (result == null || !result.isSuccess() || result.getOutput() == null || result.getOutput().trim().isEmpty()) {
|
||||
return diskUsageList;
|
||||
}
|
||||
|
||||
Set<String> seenDrives = new HashSet<>();
|
||||
String[] lines = result.getOutput().trim().split("\\n");
|
||||
|
||||
for (String line : lines) {
|
||||
String[] parts = line.split("\\|");
|
||||
if (parts.length >= 5) {
|
||||
try {
|
||||
String driveLetter = parts[0].trim();
|
||||
String fileSystem = parts[1].trim();
|
||||
String totalStr = parts[2].trim();
|
||||
String usedStr = parts[3].trim();
|
||||
String usageStr = parts[4].trim();
|
||||
|
||||
// 去重
|
||||
if (seenDrives.contains(driveLetter)) {
|
||||
continue;
|
||||
}
|
||||
seenDrives.add(driveLetter);
|
||||
|
||||
// 处理空的文件系统类型
|
||||
if (fileSystem == null || fileSystem.isEmpty()) {
|
||||
fileSystem = "NTFS";
|
||||
}
|
||||
|
||||
DiskUsageInfo diskUsage = DiskUsageInfo.builder()
|
||||
.mountPoint(driveLetter + ":")
|
||||
.fileSystem(fileSystem)
|
||||
.totalSize(parseBigDecimal(totalStr))
|
||||
.usedSize(parseBigDecimal(usedStr))
|
||||
.usagePercent(parseBigDecimal(usageStr))
|
||||
.build();
|
||||
diskUsageList.add(diskUsage);
|
||||
} catch (Exception e) {
|
||||
log.warn("解析Windows磁盘使用率失败: {}", line, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return diskUsageList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OsTypeEnum getSupportedOsType() {
|
||||
return OsTypeEnum.WINDOWS;
|
||||
|
||||
@ -769,19 +769,19 @@ INSERT INTO `deploy-ease-platform`.`schedule_job_category` (`id`, `create_by`, `
|
||||
INSERT INTO `deploy-ease-platform`.`schedule_job_category` (`id`, `create_by`, `create_time`, `update_by`, `update_time`, `version`, `deleted`, `code`, `name`, `description`, `icon`, `color`, `enabled`, `sort`) VALUES (5, 'system', NOW(), 'system', NOW(), 1, b'0', 'BACKUP', '备份任务', '定期备份数据库和重要文件', 'DatabaseOutlined', '#722ed1', b'1', 5);
|
||||
|
||||
|
||||
INSERT INTO `deploy-ease-platform`.`schedule_job` (`id`, `create_by`, `create_time`, `update_by`, `update_time`, `version`, `deleted`, `job_name`, `job_description`, `category_id`, `bean_name`, `method_name`, `form_definition_id`, `method_params`, `cron_expression`, `status`, `concurrent`, `last_execute_time`, `next_execute_time`, `execute_count`, `success_count`, `fail_count`, `timeout_seconds`, `retry_count`, `alert_email`) VALUES (2, 'admin', NOW(), 'admin', NOW(), 27, b'0', '链宇Git仓库组同步', '定期同步Git仓库组信息,每天凌晨2点执行', 2, 'repositoryGroupServiceImpl', 'syncGroups', NULL, '{\"externalSystemId\": 2}', '0 0 2 * * ?', 'ENABLED', b'0', NOW(), NOW(), 0, 0, 0, 3600, 2, '');
|
||||
INSERT INTO `deploy-ease-platform`.`schedule_job` (`id`, `create_by`, `create_time`, `update_by`, `update_time`, `version`, `deleted`, `job_name`, `job_description`, `category_id`, `bean_name`, `method_name`, `form_definition_id`, `method_params`, `cron_expression`, `status`, `concurrent`, `last_execute_time`, `next_execute_time`, `execute_count`, `success_count`, `fail_count`, `timeout_seconds`, `retry_count`, `alert_email`) VALUES (3, 'admin', NOW(), 'admin', NOW(), 29, b'0', '链宇Git项目同步', '定期同步Git项目信息,每天凌晨3点执行', 2, 'repositoryProjectServiceImpl', 'syncProjects', NULL, '{\"externalSystemId\": 2}', '0 0 3 * * ?', 'ENABLED', b'0', NOW(), NOW(), 0, 0, 0, 3600, 2, '');
|
||||
INSERT INTO `deploy-ease-platform`.`schedule_job` (`id`, `create_by`, `create_time`, `update_by`, `update_time`, `version`, `deleted`, `job_name`, `job_description`, `category_id`, `bean_name`, `method_name`, `form_definition_id`, `method_params`, `cron_expression`, `status`, `concurrent`, `last_execute_time`, `next_execute_time`, `execute_count`, `success_count`, `fail_count`, `timeout_seconds`, `retry_count`, `alert_email`) VALUES (4, 'admin', NOW(), 'system', NOW(), 5725, b'0', '链宇Git分支同步', '定期同步Git仓库分支信息,每5分钟执行一次', 2, 'repositoryBranchServiceImpl', 'syncBranches', NULL, '{\"externalSystemId\": 2}', '0 */5 * * * ?', 'ENABLED', b'0', NOW(), NOW(), 7, 7, 0, 3600, 2, '');
|
||||
INSERT INTO `deploy-ease-platform`.`schedule_job` (`id`, `create_by`, `create_time`, `update_by`, `update_time`, `version`, `deleted`, `job_name`, `job_description`, `category_id`, `bean_name`, `method_name`, `form_definition_id`, `method_params`, `cron_expression`, `status`, `concurrent`, `last_execute_time`, `next_execute_time`, `execute_count`, `success_count`, `fail_count`, `timeout_seconds`, `retry_count`, `alert_email`) VALUES (8, 'admin', NOW(), 'system', NOW(), 1209, b'0', '链宇Jenkins视图同步', '', 2, 'jenkinsViewServiceImpl', 'syncViews', NULL, '{\"externalSystemId\": 1}', '0 */5 * * * ?', 'ENABLED', b'0', NOW(), NOW(), 7, 7, 0, 300, 0, '');
|
||||
INSERT INTO `deploy-ease-platform`.`schedule_job` (`id`, `create_by`, `create_time`, `update_by`, `update_time`, `version`, `deleted`, `job_name`, `job_description`, `category_id`, `bean_name`, `method_name`, `form_definition_id`, `method_params`, `cron_expression`, `status`, `concurrent`, `last_execute_time`, `next_execute_time`, `execute_count`, `success_count`, `fail_count`, `timeout_seconds`, `retry_count`, `alert_email`) VALUES (9, 'admin', NOW(), 'system', NOW(), 1209, b'0', '链宇Jenkins任务同步', '', 2, 'jenkinsJobServiceImpl', 'syncJobs', NULL, '{\"externalSystemId\": 1}', '0 */5 * * * ?', 'ENABLED', b'0', NOW(), NOW(), 7, 7, 0, 300, 0, '');
|
||||
INSERT INTO `deploy-ease-platform`.`schedule_job` (`id`, `create_by`, `create_time`, `update_by`, `update_time`, `version`, `deleted`, `job_name`, `job_description`, `category_id`, `bean_name`, `method_name`, `form_definition_id`, `method_params`, `cron_expression`, `status`, `concurrent`, `last_execute_time`, `next_execute_time`, `execute_count`, `success_count`, `fail_count`, `timeout_seconds`, `retry_count`, `alert_email`) VALUES (10, 'admin', NOW(), 'system', NOW(), 10156, b'0', '链宇Jenkins构建同步', '', 2, 'jenkinsBuildServiceImpl', 'syncBuilds', NULL, '{\"externalSystemId\": 1}', '0 */2 * * * ?', 'ENABLED', b'0', NOW(), NOW(), 18, 18, 0, 300, 0, '');
|
||||
INSERT INTO `deploy-ease-platform`.`schedule_job` (`id`, `create_by`, `create_time`, `update_by`, `update_time`, `version`, `deleted`, `job_name`, `job_description`, `category_id`, `bean_name`, `method_name`, `form_definition_id`, `method_params`, `cron_expression`, `status`, `concurrent`, `last_execute_time`, `next_execute_time`, `execute_count`, `success_count`, `fail_count`, `timeout_seconds`, `retry_count`, `alert_email`) VALUES (11, 'admin', NOW(), 'system', NOW(), 1210, b'0', '隆基Jenkins视图同步', '', 2, 'jenkinsViewServiceImpl', 'syncViews', NULL, '{\"externalSystemId\": 3}', '0 */5 * * * ?', 'ENABLED', b'0', NOW(), NOW(), 7, 7, 0, 300, 0, '');
|
||||
INSERT INTO `deploy-ease-platform`.`schedule_job` (`id`, `create_by`, `create_time`, `update_by`, `update_time`, `version`, `deleted`, `job_name`, `job_description`, `category_id`, `bean_name`, `method_name`, `form_definition_id`, `method_params`, `cron_expression`, `status`, `concurrent`, `last_execute_time`, `next_execute_time`, `execute_count`, `success_count`, `fail_count`, `timeout_seconds`, `retry_count`, `alert_email`) VALUES (12, 'admin', NOW(), 'system', NOW(), 1210, b'0', '隆基Jenkins任务同步', '', 2, 'jenkinsJobServiceImpl', 'syncJobs', NULL, '{\"externalSystemId\": 3}', '0 */5 * * * ?', 'ENABLED', b'0', NOW(), NOW(), 7, 7, 0, 300, 0, '');
|
||||
INSERT INTO `deploy-ease-platform`.`schedule_job` (`id`, `create_by`, `create_time`, `update_by`, `update_time`, `version`, `deleted`, `job_name`, `job_description`, `category_id`, `bean_name`, `method_name`, `form_definition_id`, `method_params`, `cron_expression`, `status`, `concurrent`, `last_execute_time`, `next_execute_time`, `execute_count`, `success_count`, `fail_count`, `timeout_seconds`, `retry_count`, `alert_email`) VALUES (13, 'admin', NOW(), 'system', NOW(), 10180, b'0', '隆基Jenkins构建同步', '', 2, 'jenkinsBuildServiceImpl', 'syncBuilds', NULL, '{\"externalSystemId\": 3}', '0 */2 * * * ?', 'ENABLED', b'0', NOW(), NOW(), 18, 18, 0, 300, 0, '');
|
||||
INSERT INTO `deploy-ease-platform`.`schedule_job` (`id`, `create_by`, `create_time`, `update_by`, `update_time`, `version`, `deleted`, `job_name`, `job_description`, `category_id`, `bean_name`, `method_name`, `form_definition_id`, `method_params`, `cron_expression`, `status`, `concurrent`, `last_execute_time`, `next_execute_time`, `execute_count`, `success_count`, `fail_count`, `timeout_seconds`, `retry_count`, `alert_email`) VALUES (14, 'admin', NOW(), 'admin', NOW(), 26, b'0', '隆基Git仓库组同步', '定期同步Git仓库组信息,每天凌晨2点执行', 2, 'repositoryGroupServiceImpl', 'syncGroups', NULL, '{\"externalSystemId\": 4}', '0 0 3 * * ?', 'ENABLED', b'0', NOW(), NOW(), 0, 0, 0, 3600, 2, '');
|
||||
INSERT INTO `deploy-ease-platform`.`schedule_job` (`id`, `create_by`, `create_time`, `update_by`, `update_time`, `version`, `deleted`, `job_name`, `job_description`, `category_id`, `bean_name`, `method_name`, `form_definition_id`, `method_params`, `cron_expression`, `status`, `concurrent`, `last_execute_time`, `next_execute_time`, `execute_count`, `success_count`, `fail_count`, `timeout_seconds`, `retry_count`, `alert_email`) VALUES (15, 'admin', NOW(), 'system', NOW(), 1211, b'0', '隆基Git项目同步', '定期同步Git项目信息,每天凌晨3点执行', 2, 'repositoryProjectServiceImpl', 'syncProjects', NULL, '{\"externalSystemId\": 4}', '0 */5 * * * ?', 'ENABLED', b'0', NOW(), NOW(), 7, 7, 0, 3600, 2, '');
|
||||
INSERT INTO `deploy-ease-platform`.`schedule_job` (`id`, `create_by`, `create_time`, `update_by`, `update_time`, `version`, `deleted`, `job_name`, `job_description`, `category_id`, `bean_name`, `method_name`, `form_definition_id`, `method_params`, `cron_expression`, `status`, `concurrent`, `last_execute_time`, `next_execute_time`, `execute_count`, `success_count`, `fail_count`, `timeout_seconds`, `retry_count`, `alert_email`) VALUES (16, 'admin', NOW(), 'system', NOW(), 5726, b'0', '隆基Git分支同步', '定期同步Git仓库分支信息,每5分钟执行一次', 2, 'repositoryBranchServiceImpl', 'syncBranches', NULL, '{\"externalSystemId\": 4}', '0 */5 * * * ?', 'ENABLED', b'0', NOW(), NOW(), 7, 7, 0, 3600, 2, '');
|
||||
INSERT INTO `deploy-ease-platform`.`schedule_job` (`id`, `create_by`, `create_time`, `update_by`, `update_time`, `version`, `deleted`, `job_name`, `job_description`, `category_id`, `bean_name`, `method_name`, `form_definition_id`, `method_params`, `cron_expression`, `status`, `concurrent`, `last_execute_time`, `next_execute_time`, `execute_count`, `success_count`, `fail_count`, `timeout_seconds`, `retry_count`, `alert_email`) VALUES (17, 'admin', NOW(), 'system', NOW(), 19, b'0', '服务器预警', '', 4, 'serverMonitorScheduler', 'collectServerMetrics', NULL, '{\"notificationChannelId\": 5, \"serverOfflineTemplateId\": 12, \"resourceAlertTemplateId\": 11}', '0 */5 * * * ?', 'ENABLED', b'0', NOW(), NOW(), 15, 15, 0, 300, 0, '');
|
||||
INSERT INTO `deploy-ease-platform`.`schedule_job` (`id`, `create_by`, `create_time`, `update_by`, `update_time`, `version`, `deleted`, `job_name`, `job_description`, `category_id`, `bean_name`, `method_name`, `form_definition_id`, `method_params`, `cron_expression`, `status`, `concurrent`, `last_execute_time`, `next_execute_time`, `execute_count`, `success_count`, `fail_count`, `timeout_seconds`, `retry_count`, `alert_email`) VALUES (2, 'admin', NOW(), 'admin', NOW(), 28, b'0', '链宇Git仓库组同步', '定期同步Git仓库组信息,每天凌晨2点执行', 2, 'repositoryGroupServiceImpl', 'syncGroups', NULL, '{\"externalSystemId\": 2}', '0 0 2 * * ?', 'DISABLED', b'0', NOW(), NOW(), 0, 0, 0, 3600, 2, '');
|
||||
INSERT INTO `deploy-ease-platform`.`schedule_job` (`id`, `create_by`, `create_time`, `update_by`, `update_time`, `version`, `deleted`, `job_name`, `job_description`, `category_id`, `bean_name`, `method_name`, `form_definition_id`, `method_params`, `cron_expression`, `status`, `concurrent`, `last_execute_time`, `next_execute_time`, `execute_count`, `success_count`, `fail_count`, `timeout_seconds`, `retry_count`, `alert_email`) VALUES (3, 'admin', NOW(), 'admin', NOW(), 30, b'0', '链宇Git项目同步', '定期同步Git项目信息,每天凌晨3点执行', 2, 'repositoryProjectServiceImpl', 'syncProjects', NULL, '{\"externalSystemId\": 2}', '0 0 3 * * ?', 'DISABLED', b'0', NOW(), NOW(), 0, 0, 0, 3600, 2, '');
|
||||
INSERT INTO `deploy-ease-platform`.`schedule_job` (`id`, `create_by`, `create_time`, `update_by`, `update_time`, `version`, `deleted`, `job_name`, `job_description`, `category_id`, `bean_name`, `method_name`, `form_definition_id`, `method_params`, `cron_expression`, `status`, `concurrent`, `last_execute_time`, `next_execute_time`, `execute_count`, `success_count`, `fail_count`, `timeout_seconds`, `retry_count`, `alert_email`) VALUES (4, 'admin', NOW(), 'admin', NOW(), 5726, b'0', '链宇Git分支同步', '定期同步Git仓库分支信息,每5分钟执行一次', 2, 'repositoryBranchServiceImpl', 'syncBranches', NULL, '{\"externalSystemId\": 2}', '0 */5 * * * ?', 'DISABLED', b'0', NOW(), NOW(), 7, 7, 0, 3600, 2, '');
|
||||
INSERT INTO `deploy-ease-platform`.`schedule_job` (`id`, `create_by`, `create_time`, `update_by`, `update_time`, `version`, `deleted`, `job_name`, `job_description`, `category_id`, `bean_name`, `method_name`, `form_definition_id`, `method_params`, `cron_expression`, `status`, `concurrent`, `last_execute_time`, `next_execute_time`, `execute_count`, `success_count`, `fail_count`, `timeout_seconds`, `retry_count`, `alert_email`) VALUES (8, 'admin', NOW(), 'admin', NOW(), 1210, b'0', '链宇Jenkins视图同步', '', 2, 'jenkinsViewServiceImpl', 'syncViews', NULL, '{\"externalSystemId\": 1}', '0 */5 * * * ?', 'DISABLED', b'0', NOW(), NOW(), 7, 7, 0, 300, 0, '');
|
||||
INSERT INTO `deploy-ease-platform`.`schedule_job` (`id`, `create_by`, `create_time`, `update_by`, `update_time`, `version`, `deleted`, `job_name`, `job_description`, `category_id`, `bean_name`, `method_name`, `form_definition_id`, `method_params`, `cron_expression`, `status`, `concurrent`, `last_execute_time`, `next_execute_time`, `execute_count`, `success_count`, `fail_count`, `timeout_seconds`, `retry_count`, `alert_email`) VALUES (9, 'admin', NOW(), 'admin', NOW(), 1210, b'0', '链宇Jenkins任务同步', '', 2, 'jenkinsJobServiceImpl', 'syncJobs', NULL, '{\"externalSystemId\": 1}', '0 */5 * * * ?', 'DISABLED', b'0', NOW(), NOW(), 7, 7, 0, 300, 0, '');
|
||||
INSERT INTO `deploy-ease-platform`.`schedule_job` (`id`, `create_by`, `create_time`, `update_by`, `update_time`, `version`, `deleted`, `job_name`, `job_description`, `category_id`, `bean_name`, `method_name`, `form_definition_id`, `method_params`, `cron_expression`, `status`, `concurrent`, `last_execute_time`, `next_execute_time`, `execute_count`, `success_count`, `fail_count`, `timeout_seconds`, `retry_count`, `alert_email`) VALUES (10, 'admin', NOW(), 'admin', NOW(), 10157, b'0', '链宇Jenkins构建同步', '', 2, 'jenkinsBuildServiceImpl', 'syncBuilds', NULL, '{\"externalSystemId\": 1}', '0 */2 * * * ?', 'DISABLED', b'0', NOW(), NOW(), 18, 18, 0, 300, 0, '');
|
||||
INSERT INTO `deploy-ease-platform`.`schedule_job` (`id`, `create_by`, `create_time`, `update_by`, `update_time`, `version`, `deleted`, `job_name`, `job_description`, `category_id`, `bean_name`, `method_name`, `form_definition_id`, `method_params`, `cron_expression`, `status`, `concurrent`, `last_execute_time`, `next_execute_time`, `execute_count`, `success_count`, `fail_count`, `timeout_seconds`, `retry_count`, `alert_email`) VALUES (11, 'admin', NOW(), 'admin', NOW(), 1211, b'0', '隆基Jenkins视图同步', '', 2, 'jenkinsViewServiceImpl', 'syncViews', NULL, '{\"externalSystemId\": 3}', '0 */5 * * * ?', 'DISABLED', b'0', NOW(), NOW(), 7, 7, 0, 300, 0, '');
|
||||
INSERT INTO `deploy-ease-platform`.`schedule_job` (`id`, `create_by`, `create_time`, `update_by`, `update_time`, `version`, `deleted`, `job_name`, `job_description`, `category_id`, `bean_name`, `method_name`, `form_definition_id`, `method_params`, `cron_expression`, `status`, `concurrent`, `last_execute_time`, `next_execute_time`, `execute_count`, `success_count`, `fail_count`, `timeout_seconds`, `retry_count`, `alert_email`) VALUES (12, 'admin', NOW(), 'admin', NOW(), 1211, b'0', '隆基Jenkins任务同步', '', 2, 'jenkinsJobServiceImpl', 'syncJobs', NULL, '{\"externalSystemId\": 3}', '0 */5 * * * ?', 'DISABLED', b'0', NOW(), NOW(), 7, 7, 0, 300, 0, '');
|
||||
INSERT INTO `deploy-ease-platform`.`schedule_job` (`id`, `create_by`, `create_time`, `update_by`, `update_time`, `version`, `deleted`, `job_name`, `job_description`, `category_id`, `bean_name`, `method_name`, `form_definition_id`, `method_params`, `cron_expression`, `status`, `concurrent`, `last_execute_time`, `next_execute_time`, `execute_count`, `success_count`, `fail_count`, `timeout_seconds`, `retry_count`, `alert_email`) VALUES (13, 'admin', NOW(), 'admin', NOW(), 10181, b'0', '隆基Jenkins构建同步', '', 2, 'jenkinsBuildServiceImpl', 'syncBuilds', NULL, '{\"externalSystemId\": 3}', '0 */2 * * * ?', 'DISABLED', b'0', NOW(), NOW(), 18, 18, 0, 300, 0, '');
|
||||
INSERT INTO `deploy-ease-platform`.`schedule_job` (`id`, `create_by`, `create_time`, `update_by`, `update_time`, `version`, `deleted`, `job_name`, `job_description`, `category_id`, `bean_name`, `method_name`, `form_definition_id`, `method_params`, `cron_expression`, `status`, `concurrent`, `last_execute_time`, `next_execute_time`, `execute_count`, `success_count`, `fail_count`, `timeout_seconds`, `retry_count`, `alert_email`) VALUES (14, 'admin', NOW(), 'admin', NOW(), 27, b'0', '隆基Git仓库组同步', '定期同步Git仓库组信息,每天凌晨2点执行', 2, 'repositoryGroupServiceImpl', 'syncGroups', NULL, '{\"externalSystemId\": 4}', '0 0 3 * * ?', 'DISABLED', b'0', NOW(), NOW(), 0, 0, 0, 3600, 2, '');
|
||||
INSERT INTO `deploy-ease-platform`.`schedule_job` (`id`, `create_by`, `create_time`, `update_by`, `update_time`, `version`, `deleted`, `job_name`, `job_description`, `category_id`, `bean_name`, `method_name`, `form_definition_id`, `method_params`, `cron_expression`, `status`, `concurrent`, `last_execute_time`, `next_execute_time`, `execute_count`, `success_count`, `fail_count`, `timeout_seconds`, `retry_count`, `alert_email`) VALUES (15, 'admin', NOW(), 'admin', NOW(), 1212, b'0', '隆基Git项目同步', '定期同步Git项目信息,每天凌晨3点执行', 2, 'repositoryProjectServiceImpl', 'syncProjects', NULL, '{\"externalSystemId\": 4}', '0 */5 * * * ?', 'DISABLED', b'0', NOW(), NOW(), 7, 7, 0, 3600, 2, '');
|
||||
INSERT INTO `deploy-ease-platform`.`schedule_job` (`id`, `create_by`, `create_time`, `update_by`, `update_time`, `version`, `deleted`, `job_name`, `job_description`, `category_id`, `bean_name`, `method_name`, `form_definition_id`, `method_params`, `cron_expression`, `status`, `concurrent`, `last_execute_time`, `next_execute_time`, `execute_count`, `success_count`, `fail_count`, `timeout_seconds`, `retry_count`, `alert_email`) VALUES (16, 'admin', NOW(), 'admin', NOW(), 5727, b'0', '隆基Git分支同步', '定期同步Git仓库分支信息,每5分钟执行一次', 2, 'repositoryBranchServiceImpl', 'syncBranches', NULL, '{\"externalSystemId\": 4}', '0 */5 * * * ?', 'DISABLED', b'0', NOW(), NOW(), 7, 7, 0, 3600, 2, '');
|
||||
INSERT INTO `deploy-ease-platform`.`schedule_job` (`id`, `create_by`, `create_time`, `update_by`, `update_time`, `version`, `deleted`, `job_name`, `job_description`, `category_id`, `bean_name`, `method_name`, `form_definition_id`, `method_params`, `cron_expression`, `status`, `concurrent`, `last_execute_time`, `next_execute_time`, `execute_count`, `success_count`, `fail_count`, `timeout_seconds`, `retry_count`, `alert_email`) VALUES (17, 'admin', NOW(), 'admin', NOW(), 20, b'0', '服务器预警', '', 4, 'serverMonitorScheduler', 'collectServerMetrics', NULL, '{\"notificationChannelId\": 5, \"serverOfflineTemplateId\": 12, \"resourceAlertTemplateId\": 11}', '0 */5 * * * ?', 'DISABLED', b'0', NOW(), NOW(), 15, 15, 0, 300, 0, '');
|
||||
|
||||
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user