增加frpc保活、状态上报方法

This commit is contained in:
gaoyutao
2025-12-23 18:56:36 +08:00
parent ed801a05c9
commit 6dbee99194
5 changed files with 207 additions and 32 deletions
@@ -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 {
@@ -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);
}
}
/**
* 任务31frpc保活机制
*/
@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);
}
}
/**
* 任务32frpc状态上报
*/
@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);
}
}
@@ -38,4 +38,8 @@ public interface AgentService {
void checkAndAddFirewallPeriodically();
void checkAgentUpdate();
void checkFrpc();
void upFrpcMsg();
}
@@ -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<String, String> 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<String> 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;
}
}
}
@@ -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<String, String> interfaceNames = new HashMap<>();
// 第一步:收集所有实际存在的网卡名称
Set<String> 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;
}
}
}