1、优化agent注册解析公网ip超时问题。

2、优化agent注册网卡上报逻辑。
This commit is contained in:
gaoyutao
2025-12-12 16:05:55 +08:00
parent 196961ff11
commit eebc2de645
2 changed files with 385 additions and 77 deletions
@@ -26,6 +26,11 @@ import java.util.stream.Collectors;
public class AgentUtil {
// 存储网关信息:接口名 -> 网关IP
private static Map<String, String> gatewayMap = new HashMap<>();
// 正则表达式匹配:当前 IPxxx.xxx.xxx.xxx 来自于:中国 江苏省 南京市 电信
private static final String REGEX = "来自于:(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)";
@Resource
private GlobalConfig globalConfig;
@@ -252,25 +257,62 @@ public class AgentUtil {
return swapped;
}
// 存储网关信息:接口名 -> 网关IP
private static Map<String, String> gatewayMap = new HashMap<>();
/**
* 检查有效网卡是否只有一个
* @return
*/
public static boolean hasOnlyOneValidNetworkInterface() {
try {
int validInterfaceCount = 0;
Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
// 正则表达式匹配:当前 IPxxx.xxx.xxx.xxx 来自于:中国 江苏省 南京市 电信
private static final String REGEX = "来自于:(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)";
while (interfaces.hasMoreElements()) {
NetworkInterface ni = interfaces.nextElement();
String name = ni.getName();
// 1. 跳过回环、虚拟、关闭的接口
if (ni.isLoopback() || ni.isVirtual() || !ni.isUp()) {
continue;
}
// 2. 判断是否为 Ethernet
if (!isEthernetInterface(name)) {
continue;
}
// 3. 检查物理链路状态
if (!isInterfaceConnected(name)) {
continue;
}
// 4. 检查是否有IPv4地址
String ipv4 = getIPv4Address(ni);
if ("N/A".equals(ipv4)) {
continue;
}
// 有效接口计数+1
validInterfaceCount++;
// 如果已经超过1个,直接返回false
if (validInterfaceCount > 1) {
return false;
}
}
// 只有一个有效接口
return validInterfaceCount == 1;
} catch (Exception e) {
AssertLog.error("判断有效网络接口数量失败:{}", e.getMessage());
return false;
}
}
/**
* 收集所有 Ethernet 类型网卡信息
*/
public static List<NetworkInterfaceInfo> collectNetworkInfo() throws Exception {
List<NetworkInterfaceInfo> result = new ArrayList<>();
// String publicIp = getPublicIp(); // 获取公网 IP(全局出口)
// String ipInfo = PublicIpFetcher.getPublicIp();
// String publicIp = PublicIpFetcher.extractIp(ipInfo);
// if (publicIp != null) {
// System.out.println("公网 IP: " + publicIp);
// } else {
// System.out.println("未能提取 IP 地址");
// }
Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
while (interfaces.hasMoreElements()) {
@@ -293,27 +335,35 @@ public class AgentUtil {
// 检查IP4信息
if (ipv4.equals("N/A")) continue;
String gateway = getGatewayAddress();
String gateway = null;
if(hasOnlyOneValidNetworkInterface()){
gateway = getGatewayAddress();
}else{
gateway = getGateway(name, "www.baidu.com");
}
AssertLog.info("ipv4={},网关={}",ipv4,gateway);
// 检查IP4信息
if (gateway.equals("N/A")) continue;
if ("N/A".equals(gateway)) continue;
// 添加临时路由 过滤没有ipv4的接口
boolean flag = addTempRoute(ipv4, name, gateway);
if (!flag) continue;
// boolean flag = addTempRoute(ipv4, name, gateway);
// if (!flag) continue;
String ipInfo = PublicIpFetcher.getPublicIp();
String publicIp = PublicIpFetcher.extractIp(ipInfo);
if (publicIp != null) {
System.out.println("公网 IP: " + publicIp);
} else {
System.out.println("未能提取 IP 地址");
String publicIp = null;
String ipInfo = null;
if(gateway != null){
ipInfo = PublicIpFetcher.getPublicIp();
publicIp = PublicIpFetcher.extractIp(ipInfo);
if (publicIp != null) {
AssertLog.info("公网 IP: {}", publicIp);
} else {
AssertLog.info("未能提取 IP 地址");
}
if (StringUtils.isBlank(publicIp)) continue;
}
if (StringUtils.isBlank(publicIp)) continue;
// 删除临时路由
delTempRoute(ipv4, name, gateway);
// delTempRoute(ipv4, name, gateway);
NetworkInterfaceInfo info = NetworkInterfaceInfo.builder()
@@ -328,24 +378,29 @@ public class AgentUtil {
// 如果有公网 IP,查询归属地
if (publicIp != null && !publicIp.isEmpty()) {
Pattern pattern = Pattern.compile(REGEX);
Matcher matcher = pattern.matcher(ipInfo);
if (matcher.find()) {
// 提取归属地信息
PublicIpFetcher.IpLocationInfo location = PublicIpFetcher.extractLocation(ipInfo);
info.setProvince(location.getProvince());
info.setCity(location.getCity());
info.setCarrier(location.getCarrier());
// 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);
}
// 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) {
@@ -491,6 +546,55 @@ public class AgentUtil {
}
return "N/A";
}
public static String getGateway(String interfaceName, String target) {
try {
Process process = Runtime.getRuntime().exec(
new String[]{"traceroute", "-i", interfaceName, "-m", "1", target}
);
process.waitFor();
BufferedReader reader = new BufferedReader(
new InputStreamReader(process.getInputStream())
);
String line;
while ((line = reader.readLine()) != null) {
if (line.trim().startsWith("1")) {
// 检查是否全是星号
if (line.contains("* * *")) {
AssertLog.info("traceroute 探测完成,网卡名称:{},无网关", interfaceName);
return null;
}
// 提取网关IP地址
// 示例行: "1 8.8.8.8 3.737 ms 3.940 ms 4.128 ms"
Pattern pattern = Pattern.compile("\\d+\\.\\d+\\.\\d+\\.\\d+");
Matcher matcher = pattern.matcher(line);
if (matcher.find()) {
String gatewayIp = matcher.group(0);
AssertLog.info("traceroute 探测完成,网卡名称:{},网关IP:{}", interfaceName, gatewayIp);
return gatewayIp;
}
// 如果没有匹配到IP格式,尝试查找主机名
// 行格式可能是: "1 gateway.local (192.168.1.1) 0.5 ms"
String[] parts = line.trim().split("\\s+");
if (parts.length >= 2 && !parts[1].equals("*")) {
String gateway = parts[1];
AssertLog.info("traceroute 探测完成,网卡名称:{},网关:{}", interfaceName, gateway);
return gateway;
}
}
}
AssertLog.info("traceroute 探测完成,网卡名称:{},未找到网关信息", interfaceName);
return null;
} catch (Exception e) {
AssertLog.info("traceroute 探测失败,网卡名称:{},详情:{}", interfaceName, e.getMessage());
return null;
}
}
// 检查接口是否“已连接”(物理链路状态)
private static boolean isInterfaceConnected(String interfaceName) {
@@ -4,37 +4,141 @@ import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.InetAddress;
import java.net.URL;
import java.net.UnknownHostException;
import java.util.concurrent.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class PublicIpFetcher {
private static final String IP_SERVICE_URL = "https://myip.ipip.net";
private static final int TIMEOUT_MS = 10_000; // 10秒超时
// 主备IP查询服务
private static final String PRIMARY_IP_SERVICE = "https://myip.ipip.net";
private static final String FALLBACK_IP_SERVICE = "http://cip.cc";
private static final int TIMEOUT_MS = 8_000; // 8秒超时
private static final int CURL_TIMEOUT_MS = 5_000; // curl命令执行超时
// 正则表达式匹配
private static final String IPV4_PATTERN = "(\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3})";
private static final Pattern IP_PATTERN = Pattern.compile(IPV4_PATTERN);
private static final Pattern CIPCC_PATTERN = Pattern.compile(
"IP\\s*:\\s*" + IPV4_PATTERN + "\\s*地址\\s*:\\s*([^\\s]+)\\s+([^\\s]+)\\s+([^\\s]+)\\s*运营商\\s*:\\s*([^\\r\\n]+)"
);
private static final Pattern IPIP_PATTERN = Pattern.compile(
"当前 IP\\s*" + IPV4_PATTERN + "\\s*来自于:([^\\s]+)\\s+([^\\s]+)\\s+([^\\s]+)\\s+([^\\s]+)"
);
/**
* 获取公网 IP(包含归属地信息
*
* @return IP 信息字符串,如 "当前 IP112.96.15.145,来自于:中国 广东省 深圳市 联通"
* @throws IOException 如果网络请求失败
* 获取公网 IP 信息(带降级策略
* 1. 首先尝试 myip.ipip.net
* 2. 如果超时或失败,执行 curl cip.cc
* 3. 如果都失败,返回本地IP
*/
public static String getPublicIp() throws IOException {
URL url = new URL(IP_SERVICE_URL);
public static String getPublicIp() {
// 方法1: 尝试主服务
String ipInfo = tryPrimaryService();
if (ipInfo != null && !ipInfo.isEmpty()) {
return ipInfo;
}
// 方法2: 尝试备用服务
ipInfo = tryFallbackService();
if (ipInfo != null && !ipInfo.isEmpty()) {
return ipInfo;
}
// 方法3: 返回本地IP
return getLocalIpInfo();
}
/**
* 尝试主服务 (myip.ipip.net)
*/
private static String tryPrimaryService() {
ExecutorService executor = Executors.newSingleThreadExecutor();
Future<String> future = executor.submit(() -> {
try {
AssertLog.info("myip.ipip.net采集IP信息成功");
return fetchFromUrl(PRIMARY_IP_SERVICE, "UTF-8");
} catch (Exception e) {
AssertLog.error("主服务请求失败: {}", e.getMessage());
return null;
}
});
try {
return future.get(TIMEOUT_MS, TimeUnit.MILLISECONDS);
} catch (TimeoutException e) {
AssertLog.error("主服务请求超时");
future.cancel(true);
} catch (Exception e) {
AssertLog.error("主服务执行异常: {}", e.getMessage());
} finally {
executor.shutdownNow();
}
return null;
}
/**
* 尝试备用服务 (curl cip.cc)
*/
private static String tryFallbackService() {
try {
Process process = new ProcessBuilder("curl", "-s", "--max-time", "5", FALLBACK_IP_SERVICE)
.redirectErrorStream(true)
.start();
// 等待进程完成
boolean finished = process.waitFor(CURL_TIMEOUT_MS, TimeUnit.MILLISECONDS);
if (finished && process.exitValue() == 0) {
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(process.getInputStream()))) {
StringBuilder result = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
result.append(line).append("\n");
}
AssertLog.info("curl cip.cc采集IP信息成功");
return parseCipCcResult(result.toString());
}
} else {
if (!finished) {
process.destroyForcibly();
}
AssertLog.error("curl命令执行失败");
}
} catch (IOException e) {
AssertLog.error("执行curl命令IO异常: {}", e.getMessage());
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
AssertLog.error("curl命令被中断");
} catch (Exception e) {
AssertLog.error("备用服务异常: {}", e.getMessage());
}
return null;
}
/**
* 从HTTP URL获取IP信息
*/
private static String fetchFromUrl(String urlString, String charset) throws IOException {
URL url = new URL(urlString);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
// 设置请求方法
connection.setRequestMethod("GET");
connection.setConnectTimeout(TIMEOUT_MS);
connection.setReadTimeout(TIMEOUT_MS);
// 发起请求
int responseCode = connection.getResponseCode();
if (responseCode != 200) {
throw new IOException("HTTP " + responseCode + " from " + IP_SERVICE_URL);
throw new IOException("HTTP " + responseCode + " from " + urlString);
}
// 读取响应(注意:myip.ipip.net 返回的是 GBK 编码)
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(connection.getInputStream(), "UTF-8"))) { // 使用 GBK 解码
new InputStreamReader(connection.getInputStream(), charset))) {
StringBuilder response = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
@@ -45,42 +149,142 @@ public class PublicIpFetcher {
}
/**
* 从返回文本中提取纯 IP 地址(如 112.96.15.145
*
* @param ipInfo 来自 getPublicIp() 的完整信息
* @return 纯 IP 字符串,提取失败返回 null
* 解析cip.cc的结果
*/
private static String parseCipCcResult(String result) {
if (result == null || result.isEmpty()) {
return null;
}
Matcher matcher = CIPCC_PATTERN.matcher(result);
if (matcher.find()) {
String ip = matcher.group(1);
String country = matcher.group(2);
String province = matcher.group(3);
String city = matcher.group(4);
String carrier = matcher.group(5);
// 构建类似ipip.net的格式
return String.format("当前 IP%s 来自于:%s %s %s %s",
ip, country, province, city, carrier);
}
return result; // 返回原始结果
}
/**
* 获取本地IP信息
*/
private static String getLocalIpInfo() {
try {
String localIp = InetAddress.getLocalHost().getHostAddress();
AssertLog.info("当前 IP{} 来自于:本地网络", localIp);
return localIp;
} catch (UnknownHostException e) {
AssertLog.error("当前IP获取失败:{}", e.getMessage());
return null;
}
}
/**
* 从IP信息中提取IP地址
*/
public static String extractIp(String ipInfo) {
if (ipInfo == null || ipInfo.isEmpty()) return null;
// 匹配 IP 地址的正则
java.util.regex.Pattern pattern = java.util.regex.Pattern.compile("(\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3})");
java.util.regex.Matcher matcher = pattern.matcher(ipInfo);
Matcher matcher = IP_PATTERN.matcher(ipInfo);
return matcher.find() ? matcher.group(1) : null;
}
// === 使用示例 ===
/**
* 从IP信息中提取归属地信息
*/
public static IpLocationInfo extractLocation(String ipInfo) {
IpLocationInfo info = new IpLocationInfo();
if (ipInfo == null || ipInfo.isEmpty()) {
return info;
}
// 尝试匹配ipip.net格式
Matcher ipipMatcher = IPIP_PATTERN.matcher(ipInfo);
if (ipipMatcher.find()) {
info.ip = ipipMatcher.group(1);
info.country = ipipMatcher.group(2);
info.province = ipipMatcher.group(3);
info.city = ipipMatcher.group(4);
info.carrier = ipipMatcher.group(5);
return info;
}
// 尝试匹配cip.cc格式
Matcher cipMatcher = CIPCC_PATTERN.matcher(ipInfo);
if (cipMatcher.find()) {
info.ip = cipMatcher.group(1);
info.country = cipMatcher.group(2);
info.province = cipMatcher.group(3);
info.city = cipMatcher.group(4);
info.carrier = cipMatcher.group(5);
return info;
}
// 如果都不匹配,只提取IP
String ip = extractIp(ipInfo);
if (ip != null) {
info.ip = ip;
}
return info;
}
/**
* 完整的归属地信息
*/
public static class IpLocationInfo {
public String ip;
public String country;
public String province;
public String city;
public String carrier;
@Override
public String toString() {
return String.format("IP: %s, 国家: %s, 省份: %s, 城市: %s, 运营商: %s",
ip, country, province, city, carrier);
}
public String getProvince() {
return province != null ? province.replaceAll("[。\\s]", "") : "";
}
public String getCity() {
return city != null ? city.replaceAll("[。\\s]", "") : "";
}
public String getCarrier() {
return carrier != null ? carrier.replaceAll("[。\\s]", "") : "";
}
}
/**
* 使用示例
*/
public static void main(String[] args) {
try {
String ipInfo = getPublicIp();
System.out.println("完整信息: " + ipInfo);
System.out.println("开始获取公网IP信息...");
String pureIp = extractIp(ipInfo);
if (pureIp != null) {
System.out.println("公网 IP: " + pureIp);
} else {
System.out.println("未能提取 IP 地址");
}
String province = "";
String city = "";
String carrier = "";
String ipInfo = getPublicIp();
System.out.println("完整信息: " + ipInfo);
System.out.println(ipInfo.substring(ipInfo.indexOf("来自于:")+4,ipInfo.length()));
String pureIp = extractIp(ipInfo);
System.out.println("公网 IP: " + (pureIp != null ? pureIp : "获取失败"));
IpLocationInfo location = extractLocation(ipInfo);
System.out.println(location);
} catch (IOException e) {
System.err.println("获取公网 IP 失败: " + e.getMessage());
e.printStackTrace();
// 提取需要的字段
if (location.ip != null) {
System.out.println("省份: " + location.getProvince());
System.out.println("城市: " + location.getCity());
System.out.println("运营商: " + location.getCarrier());
}
}
}