初始化
This commit is contained in:
@@ -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> <!– 请检查并使用最新版本 –>-->
|
||||
<!-- </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 {
|
||||
|
||||
//文件地址,外网HTTP(S)地址
|
||||
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;
|
||||
}
|
||||
}
|
||||
+121
@@ -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);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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() {
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user