mtr监控策略初版

This commit is contained in:
gaoyutao
2025-11-19 18:42:49 +08:00
parent ae084bfa52
commit 7e69fddafd
11 changed files with 415 additions and 7 deletions
+2 -2
View File
@@ -9,9 +9,9 @@
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.tongran.agent</groupId>
<artifactId>tr-agent-client</artifactId>
<artifactId>tr-mtragent-client</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>tr-agent-client</name>
<name>tr-mtragent-client</name>
<description>tr-agent-client</description>
<properties>
@@ -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 = "";
/**
* 采集标识
*/
@@ -67,6 +67,8 @@ public enum MsgEnum {
Agent版本更新应答("AGENT_VERSION_UPDATE_RSP"),
MTR探测上报("MTR_DETECT"),
多网IP探测上报("NETWORK_DETECT");
private String value;
@@ -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;
}
@@ -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; // 错误信息
}
@@ -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<MtrPolicyConfigEO> 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<MtrVO> 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);
}
@@ -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<MtrVO> mtrList(long timestamp, List<MtrPolicyConfigEO> mtrPolicyConfigEOList);
}
@@ -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<MtrPolicyConfigEO> 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();
//检查外置目录是否存在
@@ -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<MtrVO> mtrList(long timestamp, List<MtrPolicyConfigEO> mtrPolicyConfigEOList) {
List<CompletableFuture<MtrVO>> futures = new ArrayList<>();
List<MtrVO> 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<MtrVO> future = CompletableFuture.supplyAsync(() ->
executeSingleMtrProbe(ip, policy, timestamp)
);
futures.add(future);
}
}
}
// 等待所有任务完成
CompletableFuture<Void> allFutures = CompletableFuture.allOf(
futures.toArray(new CompletableFuture[0])
);
// 获取所有结果(设置超时时间,比如2分钟)
allFutures.get(2, TimeUnit.MINUTES);
for (CompletableFuture<MtrVO> 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());
}
}
}
+1 -1
View File
@@ -17,7 +17,7 @@ logging:
netty:
server:
host: 120.211.95.173
port: 6620
port: 6610
client:
client-id: client-001
reconnect-interval: 5
+2 -2
View File
@@ -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