采集网口信息增加 协商速度、工作模式

增加客户端处理服务端下发-文件下载
增加客户端处理服务端下发-执行命令
This commit is contained in:
qiminbao
2025-08-29 11:21:07 +08:00
parent 25572f7804
commit dfda888d9a
10 changed files with 895 additions and 57 deletions
@@ -42,7 +42,15 @@ public enum MsgEnum {
更新系统采集间隔("TIME_SYSTEM"),
更新系统采集间隔应答("TIME_SYSTEM_RSP");
更新系统采集间隔应答("TIME_SYSTEM_RSP"),
文件下载("DOWN_FILE"),
文件下载应答("DOWN_FILE_RSP"),
执行命令("EXECUTE_COMMAND"),
执行命令应答("EXECUTE_COMMAND_RSP");
private String value;
@@ -9,9 +9,9 @@ import oshi.hardware.HardwareAbstractionLayer;
import oshi.hardware.NetworkIF;
import oshi.util.FormatUtil;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.*;
import java.util.concurrent.TimeUnit;
@Service
@@ -42,6 +42,11 @@ public class NetServiceImpl implements NetService {
netVO.setStatus(net.isConnectorPresent() ? "已连接" : "未连接");//运行状态
netVO.setType(EscapeUtil.getInterfaceType(net));//接口类型
netVO.setIpV4(String.join(", ", net.getIPv4addr()));//IPv4
Map<String, String> map = getNetworkMode(net.getName());
if (map != null && !map.isEmpty()) {
netVO.setSpeed(map.get("speed"));
netVO.setDuplex(map.get("duplex"));
}
temp.add(netVO);
}
// 2. 实时带宽监控(需要两次采样)
@@ -159,4 +164,36 @@ public class NetServiceImpl implements NetService {
return bytes * 8.0 / 1_000_000; // bytes to megabits
}
public static Map<String, String> getNetworkMode(String interfaceName) {
Map<String, String> result = new HashMap<>();
ProcessBuilder pb = new ProcessBuilder("ethtool", interfaceName);
pb.redirectErrorStream(true);
try {
Process process = pb.start();
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line;
while ((line = reader.readLine()) != null) {
if (line.contains("Speed:")) {
result.put("speed", line.split(":")[1].trim());
} else if (line.contains("Duplex:")) {
result.put("duplex", line.split(":")[1].trim());
} else if (line.contains("Auto-negotiation:")) {
result.put("auto-negotiation", line.split(":")[1].trim());
}
}
int exitCode = process.waitFor();
if (exitCode != 0) {
result.put("error", "ethtool command failed with exit code " + exitCode);
}
} catch (Exception e) {
result.put("error", e.getMessage());
}
return result;
}
}
@@ -43,8 +43,11 @@ public class NetVO implements Serializable {
@Schema(description = "接收流量")
private long inSpeed;
// @Schema(description = "接收速度")
// private String speed;
@Schema(description = "协商速度")
private String speed;
@Schema(description = "工作模式")
private String duplex;
@Schema(description = "时间戳")
private long timestamp;
@@ -0,0 +1,121 @@
package com.tongran.agentserver.server.file;
import java.io.BufferedInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.function.Consumer;
public class AdvancedAsyncDownloader {
private static final ExecutorService downloadExecutor =
Executors.newFixedThreadPool(5);
/**
* 带进度回调的异步下载
*/
public static CompletableFuture<String> downloadWithProgress(
String fileUrl,
String saveDir,
String fileName,
Consumer<Double> progressCallback) {
return CompletableFuture.supplyAsync(() -> {
try {
URL url = new URL(fileUrl);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
// 获取文件大小
long fileSize = connection.getContentLengthLong();
// 创建保存目录
java.nio.file.Path directory = java.nio.file.Paths.get(saveDir);
if (!java.nio.file.Files.exists(directory)) {
java.nio.file.Files.createDirectories(directory);
}
String filePath = directory.resolve(fileName != null ? fileName :
extractFileNameFromUrl(fileUrl)).toString();
try (BufferedInputStream in = new BufferedInputStream(connection.getInputStream());
FileOutputStream out = new FileOutputStream(filePath)) {
byte[] buffer = new byte[8192];
int bytesRead;
long totalRead = 0;
while ((bytesRead = in.read(buffer)) != -1) {
out.write(buffer, 0, bytesRead);
totalRead += bytesRead;
// 回调进度
if (progressCallback != null && fileSize > 0) {
double progress = (double) totalRead / fileSize * 100;
progressCallback.accept(progress);
}
}
}
return filePath;
} catch (Exception e) {
throw new RuntimeException("下载失败", e);
}
}, downloadExecutor);
}
private static String extractFileNameFromUrl(String fileUrl) {
// 实现文件名提取逻辑
return fileUrl.substring(fileUrl.lastIndexOf('/') + 1);
}
/**
* 检查目录是否存在,不存在则创建(不创建父目录)
* @param directoryPath 目录路径
* @return 创建成功返回true,否则返回false
*/
public static boolean createSingleDirectoryIfNotExists(String directoryPath) {
try {
Path path = Paths.get(directoryPath);
if (!Files.exists(path)) {
// 只创建单级目录(父目录必须存在)
Files.createDirectory(path);
System.out.println("目录创建成功: " + directoryPath);
return true;
} else {
System.out.println("目录已存在: " + directoryPath);
return true;
}
} catch (IOException e) {
System.err.println("创建目录失败: " + directoryPath);
System.err.println("错误信息: " + e.getMessage());
return false;
}
}
/**
* 使用示例
*/
public static void main(String[] args) {
String fileUrl = "https://www.tzdsp.net/ksc-andromedae";
String saveDir = "D:/down";
downloadWithProgress(fileUrl, saveDir, null, progress -> {
System.out.printf("下载进度: %.1f%%\n", progress);
}).thenAccept(filePath -> {
System.out.println("下载完成: " + filePath);
}).exceptionally(ex -> {
System.err.println("下载错误: " + ex.getMessage());
return null;
});
// 主线程继续执行
System.out.println("异步下载已启动...");
}
}
@@ -0,0 +1,270 @@
package com.tongran.agentserver.server.file;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.PosixFilePermission;
import java.util.List;
import java.util.Set;
import java.util.concurrent.*;
import java.util.function.Consumer;
public class AsyncCommandExecutor {
// 使用线程池管理异步任务
private static final ExecutorService executorService = Executors.newCachedThreadPool();
private static final ScheduledExecutorService timeoutExecutor = Executors.newScheduledThreadPool(5);
/**
* 异步为文件添加可执行权限
*/
public static CompletableFuture<Boolean> makeFileExecutableAsync(String filePath) {
return CompletableFuture.supplyAsync(() -> {
try {
Path path = Paths.get(filePath);
if (!Files.exists(path)) {
throw new RuntimeException("文件不存在: " + filePath);
}
Set<PosixFilePermission> permissions = Files.getPosixFilePermissions(path);
permissions.add(PosixFilePermission.OWNER_EXECUTE);
permissions.add(PosixFilePermission.GROUP_EXECUTE);
permissions.add(PosixFilePermission.OTHERS_EXECUTE);
Files.setPosixFilePermissions(path, permissions);
return true;
} catch (Exception e) {
throw new RuntimeException("设置执行权限失败: " + e.getMessage(), e);
}
}, executorService);
}
/**
* 异步执行命令(基础版本)
*/
public static CompletableFuture<CommandResult> executeCommandAsync(
List<String> command,
long timeout,
TimeUnit timeUnit) {
return CompletableFuture.supplyAsync(() -> {
Process process = null;
try {
ProcessBuilder processBuilder = new ProcessBuilder(command);
processBuilder.redirectErrorStream(true);
// processBuilder.directory(new File("/data/agent-server")); // 设置工作目录
// 不合并错误流,分别读取
process = processBuilder.start();
// 异步读取输出
Process finalProcess = process;
Future<String> outputFuture = executorService.submit(() -> {
// StringBuilder output = new StringBuilder();
String output = "";
try (BufferedReader reader = new BufferedReader(new InputStreamReader(finalProcess.getInputStream()))) {
String line;
while ((line = reader.readLine()) != null) {
// output.append(line).append("\n");
output = line;
}
}
return output;
});
// 设置超时
if (timeout > 0) {
boolean finished = process.waitFor(timeout, timeUnit);
if (!finished) {
process.destroy();
throw new TimeoutException("命令执行超时");
}
} else {
process.waitFor();
}
String output = outputFuture.get(1, TimeUnit.SECONDS);
int exitCode = process.exitValue();
return new CommandResult(exitCode, output, "");
} catch (Exception e) {
return new CommandResult(-1, "", "执行失败: " + e.getMessage());
} finally {
if (process != null) {
process.destroy();
}
}
}, executorService);
}
/**
* 带实时输出的异步命令执行
*/
public static CompletableFuture<CommandResult> executeCommandWithRealtimeOutput(
String[] command,
long timeout,
TimeUnit timeUnit,
Consumer<String> outputConsumer,
Consumer<String> errorConsumer) {
return CompletableFuture.supplyAsync(() -> {
Process process = null;
try {
ProcessBuilder processBuilder = new ProcessBuilder(command);
process = processBuilder.start();
// 启动输出读取线程
CompletableFuture<Void> outputFuture = readStreamAsync(
process.getInputStream(), outputConsumer, "OUTPUT");
CompletableFuture<Void> errorFuture = readStreamAsync(
process.getErrorStream(), errorConsumer, "ERROR");
// 设置超时监控
ScheduledFuture<?> timeoutFuture = null;
if (timeout > 0) {
Process finalProcess = process;
timeoutFuture = timeoutExecutor.schedule(() -> {
if (finalProcess.isAlive()) {
finalProcess.destroy();
}
}, timeout, timeUnit);
}
// 等待进程完成
int exitCode = process.waitFor();
// 取消超时任务
if (timeoutFuture != null) {
timeoutFuture.cancel(false);
}
// 等待输出读取完成
CompletableFuture.allOf(outputFuture, errorFuture).get(2, TimeUnit.SECONDS);
return new CommandResult(exitCode, "", "");
} catch (Exception e) {
return new CommandResult(-1, "", "执行异常: " + e.getMessage());
} finally {
if (process != null) {
process.destroy();
}
}
}, executorService);
}
/**
* 异步读取流数据
*/
private static CompletableFuture<Void> readStreamAsync(
InputStream inputStream,
Consumer<String> lineConsumer,
String streamType) {
return CompletableFuture.runAsync(() -> {
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(inputStream))) {
String line;
while ((line = reader.readLine()) != null) {
if (lineConsumer != null) {
lineConsumer.accept("[" + streamType + "] " + line);
}
}
} catch (IOException e) {
if (lineConsumer != null) {
lineConsumer.accept("[" + streamType + "-ERROR] " + e.getMessage());
}
}
}, executorService);
}
/**
* 异步执行脚本文件
*/
public static CompletableFuture<CommandResult> executeScriptAsync(
String scriptPath,
String[] args,
long timeout,
TimeUnit timeUnit,
Consumer<String> realtimeOutputConsumer) {
return makeFileExecutableAsync(scriptPath)
.thenCompose(permissionSuccess -> {
if (!permissionSuccess) {
return CompletableFuture.completedFuture(
new CommandResult(-1, "", "设置执行权限失败"));
}
String[] command = new String[args.length + 1];
command[0] = scriptPath;
System.arraycopy(args, 0, command, 1, args.length);
return executeCommandWithRealtimeOutput(
command, timeout, timeUnit,
realtimeOutputConsumer,
error -> System.err.println(error));
});
}
/**
* 批量异步执行命令
*/
// public static List<CompletableFuture<CommandResult>> executeCommandsAsync(
// List<String[]> commands,
// long timeout,
// TimeUnit timeUnit) {
//
// return commands.stream()
// .map(command -> executeCommandAsync(command, timeout, timeUnit))
// .collect(java.util.stream.Collectors.toList());
// }
/**
* 关闭执行器
*/
public static void shutdown() {
executorService.shutdown();
timeoutExecutor.shutdown();
try {
if (!executorService.awaitTermination(5, TimeUnit.SECONDS)) {
executorService.shutdownNow();
}
if (!timeoutExecutor.awaitTermination(2, TimeUnit.SECONDS)) {
timeoutExecutor.shutdownNow();
}
} catch (InterruptedException e) {
executorService.shutdownNow();
timeoutExecutor.shutdownNow();
Thread.currentThread().interrupt();
}
}
/**
* 执行结果封装类
*/
public static class CommandResult {
private final int exitCode;
private final String output;
private final String error;
public CommandResult(int exitCode, String output, String error) {
this.exitCode = exitCode;
this.output = output;
this.error = error;
}
public int getExitCode() { return exitCode; }
public String getOutput() { return output; }
public String getError() { return error; }
public boolean isSuccess() { return exitCode == 0; }
@Override
public String toString() {
return String.format("Exit Code: %d\nOutput: %s\nError: %s",
exitCode, output, error);
}
}
}
@@ -0,0 +1,175 @@
package com.tongran.agentserver.server.file;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.PosixFilePermission;
import java.util.Set;
import java.util.concurrent.TimeUnit;
public class LinuxCommandExecutor {
/**
* 为文件添加可执行权限
* @param filePath 文件路径
* @return 成功返回true,失败返回false
*/
public static boolean makeFileExecutable(String filePath) {
try {
Path path = Paths.get(filePath);
// 检查文件是否存在
if (!Files.exists(path)) {
System.err.println("文件不存在: " + filePath);
return false;
}
// 获取当前权限
Set<PosixFilePermission> permissions = Files.getPosixFilePermissions(path);
// 添加执行权限
permissions.add(PosixFilePermission.OWNER_EXECUTE);
permissions.add(PosixFilePermission.GROUP_EXECUTE);
permissions.add(PosixFilePermission.OTHERS_EXECUTE);
// 设置新权限
Files.setPosixFilePermissions(path, permissions);
System.out.println("文件已赋予可执行权限: " + filePath);
return true;
} catch (IOException e) {
System.err.println("设置执行权限失败: " + e.getMessage());
return false;
} catch (UnsupportedOperationException e) {
System.err.println("当前系统不支持POSIX权限: " + e.getMessage());
return false;
}
}
/**
* 使用chmod命令赋予可执行权限(备用方法)
*/
public static boolean makeFileExecutableWithChmod(String filePath) {
try {
Process process = Runtime.getRuntime().exec(new String[]{
"chmod", "+x", filePath
});
int exitCode = process.waitFor();
return exitCode == 0;
} catch (IOException | InterruptedException e) {
System.err.println("chmod命令执行失败: " + e.getMessage());
return false;
}
}
/**
* 执行命令并获取结果
* @param command 要执行的命令数组
* @param timeout 超时时间(秒),0表示无超时
* @return 执行结果对象
*/
public static CommandResult executeCommand(String[] command, int timeout) {
Process process = null;
try {
// 创建进程
ProcessBuilder processBuilder = new ProcessBuilder(command);
processBuilder.redirectErrorStream(true); // 合并错误流和输出流
process = processBuilder.start();
// 读取输出
StringBuilder output = new StringBuilder();
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(process.getInputStream()))) {
String line;
while ((line = reader.readLine()) != null) {
output.append(line).append("\n");
}
}
// 等待进程完成
boolean finished;
if (timeout > 0) {
finished = process.waitFor(timeout, TimeUnit.SECONDS);
} else {
process.waitFor();
finished = true;
}
if (!finished) {
process.destroy();
return new CommandResult(-1, "命令执行超时", output.toString());
}
int exitCode = process.exitValue();
return new CommandResult(exitCode, exitCode == 0 ? "执行成功" : "执行失败", output.toString());
} catch (IOException | InterruptedException e) {
return new CommandResult(-1, "执行异常: " + e.getMessage(), "");
} finally {
if (process != null) {
process.destroy();
}
}
}
/**
* 执行shell脚本文件
* @param scriptPath 脚本文件路径
* @param args 脚本参数
* @param timeout 超时时间(秒)
* @return 执行结果
*/
public static CommandResult executeScript(String scriptPath, String[] args, int timeout) {
// 首先确保脚本有执行权限
if (!makeFileExecutable(scriptPath)) {
return new CommandResult(-1, "无法设置执行权限", "");
}
// 构建命令
String[] command = new String[args.length + 1];
command[0] = scriptPath;
System.arraycopy(args, 0, command, 1, args.length);
return executeCommand(command, timeout);
}
/**
* 执行命令的简化方法
*/
public static CommandResult executeCommand(String command, int timeout) {
return executeCommand(new String[]{"sh", "-c", command}, timeout);
}
/**
* 执行结果封装类
*/
public static class CommandResult {
private final int exitCode;
private final String message;
private final String output;
public CommandResult(int exitCode, String message, String output) {
this.exitCode = exitCode;
this.message = message;
this.output = output;
}
public int getExitCode() { return exitCode; }
public String getMessage() { return message; }
public String getOutput() { return output; }
public boolean isSuccess() { return exitCode == 0; }
@Override
public String toString() {
return String.format("Exit Code: %d\nMessage: %s\nOutput:\n%s",
exitCode, message, output);
}
}
}
@@ -66,7 +66,7 @@ public class NettyTcpClient {
pipeline.addLast("encoder", new StringEncoder());
// 添加心跳机制
pipeline.addLast("idleStateHandler",
new IdleStateHandler(0, 0, 90, TimeUnit.SECONDS));
new IdleStateHandler(0, 30, 0, TimeUnit.SECONDS));
// 添加自定义处理器
pipeline.addLast("clientHandler", new NettyClientHandler());
}
@@ -1,41 +1,55 @@
package com.tongran.agentserver.server.netty.enpoint;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONException;
import com.alibaba.fastjson2.JSONObject;
import com.tongran.agentserver.core.enums.MsgEnum;
import com.tongran.agentserver.scheduler.*;
import com.tongran.agentserver.server.file.AdvancedAsyncDownloader;
import com.tongran.agentserver.server.file.AsyncCommandExecutor;
import com.tongran.agentserver.server.file.LinuxCommandExecutor;
import com.tongran.agentserver.server.netty.NettyTcpClient;
import com.tongran.agentserver.server.netty.annotation.AgentDispatcher;
import com.tongran.agentserver.server.netty.basics.AgentHandler;
import com.tongran.agentserver.server.netty.model.Message;
import com.tongran.agentserver.server.netty.model.UpMsgResponse;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
@Component
public class AgentEndpoint {
@Resource
private CpuScheduler cpuScheduler;
// @Resource
// private CpuScheduler cpuScheduler;
//
// @Resource
// private DiskScheduler diskScheduler;
//
// @Resource
// private DockerScheduler dockerScheduler;
//
// @Resource
// private MemoryScheduler memoryScheduler;
//
// @Resource
// private NetScheduler netScheduler;
//
// @Resource
// private PointScheduler pointScheduler;
//
// @Resource
// private SwitchBoardScheduler switchBoardScheduler;
//
// @Resource
// private SysScheduler sysScheduler;
@Resource
private DiskScheduler diskScheduler;
@Resource
private DockerScheduler dockerScheduler;
@Resource
private MemoryScheduler memoryScheduler;
@Resource
private NetScheduler netScheduler;
@Resource
private PointScheduler pointScheduler;
@Resource
private SwitchBoardScheduler switchBoardScheduler;
@Resource
private SysScheduler sysScheduler;
private NettyTcpClient nettyTcpClient;
@AgentDispatcher(msgId = MsgEnum.更新CPU采集间隔)
@@ -44,7 +58,7 @@ public class AgentEndpoint {
public UpMsgResponse upHandle(String data, String clientId) {
JSONObject jsonObject = JSONObject.parseObject(data);
long intervalMillis = jsonObject.getLong("intervalMillis");
cpuScheduler.updateSchedule(25000, intervalMillis);
// cpuScheduler.updateSchedule(25000, intervalMillis);
JSONObject json = new JSONObject();
json.put("clientId",clientId);
json.put("resCode",1);
@@ -58,7 +72,7 @@ public class AgentEndpoint {
public UpMsgResponse upHandle(String data, String clientId) {
JSONObject jsonObject = JSONObject.parseObject(data);
long intervalMillis = jsonObject.getLong("intervalMillis");
diskScheduler.updateSchedule(35000, intervalMillis);
// diskScheduler.updateSchedule(35000, intervalMillis);
JSONObject json = new JSONObject();
json.put("clientId",clientId);
json.put("resCode",1);
@@ -72,7 +86,7 @@ public class AgentEndpoint {
public UpMsgResponse upHandle(String data, String clientId) {
JSONObject jsonObject = JSONObject.parseObject(data);
long intervalMillis = jsonObject.getLong("intervalMillis");
cpuScheduler.updateSchedule(45000, intervalMillis);
// dockerScheduler.updateSchedule(45000, intervalMillis);
JSONObject json = new JSONObject();
json.put("clientId",clientId);
json.put("resCode",1);
@@ -86,7 +100,7 @@ public class AgentEndpoint {
public UpMsgResponse upHandle(String data, String clientId) {
JSONObject jsonObject = JSONObject.parseObject(data);
long intervalMillis = jsonObject.getLong("intervalMillis");
memoryScheduler.updateSchedule(55000, intervalMillis);
// memoryScheduler.updateSchedule(55000, intervalMillis);
JSONObject json = new JSONObject();
json.put("clientId",clientId);
json.put("resCode",1);
@@ -100,7 +114,7 @@ public class AgentEndpoint {
public UpMsgResponse upHandle(String data, String clientId) {
JSONObject jsonObject = JSONObject.parseObject(data);
long intervalMillis = jsonObject.getLong("intervalMillis");
netScheduler.updateSchedule(65000, intervalMillis);
// netScheduler.updateSchedule(65000, intervalMillis);
JSONObject json = new JSONObject();
json.put("clientId",clientId);
json.put("resCode",1);
@@ -114,7 +128,7 @@ public class AgentEndpoint {
public UpMsgResponse upHandle(String data, String clientId) {
JSONObject jsonObject = JSONObject.parseObject(data);
long intervalMillis = jsonObject.getLong("intervalMillis");
pointScheduler.updateSchedule(75000, intervalMillis);
// pointScheduler.updateSchedule(75000, intervalMillis);
JSONObject json = new JSONObject();
json.put("clientId",clientId);
json.put("resCode",1);
@@ -128,7 +142,7 @@ public class AgentEndpoint {
public UpMsgResponse upHandle(String data, String clientId) {
JSONObject jsonObject = JSONObject.parseObject(data);
long intervalMillis = jsonObject.getLong("intervalMillis");
switchBoardScheduler.updateSchedule(85000, intervalMillis);
// switchBoardScheduler.updateSchedule(85000, intervalMillis);
JSONObject json = new JSONObject();
json.put("clientId",clientId);
json.put("resCode",1);
@@ -142,7 +156,7 @@ public class AgentEndpoint {
public UpMsgResponse upHandle(String data, String clientId) {
JSONObject jsonObject = JSONObject.parseObject(data);
long intervalMillis = jsonObject.getLong("intervalMillis");
sysScheduler.updateSchedule(40000, intervalMillis);
// sysScheduler.updateSchedule(40000, intervalMillis);
JSONObject json = new JSONObject();
json.put("clientId",clientId);
json.put("resCode",1);
@@ -150,5 +164,129 @@ public class AgentEndpoint {
}
}
@AgentDispatcher(msgId = MsgEnum.文件下载)
public class DownFileHandler implements AgentHandler {
@Override
public UpMsgResponse upHandle(String data, String clientId) {
JSONObject jsonObject = JSONObject.parseObject(data);
String fileUrl = jsonObject.getString("fileUrl");
String saveDir = "";
try {
saveDir = jsonObject.getString("saveDir");
} catch (JSONException e) {
saveDir = "/usr/local/tongran/downloaded";
}
if(AdvancedAsyncDownloader.createSingleDirectoryIfNotExists(saveDir)){
String finalSaveDir = saveDir;
AdvancedAsyncDownloader.downloadWithProgress(fileUrl, saveDir, null, progress -> {
// System.out.printf("下载进度: %.1f%%\n", progress);
}).thenAccept(filePath -> {
JSONObject json = new JSONObject();
json.put("clientId",clientId);
json.put("saveDir", finalSaveDir);
json.put("fileUrl",fileUrl);
json.put("resCode",1);
Message message = Message.builder().clientId(clientId).dataType(MsgEnum.文件下载应答.getValue()).data(json.toString()).build();
// 将对象转为 JSON 字符串
String msg = "agent-tcp:"+ JSON.toJSONString(message)+"@tong-ran";
nettyTcpClient.sendMessage(msg);
System.out.println("下载完成: " + filePath);
}).exceptionally(ex -> {
JSONObject json = new JSONObject();
json.put("clientId",clientId);
json.put("fileUrl",fileUrl);
json.put("resCode",0);
Message message = Message.builder().clientId(clientId).dataType(MsgEnum.文件下载应答.getValue()).data(json.toString()).build();
// 将对象转为 JSON 字符串
String msg = "agent-tcp:"+ JSON.toJSONString(message)+"@tong-ran";
nettyTcpClient.sendMessage(msg);
System.err.println("下载错误: " + ex.getMessage());
return null;
});
}
return null;
}
}
@AgentDispatcher(msgId = MsgEnum.执行命令)
public class ExecuteCommandHandler implements AgentHandler {
@Override
public UpMsgResponse upHandle(String data, String clientId) {
JSONObject jsonObject = JSONObject.parseObject(data);
String scriptPath = "";
try {
scriptPath = jsonObject.getString("scriptPath");
} catch (JSONException e) {
scriptPath = "";
}
String command = jsonObject.getString("command");
boolean isFalse = false;
if(StringUtils.isNotBlank(scriptPath)){
isFalse = true;
}
if(isFalse){
if(LinuxCommandExecutor.makeFileExecutable(scriptPath)){
isFalse = false;
}
}
if(!isFalse){
System.out.println("=== 基本异步执行 ===");
// List<String> commands = Arrays.asList(
// "/data/agent-server/ksc-andromedae",
// "test_upspeed",
// "-s", "3db15c7e2b86d2cbf91bef7c6ccc93a3",
// "-i", "eth0"
// );
List<String> commands = Arrays.asList(command.split("\\s+"));
CompletableFuture<AsyncCommandExecutor.CommandResult> future =
AsyncCommandExecutor.executeCommandAsync(
commands,
60, TimeUnit.SECONDS);
future.thenAccept(result -> {
if (result.isSuccess()) {
System.out.println("脚本执行成功");
System.out.println("[成功resOut] " + result.getOutput());
JSONObject json = new JSONObject();
json.put("clientId",clientId);
json.put("command",command);
json.put("resCode",1);
json.put("resOut",result.getOutput());
json.put("error",result.getError());
Message message = Message.builder().clientId(clientId).dataType(MsgEnum.执行命令应答.getValue()).data(json.toString()).build();
// 将对象转为 JSON 字符串
String msg = "agent-tcp:"+ JSON.toJSONString(message)+"@tong-ran";
nettyTcpClient.sendMessage(msg);
} else {
System.out.println("脚本执行失败: " + result.getError());
System.out.println("[失败resOut] " + result.getOutput());
JSONObject json = new JSONObject();
json.put("clientId",clientId);
json.put("command",command);
json.put("resCode",0);
json.put("resOut",result.getOutput());
json.put("error",result.getError());
Message message = Message.builder().clientId(clientId).dataType(MsgEnum.执行命令应答.getValue()).data(json.toString()).build();
// 将对象转为 JSON 字符串
String msg = "agent-tcp:"+ JSON.toJSONString(message)+"@tong-ran";
nettyTcpClient.sendMessage(msg);
}
}).exceptionally(ex -> {
System.err.println("执行失败: " + ex.getMessage());
JSONObject json = new JSONObject();
json.put("clientId",clientId);
json.put("command",command);
json.put("resCode",0);
json.put("error",ex.getMessage());
Message message = Message.builder().clientId(clientId).dataType(MsgEnum.执行命令应答.getValue()).data(json.toString()).build();
// 将对象转为 JSON 字符串
String msg = "agent-tcp:"+ JSON.toJSONString(message)+"@tong-ran";
nettyTcpClient.sendMessage(msg);
return null;
});
}
return null;
}
}
}
@@ -1,8 +1,12 @@
package com.tongran.agentserver.server.netty.handler;
import cn.hutool.cache.Cache;
import cn.hutool.cache.CacheUtil;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.tongran.agentserver.core.session.SessionManager;
import com.tongran.agentserver.server.netty.annotation.AgentDispatcher;
import com.tongran.agentserver.server.netty.basics.AgentDispatcherManager;
import com.tongran.agentserver.server.netty.basics.AgentHandler;
@@ -23,9 +27,17 @@ import java.util.Objects;
@ChannelHandler.Sharable
public class DecoderHandler extends ChannelInboundHandlerAdapter {
protected final SessionManager sessionManager;
public DecoderHandler() {
this.sessionManager = SessionManager.getInstance();
}
@Resource
AgentDispatcherManager agentDispatcherManager;
// 用来临时保留没有处理过的请求报文
private final Cache<String, String> lruCache = CacheUtil.newLRUCache(3000);
/**
* 消息解码器
@@ -36,29 +48,101 @@ public class DecoderHandler extends ChannelInboundHandlerAdapter {
if (msg instanceof ByteBuf) {
ByteBuf byteBuf = (ByteBuf) msg;
String messages = byteBuf.toString(CharsetUtil.UTF_8); // 指定字符集解码
AssertLog.info("<<[up]:[up-content]==>{}", messages);
JSONObject jsonObject = JSONObject.parseObject(messages);
String clientId = jsonObject.getString("clientId");
String dataType = jsonObject.getString("dataType");
AssertLog.info("<<11111111111111==>{}");
if(Objects.nonNull(agentDispatcherManager)){
AgentHandler msgHandler = agentDispatcherManager.getHandler(dataType + "&" + AgentDispatcher.VersionEnum.V1.value);
AssertLog.info("<<22222222222222==>{}");
if (ObjectUtil.isNotEmpty(msgHandler)) {
AssertLog.info("<<3333333333333==>{}");
UpMsgResponse response = msgHandler.upHandle(messages,clientId);
AssertLog.info("<<4444444444444==>{}");
if(Objects.nonNull(response)){
Message agentMessage = Message.builder().build();
agentMessage.setClientId(clientId);
agentMessage.setDataType(response.getDataType());
agentMessage.setData(response.getContent());
String json = "agent-tcp:"+ JSON.toJSONString(agentMessage)+"@tong-ran";
ctx.fireChannelRead(json);//传递到下一个handler
AssertLog.info("<<555555555555555==>{}");
AssertLog.info("<<[up1]:[up-content]==>{}", messages);
String content = "";
boolean isClear = false;
String tempMsg = lruCache.get(sessionManager.client(ctx));
tempMsg = tempMsg == null ? "" : tempMsg;
int tmpMsgSize = tempMsg.length();
boolean startsWith = messages.startsWith("agent-server:");
boolean endsWith = messages.endsWith("@tong-ran");
String ms = messages.replaceAll("agent-server:","");
String[] arr_msg = ms.split("@tong-ran");
if(startsWith){
//判定是否整包
if(arr_msg.length == 1){
//判定是否拆包
if(endsWith){
lruCache.remove(sessionManager.client(ctx));
tmpMsgSize = 0;
content = arr_msg[0]+"@tong-ran";
}else{
lruCache.remove(sessionManager.client(ctx));
tmpMsgSize = 0;
content = arr_msg[0];
lruCache.put(sessionManager.client(ctx), content, DateUnit.SECOND.getMillis() * 3000);
}
}
//判定是否粘包
if(arr_msg.length > 1){
if(!endsWith){
lruCache.remove(sessionManager.client(ctx));
tmpMsgSize = 0;
content = arr_msg[0]+"@tong-ran";
lruCache.put(sessionManager.client(ctx), arr_msg[1], DateUnit.SECOND.getMillis() * 3000);
}else{
lruCache.remove(sessionManager.client(ctx));
tmpMsgSize = 0;
content = String.join("@tong-ran", arr_msg);
}
}
}
if (tmpMsgSize > 0) {
content = tempMsg + messages;
isClear = true;
}
AssertLog.info("<<[up2]:IP:{},[up-content]==>{}", sessionManager.client(ctx),content);
endsWith = content.endsWith("@tong-ran");
//判断是否是整包
if(endsWith){
String[] arr = content.split("@tong-ran");
for (String message : arr) {
JSONObject jsonObject = JSONObject.parseObject(message);
String clientId = jsonObject.getString("clientId");
String dataType = jsonObject.getString("dataType");
String data = jsonObject.getString("data");
if(Objects.nonNull(agentDispatcherManager)){
AgentHandler msgHandler = agentDispatcherManager.getHandler(dataType + "&" + AgentDispatcher.VersionEnum.V1.value);
if (ObjectUtil.isNotEmpty(msgHandler)) {
UpMsgResponse response = msgHandler.upHandle(data, clientId );
AssertLog.info("<<[up-after-handle]:clientId:{},type={},[handle-content]={}", clientId, dataType, data);
if(Objects.nonNull(response)){
Message agentMessage = Message.builder().build();
agentMessage.setClientId(clientId);
agentMessage.setDataType(response.getDataType());
agentMessage.setData(response.getContent());
String json = "agent-tcp:"+ JSON.toJSONString(agentMessage)+"@tong-ran";
ctx.fireChannelRead(json);//传递到下一个handler
}
}
}
}
}
// JSONObject jsonObject = JSONObject.parseObject(messages);
// String clientId = jsonObject.getString("clientId");
// String dataType = jsonObject.getString("dataType");
// AssertLog.info("<<11111111111111==>{}");
// if(Objects.nonNull(agentDispatcherManager)){
// AgentHandler msgHandler = agentDispatcherManager.getHandler(dataType + "&" + AgentDispatcher.VersionEnum.V1.value);
// AssertLog.info("<<22222222222222==>{}");
// if (ObjectUtil.isNotEmpty(msgHandler)) {
// AssertLog.info("<<3333333333333==>{}");
// UpMsgResponse response = msgHandler.upHandle(messages,clientId);
// AssertLog.info("<<4444444444444==>{}");
// if(Objects.nonNull(response)){
// Message agentMessage = Message.builder().build();
// agentMessage.setClientId(clientId);
// agentMessage.setDataType(response.getDataType());
// agentMessage.setData(response.getContent());
// String json = "agent-tcp:"+ JSON.toJSONString(agentMessage)+"@tong-ran";
// ctx.fireChannelRead(json);//传递到下一个handler
// AssertLog.info("<<555555555555555==>{}");
// }
// }
// }
byteBuf.release(); // 释放 ByteBuf 资源(重要!)
} else {
System.out.println("Unexpected message type: " + msg.getClass());
+2
View File
@@ -12,10 +12,12 @@ knife4j:
logging:
file:
path: /usr/local/tongran/logs
# path: /data/agent-server/logs
netty:
server:
host: 172.16.15.103
# host: 127.0.0.1
port: 6610
client:
client-id: client-001