优化SSH线程池
This commit is contained in:
parent
1a135d26cf
commit
44de0ca028
@ -85,7 +85,7 @@ public class ServerApiController
|
|||||||
return Response.success(result);
|
return Response.success(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operation(summary = "测试SSH连接并获取服务器信息", description = "测试服务器SSH连接并自动采集硬件信息")
|
@Operation(summary = "测试SSH连接", description = "轻量级连接测试,只验证连通性,不采集硬件信息(性能优化)")
|
||||||
@PostMapping("/{id}/test-connection")
|
@PostMapping("/{id}/test-connection")
|
||||||
public Response<ServerInfoDTO> testConnection(
|
public Response<ServerInfoDTO> testConnection(
|
||||||
@Parameter(description = "服务器ID", required = true) @PathVariable Long id
|
@Parameter(description = "服务器ID", required = true) @PathVariable Long id
|
||||||
@ -94,6 +94,15 @@ public class ServerApiController
|
|||||||
return Response.success(info);
|
return Response.success(info);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "采集服务器硬件信息", description = "采集并更新服务器硬件信息(hostname、CPU、内存、磁盘等)")
|
||||||
|
@PostMapping("/{id}/collect-hardware")
|
||||||
|
public Response<ServerInfoDTO> collectHardwareInfo(
|
||||||
|
@Parameter(description = "服务器ID", required = true) @PathVariable Long id
|
||||||
|
) {
|
||||||
|
ServerInfoDTO info = serverService.collectHardwareInfo(id);
|
||||||
|
return Response.success(info);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void exportData(HttpServletResponse response, List<ServerDTO> data) {
|
protected void exportData(HttpServletResponse response, List<ServerDTO> data) {
|
||||||
log.info("导出服务器数据,数据量:{}", data.size());
|
log.info("导出服务器数据,数据量:{}", data.size());
|
||||||
|
|||||||
@ -36,12 +36,18 @@ public class ServerMonitorDataDTO {
|
|||||||
@Schema(description = "各分区磁盘使用率")
|
@Schema(description = "各分区磁盘使用率")
|
||||||
private List<DiskUsageInfo> diskUsage;
|
private List<DiskUsageInfo> diskUsage;
|
||||||
|
|
||||||
@Schema(description = "网络接收流量(KB/s)")
|
@Schema(description = "网络接收累计流量(KB) - 用于数据库存储")
|
||||||
private Long networkRx;
|
private Long networkRx;
|
||||||
|
|
||||||
@Schema(description = "网络发送流量(KB/s)")
|
@Schema(description = "网络发送累计流量(KB) - 用于数据库存储")
|
||||||
private Long networkTx;
|
private Long networkTx;
|
||||||
|
|
||||||
|
@Schema(description = "网络接收速率(KB/s) - 用于告警检查")
|
||||||
|
private Long networkRxSpeed;
|
||||||
|
|
||||||
|
@Schema(description = "网络发送速率(KB/s) - 用于告警检查")
|
||||||
|
private Long networkTxSpeed;
|
||||||
|
|
||||||
@Schema(description = "采集时间")
|
@Schema(description = "采集时间")
|
||||||
private LocalDateTime collectTime;
|
private LocalDateTime collectTime;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,6 +11,7 @@ import jakarta.annotation.PostConstruct;
|
|||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
|
import java.math.RoundingMode;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 服务器监控阈值检测器工厂
|
* 服务器监控阈值检测器工厂
|
||||||
@ -45,8 +46,30 @@ public class ServerMonitorThresholdCheckerFactory extends ServerThresholdChecker
|
|||||||
.orElse(null);
|
.orElse(null);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// 注册网络检测器
|
// 注册网络检测器(读取 DTO 中已计算好的速率,取 Rx 和 Tx 速率的最大值)
|
||||||
checkers.put(MonitorMetricEnum.NETWORK,
|
checkers.put(MonitorMetricEnum.NETWORK,
|
||||||
new NetworkThresholdChecker<>(ServerMonitorDataDTO::getNetworkUsage));
|
new NetworkThresholdChecker<>(data -> {
|
||||||
|
if (data == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Long rxSpeed = data.getNetworkRxSpeed();
|
||||||
|
Long txSpeed = data.getNetworkTxSpeed();
|
||||||
|
|
||||||
|
// 如果速率都为null,返回null
|
||||||
|
if (rxSpeed == null && txSpeed == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 取最大值(KB/s)
|
||||||
|
long maxSpeedKBps = Math.max(
|
||||||
|
rxSpeed != null ? rxSpeed : 0,
|
||||||
|
txSpeed != null ? txSpeed : 0
|
||||||
|
);
|
||||||
|
|
||||||
|
// 转换为 MB/s(阈值配置单位)
|
||||||
|
return new BigDecimal(maxSpeedKBps)
|
||||||
|
.divide(new BigDecimal(1024), 2, RoundingMode.HALF_UP);
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -22,6 +22,11 @@ public interface IServerMonitorRepository extends JpaRepository<ServerMonitor, L
|
|||||||
List<ServerMonitor> findByServerIdAndCollectTimeBetweenOrderByCollectTimeDesc(
|
List<ServerMonitor> findByServerIdAndCollectTimeBetweenOrderByCollectTimeDesc(
|
||||||
Long serverId, LocalDateTime startTime, LocalDateTime endTime);
|
Long serverId, LocalDateTime startTime, LocalDateTime endTime);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询指定服务器的最新一条监控记录(用于计算网络流量速率)
|
||||||
|
*/
|
||||||
|
ServerMonitor findFirstByServerIdOrderByCollectTimeDesc(Long serverId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 删除指定时间之前的监控记录
|
* 删除指定时间之前的监控记录
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import com.qqchen.deploy.backend.deploy.dto.ServerMonitorDataDTO;
|
|||||||
import com.qqchen.deploy.backend.deploy.dto.ServerMonitorNotificationConfig;
|
import com.qqchen.deploy.backend.deploy.dto.ServerMonitorNotificationConfig;
|
||||||
import com.qqchen.deploy.backend.deploy.entity.Server;
|
import com.qqchen.deploy.backend.deploy.entity.Server;
|
||||||
import com.qqchen.deploy.backend.deploy.entity.ServerAlertRule;
|
import com.qqchen.deploy.backend.deploy.entity.ServerAlertRule;
|
||||||
|
import com.qqchen.deploy.backend.deploy.entity.ServerMonitor;
|
||||||
import com.qqchen.deploy.backend.deploy.enums.ServerStatusEnum;
|
import com.qqchen.deploy.backend.deploy.enums.ServerStatusEnum;
|
||||||
import com.qqchen.deploy.backend.deploy.repository.IServerAlertRuleRepository;
|
import com.qqchen.deploy.backend.deploy.repository.IServerAlertRuleRepository;
|
||||||
import com.qqchen.deploy.backend.deploy.repository.IServerRepository;
|
import com.qqchen.deploy.backend.deploy.repository.IServerRepository;
|
||||||
@ -297,25 +298,68 @@ public class ServerMonitorScheduler {
|
|||||||
.intValue();
|
.intValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. 转换为业务层DTO
|
// 3. 计算网络流量速率(用于告警检查)
|
||||||
|
Long networkRxSpeed = 0L;
|
||||||
|
Long networkTxSpeed = 0L;
|
||||||
|
|
||||||
|
if (monitorData.getNetworkRx() != null && monitorData.getNetworkTx() != null) {
|
||||||
|
// 查询上次监控数据(数据库中存储的是累计值)
|
||||||
|
ServerMonitor lastMonitor = monitorService.getLastMonitorData(server.getId());
|
||||||
|
|
||||||
|
if (lastMonitor != null && lastMonitor.getNetworkRx() != null && lastMonitor.getNetworkTx() != null) {
|
||||||
|
// 计算时间差(秒)
|
||||||
|
long timeDiffSeconds = java.time.Duration.between(
|
||||||
|
lastMonitor.getCollectTime(),
|
||||||
|
LocalDateTime.now()
|
||||||
|
).getSeconds();
|
||||||
|
|
||||||
|
if (timeDiffSeconds > 0) {
|
||||||
|
// 计算速率 = (当前累计值 - 上次累计值) / 时间差
|
||||||
|
long rxDiff = monitorData.getNetworkRx() - lastMonitor.getNetworkRx();
|
||||||
|
long txDiff = monitorData.getNetworkTx() - lastMonitor.getNetworkTx();
|
||||||
|
|
||||||
|
// 防止负数(服务器重启导致计数器重置)
|
||||||
|
if (rxDiff >= 0 && txDiff >= 0) {
|
||||||
|
networkRxSpeed = rxDiff / timeDiffSeconds; // KB/s
|
||||||
|
networkTxSpeed = txDiff / timeDiffSeconds; // KB/s
|
||||||
|
|
||||||
|
log.debug("网络速率计算: serverId={}, timeDiff={}s, rxSpeed={}KB/s, txSpeed={}KB/s",
|
||||||
|
server.getId(), timeDiffSeconds, networkRxSpeed, networkTxSpeed);
|
||||||
|
} else {
|
||||||
|
log.warn("检测到网络计数器重置(负差值): serverId={}, rxDiff={}, txDiff={}",
|
||||||
|
server.getId(), rxDiff, txDiff);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.warn("时间差异无效: serverId={}, timeDiff={}s", server.getId(), timeDiffSeconds);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.debug("首次采集或无历史数据: serverId={}, 速率设为0", server.getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 转换为业务层DTO
|
||||||
ServerMonitorDataDTO data = ServerMonitorDataDTO.builder()
|
ServerMonitorDataDTO data = ServerMonitorDataDTO.builder()
|
||||||
.serverId(server.getId())
|
.serverId(server.getId())
|
||||||
.cpuUsage(monitorData.getCpuUsage())
|
.cpuUsage(monitorData.getCpuUsage())
|
||||||
.memoryUsage(monitorData.getMemoryUsage())
|
.memoryUsage(monitorData.getMemoryUsage())
|
||||||
.memoryUsed(memoryUsed)
|
.memoryUsed(memoryUsed)
|
||||||
.diskUsage(monitorData.getDiskUsage())
|
.diskUsage(monitorData.getDiskUsage())
|
||||||
.networkRx(monitorData.getNetworkRx())
|
.networkRx(monitorData.getNetworkRx()) // ✅ 累计值(存数据库)
|
||||||
.networkTx(monitorData.getNetworkTx())
|
.networkTx(monitorData.getNetworkTx()) // ✅ 累计值(存数据库)
|
||||||
|
.networkRxSpeed(networkRxSpeed) // ✅ 速率(告警检查)
|
||||||
|
.networkTxSpeed(networkTxSpeed) // ✅ 速率(告警检查)
|
||||||
.collectTime(LocalDateTime.now())
|
.collectTime(LocalDateTime.now())
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
log.debug("服务器监控数据采集成功: serverId={}, cpu={}%, mem={}%, diskCount={}, netRx={}KB/s, netTx={}KB/s",
|
log.debug("服务器监控数据采集成功: serverId={}, cpu={}%, mem={}%, diskCount={}, netRxTotal={}KB, netTxTotal={}KB, netRxSpeed={}KB/s, netTxSpeed={}KB/s",
|
||||||
server.getId(),
|
server.getId(),
|
||||||
monitorData.getCpuUsage(),
|
monitorData.getCpuUsage(),
|
||||||
monitorData.getMemoryUsage(),
|
monitorData.getMemoryUsage(),
|
||||||
monitorData.getDiskUsage() != null ? monitorData.getDiskUsage().size() : 0,
|
monitorData.getDiskUsage() != null ? monitorData.getDiskUsage().size() : 0,
|
||||||
monitorData.getNetworkRx(),
|
monitorData.getNetworkRx(),
|
||||||
monitorData.getNetworkTx());
|
monitorData.getNetworkTx(),
|
||||||
|
networkRxSpeed,
|
||||||
|
networkTxSpeed);
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
package com.qqchen.deploy.backend.deploy.service;
|
package com.qqchen.deploy.backend.deploy.service;
|
||||||
|
|
||||||
import com.qqchen.deploy.backend.deploy.dto.ServerMonitorDataDTO;
|
import com.qqchen.deploy.backend.deploy.dto.ServerMonitorDataDTO;
|
||||||
|
import com.qqchen.deploy.backend.deploy.entity.ServerMonitor;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -19,4 +20,13 @@ public interface IServerMonitorService {
|
|||||||
* 删除指定时间之前的历史数据
|
* 删除指定时间之前的历史数据
|
||||||
*/
|
*/
|
||||||
int deleteOldData(LocalDateTime beforeTime);
|
int deleteOldData(LocalDateTime beforeTime);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询指定服务器的最新一条监控记录
|
||||||
|
* 用于计算网络流量速率
|
||||||
|
*
|
||||||
|
* @param serverId 服务器ID
|
||||||
|
* @return 最新的监控记录,如果没有则返回null
|
||||||
|
*/
|
||||||
|
ServerMonitor getLastMonitorData(Long serverId);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -22,12 +22,42 @@ public interface IServerService extends IBaseService<Server, ServerDTO, ServerQu
|
|||||||
ServerDTO initializeServerInfo(Long serverId, ServerInitializeDTO dto);
|
ServerDTO initializeServerInfo(Long serverId, ServerInitializeDTO dto);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 测试服务器SSH连接并获取服务器信息
|
* 测试服务器SSH连接(轻量级,只验证连通性)
|
||||||
* 通过SSH自动采集服务器硬件信息
|
*
|
||||||
|
* 用途:
|
||||||
|
* - 定时监控调用(不采集硬件信息,性能优化)
|
||||||
|
* - 快速检测服务器在线状态
|
||||||
|
* - 前端"测试连接"按钮
|
||||||
|
*
|
||||||
|
* 执行的操作:
|
||||||
|
* - 创建SSH连接并验证
|
||||||
|
* - 更新服务器状态(ONLINE/OFFLINE)
|
||||||
|
* - 更新最后连接时间
|
||||||
|
* - 不采集硬件信息(hostname、CPU等)
|
||||||
*
|
*
|
||||||
* @param serverId 服务器ID
|
* @param serverId 服务器ID
|
||||||
* @return 服务器详细信息(包含连接状态和硬件信息)
|
* @return 连接状态信息(只包含连通性、错误信息、响应时间)
|
||||||
*/
|
*/
|
||||||
ServerInfoDTO testConnection(Long serverId);
|
ServerInfoDTO testConnection(Long serverId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 采集服务器硬件信息(需要先确保服务器在线)
|
||||||
|
*
|
||||||
|
* 用途:
|
||||||
|
* - 首次添加服务器后调用(前端先 testConnection,成功后调用此方法)
|
||||||
|
* - 前端"刷新硬件信息"按钮
|
||||||
|
* - 服务器硬件升级后手动刷新
|
||||||
|
*
|
||||||
|
* 执行的操作:
|
||||||
|
* - 采集 hostname、OS版本、CPU核心数、内存大小、磁盘信息
|
||||||
|
* - 更新数据库中的硬件信息
|
||||||
|
*
|
||||||
|
* 前置条件:
|
||||||
|
* - 服务器必须在线(建议先调用 testConnection)
|
||||||
|
*
|
||||||
|
* @param serverId 服务器ID
|
||||||
|
* @return 完整的硬件信息
|
||||||
|
*/
|
||||||
|
ServerInfoDTO collectHardwareInfo(Long serverId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -318,8 +318,6 @@ public class RepositoryBranchServiceImpl extends BaseServiceImpl<RepositoryBranc
|
|||||||
updateBranchInfo(branch, externalSystemId, project.getId(), project.getRepoProjectId(), remoteBranch);
|
updateBranchInfo(branch, externalSystemId, project.getId(), project.getRepoProjectId(), remoteBranch);
|
||||||
branchesToUpdate.add(branch);
|
branchesToUpdate.add(branch);
|
||||||
log.debug("Branch {} has changes, will be updated", remoteBranch.getName());
|
log.debug("Branch {} has changes, will be updated", remoteBranch.getName());
|
||||||
} else {
|
|
||||||
log.debug("Branch {} has no changes, skipping update", remoteBranch.getName());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
processedBranches.add(remoteBranch.getName());
|
processedBranches.add(remoteBranch.getName());
|
||||||
|
|||||||
@ -51,6 +51,13 @@ public class ServerMonitorServiceImpl implements IServerMonitorService {
|
|||||||
return deletedCount;
|
return deletedCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ServerMonitor getLastMonitorData(Long serverId) {
|
||||||
|
ServerMonitor lastMonitor = monitorRepository.findFirstByServerIdOrderByCollectTimeDesc(serverId);
|
||||||
|
log.debug("查询最新监控数据: serverId={}, found={}", serverId, lastMonitor != null);
|
||||||
|
return lastMonitor;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 转换DTO为实体
|
* 转换DTO为实体
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -89,11 +89,13 @@ public class ServerServiceImpl
|
|||||||
return converter.toDto(updated);
|
return converter.toDto(updated);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
@Transactional
|
* 创建SSH连接的通用方法(封装重复逻辑)
|
||||||
public ServerInfoDTO testConnection(Long serverId) {
|
*
|
||||||
long startTime = System.currentTimeMillis();
|
* @param serverId 服务器ID
|
||||||
|
* @return SSH连接信息 {server, sshClient, sshService}
|
||||||
|
*/
|
||||||
|
private SSHConnectionContext createSSHConnection(Long serverId) throws Exception {
|
||||||
// 1. 查询服务器信息
|
// 1. 查询服务器信息
|
||||||
Server server = serverRepository.findById(serverId)
|
Server server = serverRepository.findById(serverId)
|
||||||
.orElseThrow(() -> new BusinessException(ResponseCode.DATA_NOT_FOUND));
|
.orElseThrow(() -> new BusinessException(ResponseCode.DATA_NOT_FOUND));
|
||||||
@ -109,69 +111,164 @@ public class ServerServiceImpl
|
|||||||
new Object[]{"请先选择操作系统类型"});
|
new Object[]{"请先选择操作系统类型"});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (server.getAuthType() == null) {
|
||||||
|
throw new BusinessException(ResponseCode.INVALID_PARAM,
|
||||||
|
new Object[]{"请选择认证方式"});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 获取对应OS的SSH命令服务
|
||||||
|
ISSHCommandService sshService = sshCommandServiceFactory.getService(server.getOsType());
|
||||||
|
|
||||||
|
// 4. 根据认证类型准备参数
|
||||||
|
String password = null;
|
||||||
|
String privateKey = null;
|
||||||
|
String passphrase = null;
|
||||||
|
|
||||||
|
switch (server.getAuthType()) {
|
||||||
|
case PASSWORD:
|
||||||
|
if (server.getSshPassword() == null || server.getSshPassword().isEmpty()) {
|
||||||
|
throw new BusinessException(ResponseCode.INVALID_PARAM,
|
||||||
|
new Object[]{"SSH密码不能为空"});
|
||||||
|
}
|
||||||
|
password = server.getSshPassword();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case KEY:
|
||||||
|
if (server.getSshPrivateKey() == null || server.getSshPrivateKey().isEmpty()) {
|
||||||
|
throw new BusinessException(ResponseCode.INVALID_PARAM,
|
||||||
|
new Object[]{"SSH私钥不能为空"});
|
||||||
|
}
|
||||||
|
privateKey = server.getSshPrivateKey();
|
||||||
|
passphrase = server.getSshPassphrase();
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new BusinessException(ResponseCode.INVALID_PARAM,
|
||||||
|
new Object[]{"不支持的认证类型: " + server.getAuthType()});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. 创建SSH连接
|
||||||
|
SSHClient sshClient = sshService.createConnection(
|
||||||
|
server.getHostIp(),
|
||||||
|
server.getSshPort(),
|
||||||
|
server.getSshUser(),
|
||||||
|
password,
|
||||||
|
privateKey,
|
||||||
|
passphrase
|
||||||
|
);
|
||||||
|
|
||||||
|
log.debug("SSH连接已创建: serverId={}, {}@{}:{}",
|
||||||
|
serverId, server.getSshUser(), server.getHostIp(), server.getSshPort());
|
||||||
|
|
||||||
|
return new SSHConnectionContext(server, sshClient, sshService);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SSH连接上下文(封装连接相关信息)
|
||||||
|
*/
|
||||||
|
private static class SSHConnectionContext {
|
||||||
|
final Server server;
|
||||||
|
final SSHClient sshClient;
|
||||||
|
final ISSHCommandService sshService;
|
||||||
|
|
||||||
|
SSHConnectionContext(Server server, SSHClient sshClient, ISSHCommandService sshService) {
|
||||||
|
this.server = server;
|
||||||
|
this.sshClient = sshClient;
|
||||||
|
this.sshService = sshService;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 测试服务器SSH连接(轻量级,只验证连通性)
|
||||||
|
*
|
||||||
|
* 性能优化:不采集硬件信息,减少SSH命令执行
|
||||||
|
*
|
||||||
|
* @param serverId 服务器ID
|
||||||
|
* @return 连接状态信息(只包含连通性、错误信息、响应时间)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public ServerInfoDTO testConnection(Long serverId) {
|
||||||
|
long startTime = System.currentTimeMillis();
|
||||||
ServerInfoDTO info = new ServerInfoDTO();
|
ServerInfoDTO info = new ServerInfoDTO();
|
||||||
SSHClient sshClient = null;
|
SSHConnectionContext context = null;
|
||||||
ISSHCommandService sshService = null;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 3. 获取对应OS的SSH命令服务
|
// 1. 创建SSH连接(使用通用方法 ✅)
|
||||||
sshService = sshCommandServiceFactory.getService(server.getOsType());
|
context = createSSHConnection(serverId);
|
||||||
log.info("使用{}服务测试连接: {}@{}:{} [认证方式: {}]",
|
|
||||||
server.getOsType(), server.getSshUser(), server.getHostIp(),
|
|
||||||
server.getSshPort(), server.getAuthType());
|
|
||||||
|
|
||||||
// 4. 根据认证类型创建SSH连接
|
// 2. 连接成功(不采集硬件信息 ✅)
|
||||||
if (server.getAuthType() == null) {
|
|
||||||
throw new BusinessException(ResponseCode.INVALID_PARAM,
|
|
||||||
new Object[]{"请选择认证方式"});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 根据认证类型传递对应的参数
|
|
||||||
String password = null;
|
|
||||||
String privateKey = null;
|
|
||||||
String passphrase = null;
|
|
||||||
|
|
||||||
switch (server.getAuthType()) {
|
|
||||||
case PASSWORD:
|
|
||||||
// 密码认证:只传密码
|
|
||||||
if (server.getSshPassword() == null || server.getSshPassword().isEmpty()) {
|
|
||||||
throw new BusinessException(ResponseCode.INVALID_PARAM,
|
|
||||||
new Object[]{"SSH密码不能为空"});
|
|
||||||
}
|
|
||||||
password = server.getSshPassword();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case KEY:
|
|
||||||
// 密钥认证:只传密钥和密码短语
|
|
||||||
if (server.getSshPrivateKey() == null || server.getSshPrivateKey().isEmpty()) {
|
|
||||||
throw new BusinessException(ResponseCode.INVALID_PARAM,
|
|
||||||
new Object[]{"SSH私钥不能为空"});
|
|
||||||
}
|
|
||||||
privateKey = server.getSshPrivateKey();
|
|
||||||
passphrase = server.getSshPassphrase();
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
throw new BusinessException(ResponseCode.INVALID_PARAM,
|
|
||||||
new Object[]{"不支持的认证类型: " + server.getAuthType()});
|
|
||||||
}
|
|
||||||
|
|
||||||
sshClient = sshService.createConnection(
|
|
||||||
server.getHostIp(),
|
|
||||||
server.getSshPort(),
|
|
||||||
server.getSshUser(),
|
|
||||||
password,
|
|
||||||
privateKey,
|
|
||||||
passphrase
|
|
||||||
);
|
|
||||||
|
|
||||||
// 5. 采集服务器信息
|
|
||||||
info.setConnected(true);
|
info.setConnected(true);
|
||||||
info.setHostname(sshService.getHostname(sshClient));
|
log.info("SSH连接测试成功: serverId={}", serverId);
|
||||||
info.setOsVersion(sshService.getOsVersion(sshClient));
|
|
||||||
info.setCpuCores(sshService.getCpuCores(sshClient));
|
// 3. 更新服务器状态(只更新状态和连接时间)
|
||||||
info.setMemorySize(sshService.getMemorySize(sshClient));
|
context.server.setStatus(ServerStatusEnum.ONLINE);
|
||||||
info.setDiskInfo(sshService.getDiskInfo(sshClient));
|
context.server.setLastConnectTime(LocalDateTime.now());
|
||||||
|
serverRepository.save(context.server);
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("测试连接失败: serverId={}, error={}", serverId, e.getMessage(), e);
|
||||||
|
|
||||||
|
// 连接失败,设置错误信息
|
||||||
|
info.setConnected(false);
|
||||||
|
info.setErrorMessage(e.getMessage());
|
||||||
|
|
||||||
|
// 尝试更新服务器状态为离线
|
||||||
|
try {
|
||||||
|
Server server = serverRepository.findById(serverId).orElse(null);
|
||||||
|
if (server != null) {
|
||||||
|
server.setStatus(ServerStatusEnum.OFFLINE);
|
||||||
|
serverRepository.save(server);
|
||||||
|
}
|
||||||
|
} catch (Exception ex) {
|
||||||
|
log.warn("更新服务器离线状态失败: serverId={}", serverId, ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
} finally {
|
||||||
|
// 4. 关闭SSH连接
|
||||||
|
if (context != null && context.sshService != null) {
|
||||||
|
context.sshService.closeConnection(context.sshClient);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. 设置响应时间
|
||||||
|
info.setResponseTime(System.currentTimeMillis() - startTime);
|
||||||
|
info.setConnectTime(LocalDateTime.now());
|
||||||
|
}
|
||||||
|
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 采集服务器硬件信息(需要先确保服务器在线)
|
||||||
|
*
|
||||||
|
* 前置条件:服务器必须在线(建议先调用 testConnection)
|
||||||
|
*
|
||||||
|
* @param serverId 服务器ID
|
||||||
|
* @return 完整的硬件信息
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public ServerInfoDTO collectHardwareInfo(Long serverId) {
|
||||||
|
long startTime = System.currentTimeMillis();
|
||||||
|
ServerInfoDTO info = new ServerInfoDTO();
|
||||||
|
SSHConnectionContext context = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 1. 创建SSH连接(使用通用方法 ✅)
|
||||||
|
context = createSSHConnection(serverId);
|
||||||
|
|
||||||
|
// 2. 验证服务器状态(建议先在线)
|
||||||
|
if (context.server.getStatus() == ServerStatusEnum.OFFLINE) {
|
||||||
|
log.warn("服务器当前离线,建议先调用testConnection: serverId={}", serverId);
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info("开始采集硬件信息: serverId={}", serverId);
|
||||||
|
|
||||||
|
// 3. 采集硬件信息
|
||||||
|
info.setConnected(true);
|
||||||
|
info.setHostname(context.sshService.getHostname(context.sshClient));
|
||||||
|
info.setOsVersion(context.sshService.getOsVersion(context.sshClient));
|
||||||
|
info.setCpuCores(context.sshService.getCpuCores(context.sshClient));
|
||||||
|
info.setMemorySize(context.sshService.getMemorySize(context.sshClient));
|
||||||
|
info.setDiskInfo(context.sshService.getDiskInfo(context.sshClient));
|
||||||
|
|
||||||
// 计算磁盘总容量
|
// 计算磁盘总容量
|
||||||
if (info.getDiskInfo() != null && !info.getDiskInfo().isEmpty()) {
|
if (info.getDiskInfo() != null && !info.getDiskInfo().isEmpty()) {
|
||||||
@ -181,41 +278,33 @@ public class ServerServiceImpl
|
|||||||
info.setDiskSize(totalSize);
|
info.setDiskSize(totalSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
log.info("服务器信息采集成功: serverId={}, hostname={}, cpu={}核, mem={}GB, disk={}GB, diskCount={}",
|
log.info("硬件信息采集成功: serverId={}, hostname={}, cpu={}核, mem={}GB, disk={}GB, diskCount={}",
|
||||||
serverId, info.getHostname(), info.getCpuCores(), info.getMemorySize(),
|
serverId, info.getHostname(), info.getCpuCores(), info.getMemorySize(),
|
||||||
info.getDiskSize(), info.getDiskInfo() != null ? info.getDiskInfo().size() : 0);
|
info.getDiskSize(), info.getDiskInfo() != null ? info.getDiskInfo().size() : 0);
|
||||||
|
|
||||||
// 6. 更新服务器信息到数据库
|
// 4. 更新数据库中的硬件信息
|
||||||
server.setStatus(ServerStatusEnum.ONLINE);
|
context.server.setHostname(info.getHostname());
|
||||||
server.setLastConnectTime(LocalDateTime.now());
|
context.server.setOsVersion(info.getOsVersion());
|
||||||
server.setHostname(info.getHostname());
|
context.server.setCpuCores(info.getCpuCores());
|
||||||
server.setOsVersion(info.getOsVersion());
|
context.server.setMemorySize(info.getMemorySize());
|
||||||
server.setCpuCores(info.getCpuCores());
|
context.server.setDiskSize(info.getDiskSize());
|
||||||
server.setMemorySize(info.getMemorySize());
|
context.server.setDiskInfo(info.getDiskInfo());
|
||||||
server.setDiskSize(info.getDiskSize());
|
serverRepository.save(context.server);
|
||||||
server.setDiskInfo(info.getDiskInfo());
|
|
||||||
serverRepository.save(server);
|
|
||||||
|
|
||||||
log.info("服务器状态已更新为ONLINE: serverId={}", serverId);
|
log.info("硬件信息已更新到数据库: serverId={}", serverId);
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("测试连接失败: serverId={}, error={}", serverId, e.getMessage(), e);
|
log.error("采集硬件信息失败: serverId={}, error={}", serverId, e.getMessage(), e);
|
||||||
|
|
||||||
// 连接失败,设置错误信息
|
|
||||||
info.setConnected(false);
|
info.setConnected(false);
|
||||||
info.setErrorMessage(e.getMessage());
|
info.setErrorMessage("采集硬件信息失败: " + e.getMessage());
|
||||||
|
|
||||||
// 更新服务器状态为离线
|
|
||||||
server.setStatus(ServerStatusEnum.OFFLINE);
|
|
||||||
serverRepository.save(server);
|
|
||||||
|
|
||||||
} finally {
|
} finally {
|
||||||
// 7. 关闭SSH连接
|
// 5. 关闭SSH连接
|
||||||
if (sshService != null) {
|
if (context != null && context.sshService != null) {
|
||||||
sshService.closeConnection(sshClient);
|
context.sshService.closeConnection(context.sshClient);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 8. 设置响应时间
|
// 6. 设置响应时间
|
||||||
info.setResponseTime(System.currentTimeMillis() - startTime);
|
info.setResponseTime(System.currentTimeMillis() - startTime);
|
||||||
info.setConnectTime(LocalDateTime.now());
|
info.setConnectTime(LocalDateTime.now());
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,15 +14,17 @@ spring:
|
|||||||
password: lianyu_123
|
password: lianyu_123
|
||||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||||
hikari:
|
hikari:
|
||||||
# 连接池最大连接数
|
# 连接池最大连接数(方案2:充足配置,生产环境)
|
||||||
maximum-pool-size: 10
|
# 峰值并发:服务器监控(150) + Jenkins同步(100) + Git同步(100) + Web请求(20) ≈ 370
|
||||||
# 最小空闲连接数
|
# 预留buffer,设置为200
|
||||||
minimum-idle: 5
|
maximum-pool-size: 200
|
||||||
|
# 最小空闲连接数(保持足够的空闲连接,减少高峰时创建连接的开销)
|
||||||
|
minimum-idle: 80
|
||||||
# 空闲连接超时时间,默认600000(10分钟)
|
# 空闲连接超时时间,默认600000(10分钟)
|
||||||
idle-timeout: 300000
|
idle-timeout: 300000
|
||||||
# 连接最大存活时间,默认1800000(30分钟)
|
# 连接最大存活时间,默认1800000(30分钟)
|
||||||
max-lifetime: 1800000
|
max-lifetime: 1800000
|
||||||
# 连接超时时间,默认30000(30秒)
|
# 连接超时时间(优化:防止长时间等待)
|
||||||
connection-timeout: 30000
|
connection-timeout: 30000
|
||||||
# 测试连接是否有效的查询语句
|
# 测试连接是否有效的查询语句
|
||||||
connection-test-query: SELECT 1
|
connection-test-query: SELECT 1
|
||||||
|
|||||||
@ -14,15 +14,17 @@ spring:
|
|||||||
password: Qichen5210523
|
password: Qichen5210523
|
||||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||||
hikari:
|
hikari:
|
||||||
# 连接池最大连接数
|
# 连接池最大连接数(方案2:充足配置)
|
||||||
maximum-pool-size: 10
|
# 峰值并发:服务器监控(150) + Jenkins同步(100) + Git同步(100) + Web请求(20) ≈ 370
|
||||||
# 最小空闲连接数
|
# 预留buffer,设置为200
|
||||||
minimum-idle: 5
|
maximum-pool-size: 200
|
||||||
|
# 最小空闲连接数(保持足够的空闲连接,减少高峰时创建连接的开销)
|
||||||
|
minimum-idle: 80
|
||||||
# 空闲连接超时时间,默认600000(10分钟)
|
# 空闲连接超时时间,默认600000(10分钟)
|
||||||
idle-timeout: 300000
|
idle-timeout: 300000
|
||||||
# 连接最大存活时间,默认1800000(30分钟)
|
# 连接最大存活时间,默认1800000(30分钟)
|
||||||
max-lifetime: 1800000
|
max-lifetime: 1800000
|
||||||
# 连接超时时间,默认30000(30秒)
|
# 连接超时时间(优化:防止长时间等待)
|
||||||
connection-timeout: 30000
|
connection-timeout: 30000
|
||||||
# 测试连接是否有效的查询语句
|
# 测试连接是否有效的查询语句
|
||||||
connection-test-query: SELECT 1
|
connection-test-query: SELECT 1
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user