From 6dbee99194b837f68c2fbb90e24cf2ea3593d2c1 Mon Sep 17 00:00:00 2001 From: gaoyutao Date: Tue, 23 Dec 2025 18:56:36 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0frpc=E4=BF=9D=E6=B4=BB?= =?UTF-8?q?=E3=80=81=E7=8A=B6=E6=80=81=E4=B8=8A=E6=8A=A5=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../scheduler/service/AppInitializer.java | 5 +- .../scheduler/service/BusinessTasks.java | 36 ++++ .../agent/client/service/AgentService.java | 4 + .../client/service/impl/AgentServiceImpl.java | 169 ++++++++++++++++-- .../client/utils/NetworkInterfaceUtil.java | 25 ++- 5 files changed, 207 insertions(+), 32 deletions(-) diff --git a/src/main/java/com/tongran/agent/client/scheduler/service/AppInitializer.java b/src/main/java/com/tongran/agent/client/scheduler/service/AppInitializer.java index 0de2717..adb77fd 100644 --- a/src/main/java/com/tongran/agent/client/scheduler/service/AppInitializer.java +++ b/src/main/java/com/tongran/agent/client/scheduler/service/AppInitializer.java @@ -100,7 +100,10 @@ public class AppInitializer implements CommandLineRunner { dynamicTaskService.scheduleTask("checkFirewall", businessTasks::checkFirewallTask, 15000, 600000); AssertLog.info("检测agent更新配置"); agentService.checkAgentUpdate(); - + AssertLog.info("启动frpc保活定时任务 - 延迟: {}ms, 间隔: {}ms", 15000, 600000); + dynamicTaskService.scheduleTask("checkFrpc", businessTasks::checkFrpcTask, 15000, 600000); + AssertLog.info("启动frpc状态上报定时任务 - 延迟: {}ms, 间隔: {}ms", 15000, 300000); + dynamicTaskService.scheduleTask("upFrpcMsg", businessTasks::upFrpcMsgTask, 15000, 300000); }else{ //未注册,发送注册 try { 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 22ea99b..86421a7 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 @@ -61,6 +61,8 @@ public class BusinessTasks { private final AtomicInteger connectionTask = new AtomicInteger(0); private final AtomicInteger networkDetectTask = new AtomicInteger(0); private final AtomicInteger checkFirewallTask = new AtomicInteger(0); + private final AtomicInteger checkFrpcTask = new AtomicInteger(0); + private final AtomicInteger upFrpcMsgTask = new AtomicInteger(0); @@ -856,6 +858,10 @@ public class BusinessTasks { dynamicTaskService.scheduleTask("checkFirewall", businessTasks::checkFirewallTask, 15000, 600000); AssertLog.info("检测agent更新配置"); agentService.checkAgentUpdate(); + AssertLog.info("启动frpc保活定时任务 - 延迟: {}ms, 间隔: {}ms", 15000, 600000); + dynamicTaskService.scheduleTask("checkFrpc", businessTasks::checkFrpcTask, 15000, 600000); + AssertLog.info("启动frpc状态上报定时任务 - 延迟: {}ms, 间隔: {}ms", 15000, 300000); + dynamicTaskService.scheduleTask("upFrpcMsg", businessTasks::upFrpcMsgTask, 15000, 300000); }else{ //未注册,发送注册 try { @@ -933,6 +939,36 @@ public class BusinessTasks { AssertLog.info("断开连接--监测防火墙策略定时任务执行 - task #{} completed", count); } } + /** + * 任务31:frpc保活机制 + */ + @Async("taskExecutor") + public void checkFrpcTask() { + int count = checkFrpcTask.incrementAndGet(); + AssertLog.info("frpc保活机制定时任务执行 - 时间: {},task #{}", LocalDateTime.now(), count); + // 判定客户端与服务端是否连接 + if (Objects.nonNull(sessionManager.getSessionById(GlobalConfig.CLIENT_ID))) { + // frpc保活机制 + agentService.checkFrpc(); + }else{ + AssertLog.info("断开连接--frpc保活机制定时任务执行 - task #{} completed", count); + } + } + /** + * 任务32:frpc状态上报 + */ + @Async("taskExecutor") + public void upFrpcMsgTask() { + int count = upFrpcMsgTask.incrementAndGet(); + AssertLog.info("frpc状态上报定时任务执行 - 时间: {},task #{}", LocalDateTime.now(), count); + // 判定客户端与服务端是否连接 + if (Objects.nonNull(sessionManager.getSessionById(GlobalConfig.CLIENT_ID))) { + // frpc状态上报 + agentService.upFrpcMsg(); + }else{ + AssertLog.info("断开连接--frpc状态上报定时任务执行 - task #{} completed", count); + } + } diff --git a/src/main/java/com/tongran/agent/client/service/AgentService.java b/src/main/java/com/tongran/agent/client/service/AgentService.java index 0c40dfe..9c62085 100644 --- a/src/main/java/com/tongran/agent/client/service/AgentService.java +++ b/src/main/java/com/tongran/agent/client/service/AgentService.java @@ -38,4 +38,8 @@ public interface AgentService { void checkAndAddFirewallPeriodically(); void checkAgentUpdate(); + + void checkFrpc(); + + void upFrpcMsg(); } 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 4305eaf..a18d411 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 @@ -315,6 +315,10 @@ public class AgentServiceImpl implements AgentService { dynamicTaskService.scheduleTask("checkFirewall", businessTasks::checkFirewallTask, 15000, 600000); AssertLog.info("检测agent更新配置"); checkAgentUpdate(); + AssertLog.info("启动frpc保活定时任务 - 延迟: {}ms, 间隔: {}ms", 15000, 600000); + dynamicTaskService.scheduleTask("checkFrpc", businessTasks::checkFrpcTask, 15000, 600000); + AssertLog.info("启动frpc状态上报定时任务 - 延迟: {}ms, 间隔: {}ms", 15000, 300000); + dynamicTaskService.scheduleTask("upFrpcMsg", businessTasks::upFrpcMsgTask, 15000, 300000); } @Override @@ -904,27 +908,54 @@ public class AgentServiceImpl implements AgentService { String resMsg = "frpc.toml修改成功"; try { - // 停止进程 - Runtime.getRuntime().exec("pkill -f frpc").waitFor(); + // 停止 + ProcessBuilder killPb = new ProcessBuilder( + "bash", "-c", + "pkill -f 'frpc.*toml'" + ); + killPb.start().waitFor(); + Thread.sleep(1000); - // 启动进程 - String[] cmd = {frpPath + "/frpc", "-c", frpPath + "/frpc.toml"}; - new ProcessBuilder(cmd) - .directory(new File(frpPath)) - .redirectErrorStream(true) - .start(); + // 启动 - 使用完整的shell命令 + String startCommand = String.format( + "nohup %s/frpc -c %s/frpc.toml > %s/frpc.log 2>&1 &", + properties.getFrpPath(), + properties.getFrpPath(), + properties.getFrpPath() + ); + + ProcessBuilder startPb = new ProcessBuilder( + "bash", "-c", + startCommand + ); + startPb.start(); + + // 等待一小段时间确保进程启动 + Thread.sleep(2000); } catch (Exception e) { resCode = 0; resMsg = "操作失败: " + e.getMessage(); } + // 上报状态 + reportFrpcStatus(resCode, resMsg, frpPath); + } + /** + * 上报FRPC状态 + * @param resCode 响应码 + * @param resMsg 响应消息 + * @param frpPath frp路径 + */ + private void reportFrpcStatus(int resCode, String resMsg, String frpPath) { + long timestamp = System.currentTimeMillis() / 1000; + JSONObject json = new JSONObject(); // 基本响应 json.put("resCode", resCode); json.put("resMsg", resMsg); json.put("timestamp", timestamp); - json.put("isRunning", isFrpcRunning()?"1":"0"); + json.put("isRunning", isFrpcRunning() ? "1" : "0"); // 从配置文件获取所有配置 Map config = readFrpcConfig(frpPath + "/frpc.toml"); @@ -947,7 +978,6 @@ public class AgentServiceImpl implements AgentService { AssertLog.info("发送FRP配置文件更新应答={}", JSON.toJSONString(message)); } } - /** * 从frpc.toml配置文件读取配置 */ @@ -999,12 +1029,18 @@ public class AgentServiceImpl implements AgentService { /** * 检查frpc进程是否在运行 */ - private boolean isFrpcRunning() { + public boolean isFrpcRunning() { try { - Process process = Runtime.getRuntime().exec(new String[]{"ps", "-ef"}); - String output = new String(process.getInputStream().readAllBytes()); - return output.contains("frpc") && output.contains("-c"); - } catch (IOException e) { + ProcessBuilder pb = new ProcessBuilder( + "bash", "-c", "pgrep -f 'frpc.*toml'" + ); + + Process process = pb.start(); + int exitCode = process.waitFor(); + AssertLog.info("是否运行:{}",exitCode == 0); + return exitCode == 0; + + } catch (Exception e) { return false; } } @@ -1048,7 +1084,7 @@ public class AgentServiceImpl implements AgentService { // 写入配置文件 Files.write(file.toPath(), newLines, StandardCharsets.UTF_8); - + AssertLog.info("frpc.toml配置文件修改成功"); } catch (IOException e) { throw new RuntimeException("修改frpc配置失败: " + e.getMessage(), e); } @@ -1060,6 +1096,7 @@ public class AgentServiceImpl implements AgentService { try (ServerSocket serverSocket = new ServerSocket(port)) { return false; // 端口可用 } catch (IOException e) { + AssertLog.error("端口已被占用或其他异常:{}", e.getMessage()); return true; // 端口已被占用或其他异常 } } @@ -1923,4 +1960,104 @@ public class AgentServiceImpl implements AgentService { AssertLog.debug("agent更新配置文件不存在: {}", agentUpdateFile.getPath()); } } + + @Override + public void upFrpcMsg() { + int resCode = 3; + String resMsg = "循环上报frpc状态"; + String frpPath = properties.getFrpPath(); + reportFrpcStatus(resCode, resMsg, frpPath); + } + @Override + public void checkFrpc() { + if (hasConfig() && !isFrpcRunning()) { + AssertLog.info("检测到frpc未运行,尝试启动..."); + startFrpc(); + } + } + private boolean hasConfig() { + try { + String frpPath = properties.getFrpPath(); + if (StringUtils.isBlank(frpPath)) { + return false; + } + + File configFile = new File(frpPath + "/frpc.toml"); + if (!configFile.exists() || !configFile.isFile()) { + return false; + } + + List lines = Files.readAllLines(configFile.toPath()); + for (String line : lines) { + // 移除行内注释 + int commentIndex = line.indexOf('#'); + if (commentIndex != -1) { + line = line.substring(0, commentIndex); + } + + line = line.trim(); + if (line.isEmpty()) { + continue; + } + + // 检查是否包含 serverAddr + if (line.contains("serverAddr")) { + // 按等号分割 + String[] parts = line.split("=", 2); + if (parts.length == 2) { + String value = parts[1].trim(); + // 检查等号后面是否有值 + if (!value.isEmpty()) { + // 如果是带引号的值,检查引号内是否有内容 + if ((value.startsWith("\"") && value.endsWith("\"")) || + (value.startsWith("'") && value.endsWith("'"))) { + String innerValue = value.substring(1, value.length() - 1); + if (!innerValue.trim().isEmpty()) { + return true; + } + } else { + // 不带引号的值 + return true; + } + } + } + } + } + return false; + } catch (Exception e) { + return false; + } + } + public boolean startFrpc() { + try { + // 先停止可能残留的进程 + new ProcessBuilder("pkill", "-f", "frpc.*toml").start().waitFor(); + + // 构建启动命令 + String frpcPath = properties.getFrpPath() + "/frpc"; + String configPath = properties.getFrpPath() + "/frpc.toml"; + + File frpcFile = new File(frpcPath); + if (!frpcFile.exists() || !frpcFile.canExecute()) { + AssertLog.error("frpc可执行文件不存在或无执行权限: {}", frpcPath); + return false; + } + StringBuilder startCommandBuilder = new StringBuilder(); + startCommandBuilder.append("nohup ").append(frpcPath).append(" -c ").append(configPath); + + ProcessBuilder pb = new ProcessBuilder( + "bash", "-c", + startCommandBuilder.toString() + ); + pb.start(); + + // 等待2秒后检查是否启动成功 + Thread.sleep(2000); + return isFrpcRunning(); + + } catch (Exception e) { + AssertLog.error("启动frpc失败: {}", e.getMessage()); + return false; + } + } } diff --git a/src/main/java/com/tongran/agent/client/utils/NetworkInterfaceUtil.java b/src/main/java/com/tongran/agent/client/utils/NetworkInterfaceUtil.java index 09b7840..adfa407 100644 --- a/src/main/java/com/tongran/agent/client/utils/NetworkInterfaceUtil.java +++ b/src/main/java/com/tongran/agent/client/utils/NetworkInterfaceUtil.java @@ -5,10 +5,7 @@ import org.apache.commons.lang3.StringUtils; import java.io.BufferedReader; import java.io.InputStreamReader; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -262,21 +259,19 @@ public class NetworkInterfaceUtil { reader.close(); process.waitFor(); - // 补充父接口信息 - Map interfaceNames = new HashMap<>(); + // 第一步:收集所有实际存在的网卡名称 + Set existingInterfaceNames = new HashSet<>(); for (NetworkInterfaceDetails iface : interfaces) { - interfaceNames.put(iface.name, iface.name); + existingInterfaceNames.add(iface.name); } - // 确保所有父接口都存在 + // 第二步:检查并清理父网卡设置 for (NetworkInterfaceDetails iface : interfaces) { - if (iface.parent != null && !interfaceNames.containsKey(iface.parent)) { - // 如果父接口不存在,可能是类似 eth0.203 的情况 - if (iface.parent.contains(".")) { - String[] parts = iface.parent.split("\\."); - if (parts.length > 0) { - iface.parent = parts[0]; - } + if (iface.parent != null) { + // 如果父网卡不存在于实际网卡列表中,清空parent字段 + if (!existingInterfaceNames.contains(iface.parent)) { + iface.parent = null; + iface.realName = null; } } }