告警设置、告警上报

This commit is contained in:
qiminbao
2025-09-11 18:33:14 +08:00
parent 2c23692f01
commit 2c29156748
12 changed files with 657 additions and 67 deletions
@@ -8,4 +8,6 @@ public interface AgentService {
void switchCollectStart(String data);
void switchCollectStop();
void alarmMonitor();
}
@@ -0,0 +1,9 @@
package com.tongran.agent.client.service;
import com.tongran.agent.client.core.vo.AlarmVO;
import java.util.List;
public interface AlarmService {
List<AlarmVO> alarmMonitor(long timestamp);
}
@@ -377,5 +377,19 @@ public class AgentServiceImpl implements AgentService {
GlobalConfig.switchHwCurrentPowerInterval = 300; //系统实时功率(mW)采集间隔
}
@Override
public void alarmMonitor() {
if(GlobalConfig.IS_ALARM){
//开启告警监控
long milli = AgentUtil.getMillisToNextMinute() + 60000;
dynamicTaskService.scheduleTask("alarmTask",
businessTasks::alarmTask, milli, GlobalConfig.ALARM_INTERVAL*1000L);
}else{
//关闭告警监控
dynamicTaskService.cancelTask("alarmTask");
}
}
}
@@ -0,0 +1,456 @@
package com.tongran.agent.client.service.impl;
import cn.hutool.core.collection.CollectionUtil;
import com.github.dockerjava.api.DockerClient;
import com.github.dockerjava.api.model.Container;
import com.github.dockerjava.core.DefaultDockerClientConfig;
import com.github.dockerjava.core.DockerClientImpl;
import com.github.dockerjava.httpclient5.ApacheDockerHttpClient;
import com.github.dockerjava.transport.DockerHttpClient;
import com.tongran.agent.client.core.config.GlobalConfig;
import com.tongran.agent.client.core.eo.AlarmEO;
import com.tongran.agent.client.core.eo.NativeNetworkInterfaceEO;
import com.tongran.agent.client.core.vo.AlarmVO;
import com.tongran.agent.client.service.AlarmService;
import com.tongran.agent.client.utils.AgentDataUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import oshi.SystemInfo;
import oshi.hardware.CentralProcessor;
import oshi.hardware.HardwareAbstractionLayer;
import oshi.hardware.NetworkIF;
import oshi.software.os.OSFileStore;
import oshi.software.os.OperatingSystem;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
@Service
public class AlarmServiceImpl implements AlarmService {
@Override
public List<AlarmVO> alarmMonitor(long timestamp) {
List<AlarmVO> alarmVOList = new ArrayList<>();
List<AlarmEO> list = GlobalConfig.ALARM_LIST.stream().filter(a -> a.isCollect() == true).collect(Collectors.toList());
if(CollectionUtil.isNotEmpty(list)){
for (AlarmEO alarmEO : list) {
String type = alarmEO.getType();
String threshold = alarmEO.getThreshold();
int compareType = alarmEO.getCompareType();
AlarmVO alarmVO = AlarmVO.builder().type(type).timestamp(timestamp).build();
switch(type) {
case "systemCpuUti": //CPU使用率
alarmVO.setResult(handleSystemCpuUti(threshold, compareType));
alarmVOList.add(alarmVO);
break;
case "memoryUtilization": //内存利用率
alarmVO.setResult(handleMemoryUtilization(threshold, compareType));
alarmVOList.add(alarmVO);
break;
case "systemSwapSizePercent": //可用交换空间百分比
alarmVO.setResult(handleSystemSwapSizePercent(threshold, compareType));
alarmVOList.add(alarmVO);
break;
case "systemUsersNum": //登录用户数
alarmVO.setResult(handleSystemUsersNum(threshold, compareType));
alarmVOList.add(alarmVO);
break;
case "vfsFsUtil": //挂载点的空间利用率
alarmVO.setResult(handleVfsFsUtil(threshold, compareType));
alarmVOList.add(alarmVO);
break;
case "netUtil": //网络带宽使用率
alarmVO.setResult(handleNetUtil(threshold, compareType));
alarmVOList.add(alarmVO);
break;
case "containerMemUtil": //容器内存使用率
alarmVO.setResult(handleContainerMemUtil(threshold, compareType));
alarmVOList.add(alarmVO);
break;
case "netIfStatus": //网络运行状态UP变down
alarmVO.setResult(handleNetIfStatus());
alarmVOList.add(alarmVO);
break;
case "extraPorts": //多余端口
alarmVO.setResult(handleExtraPorts(threshold));
alarmVOList.add(alarmVO);
break;
default: //其他
break;
}
}
}
return alarmVOList;
}
private String handleSystemCpuUti(String threshold, int compareType){
try {
SystemInfo si = new SystemInfo();
HardwareAbstractionLayer hal = si.getHardware();
CentralProcessor processor = hal.getProcessor();
long[] prevTicks = processor.getSystemCpuLoadTicks();
try {
TimeUnit.SECONDS.sleep(1); // 等待1秒
} catch (InterruptedException e) {
e.printStackTrace();
}
// 计算使用率
double cpuUsage = processor.getSystemCpuLoadBetweenTicks(prevTicks) * 100;
boolean isFalse = false;
if(StringUtils.isNotBlank(threshold)){
isFalse = compare(cpuUsage, threshold, compareType);
}
if(isFalse){
return "当前CPU使用率:"+cpuUsage +"%";
}
} catch (Exception e) {
e.printStackTrace();
}
return "";
}
private String handleMemoryUtilization(String threshold, int compareType){
try {
Map<String, Long> memInfo = AgentDataUtil.parseMemInfo();
long total = memInfo.get("MemTotal");
long available = memInfo.getOrDefault("MemAvailable",
memInfo.get("MemFree") +
memInfo.getOrDefault("Buffers", 0L) +
memInfo.getOrDefault("Cached", 0L) +
memInfo.getOrDefault("SReclaimable", 0L));
// 总内存使用率
double totalUsage = (double)(total - available) / total * 100;
boolean isFalse = false;
if(StringUtils.isNotBlank(threshold)){
isFalse = compare(totalUsage, threshold, compareType);
}
if(isFalse){
return "当前内存利用率:"+totalUsage +"%";
}
} catch (IOException e) {
e.printStackTrace();
}
return "";
}
private String handleSystemSwapSizePercent(String threshold, int compareType){
try {
Map<String, Long> memInfo = AgentDataUtil.parseMemInfo();
long swapTotal = memInfo.getOrDefault("SwapTotal", 0L);
long swapFree = memInfo.getOrDefault("SwapFree", 0L);
double swapSizePercent = (double) swapFree / swapTotal * 100; //可用交换空间百分比
boolean isFalse = false;
if(StringUtils.isNotBlank(threshold)){
isFalse = compare(swapSizePercent, threshold, compareType);
}
if(isFalse){
return "当前可用交换空间百分比:"+swapSizePercent +"%";
}
} catch (IOException e) {
e.printStackTrace();
}
return "";
}
private String handleSystemUsersNum(String threshold, int compareType){
SystemInfo si = new SystemInfo();
OperatingSystem os = si.getOperatingSystem();
int userNum = os.getSessions().size();
boolean isFalse = false;
if(StringUtils.isNotBlank(threshold)){
isFalse = compare(userNum, threshold, compareType);
}
if(isFalse){
return "当前登录用户数:"+userNum;
}
return "";
}
private String handleVfsFsUtil(String threshold, int compareType){
String result = "";
SystemInfo si = new SystemInfo();
for (OSFileStore fs : si.getOperatingSystem().getFileSystem().getFileStores()) {
String mount = fs.getMount();
long totalSpace = fs.getTotalSpace();
long freeSpace = fs.getFreeSpace();
double usagePercentage = totalSpace > 0 ?
(double) (totalSpace - freeSpace) / totalSpace * 100 : 0;
if(StringUtils.isNotBlank(threshold)){
boolean isFalse = compare(usagePercentage, threshold, compareType);
if(isFalse){
result +="挂载点:"+mount+",当前空间利用率:"+usagePercentage+"%";
}
}
}
return result;
}
private String handleNetUtil(String threshold, int compareType){
String result = "";
try {
SystemInfo si = new SystemInfo();
HardwareAbstractionLayer hal = si.getHardware();
// 获取所有网络接口
List<NetworkIF> networkIFs = hal.getNetworkIFs();
// 第一次采样
for (NetworkIF net : networkIFs) {
net.updateAttributes();
}
// 等待1秒
TimeUnit.SECONDS.sleep(1);
// 第二次采样并计算速率
for (NetworkIF net : networkIFs) {
// 只显示以太网接口
if (net.getName().startsWith("eth") ||
net.getName().startsWith("en") ||
net.getDisplayName().toLowerCase().contains("ethernet")) {
long prevBytesSent = net.getBytesSent();
net.updateAttributes();
long bytesSent = net.getBytesSent() - prevBytesSent;
// 获取最大带宽
double maxBandwidth = getInterfaceMaxBandwidth(net);
// 计算使用率百分比
double sentUsagePercentage = (bytesToMbps(bytesSent) / maxBandwidth) * 100;
boolean isFalse = false;
if(StringUtils.isNotBlank(threshold)){
isFalse = compare(sentUsagePercentage, threshold, compareType);
}
if(isFalse){
result += "网络接口:"+ net.getName() + ",当前发送流量带宽使用率:" + sentUsagePercentage +"%";
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
private String handleContainerMemUtil(String threshold, int compareType){
// 配置Docker客户端
DefaultDockerClientConfig config = DefaultDockerClientConfig.createDefaultConfigBuilder()
.withDockerHost("unix:///var/run/docker.sock")
.withDockerTlsVerify(false) // 根据你的配置调整
.build();
DockerHttpClient httpClient = new ApacheDockerHttpClient.Builder()
.dockerHost(config.getDockerHost())
.sslConfig(config.getSSLConfig())
.maxConnections(100)
.connectionTimeout(Duration.ofSeconds(30))
.responseTimeout(Duration.ofSeconds(45))
.build();
DockerClient dockerClient = DockerClientImpl.getInstance(config, httpClient);
String result = "";
try {
// 获取正在运行的容器列表
List<Container> containers = dockerClient.listContainersCmd()
.withShowAll(false) // 只显示运行中的容器
.exec();
// 打印容器信息
System.out.println("运行中的Docker容器:");
System.out.println("容器ID\t\t镜像\t\t状态\t\t名称");
for (Container container : containers) {
String id = container.getId().substring(0, 12); // 只显示短ID
String name = container.getNames()[0].replaceFirst("/", "");
// 执行 docker stats 命令(--no-stream 表示只输出一次)
Process process = new ProcessBuilder(
"docker", "stats", "--no-stream", id,
"--format", "'table {{.ID}}\t{{.Name}}\t{{.CPUPerc}}\t{{.MemPerc}}\t{{.NetIO}}"
).start();
// 读取命令输出
BufferedReader reader = new BufferedReader(
new InputStreamReader(process.getInputStream())
);
String line;
while ((line = reader.readLine()) != null) {
String[] parts = line.trim().split("\\s+");
String memUtil = parts[4]; // 内存使用率
memUtil = memUtil.replace("%","");
if(StringUtils.isNotBlank(memUtil)){
boolean isFalse = false;
if(StringUtils.isNotBlank(threshold)){
isFalse = compare(Double.parseDouble(memUtil), threshold, compareType);
}
if(isFalse){
result += "容器:"+name+",当前内存使用率"+memUtil+"%";
}
}
}
// 等待命令执行完成并获取退出码
int exitCode = process.waitFor();
if (exitCode != 0) {
System.err.println("命令执行失败,退出码: " + exitCode);
}
reader.close();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
dockerClient.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return result;
}
private String handleNetIfStatus(){
String result = "";
try {
List<NativeNetworkInterfaceEO> list = getAllNetworkInterfaces();
if(CollectionUtil.isNotEmpty(GlobalConfig.NET_LIST)){
List<String> natList = list.stream().filter(n -> n.isUp() == false).map(NativeNetworkInterfaceEO::getName).collect(Collectors.toList());
List<NativeNetworkInterfaceEO> temp = GlobalConfig.NET_LIST.stream().filter(n -> n.isUp())
.collect(Collectors.toList());
for (NativeNetworkInterfaceEO n : temp) {
if(natList.contains(n.getName())){
result += "网络接口:"+n.getName()+",运行状态由UP转为DOWN";
}
}
}
GlobalConfig.NET_LIST = list;
} catch (SocketException e) {
System.err.println("获取网络接口信息失败: " + e.getMessage());
}
return result;
}
private String handleExtraPorts(String threshold){
String result = "";
try {
List<Integer> list = scanPortsUsingNmap();
for (Integer port : list) {
boolean flag = verifyPort(port, threshold);
if(!flag){
result += "端口:"+port+"已开放;";
}
}
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
private boolean compare(double value, String threshold, int compareType){
boolean isFalse = false;
switch(compareType) {
case 1: //大于且等于
if(value >= Double.parseDouble(threshold)){
isFalse = true;
}
break;
case 2: //小于
if(value < Double.parseDouble(threshold)){
isFalse = true;
}
break;
case 3: //小于且等于
if(value <= Double.parseDouble(threshold)){
isFalse = true;
}
break;
default: //大于
if(value > Double.parseDouble(threshold)){
isFalse = true;
}
break;
}
return isFalse;
}
private static double bytesToMbps(long bytes) {
return bytes * 8.0 / 1_000_000; // bytes to megabits
}
/**
* 获取接口的理论最大带宽(Mbps)
*/
private double getInterfaceMaxBandwidth(NetworkIF netIf) {
String name = netIf.getName().toLowerCase();
long speed = netIf.getSpeed(); // 获取接口速度(bps
if (speed > 0) {
return speed / 1_000_000.0; // 转换为Mbps
}
// 如果无法获取速度,根据常见接口类型估算
if (name.contains("eth") || name.contains("en") || name.contains("gigabit")) {
return 1000.0; // 千兆以太网
} else if (name.contains("10g") || name.contains("10000")) {
return 10000.0; // 万兆以太网
} else {
return 100.0; // 默认百兆以太网
}
}
/**
* 获取所有网络接口信息
*/
public List<NativeNetworkInterfaceEO> getAllNetworkInterfaces() throws SocketException {
List<NativeNetworkInterfaceEO> interfaces = new ArrayList<>();
Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();
while (networkInterfaces.hasMoreElements()) {
NetworkInterface ni = networkInterfaces.nextElement();
NativeNetworkInterfaceEO interfaceInfo = NativeNetworkInterfaceEO.builder()
.name(ni.getName())
.displayName(ni.getDisplayName())
.up(ni.isUp())
.build();
interfaces.add(interfaceInfo);
}
return interfaces;
}
/**
* 获取所有开放端口
*/
public List<Integer> scanPortsUsingNmap() throws Exception {
List<Integer> openPorts = new ArrayList<>();
Process process = Runtime.getRuntime().exec("nmap -p 1-65535 localhost");
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line;
Pattern pattern = Pattern.compile("\\d+/tcp open"); // Adjust the regex based on nmap output format you expect
while ((line = reader.readLine()) != null) {
Matcher matcher = pattern.matcher(line);
if (matcher.find()) { // Assuming the output format is "port/protocol state" where state is "open" for open ports
String portStr = matcher.group(0).split("/")[0]; // Extract the port number part before "/tcp" or "/udp" etc.
openPorts.add(Integer.parseInt(portStr)); // Add the port number to the list of open ports
}
}
reader.close();
return openPorts;
}
public boolean verifyPort(int port, String threshold){
String[] ports = threshold.split(";");
for (int i = 0; i < ports.length; i++) {
String p = ports[i];
int start = 0;
int end = 0;
String[] arr = p.split("-");
start = Integer.parseInt(arr[0]);
if(arr.length == 2){
end = Integer.parseInt(arr[1]);
}
if(start <= port && port <= end){
return true;
}
}
return false;
}
}