初始化

This commit is contained in:
qiminbao
2025-09-10 15:36:49 +08:00
commit 4330ffdff1
75 changed files with 6674 additions and 0 deletions
@@ -0,0 +1,22 @@
package com.tongran.agent.client.netty;
import com.tongran.agent.client.netty.config.BaseNettyConfig;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
/**
* Netty配置
*/
@Data
@Configuration
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
@ConfigurationProperties(prefix = "tcp.netty.charge")
public class AgentNettyConfig extends BaseNettyConfig {
private static final long serialVersionUID = -4284214721383107291L;
}
@@ -0,0 +1,57 @@
package com.tongran.agent.client.netty;
import com.tongran.agent.client.netty.handler.AgentDecoderHandler;
import com.tongran.agent.client.netty.handler.AgentDispatcherHandler;
import com.tongran.agent.client.netty.handler.AgentEncoderHandler;
import com.tongran.agent.client.netty.handler.TCPListenHandler;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.timeout.IdleStateHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.annotation.Resource;
@Configuration
public class AgentNettyServer extends BaseNettyServer {
@Resource
private AgentNettyConfig config;
@Resource
private TCPListenHandler tcpListenHandler;
@Resource
private AgentDecoderHandler decoderHandler;
@Resource
private AgentEncoderHandler encoderHandler;
@Resource
private AgentDispatcherHandler dispatcherHandler;
protected AgentNettyServer(AgentNettyConfig config) {
super(config);
}
@Bean(name = "NettyCharge", initMethod = "start", destroyMethod = "stop")
BaseNettyServer run() {
config.setHander(new ChannelInitializer<NioSocketChannel>() {
@Override
public void initChannel(NioSocketChannel ch) throws Exception {
ch.pipeline().addLast(new IdleStateHandler(config.readerIdleTime, config.writerIdleTime, config.allIdleTime));// 心跳
ch.pipeline().addLast(tcpListenHandler);// 监听器
//入栈
ch.pipeline().addLast(decoderHandler);//解码器
//出栈
ch.pipeline().addLast(encoderHandler);//加码器
// 业务分发是入站最后一个处理器, 出站第一个处理器, 位置要放在最后
ch.pipeline().addLast(businessGroup, dispatcherHandler);//业务分发 这里使用业务线程组
}
});
return new AgentNettyServer(config);
}
}
@@ -0,0 +1,117 @@
package com.tongran.agent.client.netty;
import com.tongran.agent.client.netty.config.BaseNettyConfig;
import com.tongran.agent.client.utils.AssertLog;
import io.netty.bootstrap.AbstractBootstrap;
import io.netty.bootstrap.Bootstrap;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioChannelOption;
import io.netty.channel.socket.nio.NioDatagramChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.util.ResourceLeakDetector;
import io.netty.util.concurrent.DefaultEventExecutorGroup;
import io.netty.util.concurrent.DefaultThreadFactory;
import io.netty.util.concurrent.EventExecutorGroup;
import io.netty.util.concurrent.Future;
/**
* 基础Netty服务
*/
public abstract class BaseNettyServer {
protected boolean isRunning;
protected BaseNettyConfig config;
protected EventLoopGroup bossGroup;
protected EventLoopGroup workerGroup;
protected EventExecutorGroup businessGroup;
protected BaseNettyServer(BaseNettyConfig config) {
this.config = config;
}
protected AbstractBootstrap<?, ?> initializeTcp() {
bossGroup = new NioEventLoopGroup(1, new DefaultThreadFactory(config.name, Thread.MAX_PRIORITY));
workerGroup = new NioEventLoopGroup(config.workerCore, new DefaultThreadFactory(config.name, Thread.MAX_PRIORITY));
if (config.businessCore > 0) {
businessGroup = new DefaultEventExecutorGroup(config.businessCore);
}
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childOption(ChannelOption.SO_REUSEADDR, true)
.option(ChannelOption.SO_BACKLOG, 1024)
.childOption(NioChannelOption.TCP_NODELAY, true)
.childHandler(config.hander);
//内存泄漏检测 开发推荐PARANOID 线上SIMPLE
ResourceLeakDetector.setLevel(ResourceLeakDetector.Level.SIMPLE);
return serverBootstrap;
}
protected AbstractBootstrap<?, ?> initializeUdp() {
bossGroup = new NioEventLoopGroup(1, new DefaultThreadFactory(config.name, Thread.MAX_PRIORITY));
if (config.businessCore > 0) {
businessGroup = new DefaultEventExecutorGroup(config.businessCore);
}
return new Bootstrap()
.group(bossGroup).channel(NioDatagramChannel.class)
.option(NioChannelOption.SO_REUSEADDR, true)
.option(NioChannelOption.SO_RCVBUF, 1024 * 1024 * 50)
.handler(config.hander);
}
public synchronized boolean start() {
if (!config.enable) {
return false;
}
if (isRunning) {
AssertLog.info("======{}已经启动,port:{}======", config.name, config.port);
return isRunning;
}
AbstractBootstrap<?, ?> bootstrap = config.isTcp ? initializeTcp() : initializeUdp();
ChannelFuture future = bootstrap.bind(config.port).awaitUninterruptibly();
future.channel().closeFuture().addListener(f -> {
if (isRunning) {
stop();
}
});
if (future.cause() != null) {
AssertLog.error("===启动失败===", future.cause());
}
if (isRunning = future.isSuccess()) {
AssertLog.info("\n\n\t\t\t\t\t\t\t\t======{}启动成功,port:{}======\n", config.name, config.port);
}
return isRunning;
}
public synchronized void stop() {
if (!config.enable) {
return;
}
isRunning = false;
try {
Future<?> future = this.workerGroup.shutdownGracefully().await();
if (!future.isSuccess()) {
AssertLog.error("workerGroup 无法正常停止:{}", future.cause());
}
future = this.bossGroup.shutdownGracefully().await();
if (!future.isSuccess()) {
AssertLog.error("bossGroup 无法正常停止:{}", future.cause());
}
future = this.businessGroup.shutdownGracefully().await();
if (!future.isSuccess()) {
AssertLog.error("businessGroup 无法正常停止:{}", future.cause());
}
} catch (InterruptedException e) {
e.printStackTrace();
}
AssertLog.info("\n\n\t\t\t\t\t\t\t\t======{} 已经停止,port:{}======\n", config.name, config.port);
}
}
@@ -0,0 +1,47 @@
package com.tongran.agent.client.netty.annotation;
import com.tongran.agent.client.core.enums.MsgEnum;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.springframework.stereotype.Component;
import java.lang.annotation.*;
/**
* 指明类为Agent消息
*/
@Component
@Documented
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface AgentDispatcher {
@Getter
@AllArgsConstructor
enum VersionEnum {
V1("2026");
public final String value;
}
/**
* 消息ID
*
* @return
*/
MsgEnum msgId();
/**
* 消息版本 默认版本2025
*
* @return
*/
VersionEnum version() default VersionEnum.V1;
/**
* 描述
*
* @return
*/
String desc() default "";
}
@@ -0,0 +1,48 @@
package com.tongran.agent.client.netty.basics;
import com.tongran.agent.client.netty.annotation.AgentDispatcher;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@Component
@Order(1)
public class AgentDispatcherManager implements ApplicationContextAware {
/**
* 所有实现的包处理器
*/
private static Map<String, AgentHandler> MSG_HANDLER_MAP;
/**
* 唤醒时 初始化 packHandlerMap
*/
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
// 仅一次性初始化完成
if (MSG_HANDLER_MAP == null) {
MSG_HANDLER_MAP = new ConcurrentHashMap<>();
Map<String, Object> handlers = applicationContext.getBeansWithAnnotation(AgentDispatcher.class);
if (!CollectionUtils.isEmpty(handlers)) {
handlers.values().forEach(tempHandler -> {
boolean result = tempHandler.getClass().isAnnotationPresent(AgentDispatcher.class);
if (result) {
AgentDispatcher annotation = tempHandler.getClass().getAnnotation(AgentDispatcher.class);
MSG_HANDLER_MAP.put(annotation.msgId().getValue() + "&" + annotation.version().value, (AgentHandler) tempHandler);
}
});
}
}
}
public AgentHandler getHandler(String msgIdVersion) {
return MSG_HANDLER_MAP == null ? null : MSG_HANDLER_MAP.get(msgIdVersion);
}
}
@@ -0,0 +1,21 @@
package com.tongran.agent.client.netty.basics;
import com.tongran.agent.client.netty.model.UpMsgResponse;
public interface AgentHandler {
/**
* 处理终端传入的消息, 然后进行返回
*
* @return 需要发送给终端的消息
*/
default UpMsgResponse upHandle(String data, String clientId) {
return null;
}
default String downHandle(String data) {
return "";
}
}
@@ -0,0 +1,58 @@
package com.tongran.agent.client.netty.config;
import com.tongran.agent.client.core.session.SessionManager;
import io.netty.channel.ChannelHandler;
import io.netty.util.NettyRuntime;
import lombok.Data;
import java.io.Serializable;
/**
* 基础Netty配置
*/
@Data
public class BaseNettyConfig implements Serializable {
private static final long serialVersionUID = -2736718499972710895L;
/**
* 是否开启
*/
public Boolean enable = false;
/**
* 是否TCP
*/
public Boolean isTcp = true;
/**
* 服务名称
*/
public String name = "Netty";
/**
* 端口号
*/
public Integer port = 11111;
public Integer workerCore = NettyRuntime.availableProcessors() * 2;
public Integer businessCore = Math.max(1, NettyRuntime.availableProcessors() >> 1);
public Integer readerIdleTime = 240;
public Integer writerIdleTime = 0;
public Integer allIdleTime = 0;
/**
* netty Session管理
*/
public SessionManager sessionManager;
/**
* netty Channel处理列表
*/
public ChannelHandler hander;
}
@@ -0,0 +1,484 @@
package com.tongran.agent.client.netty.enpoint;
import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.alibaba.fastjson2.TypeReference;
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.ScriptPolicyEO;
import com.tongran.agent.client.netty.annotation.AgentDispatcher;
import com.tongran.agent.client.netty.basics.AgentHandler;
import com.tongran.agent.client.netty.model.Message;
import com.tongran.agent.client.netty.model.UpMsgResponse;
import com.tongran.agent.client.scheduler.service.AdvancedAsyncDownloader;
import com.tongran.agent.client.scheduler.service.AppInitializer;
import com.tongran.agent.client.scheduler.service.AsyncCommandExecutor;
import com.tongran.agent.client.scheduler.service.Java8FileDownloader;
import com.tongran.agent.client.scheduler.task.SpecificTimeRequest;
import com.tongran.agent.client.scheduler.task.SpecificTimeTaskService;
import com.tongran.agent.client.service.AgentService;
import com.tongran.agent.client.utils.AgentUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
@Component
public class AgentEndpoint {
@Resource
private AppInitializer appInitializer;
@Resource
private SpecificTimeTaskService taskService;
@Resource
private AgentService agentService;
@AgentDispatcher(msgId = MsgEnum.注册)
public class RegisterHandler implements AgentHandler {
@Override
public UpMsgResponse upHandle(String data, String clientId) {
//更改全局变量
JSONObject jsonObject = JSONObject.parseObject(data);
if(jsonObject.containsKey("switchBoard")){
String switchBoard = jsonObject.getString("switchBoard");
jsonObject = JSONObject.parseObject(switchBoard);
if(jsonObject.containsKey("community")){
GlobalConfig.SWITCH_COMMUNITY = jsonObject.getString("community");
}
if(jsonObject.containsKey("ip")){
GlobalConfig.SWITCH_IP = jsonObject.getString("ip");
}
if(jsonObject.containsKey("port")){
GlobalConfig.SWITCH_PORT = jsonObject.getInteger("port");
}
if(jsonObject.containsKey("netOID")){
String netOID = jsonObject.getString("netOID");
LinkedHashMap<String, String> map = JSON.parseObject(netOID, new TypeReference<LinkedHashMap<String, String>>() {});
GlobalConfig.SWITCH_NET_OID = map;
}
if(jsonObject.containsKey("moduleOID")){
String moduleOID = jsonObject.getString("moduleOID");
LinkedHashMap<String, String> map = JSON.parseObject(moduleOID, new TypeReference<LinkedHashMap<String, String>>() {});
GlobalConfig.SWITCH_MODULE_OID = map;
}
if(jsonObject.containsKey("mpuOID")){
String mpuOID = jsonObject.getString("mpuOID");
LinkedHashMap<String, String> map = JSON.parseObject(mpuOID, new TypeReference<LinkedHashMap<String, String>>() {});
GlobalConfig.SWITCH_MPU_OID = map;
}
if(jsonObject.containsKey("pwrOID")){
String pwrOID = jsonObject.getString("pwrOID");
LinkedHashMap<String, String> map = JSON.parseObject(pwrOID, new TypeReference<LinkedHashMap<String, String>>() {});
GlobalConfig.SWITCH_PWR_OID = map;
}
if(jsonObject.containsKey("fanOID")){
String fanOID = jsonObject.getString("fanOID");
LinkedHashMap<String, String> map = JSON.parseObject(fanOID, new TypeReference<LinkedHashMap<String, String>>() {});
GlobalConfig.SWITCH_FAN_OID = map;
}
if(jsonObject.containsKey("otherOID")){
String otherOID = jsonObject.getString("otherOID");
LinkedHashMap<String, String> map = JSON.parseObject(otherOID, new TypeReference<LinkedHashMap<String, String>>() {});
GlobalConfig.SWITCH_OTHER_OID = map;
}
}
GlobalConfig.isCollect = true;
GlobalConfig.CLIENT_ID = clientId;
//调用采集任务
try {
appInitializer.run();
} catch (Exception e) {
e.printStackTrace();
}
long timestamp = System.currentTimeMillis();
timestamp = Math.round(timestamp / 1000.0);
JSONObject json = new JSONObject();
json.put("resCode",1);
json.put("resMag","");
json.put("timestamp",timestamp);
return UpMsgResponse.builder().clientId(clientId).dataType(MsgEnum.注册应答.getValue())
.content(json.toString()).build();
}
}
@AgentDispatcher(msgId = MsgEnum.断开)
public class DisconnectHandler implements AgentHandler {
@Override
public UpMsgResponse upHandle(String data, String clientId) {
//更改全局变量
GlobalConfig.isCollect = false;
//调用采集任务
try {
appInitializer.run();
} catch (Exception e) {
e.printStackTrace();
}
long timestamp = System.currentTimeMillis();
timestamp = Math.round(timestamp / 1000.0);
JSONObject json = new JSONObject();
json.put("resCode",1);
json.put("resMag","");
json.put("timestamp",timestamp);
return UpMsgResponse.builder().clientId(clientId).dataType(MsgEnum.断开应答.getValue())
.content(json.toString()).build();
}
}
@AgentDispatcher(msgId = MsgEnum.开启系统采集)
public class SysCollectStartHandler implements AgentHandler {
@Override
public UpMsgResponse upHandle(String data, String clientId) {
//更改全局变量
agentService.systemCollectStart(data);
long timestamp = System.currentTimeMillis();
timestamp = Math.round(timestamp / 1000.0);
JSONObject json = new JSONObject();
json.put("resCode",1);
json.put("resMag","");
json.put("timestamp",timestamp);
return UpMsgResponse.builder().dataType(MsgEnum.开启系统采集应答.getValue()).content(json.toString()).build();
}
}
@AgentDispatcher(msgId = MsgEnum.关闭系统采集)
public class SysCollectStopHandler implements AgentHandler {
@Override
public UpMsgResponse upHandle(String data, String clientId) {
//更改全局变量
agentService.systemCollectStop();
long timestamp = System.currentTimeMillis();
timestamp = Math.round(timestamp / 1000.0);
JSONObject json = new JSONObject();
json.put("resCode",1);
json.put("resMag","");
json.put("timestamp",timestamp);
return UpMsgResponse.builder().dataType(MsgEnum.关闭系统采集应答.getValue()).content(json.toString()).build();
}
}
@AgentDispatcher(msgId = MsgEnum.开启交换机采集)
public class SwitchCollectStartHandler implements AgentHandler {
@Override
public UpMsgResponse upHandle(String data, String clientId) {
//更改全局变量
agentService.switchCollectStart(data);
long timestamp = System.currentTimeMillis();
timestamp = Math.round(timestamp / 1000.0);
JSONObject json = new JSONObject();
json.put("resCode",1);
json.put("resMag","");
json.put("timestamp",timestamp);
return UpMsgResponse.builder().dataType(MsgEnum.开启交换机采集应答.getValue()).content(json.toString()).build();
}
}
@AgentDispatcher(msgId = MsgEnum.关闭交换机采集)
public class SwitchCollectStopHandler implements AgentHandler {
@Override
public UpMsgResponse upHandle(String data, String clientId) {
//更改全局变量
agentService.switchCollectStop();
long timestamp = System.currentTimeMillis();
timestamp = Math.round(timestamp / 1000.0);
JSONObject json = new JSONObject();
json.put("resCode",1);
json.put("resMag","");
json.put("timestamp",timestamp);
return UpMsgResponse.builder().dataType(MsgEnum.关闭交换机采集应答.getValue()).content(json.toString()).build();
}
}
@AgentDispatcher(msgId = MsgEnum.告警设置)
public class AlarmSetHandler implements AgentHandler {
@Override
public UpMsgResponse upHandle(String data, String clientId) {
// //更改全局变量
// JSONObject jsonObject = JSONObject.parseObject(data);
// if(jsonObject.containsKey("switchBoard")){
// String switchBoard = jsonObject.getString("switchBoard");
// jsonObject = JSONObject.parseObject(switchBoard);
// if(jsonObject.containsKey("community")){
// GlobalConfig.SWITCH_COMMUNITY = jsonObject.getString("community");
// }
// if(jsonObject.containsKey("ip")){
// GlobalConfig.SWITCH_IP = jsonObject.getString("ip");
// }
// if(jsonObject.containsKey("port")){
// GlobalConfig.SWITCH_PORT = jsonObject.getInteger("port");
// }
// if(jsonObject.containsKey("netOID")){
// String netOID = jsonObject.getString("netOID");
// LinkedHashMap<String, String> map = JSON.parseObject(netOID, new TypeReference<LinkedHashMap<String, String>>() {});
// GlobalConfig.SWITCH_NET_OID = map;
// }
// if(jsonObject.containsKey("moduleOID")){
// String moduleOID = jsonObject.getString("moduleOID");
// LinkedHashMap<String, String> map = JSON.parseObject(moduleOID, new TypeReference<LinkedHashMap<String, String>>() {});
// GlobalConfig.SWITCH_MODULE_OID = map;
// }
// if(jsonObject.containsKey("mpuOID")){
// String mpuOID = jsonObject.getString("mpuOID");
// LinkedHashMap<String, String> map = JSON.parseObject(mpuOID, new TypeReference<LinkedHashMap<String, String>>() {});
// GlobalConfig.SWITCH_MPU_OID = map;
// }
// if(jsonObject.containsKey("pwrOID")){
// String pwrOID = jsonObject.getString("pwrOID");
// LinkedHashMap<String, String> map = JSON.parseObject(pwrOID, new TypeReference<LinkedHashMap<String, String>>() {});
// GlobalConfig.SWITCH_PWR_OID = map;
// }
// if(jsonObject.containsKey("fanOID")){
// String fanOID = jsonObject.getString("fanOID");
// LinkedHashMap<String, String> map = JSON.parseObject(fanOID, new TypeReference<LinkedHashMap<String, String>>() {});
// GlobalConfig.SWITCH_FAN_OID = map;
// }
// if(jsonObject.containsKey("otherOID")){
// String otherOID = jsonObject.getString("otherOID");
// LinkedHashMap<String, String> map = JSON.parseObject(otherOID, new TypeReference<LinkedHashMap<String, String>>() {});
// GlobalConfig.SWITCH_OTHER_OID = map;
// }
// }
// GlobalConfig.isCollect = true;
// GlobalConfig.CLIENT_ID = clientId;
// //调用采集任务
// try {
// appInitializer.run();
// } catch (Exception e) {
// e.printStackTrace();
// }
// JSONObject json = new JSONObject();
// json.put("resCode",1);
return UpMsgResponse.builder().clientId(clientId).dataType(MsgEnum.告警设置应答.getValue())
.content("").build();
}
}
@AgentDispatcher(msgId = MsgEnum.执行脚本策略)
public class ScriptPolicyHandler implements AgentHandler {
@Override
public UpMsgResponse upHandle(String data, String clientId) {
ScriptPolicyEO policy = JSON.parseObject(data, ScriptPolicyEO.class);
boolean isFalse = false;
if(StringUtils.isNotBlank(policy.getFilePath())){
if(AdvancedAsyncDownloader.createSingleDirectoryIfNotExists(policy.getFilePath())){
if(CollectionUtil.isNotEmpty(policy.getFiles())){
isFalse = true;
for (ScriptPolicyEO.ScriptFile f : policy.getFiles()) {
String finalSaveDir = policy.getFilePath();
//文件类型:0、平台文件地址;1、外网HTTP(S)
if(f.getFileType() == 0){
String filePath = policy.getFilePath() + "/" + f.getFileName();
// 移除Base64 URL前缀(如果存在)
// if (f.getFileData().contains(",")) {
// f.setFileData(f.getFileData().split(",")[1]);
// }
try {
AgentUtil.base64ToFile(f.getFileData(), filePath);
System.out.println("文件保存成功: " + filePath);
isFalse = true;
} catch (IOException e) {
isFalse = false;
System.err.println("文件保存失败: " + e.getMessage());
}
}else{
Java8FileDownloader.DownloadResult result = Java8FileDownloader.downloadFile(f.getFileUrl(), finalSaveDir, f.getFileName());
// 验证下载是否完成
if (result.isSuccess()) {
boolean isComplete = Java8FileDownloader.verifyFileCompletion(
result.getFilePath(), result.getExpectedSize());
if(isComplete){
isFalse = true;
System.out.println("下载完成: " + finalSaveDir+"/"+f.getFileName());
}else{
isFalse = false;
System.err.println("下载失败");
}
}
}
}
}
}
}
//判断文件是否保存成功
if(isFalse){
if(CollectionUtil.isNotEmpty(policy.getCommands())){
//执行方式:0、立即执行;1、定时执行;
if(policy.getMethod() == 1){
SpecificTimeRequest request = SpecificTimeRequest.builder()
.taskId("")
.taskName("")
.taskData(policy.getCommands())
.specificDateTime(policy.getPolicyTime())
.clientId(clientId)
.build();
try {
taskService.createSpecificTimeTask(request);
JSONObject json = new JSONObject();
json.put("resCode",1);
json.put("resMsg", "执行脚本策略定时任务保存成功");
Message message = Message.builder().clientId(clientId).dataType(MsgEnum.执行脚本策略应答.getValue()).data(json.toString()).build();
return UpMsgResponse.builder().clientId(clientId).dataType(MsgEnum.执行脚本策略应答.getValue()).content(json.toString()).build();
} catch (Exception e) {
JSONObject json = new JSONObject();
json.put("resCode",0);
json.put("resMsg", "执行脚本策略定时任务保存失败");
Message message = Message.builder().clientId(clientId).dataType(MsgEnum.执行脚本策略应答.getValue()).data(json.toString()).build();
return UpMsgResponse.builder().clientId(clientId).dataType(MsgEnum.执行脚本策略应答.getValue()).content(json.toString()).build();
}
}else{
List<Map<String,String>> list = new ArrayList<>();
for (String command : policy.getCommands()) {
Map<String,String> map = new HashMap<>();
List<String> cmd = Arrays.asList(command.split("\\s+"));
CompletableFuture<AsyncCommandExecutor.CommandResult> future =
AsyncCommandExecutor.executeCommandAsync(
cmd,
60, TimeUnit.SECONDS);
future.thenAccept(result -> {
if (result.isSuccess()) {
System.out.println("脚本执行成功");
System.out.println("[成功resOut] " + result.getOutput());
map.put("command",command);
map.put("resOut",result.getOutput());
} else {
System.out.println("脚本执行失败");
System.out.println("[失败resOut] " + result.getOutput());
map.put("command",command);
map.put("resOut",result.getOutput());
}
}).exceptionally(ex -> {
System.err.println("执行失败: " + ex.getMessage());
map.put("command",command);
map.put("resOut","Policy execute filed");
return null;
});
list.add(map);
}
if(CollectionUtil.isNotEmpty(list)){
JSONObject json = new JSONObject();
json.put("resCode",1);
json.put("resMsg", "");
json.put("result", AgentUtil.toJsonString(list));
return UpMsgResponse.builder().clientId(clientId).dataType(MsgEnum.执行脚本策略应答.getValue()).content(json.toString()).build();
}
}
}
}else{
JSONObject json = new JSONObject();
json.put("resCode",0);
json.put("resMsg", "A执行脚本策略文件保存失败");
return UpMsgResponse.builder().clientId(clientId).dataType(MsgEnum.执行脚本策略应答.getValue()).content(json.toString()).build();
}
return null;
}
}
@AgentDispatcher(msgId = MsgEnum.Agent版本更新)
public class VersionUpdateHandler implements AgentHandler {
@Override
public UpMsgResponse upHandle(String data, String clientId) {
AgentVersionUpdateEO versionUpdateEO = JSON.parseObject(data, AgentVersionUpdateEO.class);
boolean isFalse = false;
if(StringUtils.isNotBlank(versionUpdateEO.getFileUrl()) && StringUtils.isNotBlank(versionUpdateEO.getFilePath())){
Java8FileDownloader.DownloadResult result = Java8FileDownloader.downloadFile(versionUpdateEO.getFileUrl(), versionUpdateEO.getFilePath(), "");
// 验证下载是否完成
if (result.isSuccess()) {
boolean isComplete = Java8FileDownloader.verifyFileCompletion(
result.getFilePath(), result.getExpectedSize());
if(isComplete){
isFalse = true;
System.out.println("Agent版本更新下载完成");
}else{
isFalse = false;
System.err.println("Agent版本更新下载失败");
}
}
}
//判断文件是否保存成功
if(isFalse){
if(CollectionUtil.isNotEmpty(versionUpdateEO.getCommands())){
//执行方式:0、立即执行;1、定时执行;
if(versionUpdateEO.getMethod() == 1){
SpecificTimeRequest request = SpecificTimeRequest.builder()
.taskId("")
.taskName("")
.taskData(versionUpdateEO.getCommands())
.specificDateTime(versionUpdateEO.getPolicyTime())
.clientId(clientId)
.dataType(MsgEnum.Agent版本更新应答.getValue())
.build();
try {
taskService.createSpecificTimeTask(request);
JSONObject json = new JSONObject();
json.put("resCode",1);
json.put("resMsg", "Agent版本更新定时任务保存成功");
// Message message = Message.builder().clientId(clientId).dataType(MsgEnum.Agent更新应答.getValue()).data(json.toString()).build();
return UpMsgResponse.builder().clientId(clientId).dataType(MsgEnum.Agent版本更新应答.getValue()).content(json.toString()).build();
} catch (Exception e) {
JSONObject json = new JSONObject();
json.put("resCode",0);
json.put("resMsg", "Agent版本更新定时任务保存失败");
// Message message = Message.builder().clientId(clientId).dataType(MsgEnum.Agent更新应答.getValue()).data(json.toString()).build();
return UpMsgResponse.builder().clientId(clientId).dataType(MsgEnum.Agent版本更新应答.getValue()).content(json.toString()).build();
}
}else{
List<Map<String,String>> list = new ArrayList<>();
for (String command : versionUpdateEO.getCommands()) {
Map<String,String> map = new HashMap<>();
List<String> cmd = Arrays.asList(command.split("\\s+"));
CompletableFuture<AsyncCommandExecutor.CommandResult> future =
AsyncCommandExecutor.executeCommandAsync(
cmd,
60, TimeUnit.SECONDS);
future.thenAccept(result -> {
if (result.isSuccess()) {
System.out.println("脚本执行成功");
System.out.println("[成功resOut] " + result.getOutput());
map.put("command",command);
map.put("resOut",result.getOutput());
} else {
System.out.println("脚本执行失败");
System.out.println("[失败resOut] " + result.getOutput());
map.put("command",command);
map.put("resOut",result.getOutput());
}
}).exceptionally(ex -> {
System.err.println("执行失败: " + ex.getMessage());
map.put("command",command);
map.put("resOut","Policy execute filed");
return null;
});
list.add(map);
}
if(CollectionUtil.isNotEmpty(list)){
JSONObject json = new JSONObject();
json.put("resCode",1);
json.put("resMsg", "");
json.put("result", AgentUtil.toJsonString(list));
// Message message = Message.builder().clientId(clientId).dataType(MsgEnum.Agent版本更新应答.getValue()).data(json.toString()).build();
return UpMsgResponse.builder().clientId(clientId).dataType(MsgEnum.Agent版本更新应答.getValue()).content(json.toString()).build();
}
}
}
}else{
JSONObject json = new JSONObject();
json.put("resCode",0);
json.put("resMsg", "Agent版本更新文件保存失败");
// Message message = Message.builder().clientId(clientId).dataType(MsgEnum.Agent版本更新应答.getValue()).data(json.toString()).build();
return UpMsgResponse.builder().clientId(clientId).dataType(MsgEnum.Agent版本更新应答.getValue()).content(json.toString()).build();
}
return null;
}
}
}
@@ -0,0 +1,141 @@
package com.tongran.agent.client.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.JSONObject;
import com.tongran.agent.client.core.session.SessionManager;
import com.tongran.agent.client.netty.annotation.AgentDispatcher;
import com.tongran.agent.client.netty.basics.AgentDispatcherManager;
import com.tongran.agent.client.netty.basics.AgentHandler;
import com.tongran.agent.client.netty.model.Message;
import com.tongran.agent.client.netty.model.UpMsgResponse;
import com.tongran.agent.client.utils.AssertLog;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.CharsetUtil;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.Objects;
@Component
@ChannelHandler.Sharable
public class AgentDecoderHandler extends ChannelInboundHandlerAdapter {
protected final SessionManager sessionManager;
@Resource
private AgentDispatcherManager agentDispatcherManager;
public AgentDecoderHandler() {
this.sessionManager = SessionManager.getInstance();
}
// 用来临时保留没有处理过的请求报文
private final Cache<String, String> lruCache = CacheUtil.newLRUCache(3000);
/**
* 消息解码器
*/
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
// 检查是否为 ByteBuf(未解码的原始数据)
if (msg instanceof ByteBuf) {
ByteBuf byteBuf = (ByteBuf) msg;
String messages = byteBuf.toString(CharsetUtil.UTF_8); // 指定字符集解码
AssertLog.info("<<[up1]:IP:{},[up-content]==>{}", sessionManager.client(ctx),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;
endsWith = content.endsWith("@tong-ran");
if(endsWith){
isClear = true;
}else{
lruCache.remove(sessionManager.client(ctx));
tmpMsgSize = 0;
lruCache.put(sessionManager.client(ctx), content, DateUnit.SECOND.getMillis() * 3000);
}
}
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");
AgentHandler msgHandler = agentDispatcherManager.getHandler(dataType + "&" + AgentDispatcher.VersionEnum.V1.value);
if (ObjectUtil.isNotEmpty(msgHandler)) {
UpMsgResponse response = msgHandler.upHandle(message, clientId );
AssertLog.info("<<[up-after-handle]:clientId:{},type={},[handle-content]={}", response.getClientId(), dataType, message);
if(Objects.nonNull(response)){
Message agentMessage = Message.builder().build();
agentMessage.setClientId(response.getClientId());
agentMessage.setDataType(response.getDataType());
agentMessage.setData(response.getContent());
ctx.fireChannelRead(agentMessage);//传递到下一个handler
}
}
}
}
if (isClear) {
lruCache.remove(sessionManager.client(ctx));
}
byteBuf.release(); // 释放 ByteBuf 资源(重要!)
} else {
System.out.println("Unexpected message type: " + msg.getClass());
}
}
//拆包
public byte[] subByte(byte[] b, int off, int length) {
byte[] bytes = new byte[length];
System.arraycopy(b, off, bytes, 0, length);
return bytes;
}
}
@@ -0,0 +1,38 @@
package com.tongran.agent.client.netty.handler;
import com.tongran.agent.client.core.session.Session;
import com.tongran.agent.client.core.session.SessionManager;
import com.tongran.agent.client.netty.model.Message;
import com.tongran.agent.client.utils.AssertLog;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
@Component
@ChannelHandler.Sharable
public class AgentDispatcherHandler extends SimpleChannelInboundHandler<Message> {
protected final SessionManager sessionManager;
public AgentDispatcherHandler() {
this.sessionManager = SessionManager.getInstance();
}
@Override
protected void channelRead0(ChannelHandlerContext ctx, Message msg) {
// AssertLog.info(">>>>>>[要处理的终端数据] {}", msg.toString());
String clientId = msg.getClientId();
if (StringUtils.isBlank(clientId)) {
AssertLog.error("<<<<<<错误的信息from:{}", ctx.channel().remoteAddress());
return;
}
Session session = Session.buildSession(ctx, clientId);
sessionManager.put(clientId, session);
if (StringUtils.isNotBlank(msg.getData())) {
// AssertLog.info(">>>>>>[getSessionById的终端数据] {}", sessionManager.getSessionById(clientId));
sessionManager.writeAndFlush(sessionManager.getSessionById(clientId).getChannel(), msg);
}
}
}
@@ -0,0 +1,49 @@
package com.tongran.agent.client.netty.handler;
import com.alibaba.fastjson2.JSON;
import com.tongran.agent.client.core.session.SessionManager;
import com.tongran.agent.client.netty.model.Message;
import com.tongran.agent.client.utils.AssertLog;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelOutboundHandlerAdapter;
import io.netty.channel.ChannelPromise;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import java.nio.charset.StandardCharsets;
@Component
@ChannelHandler.Sharable
public class AgentEncoderHandler extends ChannelOutboundHandlerAdapter {
protected final SessionManager sessionManager;
public AgentEncoderHandler() {
this.sessionManager = SessionManager.getInstance();
}
/**
* 消息解码器
*/
@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
if (msg instanceof Message) {
Message entity = (Message) msg;
if (StringUtils.isBlank(entity.getData())) {
AssertLog.error(">>[down]:IP:{},errorContent:{}", sessionManager.client(ctx), msg);
return;
}
AssertLog.info(">>[down]:IP:{},[content]==>{}", sessionManager.client(ctx), entity.getData());
// String json = JSON.toJSONString(entity);
String json = "agent-client:"+ JSON.toJSONString(entity)+"@tong-ran";
byte[] bytes = json.getBytes(StandardCharsets.UTF_8); // 显式指定 UTF-8
// byte[] bytes = EscapeUtil.hexStringToByteArray(entity.getContent());
ByteBuf buf = Unpooled.wrappedBuffer(bytes);
ctx.write(buf, promise);
}
}
}
@@ -0,0 +1,78 @@
package com.tongran.agent.client.netty.handler;
import com.tongran.agent.client.core.config.GlobalConfig;
import com.tongran.agent.client.core.session.SessionManager;
import com.tongran.agent.client.scheduler.service.AppInitializer;
import com.tongran.agent.client.utils.AssertLog;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.io.IOException;
/**
* @Description: TCP消息适配器
*/
@Component
@ChannelHandler.Sharable
public class TCPListenHandler extends ChannelInboundHandlerAdapter {
@Resource
private AppInitializer appInitializer;
private final SessionManager sessionManager;
public TCPListenHandler() {
this.sessionManager = SessionManager.getInstance();
}
@Override
public void channelActive(ChannelHandlerContext ctx) {
AssertLog.info("<<<<<<[终端连接]{}", ctx.channel().remoteAddress());
}
@Override
public void channelInactive(ChannelHandlerContext ctx) {
AssertLog.info(">>>>>>[断开连接]{}", sessionManager.client(ctx));
sessionManager.remove(ctx.channel());
try {
GlobalConfig.isCollect = false;
appInitializer.run();
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable e) {
if (e instanceof IOException) {
AssertLog.info("<<<<<<[终端断开连接]{} {}", sessionManager.client(ctx), e.getMessage());
} else {
AssertLog.info(">>>>>>[消息处理异常]" + sessionManager.client(ctx), e);
}
}
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) {
if (evt instanceof IdleStateEvent) {
IdleStateEvent event = (IdleStateEvent) evt;
IdleState state = event.state();
if (state == IdleState.READER_IDLE || state == IdleState.WRITER_IDLE || state == IdleState.ALL_IDLE) {
AssertLog.warn(">>>>>>[终端心跳超时]{} {}", state, sessionManager.client(ctx));
sessionManager.remove(ctx.channel());
ctx.close();
try {
GlobalConfig.isCollect = false;
appInitializer.run();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
@@ -0,0 +1,24 @@
package com.tongran.agent.client.netty.handler;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.socket.DatagramPacket;
import org.springframework.stereotype.Component;
/**
* @Description: UDP消息适配器
*/
@Component
@ChannelHandler.Sharable
public class UDPListenHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
DatagramPacket packet = (DatagramPacket) msg;
ByteBuf buf = packet.content();
buf.clear();// 暂未实现
}
}
@@ -0,0 +1,37 @@
package com.tongran.agent.client.netty.model;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
/**
* @author egrias
*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class Message implements Serializable {
private static final long serialVersionUID = -1267013167162440610L;
/**
* clientId
*/
private String clientId;
/**
* 数据类型:LOGIN、HEARTBEAT、CPU、MEMORY、SYSTEM、POINT、NET、DISK、DOCKER、SWITCHBOARD
*/
private String dataType;
/**
* 发送内容
*/
private String data;
}
@@ -0,0 +1,24 @@
package com.tongran.agent.client.netty.model;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
@Data
@SuperBuilder
@AllArgsConstructor
@NoArgsConstructor
public class UpMsgResponse {
private String clientId;// 终端号
/**
* type
*/
private String dataType;
private String content;
}