diff --git a/pom.xml b/pom.xml
index 2e349d2..8a6387a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -9,9 +9,9 @@
com.tongran.agent
- tr-agent-client
+ tr-mtragent-client
0.0.1-SNAPSHOT
- tr-agent-client
+ tr-mtragent-client
tr-agent-client
diff --git a/src/main/java/com/tongran/agent/client/core/config/GlobalConfig.java b/src/main/java/com/tongran/agent/client/core/config/GlobalConfig.java
index 13c5ef5..3d7b39b 100644
--- a/src/main/java/com/tongran/agent/client/core/config/GlobalConfig.java
+++ b/src/main/java/com/tongran/agent/client/core/config/GlobalConfig.java
@@ -56,6 +56,7 @@ public class GlobalConfig {
public static long SCRIPT_TIME = 0L;
public static long VERSION_TIME = 0L;
public static long ROUTE_TIME = 0L;
+ public static String MTRPOLICYMSG = "";
/**
* 采集标识
*/
diff --git a/src/main/java/com/tongran/agent/client/core/enums/MsgEnum.java b/src/main/java/com/tongran/agent/client/core/enums/MsgEnum.java
index 1892650..ce7cdea 100644
--- a/src/main/java/com/tongran/agent/client/core/enums/MsgEnum.java
+++ b/src/main/java/com/tongran/agent/client/core/enums/MsgEnum.java
@@ -67,6 +67,8 @@ public enum MsgEnum {
Agent版本更新应答("AGENT_VERSION_UPDATE_RSP"),
+ MTR探测上报("MTR_DETECT"),
+
多网IP探测上报("NETWORK_DETECT");
private String value;
diff --git a/src/main/java/com/tongran/agent/client/core/eo/MtrPolicyConfigEO.java b/src/main/java/com/tongran/agent/client/core/eo/MtrPolicyConfigEO.java
new file mode 100644
index 0000000..d42a4b3
--- /dev/null
+++ b/src/main/java/com/tongran/agent/client/core/eo/MtrPolicyConfigEO.java
@@ -0,0 +1,45 @@
+package com.tongran.agent.client.core.eo;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+
+import java.util.Date;
+
+/**
+ * mtr策略信息
+ */
+@Data
+public class MtrPolicyConfigEO {
+ private static final long serialVersionUID = 1L;
+
+ /** 主键ID */
+ private Long id;
+
+ /** 策略名称 */
+ private String policyName;
+
+ /** 优先级 */
+ private Long priority;
+
+ /** MTR客户端ID */
+ private String mtrClientId;
+
+ /** 服务器集合(换行符分割) */
+ private String serverGroup;
+ /** 探测目标ip集合 */
+ private String serveripGroup;
+
+ /** 是否探测(0-否,1-是) */
+ private Long probeFlag;
+
+ /** 开始时间 */
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+ private Date startTime;
+
+ /** 结束时间 */
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+ private Date endTime;
+
+ /** 探测频率(秒) */
+ private Long probeFrequency;
+}
diff --git a/src/main/java/com/tongran/agent/client/core/vo/MtrVO.java b/src/main/java/com/tongran/agent/client/core/vo/MtrVO.java
new file mode 100644
index 0000000..c089fae
--- /dev/null
+++ b/src/main/java/com/tongran/agent/client/core/vo/MtrVO.java
@@ -0,0 +1,16 @@
+package com.tongran.agent.client.core.vo;
+
+import lombok.Data;
+
+import java.util.Date;
+
+@Data
+public class MtrVO {
+ private String targetIp; // 目标IP
+ private Long policyId; // 策略ID
+ private double firstLossPercent; // 第一次探测丢包率
+ private double finalLossPercent; // 最终丢包率
+ private long timestamp; // 探测时间戳
+ private boolean hasRetry; // 是否重试
+ private String errorMsg; // 错误信息
+}
diff --git a/src/main/java/com/tongran/agent/client/scheduler/service/BusinessTasks.java b/src/main/java/com/tongran/agent/client/scheduler/service/BusinessTasks.java
index 7288315..f033a85 100644
--- a/src/main/java/com/tongran/agent/client/scheduler/service/BusinessTasks.java
+++ b/src/main/java/com/tongran/agent/client/scheduler/service/BusinessTasks.java
@@ -7,6 +7,7 @@ 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.eo.MtrPolicyConfigEO;
import com.tongran.agent.client.core.session.SessionManager;
import com.tongran.agent.client.core.vo.*;
import com.tongran.agent.client.netty.MultiTargetNettyClient;
@@ -59,6 +60,7 @@ public class BusinessTasks {
private final AtomicInteger registerTask = new AtomicInteger(0);
private final AtomicInteger connectionTask = new AtomicInteger(0);
private final AtomicInteger networkDetectTask = new AtomicInteger(0);
+ private final AtomicInteger mtrTask = new AtomicInteger(0);
@@ -108,6 +110,9 @@ public class BusinessTasks {
@Resource
private AgentService agentService;
+ @Resource
+ private MtrService mtrService;
+
/**
* 任务1:心跳上报任务
*/
@@ -900,6 +905,55 @@ public class BusinessTasks {
AssertLog.info("发送多网IP探测定时任务执行 - task #{} completed", count);
}
+ /**
+ * 任务30:MTR网络探测任务
+ */
+ @Async("taskExecutor")
+ public void mtrTask() {
+ long timestamp = AgentUtil.roundMinutes(); // 使用5分钟整点时间
+ int count = mtrTask.incrementAndGet();
+ AssertLog.info("MTR网络探测定时任务执行 - 时间: {},task #{}", LocalDateTime.now(), count);
+
+ try {
+ // 检查当前是否有有效的MTR策略
+ if (StringUtils.isBlank(GlobalConfig.MTRPOLICYMSG)) {
+ AssertLog.info("没有MTR策略配置,跳过本次执行");
+ return;
+ }
+
+ // 解析策略
+ List mtrPolicyConfigEOList = JSON.parseArray(GlobalConfig.MTRPOLICYMSG, MtrPolicyConfigEO.class);
+ if (CollectionUtil.isEmpty(mtrPolicyConfigEOList)) {
+ AssertLog.info("MTR策略列表为空,跳过本次执行");
+ return;
+ }
+
+ // 判定客户端与服务端是否连接
+ if (Objects.nonNull(sessionManager.getSessionById(GlobalConfig.CLIENT_ID))) {
+ // 执行MTR探测并获取结果
+ String data = "";
+ List list = mtrService.mtrList(timestamp, mtrPolicyConfigEOList);
+ if (CollectionUtil.isNotEmpty(list)) {
+ data = JSONArray.toJSONString(list);
+
+ // 发送MTR探测结果
+ Message message = Message.builder()
+ .clientId(GlobalConfig.CLIENT_ID)
+ .dataType(MsgEnum.MTR探测上报.getValue())
+ .data(data)
+ .build();
+ sessionManager.writeAndFlush(sessionManager.getSessionById(GlobalConfig.CLIENT_ID).getChannel(), message);
+ AssertLog.info("发送MTR探测信息包,结果数量: {}", list.size());
+ } else {
+ AssertLog.info("本次MTR探测无有效结果");
+ }
+ }
+ } catch (Exception e) {
+ AssertLog.error("MTR定时任务执行异常", e);
+ }
+
+ AssertLog.info("MTR网络探测定时任务执行 - task #{} completed", count);
+ }
diff --git a/src/main/java/com/tongran/agent/client/service/MtrService.java b/src/main/java/com/tongran/agent/client/service/MtrService.java
new file mode 100644
index 0000000..a170268
--- /dev/null
+++ b/src/main/java/com/tongran/agent/client/service/MtrService.java
@@ -0,0 +1,10 @@
+package com.tongran.agent.client.service;
+
+import com.tongran.agent.client.core.eo.MtrPolicyConfigEO;
+import com.tongran.agent.client.core.vo.MtrVO;
+
+import java.util.List;
+
+public interface MtrService {
+ List mtrList(long timestamp, List mtrPolicyConfigEOList);
+}
diff --git a/src/main/java/com/tongran/agent/client/service/impl/AgentServiceImpl.java b/src/main/java/com/tongran/agent/client/service/impl/AgentServiceImpl.java
index 1ddf808..56a7d54 100644
--- a/src/main/java/com/tongran/agent/client/service/impl/AgentServiceImpl.java
+++ b/src/main/java/com/tongran/agent/client/service/impl/AgentServiceImpl.java
@@ -9,8 +9,10 @@ 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.CollectEO;
+import com.tongran.agent.client.core.eo.MtrPolicyConfigEO;
import com.tongran.agent.client.core.eo.ScriptPolicyEO;
import com.tongran.agent.client.core.session.SessionManager;
+import com.tongran.agent.client.core.vo.MtrVO;
import com.tongran.agent.client.netty.MultiTargetNettyClient;
import com.tongran.agent.client.netty.config.AgentNettyConfig;
import com.tongran.agent.client.netty.model.Message;
@@ -35,6 +37,9 @@ import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
import java.util.stream.Collectors;
@Service
@@ -825,11 +830,73 @@ public class AgentServiceImpl implements AgentService {
}
}
}
-
}
+ if(jsonObject.containsKey("mtrPolicys")){
+ String mtrPolicys = jsonObject.getString("mtrPolicys");
+ if(!mtrPolicys.equals(GlobalConfig.MTRPOLICYMSG)){
+ GlobalConfig.MTRPOLICYMSG = mtrPolicys;
+ List mtrPolicyConfigEOList = JSON.parseArray(mtrPolicys, MtrPolicyConfigEO.class);
+ try {
+ if (CollectionUtil.isEmpty(mtrPolicyConfigEOList)) {
+ // 如果没有策略,取消定时任务
+ dynamicTaskService.cancelTask("mtrTask");
+ AssertLog.info("MTR策略为空,取消MTR定时任务");
+ return;
+ }
+ // 检查是否有有效的策略(在时间范围内的策略)
+ boolean hasValidPolicy = false;
+ long currentTimestamp = System.currentTimeMillis() / 1000;
+
+ for (MtrPolicyConfigEO policy : mtrPolicyConfigEOList) {
+ if (isPolicyTimeValid(policy, currentTimestamp)) {
+ hasValidPolicy = true;
+ break;
+ }
+ }
+
+ if (!hasValidPolicy) {
+ // 没有有效策略,取消定时任务
+ dynamicTaskService.cancelTask("mtrTask");
+ AssertLog.info("没有有效的MTR策略,取消MTR定时任务");
+ return;
+ }
+
+ // 计算下一个5分钟整点的延迟时间
+ long milli = AgentUtil.millisecondsToNext5Minute();
+
+ // 启动或更新定时任务
+ dynamicTaskService.scheduleTask("mtrTask",
+ businessTasks::mtrTask, milli, 300000L); // 5分钟间隔
+
+ AssertLog.info("MTR定时任务已启动/更新,延迟: {}ms, 间隔: 300000ms", milli);
+
+ } catch (Exception e) {
+ AssertLog.error("启动MTR定时任务异常", e);
+ }
+ }
+ }
}
+ /**
+ * 检查策略时间是否有效
+ */
+ private boolean isPolicyTimeValid(MtrPolicyConfigEO policy, long currentTimestamp) {
+ try {
+ Date currentTime = new Date(currentTimestamp * 1000);
+ Date startTime = policy.getStartTime();
+ Date endTime = policy.getEndTime();
+ // 开始时间为空表示立即开始
+ boolean startValid = startTime == null || !currentTime.before(startTime);
+ // 结束时间为空表示永不过期
+ boolean endValid = endTime == null || !currentTime.after(endTime);
+
+ return startValid && endValid;
+ } catch (Exception e) {
+ AssertLog.error("检查策略时间有效性异常", e);
+ return false;
+ }
+ }
@Override
public void checkTrAgent() {
String CRON_FILE_PATH = properties.getScriptPath()+"/tr_live.cron";
@@ -904,7 +971,7 @@ public class AgentServiceImpl implements AgentService {
@Override
public boolean connection() {
- String clientId = MachineFingerprint.getHardwareFingerprint();
+ String clientId = "MTR" + MachineFingerprint.getHardwareFingerprint();
//初始化SN信息
GlobalConfig.DEVICE_SN = AgentUtil.getDeviceSN();
//检查外置目录是否存在
diff --git a/src/main/java/com/tongran/agent/client/service/impl/MtrServiceImpl.java b/src/main/java/com/tongran/agent/client/service/impl/MtrServiceImpl.java
new file mode 100644
index 0000000..6b15987
--- /dev/null
+++ b/src/main/java/com/tongran/agent/client/service/impl/MtrServiceImpl.java
@@ -0,0 +1,213 @@
+package com.tongran.agent.client.service.impl;
+
+import cn.hutool.core.collection.CollectionUtil;
+import com.alibaba.fastjson2.JSON;
+import com.alibaba.fastjson2.JSONObject;
+import com.tongran.agent.client.core.eo.MtrPolicyConfigEO;
+import com.tongran.agent.client.core.vo.MtrVO;
+import com.tongran.agent.client.core.vo.NetVO;
+import com.tongran.agent.client.service.MtrService;
+import com.tongran.agent.client.utils.AgentUtil;
+import com.tongran.agent.client.utils.AssertLog;
+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.CompletableFuture;
+import java.util.concurrent.TimeUnit;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+@Service
+public class MtrServiceImpl implements MtrService {
+ /**
+ * 执行MTR探测列表 - 异步版本
+ */
+ public List mtrList(long timestamp, List mtrPolicyConfigEOList) {
+ List> futures = new ArrayList<>();
+ List mtrResults = new ArrayList<>();
+
+ try {
+ if (CollectionUtil.isEmpty(mtrPolicyConfigEOList)) {
+ return mtrResults;
+ }
+
+ // 为每个IP创建异步任务
+ for (MtrPolicyConfigEO policy : mtrPolicyConfigEOList) {
+ if (!isPolicyTimeValid(policy, timestamp)) {
+ continue;
+ }
+
+ String[] targetIps = policy.getServeripGroup().split(";");
+ for (String targetIp : targetIps) {
+ String ip = targetIp.trim();
+ if (!ip.isEmpty()) {
+ // 异步执行每个IP的探测
+ CompletableFuture future = CompletableFuture.supplyAsync(() ->
+ executeSingleMtrProbe(ip, policy, timestamp)
+ );
+ futures.add(future);
+ }
+ }
+ }
+
+ // 等待所有任务完成
+ CompletableFuture allFutures = CompletableFuture.allOf(
+ futures.toArray(new CompletableFuture[0])
+ );
+
+ // 获取所有结果(设置超时时间,比如2分钟)
+ allFutures.get(2, TimeUnit.MINUTES);
+
+ for (CompletableFuture future : futures) {
+ MtrVO result = future.get();
+ if (result != null) {
+ mtrResults.add(result);
+ }
+ }
+
+ } catch (Exception e) {
+ AssertLog.error("MTR异步探测异常", e);
+ }
+
+ return mtrResults;
+ }
+
+
+ /**
+ * 检查策略时间是否有效
+ */
+ private boolean isPolicyTimeValid(MtrPolicyConfigEO policy, long currentTimestamp) {
+ try {
+ // 将时间戳转换为Date对象进行比较
+ Date currentTime = new Date(currentTimestamp * 1000);
+ Date startTime = policy.getStartTime();
+ Date endTime = policy.getEndTime();
+
+ // 开始时间为空表示立即开始
+ boolean startValid = startTime == null || !currentTime.before(startTime);
+ // 结束时间为空表示永不过期
+ boolean endValid = endTime == null || !currentTime.after(endTime);
+
+ return startValid && endValid;
+ } catch (Exception e) {
+ AssertLog.error("检查策略时间有效性异常", e);
+ return false;
+ }
+ }
+
+ /**
+ * 执行单个IP的MTR探测
+ */
+ private MtrVO executeSingleMtrProbe(String targetIp, MtrPolicyConfigEO policy, long timestamp) {
+ MtrVO mtrVO = new MtrVO();
+ mtrVO.setTargetIp(targetIp);
+ mtrVO.setPolicyId(policy.getId());
+ mtrVO.setTimestamp(timestamp);
+
+ try {
+ // 第一次MTR探测
+ double firstLossPercent = executeMtrCommand(targetIp);
+ mtrVO.setFirstLossPercent(firstLossPercent);
+
+ // 如果丢包率超过5%,30秒后重试
+ if (firstLossPercent > 5.0) {
+ AssertLog.info("目标IP {} 丢包率 {}% > 5%,30秒后重试探测", targetIp, firstLossPercent);
+
+ // 等待30秒
+ Thread.sleep(30000);
+
+ // 第二次MTR探测
+ double secondLossPercent = executeMtrCommand(targetIp);
+ mtrVO.setFinalLossPercent(secondLossPercent);
+ mtrVO.setHasRetry(true);
+
+ AssertLog.info("目标IP {} 重试探测完成,最终丢包率: {}%", targetIp, secondLossPercent);
+ } else {
+ mtrVO.setFinalLossPercent(firstLossPercent);
+ mtrVO.setHasRetry(false);
+ AssertLog.info("目标IP {} 丢包率 {}%,无需重试", targetIp, firstLossPercent);
+ }
+
+ return mtrVO;
+
+ } catch (Exception e) {
+ AssertLog.error("MTR探测异常 - IP: {}", targetIp, e);
+ mtrVO.setFinalLossPercent(-1.0); // -1表示探测失败
+ mtrVO.setErrorMsg(e.getMessage());
+ return mtrVO;
+ }
+ }
+
+ /**
+ * 执行mtr -r命令并解析结果
+ */
+ private double executeMtrCommand(String targetIp) {
+ Process process = null;
+ try {
+ // 执行mtr -r -c 10命令
+ ProcessBuilder processBuilder = new ProcessBuilder("mtr", "-r", "-c", "10", targetIp);
+ process = processBuilder.start();
+
+ // 读取命令输出
+ BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
+ String line;
+ String lastValidLine = null;
+
+ while ((line = reader.readLine()) != null) {
+ // 跳过标题行,记录最后一条有效数据行
+ if (!line.trim().isEmpty() &&
+ !line.startsWith("Start:") &&
+ !line.startsWith("HOST:") &&
+ Character.isDigit(line.trim().charAt(0))) {
+ lastValidLine = line;
+ }
+ }
+
+ // 等待命令执行完成
+ boolean finished = process.waitFor(2, TimeUnit.MINUTES);
+ if (!finished) {
+ process.destroy();
+ throw new RuntimeException("MTR命令执行超时");
+ }
+
+ if (lastValidLine == null) {
+ throw new RuntimeException("MTR命令无有效输出");
+ }
+
+ // 解析丢包率
+ return parseLossPercentFromMtrOutput(lastValidLine);
+
+ } catch (Exception e) {
+ throw new RuntimeException("MTR命令执行异常: " + e.getMessage());
+ } finally {
+ if (process != null) {
+ process.destroy();
+ }
+ }
+ }
+
+ /**
+ * 从MTR输出中解析丢包率
+ */
+ private double parseLossPercentFromMtrOutput(String mtrOutputLine) {
+ try {
+ // 使用正则表达式匹配Loss%列
+ Pattern pattern = Pattern.compile("(\\d+\\.?\\d*)%");
+ Matcher matcher = pattern.matcher(mtrOutputLine);
+
+ if (matcher.find()) {
+ return Double.parseDouble(matcher.group(1));
+ }
+
+ throw new RuntimeException("未找到Loss%数据: " + mtrOutputLine);
+ } catch (Exception e) {
+ throw new RuntimeException("解析Loss%失败: " + e.getMessage());
+ }
+ }
+}
diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml
index 9efad49..596d0af 100644
--- a/src/main/resources/application-dev.yml
+++ b/src/main/resources/application-dev.yml
@@ -17,7 +17,7 @@ logging:
netty:
server:
host: 120.211.95.173
- port: 6620
+ port: 6610
client:
client-id: client-001
reconnect-interval: 5
diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml
index 288a043..9fa03c2 100644
--- a/src/main/resources/application.yml
+++ b/src/main/resources/application.yml
@@ -5,8 +5,8 @@ spring:
pathmatch:
matching-strategy: ant_path_matcher
application:
- name: tr-agent-client
- version: 1.1.4
+ name: tr-mtragent-client
+ version: 1.0.0
conf-path: /usr/local/tongran/conf
script-path: /usr/local/tongran/sbin
tmp-path: /usr/local/tongran/tmp