初始化

This commit is contained in:
qiminbao
2025-09-10 15:36:49 +08:00
commit 4330ffdff1
75 changed files with 6674 additions and 0 deletions
+149
View File
@@ -0,0 +1,149 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.6</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.tongran.agent</groupId>
<artifactId>tr-agent-client</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>tr-agent-client</name>
<description>tr-agent-client</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.7.11</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.86.Final</version>
</dependency>
<!-- <dependency>-->
<!-- <groupId>io.swagger.core.v3</groupId>-->
<!-- <artifactId>swagger-annotations</artifactId>-->
<!-- <version>2.2.19</version> &lt;!&ndash; 请检查并使用最新版本 &ndash;&gt;-->
<!-- </dependency>-->
<dependency>
<groupId>com.github.oshi</groupId>
<artifactId>oshi-core</artifactId>
<version>6.4.4</version>
</dependency>
<dependency>
<groupId>com.github.docker-java</groupId>
<artifactId>docker-java</artifactId>
<version>3.3.0</version>
</dependency>
<!-- Docker Java API -->
<dependency>
<groupId>com.github.docker-java</groupId>
<artifactId>docker-java-api</artifactId>
<version>3.3.0</version>
</dependency>
<!-- 使用 Apache HttpClient 5 实现 -->
<dependency>
<groupId>com.github.docker-java</groupId>
<artifactId>docker-java-transport-httpclient5</artifactId>
<version>3.3.0</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
<version>2.0.31</version>
</dependency>
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-openapi3-spring-boot-starter</artifactId>
<version>4.5.0</version>
</dependency>
<!-- SNMP4J 核心库 -->
<dependency>
<groupId>org.snmp4j</groupId>
<artifactId>snmp4j</artifactId>
<version>2.8.9</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.36</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>1.7.36</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
@@ -0,0 +1,17 @@
package com.tongran.agent.client;
import com.tongran.agent.client.core.config.GlobalConfig;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import java.time.LocalDateTime;
@SpringBootApplication
public class TrAgentClientApplication {
public static void main(String[] args) {
GlobalConfig.startupTime = System.currentTimeMillis();
SpringApplication.run(TrAgentClientApplication.class, args);
}
}
@@ -0,0 +1,29 @@
package com.tongran.agent.client.core.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Component
@ConfigurationProperties(prefix = "spring.application")
public class ApplicationProperties {
private String name;
private String version;
// Getter 和 Setter 方法
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getVersion() {
return version;
}
public void setVersion(String version) {
this.version = version;
}
}
@@ -0,0 +1,140 @@
package com.tongran.agent.client.core.config;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* 全局配置类
*/
public class GlobalConfig {
/**
* 服务启动时间
*/
public static long startupTime;
/**
* 客户端ID - 对应平台唯一SN
*/
public static String CLIENT_ID;
/**
* 交换机
*/
public static String SWITCH_COMMUNITY; //团名
public static String SWITCH_IP; //交换机IP
public static int SWITCH_PORT; //交换机端口
public static LinkedHashMap<String, String> SWITCH_NET_OID; //交换机网络端口发现OID
public static LinkedHashMap<String, String> SWITCH_MODULE_OID; //交换机光模块发现OID
public static LinkedHashMap<String, String> SWITCH_MPU_OID; //交换机MPU发现OID
public static LinkedHashMap<String, String> SWITCH_PWR_OID; //交换机电源发现OID
public static LinkedHashMap<String, String> SWITCH_FAN_OID; //交换机风扇发现OID
public static LinkedHashMap<String, String> SWITCH_OTHER_OID; //交换机系统其他OID
/**
* 采集标识
*/
public static boolean isCollect = false;
public static Map<String, Long> taskIds = new ConcurrentHashMap<>();
/**
* 系统监控
*/
public static boolean cpuCollect = false; //cpu采集
public static long cpuInterval = 300; //cpu采集间隔
public static boolean vfsCollect = false; //挂载采集
public static long vfsInterval = 300; //挂载采集间隔
public static boolean netCollect = false; //网络采集
public static long netInterval = 300; //网络采集间隔
public static boolean diskCollect = false; //硬盘采集
public static long diskInterval = 300; //硬盘采集间隔
public static boolean dockerCollect = false; //docker采集
public static long dockerInterval = 300; //docker采集间隔
/**
* 系统其他监控
*/
public static boolean systemSwapSizeFreeCollect = false; //交换卷/文件的可用空间(字节)采集
public static long systemSwapSizeFreeInterval = 300; //交换卷/文件的可用空间(字节)采集间隔
public static boolean memoryUtilizationCollect = false; //内存利用率采集
public static long memoryUtilizationInterval = 300; //内存利用率采集间隔
public static boolean systemSwapSizePercentCollect = false; //可用交换空间百分比采集
public static long systemSwapSizePercentInterval = 300; //可用交换空间百分比采集间隔
public static boolean memorySizeAvailableCollect = false; //可用内存采集
public static long memorySizeAvailableInterval = 300; //可用内存采集间隔
public static boolean memorySizePercentCollect = false; //可用内存百分比采集
public static long memorySizePercentInterval = 300; //可用内存百分比采集间隔
public static boolean memorySizeTotalCollect = false; //总内存采集
public static long memorySizeTotalInterval = 300; //总内存采集间隔
public static boolean systemSwOsCollect = false; //操作系统采集
public static long systemSwOsInterval = 300; //操作系统采集间隔
public static boolean systemSwArchCollect = false; //操作系统架构采集
public static long systemSwArchInterval = 300; //操作系统架构采集间隔
public static boolean kernelMaxprocCollect = false; //最大进程数采集
public static long kernelMaxprocInterval = 300; //最大进程数采集间隔
public static boolean procNumRunCollect = false; //正在运行的进程数采集
public static long procNumRunInterval = 300; //正在运行的进程数采集间隔
public static boolean systemUsersNumCollect = false; //登录用户数采集
public static long systemUsersNumInterval = 300; //登录用户数采集间隔
public static boolean systemDiskSizeTotalCollect = false; ///硬盘总可用空间采集
public static long systemDiskSizeTotalInterval = 300; //硬盘总可用空间采集间隔
public static boolean systemBoottimeCollect = false; //系统启动时间采集
public static long systemBoottimeInterval = 300; //系统启动时间采集间隔
public static boolean systemUnameCollect = false; //系统描述采集
public static long systemUnameInterval = 300; //系统描述采集间隔
public static boolean systemLocaltimeCollect = false; //系统本地时间采集
public static long systemLocaltimeInterval = 300; //系统本地时间采集间隔
public static boolean systemUptimeCollect = false; //系统正常运行时间采集
public static long systemUptimeInterval = 300; //系统正常运行时间采集间隔
public static boolean procNumCollect = false; //进程数采集
public static long procNumInterval = 300; //进程数采集间隔
/**
* 交换机监控
*/
public static boolean switchNetCollect = false; //交换机网络采集
public static long switchNetInterval = 300; //交换机网络采集间隔
public static boolean switchModuleCollect = false; //光模块采集
public static long switchModuleInterval = 300; //光模块采集间隔
public static boolean switchMpuCollect = false; //MPU采集
public static long switchMpuInterval = 300; //MPU采集间隔
public static boolean switchPwrCollect = false; //电源采集
public static long switchPwrInterval = 300; //电源采集间隔
public static boolean switchFanCollect = false; //风扇采集
public static long switchFanInterval = 300; //风扇采集间隔
/**
* 交换机其他监控
*/
public static boolean switchSysDescrCollect = false; //系统描述采集
public static long switchSysDescrInterval = 300; //系统描述采集间隔
public static boolean switchSysObjectIDCollect = false; //系统Object ID采集
public static long switchSysObjectIDInterval = 300; //系统Object ID采集间隔
public static boolean switchSysUpTimeCollect = false; //系统运行时间采集
public static long switchSysUpTimeInterval = 300; //系统运行时间采集间隔
public static boolean switchSysContactCollect = false; //系统联系信息采集
public static long switchSysContactInterval = 300; //系统联系信息采集间隔
public static boolean switchSysNameCollect = false; //系统名称采集
public static long switchSysNameInterval = 300; //系统名称采集间隔
public static boolean switchSysLocationCollect = false; //系统位置采集
public static long switchSysLocationInterval = 300; //系统位置采集间隔
public static boolean switchHwStackSystemMacCollect = false; //系统MAC地址采集
public static long switchHwStackSystemMacInterval = 300; //系统MAC地址采集间隔
public static boolean switchEntIndexCollect = false; //设备索引采集
public static long switchEntIndexInterval = 300; //设备索引采集间隔
public static boolean switchEntPhysicalNameCollect = false; //设备名称采集
public static long switchEntPhysicalNameInterval = 300; //设备名称采集间隔
public static boolean switchEntPhysicalSoftwareRevCollect = false; //设备软件版本采集
public static long switchEntPhysicalSoftwareRevInterval = 300; //设备软件版本采集间隔
public static boolean switchHwEntityCpuUsageCollect = false; //设备CPU使用率(%)采集
public static long switchHwEntityCpuUsageInterval = 300; //设备CPU使用率(%)采集间隔
public static boolean switchHwEntityMemUsageCollect = false; //设备内存使用率(%)采集
public static long switchHwEntityMemUsageInterval = 300; //设备内存使用率(%)采集间隔
public static boolean switchHwAveragePowerCollect = false; //系统平均功率(mW)采集
public static long switchHwAveragePowerInterval = 300; //系统平均功率(mW)采集间隔
public static boolean switchHwCurrentPowerCollect = false; //系统实时功率(mW)采集
public static long switchHwCurrentPowerInterval = 300; //系统实时功率(mW)采集间隔
}
@@ -0,0 +1,73 @@
package com.tongran.agent.client.core.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
/**
* 控制码
*/
@Getter
@AllArgsConstructor
@NoArgsConstructor
public enum MsgEnum {
注册("REGISTER"),
注册应答("REGISTER_RSP"),
断开("DISCONNECT"),
断开应答("DISCONNECT_RSP"),
心跳上报("HEARTBEAT"),
CPU上报("CPU"),
磁盘上报("DISK"),
容器上报("DOCKER"),
内存上报("MEMORY"),
网络上报("NET"),
挂载上报("POINT"),
交换机上报("SWITCHBOARD"),
系统其他上报("OTHER_SYSTEM"),
开启系统采集("SYSTEM_COLLECT_START"),
开启系统采集应答("SYSTEM_COLLECT_START_RSP"),
关闭系统采集("SYSTEM_COLLECT_STOP"),
关闭系统采集应答("SYSTEM_COLLECT_STOP_RSP"),
开启交换机采集("SWITCH_COLLECT_START"),
开启交换机采集应答("SWITCH_COLLECT_START_RSP"),
关闭交换机采集("SWITCH_COLLECT_STOP"),
关闭交换机采集应答("SWITCH_COLLECT_STOP_RSP"),
告警设置("ALARM_SET"),
告警设置应答("ALARM_SET_RSP"),
执行脚本策略("SCRIPT_POLICY"),
执行脚本策略应答("SCRIPT_POLICY_RSP"),
Agent版本更新("AGENT_VERSION_UPDATE"),
Agent版本更新应答("AGENT_VERSION_UPDATE_RSP");
private String value;
}
@@ -0,0 +1,35 @@
package com.tongran.agent.client.core.eo;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
import java.util.List;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class AgentVersionUpdateEO {
//文件地址,外网HTTPS)地址
private String fileUrl;
//保存文件地址
private String filePath;
//命令
private List<String> commands;
//执行方式:0、立即执行;1、定时执行;
private int method;
//定时时间,执行方式为1、定时执行时该字段必传
private LocalDateTime policyTime;
//时间戳
private long timestamp;
}
@@ -0,0 +1,16 @@
package com.tongran.agent.client.core.eo;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class CollectEO {
private String type;
private boolean collect = false;
private int interval = 300;
}
@@ -0,0 +1,58 @@
package com.tongran.agent.client.core.eo;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.time.LocalDateTime;
import java.util.List;
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class ScriptPolicyEO implements Serializable {
private static final long serialVersionUID = -1267013167162440612L;
/**
* 文件
*/
private List<ScriptFile> files;
//目标路径地址
private String filePath;
//命令
private List<String> commands;
//执行方式:0、立即执行;1、定时执行;
private int method;
//执行方式为1、定时执行时必传-指定时间
private LocalDateTime policyTime;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public static class ScriptFile{
//文件类型:0、平台文件地址;1、外网HTTP(S)
private int fileType;
//文件类型为1、外网HTTP(S)时必传
private String fileUrl;
//文件类型为0、平台文件地址时必传(文件名)
private String fileName;
//文件类型为0、平台文件地址时必传(文件流)
private String fileData;
}
}
@@ -0,0 +1,85 @@
package com.tongran.agent.client.core.session;
import io.netty.channel.ChannelHandlerContext;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
/**
* SESSION
*
*/
@Data
@Builder
@Accessors(chain = true)
@AllArgsConstructor
@NoArgsConstructor
public class Session implements Serializable {
private static final long serialVersionUID = 1L;
/**
* sessionId
*/
private String sessionId;
/**
* 客户端唯一标识
*/
private String clientId;
/**
* Seesion额外参数
*/
private ConcurrentHashMap<String,Object> extraMap;
/**
* 是否鉴权
*/
@Builder.Default
private boolean isAuthenticated = false;
@Builder.Default
private long createTime = System.currentTimeMillis();
@Builder.Default
private long updateTime = System.currentTimeMillis();
/**
* 消息渠道
*/
private ChannelHandlerContext channel;
@Builder.Default
private AtomicInteger serialNo = new AtomicInteger(0);
public static String buildSessionId(ChannelHandlerContext channel) {
return channel.channel().id().asLongText();
}
public static Session buildSession(ChannelHandlerContext channel, String clientId) {
return Session.builder().channel(channel).sessionId(buildSessionId(channel)).clientId(clientId).build();
}
/**
* 自生成流水号
*
* @return
*/
public int nextSerialNo() {
int current;
int next;
do {
current = serialNo.get();
next = current > 0xffff ? 0 : current;
} while (!serialNo.compareAndSet(current, next + 1));
return next;
}
}
@@ -0,0 +1,137 @@
package com.tongran.agent.client.core.session;
import cn.hutool.cache.Cache;
import cn.hutool.cache.CacheUtil;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import com.tongran.agent.client.exception.ServerException;
import com.tongran.agent.client.utils.AssertLog;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.util.Attribute;
import io.netty.util.AttributeKey;
import lombok.Data;
import org.apache.commons.lang3.StringUtils;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@Data
public class SessionManager {
private static volatile SessionManager instance = null;
// clientId-Session
private final Map<String, Session> sessionMap = new ConcurrentHashMap<>();
private final Cache<String, Object> sessionCache = CacheUtil.newTimedCache(10 * 60 * 1000);
public static SessionManager getInstance() {
if (instance == null) {
synchronized (SessionManager.class) {
if (instance == null) {
instance = new SessionManager();
}
}
}
return instance;
}
public synchronized void put(String clientId, Session session) {
if (StringUtils.isNotBlank(session.getClientId())) {
sessionMap.put(session.getClientId(), session);
}
}
public synchronized void remove(String clientId) {
if (StringUtils.isNotBlank(clientId)) {
sessionMap.remove(clientId);
}
}
public synchronized void remove(Session session) {
if (session != null && StringUtils.isNotBlank(session.getClientId())) {
sessionMap.remove(session.getClientId(), session);
}
}
public synchronized void remove(Channel channel) {
remove(getSessionByChannel(channel));
}
public Session getSessionById(String clientId) {
return sessionMap.get(clientId);
}
public Session getSessionByChannel(Channel channel) {
Session session = new Session();
sessionMap.values().forEach(s -> {
if (s.getChannel().channel() == channel) {
BeanUtil.copyProperties(s, session, true);
}
});
return session;
}
public boolean containsSession(String clientId) {
return sessionMap.containsKey(clientId);
}
public boolean containsSession(Session session) {
return sessionMap.containsValue(session);
}
public void setSessionCache(String clientId, Object value) {
sessionCache.put(clientId, value);
}
public Object getSessionCache(String clientId) {
return sessionCache.get(clientId);
}
public String client(ChannelHandlerContext ctx) {
Channel channel = ctx.channel();
Session session = this.getSessionByChannel(channel);
if (ObjectUtil.isNotNull(session) && StringUtils.isNotBlank(session.getClientId())) {
return channel.remoteAddress().toString() + "/" + session.getClientId();
}
return channel.remoteAddress().toString();
}
/**
* 根据channel生成流水号
*
* @param channel
* @return
*/
public short getSerialNumber(Channel channel, AttributeKey<Short> serialNumber) {
Attribute<Short> flowIdAttr = channel.attr(serialNumber);
Short flowId = flowIdAttr.get();
if (flowId == null) {
flowId = 0;
} else {
flowId++;
}
flowIdAttr.set(flowId);
return flowId;
}
public void writeAndFlush(String clientId, Object msg) {
Session session = this.getSessionById(clientId);
if (ObjectUtil.isNotNull(session)) {
this.writeAndFlush(session.getChannel(), msg);
} else {
throw new ServerException(500, "终端:" + clientId + "离线或者不存在");
}
}
public void writeAndFlush(ChannelHandlerContext ctx, Object msg) {
ctx.writeAndFlush(msg).addListener(future -> {
if (!future.isSuccess()) {
AssertLog.error("消息发送失败:{}", future.cause());
}
});
}
}
@@ -0,0 +1,60 @@
package com.tongran.agent.client.core.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
import java.io.Serializable;
@Data
@SuperBuilder
@AllArgsConstructor
@NoArgsConstructor
@Schema(description = "CPU信息")
public class CpuVO implements Serializable {
private static final long serialVersionUID = 1L;
@Schema(description = "CPU1分钟负载")
private double avg1;
@Schema(description = "CPU5分钟负载")
private double avg5;
@Schema(description = "CPU15分钟负载")
private double avg15;
@Schema(description = "CPU硬件中断提供服务时间")
private long interrupt;
@Schema(description = "CPU使用率%")
private double uti;
@Schema(description = "CPU数量")
private int num;
@Schema(description = "CPU正常运行时间/秒")
private long normal;
@Schema(description = "CPU空闲时间")
private long idle;
@Schema(description = "CPU等待响应时间")
private double iowait;
@Schema(description = "CPU系统时间")
private long system;
@Schema(description = "CPU软件无响应时间")
private double noresp;
@Schema(description = "CPU用户进程所花费的时间")
private long user;
@Schema(description = "时间戳")
private long timestamp;
}
@@ -0,0 +1,50 @@
package com.tongran.agent.client.core.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
import java.io.Serializable;
@Data
@SuperBuilder
@AllArgsConstructor
@NoArgsConstructor
@Schema(description = "磁盘信息")
public class DiskVO implements Serializable {
private static final long serialVersionUID = 5L;
@Schema(description = "磁盘名称")
private String name;
@Schema(description = "序列号")
private String serial;
@Schema(description = "磁盘大小")
private long total;
@Schema(description = "磁盘写入速率")
private long writeSpeed;
@Schema(description = "磁盘读取速率")
private long readSpeed;
@Schema(description = "磁盘写入次数")
private long writeTimes;
@Schema(description = "磁盘读取次数")
private long readTimes;
@Schema(description = "磁盘写入字节")
private long writeBytes;
@Schema(description = "磁盘读取字节")
private long readBytes;
@Schema(description = "时间戳")
private long timestamp;
}
@@ -0,0 +1,44 @@
package com.tongran.agent.client.core.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
import java.io.Serializable;
@Data
@SuperBuilder
@AllArgsConstructor
@NoArgsConstructor
@Schema(description = "容器信息")
public class DockerVO implements Serializable {
private static final long serialVersionUID = 4L;
@Schema(description = "容器ID")
private String id;
@Schema(description = "容器名称")
private String name;
@Schema(description = "容器状态")
private String status;
@Schema(description = "容器CPU使用率")
private String cpuUtil;
@Schema(description = "容器内存使用率")
private String memUtil;
@Schema(description = "容器网络接收速率")
private String netInSpeed;
@Schema(description = "容器网络发送速率")
private String netOutSpeed;
@Schema(description = "时间戳")
private long timestamp;
}
@@ -0,0 +1,40 @@
package com.tongran.agent.client.core.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
import java.io.Serializable;
@Data
@SuperBuilder
@AllArgsConstructor
@NoArgsConstructor
@Schema(description = "内存信息")
public class MemoryVO implements Serializable {
private static final long serialVersionUID = 2L;
@Schema(description = "交换卷/文件的可用空间(字节)")
private long swapSizeFree;
@Schema(description = "内存利用率")
private double untilzation;
@Schema(description = "可用交换空间百分比")
private double swapSizePercent;
@Schema(description = "可用内存")
private long available;
@Schema(description = "可用内存百分比")
private double percent;
@Schema(description = "总内存")
private long total;
@Schema(description = "时间戳")
private long timestamp;
}
@@ -0,0 +1,55 @@
package com.tongran.agent.client.core.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
import java.io.Serializable;
@Data
@SuperBuilder
@AllArgsConstructor
@NoArgsConstructor
@Schema(description = "网络信息")
public class NetVO implements Serializable {
private static final long serialVersionUID = 6L;
@Schema(description = "网卡名称")
private String name;
@Schema(description = "MAC")
private String mac;
@Schema(description = "运行状态")
private String status;
@Schema(description = "接口类型")
private String type;
@Schema(description = "IPv4")
private String ipV4;
@Schema(description = "入站丢包")
private long inDropped;
@Schema(description = "出站丢包")
private long outDropped;
@Schema(description = "发送流量")
private long outSpeed;
@Schema(description = "接收流量")
private long inSpeed;
@Schema(description = "协商速度")
private String speed;
@Schema(description = "工作模式")
private String duplex;
@Schema(description = "时间戳")
private long timestamp;
}
@@ -0,0 +1,36 @@
package com.tongran.agent.client.core.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
import java.io.Serializable;
@Data
@SuperBuilder
@AllArgsConstructor
@NoArgsConstructor
@Schema(description = "挂载点信息")
public class PointVO implements Serializable {
private static final long serialVersionUID = 7L;
@Schema(description = "挂载点")
private String mount;
@Schema(description = "文件系统类型")
private String vfsType;
@Schema(description = "可用空间")
private long vfsFree;
@Schema(description = "总空间")
private long vfsTotal;
@Schema(description = "空间利用率")
private double vfsUtil;
@Schema(description = "时间戳")
private long timestamp;
}
@@ -0,0 +1,41 @@
package com.tongran.agent.client.core.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
import java.io.Serializable;
@Data
@SuperBuilder
@AllArgsConstructor
@NoArgsConstructor
@Schema(description = "交换机信息")
public class SwitchBoardVO implements Serializable {
private static final long serialVersionUID = 8L;
@Schema(description = "网口名称")
private String name;
@Schema(description = "交换机IP")
private String switchIp;
@Schema(description = "网口类型")
private String type;
@Schema(description = "网口状态")
private String status;
@Schema(description = "接收流量")
private long inBytes;
@Schema(description = "发送流量")
private long outBytes;
@Schema(description = "时间戳")
private long timestamp;
}
@@ -0,0 +1,55 @@
package com.tongran.agent.client.core.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
import java.io.Serializable;
@Data
@SuperBuilder
@AllArgsConstructor
@NoArgsConstructor
@Schema(description = "系统信息")
public class SystemVO implements Serializable {
private static final long serialVersionUID = 3L;
@Schema(description = "操作系统")
private String os;
@Schema(description = "操作系统架构")
private String arch;
@Schema(description = "最大进程数")
private long maxProc;
@Schema(description = "正在运行的进程数")
private int runProcNum;
@Schema(description = "登录用户数")
private int usersNum;
@Schema(description = "硬盘:总可用空间(字节)")
private long diskSizeTotal;
@Schema(description = "系统启动时间")
private long bootTime;
@Schema(description = "系统描述")
private String uname;
@Schema(description = "系统本地时间")
private String localTime;
@Schema(description = "系统正常运行时间")
private long upTime;
private String uuid;
@Schema(description = "时间戳")
private long timestamp;
}
@@ -0,0 +1,27 @@
package com.tongran.agent.client.exception;
import cn.hutool.core.util.StrUtil;
import com.tongran.agent.client.exception.base.BaseException;
import com.tongran.agent.client.exception.code.ErrorCode;
public class ServerException extends BaseException {
private static final long serialVersionUID = -519977195881081739L;
public ServerException(ErrorCode code) {
super(code.getCode(), code.getMsg());
}
public ServerException(Integer code, String msg) {
super(code, msg);
}
public ServerException(String msg) {
super(500, msg);
}
public ServerException(String msg, Object... arguments) {
super(500, StrUtil.format(msg, arguments));
}
}
@@ -0,0 +1,42 @@
package com.tongran.agent.client.exception.base;
import com.tongran.agent.client.exception.code.ErrorCode;
public class BaseException extends RuntimeException implements ErrorCode {
private static final long serialVersionUID = 1966249840643379123L;
private Integer code;
private String msg;
public BaseException() {
}
public BaseException(String msg, Throwable cause) {
super(msg, cause);
}
public BaseException(Integer code, String msg) {
super(msg);
this.code = code;
this.msg = msg;
}
public BaseException(Integer code, String msg, Throwable cause) {
super(msg, cause);
this.code = code;
this.msg = msg;
}
@Override
public Integer getCode() {
return code;
}
@Override
public String getMsg() {
return msg;
}
}
@@ -0,0 +1,16 @@
package com.tongran.agent.client.exception.code;
import com.tongran.agent.client.utils.R;
public interface ErrorCode {
Integer getCode();
String getMsg();
default R<?> toResult() {
return R.error(getMsg(), getCode());
}
}
@@ -0,0 +1,20 @@
package com.tongran.agent.client.exception.code;
import lombok.AllArgsConstructor;
import lombok.Getter;
@Getter
@AllArgsConstructor
public enum GlobalErrorCode implements ErrorCode {
SUCCESS(200, "成功"),
NOT_FOUND(404, "未找到相关资源"),
ERROR(500, "系统错误");
private final Integer code;
private final String msg;
}
@@ -0,0 +1,22 @@
package com.tongran.agent.client.netty;
import com.tongran.agent.client.netty.config.BaseNettyConfig;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
/**
* Netty配置
*/
@Data
@Configuration
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
@ConfigurationProperties(prefix = "tcp.netty.charge")
public class AgentNettyConfig extends BaseNettyConfig {
private static final long serialVersionUID = -4284214721383107291L;
}
@@ -0,0 +1,57 @@
package com.tongran.agent.client.netty;
import com.tongran.agent.client.netty.handler.AgentDecoderHandler;
import com.tongran.agent.client.netty.handler.AgentDispatcherHandler;
import com.tongran.agent.client.netty.handler.AgentEncoderHandler;
import com.tongran.agent.client.netty.handler.TCPListenHandler;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.timeout.IdleStateHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.annotation.Resource;
@Configuration
public class AgentNettyServer extends BaseNettyServer {
@Resource
private AgentNettyConfig config;
@Resource
private TCPListenHandler tcpListenHandler;
@Resource
private AgentDecoderHandler decoderHandler;
@Resource
private AgentEncoderHandler encoderHandler;
@Resource
private AgentDispatcherHandler dispatcherHandler;
protected AgentNettyServer(AgentNettyConfig config) {
super(config);
}
@Bean(name = "NettyCharge", initMethod = "start", destroyMethod = "stop")
BaseNettyServer run() {
config.setHander(new ChannelInitializer<NioSocketChannel>() {
@Override
public void initChannel(NioSocketChannel ch) throws Exception {
ch.pipeline().addLast(new IdleStateHandler(config.readerIdleTime, config.writerIdleTime, config.allIdleTime));// 心跳
ch.pipeline().addLast(tcpListenHandler);// 监听器
//入栈
ch.pipeline().addLast(decoderHandler);//解码器
//出栈
ch.pipeline().addLast(encoderHandler);//加码器
// 业务分发是入站最后一个处理器, 出站第一个处理器, 位置要放在最后
ch.pipeline().addLast(businessGroup, dispatcherHandler);//业务分发 这里使用业务线程组
}
});
return new AgentNettyServer(config);
}
}
@@ -0,0 +1,117 @@
package com.tongran.agent.client.netty;
import com.tongran.agent.client.netty.config.BaseNettyConfig;
import com.tongran.agent.client.utils.AssertLog;
import io.netty.bootstrap.AbstractBootstrap;
import io.netty.bootstrap.Bootstrap;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioChannelOption;
import io.netty.channel.socket.nio.NioDatagramChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.util.ResourceLeakDetector;
import io.netty.util.concurrent.DefaultEventExecutorGroup;
import io.netty.util.concurrent.DefaultThreadFactory;
import io.netty.util.concurrent.EventExecutorGroup;
import io.netty.util.concurrent.Future;
/**
* 基础Netty服务
*/
public abstract class BaseNettyServer {
protected boolean isRunning;
protected BaseNettyConfig config;
protected EventLoopGroup bossGroup;
protected EventLoopGroup workerGroup;
protected EventExecutorGroup businessGroup;
protected BaseNettyServer(BaseNettyConfig config) {
this.config = config;
}
protected AbstractBootstrap<?, ?> initializeTcp() {
bossGroup = new NioEventLoopGroup(1, new DefaultThreadFactory(config.name, Thread.MAX_PRIORITY));
workerGroup = new NioEventLoopGroup(config.workerCore, new DefaultThreadFactory(config.name, Thread.MAX_PRIORITY));
if (config.businessCore > 0) {
businessGroup = new DefaultEventExecutorGroup(config.businessCore);
}
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childOption(ChannelOption.SO_REUSEADDR, true)
.option(ChannelOption.SO_BACKLOG, 1024)
.childOption(NioChannelOption.TCP_NODELAY, true)
.childHandler(config.hander);
//内存泄漏检测 开发推荐PARANOID 线上SIMPLE
ResourceLeakDetector.setLevel(ResourceLeakDetector.Level.SIMPLE);
return serverBootstrap;
}
protected AbstractBootstrap<?, ?> initializeUdp() {
bossGroup = new NioEventLoopGroup(1, new DefaultThreadFactory(config.name, Thread.MAX_PRIORITY));
if (config.businessCore > 0) {
businessGroup = new DefaultEventExecutorGroup(config.businessCore);
}
return new Bootstrap()
.group(bossGroup).channel(NioDatagramChannel.class)
.option(NioChannelOption.SO_REUSEADDR, true)
.option(NioChannelOption.SO_RCVBUF, 1024 * 1024 * 50)
.handler(config.hander);
}
public synchronized boolean start() {
if (!config.enable) {
return false;
}
if (isRunning) {
AssertLog.info("======{}已经启动,port:{}======", config.name, config.port);
return isRunning;
}
AbstractBootstrap<?, ?> bootstrap = config.isTcp ? initializeTcp() : initializeUdp();
ChannelFuture future = bootstrap.bind(config.port).awaitUninterruptibly();
future.channel().closeFuture().addListener(f -> {
if (isRunning) {
stop();
}
});
if (future.cause() != null) {
AssertLog.error("===启动失败===", future.cause());
}
if (isRunning = future.isSuccess()) {
AssertLog.info("\n\n\t\t\t\t\t\t\t\t======{}启动成功,port:{}======\n", config.name, config.port);
}
return isRunning;
}
public synchronized void stop() {
if (!config.enable) {
return;
}
isRunning = false;
try {
Future<?> future = this.workerGroup.shutdownGracefully().await();
if (!future.isSuccess()) {
AssertLog.error("workerGroup 无法正常停止:{}", future.cause());
}
future = this.bossGroup.shutdownGracefully().await();
if (!future.isSuccess()) {
AssertLog.error("bossGroup 无法正常停止:{}", future.cause());
}
future = this.businessGroup.shutdownGracefully().await();
if (!future.isSuccess()) {
AssertLog.error("businessGroup 无法正常停止:{}", future.cause());
}
} catch (InterruptedException e) {
e.printStackTrace();
}
AssertLog.info("\n\n\t\t\t\t\t\t\t\t======{} 已经停止,port:{}======\n", config.name, config.port);
}
}
@@ -0,0 +1,47 @@
package com.tongran.agent.client.netty.annotation;
import com.tongran.agent.client.core.enums.MsgEnum;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.springframework.stereotype.Component;
import java.lang.annotation.*;
/**
* 指明类为Agent消息
*/
@Component
@Documented
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface AgentDispatcher {
@Getter
@AllArgsConstructor
enum VersionEnum {
V1("2026");
public final String value;
}
/**
* 消息ID
*
* @return
*/
MsgEnum msgId();
/**
* 消息版本 默认版本2025
*
* @return
*/
VersionEnum version() default VersionEnum.V1;
/**
* 描述
*
* @return
*/
String desc() default "";
}
@@ -0,0 +1,48 @@
package com.tongran.agent.client.netty.basics;
import com.tongran.agent.client.netty.annotation.AgentDispatcher;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@Component
@Order(1)
public class AgentDispatcherManager implements ApplicationContextAware {
/**
* 所有实现的包处理器
*/
private static Map<String, AgentHandler> MSG_HANDLER_MAP;
/**
* 唤醒时 初始化 packHandlerMap
*/
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
// 仅一次性初始化完成
if (MSG_HANDLER_MAP == null) {
MSG_HANDLER_MAP = new ConcurrentHashMap<>();
Map<String, Object> handlers = applicationContext.getBeansWithAnnotation(AgentDispatcher.class);
if (!CollectionUtils.isEmpty(handlers)) {
handlers.values().forEach(tempHandler -> {
boolean result = tempHandler.getClass().isAnnotationPresent(AgentDispatcher.class);
if (result) {
AgentDispatcher annotation = tempHandler.getClass().getAnnotation(AgentDispatcher.class);
MSG_HANDLER_MAP.put(annotation.msgId().getValue() + "&" + annotation.version().value, (AgentHandler) tempHandler);
}
});
}
}
}
public AgentHandler getHandler(String msgIdVersion) {
return MSG_HANDLER_MAP == null ? null : MSG_HANDLER_MAP.get(msgIdVersion);
}
}
@@ -0,0 +1,21 @@
package com.tongran.agent.client.netty.basics;
import com.tongran.agent.client.netty.model.UpMsgResponse;
public interface AgentHandler {
/**
* 处理终端传入的消息, 然后进行返回
*
* @return 需要发送给终端的消息
*/
default UpMsgResponse upHandle(String data, String clientId) {
return null;
}
default String downHandle(String data) {
return "";
}
}
@@ -0,0 +1,58 @@
package com.tongran.agent.client.netty.config;
import com.tongran.agent.client.core.session.SessionManager;
import io.netty.channel.ChannelHandler;
import io.netty.util.NettyRuntime;
import lombok.Data;
import java.io.Serializable;
/**
* 基础Netty配置
*/
@Data
public class BaseNettyConfig implements Serializable {
private static final long serialVersionUID = -2736718499972710895L;
/**
* 是否开启
*/
public Boolean enable = false;
/**
* 是否TCP
*/
public Boolean isTcp = true;
/**
* 服务名称
*/
public String name = "Netty";
/**
* 端口号
*/
public Integer port = 11111;
public Integer workerCore = NettyRuntime.availableProcessors() * 2;
public Integer businessCore = Math.max(1, NettyRuntime.availableProcessors() >> 1);
public Integer readerIdleTime = 240;
public Integer writerIdleTime = 0;
public Integer allIdleTime = 0;
/**
* netty Session管理
*/
public SessionManager sessionManager;
/**
* netty Channel处理列表
*/
public ChannelHandler hander;
}
@@ -0,0 +1,484 @@
package com.tongran.agent.client.netty.enpoint;
import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.alibaba.fastjson2.TypeReference;
import com.tongran.agent.client.core.config.GlobalConfig;
import com.tongran.agent.client.core.enums.MsgEnum;
import com.tongran.agent.client.core.eo.AgentVersionUpdateEO;
import com.tongran.agent.client.core.eo.ScriptPolicyEO;
import com.tongran.agent.client.netty.annotation.AgentDispatcher;
import com.tongran.agent.client.netty.basics.AgentHandler;
import com.tongran.agent.client.netty.model.Message;
import com.tongran.agent.client.netty.model.UpMsgResponse;
import com.tongran.agent.client.scheduler.service.AdvancedAsyncDownloader;
import com.tongran.agent.client.scheduler.service.AppInitializer;
import com.tongran.agent.client.scheduler.service.AsyncCommandExecutor;
import com.tongran.agent.client.scheduler.service.Java8FileDownloader;
import com.tongran.agent.client.scheduler.task.SpecificTimeRequest;
import com.tongran.agent.client.scheduler.task.SpecificTimeTaskService;
import com.tongran.agent.client.service.AgentService;
import com.tongran.agent.client.utils.AgentUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
@Component
public class AgentEndpoint {
@Resource
private AppInitializer appInitializer;
@Resource
private SpecificTimeTaskService taskService;
@Resource
private AgentService agentService;
@AgentDispatcher(msgId = MsgEnum.注册)
public class RegisterHandler implements AgentHandler {
@Override
public UpMsgResponse upHandle(String data, String clientId) {
//更改全局变量
JSONObject jsonObject = JSONObject.parseObject(data);
if(jsonObject.containsKey("switchBoard")){
String switchBoard = jsonObject.getString("switchBoard");
jsonObject = JSONObject.parseObject(switchBoard);
if(jsonObject.containsKey("community")){
GlobalConfig.SWITCH_COMMUNITY = jsonObject.getString("community");
}
if(jsonObject.containsKey("ip")){
GlobalConfig.SWITCH_IP = jsonObject.getString("ip");
}
if(jsonObject.containsKey("port")){
GlobalConfig.SWITCH_PORT = jsonObject.getInteger("port");
}
if(jsonObject.containsKey("netOID")){
String netOID = jsonObject.getString("netOID");
LinkedHashMap<String, String> map = JSON.parseObject(netOID, new TypeReference<LinkedHashMap<String, String>>() {});
GlobalConfig.SWITCH_NET_OID = map;
}
if(jsonObject.containsKey("moduleOID")){
String moduleOID = jsonObject.getString("moduleOID");
LinkedHashMap<String, String> map = JSON.parseObject(moduleOID, new TypeReference<LinkedHashMap<String, String>>() {});
GlobalConfig.SWITCH_MODULE_OID = map;
}
if(jsonObject.containsKey("mpuOID")){
String mpuOID = jsonObject.getString("mpuOID");
LinkedHashMap<String, String> map = JSON.parseObject(mpuOID, new TypeReference<LinkedHashMap<String, String>>() {});
GlobalConfig.SWITCH_MPU_OID = map;
}
if(jsonObject.containsKey("pwrOID")){
String pwrOID = jsonObject.getString("pwrOID");
LinkedHashMap<String, String> map = JSON.parseObject(pwrOID, new TypeReference<LinkedHashMap<String, String>>() {});
GlobalConfig.SWITCH_PWR_OID = map;
}
if(jsonObject.containsKey("fanOID")){
String fanOID = jsonObject.getString("fanOID");
LinkedHashMap<String, String> map = JSON.parseObject(fanOID, new TypeReference<LinkedHashMap<String, String>>() {});
GlobalConfig.SWITCH_FAN_OID = map;
}
if(jsonObject.containsKey("otherOID")){
String otherOID = jsonObject.getString("otherOID");
LinkedHashMap<String, String> map = JSON.parseObject(otherOID, new TypeReference<LinkedHashMap<String, String>>() {});
GlobalConfig.SWITCH_OTHER_OID = map;
}
}
GlobalConfig.isCollect = true;
GlobalConfig.CLIENT_ID = clientId;
//调用采集任务
try {
appInitializer.run();
} catch (Exception e) {
e.printStackTrace();
}
long timestamp = System.currentTimeMillis();
timestamp = Math.round(timestamp / 1000.0);
JSONObject json = new JSONObject();
json.put("resCode",1);
json.put("resMag","");
json.put("timestamp",timestamp);
return UpMsgResponse.builder().clientId(clientId).dataType(MsgEnum.注册应答.getValue())
.content(json.toString()).build();
}
}
@AgentDispatcher(msgId = MsgEnum.断开)
public class DisconnectHandler implements AgentHandler {
@Override
public UpMsgResponse upHandle(String data, String clientId) {
//更改全局变量
GlobalConfig.isCollect = false;
//调用采集任务
try {
appInitializer.run();
} catch (Exception e) {
e.printStackTrace();
}
long timestamp = System.currentTimeMillis();
timestamp = Math.round(timestamp / 1000.0);
JSONObject json = new JSONObject();
json.put("resCode",1);
json.put("resMag","");
json.put("timestamp",timestamp);
return UpMsgResponse.builder().clientId(clientId).dataType(MsgEnum.断开应答.getValue())
.content(json.toString()).build();
}
}
@AgentDispatcher(msgId = MsgEnum.开启系统采集)
public class SysCollectStartHandler implements AgentHandler {
@Override
public UpMsgResponse upHandle(String data, String clientId) {
//更改全局变量
agentService.systemCollectStart(data);
long timestamp = System.currentTimeMillis();
timestamp = Math.round(timestamp / 1000.0);
JSONObject json = new JSONObject();
json.put("resCode",1);
json.put("resMag","");
json.put("timestamp",timestamp);
return UpMsgResponse.builder().dataType(MsgEnum.开启系统采集应答.getValue()).content(json.toString()).build();
}
}
@AgentDispatcher(msgId = MsgEnum.关闭系统采集)
public class SysCollectStopHandler implements AgentHandler {
@Override
public UpMsgResponse upHandle(String data, String clientId) {
//更改全局变量
agentService.systemCollectStop();
long timestamp = System.currentTimeMillis();
timestamp = Math.round(timestamp / 1000.0);
JSONObject json = new JSONObject();
json.put("resCode",1);
json.put("resMag","");
json.put("timestamp",timestamp);
return UpMsgResponse.builder().dataType(MsgEnum.关闭系统采集应答.getValue()).content(json.toString()).build();
}
}
@AgentDispatcher(msgId = MsgEnum.开启交换机采集)
public class SwitchCollectStartHandler implements AgentHandler {
@Override
public UpMsgResponse upHandle(String data, String clientId) {
//更改全局变量
agentService.switchCollectStart(data);
long timestamp = System.currentTimeMillis();
timestamp = Math.round(timestamp / 1000.0);
JSONObject json = new JSONObject();
json.put("resCode",1);
json.put("resMag","");
json.put("timestamp",timestamp);
return UpMsgResponse.builder().dataType(MsgEnum.开启交换机采集应答.getValue()).content(json.toString()).build();
}
}
@AgentDispatcher(msgId = MsgEnum.关闭交换机采集)
public class SwitchCollectStopHandler implements AgentHandler {
@Override
public UpMsgResponse upHandle(String data, String clientId) {
//更改全局变量
agentService.switchCollectStop();
long timestamp = System.currentTimeMillis();
timestamp = Math.round(timestamp / 1000.0);
JSONObject json = new JSONObject();
json.put("resCode",1);
json.put("resMag","");
json.put("timestamp",timestamp);
return UpMsgResponse.builder().dataType(MsgEnum.关闭交换机采集应答.getValue()).content(json.toString()).build();
}
}
@AgentDispatcher(msgId = MsgEnum.告警设置)
public class AlarmSetHandler implements AgentHandler {
@Override
public UpMsgResponse upHandle(String data, String clientId) {
// //更改全局变量
// JSONObject jsonObject = JSONObject.parseObject(data);
// if(jsonObject.containsKey("switchBoard")){
// String switchBoard = jsonObject.getString("switchBoard");
// jsonObject = JSONObject.parseObject(switchBoard);
// if(jsonObject.containsKey("community")){
// GlobalConfig.SWITCH_COMMUNITY = jsonObject.getString("community");
// }
// if(jsonObject.containsKey("ip")){
// GlobalConfig.SWITCH_IP = jsonObject.getString("ip");
// }
// if(jsonObject.containsKey("port")){
// GlobalConfig.SWITCH_PORT = jsonObject.getInteger("port");
// }
// if(jsonObject.containsKey("netOID")){
// String netOID = jsonObject.getString("netOID");
// LinkedHashMap<String, String> map = JSON.parseObject(netOID, new TypeReference<LinkedHashMap<String, String>>() {});
// GlobalConfig.SWITCH_NET_OID = map;
// }
// if(jsonObject.containsKey("moduleOID")){
// String moduleOID = jsonObject.getString("moduleOID");
// LinkedHashMap<String, String> map = JSON.parseObject(moduleOID, new TypeReference<LinkedHashMap<String, String>>() {});
// GlobalConfig.SWITCH_MODULE_OID = map;
// }
// if(jsonObject.containsKey("mpuOID")){
// String mpuOID = jsonObject.getString("mpuOID");
// LinkedHashMap<String, String> map = JSON.parseObject(mpuOID, new TypeReference<LinkedHashMap<String, String>>() {});
// GlobalConfig.SWITCH_MPU_OID = map;
// }
// if(jsonObject.containsKey("pwrOID")){
// String pwrOID = jsonObject.getString("pwrOID");
// LinkedHashMap<String, String> map = JSON.parseObject(pwrOID, new TypeReference<LinkedHashMap<String, String>>() {});
// GlobalConfig.SWITCH_PWR_OID = map;
// }
// if(jsonObject.containsKey("fanOID")){
// String fanOID = jsonObject.getString("fanOID");
// LinkedHashMap<String, String> map = JSON.parseObject(fanOID, new TypeReference<LinkedHashMap<String, String>>() {});
// GlobalConfig.SWITCH_FAN_OID = map;
// }
// if(jsonObject.containsKey("otherOID")){
// String otherOID = jsonObject.getString("otherOID");
// LinkedHashMap<String, String> map = JSON.parseObject(otherOID, new TypeReference<LinkedHashMap<String, String>>() {});
// GlobalConfig.SWITCH_OTHER_OID = map;
// }
// }
// GlobalConfig.isCollect = true;
// GlobalConfig.CLIENT_ID = clientId;
// //调用采集任务
// try {
// appInitializer.run();
// } catch (Exception e) {
// e.printStackTrace();
// }
// JSONObject json = new JSONObject();
// json.put("resCode",1);
return UpMsgResponse.builder().clientId(clientId).dataType(MsgEnum.告警设置应答.getValue())
.content("").build();
}
}
@AgentDispatcher(msgId = MsgEnum.执行脚本策略)
public class ScriptPolicyHandler implements AgentHandler {
@Override
public UpMsgResponse upHandle(String data, String clientId) {
ScriptPolicyEO policy = JSON.parseObject(data, ScriptPolicyEO.class);
boolean isFalse = false;
if(StringUtils.isNotBlank(policy.getFilePath())){
if(AdvancedAsyncDownloader.createSingleDirectoryIfNotExists(policy.getFilePath())){
if(CollectionUtil.isNotEmpty(policy.getFiles())){
isFalse = true;
for (ScriptPolicyEO.ScriptFile f : policy.getFiles()) {
String finalSaveDir = policy.getFilePath();
//文件类型:0、平台文件地址;1、外网HTTP(S)
if(f.getFileType() == 0){
String filePath = policy.getFilePath() + "/" + f.getFileName();
// 移除Base64 URL前缀(如果存在)
// if (f.getFileData().contains(",")) {
// f.setFileData(f.getFileData().split(",")[1]);
// }
try {
AgentUtil.base64ToFile(f.getFileData(), filePath);
System.out.println("文件保存成功: " + filePath);
isFalse = true;
} catch (IOException e) {
isFalse = false;
System.err.println("文件保存失败: " + e.getMessage());
}
}else{
Java8FileDownloader.DownloadResult result = Java8FileDownloader.downloadFile(f.getFileUrl(), finalSaveDir, f.getFileName());
// 验证下载是否完成
if (result.isSuccess()) {
boolean isComplete = Java8FileDownloader.verifyFileCompletion(
result.getFilePath(), result.getExpectedSize());
if(isComplete){
isFalse = true;
System.out.println("下载完成: " + finalSaveDir+"/"+f.getFileName());
}else{
isFalse = false;
System.err.println("下载失败");
}
}
}
}
}
}
}
//判断文件是否保存成功
if(isFalse){
if(CollectionUtil.isNotEmpty(policy.getCommands())){
//执行方式:0、立即执行;1、定时执行;
if(policy.getMethod() == 1){
SpecificTimeRequest request = SpecificTimeRequest.builder()
.taskId("")
.taskName("")
.taskData(policy.getCommands())
.specificDateTime(policy.getPolicyTime())
.clientId(clientId)
.build();
try {
taskService.createSpecificTimeTask(request);
JSONObject json = new JSONObject();
json.put("resCode",1);
json.put("resMsg", "执行脚本策略定时任务保存成功");
Message message = Message.builder().clientId(clientId).dataType(MsgEnum.执行脚本策略应答.getValue()).data(json.toString()).build();
return UpMsgResponse.builder().clientId(clientId).dataType(MsgEnum.执行脚本策略应答.getValue()).content(json.toString()).build();
} catch (Exception e) {
JSONObject json = new JSONObject();
json.put("resCode",0);
json.put("resMsg", "执行脚本策略定时任务保存失败");
Message message = Message.builder().clientId(clientId).dataType(MsgEnum.执行脚本策略应答.getValue()).data(json.toString()).build();
return UpMsgResponse.builder().clientId(clientId).dataType(MsgEnum.执行脚本策略应答.getValue()).content(json.toString()).build();
}
}else{
List<Map<String,String>> list = new ArrayList<>();
for (String command : policy.getCommands()) {
Map<String,String> map = new HashMap<>();
List<String> cmd = Arrays.asList(command.split("\\s+"));
CompletableFuture<AsyncCommandExecutor.CommandResult> future =
AsyncCommandExecutor.executeCommandAsync(
cmd,
60, TimeUnit.SECONDS);
future.thenAccept(result -> {
if (result.isSuccess()) {
System.out.println("脚本执行成功");
System.out.println("[成功resOut] " + result.getOutput());
map.put("command",command);
map.put("resOut",result.getOutput());
} else {
System.out.println("脚本执行失败");
System.out.println("[失败resOut] " + result.getOutput());
map.put("command",command);
map.put("resOut",result.getOutput());
}
}).exceptionally(ex -> {
System.err.println("执行失败: " + ex.getMessage());
map.put("command",command);
map.put("resOut","Policy execute filed");
return null;
});
list.add(map);
}
if(CollectionUtil.isNotEmpty(list)){
JSONObject json = new JSONObject();
json.put("resCode",1);
json.put("resMsg", "");
json.put("result", AgentUtil.toJsonString(list));
return UpMsgResponse.builder().clientId(clientId).dataType(MsgEnum.执行脚本策略应答.getValue()).content(json.toString()).build();
}
}
}
}else{
JSONObject json = new JSONObject();
json.put("resCode",0);
json.put("resMsg", "A执行脚本策略文件保存失败");
return UpMsgResponse.builder().clientId(clientId).dataType(MsgEnum.执行脚本策略应答.getValue()).content(json.toString()).build();
}
return null;
}
}
@AgentDispatcher(msgId = MsgEnum.Agent版本更新)
public class VersionUpdateHandler implements AgentHandler {
@Override
public UpMsgResponse upHandle(String data, String clientId) {
AgentVersionUpdateEO versionUpdateEO = JSON.parseObject(data, AgentVersionUpdateEO.class);
boolean isFalse = false;
if(StringUtils.isNotBlank(versionUpdateEO.getFileUrl()) && StringUtils.isNotBlank(versionUpdateEO.getFilePath())){
Java8FileDownloader.DownloadResult result = Java8FileDownloader.downloadFile(versionUpdateEO.getFileUrl(), versionUpdateEO.getFilePath(), "");
// 验证下载是否完成
if (result.isSuccess()) {
boolean isComplete = Java8FileDownloader.verifyFileCompletion(
result.getFilePath(), result.getExpectedSize());
if(isComplete){
isFalse = true;
System.out.println("Agent版本更新下载完成");
}else{
isFalse = false;
System.err.println("Agent版本更新下载失败");
}
}
}
//判断文件是否保存成功
if(isFalse){
if(CollectionUtil.isNotEmpty(versionUpdateEO.getCommands())){
//执行方式:0、立即执行;1、定时执行;
if(versionUpdateEO.getMethod() == 1){
SpecificTimeRequest request = SpecificTimeRequest.builder()
.taskId("")
.taskName("")
.taskData(versionUpdateEO.getCommands())
.specificDateTime(versionUpdateEO.getPolicyTime())
.clientId(clientId)
.dataType(MsgEnum.Agent版本更新应答.getValue())
.build();
try {
taskService.createSpecificTimeTask(request);
JSONObject json = new JSONObject();
json.put("resCode",1);
json.put("resMsg", "Agent版本更新定时任务保存成功");
// Message message = Message.builder().clientId(clientId).dataType(MsgEnum.Agent更新应答.getValue()).data(json.toString()).build();
return UpMsgResponse.builder().clientId(clientId).dataType(MsgEnum.Agent版本更新应答.getValue()).content(json.toString()).build();
} catch (Exception e) {
JSONObject json = new JSONObject();
json.put("resCode",0);
json.put("resMsg", "Agent版本更新定时任务保存失败");
// Message message = Message.builder().clientId(clientId).dataType(MsgEnum.Agent更新应答.getValue()).data(json.toString()).build();
return UpMsgResponse.builder().clientId(clientId).dataType(MsgEnum.Agent版本更新应答.getValue()).content(json.toString()).build();
}
}else{
List<Map<String,String>> list = new ArrayList<>();
for (String command : versionUpdateEO.getCommands()) {
Map<String,String> map = new HashMap<>();
List<String> cmd = Arrays.asList(command.split("\\s+"));
CompletableFuture<AsyncCommandExecutor.CommandResult> future =
AsyncCommandExecutor.executeCommandAsync(
cmd,
60, TimeUnit.SECONDS);
future.thenAccept(result -> {
if (result.isSuccess()) {
System.out.println("脚本执行成功");
System.out.println("[成功resOut] " + result.getOutput());
map.put("command",command);
map.put("resOut",result.getOutput());
} else {
System.out.println("脚本执行失败");
System.out.println("[失败resOut] " + result.getOutput());
map.put("command",command);
map.put("resOut",result.getOutput());
}
}).exceptionally(ex -> {
System.err.println("执行失败: " + ex.getMessage());
map.put("command",command);
map.put("resOut","Policy execute filed");
return null;
});
list.add(map);
}
if(CollectionUtil.isNotEmpty(list)){
JSONObject json = new JSONObject();
json.put("resCode",1);
json.put("resMsg", "");
json.put("result", AgentUtil.toJsonString(list));
// Message message = Message.builder().clientId(clientId).dataType(MsgEnum.Agent版本更新应答.getValue()).data(json.toString()).build();
return UpMsgResponse.builder().clientId(clientId).dataType(MsgEnum.Agent版本更新应答.getValue()).content(json.toString()).build();
}
}
}
}else{
JSONObject json = new JSONObject();
json.put("resCode",0);
json.put("resMsg", "Agent版本更新文件保存失败");
// Message message = Message.builder().clientId(clientId).dataType(MsgEnum.Agent版本更新应答.getValue()).data(json.toString()).build();
return UpMsgResponse.builder().clientId(clientId).dataType(MsgEnum.Agent版本更新应答.getValue()).content(json.toString()).build();
}
return null;
}
}
}
@@ -0,0 +1,141 @@
package com.tongran.agent.client.netty.handler;
import cn.hutool.cache.Cache;
import cn.hutool.cache.CacheUtil;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson2.JSONObject;
import com.tongran.agent.client.core.session.SessionManager;
import com.tongran.agent.client.netty.annotation.AgentDispatcher;
import com.tongran.agent.client.netty.basics.AgentDispatcherManager;
import com.tongran.agent.client.netty.basics.AgentHandler;
import com.tongran.agent.client.netty.model.Message;
import com.tongran.agent.client.netty.model.UpMsgResponse;
import com.tongran.agent.client.utils.AssertLog;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.CharsetUtil;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.Objects;
@Component
@ChannelHandler.Sharable
public class AgentDecoderHandler extends ChannelInboundHandlerAdapter {
protected final SessionManager sessionManager;
@Resource
private AgentDispatcherManager agentDispatcherManager;
public AgentDecoderHandler() {
this.sessionManager = SessionManager.getInstance();
}
// 用来临时保留没有处理过的请求报文
private final Cache<String, String> lruCache = CacheUtil.newLRUCache(3000);
/**
* 消息解码器
*/
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
// 检查是否为 ByteBuf(未解码的原始数据)
if (msg instanceof ByteBuf) {
ByteBuf byteBuf = (ByteBuf) msg;
String messages = byteBuf.toString(CharsetUtil.UTF_8); // 指定字符集解码
AssertLog.info("<<[up1]:IP:{},[up-content]==>{}", sessionManager.client(ctx),messages);
String content = "";
boolean isClear = false;
String tempMsg = lruCache.get(sessionManager.client(ctx));
tempMsg = tempMsg == null ? "" : tempMsg;
int tmpMsgSize = tempMsg.length();
boolean startsWith = messages.startsWith("agent-server:");
boolean endsWith = messages.endsWith("@tong-ran");
String ms = messages.replaceAll("agent-server:","");
String[] arr_msg = ms.split("@tong-ran");
if(startsWith){
//判定是否整包
if(arr_msg.length == 1){
//判定是否拆包
if(endsWith){
lruCache.remove(sessionManager.client(ctx));
tmpMsgSize = 0;
content = arr_msg[0]+"@tong-ran";
}else{
lruCache.remove(sessionManager.client(ctx));
tmpMsgSize = 0;
content = arr_msg[0];
lruCache.put(sessionManager.client(ctx), content, DateUnit.SECOND.getMillis() * 3000);
}
}
//判定是否粘包
if(arr_msg.length > 1){
if(!endsWith){
lruCache.remove(sessionManager.client(ctx));
tmpMsgSize = 0;
content = arr_msg[0]+"@tong-ran";
lruCache.put(sessionManager.client(ctx), arr_msg[1], DateUnit.SECOND.getMillis() * 3000);
}else{
lruCache.remove(sessionManager.client(ctx));
tmpMsgSize = 0;
content = String.join("@tong-ran", arr_msg);
}
}
}
if (tmpMsgSize > 0) {
content = tempMsg + messages;
endsWith = content.endsWith("@tong-ran");
if(endsWith){
isClear = true;
}else{
lruCache.remove(sessionManager.client(ctx));
tmpMsgSize = 0;
lruCache.put(sessionManager.client(ctx), content, DateUnit.SECOND.getMillis() * 3000);
}
}
AssertLog.info("<<[up2]:IP:{},[up-content]==>{}", sessionManager.client(ctx),content);
endsWith = content.endsWith("@tong-ran");
//判断是否是整包
if(endsWith){
String[] arr = content.split("@tong-ran");
for (String message : arr) {
JSONObject jsonObject = JSONObject.parseObject(message);
String clientId = jsonObject.getString("clientId");
String dataType = jsonObject.getString("dataType");
AgentHandler msgHandler = agentDispatcherManager.getHandler(dataType + "&" + AgentDispatcher.VersionEnum.V1.value);
if (ObjectUtil.isNotEmpty(msgHandler)) {
UpMsgResponse response = msgHandler.upHandle(message, clientId );
AssertLog.info("<<[up-after-handle]:clientId:{},type={},[handle-content]={}", response.getClientId(), dataType, message);
if(Objects.nonNull(response)){
Message agentMessage = Message.builder().build();
agentMessage.setClientId(response.getClientId());
agentMessage.setDataType(response.getDataType());
agentMessage.setData(response.getContent());
ctx.fireChannelRead(agentMessage);//传递到下一个handler
}
}
}
}
if (isClear) {
lruCache.remove(sessionManager.client(ctx));
}
byteBuf.release(); // 释放 ByteBuf 资源(重要!)
} else {
System.out.println("Unexpected message type: " + msg.getClass());
}
}
//拆包
public byte[] subByte(byte[] b, int off, int length) {
byte[] bytes = new byte[length];
System.arraycopy(b, off, bytes, 0, length);
return bytes;
}
}
@@ -0,0 +1,38 @@
package com.tongran.agent.client.netty.handler;
import com.tongran.agent.client.core.session.Session;
import com.tongran.agent.client.core.session.SessionManager;
import com.tongran.agent.client.netty.model.Message;
import com.tongran.agent.client.utils.AssertLog;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
@Component
@ChannelHandler.Sharable
public class AgentDispatcherHandler extends SimpleChannelInboundHandler<Message> {
protected final SessionManager sessionManager;
public AgentDispatcherHandler() {
this.sessionManager = SessionManager.getInstance();
}
@Override
protected void channelRead0(ChannelHandlerContext ctx, Message msg) {
// AssertLog.info(">>>>>>[要处理的终端数据] {}", msg.toString());
String clientId = msg.getClientId();
if (StringUtils.isBlank(clientId)) {
AssertLog.error("<<<<<<错误的信息from:{}", ctx.channel().remoteAddress());
return;
}
Session session = Session.buildSession(ctx, clientId);
sessionManager.put(clientId, session);
if (StringUtils.isNotBlank(msg.getData())) {
// AssertLog.info(">>>>>>[getSessionById的终端数据] {}", sessionManager.getSessionById(clientId));
sessionManager.writeAndFlush(sessionManager.getSessionById(clientId).getChannel(), msg);
}
}
}
@@ -0,0 +1,49 @@
package com.tongran.agent.client.netty.handler;
import com.alibaba.fastjson2.JSON;
import com.tongran.agent.client.core.session.SessionManager;
import com.tongran.agent.client.netty.model.Message;
import com.tongran.agent.client.utils.AssertLog;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelOutboundHandlerAdapter;
import io.netty.channel.ChannelPromise;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import java.nio.charset.StandardCharsets;
@Component
@ChannelHandler.Sharable
public class AgentEncoderHandler extends ChannelOutboundHandlerAdapter {
protected final SessionManager sessionManager;
public AgentEncoderHandler() {
this.sessionManager = SessionManager.getInstance();
}
/**
* 消息解码器
*/
@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
if (msg instanceof Message) {
Message entity = (Message) msg;
if (StringUtils.isBlank(entity.getData())) {
AssertLog.error(">>[down]:IP:{},errorContent:{}", sessionManager.client(ctx), msg);
return;
}
AssertLog.info(">>[down]:IP:{},[content]==>{}", sessionManager.client(ctx), entity.getData());
// String json = JSON.toJSONString(entity);
String json = "agent-client:"+ JSON.toJSONString(entity)+"@tong-ran";
byte[] bytes = json.getBytes(StandardCharsets.UTF_8); // 显式指定 UTF-8
// byte[] bytes = EscapeUtil.hexStringToByteArray(entity.getContent());
ByteBuf buf = Unpooled.wrappedBuffer(bytes);
ctx.write(buf, promise);
}
}
}
@@ -0,0 +1,78 @@
package com.tongran.agent.client.netty.handler;
import com.tongran.agent.client.core.config.GlobalConfig;
import com.tongran.agent.client.core.session.SessionManager;
import com.tongran.agent.client.scheduler.service.AppInitializer;
import com.tongran.agent.client.utils.AssertLog;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.io.IOException;
/**
* @Description: TCP消息适配器
*/
@Component
@ChannelHandler.Sharable
public class TCPListenHandler extends ChannelInboundHandlerAdapter {
@Resource
private AppInitializer appInitializer;
private final SessionManager sessionManager;
public TCPListenHandler() {
this.sessionManager = SessionManager.getInstance();
}
@Override
public void channelActive(ChannelHandlerContext ctx) {
AssertLog.info("<<<<<<[终端连接]{}", ctx.channel().remoteAddress());
}
@Override
public void channelInactive(ChannelHandlerContext ctx) {
AssertLog.info(">>>>>>[断开连接]{}", sessionManager.client(ctx));
sessionManager.remove(ctx.channel());
try {
GlobalConfig.isCollect = false;
appInitializer.run();
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable e) {
if (e instanceof IOException) {
AssertLog.info("<<<<<<[终端断开连接]{} {}", sessionManager.client(ctx), e.getMessage());
} else {
AssertLog.info(">>>>>>[消息处理异常]" + sessionManager.client(ctx), e);
}
}
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) {
if (evt instanceof IdleStateEvent) {
IdleStateEvent event = (IdleStateEvent) evt;
IdleState state = event.state();
if (state == IdleState.READER_IDLE || state == IdleState.WRITER_IDLE || state == IdleState.ALL_IDLE) {
AssertLog.warn(">>>>>>[终端心跳超时]{} {}", state, sessionManager.client(ctx));
sessionManager.remove(ctx.channel());
ctx.close();
try {
GlobalConfig.isCollect = false;
appInitializer.run();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
@@ -0,0 +1,24 @@
package com.tongran.agent.client.netty.handler;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.socket.DatagramPacket;
import org.springframework.stereotype.Component;
/**
* @Description: UDP消息适配器
*/
@Component
@ChannelHandler.Sharable
public class UDPListenHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
DatagramPacket packet = (DatagramPacket) msg;
ByteBuf buf = packet.content();
buf.clear();// 暂未实现
}
}
@@ -0,0 +1,37 @@
package com.tongran.agent.client.netty.model;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
/**
* @author egrias
*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class Message implements Serializable {
private static final long serialVersionUID = -1267013167162440610L;
/**
* clientId
*/
private String clientId;
/**
* 数据类型:LOGIN、HEARTBEAT、CPU、MEMORY、SYSTEM、POINT、NET、DISK、DOCKER、SWITCHBOARD
*/
private String dataType;
/**
* 发送内容
*/
private String data;
}
@@ -0,0 +1,24 @@
package com.tongran.agent.client.netty.model;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
@Data
@SuperBuilder
@AllArgsConstructor
@NoArgsConstructor
public class UpMsgResponse {
private String clientId;// 终端号
/**
* type
*/
private String dataType;
private String content;
}
@@ -0,0 +1,37 @@
package com.tongran.agent.client.scheduler.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import java.util.concurrent.ThreadPoolExecutor;
@Configuration
@EnableScheduling
@EnableAsync
public class SchedulerConfig {
@Bean
public ThreadPoolTaskScheduler taskScheduler() {
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
scheduler.setPoolSize(10); // 核心线程数
scheduler.setThreadNamePrefix("dynamic-scheduler-");
scheduler.setAwaitTerminationSeconds(60);
scheduler.setWaitForTasksToCompleteOnShutdown(true);
scheduler.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
scheduler.setRemoveOnCancelPolicy(true);
return scheduler;
}
@Bean("taskExecutor")
public ThreadPoolTaskScheduler taskExecutor() {
ThreadPoolTaskScheduler executor = new ThreadPoolTaskScheduler();
executor.setPoolSize(10); // 工作线程数
executor.setThreadNamePrefix("task-worker-");
executor.setAwaitTerminationSeconds(30);
executor.setWaitForTasksToCompleteOnShutdown(true);
return executor;
}
}
@@ -0,0 +1,121 @@
package com.tongran.agent.client.scheduler.service;
import java.io.BufferedInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.function.Consumer;
public class AdvancedAsyncDownloader {
private static final ExecutorService downloadExecutor =
Executors.newFixedThreadPool(5);
/**
* 带进度回调的异步下载
*/
public static CompletableFuture<String> downloadWithProgress(
String fileUrl,
String saveDir,
String fileName,
Consumer<Double> progressCallback) {
return CompletableFuture.supplyAsync(() -> {
try {
URL url = new URL(fileUrl);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
// 获取文件大小
long fileSize = connection.getContentLengthLong();
// 创建保存目录
Path directory = Paths.get(saveDir);
if (!Files.exists(directory)) {
Files.createDirectories(directory);
}
String filePath = directory.resolve(fileName != null ? fileName :
extractFileNameFromUrl(fileUrl)).toString();
try (BufferedInputStream in = new BufferedInputStream(connection.getInputStream());
FileOutputStream out = new FileOutputStream(filePath)) {
byte[] buffer = new byte[8192];
int bytesRead;
long totalRead = 0;
while ((bytesRead = in.read(buffer)) != -1) {
out.write(buffer, 0, bytesRead);
totalRead += bytesRead;
// 回调进度
if (progressCallback != null && fileSize > 0) {
double progress = (double) totalRead / fileSize * 100;
progressCallback.accept(progress);
}
}
}
return filePath;
} catch (Exception e) {
throw new RuntimeException("下载失败", e);
}
}, downloadExecutor);
}
private static String extractFileNameFromUrl(String fileUrl) {
// 实现文件名提取逻辑
return fileUrl.substring(fileUrl.lastIndexOf('/') + 1);
}
/**
* 检查目录是否存在,不存在则创建(不创建父目录)
* @param directoryPath 目录路径
* @return 创建成功返回true,否则返回false
*/
public static boolean createSingleDirectoryIfNotExists(String directoryPath) {
try {
Path path = Paths.get(directoryPath);
if (!Files.exists(path)) {
// 只创建单级目录(父目录必须存在)
Files.createDirectory(path);
System.out.println("目录创建成功: " + directoryPath);
return true;
} else {
System.out.println("目录已存在: " + directoryPath);
return true;
}
} catch (IOException e) {
System.err.println("创建目录失败: " + directoryPath);
System.err.println("错误信息: " + e.getMessage());
return false;
}
}
/**
* 使用示例
*/
public static void main(String[] args) {
String fileUrl = "https://www.tzdsp.net/ksc-andromedae";
String saveDir = "D:/down";
downloadWithProgress(fileUrl, saveDir, null, progress -> {
System.out.printf("下载进度: %.1f%%\n", progress);
}).thenAccept(filePath -> {
System.out.println("下载完成: " + filePath);
}).exceptionally(ex -> {
System.err.println("下载错误: " + ex.getMessage());
return null;
});
// 主线程继续执行
System.out.println("异步下载已启动...");
}
}
@@ -0,0 +1,98 @@
package com.tongran.agent.client.scheduler.service;
import com.tongran.agent.client.core.config.GlobalConfig;
import com.tongran.agent.client.utils.AssertLog;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
@Component
public class AppInitializer implements CommandLineRunner {
private final BusinessTasks businessTasks;
private final DynamicTaskService dynamicTaskService;
// 使用构造函数注入,避免循环依赖
public AppInitializer(DynamicTaskService dynamicTaskService,
@Lazy BusinessTasks businessTasks) {
this.dynamicTaskService = dynamicTaskService;
this.businessTasks = businessTasks;
}
@Override
public void run(String... args) throws Exception {
System.out.println("应用启动完成,开始初始化定时任务...");
if(!GlobalConfig.isCollect){
// 开启定时任务
initScheduledTasks();
}else{
// 关闭定时任务
cancelTasks();
}
System.out.println("定时任务初始化完成");
}
private void initScheduledTasks() {
// 每30秒执行心跳任务
AssertLog.info("初始化启动心跳定时任务 - 延迟: {}ms, 间隔: {}ms", 5000, 30000);
dynamicTaskService.scheduleTask("heartbeat",
businessTasks::heartbeatTask, 5000, 30000);
// long milli = AgentUtil.getMillisToNextMinute() + 60000;
// // 每300秒执行CPU信息采集任务
// AssertLog.info("初始化启动CPU信息采集定时任务 - 延迟: {}ms, 间隔: {}ms", milli, 300000);
// dynamicTaskService.scheduleTask("cpu",
// businessTasks::cpuTask, milli, 300000); // 25秒后开始,每300秒执行
//
// // 每300秒执行磁盘信息采集任务
// AssertLog.info("初始化启动磁盘信息采集定时任务 - 延迟: {}ms, 间隔: {}ms", milli, 300000);
// dynamicTaskService.scheduleTask("disk",
// businessTasks::diskTask, milli, 300000); // 35秒后开始,每300秒执行
//
// // 每300秒执行系统信息采集任务
// AssertLog.info("初始化启动系统信息采集定时任务 - 延迟: {}ms, 间隔: {}ms", milli, 300000);
// dynamicTaskService.scheduleTask("system",
// businessTasks::systemTask, milli, 300000); // 40秒后开始,每300秒执行
//
// // 每300秒执行容器信息采集任务
// AssertLog.info("初始化启动容器信息采集定时任务 - 延迟: {}ms, 间隔: {}ms", milli, 300000);
// dynamicTaskService.scheduleTask("docker",
// businessTasks::dockerTask, milli, 300000); // 45秒后开始,每300秒执行
//
// // 每300秒执行内存信息采集任务
// AssertLog.info("初始化启动内存信息采集定时任务 - 延迟: {}ms, 间隔: {}ms", milli, 300000);
// dynamicTaskService.scheduleTask("memory",
// businessTasks::memoryTask, milli, 300000); // 55秒后开始,每300秒执行
//
//
// //获取当前时间距离下一个“分钟为 0 或 5”的时间点还差多少毫秒
// long millis = AgentUtil.millisecondsToNext5Minute();
// // 每300秒执行网络信息采集任务
// AssertLog.info("初始化启动网络信息采集定时任务 - 延迟: {}ms, 间隔: {}ms", millis, 300000);
// dynamicTaskService.scheduleTask("net",
// businessTasks::netTask, millis, 300000); // 65秒后开始,每300秒执行
//
// // 每300秒执行挂载信息采集任务
// AssertLog.info("初始化启动挂载信息采集定时任务 - 延迟: {}ms, 间隔: {}ms", milli, 300000);
// dynamicTaskService.scheduleTask("point",
// businessTasks::pointTask, milli, 300000); // 75秒后开始,每300秒执行
//
// // 每300秒执行交换机信息采集任务
// AssertLog.info("初始化启动交换机信息采集定时任务 - 延迟: {}ms, 间隔: {}ms", millis, 300000);
// dynamicTaskService.scheduleTask("switchBoard",
// businessTasks::switchBoardTask, millis, 300000); // 85秒后开始,每300秒执行
}
private void cancelTasks() {
dynamicTaskService.cancelTask("heartbeat");
// dynamicTaskService.cancelTask("cpu");
// dynamicTaskService.cancelTask("disk");
// dynamicTaskService.cancelTask("system");
// dynamicTaskService.cancelTask("docker");
// dynamicTaskService.cancelTask("memory");
// dynamicTaskService.cancelTask("net");
// dynamicTaskService.cancelTask("point");
// dynamicTaskService.cancelTask("switchBoard");
}
}
@@ -0,0 +1,273 @@
package com.tongran.agent.client.scheduler.service;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.PosixFilePermission;
import java.util.List;
import java.util.Set;
import java.util.concurrent.*;
import java.util.function.Consumer;
public class AsyncCommandExecutor {
// 使用线程池管理异步任务
private static final ExecutorService executorService = Executors.newCachedThreadPool();
private static final ScheduledExecutorService timeoutExecutor = Executors.newScheduledThreadPool(5);
/**
* 异步为文件添加可执行权限
*/
public static CompletableFuture<Boolean> makeFileExecutableAsync(String filePath) {
return CompletableFuture.supplyAsync(() -> {
try {
Path path = Paths.get(filePath);
if (!Files.exists(path)) {
throw new RuntimeException("文件不存在: " + filePath);
}
Set<PosixFilePermission> permissions = Files.getPosixFilePermissions(path);
permissions.add(PosixFilePermission.OWNER_EXECUTE);
permissions.add(PosixFilePermission.GROUP_EXECUTE);
permissions.add(PosixFilePermission.OTHERS_EXECUTE);
Files.setPosixFilePermissions(path, permissions);
return true;
} catch (Exception e) {
throw new RuntimeException("设置执行权限失败: " + e.getMessage(), e);
}
}, executorService);
}
/**
* 异步执行命令(基础版本)
*/
public static CompletableFuture<CommandResult> executeCommandAsync(
List<String> command,
long timeout,
TimeUnit timeUnit) {
return CompletableFuture.supplyAsync(() -> {
Process process = null;
try {
ProcessBuilder processBuilder = new ProcessBuilder(command);
processBuilder.redirectErrorStream(true);
// processBuilder.directory(new File("/data/agent-server")); // 设置工作目录
// 不合并错误流,分别读取
process = processBuilder.start();
// 异步读取输出
Process finalProcess = process;
Future<String> outputFuture = executorService.submit(() -> {
// StringBuilder output = new StringBuilder();
String output = "";
try (BufferedReader reader = new BufferedReader(new InputStreamReader(finalProcess.getInputStream()))) {
String line;
while ((line = reader.readLine()) != null) {
// output.append(line).append("\n");
output = line;
}
}
return output;
});
// 设置超时
if (timeout > 0) {
boolean finished = process.waitFor(timeout, timeUnit);
if (!finished) {
process.destroy();
throw new TimeoutException("命令执行超时");
}
} else {
process.waitFor();
}
String output = outputFuture.get(1, TimeUnit.SECONDS);
int exitCode = process.exitValue();
return new CommandResult(exitCode, output, "");
} catch (Exception e) {
return new CommandResult(-1, "", "执行失败: " + e.getMessage());
} finally {
if (process != null) {
process.destroy();
}
}
}, executorService);
}
/**
* 带实时输出的异步命令执行
*/
public static CompletableFuture<CommandResult> executeCommandWithRealtimeOutput(
String[] command,
long timeout,
TimeUnit timeUnit,
Consumer<String> outputConsumer,
Consumer<String> errorConsumer) {
return CompletableFuture.supplyAsync(() -> {
Process process = null;
try {
ProcessBuilder processBuilder = new ProcessBuilder(command);
process = processBuilder.start();
// 启动输出读取线程
CompletableFuture<Void> outputFuture = readStreamAsync(
process.getInputStream(), outputConsumer, "OUTPUT");
CompletableFuture<Void> errorFuture = readStreamAsync(
process.getErrorStream(), errorConsumer, "ERROR");
// 设置超时监控
ScheduledFuture<?> timeoutFuture = null;
if (timeout > 0) {
Process finalProcess = process;
timeoutFuture = timeoutExecutor.schedule(() -> {
if (finalProcess.isAlive()) {
finalProcess.destroy();
}
}, timeout, timeUnit);
}
// 等待进程完成
int exitCode = process.waitFor();
// 取消超时任务
if (timeoutFuture != null) {
timeoutFuture.cancel(false);
}
// 等待输出读取完成
CompletableFuture.allOf(outputFuture, errorFuture).get(2, TimeUnit.SECONDS);
return new CommandResult(exitCode, "", "");
} catch (Exception e) {
return new CommandResult(-1, "", "执行异常: " + e.getMessage());
} finally {
if (process != null) {
process.destroy();
}
}
}, executorService);
}
/**
* 异步读取流数据
*/
private static CompletableFuture<Void> readStreamAsync(
InputStream inputStream,
Consumer<String> lineConsumer,
String streamType) {
return CompletableFuture.runAsync(() -> {
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(inputStream))) {
String line;
while ((line = reader.readLine()) != null) {
if (lineConsumer != null) {
lineConsumer.accept("[" + streamType + "] " + line);
}
}
} catch (IOException e) {
if (lineConsumer != null) {
lineConsumer.accept("[" + streamType + "-ERROR] " + e.getMessage());
}
}
}, executorService);
}
/**
* 异步执行脚本文件
*/
public static CompletableFuture<CommandResult> executeScriptAsync(
String scriptPath,
String[] args,
long timeout,
TimeUnit timeUnit,
Consumer<String> realtimeOutputConsumer) {
return makeFileExecutableAsync(scriptPath)
.thenCompose(permissionSuccess -> {
if (!permissionSuccess) {
return CompletableFuture.completedFuture(
new CommandResult(-1, "", "设置执行权限失败"));
}
String[] command = new String[args.length + 1];
command[0] = scriptPath;
System.arraycopy(args, 0, command, 1, args.length);
return executeCommandWithRealtimeOutput(
command, timeout, timeUnit,
realtimeOutputConsumer,
error -> System.err.println(error));
});
}
/**
* 批量异步执行命令
*/
// public static List<CompletableFuture<CommandResult>> executeCommandsAsync(
// List<String[]> commands,
// long timeout,
// TimeUnit timeUnit) {
//
// return commands.stream()
// .map(command -> executeCommandAsync(command, timeout, timeUnit))
// .collect(java.util.stream.Collectors.toList());
// }
/**
* 关闭执行器
*/
public static void shutdown() {
executorService.shutdown();
timeoutExecutor.shutdown();
try {
if (!executorService.awaitTermination(5, TimeUnit.SECONDS)) {
executorService.shutdownNow();
}
if (!timeoutExecutor.awaitTermination(2, TimeUnit.SECONDS)) {
timeoutExecutor.shutdownNow();
}
} catch (InterruptedException e) {
executorService.shutdownNow();
timeoutExecutor.shutdownNow();
Thread.currentThread().interrupt();
}
}
/**
* 执行结果封装类
*/
public static class CommandResult {
private final int exitCode;
private final String output;
private final String error;
public CommandResult(int exitCode, String output, String error) {
this.exitCode = exitCode;
this.output = output;
this.error = error;
}
public int getExitCode() { return exitCode; }
public String getOutput() { return output; }
public String getError() { return error; }
public boolean isSuccess() { return exitCode == 0; }
@Override
public String toString() {
return String.format("Exit Code: %d\nOutput: %s\nError: %s",
exitCode, output, error);
}
}
}
@@ -0,0 +1,269 @@
package com.tongran.agent.client.scheduler.service;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.tongran.agent.client.core.config.ApplicationProperties;
import com.tongran.agent.client.core.config.GlobalConfig;
import com.tongran.agent.client.core.enums.MsgEnum;
import com.tongran.agent.client.core.session.SessionManager;
import com.tongran.agent.client.core.vo.*;
import com.tongran.agent.client.netty.model.Message;
import com.tongran.agent.client.service.*;
import com.tongran.agent.client.utils.AgentUtil;
import com.tongran.agent.client.utils.AssertLog;
import org.springframework.context.annotation.Lazy;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;
@Component
public class BusinessTasks {
private final AtomicInteger task1Counter = new AtomicInteger(0);
private final AtomicInteger task2Counter = new AtomicInteger(0);
private final AtomicInteger task3Counter = new AtomicInteger(0);
private final AtomicInteger task4Counter = new AtomicInteger(0);
private final AtomicInteger task5Counter = new AtomicInteger(0);
private final AtomicInteger task6Counter = new AtomicInteger(0);
private final AtomicInteger task7Counter = new AtomicInteger(0);
private final AtomicInteger task8Counter = new AtomicInteger(0);
private final AtomicInteger task9Counter = new AtomicInteger(0);
protected final SessionManager sessionManager;
private final BusinessTasks businessTasks;
private final DynamicTaskService dynamicTaskService;
// 使用构造函数注入,避免循环依赖
public BusinessTasks(DynamicTaskService dynamicTaskService,
@Lazy BusinessTasks businessTasks) {
this.dynamicTaskService = dynamicTaskService;
this.businessTasks = businessTasks;
this.sessionManager = SessionManager.getInstance();
}
@Resource
private CPUService cpuService;
@Resource
private DiskService diskService;
@Resource
private DockerService dockerService;
@Resource
private MemoryService memoryService;
@Resource
private NetService netService;
@Resource
private SwitchBoardService switchBoardService;
@Resource
private SystemService systemService;
@Resource
private ApplicationProperties properties;
/**
* 任务1:心跳上报任务
*/
@Async("taskExecutor")
public void heartbeatTask() {
long timestamp = System.currentTimeMillis();
timestamp = Math.round(timestamp / 1000.0);
int count = task1Counter.incrementAndGet();
AssertLog.info("心跳定时任务执行 - 时间: {},task #{}", LocalDateTime.now(), count);
// 业务处理
// 判定客户端与服务端是否连接
if (Objects.nonNull(sessionManager.getSessionById(GlobalConfig.CLIENT_ID))) {
// 发送心跳包
JSONObject object = new JSONObject();
object.put("strength","31");
object.put("name", properties.getName());
object.put("version", properties.getVersion());
object.put("startupTime", GlobalConfig.startupTime);
object.put("timestamp",timestamp);
Message message = Message.builder().clientId(GlobalConfig.CLIENT_ID).dataType(MsgEnum.心跳上报.getValue()).data(object.toString()).build();
sessionManager.writeAndFlush(sessionManager.getSessionById(GlobalConfig.CLIENT_ID).getChannel(), message);
}
AssertLog.info("心跳定时任务执行 - task #{} completed", count);
}
/**
* 任务2:cpu信息采集任务
*/
@Async("taskExecutor")
public void cpuTask() {
long timestamp = System.currentTimeMillis();
timestamp = Math.round(timestamp / 1000.0);
int count = task2Counter.incrementAndGet();
AssertLog.info("CPU信息采集定时任务执行 - 时间: {},task #{}", LocalDateTime.now(), count);
// 判定客户端与服务端是否连接
if (Objects.nonNull(sessionManager.getSessionById(GlobalConfig.CLIENT_ID))) {
// 发送CPU信息包
CpuVO cpuVO = cpuService.get();
cpuVO.setTimestamp(timestamp);
String data = JSON.toJSONString(cpuVO);
Message message = Message.builder().clientId(GlobalConfig.CLIENT_ID).dataType(MsgEnum.CPU上报.getValue()).data(data).build();
sessionManager.writeAndFlush(sessionManager.getSessionById(GlobalConfig.CLIENT_ID).getChannel(), message);
}
AssertLog.info("CPU信息采集定时任务执行 - task #{} completed", count);
}
/**
* 任务3:磁盘信息采集任务
*/
@Async("taskExecutor")
public void diskTask() {
long timestamp = System.currentTimeMillis();
timestamp = Math.round(timestamp / 1000.0);
int count = task3Counter.incrementAndGet();
AssertLog.info("磁盘信息采集定时任务执行 - 时间: {},task #{}", LocalDateTime.now(), count);
// 判定客户端与服务端是否连接
if (Objects.nonNull(sessionManager.getSessionById(GlobalConfig.CLIENT_ID))) {
// 发送磁盘信息包
List<DiskVO> list = diskService.diskList(timestamp);
String data = JSONArray.toJSONString(list);
Message message = Message.builder().clientId(GlobalConfig.CLIENT_ID).dataType(MsgEnum.磁盘上报.getValue()).data(data).build();
sessionManager.writeAndFlush(sessionManager.getSessionById(GlobalConfig.CLIENT_ID).getChannel(), message);
}
AssertLog.info("磁盘信息采集定时任务执行 - task #{} completed", count);
}
/**
* 任务4:容器信息采集任务
*/
@Async("taskExecutor")
public void dockerTask() {
long timestamp = System.currentTimeMillis();
timestamp = Math.round(timestamp / 1000.0);
int count = task4Counter.incrementAndGet();
AssertLog.info("容器信息采集定时任务执行 - 时间: {},task #{}", LocalDateTime.now(), count);
// 判定客户端与服务端是否连接
if (Objects.nonNull(sessionManager.getSessionById(GlobalConfig.CLIENT_ID))) {
// 发送容器包
List<DockerVO> list = dockerService.dockerList(timestamp);
String data = JSONArray.toJSONString(list);
Message message = Message.builder().clientId(GlobalConfig.CLIENT_ID).dataType(MsgEnum.容器上报.getValue()).data(data).build();
sessionManager.writeAndFlush(sessionManager.getSessionById(GlobalConfig.CLIENT_ID).getChannel(), message);
}
AssertLog.info("容器信息采集定时任务执行 - task #{} completed", count);
}
/**
* 任务5:内存信息采集任务
*/
@Async("taskExecutor")
public void memoryTask() {
long timestamp = System.currentTimeMillis();
timestamp = Math.round(timestamp / 1000.0);
int count = task5Counter.incrementAndGet();
AssertLog.info("内存信息采集定时任务执行 - 时间: {},task #{}", LocalDateTime.now(), count);
// 判定客户端与服务端是否连接
if (Objects.nonNull(sessionManager.getSessionById(GlobalConfig.CLIENT_ID))) {
// 发送内存信息包
MemoryVO memoryVO = memoryService.get();
memoryVO.setTimestamp(timestamp);
String data = JSON.toJSONString(memoryVO);
Message message = Message.builder().clientId(GlobalConfig.CLIENT_ID).dataType(MsgEnum.内存上报.getValue()).data(data).build();
sessionManager.writeAndFlush(sessionManager.getSessionById(GlobalConfig.CLIENT_ID).getChannel(), message);
}
AssertLog.info("内存信息采集定时任务执行 - task #{} completed", count);
}
/**
* 任务6:网络信息采集任务
*/
@Async("taskExecutor")
public void netTask() {
long timestamp = AgentUtil.roundMinutes();
int count = task6Counter.incrementAndGet();
AssertLog.info("网络信息采集定时任务执行 - 时间: {},task #{}", LocalDateTime.now(), count);
// 判定客户端与服务端是否连接
if (Objects.nonNull(sessionManager.getSessionById(GlobalConfig.CLIENT_ID))) {
// 发送网卡信息包
List<NetVO> list = netService.netList(timestamp);
String data = JSONArray.toJSONString(list);
Message message = Message.builder().clientId(GlobalConfig.CLIENT_ID).dataType(MsgEnum.网络上报.getValue()).data(data).build();
sessionManager.writeAndFlush(sessionManager.getSessionById(GlobalConfig.CLIENT_ID).getChannel(), message);
}
AssertLog.info("网络信息采集定时任务执行 - task #{} completed", count);
}
/**
* 任务7:挂载信息采集任务
*/
@Async("taskExecutor")
public void pointTask() {
long timestamp = System.currentTimeMillis();
timestamp = Math.round(timestamp / 1000.0);
int count = task7Counter.incrementAndGet();
AssertLog.info("挂载信息采集定时任务执行 - 时间: {},task #{}", LocalDateTime.now(), count);
// 判定客户端与服务端是否连接
if (Objects.nonNull(sessionManager.getSessionById(GlobalConfig.CLIENT_ID))) {
// 发送挂载点信息包
List<PointVO> list = diskService.pointList(timestamp);
String data = JSONArray.toJSONString(list);
Message message = Message.builder().clientId(GlobalConfig.CLIENT_ID).dataType(MsgEnum.挂载上报.getValue()).data(data).build();
sessionManager.writeAndFlush(sessionManager.getSessionById(GlobalConfig.CLIENT_ID).getChannel(), message);
}
AssertLog.info("挂载点信息采集定时任务执行 - task #{} completed", count);
}
/**
* 任务8:交换机信息采集任务
*/
@Async("taskExecutor")
public void switchBoardTask(String type) {
long timestamp = System.currentTimeMillis();
timestamp = Math.round(timestamp / 1000.0);
int count = task8Counter.incrementAndGet();
AssertLog.info("交换机信息采集定时任务执行 - 时间: {},task #{}", LocalDateTime.now(), count);
// 判定客户端与服务端是否连接
if (Objects.nonNull(sessionManager.getSessionById(GlobalConfig.CLIENT_ID))) {
// 发送交换机信息包
// List<SwitchBoardVO> list = switchBoardService.switchBoardList(timestamp);
// String data = JSONArray.toJSONString(list);
// Message message = Message.builder().clientId(GlobalConfig.CLIENT_ID).dataType(MsgEnum.交换机上报.getValue()).data(data).build();
String data = switchBoardService.getSwitchDataByType(type);
JSONObject jsonObject = JSONObject.parseObject(data);
jsonObject.put("timestamp", timestamp);
Message message = Message.builder().clientId(GlobalConfig.CLIENT_ID).dataType(MsgEnum.交换机上报.getValue()).data(jsonObject.toString()).build();
sessionManager.writeAndFlush(sessionManager.getSessionById(GlobalConfig.CLIENT_ID).getChannel(), message);
}
AssertLog.info("交换机信息采集定时任务执行 - task #{} completed", count);
}
/**
* 任务9:系统机信息采集任务
*/
@Async("taskExecutor")
public void systemTask(String type) {
long timestamp = System.currentTimeMillis();
int count = task9Counter.incrementAndGet();
AssertLog.info("系统信息采集定时任务执行 - 时间: {},task #{}", LocalDateTime.now(), count);
// 判定客户端与服务端是否连接
if (Objects.nonNull(sessionManager.getSessionById(GlobalConfig.CLIENT_ID))) {
// 发送系统信息包
// SystemVO systemVO = systemService.get();
// systemVO.setTimestamp(timestamp);
// String data = JSON.toJSONString(systemVO);
// Message message = Message.builder().clientId(GlobalConfig.CLIENT_ID).dataType(MsgEnum.系统其他上报.getValue()).data(data).build();
String data = systemService.otherSystem(type);
JSONObject jsonObject = JSONObject.parseObject(data);
jsonObject.put("timestamp", timestamp);
Message message = Message.builder().clientId(GlobalConfig.CLIENT_ID).dataType(MsgEnum.系统其他上报.getValue()).data(jsonObject.toString()).build();
sessionManager.writeAndFlush(sessionManager.getSessionById(GlobalConfig.CLIENT_ID).getChannel(), message);
}
AssertLog.info("系统信息采集定时任务执行 - task #{} completed", count);
}
}
@@ -0,0 +1,57 @@
package com.tongran.agent.client.scheduler.service;
import org.springframework.scheduling.Trigger;
import org.springframework.scheduling.TriggerContext;
import java.util.Date;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
public class CompensatedTrigger implements Trigger {
private final long period;
private final AtomicLong nextExecutionTime;
private final TimeUnit timeUnit;
public CompensatedTrigger(long initialDelay, long period) {
this(initialDelay, period, TimeUnit.MILLISECONDS);
}
public CompensatedTrigger(long initialDelay, long period, TimeUnit timeUnit) {
this.period = period;
this.timeUnit = timeUnit;
this.nextExecutionTime = new AtomicLong(
System.currentTimeMillis() + timeUnit.toMillis(initialDelay)
);
}
@Override
public Date nextExecutionTime(TriggerContext triggerContext) {
long lastCompletionTime = triggerContext.lastCompletionTime() != null
? triggerContext.lastCompletionTime().getTime()
: System.currentTimeMillis();
// 计算下一次执行时间(考虑补偿)
long currentNextTime = nextExecutionTime.get();
long now = System.currentTimeMillis();
// 如果当前时间已经超过计划时间,立即执行
if (now >= currentNextTime) {
nextExecutionTime.set(now + timeUnit.toMillis(period));
return new Date(now);
}
// 否则按计划时间执行
nextExecutionTime.set(currentNextTime + timeUnit.toMillis(period));
return new Date(currentNextTime);
}
public void updatePeriod(long newPeriod) {
// 更新执行间隔
long currentTime = System.currentTimeMillis();
long remaining = nextExecutionTime.get() - currentTime;
if (remaining > 0) {
nextExecutionTime.set(currentTime + timeUnit.toMillis(newPeriod));
}
}
}
@@ -0,0 +1,147 @@
package com.tongran.agent.client.scheduler.service;
import com.tongran.agent.client.core.config.GlobalConfig;
import lombok.AllArgsConstructor;
import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.stereotype.Service;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.atomic.AtomicLong;
@Service
public class DynamicTaskService {
@Autowired
private TaskScheduler taskScheduler;
private final Map<String, ScheduledFuture<?>> taskFutures = new ConcurrentHashMap<>();
private final Map<String, TaskConfig> taskConfigs = new ConcurrentHashMap<>();
private final Map<String, AtomicLong> taskExecutions = new ConcurrentHashMap<>();
private final Map<String, Long> lastExecutionTimes = new ConcurrentHashMap<>();
/**
* 添加或更新定时任务
*/
public void scheduleTask(String taskId, Runnable task, long initialDelay, long period) {
// 取消现有任务
cancelTask(taskId);
TaskConfig config = new TaskConfig(initialDelay, period, System.currentTimeMillis());
taskConfigs.put(taskId, config);
taskExecutions.put(taskId, new AtomicLong(0));
// 使用补偿机制调度任务
ScheduledFuture<?> future = taskScheduler.schedule(
createCompensatedTask(taskId, task),
new CompensatedTrigger(initialDelay, period)
);
taskFutures.put(taskId, future);
GlobalConfig.taskIds.put(taskId, 0L);
}
/**
* 创建带补偿的任务
*/
private Runnable createCompensatedTask(String taskId, Runnable originalTask) {
return () -> {
long startTime = System.currentTimeMillis();
lastExecutionTimes.put(taskId, startTime);
try {
originalTask.run();
} catch (Exception e) {
System.err.println("Task " + taskId + " execution failed: " + e.getMessage());
} finally {
long endTime = System.currentTimeMillis();
long executionTime = endTime - startTime;
taskExecutions.get(taskId).incrementAndGet();
System.out.printf("Task %s executed in %dms, total executions: %d%n",
taskId, executionTime, taskExecutions.get(taskId).get());
}
};
}
/**
* 取消任务
*/
public boolean cancelTask(String taskId) {
ScheduledFuture<?> future = taskFutures.remove(taskId);
if (future != null) {
future.cancel(false);
taskConfigs.remove(taskId);
taskExecutions.remove(taskId);
lastExecutionTimes.remove(taskId);
GlobalConfig.taskIds.remove(taskId);
return true;
}
return false;
}
/**
* 更新任务间隔
*/
public void updateTaskInterval(String taskId, long newPeriod) {
TaskConfig config = taskConfigs.get(taskId);
if (config != null) {
Runnable task = () -> {}; // 这里需要根据实际情况获取原始任务
scheduleTask(taskId, task, 0, newPeriod); // 立即重新调度
}
}
/**
* 获取任务状态
*/
public TaskStatus getTaskStatus(String taskId) {
TaskConfig config = taskConfigs.get(taskId);
AtomicLong executions = taskExecutions.get(taskId);
Long lastTime = lastExecutionTimes.get(taskId);
if (config != null && executions != null) {
return new TaskStatus(
taskId,
config.getPeriod(),
executions.get(),
lastTime,
taskFutures.get(taskId) != null && !taskFutures.get(taskId).isCancelled()
);
}
return null;
}
/**
* 获取所有任务状态
*/
public Map<String, TaskStatus> getAllTaskStatus() {
Map<String, TaskStatus> statusMap = new ConcurrentHashMap<>();
for (String taskId : taskConfigs.keySet()) {
statusMap.put(taskId, getTaskStatus(taskId));
}
return statusMap;
}
// 配置类
@Data
@AllArgsConstructor
public static class TaskConfig {
private long initialDelay;
private long period;
private long createTime;
}
// 状态类
@Data
@AllArgsConstructor
public static class TaskStatus {
private String taskId;
private long interval;
private long executionCount;
private Long lastExecutionTime;
private boolean isRunning;
}
}
@@ -0,0 +1,181 @@
package com.tongran.agent.client.scheduler.service;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import lombok.AllArgsConstructor;
import lombok.Data;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.io.IOException;
import java.net.ServerSocket;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@Service
public class DynamicTcpPortService {
private static final Logger log = LoggerFactory.getLogger(DynamicTcpPortService.class);
private final Map<Integer, TcpServerInstance> activeServers = new ConcurrentHashMap<>();
private EventLoopGroup bossGroup;
private EventLoopGroup workerGroup;
@PostConstruct
public void init() {
bossGroup = new NioEventLoopGroup(1);
workerGroup = new NioEventLoopGroup();
log.info("Dynamic TCP port service initialized");
}
/**
* 动态创建TCP服务器
*/
public TcpServerInstance createTcpServer(int port, ChannelHandler handler) throws Exception {
return createTcpServer(port, handler, null);
}
public TcpServerInstance createTcpServer(int port, ChannelHandler handler,
Map<String, Object> options) throws Exception {
if (activeServers.containsKey(port)) {
throw new IllegalArgumentException("Port " + port + " is already in use");
}
if (!isPortAvailable(port)) {
throw new IllegalArgumentException("Port " + port + " is not available");
}
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(handler);
// 设置可选参数
if (options != null) {
if (options.containsKey("soBacklog")) {
bootstrap.option(ChannelOption.SO_BACKLOG, (Integer) options.get("soBacklog"));
}
if (options.containsKey("soKeepalive")) {
bootstrap.childOption(ChannelOption.SO_KEEPALIVE, (Boolean) options.get("soKeepalive"));
}
if (options.containsKey("tcpNoDelay")) {
bootstrap.childOption(ChannelOption.TCP_NODELAY, (Boolean) options.get("tcpNoDelay"));
}
}
ChannelFuture future = bootstrap.bind(port).sync();
TcpServerInstance instance = new TcpServerInstance(port, future, handler, new Date());
activeServers.put(port, instance);
log.info("TCP server started on port: {}", port);
return instance;
}
/**
* 停止TCP服务器
*/
public boolean stopTcpServer(int port) {
TcpServerInstance instance = activeServers.get(port);
if (instance != null) {
try {
instance.getChannelFuture().channel().close().sync();
activeServers.remove(port);
log.info("TCP server stopped on port: {}", port);
return true;
} catch (InterruptedException e) {
log.error("Error stopping TCP server on port {}", port, e);
Thread.currentThread().interrupt();
}
}
return false;
}
/**
* 获取所有活跃的TCP服务器
*/
public List<TcpServerInstance> getActiveServers() {
return new ArrayList<>(activeServers.values());
}
/**
* 检查端口是否可用
*/
public boolean isPortAvailable(int port) {
try (ServerSocket socket = new ServerSocket(port)) {
return true;
} catch (IOException e) {
return false;
}
}
/**
* 获取可用端口列表
*/
public List<Integer> getAvailablePorts(int start, int end) {
List<Integer> availablePorts = new ArrayList<>();
for (int port = start; port <= end; port++) {
if (isPortAvailable(port) && !activeServers.containsKey(port)) {
availablePorts.add(port);
}
}
return availablePorts;
}
@PreDestroy
public void shutdown() {
// 停止所有TCP服务器
for (Integer port : new ArrayList<>(activeServers.keySet())) {
stopTcpServer(port);
}
// 关闭EventLoopGroup
if (bossGroup != null) {
bossGroup.shutdownGracefully();
}
if (workerGroup != null) {
workerGroup.shutdownGracefully();
}
log.info("Dynamic TCP port service shutdown completed");
}
/**
* TCP服务器实例信息类
*/
@Data
@AllArgsConstructor
public static class TcpServerInstance {
private int port;
private ChannelFuture channelFuture;
private ChannelHandler handler;
private Date startTime;
private int connectionCount;
public TcpServerInstance(int port, ChannelFuture channelFuture,
ChannelHandler handler, Date startTime) {
this.port = port;
this.channelFuture = channelFuture;
this.handler = handler;
this.startTime = startTime;
this.connectionCount = 0;
}
public void incrementConnectionCount() {
this.connectionCount++;
}
public String getStatus() {
return channelFuture.channel().isActive() ? "ACTIVE" : "INACTIVE";
}
}
}
@@ -0,0 +1,182 @@
package com.tongran.agent.client.scheduler.service;
import java.io.BufferedInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URL;
import java.net.URLConnection;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
public class Java8FileDownloader {
/**
* 下载结果类
*/
public static class DownloadResult {
private boolean success;
private String message;
private long expectedSize;
private long downloadedSize;
private String filePath;
public DownloadResult() {
this.success = false;
this.message = "";
}
// Getter和Setter方法
public boolean isSuccess() { return success; }
public void setSuccess(boolean success) { this.success = success; }
public String getMessage() { return message; }
public void setMessage(String message) { this.message = message; }
public long getExpectedSize() { return expectedSize; }
public void setExpectedSize(long expectedSize) { this.expectedSize = expectedSize; }
public long getDownloadedSize() { return downloadedSize; }
public void setDownloadedSize(long downloadedSize) { this.downloadedSize = downloadedSize; }
public String getFilePath() { return filePath; }
public void setFilePath(String filePath) { this.filePath = filePath; }
@Override
public String toString() {
if (success) {
return String.format("下载成功: %s (%d/%d 字节)",
filePath, downloadedSize, expectedSize);
} else {
return String.format("下载失败: %s", message);
}
}
}
/**
* 同步下载文件
* @param fileURL 文件URL地址
* @param saveDir 保存目录
* @param fileName 文件名(如果为null则从URL获取)
* @return 下载结果
*/
public static DownloadResult downloadFile(String fileURL, String saveDir, String fileName) {
DownloadResult result = new DownloadResult();
try {
// 创建保存目录
Path dirPath = Paths.get(saveDir);
if (!Files.exists(dirPath)) {
Files.createDirectories(dirPath);
}
// 获取文件名
String actualFileName = getFileName(fileURL, fileName);
String savePath = Paths.get(saveDir, actualFileName).toString();
result.setFilePath(savePath);
URL url = new URL(fileURL);
URLConnection connection = url.openConnection();
connection.setConnectTimeout(10000); // 10秒连接超时
connection.setReadTimeout(30000); // 30秒读取超时
// 获取文件信息
long expectedSize = connection.getContentLengthLong();
String contentType = connection.getContentType();
result.setExpectedSize(expectedSize);
System.out.println("开始下载: " + actualFileName);
System.out.println("文件大小: " + formatFileSize(expectedSize));
System.out.println("文件类型: " + contentType);
// 下载文件
try (BufferedInputStream in = new BufferedInputStream(connection.getInputStream());
FileOutputStream out = new FileOutputStream(savePath)) {
byte[] buffer = new byte[8192]; // 8KB缓冲区
int bytesRead;
long totalRead = 0;
while ((bytesRead = in.read(buffer)) != -1) {
out.write(buffer, 0, bytesRead);
totalRead += bytesRead;
// 显示进度(可选)
if (expectedSize > 0) {
int progress = (int) ((totalRead * 100) / expectedSize);
if (progress % 10 == 0) { // 每10%显示一次进度
System.out.printf("下载进度: %d%%\n", progress);
}
}
}
result.setDownloadedSize(totalRead);
result.setSuccess(true);
// 验证文件完整性
if (expectedSize > 0 && totalRead != expectedSize) {
result.setSuccess(false);
result.setMessage(String.format("文件不完整: 期望 %d 字节, 实际 %d 字节",
expectedSize, totalRead));
} else {
result.setMessage("下载完成,文件完整性验证通过");
}
}
} catch (IOException e) {
result.setSuccess(false);
result.setMessage("下载失败: " + e.getClass().getSimpleName() + " - " + e.getMessage());
}
return result;
}
/**
* 从URL中提取文件名
*/
private static String getFileName(String fileURL, String customFileName) {
if (customFileName != null && !customFileName.trim().isEmpty()) {
return customFileName;
}
try {
URL url = new URL(fileURL);
String path = url.getPath();
if (path.contains("/")) {
return path.substring(path.lastIndexOf("/") + 1);
}
return "downloaded_file";
} catch (Exception e) {
return "downloaded_file";
}
}
/**
* 格式化文件大小
*/
private static String formatFileSize(long size) {
if (size <= 0) return "0 B";
final String[] units = new String[]{"B", "KB", "MB", "GB", "TB"};
int digitGroups = (int) (Math.log10(size) / Math.log10(1024));
return String.format("%.1f %s", size / Math.pow(1024, digitGroups), units[digitGroups]);
}
/**
* 验证文件是否完整下载
*/
public static boolean verifyFileCompletion(String filePath, long expectedSize) {
try {
Path path = Paths.get(filePath);
if (Files.exists(path)) {
long actualSize = Files.size(path);
return actualSize == expectedSize;
}
return false;
} catch (IOException e) {
return false;
}
}
}
@@ -0,0 +1,63 @@
package com.tongran.agent.client.scheduler.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.Map;
@RestController
@RequestMapping("/api/scheduler")
public class SchedulerController {
@Autowired
private DynamicTaskService dynamicTaskService;
/**
* 获取所有任务状态
*/
@GetMapping("/tasks")
public Map<String, DynamicTaskService.TaskStatus> getAllTasks() {
return dynamicTaskService.getAllTaskStatus();
}
/**
* 更新任务间隔
*/
@PostMapping("/{taskId}/interval")
public String updateInterval(@PathVariable String taskId,
@RequestParam long intervalMs) {
dynamicTaskService.updateTaskInterval(taskId, intervalMs);
return "Task " + taskId + " interval updated to " + intervalMs + "ms";
}
/**
* 暂停任务
*/
@PostMapping("/{taskId}/pause")
public String pauseTask(@PathVariable String taskId) {
boolean success = dynamicTaskService.cancelTask(taskId);
return success ? "Task paused" : "Task not found";
}
/**
* 恢复任务
*/
@PostMapping("/{taskId}/resume")
public String resumeTask(@PathVariable String taskId,
@RequestParam(defaultValue = "30000") long intervalMs) {
// 这里需要根据taskId获取对应的Runnable任务
// 实际项目中可能需要一个任务注册表
Runnable task = getTaskById(taskId);
if (task != null) {
// dynamicTaskService.scheduleTask(taskId, task, 0, intervalMs);
return "Task resumed with interval " + intervalMs + "ms";
}
return "Task not found";
}
private Runnable getTaskById(String taskId) {
// 根据taskId返回对应的Runnable
// 实际项目中可以维护一个任务注册表
return () -> {};
}
}
@@ -0,0 +1,53 @@
package com.tongran.agent.client.scheduler.task;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
import java.util.List;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class SpecificTimeRequest {
private String taskId;
private String taskName;
private List<String> taskData;
private String clientId;
private String dataType;
// 多种时间指定方式
private String cronExpression; // Cron表达式
private LocalDateTime specificDateTime; // 具体日期时间
private String time; // 每天固定时间(HH:mm:ss
private Integer delaySeconds; // 延迟秒数
// // 构造方法、getter、setter
// public SpecificTimeRequest() {}
//
// // getter 和 setter 方法
// public String getTaskId() { return taskId; }
// public void setTaskId(String taskId) { this.taskId = taskId; }
//
// public String getTaskName() { return taskName; }
// public void setTaskName(String taskName) { this.taskName = taskName; }
//
// public String getTaskData() { return taskData; }
// public void setTaskData(String taskData) { this.taskData = taskData; }
//
// public String getCronExpression() { return cronExpression; }
// public void setCronExpression(String cronExpression) { this.cronExpression = cronExpression; }
//
// public LocalDateTime getSpecificDateTime() { return specificDateTime; }
// public void setSpecificDateTime(LocalDateTime specificDateTime) { this.specificDateTime = specificDateTime; }
//
// public String getTime() { return time; }
// public void setTime(String time) { this.time = time; }
//
// public Integer getDelaySeconds() { return delaySeconds; }
// public void setDelaySeconds(Integer delaySeconds) { this.delaySeconds = delaySeconds; }
}
@@ -0,0 +1,114 @@
package com.tongran.agent.client.scheduler.task;
import lombok.Data;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.CronTask;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledFuture;
@Data
@Configuration
public class SpecificTimeTaskConfig implements SchedulingConfigurer {
private ScheduledTaskRegistrar taskRegistrar;
private final Map<String, ScheduledFuture<?>> taskFutures = new ConcurrentHashMap<>();
private final Map<String, Object> taskMap = new ConcurrentHashMap<>();
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
this.taskRegistrar = taskRegistrar;
}
/**
* 添加Cron表达式任务
*/
public void addCronTask(String taskId, Runnable task, String cronExpression) {
removeTaskIfExists(taskId);
CronTask cronTask = new CronTask(task, cronExpression);
ScheduledFuture<?> future = taskRegistrar.getScheduler().schedule(
cronTask.getRunnable(),
cronTask.getTrigger()
);
taskMap.put(taskId, cronTask);
taskFutures.put(taskId, future);
}
/**
* 添加指定日期时间任务(只执行一次)
*/
public void addSpecificDateTimeTask(String taskId, Runnable task, LocalDateTime dateTime) {
removeTaskIfExists(taskId);
Date startTime = Date.from(dateTime.atZone(ZoneId.systemDefault()).toInstant());
long delay = startTime.getTime() - System.currentTimeMillis();
if (delay < 0) {
throw new IllegalArgumentException("指定时间不能是过去时间");
}
ScheduledFuture<?> future = taskRegistrar.getScheduler().schedule(
task,
new Date(System.currentTimeMillis() + delay)
);
taskMap.put(taskId, "ONCE_" + dateTime.toString());
taskFutures.put(taskId, future);
}
/**
* 添加每天固定时间任务
*/
public void addDailyTimeTask(String taskId, Runnable task, String time) {
String[] timeParts = time.split(":");
if (timeParts.length < 2) {
throw new IllegalArgumentException("时间格式应为 HH:mm 或 HH:mm:ss");
}
int hour = Integer.parseInt(timeParts[0]);
int minute = Integer.parseInt(timeParts[1]);
int second = timeParts.length > 2 ? Integer.parseInt(timeParts[2]) : 0;
String cronExpression = String.format("%d %d %d * * ?", second, minute, hour);
addCronTask(taskId, task, cronExpression);
}
/**
* 添加延迟任务
*/
public void addDelayTask(String taskId, Runnable task, int delaySeconds) {
removeTaskIfExists(taskId);
ScheduledFuture<?> future = taskRegistrar.getScheduler().schedule(
task,
new Date(System.currentTimeMillis() + delaySeconds * 1000L)
);
taskMap.put(taskId, "DELAY_" + delaySeconds);
taskFutures.put(taskId, future);
}
private void removeTaskIfExists(String taskId) {
if (taskFutures.containsKey(taskId)) {
taskFutures.get(taskId).cancel(true);
taskFutures.remove(taskId);
taskMap.remove(taskId);
}
}
public void removeTask(String taskId) {
removeTaskIfExists(taskId);
}
public boolean containsTask(String taskId) {
return taskMap.containsKey(taskId);
}
}
@@ -0,0 +1,116 @@
package com.tongran.agent.client.scheduler.task;
import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson2.JSONObject;
import com.tongran.agent.client.core.session.SessionManager;
import com.tongran.agent.client.netty.model.Message;
import com.tongran.agent.client.scheduler.service.AsyncCommandExecutor;
import com.tongran.agent.client.utils.AgentUtil;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
@Service
public class SpecificTimeTaskService {
protected final SessionManager sessionManager;
@Resource
private SpecificTimeTaskConfig taskConfig;
public SpecificTimeTaskService() {
this.sessionManager = SessionManager.getInstance();
}
/**
* 创建指定时间任务
*/
public boolean createSpecificTimeTask(SpecificTimeRequest request) {
String taskId = request.getTaskId() != null ?
request.getTaskId() : "task-" + System.currentTimeMillis();
Runnable task = createTaskRunnable(request);
try {
if (request.getCronExpression() != null) {
taskConfig.addCronTask(taskId, task, request.getCronExpression());
} else if (request.getSpecificDateTime() != null) {
taskConfig.addSpecificDateTimeTask(taskId, task, request.getSpecificDateTime());
} else if (request.getTime() != null) {
taskConfig.addDailyTimeTask(taskId, task, request.getTime());
} else if (request.getDelaySeconds() != null) {
taskConfig.addDelayTask(taskId, task, request.getDelaySeconds());
} else {
throw new IllegalArgumentException("必须指定一种时间方式");
}
return true;
} catch (Exception e) {
throw new RuntimeException("创建任务失败: " + e.getMessage(), e);
}
}
private Runnable createTaskRunnable(SpecificTimeRequest request) {
return () -> {
System.out.println("执行定时任务: " + request.getTaskName());
System.out.println("任务数据: " + request.getTaskData());
System.out.println("执行时间: " + LocalDateTime.now());
System.out.println("-----------------------------------");
// 具体的业务逻辑
executeBusinessLogic(request);
};
}
private void executeBusinessLogic(SpecificTimeRequest request) {
// 实现你的业务逻辑
try {
System.out.println("处理业务: " + request.getTaskData());
List<Map<String,String>> list = new ArrayList<>();
for (String command : request.getTaskData()) {
Map<String,String> map = new HashMap<>();
List<String> cmd = Arrays.asList(command.split("\\s+"));
CompletableFuture<AsyncCommandExecutor.CommandResult> future =
AsyncCommandExecutor.executeCommandAsync(
cmd,
60, TimeUnit.SECONDS);
future.thenAccept(result -> {
if (result.isSuccess()) {
System.out.println("脚本执行成功");
System.out.println("[成功resOut] " + result.getOutput());
map.put("command",command);
map.put("resOut",result.getOutput());
} else {
System.out.println("脚本执行失败");
System.out.println("[失败resOut] " + result.getOutput());
map.put("command",command);
map.put("resOut",result.getOutput());
}
}).exceptionally(ex -> {
System.err.println("执行失败: " + ex.getMessage());
map.put("command",command);
map.put("resOut","Policy execute filed");
return null;
});
list.add(map);
}
if(CollectionUtil.isNotEmpty(list)){
JSONObject json = new JSONObject();
json.put("resCode",1);
json.put("resMsg", "");
json.put("result", AgentUtil.toJsonString(list));
Message message = Message.builder().clientId(request.getClientId()).dataType(request.getDataType()).data(json.toString()).build();
if (Objects.nonNull(sessionManager.getSessionById(request.getClientId()))) {
sessionManager.writeAndFlush(sessionManager.getSessionById(request.getClientId()).getChannel(), message);
}
}
// 调用其他服务等
} catch (Exception e) {
System.err.println("任务执行异常: " + e.getMessage());
}
}
}
@@ -0,0 +1,11 @@
package com.tongran.agent.client.service;
public interface AgentService {
void systemCollectStart(String data);
void systemCollectStop();
void switchCollectStart(String data);
void switchCollectStop();
}
@@ -0,0 +1,7 @@
package com.tongran.agent.client.service;
import com.tongran.agent.client.core.vo.CpuVO;
public interface CPUService {
CpuVO get();
}
@@ -0,0 +1,12 @@
package com.tongran.agent.client.service;
import com.tongran.agent.client.core.vo.DiskVO;
import com.tongran.agent.client.core.vo.PointVO;
import java.util.List;
public interface DiskService {
List<DiskVO> diskList(long timestamp);
List<PointVO> pointList(long timestamp);
}
@@ -0,0 +1,9 @@
package com.tongran.agent.client.service;
import com.tongran.agent.client.core.vo.DockerVO;
import java.util.List;
public interface DockerService {
List<DockerVO> dockerList(long timestamp);
}
@@ -0,0 +1,7 @@
package com.tongran.agent.client.service;
import com.tongran.agent.client.core.vo.MemoryVO;
public interface MemoryService {
MemoryVO get();
}
@@ -0,0 +1,9 @@
package com.tongran.agent.client.service;
import com.tongran.agent.client.core.vo.NetVO;
import java.util.List;
public interface NetService {
List<NetVO> netList(long timestamp);
}
@@ -0,0 +1,11 @@
package com.tongran.agent.client.service;
import com.tongran.agent.client.core.vo.SwitchBoardVO;
import java.util.List;
public interface SwitchBoardService {
List<SwitchBoardVO> switchBoardList(long timestamp);
String getSwitchDataByType(String type);
}
@@ -0,0 +1,9 @@
package com.tongran.agent.client.service;
import com.tongran.agent.client.core.vo.SystemVO;
public interface SystemService {
SystemVO get();
String otherSystem(String type);
}
@@ -0,0 +1,381 @@
package com.tongran.agent.client.service.impl;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.alibaba.fastjson2.TypeReference;
import com.tongran.agent.client.core.config.GlobalConfig;
import com.tongran.agent.client.core.eo.CollectEO;
import com.tongran.agent.client.scheduler.service.BusinessTasks;
import com.tongran.agent.client.scheduler.service.DynamicTaskService;
import com.tongran.agent.client.service.AgentService;
import com.tongran.agent.client.utils.AgentUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class AgentServiceImpl implements AgentService {
private final DynamicTaskService dynamicTaskService;
private final BusinessTasks businessTasks;
// 在注入点使用@Lazy
@Autowired
public AgentServiceImpl(DynamicTaskService dynamicTaskService,
@Lazy BusinessTasks businessTasks) {
this.dynamicTaskService = dynamicTaskService;
this.businessTasks = businessTasks;
}
@Override
public void systemCollectStart(String data) {
//更改全局变量
JSONObject jsonObject = JSONObject.parseObject(data);
if(jsonObject.containsKey("collects")){
String collects = jsonObject.getString("collects");
List<CollectEO> configList = JSON.parseObject(collects, new TypeReference<List<CollectEO>>() {});
for (CollectEO c : configList) {
String type = c.getType();
boolean collect = c.isCollect();
switch(type) {
case "cpuCollect": //cpu采集
GlobalConfig.cpuCollect = collect;
GlobalConfig.cpuInterval = c.getInterval();
if(collect){
long milli = AgentUtil.getMillisToNextMinute() + 60000;
dynamicTaskService.scheduleTask(c.getType(),
businessTasks::cpuTask, milli, GlobalConfig.cpuInterval*1000L);
}
break;
case "vfsCollect": //挂载采集
GlobalConfig.vfsCollect = collect;
GlobalConfig.vfsInterval = c.getInterval();
if(collect){
long milli = AgentUtil.getMillisToNextMinute() + 60000;
dynamicTaskService.scheduleTask(c.getType(),
businessTasks::pointTask, milli, GlobalConfig.vfsInterval*1000L);
}
break;
case "netCollect": //网络采集
GlobalConfig.netCollect = collect;
GlobalConfig.netInterval = c.getInterval();
if(collect){
long milli = AgentUtil.millisecondsToNext5Minute();
dynamicTaskService.scheduleTask(c.getType(),
businessTasks::netTask, milli, GlobalConfig.netInterval*1000L);
}
break;
case "diskCollect": //磁盘采集
GlobalConfig.diskCollect = collect;
GlobalConfig.diskInterval = c.getInterval();
if(collect){
long milli = AgentUtil.getMillisToNextMinute() + 60000;
dynamicTaskService.scheduleTask(c.getType(),
businessTasks::diskTask, milli, GlobalConfig.diskInterval*1000L);
}
break;
case "dockerCollect": //docker采集
GlobalConfig.dockerCollect = collect;
GlobalConfig.dockerInterval = c.getInterval();
if(collect){
long milli = AgentUtil.getMillisToNextMinute() + 60000;
dynamicTaskService.scheduleTask(c.getType(),
businessTasks::dockerTask, milli, GlobalConfig.dockerInterval*1000L);
}
break;
default: //系统其他采集
if(StringUtils.equals(type, "systemSwapSizeFreeCollect")){
GlobalConfig.systemSwapSizeFreeCollect = collect;
GlobalConfig.systemSwapSizeFreeInterval = c.getInterval();
}
if(StringUtils.equals(type, "memoryUtilizationCollect")){
GlobalConfig.memoryUtilizationCollect = collect;
GlobalConfig.memoryUtilizationInterval = c.getInterval();
}
if(StringUtils.equals(type, "systemSwapSizePercentCollect")){
GlobalConfig.systemSwapSizePercentCollect = collect;
GlobalConfig.systemSwapSizePercentInterval = c.getInterval();
}
if(StringUtils.equals(type, "memorySizeAvailableCollect")){
GlobalConfig.memorySizeAvailableCollect = collect;
GlobalConfig.memorySizeAvailableInterval = c.getInterval();
}
if(StringUtils.equals(type, "memorySizePercentCollect")){
GlobalConfig.memorySizePercentCollect = collect;
GlobalConfig.memorySizePercentInterval = c.getInterval();
}
if(StringUtils.equals(type, "memorySizeTotalCollect")){
GlobalConfig.memorySizeTotalCollect = collect;
GlobalConfig.memorySizeTotalInterval = c.getInterval();
}
if(StringUtils.equals(type, "systemSwOsCollect")){
GlobalConfig.systemSwOsCollect = collect;
GlobalConfig.systemSwOsInterval = c.getInterval();
}
if(StringUtils.equals(type, "systemSwArchCollect")){
GlobalConfig.systemSwArchCollect = collect;
GlobalConfig.systemSwArchInterval = c.getInterval();
}
if(StringUtils.equals(type, "kernelMaxprocCollect")){
GlobalConfig.kernelMaxprocCollect = collect;
GlobalConfig.kernelMaxprocInterval = c.getInterval();
}
if(StringUtils.equals(type, "procNumRunCollect")){
GlobalConfig.procNumRunCollect = collect;
GlobalConfig.procNumRunInterval = c.getInterval();
}
if(StringUtils.equals(type, "systemUsersNumCollect")){
GlobalConfig.systemUsersNumCollect = collect;
GlobalConfig.systemUsersNumInterval = c.getInterval();
}
if(StringUtils.equals(type, "systemDiskSizeTotalCollect")){
GlobalConfig.systemDiskSizeTotalCollect = collect;
GlobalConfig.systemDiskSizeTotalInterval = c.getInterval();
}
if(StringUtils.equals(type, "systemBoottimeCollect")){
GlobalConfig.systemBoottimeCollect = collect;
GlobalConfig.systemBoottimeInterval = c.getInterval();
}
if(StringUtils.equals(type, "systemUnameCollect")){
GlobalConfig.systemUnameCollect = collect;
GlobalConfig.systemUnameInterval = c.getInterval();
}
if(StringUtils.equals(type, "systemLocaltimeCollect")){
GlobalConfig.systemLocaltimeCollect = collect;
GlobalConfig.systemLocaltimeInterval = c.getInterval();
}
if(StringUtils.equals(type, "systemUptimeCollect")){
GlobalConfig.systemUptimeCollect = collect;
GlobalConfig.systemUptimeInterval = c.getInterval();
}
if(StringUtils.equals(type, "procNumCollect")){
GlobalConfig.procNumCollect = collect;
GlobalConfig.procNumInterval = c.getInterval();
}
long milli = AgentUtil.getMillisToNextMinute() + 60000;
dynamicTaskService.scheduleTask(c.getType(),
() -> businessTasks.systemTask(c.getType()), milli, c.getInterval()*1000L);
break;
}
}
}
}
@Override
public void systemCollectStop() {
String[] arr = {"cpuCollect","vfsCollect","netCollect","diskCollect","dockerCollect","systemSwapSizeFreeCollect","memoryUtilizationCollect"
,"systemSwapSizePercentCollect","memorySizeAvailableCollect","memorySizePercentCollect","memorySizeTotalCollect","systemSwOsCollect"
,"systemSwArchCollect","kernelMaxprocCollect","procNumRunCollect","systemUsersNumCollect","systemDiskSizeTotalCollect","systemBoottimeCollect"
,"systemUnameCollect","systemLocaltimeCollect","systemUptimeCollect","procNumCollect"};
for (String taskId : arr) {
dynamicTaskService.cancelTask(taskId);
}
/**
* 系统监控
*/
GlobalConfig.cpuCollect = false; //cpu采集
GlobalConfig.cpuInterval = 300; //cpu采集间隔
GlobalConfig.vfsCollect = false; //挂载采集
GlobalConfig.vfsInterval = 300; //挂载采集间隔
GlobalConfig.netCollect = false; //网络采集
GlobalConfig.netInterval = 300; //网络采集间隔
GlobalConfig.diskCollect = false; //硬盘采集
GlobalConfig.diskInterval = 300; //硬盘采集间隔
GlobalConfig.dockerCollect = false; //docker采集
GlobalConfig.dockerInterval = 300; //docker采集间隔
/**
* 系统其他监控
*/
GlobalConfig.systemSwapSizeFreeCollect = false; //交换卷/文件的可用空间(字节)采集
GlobalConfig.systemSwapSizeFreeInterval = 300; //交换卷/文件的可用空间(字节)采集间隔
GlobalConfig.memoryUtilizationCollect = false; //内存利用率采集
GlobalConfig.memoryUtilizationInterval = 300; //内存利用率采集间隔
GlobalConfig.systemSwapSizePercentCollect = false; //可用交换空间百分比采集
GlobalConfig.systemSwapSizePercentInterval = 300; //可用交换空间百分比采集间隔
GlobalConfig.memorySizeAvailableCollect = false; //可用内存采集
GlobalConfig.memorySizeAvailableInterval = 300; //可用内存采集间隔
GlobalConfig.memorySizePercentCollect = false; //可用内存百分比采集
GlobalConfig.memorySizePercentInterval = 300; //可用内存百分比采集间隔
GlobalConfig.memorySizeTotalCollect = false; //总内存采集
GlobalConfig.memorySizeTotalInterval = 300; //总内存采集间隔
GlobalConfig.systemSwOsCollect = false; //操作系统采集
GlobalConfig.systemSwOsInterval = 300; //操作系统采集间隔
GlobalConfig.systemSwArchCollect = false; //操作系统架构采集
GlobalConfig.systemSwArchInterval = 300; //操作系统架构采集间隔
GlobalConfig.kernelMaxprocCollect = false; //最大进程数采集
GlobalConfig.kernelMaxprocInterval = 300; //最大进程数采集间隔
GlobalConfig.procNumRunCollect = false; //正在运行的进程数采集
GlobalConfig.procNumRunInterval = 300; //正在运行的进程数采集间隔
GlobalConfig.systemUsersNumCollect = false; //登录用户数采集
GlobalConfig.systemUsersNumInterval = 300; //登录用户数采集间隔
GlobalConfig.systemDiskSizeTotalCollect = false; ///硬盘总可用空间采集
GlobalConfig.systemDiskSizeTotalInterval = 300; //硬盘总可用空间采集间隔
GlobalConfig.systemBoottimeCollect = false; //系统启动时间采集
GlobalConfig.systemBoottimeInterval = 300; //系统启动时间采集间隔
GlobalConfig.systemUnameCollect = false; //系统描述采集
GlobalConfig.systemUnameInterval = 300; //系统描述采集间隔
GlobalConfig.systemLocaltimeCollect = false; //系统本地时间采集
GlobalConfig.systemLocaltimeInterval = 300; //系统本地时间采集间隔
GlobalConfig.systemUptimeCollect = false; //系统正常运行时间采集
GlobalConfig.systemUptimeInterval = 300; //系统正常运行时间采集间隔
GlobalConfig.procNumCollect = false; //进程数采集
GlobalConfig.procNumInterval = 300; //进程数采集间隔
}
@Override
public void switchCollectStart(String data) {
//更改全局变量
JSONObject jsonObject = JSONObject.parseObject(data);
if(jsonObject.containsKey("collects")){
String collects = jsonObject.getString("collects");
List<CollectEO> configList = JSON.parseObject(collects, new TypeReference<List<CollectEO>>() {});
for (CollectEO c : configList) {
String type = c.getType();
boolean collect = c.isCollect();
switch(type) {
case "switchNetCollect": //交换机网络采集
GlobalConfig.switchNetCollect = collect;
GlobalConfig.switchNetInterval = c.getInterval();
break;
case "switchModuleCollect": //光模块采集
GlobalConfig.switchModuleCollect = collect;
GlobalConfig.switchModuleInterval = c.getInterval();
break;
case "switchMpuCollect": //MPU采集
GlobalConfig.switchMpuCollect = collect;
GlobalConfig.switchMpuInterval = c.getInterval();
break;
case "switchPwrCollect": //电源采集
GlobalConfig.switchPwrCollect = collect;
GlobalConfig.switchPwrInterval = c.getInterval();
break;
case "dockerCollect": //风扇采集
GlobalConfig.switchFanCollect = collect;
GlobalConfig.switchFanInterval = c.getInterval();
break;
default: //系统其他采集
if(StringUtils.equals(type, "switchSysDescrCollect")){
GlobalConfig.switchSysDescrCollect = collect;
GlobalConfig.switchSysDescrInterval = c.getInterval();
}
if(StringUtils.equals(type, "switchSysObjectIDCollect")){
GlobalConfig.switchSysObjectIDCollect = collect;
GlobalConfig.switchSysObjectIDInterval = c.getInterval();
}
if(StringUtils.equals(type, "switchSysUpTimeCollect")){
GlobalConfig.switchSysUpTimeCollect = collect;
GlobalConfig.switchSysUpTimeInterval = c.getInterval();
}
if(StringUtils.equals(type, "switchSysContactCollect")){
GlobalConfig.switchSysContactCollect = collect;
GlobalConfig.switchSysContactInterval = c.getInterval();
}
if(StringUtils.equals(type, "switchSysNameCollect")){
GlobalConfig.switchSysNameCollect = collect;
GlobalConfig.switchSysNameInterval = c.getInterval();
}
if(StringUtils.equals(type, "switchSysLocationCollect")){
GlobalConfig.switchSysLocationCollect = collect;
GlobalConfig.switchSysLocationInterval = c.getInterval();
}
if(StringUtils.equals(type, "switchHwStackSystemMacCollect")){
GlobalConfig.switchHwStackSystemMacCollect = collect;
GlobalConfig.switchHwStackSystemMacInterval = c.getInterval();
}
if(StringUtils.equals(type, "switchEntIndexCollect")){
GlobalConfig.switchEntIndexCollect = collect;
GlobalConfig.switchEntIndexInterval = c.getInterval();
}
if(StringUtils.equals(type, "switchEntPhysicalNameCollect")){
GlobalConfig.switchEntPhysicalNameCollect = collect;
GlobalConfig.switchEntPhysicalNameInterval = c.getInterval();
}
if(StringUtils.equals(type, "switchEntPhysicalSoftwareRevCollect")){
GlobalConfig.switchEntPhysicalSoftwareRevCollect = collect;
GlobalConfig.switchEntPhysicalSoftwareRevInterval = c.getInterval();
}
if(StringUtils.equals(type, "switchHwEntityCpuUsageCollect")){
GlobalConfig.switchHwEntityCpuUsageCollect = collect;
GlobalConfig.switchHwEntityCpuUsageInterval = c.getInterval();
}
if(StringUtils.equals(type, "switchHwEntityMemUsageCollect")){
GlobalConfig.switchHwEntityMemUsageCollect = collect;
GlobalConfig.switchHwEntityMemUsageInterval = c.getInterval();
}
if(StringUtils.equals(type, "switchHwAveragePowerCollect")){
GlobalConfig.switchHwAveragePowerCollect = collect;
GlobalConfig.switchHwAveragePowerInterval = c.getInterval();
}
if(StringUtils.equals(type, "switchHwCurrentPowerCollect")){
GlobalConfig.switchHwCurrentPowerCollect = collect;
GlobalConfig.switchHwCurrentPowerInterval = c.getInterval();
}
break;
}
if(collect){
long milli = AgentUtil.millisecondsToNext5Minute();
dynamicTaskService.scheduleTask(c.getType(),
() -> businessTasks.switchBoardTask(c.getType()), milli, GlobalConfig.switchNetInterval*1000L);
}
}
}
}
@Override
public void switchCollectStop() {
String[] arr = {"switchNetCollect","switchModuleCollect","switchMpuCollect","switchPwrCollect","switchFanCollect","switchSysDescrCollect"
,"switchSysObjectIDCollect","switchSysUpTimeCollect","switchSysContactCollect","switchSysNameCollect","switchSysLocationCollect"
,"switchHwStackSystemMacCollect","switchEntIndexCollect","switchEntPhysicalNameCollect","switchEntPhysicalSoftwareRevCollect"
,"switchHwEntityCpuUsageCollect","switchHwEntityMemUsageCollect","switchHwAveragePowerCollect","switchHwCurrentPowerCollect"};
for (String taskId : arr) {
dynamicTaskService.cancelTask(taskId);
}
GlobalConfig.switchNetCollect = false; //交换机网络采集
GlobalConfig.switchNetInterval = 300; //交换机网络采集间隔
GlobalConfig.switchModuleCollect = false; //光模块采集
GlobalConfig.switchModuleInterval = 300; //光模块采集间隔
GlobalConfig.switchMpuCollect = false; //MPU采集
GlobalConfig.switchMpuInterval = 300; //MPU采集间隔
GlobalConfig.switchPwrCollect = false; //电源采集
GlobalConfig.switchPwrInterval = 300; //电源采集间隔
GlobalConfig.switchFanCollect = false; //风扇采集
GlobalConfig.switchFanInterval = 300; //风扇采集间隔
/**
* 交换机其他监控
*/
GlobalConfig.switchSysDescrCollect = false; //系统描述采集
GlobalConfig.switchSysDescrInterval = 300; //系统描述采集间隔
GlobalConfig.switchSysObjectIDCollect = false; //系统Object ID采集
GlobalConfig.switchSysObjectIDInterval = 300; //系统Object ID采集间隔
GlobalConfig.switchSysUpTimeCollect = false; //系统运行时间采集
GlobalConfig.switchSysUpTimeInterval = 300; //系统运行时间采集间隔
GlobalConfig.switchSysContactCollect = false; //系统联系信息采集
GlobalConfig.switchSysContactInterval = 300; //系统联系信息采集间隔
GlobalConfig.switchSysNameCollect = false; //系统名称采集
GlobalConfig.switchSysNameInterval = 300; //系统名称采集间隔
GlobalConfig.switchSysLocationCollect = false; //系统位置采集
GlobalConfig.switchSysLocationInterval = 300; //系统位置采集间隔
GlobalConfig.switchHwStackSystemMacCollect = false; //系统MAC地址采集
GlobalConfig.switchHwStackSystemMacInterval = 300; //系统MAC地址采集间隔
GlobalConfig.switchEntIndexCollect = false; //设备索引采集
GlobalConfig.switchEntIndexInterval = 300; //设备索引采集间隔
GlobalConfig.switchEntPhysicalNameCollect = false; //设备名称采集
GlobalConfig.switchEntPhysicalNameInterval = 300; //设备名称采集间隔
GlobalConfig.switchEntPhysicalSoftwareRevCollect = false; //设备软件版本采集
GlobalConfig.switchEntPhysicalSoftwareRevInterval = 300; //设备软件版本采集间隔
GlobalConfig.switchHwEntityCpuUsageCollect = false; //设备CPU使用率(%)采集
GlobalConfig.switchHwEntityCpuUsageInterval = 300; //设备CPU使用率(%)采集间隔
GlobalConfig.switchHwEntityMemUsageCollect = false; //设备内存使用率(%)采集
GlobalConfig.switchHwEntityMemUsageInterval = 300; //设备内存使用率(%)采集间隔
GlobalConfig.switchHwAveragePowerCollect = false; //系统平均功率(mW)采集
GlobalConfig.switchHwAveragePowerInterval = 300; //系统平均功率(mW)采集间隔
GlobalConfig.switchHwCurrentPowerCollect = false; //系统实时功率(mW)采集
GlobalConfig.switchHwCurrentPowerInterval = 300; //系统实时功率(mW)采集间隔
}
}
@@ -0,0 +1,98 @@
package com.tongran.agent.client.service.impl;
import com.tongran.agent.client.core.vo.CpuVO;
import com.tongran.agent.client.service.CPUService;
import org.springframework.stereotype.Service;
import oshi.SystemInfo;
import oshi.hardware.CentralProcessor;
import oshi.hardware.HardwareAbstractionLayer;
import oshi.software.os.OperatingSystem;
import java.util.concurrent.TimeUnit;
@Service
public class CPUServiceImpl implements CPUService {
@Override
public CpuVO get() {
CpuVO cpuVO = CpuVO.builder().build();
try {
SystemInfo si = new SystemInfo();
HardwareAbstractionLayer hal = si.getHardware();
CentralProcessor processor = hal.getProcessor();
OperatingSystem os = si.getOperatingSystem();
System.out.println("=========================================================");
// 1. CPU基本信息
cpuVO.setNum(processor.getPhysicalProcessorCount()); // CUP数量
System.out.println("=== CPU基本信息 ===");
System.out.println("CPU型号: " + processor.getProcessorIdentifier().getName());
System.out.println("物理核心数: " + processor.getPhysicalProcessorCount());
System.out.println("逻辑核心数: " + processor.getLogicalProcessorCount());
System.out.println("最大频率: " + processor.getMaxFreq() / 1_000_000.0 + " GHz");
// 2. CPU负载信息
System.out.println("\n=== CPU负载信息 ===");
double[] loadAverage = processor.getSystemLoadAverage(3);
cpuVO.setAvg1(loadAverage[0]);// CUP1分钟负载
cpuVO.setAvg1(loadAverage[1]);// CUP5分钟负载
cpuVO.setAvg1(loadAverage[2]);// CUP15分钟负载
System.out.println("1分钟平均负载: " + loadAverage[0]);
System.out.println("5分钟平均负载: " + loadAverage[1]);
System.out.println("15分钟平均负载: " + loadAverage[2]);
// 3. CPU使用率(需要两次采样)
System.out.println("\n=== CPU使用率 ===");
long[] prevTicks = processor.getSystemCpuLoadTicks();
TimeUnit.SECONDS.sleep(1); // 等待1秒
// 计算使用率
double cpuUsage = processor.getSystemCpuLoadBetweenTicks(prevTicks);
cpuVO.setUti(cpuUsage * 100); // CPU使用率
System.out.printf("CPU 使用率: %.2f%%\n", cpuUsage * 100);
long[] ticks = processor.getSystemCpuLoadTicks();
long user = ticks[CentralProcessor.TickType.USER.getIndex()] -
prevTicks[CentralProcessor.TickType.USER.getIndex()];
long nice = ticks[CentralProcessor.TickType.NICE.getIndex()] -
prevTicks[CentralProcessor.TickType.NICE.getIndex()];
long sys = ticks[CentralProcessor.TickType.SYSTEM.getIndex()] -
prevTicks[CentralProcessor.TickType.SYSTEM.getIndex()];
long idle = ticks[CentralProcessor.TickType.IDLE.getIndex()] -
prevTicks[CentralProcessor.TickType.IDLE.getIndex()];
long iowait = ticks[CentralProcessor.TickType.IOWAIT.getIndex()] -
prevTicks[CentralProcessor.TickType.IOWAIT.getIndex()];
long irq = ticks[CentralProcessor.TickType.IRQ.getIndex()] -
prevTicks[CentralProcessor.TickType.IRQ.getIndex()];
long softirq = ticks[CentralProcessor.TickType.SOFTIRQ.getIndex()] -
prevTicks[CentralProcessor.TickType.SOFTIRQ.getIndex()];
long steal = ticks[CentralProcessor.TickType.STEAL.getIndex()] -
prevTicks[CentralProcessor.TickType.STEAL.getIndex()];
long total = user + nice + sys + idle + iowait + irq + softirq + steal;
// 4. CPU时间累计值
System.out.println("\n=== CPU时间累计值 ===");
long[] allTicks = processor.getSystemCpuLoadTicks();
System.out.println("中断累计时间: " +
(allTicks[CentralProcessor.TickType.IRQ.getIndex()] +
allTicks[CentralProcessor.TickType.SOFTIRQ.getIndex()]));
System.out.println("空闲累计时间: " + allTicks[CentralProcessor.TickType.IDLE.getIndex()]);
System.out.printf("I/O等待时间(CPU等待响应时间): %.2f%%\n", 100d * iowait / total);
System.out.println("系统累计时间: " + allTicks[CentralProcessor.TickType.SYSTEM.getIndex()]);
long softwareTime = sys - irq - softirq;
System.out.printf("软件相关时间(近似无响应时间): %.2f%%\n", 100d * softwareTime / total);
System.out.println("用户进程累计时间: " + allTicks[CentralProcessor.TickType.USER.getIndex()]);
cpuVO.setInterrupt((allTicks[CentralProcessor.TickType.IRQ.getIndex()] +
allTicks[CentralProcessor.TickType.SOFTIRQ.getIndex()])); // CPU硬件中断提供服务时间
cpuVO.setIdle(allTicks[CentralProcessor.TickType.IDLE.getIndex()]);// CPU空闲时间
cpuVO.setIowait(100d * iowait / total);// CPU等待响应时间
cpuVO.setSystem(allTicks[CentralProcessor.TickType.SYSTEM.getIndex()]);// CPU系统时间
cpuVO.setNoresp(100d * softwareTime / total);// CPU软件无响应时间
cpuVO.setUser(allTicks[CentralProcessor.TickType.USER.getIndex()]);// CPU用户进程所花费的时间
// 5. 系统运行时间和CPU空闲时间
long uptime = os.getSystemUptime();
cpuVO.setNormal(uptime);// CPU正常运行时间
System.out.println("CPU正常运行时间: " + cpuVO.getNormal());
} catch (InterruptedException e) {
e.printStackTrace();
}
return cpuVO;
}
}
@@ -0,0 +1,108 @@
package com.tongran.agent.client.service.impl;
import com.tongran.agent.client.core.vo.DiskVO;
import com.tongran.agent.client.core.vo.PointVO;
import com.tongran.agent.client.service.DiskService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import oshi.SystemInfo;
import oshi.hardware.HWDiskStore;
import oshi.software.os.OSFileStore;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
@Service
public class DiskServiceImpl implements DiskService {
@Override
public List<DiskVO> diskList(long timestamp) {
List<DiskVO> tempList = new ArrayList<>();
List<DiskVO> resultList = new ArrayList<>();
SystemInfo si = new SystemInfo();
System.out.println("=========================================================");
// 获取磁盘IO信息
System.out.println("\n=== 磁盘IO信息 ===");
for (HWDiskStore disk : si.getHardware().getDiskStores()) {
DiskVO diskVO = DiskVO.builder().timestamp(timestamp).build();
diskVO.setName(disk.getName());//磁盘名称
diskVO.setSerial(disk.getSerial());//序列号
diskVO.setTotal(disk.getSize());//磁盘大小
diskVO.setWriteTimes(disk.getWrites());//磁盘写入次数
diskVO.setReadTimes(disk.getReads());//磁盘读取次数
diskVO.setWriteBytes(disk.getReadBytes());//磁盘写入字节
diskVO.setReadBytes(disk.getWriteBytes());//磁盘读取字节
tempList.add(diskVO);
System.out.println("磁盘名称: " + diskVO.getName());
System.out.println("序列号: " + diskVO.getSerial());
System.out.println("磁盘大小: " + diskVO.getTotal());
System.out.println("磁盘写入次数: " + diskVO.getWriteTimes());
System.out.println("磁盘读取次数: " + diskVO.getReadTimes());
System.out.println("磁盘写入字节: " + diskVO.getWriteBytes());
System.out.println("磁盘读取字节: " + diskVO.getReadBytes());
}
try{
// 第一次采样
List<HWDiskStore> disks1 = si.getHardware().getDiskStores();
long[] readBytes1 = new long[disks1.size()];
long[] writeBytes1 = new long[disks1.size()];
for (int i = 0; i < disks1.size(); i++) {
readBytes1[i] = disks1.get(i).getReadBytes();
writeBytes1[i] = disks1.get(i).getWriteBytes();
}
// 等待1秒
Thread.sleep(1000);
// 第二次采样
List<HWDiskStore> disks2 = si.getHardware().getDiskStores();
for (int i = 0; i < disks2.size(); i++) {
long readDiff = disks2.get(i).getReadBytes() - readBytes1[i];
long writeDiff = disks2.get(i).getWriteBytes() - writeBytes1[i];
String serial = disks2.get(i).getSerial();
DiskVO diskVO = tempList.stream().filter(d -> StringUtils.equals(d.getSerial(),serial)).findFirst().orElse(null);
if(Objects.nonNull(diskVO)){
diskVO.setWriteSpeed(readDiff);//磁盘写入速率
diskVO.setReadSpeed(writeDiff);//磁盘读取速率
System.out.println("磁盘名称: " + diskVO.getName());
System.out.println("磁盘写入速率: " + diskVO.getWriteSpeed());
System.out.println("磁盘读取速率: " + diskVO.getReadSpeed());
}
resultList.add(diskVO);
}
} catch (Exception e) {
e.printStackTrace();
}
return resultList;
}
@Override
public List<PointVO> pointList(long timestamp) {
List<PointVO> list = new ArrayList<>();
SystemInfo si = new SystemInfo();
// 获取文件系统信息
System.out.println("=========================================================");
System.out.println("=== 挂载信息 ===");
for (OSFileStore fs : si.getOperatingSystem().getFileSystem().getFileStores()) {
long totalSpace = fs.getTotalSpace();
long usableSpace = fs.getUsableSpace();
long freeSpace = fs.getFreeSpace();
double usagePercentage = totalSpace > 0 ?
(double) (totalSpace - freeSpace) / totalSpace * 100 : 0;
PointVO pointVO = PointVO.builder().timestamp(timestamp).build();
pointVO.setMount(fs.getMount());//挂载点
pointVO.setVfsType(fs.getType());//文件系统类型
pointVO.setVfsTotal(totalSpace);//总空间
pointVO.setVfsFree(usableSpace);//可用空间
pointVO.setVfsUtil(usagePercentage);//空间利用率
list.add(pointVO);
System.out.println("挂载点: " + pointVO.getMount());
System.out.println("文件系统类型: " + pointVO.getVfsType());
System.out.println("总空间: " + pointVO.getVfsTotal());
System.out.println("可用空间: " + pointVO.getVfsFree());
System.out.printf("空间利用率: %.2f%%\n", usagePercentage);
}
return list;
}
}
@@ -0,0 +1,135 @@
package com.tongran.agent.client.service.impl;
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.vo.DockerVO;
import com.tongran.agent.client.service.DockerService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
@Service
public class DockerServiceImpl implements DockerService {
@Override
public List<DockerVO> dockerList(long timestamp) {
List<DockerVO> list = new ArrayList<>();
// 配置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);
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) {
DockerVO dockerVO = DockerVO.builder().timestamp(timestamp).build();
String id = container.getId().substring(0, 12); // 只显示短ID
String image = container.getImage().length() > 15 ?
container.getImage().substring(0, 15) + "..." : container.getImage();
String status = container.getStatus();
String name = container.getNames()[0].replaceFirst("/", "");
System.out.printf("%s\t%s\t%s\t%s%n", id, image, status, name);
dockerVO.setId(id);
dockerVO.setName(name);
dockerVO.setStatus(status);
DockerVO res = dockerStats(dockerVO);
list.add(res);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
dockerClient.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return list;
}
public DockerVO dockerStats(DockerVO dockerVO) {
//判定目标容器 ID
if(StringUtils.isBlank(dockerVO.getId())){
return dockerVO;
}
try {
// 执行 docker stats 命令(--no-stream 表示只输出一次)
Process process = new ProcessBuilder(
"docker", "stats", "--no-stream", dockerVO.getId(),
"--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) {
// 解析输出(示例输出格式):
// "your_container_id 0.00% 0.000 CPU % 0 B / 0 B 0 packets / 0 packets"
// 实际输出可能因 Docker 版本不同而变化,需根据实际情况调整正则表达式
if (line.contains(dockerVO.getId())) {
String[] parts = line.trim().split("\\s+");
String cpuUtil = parts[3]; // cpu使用率
String memUtil = parts[4]; // 内存使用率
// 网络流量通常在第 4 列(接收)和第 5 列(发送),格式为 "B/s" 或 "B"
dockerVO.setCpuUtil(cpuUtil);
String rxRate = "0B";// 接收速率(如 "1.23kB/s"
String txRate = "0B";// 发送速率(如 "4.56kB/s"
//数据间有空格
if(parts.length > 9){
rxRate = parts[5]+parts[6];
txRate = parts[8]+parts[9];
}else{
rxRate = parts[5];
txRate = parts[7];
}
// 网络流量通常在第 4 列(接收)和第 5 列(发送),格式为 "B/s" 或 "B"
dockerVO.setCpuUtil(cpuUtil);
dockerVO.setMemUtil(memUtil);
dockerVO.setNetInSpeed(rxRate);
dockerVO.setNetOutSpeed(txRate);
System.out.println("==================== 容器网络流量 ====================");
System.out.println("接收速率: " + rxRate);
System.out.println("发送速率: " + txRate);
}
}
// 等待命令执行完成并获取退出码
int exitCode = process.waitFor();
if (exitCode != 0) {
System.err.println("命令执行失败,退出码: " + exitCode);
}
reader.close();
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
return dockerVO;
}
}
@@ -0,0 +1,74 @@
package com.tongran.agent.client.service.impl;
import com.tongran.agent.client.core.vo.MemoryVO;
import com.tongran.agent.client.service.MemoryService;
import com.tongran.agent.client.utils.AgentDataUtil;
import org.springframework.stereotype.Service;
import java.io.IOException;
import java.util.Map;
@Service
public class MemoryServiceImpl implements MemoryService {
@Override
public MemoryVO get() {
MemoryVO memoryVO = MemoryVO.builder().build();
try {
Map<String, Long> memInfo = AgentDataUtil.parseMemInfo();
System.out.println("=========================================================");
// 1. 基本内存信息
System.out.println("=== 基本内存信息 (单位KB) ===");
System.out.println("总内存: " + memInfo.get("MemTotal"));
System.out.println("空闲内存: " + memInfo.get("MemFree"));
System.out.println("可用内存: " +
memInfo.getOrDefault("MemAvailable",
memInfo.get("MemFree") +
memInfo.getOrDefault("Buffers", 0L) +
memInfo.getOrDefault("Cached", 0L) +
memInfo.getOrDefault("SReclaimable", 0L)));
memoryVO.setAvailable(memInfo.getOrDefault("MemAvailable",
memInfo.get("MemFree") +
memInfo.getOrDefault("Buffers", 0L) +
memInfo.getOrDefault("Cached", 0L) +
memInfo.getOrDefault("SReclaimable", 0L))); //可用内存
memoryVO.setTotal(memInfo.get("MemTotal")); //总内存
memoryVO.setPercent((double) memoryVO.getAvailable() / memoryVO.getTotal() * 100); //可用内存百分比
// 3. 交换空间信息
System.out.println("\n=== 交换空间信息 ===");
long swapTotal = memInfo.getOrDefault("SwapTotal", 0L);
long swapFree = memInfo.getOrDefault("SwapFree", 0L);
System.out.println("总交换空间: " + swapTotal);
System.out.println("空闲交换空间: " + swapFree);
System.out.println("已用交换空间: " + (swapTotal - swapFree));
memoryVO.setSwapSizeFree(swapFree); //交换卷/文件的可用空间(字节)
memoryVO.setSwapSizePercent((double) swapFree / swapTotal * 100); //可用交换空间百分比
if (swapTotal > 0) {
System.out.printf("交换空间使用率: %.2f%%\n",
(double)(swapTotal - swapFree) / swapTotal * 100);
}
// 4. 内存使用率分析
System.out.println("\n=== 内存使用率分析 ===");
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;
System.out.printf("总内存使用率: %.2f%%\n", totalUsage);
// 实际内存使用率
long cached = memInfo.getOrDefault("Cached", 0L) +
memInfo.getOrDefault("SReclaimable", 0L);
long buffers = memInfo.getOrDefault("Buffers", 0L);
double actualUsage = (double)(total - available - cached - buffers) / total * 100;
System.out.printf("实际内存使用率: %.2f%%\n", actualUsage);
memoryVO.setUntilzation(actualUsage); //内存利用率
} catch (IOException e) {
e.printStackTrace();
}
return memoryVO;
}
}
@@ -0,0 +1,123 @@
package com.tongran.agent.client.service.impl;
import com.tongran.agent.client.core.vo.NetVO;
import com.tongran.agent.client.service.NetService;
import com.tongran.agent.client.utils.AgentUtil;
import org.springframework.stereotype.Service;
import oshi.SystemInfo;
import oshi.hardware.HardwareAbstractionLayer;
import oshi.hardware.NetworkIF;
import oshi.util.FormatUtil;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.*;
import java.util.concurrent.TimeUnit;
@Service
public class NetServiceImpl implements NetService {
@Override
public List<NetVO> netList(long timestamp) {
List<NetVO> list = new ArrayList<>();
try {
SystemInfo si = new SystemInfo();
HardwareAbstractionLayer hal = si.getHardware();
// 获取所有网络接口
List<NetworkIF> networkIFs = hal.getNetworkIFs();
System.out.println("=========================================================");
System.out.println("===== 网卡流量统计 =====");
List<NetVO> temp = new ArrayList<>();
for (NetworkIF net : networkIFs) {
System.out.println("接口名称: " + net.getName()+"("+net.getDisplayName()+")");
System.out.println("MAC地址: " + net.getMacaddr());
System.out.print("运行状态: ");
System.out.println(net.isConnectorPresent() ? "已连接" : "未连接");
System.out.println("接口类型: " + AgentUtil.getInterfaceType(net));
System.out.println("IPv4地址: " + String.join(", ", net.getIPv4addr()));
NetVO netVO = NetVO.builder().build();
netVO.setName(net.getName()+"("+net.getDisplayName()+")");//网卡名称
netVO.setMac(net.getMacaddr());//MAC
netVO.setStatus(net.isConnectorPresent() ? "已连接" : "未连接");//运行状态
netVO.setType(AgentUtil.getInterfaceType(net));//接口类型
netVO.setIpV4(String.join(", ", net.getIPv4addr()));//IPv4
Map<String, String> map = getNetworkMode(net.getName());
if (map != null && !map.isEmpty()) {
netVO.setSpeed(map.get("speed"));
netVO.setDuplex(map.get("duplex"));
}
temp.add(netVO);
}
// 2. 实时带宽监控(需要两次采样)
System.out.println("\n=== 实时带宽监控 ===");
// 第一次采样
for (NetworkIF net : networkIFs) {
net.updateAttributes();
}
// 等待1秒
TimeUnit.SECONDS.sleep(1);
// 第二次采样并计算速率
for (NetworkIF net : networkIFs) {
long prevBytesRecv = net.getBytesRecv();
long prevBytesSent = net.getBytesSent();
net.updateAttributes();
long bytesRecv = net.getBytesRecv() - prevBytesRecv;
long bytesSent = net.getBytesSent() - prevBytesSent;
System.out.println("接口: " + net.getName());
System.out.println("入站丢包: " + net.getInDrops());
System.out.println("出站丢包: " + net.getCollisions());
System.out.println("接收带宽: " + FormatUtil.formatBytes(bytesRecv) + "/s (" +
bytesToMbps(bytesRecv) + " Mbps)");
System.out.println("发送带宽: " + FormatUtil.formatBytes(bytesSent) + "/s (" +
bytesToMbps(bytesSent) + " Mbps)");
NetVO netVO = temp.stream().filter(n -> n.getIpV4().equals(String.join(", ", net.getIPv4addr()))
&& n.getMac().equals(net.getMacaddr())).findFirst().orElse(null);
if(Objects.nonNull(netVO)){
netVO.setInDropped(net.getInDrops());//入站丢包
netVO.setOutDropped(net.getCollisions());//出站丢包
netVO.setInSpeed(bytesRecv);//接收流量
netVO.setOutSpeed(bytesSent);//发送流量
netVO.setTimestamp(timestamp);
list.add(netVO);
}
}
} catch (Exception e) {
e.printStackTrace();
}
return list;
}
private static double bytesToMbps(long bytes) {
return bytes * 8.0 / 1_000_000; // bytes to megabits
}
public static Map<String, String> getNetworkMode(String interfaceName) {
Map<String, String> result = new HashMap<>();
ProcessBuilder pb = new ProcessBuilder("ethtool", interfaceName);
pb.redirectErrorStream(true);
try {
Process process = pb.start();
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line;
while ((line = reader.readLine()) != null) {
if (line.contains("Speed:")) {
result.put("speed", line.split(":")[1].trim());
} else if (line.contains("Duplex:")) {
result.put("duplex", line.split(":")[1].trim());
} else if (line.contains("Auto-negotiation:")) {
result.put("auto-negotiation", line.split(":")[1].trim());
}
}
int exitCode = process.waitFor();
if (exitCode != 0) {
result.put("error", "ethtool command failed with exit code " + exitCode);
}
} catch (Exception e) {
result.put("error", e.getMessage());
}
return result;
}
}
@@ -0,0 +1,468 @@
package com.tongran.agent.client.service.impl;
import com.alibaba.fastjson2.JSONObject;
import com.tongran.agent.client.core.config.GlobalConfig;
import com.tongran.agent.client.core.vo.SwitchBoardVO;
import com.tongran.agent.client.service.SwitchBoardService;
import org.apache.commons.lang3.StringUtils;
import org.snmp4j.*;
import org.snmp4j.event.ResponseEvent;
import org.snmp4j.mp.SnmpConstants;
import org.snmp4j.smi.GenericAddress;
import org.snmp4j.smi.OID;
import org.snmp4j.smi.OctetString;
import org.snmp4j.smi.VariableBinding;
import org.snmp4j.transport.DefaultUdpTransportMapping;
import org.springframework.stereotype.Service;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
@Service
public class SwitchBoardServiceImpl implements SwitchBoardService {
@Override
public List<SwitchBoardVO> switchBoardList(long timestamp) {
List<SwitchBoardVO> list = new ArrayList<>();
System.out.println("==================== 交换机流量信息 ====================");
try {
// 1. 创建传输映射
TransportMapping transport = new DefaultUdpTransportMapping();
Snmp snmp = new Snmp(transport);
transport.listen();
// 2. 创建目标对象
CommunityTarget target = new CommunityTarget();
target.setCommunity(new OctetString(GlobalConfig.SWITCH_COMMUNITY));
target.setAddress(GenericAddress.parse("udp:" + GlobalConfig.SWITCH_IP + "/" + GlobalConfig.SWITCH_PORT));
target.setRetries(2);
target.setTimeout(5000);
target.setVersion(SnmpConstants.version2c);
// 3. 获取交换机基本信息
getSystemInfo(snmp, target);
// 4. 获取接口信息
list = getInterfaceInfo(snmp, target, timestamp);
snmp.close();
} catch (Exception e) {
e.printStackTrace();
}
return list;
}
@Override
public String getSwitchDataByType(String type) {
JSONObject json = new JSONObject();
json.put("type",type);
switch(type){
case "switchNetCollect":
json.put("value",handleSwitchNet());
break;
case "switchModuleCollect":
json.put("value",handleSwitchModule());
break;
case "switchMpuCollect":
json.put("value",handleSwitchMpu());
break;
case "switchPwrCollect":
json.put("value",handleSwitchPwr());
break;
case "switchFanCollect":
json.put("value",handleSwitchFan());
break;
default:
json.put("value",handleSwitchOther(type));
break;
}
return json.toString();
}
private String handleSwitchNet(){
System.out.println("==================== 交换机网络信息 ====================");
String result = "";
try {
if(GlobalConfig.SWITCH_NET_OID.isEmpty()){
return result;
}
// 1. 创建传输映射
TransportMapping transport = new DefaultUdpTransportMapping();
Snmp snmp = new Snmp(transport);
transport.listen();
// 2. 创建目标对象
CommunityTarget target = new CommunityTarget();
target.setCommunity(new OctetString(GlobalConfig.SWITCH_COMMUNITY));
target.setAddress(GenericAddress.parse("udp:" + GlobalConfig.SWITCH_IP + "/" + GlobalConfig.SWITCH_PORT));
target.setRetries(2);
target.setTimeout(5000);
target.setVersion(SnmpConstants.version2c);
result = getInterfaceInfoByType(snmp, target, "switchNet", GlobalConfig.SWITCH_NET_OID);
snmp.close();
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
private String handleSwitchModule(){
System.out.println("==================== 交换机光模块信息 ====================");
String result = "";
try {
if(GlobalConfig.SWITCH_MODULE_OID.isEmpty()){
return result;
}
// 1. 创建传输映射
TransportMapping transport = new DefaultUdpTransportMapping();
Snmp snmp = new Snmp(transport);
transport.listen();
// 2. 创建目标对象
CommunityTarget target = new CommunityTarget();
target.setCommunity(new OctetString(GlobalConfig.SWITCH_COMMUNITY));
target.setAddress(GenericAddress.parse("udp:" + GlobalConfig.SWITCH_IP + "/" + GlobalConfig.SWITCH_PORT));
target.setRetries(2);
target.setTimeout(5000);
target.setVersion(SnmpConstants.version2c);
result = getInterfaceInfoByType(snmp, target, "switchModule", GlobalConfig.SWITCH_MODULE_OID);
snmp.close();
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
private String handleSwitchMpu(){
System.out.println("==================== 交换机MPU信息 ====================");
String result = "";
try {
if(GlobalConfig.SWITCH_MPU_OID.isEmpty()){
return result;
}
// 1. 创建传输映射
TransportMapping transport = new DefaultUdpTransportMapping();
Snmp snmp = new Snmp(transport);
transport.listen();
// 2. 创建目标对象
CommunityTarget target = new CommunityTarget();
target.setCommunity(new OctetString(GlobalConfig.SWITCH_COMMUNITY));
target.setAddress(GenericAddress.parse("udp:" + GlobalConfig.SWITCH_IP + "/" + GlobalConfig.SWITCH_PORT));
target.setRetries(2);
target.setTimeout(5000);
target.setVersion(SnmpConstants.version2c);
result = getInterfaceInfoByType(snmp, target, "switchMpu", GlobalConfig.SWITCH_MPU_OID);
snmp.close();
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
private String handleSwitchPwr(){
System.out.println("==================== 交换机电源信息 ====================");
String result = "";
try {
if(GlobalConfig.SWITCH_PWR_OID.isEmpty()){
return result;
}
// 1. 创建传输映射
TransportMapping transport = new DefaultUdpTransportMapping();
Snmp snmp = new Snmp(transport);
transport.listen();
// 2. 创建目标对象
CommunityTarget target = new CommunityTarget();
target.setCommunity(new OctetString(GlobalConfig.SWITCH_COMMUNITY));
target.setAddress(GenericAddress.parse("udp:" + GlobalConfig.SWITCH_IP + "/" + GlobalConfig.SWITCH_PORT));
target.setRetries(2);
target.setTimeout(5000);
target.setVersion(SnmpConstants.version2c);
result = getInterfaceInfoByType(snmp, target, "switchPwr", GlobalConfig.SWITCH_PWR_OID);
snmp.close();
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
private String handleSwitchFan(){
System.out.println("==================== 交换机风扇信息 ====================");
String result = "";
try {
if(GlobalConfig.SWITCH_FAN_OID.isEmpty()){
return result;
}
// 1. 创建传输映射
TransportMapping transport = new DefaultUdpTransportMapping();
Snmp snmp = new Snmp(transport);
transport.listen();
// 2. 创建目标对象
CommunityTarget target = new CommunityTarget();
target.setCommunity(new OctetString(GlobalConfig.SWITCH_COMMUNITY));
target.setAddress(GenericAddress.parse("udp:" + GlobalConfig.SWITCH_IP + "/" + GlobalConfig.SWITCH_PORT));
target.setRetries(2);
target.setTimeout(5000);
target.setVersion(SnmpConstants.version2c);
result = getInterfaceInfoByType(snmp, target, "switchFan", GlobalConfig.SWITCH_FAN_OID);
snmp.close();
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
private String handleSwitchOther(String type){
System.out.println("==================== 交换机系统其他信息 ====================");
String result = "";
try {
if(GlobalConfig.SWITCH_OTHER_OID.isEmpty()){
return result;
}
// 1. 创建传输映射
TransportMapping transport = new DefaultUdpTransportMapping();
Snmp snmp = new Snmp(transport);
transport.listen();
// 2. 创建目标对象
CommunityTarget target = new CommunityTarget();
target.setCommunity(new OctetString(GlobalConfig.SWITCH_COMMUNITY));
target.setAddress(GenericAddress.parse("udp:" + GlobalConfig.SWITCH_IP + "/" + GlobalConfig.SWITCH_PORT));
target.setRetries(2);
target.setTimeout(5000);
target.setVersion(SnmpConstants.version2c);
result = getInterfaceInfoByType(snmp, target, type, GlobalConfig.SWITCH_OTHER_OID);
snmp.close();
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
private static void getSystemInfo(Snmp snmp, Target target) throws IOException {
// 系统描述 OID
String[] systemOIDs = {
"1.3.6.1.2.1.1.1.0", // sysDescr
"1.3.6.1.2.1.1.5.0", // sysName
"1.3.6.1.2.1.1.6.0", // sysLocation
"1.3.6.1.2.1.1.4.0", // sysContact
"1.3.6.1.2.1.1.3.0" // sysUpTime
};
PDU pdu = new PDU();
for (String oid : systemOIDs) {
pdu.add(new VariableBinding(new OID(oid)));
}
pdu.setType(PDU.GET);
ResponseEvent event = snmp.send(pdu, target);
if (event != null && event.getResponse() != null) {
System.out.println("\n=== 交换机基本信息 ===");
for (VariableBinding vb : event.getResponse().getVariableBindings()) {
System.out.printf("%-30s: %s%n",
getOIDDescription(vb.getOid().toString()),
vb.getVariable());
}
}
}
private static List<SwitchBoardVO> getInterfaceInfo(Snmp snmp, Target target, long timestamp) throws IOException {
// 获取接口数量
String ifNumberOID = "1.3.6.1.2.1.2.1.0";
PDU pdu = new PDU();
pdu.add(new VariableBinding(new OID(ifNumberOID)));
pdu.setType(PDU.GET);
ResponseEvent event = snmp.send(pdu, target);
if (event == null || event.getResponse() == null) {
System.err.println("无法获取接口数量");
return null;
}
int ifNumber = event.getResponse().get(0).getVariable().toInt();
System.out.println("\n=== 接口数量: " + ifNumber + " ===");
// 获取每个接口的信息
String[] ifOIDs = {
"1.3.6.1.2.1.2.2.1.2", // ifDescr
"1.3.6.1.2.1.2.2.1.3", // ifType
"1.3.6.1.2.1.2.2.1.5", // ifSpeed
"1.3.6.1.2.1.2.2.1.8", // ifOperStatus
"1.3.6.1.2.1.31.1.1.1.6", // ifInOctets
"1.3.6.1.2.1.31.1.1.1.10" // ifOutOctets
// "1.3.6.1.2.1.2.2.1.10", // ifInOctets
// "1.3.6.1.2.1.2.2.1.16" // ifOutOctets
};
System.out.printf("%-5s %-15s %-10s %-15s %-8s %-12s %-12s%n",
"Index", "Name", "Type", "Speed", "Status", "InBytes", "OutBytes");
List<SwitchBoardVO> list = new ArrayList<>();
for (int i = 1; i <= ifNumber; i++) {
pdu = new PDU();
for (String baseOID : ifOIDs) {
pdu.add(new VariableBinding(new OID(baseOID + "." + i)));
}
pdu.setType(PDU.GET);
event = snmp.send(pdu, target);
if (event != null && event.getResponse() != null) {
VariableBinding[] vbs = event.getResponse().getVariableBindings().toArray(new VariableBinding[0]);
String ifName = vbs[0].getVariable().toString();
String ifType = getInterfaceType(vbs[1].getVariable().toInt());
String speed = formatSpeed(vbs[2].getVariable().toInt());
String status = getOperStatus(vbs[3].getVariable().toInt());
long inBytes = vbs[4].getVariable().toLong();
long outBytes = vbs[5].getVariable().toLong();
SwitchBoardVO boardVO = SwitchBoardVO.builder()
.name(ifName)
.switchIp(GlobalConfig.SWITCH_IP)
.type(ifType)
.status(status)
.inBytes(inBytes)
.outBytes(outBytes)
.timestamp(timestamp)
.build();
list.add(boardVO);
System.out.printf("%-5d %-15s %-10s %-15s %-8s %-12d %-12d%n",
i, ifName, ifType, speed, status, inBytes, outBytes);
}
}
return list;
}
// 辅助方法
private static String getOIDDescription(String oid) {
switch(oid) {
case "1.3.6.1.2.1.1.1.0": return "系统描述";
case "1.3.6.1.2.1.1.5.0": return "系统名称";
case "1.3.6.1.2.1.1.6.0": return "物理位置";
case "1.3.6.1.2.1.1.4.0": return "联系人";
case "1.3.6.1.2.1.1.3.0": return "运行时间";
default: return oid;
}
}
private static String getInterfaceType(int type) {
switch(type) {
case 6: return "Ethernet";
case 23: return "PPP";
case 24: return "Loopback";
case 53: return "VLAN";
case 131: return "Tunnel";
default: return "其他("+type+")";
}
}
private static String formatSpeed(int speed) {
if (speed >= 1000000000) {
return (speed / 1000000000) + " Gbps";
} else if (speed >= 1000000) {
return (speed / 1000000) + " Mbps";
} else if (speed >= 1000) {
return (speed / 1000) + " Kbps";
}
return speed + " bps";
}
private static String getOperStatus(int status) {
switch(status) {
case 1: return "Up";
case 2: return "Down";
case 3: return "Testing";
case 4: return "Unknown";
case 5: return "Dormant";
case 6: return "NotPresent";
case 7: return "LowerLayerDown";
default: return "未知";
}
}
private static String getInterfaceInfoByType(Snmp snmp, Target target, String type, LinkedHashMap<String,String> oidParams) throws IOException {
JSONObject json = new JSONObject();
// 获取接口数量
String ifNumberOID = "1.3.6.1.2.1.2.1.0";
PDU pdu = new PDU();
pdu.add(new VariableBinding(new OID(ifNumberOID)));
pdu.setType(PDU.GET);
ResponseEvent event = snmp.send(pdu, target);
if (event == null || event.getResponse() == null) {
System.err.println("无法获取接口数量");
return "";
}
int ifNumber = event.getResponse().get(0).getVariable().toInt();
System.out.println("\n=== 接口数量: " + ifNumber + " ===");
if(oidParams.isEmpty()){
return "";
}
String[] ifOIDs = oidParams.keySet().toArray(new String[0]);
String[] params = oidParams.values().toArray(new String[0]);
// 获取每个接口的信息
// String[] ifOIDs = {
// "1.3.6.1.2.1.2.2.1.2", // ifDescr
// "1.3.6.1.2.1.2.2.1.3", // ifType
// "1.3.6.1.2.1.2.2.1.5", // ifSpeed
// "1.3.6.1.2.1.2.2.1.8", // ifOperStatus
// "1.3.6.1.2.1.31.1.1.1.6", // ifInOctets
// "1.3.6.1.2.1.31.1.1.1.10" // ifOutOctets
//// "1.3.6.1.2.1.2.2.1.10", // ifInOctets
//// "1.3.6.1.2.1.2.2.1.16" // ifOutOctets
// };
for (int i = 1; i <= ifNumber; i++) {
pdu = new PDU();
for (String baseOID : ifOIDs) {
pdu.add(new VariableBinding(new OID(baseOID + "." + i)));
}
pdu.setType(PDU.GET);
event = snmp.send(pdu, target);
if (event != null && event.getResponse() != null) {
VariableBinding[] vbs = event.getResponse().getVariableBindings().toArray(new VariableBinding[0]);
for (int m = 0; m < params.length; m++) {
if(StringUtils.isNotBlank(type)){
if(StringUtils.equals(params[m], type)){
json.put(params[m],vbs[m].getVariable().toString());
System.out.println(params[m]+""+vbs[m].getVariable().toString());
}
}else{
json.put(params[m],vbs[m].getVariable().toString());
System.out.println(params[m]+""+vbs[m].getVariable().toString());
}
}
}
}
return json.toString();
}
/**
* 结果封装类
*/
public static class MapArrays {
private final String[] keys;
private final String[] values;
public MapArrays(String[] keys, String[] values) {
this.keys = keys != null ? keys : new String[0];
this.values = values != null ? values : new String[0];
}
public String[] getKeys() {
return keys;
}
public String[] getValues() {
return values;
}
public int size() {
return keys.length;
}
public boolean isEmpty() {
return keys.length == 0;
}
@Override
public String toString() {
return "Keys: " + Arrays.toString(keys) +
"\nValues: " + Arrays.toString(values);
}
}
}
@@ -0,0 +1,401 @@
package com.tongran.agent.client.service.impl;
import com.alibaba.fastjson2.JSONObject;
import com.tongran.agent.client.core.vo.SystemVO;
import com.tongran.agent.client.service.SystemService;
import com.tongran.agent.client.utils.AgentDataUtil;
import com.tongran.agent.client.utils.AgentUtil;
import org.springframework.stereotype.Service;
import oshi.SystemInfo;
import oshi.hardware.CentralProcessor;
import oshi.hardware.HardwareAbstractionLayer;
import oshi.software.os.OperatingSystem;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Map;
@Service
public class SystemServiceImpl implements SystemService {
@Override
public SystemVO get() {
SystemVO systemVO = SystemVO.builder().build();
try {
SystemInfo si = new SystemInfo();
HardwareAbstractionLayer hal = si.getHardware();
CentralProcessor processor = hal.getProcessor();
OperatingSystem os = si.getOperatingSystem();
System.out.println("=========================================================");
// 1. 操作系统基本信息
System.out.println("=== 操作系统信息 ===");
systemVO.setOs(os.getFamily()); //操作系统
systemVO.setArch(os.getVersionInfo().getVersion() + " " + os.getBitness() + ""); //操作系统架构
systemVO.setUuid(AgentUtil.getMotherboardUUID());
System.out.println("操作系统: " + systemVO.getOs());
System.out.println("操作系统架构: " + systemVO.getArch());
System.out.println("UUID: " + AgentUtil.getMotherboardUUID());
// 2. 进程信息
System.out.println("\n=== 进程信息 ===");
long maxProcesses = getMaxProcessesLinux();
int runningProcesses = getRunningProcessesLinux();
System.out.println("最大进程数: " + maxProcesses);
System.out.println("正在运行的进程数: " + runningProcesses);
systemVO.setMaxProc(maxProcesses); //最大进程数
systemVO.setRunProcNum(runningProcesses); //正在运行的进程数
// 3. 登录用户数
System.out.println("\n=== 登录用户 ===");
systemVO.setUsersNum(os.getSessions().size()); //登录用户数
System.out.println("登录用户数: " + systemVO.getUsersNum());
// 4. 磁盘信息
System.out.println("\n=== 磁盘信息 ===");
systemVO.setDiskSizeTotal(diskSpace()); //硬盘:总可用空间
systemVO.setBootTime(systemBootTime(hal.getProcessor())); //系统启动时间
systemVO.setUname(systemDescription(si)); //系统描述
systemVO.setLocalTime(localTime()); //系统本地时间
systemVO.setUpTime(systemUptime(os)); //系统正常运行时间
System.out.println("硬盘:总可用空间: " + systemVO.getDiskSizeTotal());
System.out.println("系统启动时间: " + systemVO.getBootTime());
System.out.println("系统描述: " + systemVO.getUname());
System.out.println("系统本地时间: " + systemVO.getLocalTime());
System.out.println("系统正常运行时间: " + systemVO.getUpTime());
} catch (IOException e) {
e.printStackTrace();
}
return systemVO;
}
@Override
public String otherSystem(String type) {
JSONObject json = new JSONObject();
json.put("type",type);
switch(type){
case "systemSwapSizeFreeCollect":
json.put("value", String.valueOf(handleSystemSwapSizeFree()));
break;
case "memoryUtilizationCollect":
json.put("value", String.valueOf(handleMemoryUtilization()));
break;
case "systemSwapSizePercentCollect":
json.put("value", String.valueOf(handleSystemSwapSizePercent()));
break;
case "memorySizeAvailableCollect":
json.put("value", String.valueOf(handleMemorySizeAvailable()));
break;
case "memorySizePercentCollect":
json.put("value", String.valueOf(handleMemorySizePercent()));
break;
case "memorySizeTotalCollect":
json.put("value", String.valueOf(handleMemorySizeTotal()));
break;
case "systemSwOsCollect":
json.put("value",handleSystemSwOs());
break;
case "systemSwArchCollect":
json.put("value",handleSystemSwArch());
break;
case "kernelMaxprocCollect":
json.put("value", String.valueOf(handleKernelMaxproc()));
break;
case "procNumRunCollect":
json.put("value", String.valueOf(handleProcNumRun()));
break;
case "systemUsersNumCollect":
json.put("value", String.valueOf(handleUsersNum()));
break;
case "systemDiskSizeTotalCollect":
json.put("value", String.valueOf(handleSystemDiskSizeTotal()));
break;
case "systemBoottimeCollect":
json.put("value", String.valueOf(handleSystemBoottime()));
break;
case "systemUnameCollect":
json.put("value",handleSystemUname());
break;
case "systemLocaltimeCollect":
json.put("value",handleSystemLocaltime());
break;
case "systemUptimeCollect":
json.put("value", String.valueOf(handleSystemUptime()));
break;
case "procNumCollect":
json.put("value", String.valueOf(handleProcNum()));
break;
default:
json.put("value",handleDefault());
break;
}
return json.toString();
}
private long handleSystemSwapSizeFree(){
try {
Map<String, Long> memInfo = AgentDataUtil.parseMemInfo();
long swapFree = memInfo.getOrDefault("SwapFree", 0L);
System.out.println("=========================================================");
System.out.println("交换卷/文件的可用空间(字节): " + swapFree);
return swapFree;
} catch (IOException e) {
e.printStackTrace();
}
return 0L;
}
private double handleMemoryUtilization(){
try {
Map<String, Long> memInfo = AgentDataUtil.parseMemInfo();
System.out.println("=========================================================");
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));
// 实际内存使用率
long cached = memInfo.getOrDefault("Cached", 0L) +
memInfo.getOrDefault("SReclaimable", 0L);
long buffers = memInfo.getOrDefault("Buffers", 0L);
double actualUsage = (double)(total - available - cached - buffers) / total * 100;
System.out.printf("实际内存使用率: %.2f%%\n", actualUsage);
return actualUsage;
} catch (IOException e) {
e.printStackTrace();
}
return 0;
}
private double handleSystemSwapSizePercent(){
try {
Map<String, Long> memInfo = AgentDataUtil.parseMemInfo();
System.out.println("=========================================================");
long swapTotal = memInfo.getOrDefault("SwapTotal", 0L);
long swapFree = memInfo.getOrDefault("SwapFree", 0L);
System.out.printf("交换空间使用率: %.2f%%\n",
(double)(swapTotal - swapFree) / swapTotal * 100);
return (double) swapFree / swapTotal * 100;
} catch (IOException e) {
e.printStackTrace();
}
return 0;
}
private long handleMemorySizeAvailable(){
try {
Map<String, Long> memInfo = AgentDataUtil.parseMemInfo();
System.out.println("=========================================================");
System.out.println("可用内存: " +
memInfo.getOrDefault("MemAvailable",
memInfo.get("MemFree") +
memInfo.getOrDefault("Buffers", 0L) +
memInfo.getOrDefault("Cached", 0L) +
memInfo.getOrDefault("SReclaimable", 0L)));
return memInfo.getOrDefault("MemAvailable",
memInfo.get("MemFree") +
memInfo.getOrDefault("Buffers", 0L) +
memInfo.getOrDefault("Cached", 0L) +
memInfo.getOrDefault("SReclaimable", 0L));
} catch (IOException e) {
e.printStackTrace();
}
return 0;
}
private double handleMemorySizePercent(){
try {
Map<String, Long> memInfo = AgentDataUtil.parseMemInfo();
System.out.println("=========================================================");
long available = memInfo.getOrDefault("MemAvailable",
memInfo.get("MemFree") +
memInfo.getOrDefault("Buffers", 0L) +
memInfo.getOrDefault("Cached", 0L) +
memInfo.getOrDefault("SReclaimable", 0L));
long total = memInfo.get("MemTotal");
System.out.println("可用内存百分比: " + (double) available / total * 100);
return (double) available / total * 100;
} catch (IOException e) {
e.printStackTrace();
}
return 0;
}
private long handleMemorySizeTotal(){
try {
Map<String, Long> memInfo = AgentDataUtil.parseMemInfo();
System.out.println("=========================================================");
long total = memInfo.get("MemTotal");
System.out.println("总内存: " + total);
return total;
} catch (IOException e) {
e.printStackTrace();
}
return 0;
}
private String handleSystemSwOs(){
SystemInfo si = new SystemInfo();
OperatingSystem os = si.getOperatingSystem();
System.out.println("=========================================================");
System.out.println("操作系统: " + os.getFamily());
return os.getFamily();
}
private String handleSystemSwArch(){
SystemInfo si = new SystemInfo();
OperatingSystem os = si.getOperatingSystem();
System.out.println("=========================================================");
String arch = os.getVersionInfo().getVersion() + " " + os.getBitness() + "";
System.out.println("操作系统架构: " + arch);
return arch;
}
private long handleKernelMaxproc(){
System.out.println("=========================================================");
long maxProcesses = 0;
try {
maxProcesses = getMaxProcessesLinux();
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("最大进程数: " + maxProcesses);
return maxProcesses;
}
private long handleProcNumRun(){
System.out.println("=========================================================");
int runningProcesses = getRunningProcessesLinux();
System.out.println("正在运行的进程数: " + runningProcesses);
return runningProcesses;
}
private int handleUsersNum(){
SystemInfo si = new SystemInfo();
OperatingSystem os = si.getOperatingSystem();
System.out.println("=========================================================");
int usersNum = os.getSessions().size();
System.out.println("登录用户数: " + usersNum);
return usersNum;
}
private long handleSystemDiskSizeTotal(){
System.out.println("=========================================================");
long diskSizeTotal = diskSpace();
System.out.println("硬盘:总可用空间: " + diskSizeTotal);
return diskSizeTotal;
}
public long handleSystemBoottime(){
SystemInfo si = new SystemInfo();
HardwareAbstractionLayer hal = si.getHardware();
long boottime = systemBootTime(hal.getProcessor());
System.out.println("=========================================================");
System.out.println("系统启动时间: " + boottime);
return boottime;
}
private String handleSystemUname(){
SystemInfo si = new SystemInfo();
String uname = systemDescription(si);
System.out.println("=========================================================");
System.out.println("系统描述: " + uname);
return uname;
}
private String handleSystemLocaltime(){
System.out.println("=========================================================");
String time = localTime();
System.out.println("系统本地时间: " + time);
return time;
}
private long handleSystemUptime(){
SystemInfo si = new SystemInfo();
OperatingSystem os = si.getOperatingSystem();
System.out.println("=========================================================");
long uptime = systemUptime(os);
System.out.println("系统正常运行时间: " + uptime);
return uptime;
}
private long handleProcNum(){
SystemInfo si = new SystemInfo();
OperatingSystem os = si.getOperatingSystem();
System.out.println("=========================================================");
long procNum = os.getProcessCount();
System.out.println("进程数: " + procNum);
return procNum;
}
private String handleDefault(){
return "";
}
// 读取 /proc/sys/kernel/pid_max 获取最大进程数
private static long getMaxProcessesLinux() throws IOException {
File pidMaxFile = new File("/proc/sys/kernel/pid_max");
try (BufferedReader reader = new BufferedReader(new FileReader(pidMaxFile))) {
String line = reader.readLine().trim();
return Long.parseLong(line);
}
}
// 统计 /proc 下的数字目录数(每个目录对应一个进程)
private static int getRunningProcessesLinux() {
File procDir = new File("/proc");
File[] files = procDir.listFiles();
if (files == null) return 0;
int count = 0;
for (File file : files) {
if (file.isDirectory() && file.getName().matches("\\d+")) {
count++;
}
}
return count;
}
// 获取硬盘总可用空间
public long diskSpace() {
long diskSizeTotal = 0;
File[] roots = File.listRoots();
for (File root : roots) {
diskSizeTotal += root.getFreeSpace();
}
return diskSizeTotal;
}
// 获取系统启动时间
public long systemBootTime(CentralProcessor processor) {
long[] systemCpuLoadTicks = processor.getSystemCpuLoadTicks();
long bootTime = ManagementFactory.getRuntimeMXBean().getStartTime();
return bootTime;
}
// 获取系统描述
public String systemDescription(SystemInfo si) {
OperatingSystem os = si.getOperatingSystem();
HardwareAbstractionLayer hal = si.getHardware();
return "操作系统: " + os.toString() + ",系统版本: " + os.getVersionInfo().toString() + "" +
",处理器: " + hal.getProcessor().getProcessorIdentifier().getName() +
",物理内存: " + hal.getMemory().getTotal() / (1024 * 1024 * 1024) + " GB";
}
// 获取系统本地时间
public String localTime() {
return LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
}
// 获取系统正常运行时间
public long systemUptime(OperatingSystem os) {
long uptimeSeconds = os.getSystemUptime();
return uptimeSeconds;
}
}
@@ -0,0 +1,26 @@
package com.tongran.agent.client.utils;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
public class AgentDataUtil {
public static Map<String, Long> parseMemInfo() throws IOException {
Map<String, Long> memInfo = new HashMap<>();
try (BufferedReader br = new BufferedReader(new FileReader("/proc/meminfo"))) {
String line;
while ((line = br.readLine()) != null) {
String[] parts = line.split("\\s+");
if (parts.length >= 2) {
String key = parts[0].replace(":", "");
long value = Long.parseLong(parts[1]);
memInfo.put(key, value);
}
}
}
return memInfo;
}
}
@@ -0,0 +1,220 @@
package com.tongran.agent.client.utils;
import com.fasterxml.jackson.databind.ObjectMapper;
import oshi.hardware.NetworkIF;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.temporal.ChronoUnit;
import java.util.Base64;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;
public class AgentUtil {
public static String getMotherboardUUID() {
String hostName = getHostname();
String primaryIp = getPrimaryIp();
// SystemInfo si = new SystemInfo();
// HardwareAbstractionLayer hal = si.getHardware();
// Baseboard baseboard = hal.getComputerSystem().getBaseboard();
// if(StringUtils.isNotBlank(baseboard.getSerialNumber())){
// return baseboard.getSerialNumber();
// }
return hostName+":"+primaryIp;
}
public static String getHostname() {
try {
return InetAddress.getLocalHost().getHostName();
} catch (UnknownHostException e) {
return "unknown-host";
}
}
/**
* 获取本机首选 IP 地址(通常是第一个非回环、非虚拟网卡的 IPv4 地址)
*/
public static String getPrimaryIp() {
try {
InetAddress localHost = InetAddress.getLocalHost();
String ip = localHost.getHostAddress();
// 如果是 127.x.x.x,说明可能 hosts 配置有问题,需要手动查找
if (ip.startsWith("127.")) {
return getExternalIp();
}
return ip;
} catch (UnknownHostException e) {
return "127.0.0.1";
}
}
/**
* 获取第一个非回环、非虚拟网卡的 IPv4 地址
*/
public static String getExternalIp() {
try {
Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
while (interfaces.hasMoreElements()) {
NetworkInterface iface = interfaces.nextElement();
// 跳过虚拟网卡(如 docker, veth, lo
if (iface.isLoopback() || iface.isVirtual() || !iface.isUp()) {
continue;
}
Enumeration<InetAddress> addresses = iface.getInetAddresses();
while (addresses.hasMoreElements()) {
InetAddress addr = addresses.nextElement();
if (addr.isLoopbackAddress()) {
continue; // 跳过 127.0.0.1
}
if (addr.getHostAddress().contains(":")) {
continue; // 跳过 IPv6
}
return addr.getHostAddress();
}
}
} catch (SocketException e) {
e.printStackTrace();
}
return "127.0.0.1";
}
public static String toJsonString(List<Map<String, String>> list) {
try {
ObjectMapper mapper = new ObjectMapper();
return mapper.writeValueAsString(list);
} catch (Exception e) {
throw new RuntimeException("JSON转换失败", e);
}
}
public static void base64ToFile(String base64String, String filePath) throws IOException {
// 解码Base64字符串
byte[] decodedBytes = Base64.getDecoder().decode(base64String);
// 写入文件
try (FileOutputStream fos = new FileOutputStream(filePath)) {
fos.write(decodedBytes);
}
}
public static String getInterfaceType(NetworkIF net) {
String name = net.getName().toLowerCase();
String displayName = net.getDisplayName().toLowerCase();
if (name.startsWith("eth") || name.startsWith("en") || displayName.contains("ethernet")) {
return "Ethernet";
} else if (name.startsWith("wlan") || name.startsWith("wl") || displayName.contains("wireless")) {
return "Wi-Fi";
} else if (name.startsWith("lo") || displayName.contains("loopback")) {
return "Loopback";
} else if (name.startsWith("ppp") || displayName.contains("point-to-point")) {
return "PPP";
} else if (name.startsWith("vmnet") || displayName.contains("virtual")) {
return "Virtual";
} else if (name.startsWith("tun") || name.startsWith("tap")) {
return "TUN/TAP";
} else if (name.startsWith("br") || displayName.contains("bridge")) {
return "Bridge";
} else if (name.startsWith("bond") || displayName.contains("bond")) {
return "Bond";
} else {
return "Unknown";
}
}
/**
* 获取当前时间距离下一个“分钟为 0 或 5”的时间点还差多少毫秒
* 例如:xx:00, xx:05, xx:10, ..., xx:55
*/
public static long millisecondsToNext5Minute() {
LocalDateTime now = LocalDateTime.now();
// 当前分钟
int minute = now.getMinute();
// 计算下一个 5 分钟整点(向上取整)
int nextMinute = ((minute / 5) + 1) * 5;
LocalDateTime nextTime;
if (nextMinute < 60) {
// 在当前小时内
nextTime = now.withMinute(nextMinute).withSecond(0).withNano(0);
} else {
// 跨小时,如 10:58 → 11:00
nextTime = now.plusHours(1).withMinute(0).withSecond(0).withNano(0);
}
// 计算相差的毫秒数
return ChronoUnit.MILLIS.between(now, nextTime);
}
/**
* 获取距离下一个分钟整点的毫秒数
*/
public static long getMillisToNextMinute() {
LocalDateTime now = LocalDateTime.now();
// 获取下一个分钟整点时间
LocalDateTime nextMinute = now
.truncatedTo(ChronoUnit.MINUTES) // 截断到当前分钟
.plusMinutes(1); // 加1分钟
// 计算时间差(毫秒)
return ChronoUnit.MILLIS.between(now, nextMinute);
}
/**
* 获取距离下一个指定分钟整点的毫秒数
*/
public static long getMillisToNextMinuteInterval(int intervalMinutes) {
if (intervalMinutes <= 0) {
throw new IllegalArgumentException("Interval must be positive");
}
LocalDateTime now = LocalDateTime.now();
// 计算当前分钟在间隔中的位置
int currentMinute = now.getMinute();
int remainder = currentMinute % intervalMinutes;
// 计算需要增加的分钟数
int minutesToAdd = remainder == 0 ? intervalMinutes : intervalMinutes - remainder;
// 获取下一个间隔整点时间
LocalDateTime nextInterval = now
.truncatedTo(ChronoUnit.MINUTES) // 截断到当前分钟
.plusMinutes(minutesToAdd) // 增加到下一个整点
.withSecond(0) // 秒设为0
.withNano(0); // 纳秒设为0
return ChronoUnit.MILLIS.between(now, nextInterval);
}
public static long roundMinutes(){
LocalDateTime now = LocalDateTime.now();
// 四舍五入到最近的5分钟
LocalDateTime roundedTime = now
.truncatedTo(ChronoUnit.MINUTES) // 先去掉秒和纳秒
.withMinute((int) (Math.round(now.getMinute() / 5.0) * 5))
.withSecond(0)
.withNano(0);
// 处理分钟进位的情况
if (roundedTime.getMinute() == 60) {
roundedTime = roundedTime.plusHours(1).withMinute(0);
}
// 转换为10位时间戳
long timestamp = roundedTime.atZone(ZoneId.systemDefault()).toEpochSecond();
return timestamp;
}
}
@@ -0,0 +1,73 @@
package com.tongran.agent.client.utils;
import lombok.extern.slf4j.Slf4j;
/**
* 日志断言类
*
* @author BAO
*
*/
@Slf4j
public class AssertLog {
/**
* 打印Info 日志
*
* @param format
* @param arguments
*/
public static void info(String format, Object... arguments) {
if (log.isInfoEnabled()) {
log.info(format, arguments);
}
}
/**
* 打印Debug 日志
*
* @param format
* @param arguments
*/
public static void debug(String format, Object... arguments) {
if (log.isDebugEnabled()) {
log.debug(format, arguments);
}
}
/**
* 打印Error 日志
*
* @param format
* @param arguments
*/
public static void error(String format, Object... arguments) {
if (log.isErrorEnabled()) {
log.error(format, arguments);
}
}
/**
* 打印Trace 日志
*
* @param format
* @param arguments
*/
public static void trace(String format, Object... arguments) {
if (log.isTraceEnabled()) {
log.trace(format, arguments);
}
}
/**
* 打印Warn 日志
*
* @param format
* @param arguments
*/
public static void warn(String format, Object... arguments) {
if (log.isWarnEnabled()) {
log.warn(format, arguments);
}
}
}
@@ -0,0 +1,76 @@
package com.tongran.agent.client.utils;
import com.tongran.agent.client.exception.code.ErrorCode;
import com.tongran.agent.client.exception.code.GlobalErrorCode;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
/**
* @author BAO
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Schema(description = "接口交互统一数据返回标准")
public class R<T> implements Serializable {
private static final long serialVersionUID = 1L;
@Schema(description = "返回代码")
private Integer code;
@Schema(description = "消息描述")
private String msg;
@Schema(description = "结果对象")
private T data;
public static <T> R<T> success(String msg, T t) {
R<T> r = new R<>();
r.setData(t);
r.setMsg(msg);
r.setCode(GlobalErrorCode.SUCCESS.getCode());
return r;
}
public static <T> R<T> success(T t) {
return R.success(GlobalErrorCode.SUCCESS.getMsg(), t);
}
public static <T> R<T> success() {
return R.success(null);
}
public static <T> R<T> error(String msg, Integer code) {
R<T> r = new R<>();
r.setMsg(msg);
r.setCode(code);
return r;
}
public static <T> R<T> error(Integer code, String msg) {
R<T> r = new R<>();
r.setMsg(msg);
r.setCode(code);
return r;
}
public static <T> R<T> error(ErrorCode err) {
return R.error(err.getMsg(), err.getCode());
}
public static <T> R<T> error() {
return R.error(GlobalErrorCode.ERROR.getMsg(), GlobalErrorCode.ERROR.getCode());
}
public static <T> R<T> error(String msg) {
return R.error(msg, GlobalErrorCode.ERROR.getCode());
}
}
@@ -0,0 +1,53 @@
package com.tongran.agent.client.utils;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
public class RoundMinutes {
public static long convertMillisToRoundedSeconds(long millisTimestamp) {
// 1. 先将毫秒时间戳四舍五入到秒
long roundedSeconds = Math.round(millisTimestamp / 1000.0);
// 2. 转换为LocalDateTime进行分钟调整
LocalDateTime dateTime = LocalDateTime.ofInstant(
Instant.ofEpochSecond(roundedSeconds),
ZoneId.systemDefault()
);
// 3. 将分钟四舍五入到5分钟间隔
int minute = dateTime.getMinute();
int roundedMinute = (int) (Math.round(minute / 5.0) * 5);
// 4. 调整时间
LocalDateTime adjustedDateTime = dateTime
.withMinute(roundedMinute % 60)
.withSecond(0)
.withNano(0);
// 处理进位
if (roundedMinute == 60) {
adjustedDateTime = adjustedDateTime.plusHours(1);
}
// 5. 转换回时间戳
return adjustedDateTime.atZone(ZoneId.systemDefault()).toEpochSecond();
}
// public static void main(String[] args) {
// long millisTimestamp = System.currentTimeMillis();
// System.out.println("原始13位时间戳: " + millisTimestamp);
// System.out.println("原始时间: " + new java.util.Date(millisTimestamp));
//
// long result = convertMillisToRoundedSeconds(millisTimestamp);
// System.out.println("调整后10位时间戳: " + result);
// System.out.println("调整后时间: " + new java.util.Date(result * 1000));
// }
//
public static void main(String[] args) {
long timestamp = 1757471699499L;
long time = Math.round(timestamp / 1000.0);
System.out.println(time);
}
}
+22
View File
@@ -0,0 +1,22 @@
server:
port: 7010
servlet:
context-path: /agent-client
# 接口文档配置
knife4j:
enable: true
production: false # 开启屏蔽文档资源
# 日志配置
logging:
file:
path: /data/agent-client/logs
tcp:
netty:
charge:
enable: true
name: AGENT-TCP-服务
port: 6610
readerIdleTime: 300
+31
View File
@@ -0,0 +1,31 @@
spring:
profiles:
active: dev
mvc:
pathmatch:
matching-strategy: ant_path_matcher
application:
name: agent-client
version: 1.0
web:
resources:
static-locations: classpath*:/META-INF/resources/
logging:
config: classpath:logback/logback-${spring.profiles.active}.xml
# springdoc-openapi项目配置
knife4j:
setting:
enable-footer-custom: true
footer-custom-content: Apache License 2.0 | Copyright © 2025-[]
springdoc:
config:
title: AGENTTCP服务
description: AGENTTCP服务接口文档
contact: AGENTTCP
email:
version: ${spring.application.version}
group-configs:
- group: 'AGENTTCP服务'
paths-to-match: '/**'
packages-to-scan: com.tongran.agent.client
+116
View File
@@ -0,0 +1,116 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="60000" debug="false">
<springProperty scope="context" name="appName" source="spring.application.name" defaultValue="default"/>
<springProperty scope="context" name="logPath" source="logging.file.path" defaultValue="/logs/${appName}"/>
<contextName>${appName}</contextName>
<!--定义日志文件的存储地址 勿在 LogBack 的配置中使用相对路径 -->
<property name="LOG_HOME" value="${logPath}/${appName}"/>
<!-- 控制台输出 -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符 -->
<pattern>[requestId:%X{requestId}] [time:%d{yyyy-MM-dd HH:mm:ss.SSS}] [thread:%thread] [level:%-5level] [appName:${appName}] msg- %msg%n</pattern>
</encoder>
</appender>
<!-- 按照每天生成日志文件, debug级别 -->
<appender name="DEBUG" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 日志日常打印文件 -->
<file>${LOG_HOME}/debug.log</file>
<!-- 日志滚动规则 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--日志滚动文件名命令规则 -->
<FileNamePattern>${LOG_HOME}/debug.%d{yyyy-MM-dd}.%i.AssertLog.zip</FileNamePattern>
<!--日志文件保留天数 -->
<MaxHistory>30</MaxHistory>
<!-- 日志保存总量 -->
<totalSizeCap>10GB</totalSizeCap>
<!-- 日志切分规则 -->
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<!-- 文件切分压缩的大小阈值 -->
<maxFileSize>10MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
<!-- 日志输出的样式 -->
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符 -->
<pattern>[requestId:%X{requestId}] [time:%d{yyyy-MM-dd HH:mm:ss.SSS}] [thread:%thread] [level:%-5level] [appName:${appName}] msg- %msg%n</pattern>
</encoder>
</appender>
<!-- 按照每天生成日志文件, info级别 -->
<appender name="INFO" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 精确过滤, 只保存info级别 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>INFO</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<!-- 日志日常打印文件 -->
<file>${LOG_HOME}/info.log</file>
<!-- 日志滚动规则 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 日志滚动文件名命令规则 -->
<FileNamePattern>${LOG_HOME}/info.%d{yyyy-MM-dd}.%i.AssertLog.zip</FileNamePattern>
<!--日志文件保留天数 -->
<MaxHistory>30</MaxHistory>
<!-- 日志保存总量 -->
<totalSizeCap>10GB</totalSizeCap>
<!-- 日志切分规则 -->
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<!-- 文件切分压缩的大小阈值 -->
<maxFileSize>10MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
<!-- 日志输出的样式 -->
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符 -->
<pattern>[requestId:%X{requestId}] [time:%d{yyyy-MM-dd HH:mm:ss.SSS}] [thread:%thread] [level:%-5level] [appName:${appName}] msg- %msg%n</pattern>
</encoder>
</appender>
<!-- 按照固定大小生成日志文件, error级别 -->
<appender name="ERROR" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 精确过滤, 只保存error级别 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<file>${LOG_HOME}/error.log</file>
<!-- 日志滚动规则 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 日志滚动文件名命令规则 -->
<FileNamePattern>${LOG_HOME}/error.%d{yyyy-MM-dd}.%i.AssertLog.zip</FileNamePattern>
<!--日志文件保留天数 -->
<MaxHistory>30</MaxHistory>
<!-- 日志保存总量 -->
<totalSizeCap>10GB</totalSizeCap>
<!-- 日志切分规则 -->
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<!-- 文件切分压缩的大小阈值 -->
<maxFileSize>10MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
<!-- 日志输出的样式 -->
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符 -->
<pattern>[requestId:%X{requestId}] [time:%d{yyyy-MM-dd HH:mm:ss.SSS}] [thread:%thread] [level:%-5level] [appName:${appName}] msg- %msg%n</pattern>
</encoder>
</appender>
<logger name="com.alibaba.nacos.client" level="OFF"></logger>
<logger name="com.netflix" level="OFF"></logger>
<logger name="RocketmqClient" additivity="false">
<level value="warn" />
<appender-ref ref="ERROR"/>
</logger>
<!-- 日志输出级别 -->
<root level="info">
<appender-ref ref="STDOUT"/>
<appender-ref ref="INFO"/>
<appender-ref ref="ERROR"/>
<appender-ref ref="DEBUG"/>
</root>
</configuration>
@@ -0,0 +1,13 @@
package com.tongran.agent.client;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class TrAgentClientApplicationTests {
@Test
void contextLoads() {
}
}