增加多网IP探测上报;

优化公网运营商信息;
逻辑标识改为读取外部文件;
增加定时任务清理日志临时文件;
This commit is contained in:
qiminbao
2025-10-28 14:24:19 +08:00
parent 1485d5d46c
commit a2e3e296b5
14 changed files with 308 additions and 32 deletions
@@ -4,9 +4,11 @@ import com.tongran.agent.client.core.config.GlobalConfig;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.scheduling.annotation.EnableScheduling;
@SpringBootApplication
@EnableScheduling
@ComponentScan(basePackages = {"com.tongran.agent.client", "cn.hutool.extra.spring"})
public class TrAgentClientApplication {
@@ -10,7 +10,6 @@ public class ApplicationProperties {
private String name;
private String version;
private String confPath;
private String logicalNode;
private String scriptPath;
private String tmpPath;
private String tempPath;
@@ -41,14 +40,6 @@ public class ApplicationProperties {
this.confPath = confPath;
}
public String getLogicalNode() {
return logicalNode;
}
public void setLogicalNode(String logicalNode) {
this.logicalNode = logicalNode;
}
public String getScriptPath() {
return scriptPath;
}
@@ -3,6 +3,7 @@ package com.tongran.agent.client.core.config;
import com.tongran.agent.client.core.eo.AlarmEO;
import com.tongran.agent.client.core.eo.NativeNetworkInterfaceEO;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
@@ -37,6 +38,12 @@ public class GlobalConfig {
*/
public static String DEVICE_SN;
/**
* 逻辑标识
*/
public static LocalDateTime LOGICAL_NODE_LAST_TIME;
public static String LOGICAL_NODE;
/**
* 最新策略信息
*/
@@ -65,7 +65,9 @@ public enum MsgEnum {
Agent版本更新("AGENT_VERSION_UPDATE"),
Agent版本更新应答("AGENT_VERSION_UPDATE_RSP");
Agent版本更新应答("AGENT_VERSION_UPDATE_RSP"),
多网IP探测上报("NETWORK_DETECT");
private String value;
@@ -14,6 +14,7 @@ import lombok.experimental.SuperBuilder;
public class NetworkInterfaceInfo {
String name; // 接口名称:eth0, enp3s0
String mac; // MAC 地址
String type; // 接口类型:Ethernet
String ipv4; // IPv4 地址
String gateway; // 网关
String publicIp; // 公网 IP
@@ -0,0 +1,45 @@
package com.tongran.agent.client.scheduler.job;
import com.tongran.agent.client.core.config.ApplicationProperties;
import com.tongran.agent.client.utils.AssertLog;
import com.tongran.agent.client.utils.FileCleaner;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.time.Duration;
import java.time.LocalDateTime;
@Slf4j
@Component
public class DailyJob {
// @Value("${spring.profiles.active}")
// private String active;
@Resource
private ApplicationProperties properties;
@Scheduled(cron = "0 0 2 * * ?")
void dailyTask() {
// if (!StringUtils.equals(active, "prod")) {
// return;
// }
LocalDateTime start = LocalDateTime.now();
AssertLog.info("临时文件定时清理作业开始:{}", LocalDateTime.now());
String[] paths = new String[]{properties.getTmpPath(), properties.getTempPath()};
for (String path : paths) {
if(StringUtils.isNotBlank(path)){
FileCleaner.cleanOldFiles(path,30);
}
}
LocalDateTime end = LocalDateTime.now();
AssertLog.info("临时文件定时清理作业结束:{},耗时={}s", end, Duration.between(start, end).getSeconds());
}
}
@@ -10,6 +10,7 @@ import com.tongran.agent.client.netty.model.Message;
import com.tongran.agent.client.service.AgentService;
import com.tongran.agent.client.utils.AgentUtil;
import com.tongran.agent.client.utils.AssertLog;
import org.apache.commons.lang3.StringUtils;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
@@ -83,9 +84,17 @@ public class AppInitializer implements CommandLineRunner {
long milli = AgentUtil.getMillisToNextMinute() + 60000;
AssertLog.info("启动更新策略定时任务 - 延迟: {}ms, 间隔: {}ms", milli, 60000);
dynamicTaskService.scheduleTask("policy", businessTasks::policyTask, milli, 60000);
// 创建多网IP探测上报
AssertLog.info("启动多网IP探测定时任务 - 延迟: {}ms, 间隔: {}ms", milli, 300000);
dynamicTaskService.scheduleTask("networkDetect", businessTasks::networkDetectTask, milli, 300000);
}else{
//未注册,发送注册
try {
if(StringUtils.isBlank(GlobalConfig.DEVICE_SN)){
GlobalConfig.DEVICE_SN = AgentUtil.getDeviceSN();
}
long timestamps = System.currentTimeMillis();
timestamps = Math.round(timestamps / 1000.0);
List<NetworkInterfaceInfo> infos = AgentUtil.collectNetworkInfo();
@@ -58,6 +58,7 @@ public class BusinessTasks {
private final AtomicInteger policyTask = new AtomicInteger(0);
private final AtomicInteger registerTask = new AtomicInteger(0);
private final AtomicInteger connectionTask = new AtomicInteger(0);
private final AtomicInteger networkDetectTask = new AtomicInteger(0);
@@ -119,10 +120,13 @@ public class BusinessTasks {
// 业务处理
// 判定客户端与服务端是否连接
if (Objects.nonNull(sessionManager.getSessionById(GlobalConfig.CLIENT_ID))) {
if(StringUtils.isBlank(GlobalConfig.DEVICE_SN)){
GlobalConfig.DEVICE_SN = AgentUtil.getDeviceSN();
}
// 发送心跳包
JSONObject object = new JSONObject();
object.put("clientId", GlobalConfig.CLIENT_ID);
object.put("logicalNode", properties.getLogicalNode());
object.put("logicalNode", agentService.getLogicalNode());
object.put("sn", GlobalConfig.DEVICE_SN);
object.put("strength","31");
object.put("name", properties.getName());
@@ -737,6 +741,9 @@ public class BusinessTasks {
AssertLog.info("获取最新策略定时任务执行 - 时间: {},task #{}", LocalDateTime.now(), count);
// 判定客户端与服务端是否连接
if (Objects.nonNull(sessionManager.getSessionById(GlobalConfig.CLIENT_ID))) {
if(StringUtils.isBlank(GlobalConfig.DEVICE_SN)){
GlobalConfig.DEVICE_SN = AgentUtil.getDeviceSN();
}
// 发送更新策略
JSONObject object = new JSONObject();
object.put("clientId", GlobalConfig.CLIENT_ID);
@@ -768,6 +775,9 @@ public class BusinessTasks {
AssertLog.info("连接成功,发送初始连接消息");
try {
if (Objects.nonNull(sessionManager.getSessionById(GlobalConfig.CLIENT_ID))) {
if(StringUtils.isBlank(GlobalConfig.DEVICE_SN)){
GlobalConfig.DEVICE_SN = AgentUtil.getDeviceSN();
}
List<NetworkInterfaceInfo> infos = AgentUtil.collectNetworkInfo();
JSONObject object = new JSONObject();
object.put("clientId", GlobalConfig.CLIENT_ID);
@@ -797,6 +807,9 @@ public class BusinessTasks {
AssertLog.info("建立连接重试定时任务执行 - 时间: {},task #{}", LocalDateTime.now(), count);
boolean success = client.createConnection(GlobalConfig.CLIENT_ID, config.getHost(), config.getPort(), 5);
if(success){
if(StringUtils.isBlank(GlobalConfig.DEVICE_SN)){
GlobalConfig.DEVICE_SN = AgentUtil.getDeviceSN();
}
//连接成功,发送初始连接消息
long timestamp = System.currentTimeMillis();
timestamp = Math.round(timestamp / 1000.0);
@@ -820,6 +833,9 @@ public class BusinessTasks {
}else{
//未注册,发送注册
try {
if(StringUtils.isBlank(GlobalConfig.DEVICE_SN)){
GlobalConfig.DEVICE_SN = AgentUtil.getDeviceSN();
}
long timestamps = System.currentTimeMillis();
timestamps = Math.round(timestamps / 1000.0);
List<NetworkInterfaceInfo> infos = AgentUtil.collectNetworkInfo();
@@ -842,6 +858,39 @@ public class BusinessTasks {
AssertLog.info("建立连接重试定时任务执行 - task #{} completed", count);
}
/**
* 任务29:多网IP探测
*/
@Async("taskExecutor")
public void networkDetectTask() {
int count = networkDetectTask.incrementAndGet();
AssertLog.info("多网IP探测定时任务执行 - 时间: {}task #{}", LocalDateTime.now(), count);
// 判定客户端与服务端是否连接
if (Objects.nonNull(sessionManager.getSessionById(GlobalConfig.CLIENT_ID))) {
if(StringUtils.isBlank(GlobalConfig.DEVICE_SN)){
GlobalConfig.DEVICE_SN = AgentUtil.getDeviceSN();
}
// 发送多网IP探测
long timestamps = System.currentTimeMillis();
timestamps = Math.round(timestamps / 1000.0);
List<NetworkInterfaceInfo> infos = null;
try {
infos = AgentUtil.collectNetworkInfo();
} catch (Exception e) {
e.printStackTrace();
}
JSONObject objects = new JSONObject();
objects.put("clientId", GlobalConfig.CLIENT_ID);
objects.put("sn", GlobalConfig.DEVICE_SN);
objects.put("networkInfo", JSON.toJSONString(infos));
objects.put("timestamp", timestamps);
Message message = Message.builder().clientId(GlobalConfig.CLIENT_ID).dataType(MsgEnum.多网IP探测上报.getValue()).data(objects.toString()).build();
sessionManager.writeAndFlush(sessionManager.getSessionById(GlobalConfig.CLIENT_ID).getChannel(), message);
AssertLog.info("发送多网IP探测信息包={}",JSON.toJSONString(message));
}
AssertLog.info("发送多网IP探测定时任务执行 - task #{} completed", count);
}
@@ -26,4 +26,6 @@ public interface AgentService {
void addRoute(String name, String gateway);
void dellRoute(String name, String gateway);
String getLogicalNode();
}
@@ -31,6 +31,7 @@ import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.*;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
@@ -433,15 +434,15 @@ public class AgentServiceImpl implements AgentService {
break;
case "memorySizePercentCollect": //可用内存百分比采集
if(GlobalConfig.memorySizePercentCollect != collect || GlobalConfig.memorySizePercentInterval != interval){
GlobalConfig.memorySizePercentCollect = collect;
if(collect){
GlobalConfig.memorySizePercentInterval = interval;
long milli = AgentUtil.getMillisToNextMinute() + 60000;
dynamicTaskService.scheduleTask(type,
businessTasks::memorySizePercentTask, milli, GlobalConfig.memorySizePercentInterval*1000L);
}else{
dynamicTaskService.cancelTask(type);
GlobalConfig.memorySizePercentInterval = 300;
GlobalConfig.memorySizePercentCollect = collect;
if(collect){
GlobalConfig.memorySizePercentInterval = interval;
long milli = AgentUtil.getMillisToNextMinute() + 60000;
dynamicTaskService.scheduleTask(type,
businessTasks::memorySizePercentTask, milli, GlobalConfig.memorySizePercentInterval*1000L);
}else{
dynamicTaskService.cancelTask(type);
GlobalConfig.memorySizePercentInterval = 300;
}
}
break;
@@ -914,6 +915,9 @@ public class AgentServiceImpl implements AgentService {
client.closeConnection(GlobalConfig.CLIENT_ID);
boolean success = client.createConnection(GlobalConfig.CLIENT_ID, config.getHost(), config.getPort(), 5);
if(success){
if(StringUtils.isBlank(GlobalConfig.DEVICE_SN)){
GlobalConfig.DEVICE_SN = AgentUtil.getDeviceSN();
}
//连接成功,发送初始连接消息
long timestamp = System.currentTimeMillis();
timestamp = Math.round(timestamp / 1000.0);
@@ -999,6 +1003,39 @@ public class AgentServiceImpl implements AgentService {
}
}
@Override
public String getLogicalNode() {
String filePath = properties.getConfPath()+"/logical.conf";
String result = "";
Path path = Paths.get(filePath);
// 判断文件是否存在
if (!Files.exists(path)) {
System.err.println("文件不存在: " + filePath);
return result;
}
// 判断文件最后更新时间
LocalDateTime lastTime = AgentUtil.getLastModifiedTime(filePath);
if(GlobalConfig.LOGICAL_NODE_LAST_TIME != null){
if(lastTime.equals(GlobalConfig.LOGICAL_NODE_LAST_TIME) && StringUtils.isNotBlank(GlobalConfig.LOGICAL_NODE)){
return GlobalConfig.LOGICAL_NODE;
}
}
Properties props = new Properties();
try (InputStream input = Files.newInputStream(path)) {
// 加载配置文件
props.load(input);
result = props.getProperty("logicalNode", "");
System.out.println("配置文件加载成功");
GlobalConfig.LOGICAL_NODE_LAST_TIME = lastTime;
GlobalConfig.LOGICAL_NODE = result;
} catch (IOException e) {
System.err.println("无法加载配置文件,使用默认值");
} catch (NumberFormatException e) {
System.err.println("配置文件格式错误: " + e.getMessage());
}
return result;
}
public void caseTypeBySystem(String type, int interval, boolean collect){
@@ -2,24 +2,32 @@ package com.tongran.agent.client.utils;
import cn.hutool.json.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.tongran.agent.client.core.config.GlobalConfig;
import com.tongran.agent.client.core.vo.NetworkInterfaceInfo;
import org.snmp4j.smi.OID;
import oshi.hardware.NetworkIF;
import javax.annotation.Resource;
import java.io.*;
import java.net.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
public class AgentUtil {
@Resource
private GlobalConfig globalConfig;
public static String getMotherboardUUID() {
String hostName = getHostname();
String primaryIp = getPrimaryIp();
@@ -243,6 +251,8 @@ public class AgentUtil {
return swapped;
}
// 正则表达式匹配:当前 IPxxx.xxx.xxx.xxx 来自于:中国 江苏省 南京市 电信
private static final String REGEX = "来自于:(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)";
/**
* 收集所有 Ethernet 类型网卡信息
*/
@@ -272,6 +282,7 @@ public class AgentUtil {
NetworkInterfaceInfo info = NetworkInterfaceInfo.builder()
.name(name)
.type("Ethernet")
.mac(getMacAddress(ni))
.ipv4(getIPv4Address(ni))
.gateway(getGatewayAddress()) // 网关通常是默认路由,全局一致
@@ -280,18 +291,36 @@ public class AgentUtil {
// 如果有公网 IP,查询归属地
if (publicIp != null && !publicIp.isEmpty()) {
JSONObject location = queryIpLocation(publicIp);
if (location != null) {
info.setCarrier(location.getStr("org", "未知").replaceAll("^AS\\d+\\s*", ""));
info.setProvince(location.getStr("region", "未知"));
info.setCity(location.getStr("city", "未知"));
} else {
info.setCarrier("查询失败");
info.setProvince("查询失败");
info.setCity("查询失败");
Pattern pattern = Pattern.compile(REGEX);
Matcher matcher = pattern.matcher(ipInfo);
if (matcher.find()) {
// group(1): 国家(通常是“中国”)
// group(2): 省份
// group(3): 城市
// group(4): 运营商
String province = matcher.group(2);
String city = matcher.group(3);
String isp = matcher.group(4);
// 去掉可能的标点符号(如句号)
province = province.replaceAll("[。]", "");
city = city.replaceAll("[。]", "");
isp = isp.replaceAll("[。]", "");
info.setCarrier(isp);
info.setProvince(province);
info.setCity(city);
}
// ipInfo.substring(ipInfo.indexOf("来自于:")+4,ipInfo.length())
// JSONObject location = queryIpLocation(publicIp);
// if (location != null) {
// info.setCarrier(location.getStr("org", "未知").replaceAll("^AS\\d+\\s*", ""));
// info.setProvince(location.getStr("region", "未知"));
// info.setCity(location.getStr("city", "未知"));
// } else {
// info.setCarrier("查询失败");
// info.setProvince("查询失败");
// info.setCity("查询失败");
// }
}
result.add(info);
}
@@ -580,4 +609,27 @@ public class AgentUtil {
.collect(Collectors.joining("\n")); // 用换行符连接
}
/**
* 获取文件最后修改时间
* @param filePath
* @return
*/
public static LocalDateTime getLastModifiedTime(String filePath) {
try {
BasicFileAttributes attrs = Files.readAttributes(Paths.get(filePath), BasicFileAttributes.class);
return attrs.lastModifiedTime()
.toInstant()
.atZone(ZoneId.systemDefault())
.toLocalDateTime();
} catch (IOException e) {
System.err.println("无法读取文件时间: " + filePath);
return null;
}
// LocalDateTime mtime = FileUtils.getLastModifiedTime("/tmp/data.log");
// if (mtime != null) {
// System.out.println("修改时间: " + mtime);
// }
}
}
@@ -0,0 +1,74 @@
package com.tongran.agent.client.utils;
import java.io.IOException;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.BasicFileAttributes;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.List;
public class FileCleaner {
/**
* 删除指定目录中 30 天前修改的文件
*
* @param directoryPath 目录路径,例如:"/tmp/logs"
* @param days 保留天数,传入 30 表示删除 30 天以上的文件
*/
public static void cleanOldFiles(String directoryPath, int days) {
Path dir = Paths.get(directoryPath);
// 检查目录是否存在且是目录
if (!Files.exists(dir)) {
System.err.println("目录不存在: " + directoryPath);
return;
}
if (!Files.isDirectory(dir)) {
System.err.println("路径不是目录: " + directoryPath);
return;
}
// 计算 30 天前的时间点(Instant)
Instant cutoffTime = Instant.now().minus(days, ChronoUnit.DAYS);
List<Path> deletedFiles = new ArrayList<>();
List<Path> failedFiles = new ArrayList<>();
try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir)) {
for (Path file : stream) {
// 只处理普通文件(跳过子目录等)
if (Files.isRegularFile(file)) {
try {
BasicFileAttributes attrs = Files.readAttributes(file, BasicFileAttributes.class);
Instant lastModifiedTime = attrs.lastModifiedTime().toInstant();
if (lastModifiedTime.isBefore(cutoffTime)) {
Files.delete(file); // 删除文件
deletedFiles.add(file);
System.out.println("已删除: " + file + " (修改时间: " + lastModifiedTime + ")");
}
} catch (IOException e) {
System.err.println("无法读取或删除文件: " + file + " -> " + e.getMessage());
failedFiles.add(file);
}
}
}
} catch (IOException e) {
System.err.println("遍历目录失败: " + e.getMessage());
}
// 统计结果
System.out.println("✅ 清理完成:");
System.out.println(" 删除文件数: " + deletedFiles.size());
System.out.println(" 删除失败数: " + failedFiles.size());
}
// 使用示例
public static void main(String[] args) {
// String logDir = "/tmp/logs"; // 替换为你的实际目录
// cleanOldFiles(logDir, 30); // 删除 30 天以上的文件
}
}
@@ -34,7 +34,7 @@ public class PublicIpFetcher {
// 读取响应(注意:myip.ipip.net 返回的是 GBK 编码)
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(connection.getInputStream(), "GBK"))) { // 使用 GBK 解码
new InputStreamReader(connection.getInputStream(), "UTF-8"))) { // 使用 GBK 解码
StringBuilder response = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
@@ -71,6 +71,12 @@ public class PublicIpFetcher {
} else {
System.out.println("未能提取 IP 地址");
}
String province = "";
String city = "";
String carrier = "";
System.out.println(ipInfo.substring(ipInfo.indexOf("来自于:")+4,ipInfo.length()));
} catch (IOException e) {
System.err.println("获取公网 IP 失败: " + e.getMessage());