Compare commits

15 Commits

Author SHA1 Message Date
gaoyutao 81181869da 区分配置文件 2026-03-30 17:59:48 +08:00
gaoyutao b52f56e2e9 新增业务流量上报 2026-03-10 17:51:35 +08:00
gaoyutao 7bd91aa14f 优化tcp解码 2026-03-03 17:46:11 +08:00
gaoyutao 5244f1d09b 流量补全消息改为顺序上报 2026-02-28 18:00:56 +08:00
gaoyutao 816f833ff5 增加采集内存详情方法 2026-02-25 17:54:48 +08:00
gaoyutao 9b395b2be7 定位并解决中文乱码问题 2026-01-27 19:09:21 +08:00
gaoyutao d579f85eee 增加网络上报重试,和tcpdump结果上报 2026-01-22 17:58:31 +08:00
gaoyutao f9b9f06390 增加磁盘IOPS上报方法 2026-01-15 18:15:37 +08:00
gaoyutao 75c5db6f91 增加pppoe状态上报 2025-12-30 18:21:52 +08:00
gaoyutao 8950c0ce3c 增加nett传输日志 2025-12-26 18:31:27 +08:00
gaoyutao 5fd0e913ae 增加frpc响应处理 2025-12-22 18:54:47 +08:00
qiminbao 2017bfe437 数据格式优化 2025-10-30 22:06:09 +08:00
qiminbao c8980142d8 增加多网IP探测上报 2025-10-28 14:26:41 +08:00
qiminbao a26981a92e v1.1初始化 2025-10-27 10:26:29 +08:00
qiminbao eb53489e90 v1.1初始化 2025-10-21 14:21:54 +08:00
18 changed files with 747 additions and 698 deletions
+46
View File
@@ -0,0 +1,46 @@
######################################################################
# Build Tools
.gradle
/build/
!gradle/wrapper/gradle-wrapper.jar
target/
!.mvn/wrapper/maven-wrapper.jar
######################################################################
# IDE
### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
### JRebel ###
rebel.xml
### NetBeans ###
nbproject/private/
build/*
nbbuild/
dist/
nbdist/
.nb-gradle/
######################################################################
# Others
*.log
*.xml.versionsBackup
*.swp
!*/build/*.java
!*/build/*.html
!*/build/*.xml
@@ -1,24 +1,13 @@
package com.tongran.agent.server.controller; package com.tongran.agent.server.controller;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.tongran.agent.server.core.enums.MsgEnum;
import com.tongran.agent.server.core.session.SessionManager; import com.tongran.agent.server.core.session.SessionManager;
import com.tongran.agent.server.netty.MultiTargetNettyClient;
import com.tongran.agent.server.netty.annotation.AgentDispatcher;
import com.tongran.agent.server.netty.basics.AgentDispatcherManager; import com.tongran.agent.server.netty.basics.AgentDispatcherManager;
import com.tongran.agent.server.netty.basics.AgentHandler;
import com.tongran.agent.server.netty.model.Message;
import com.tongran.agent.server.rockermq.AgentProducer; import com.tongran.agent.server.rockermq.AgentProducer;
import com.tongran.agent.server.rockermq.model.MqMsg;
import com.tongran.agent.server.utils.AssertLog; import com.tongran.agent.server.utils.AssertLog;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.util.Objects;
@Slf4j @Slf4j
@Service @Service
@@ -28,8 +17,8 @@ public class TcpService {
@Resource @Resource
private AgentDispatcherManager agentDispatcherManager; private AgentDispatcherManager agentDispatcherManager;
@Resource // @Resource
private MultiTargetNettyClient client; // private MultiTargetNettyClient client;
@Resource @Resource
private AgentProducer agentProducer; private AgentProducer agentProducer;
@@ -41,88 +30,88 @@ public class TcpService {
public void create(String clientId){ public void create(String clientId){
boolean success1 = false; boolean success1 = false;
// 动态创建多个不同IP和端口的连接 // 动态创建多个不同IP和端口的连接
if(StringUtils.equals(clientId, "clientId007")){ // if(StringUtils.equals(clientId, "clientId007")){
success1 = client.createConnection(clientId, "127.0.0.1", 6610, 5); // success1 = client.createConnection(clientId, "127.0.0.1", 6610, 5);
}else{ // }else{
success1 = client.createConnection(clientId, "127.0.0.1", 6710, 5); // success1 = client.createConnection(clientId, "127.0.0.1", 6710, 5);
} // }
System.out.println("当前活跃连接数: " + client.getActiveConnections()); // System.out.println("当前活跃连接数: " + client.getActiveConnections());
if(success1){ // if(success1){
// 连接建立后可以发送登录认证消息 // // 连接建立后可以发送登录认证消息
long timestamp = System.currentTimeMillis(); // long timestamp = System.currentTimeMillis();
JSONObject object = new JSONObject(); // JSONObject object = new JSONObject();
object.put("uuid",clientId); // object.put("uuid",clientId);
object.put("timestamp",timestamp); // object.put("timestamp",timestamp);
Message message = Message.builder().clientId(clientId).dataType("CREATE").data(object.toString()).build(); // Message message = Message.builder().clientId(clientId).dataType("CREATE").data(object.toString()).build();
// 将对象转为 JSON 字符串 // // 将对象转为 JSON 字符串
String json = JSON.toJSONString(message); // String json = JSON.toJSONString(message);
System.out.println("连接建立成功,发送登录认证="+json); // System.out.println("连接建立成功,发送登录认证="+json);
client.sendMessages(clientId, message); // client.sendMessages(clientId, message);
} // }
} }
public void sendMessage(String message){ public void sendMessage(String message){
AssertLog.info("consumer==> received down message: {}", message); AssertLog.info("consumer==> received down message: {}", message);
try { // try {
Message dto = JSON.parseObject(message, Message.class); // Message dto = JSON.parseObject(message, Message.class);
if(StringUtils.equals(dto.getDataType(), MsgEnum.注册.getValue())){ // if(StringUtils.equals(dto.getDataType(), MsgEnum.注册.getValue())){
JSONObject jsonObject = JSONObject.parseObject(dto.getData()); // JSONObject jsonObject = JSONObject.parseObject(dto.getData());
String clientIp = ""; // String clientIp = "";
int clientPort = 0; // int clientPort = 0;
String switchBoard = ""; // String switchBoard = "";
long timestamp = 0; // long timestamp = 0;
if(jsonObject.containsKey("clientIp")){ // if(jsonObject.containsKey("clientIp")){
clientIp = jsonObject.getString("clientIp"); // clientIp = jsonObject.getString("clientIp");
} // }
if(jsonObject.containsKey("clientPort")){ // if(jsonObject.containsKey("clientPort")){
clientPort = jsonObject.getInteger("clientPort"); // clientPort = jsonObject.getInteger("clientPort");
} // }
if(jsonObject.containsKey("switchBoard")){ // if(jsonObject.containsKey("switchBoard")){
switchBoard = jsonObject.getString("switchBoard"); // switchBoard = jsonObject.getString("switchBoard");
} // }
if(jsonObject.containsKey("timestamp")){ // if(jsonObject.containsKey("timestamp")){
timestamp = jsonObject.getLongValue("timestamp"); // timestamp = jsonObject.getLongValue("timestamp");
} // }
if(StringUtils.isBlank(clientIp) || clientPort == 0){ // if(StringUtils.isBlank(clientIp) || clientPort == 0){
//返回提醒ip 端口 空 // //返回提醒ip 端口 空
JSONObject json = new JSONObject(); // JSONObject json = new JSONObject();
json.put("clientId",dto.getClientId()); // json.put("clientId",dto.getClientId());
json.put("dataType",MsgEnum.注册应答.getValue()); // json.put("dataType",MsgEnum.注册应答.getValue());
JSONObject dataJson = new JSONObject(); // JSONObject dataJson = new JSONObject();
dataJson.put("resCode",0); // dataJson.put("resCode",0);
dataJson.put("resMag","客户端IP、端口不能为空"); // dataJson.put("resMag","客户端IP、端口不能为空");
dataJson.put("timestamp", System.currentTimeMillis()); // dataJson.put("timestamp", System.currentTimeMillis());
json.put("data",dataJson.toString()); // json.put("data",dataJson.toString());
MqMsg mqMsg = MqMsg.builder().topic("tongran_agent_down").content(json.toString()).build(); // MqMsg mqMsg = MqMsg.builder().topic("tongran_agent_down").content(json.toString()).build();
agentProducer.asyncSend(mqMsg); // agentProducer.asyncSend(mqMsg);
return; // return;
} // }
boolean success = client.createConnection(dto.getClientId(), clientIp, clientPort, 5); // boolean success = client.createConnection(dto.getClientId(), clientIp, clientPort, 5);
if(success){ // if(success){
//连接成功 // //连接成功
// 连接建立后可以发送登录认证消息 // // 连接建立后可以发送登录认证消息
JSONObject object = new JSONObject(); // JSONObject object = new JSONObject();
object.put("timestamp", timestamp); // object.put("timestamp", timestamp);
if(StringUtils.isNotBlank(switchBoard)){ // if(StringUtils.isNotBlank(switchBoard)){
object.put("switchBoard",switchBoard); // object.put("switchBoard",switchBoard);
} // }
Message msg = Message.builder().clientId(dto.getClientId()).dataType(MsgEnum.注册.getValue()).data(object.toString()).build(); // Message msg = Message.builder().clientId(dto.getClientId()).dataType(MsgEnum.注册.getValue()).data(object.toString()).build();
// 将对象转为 JSON 字符串 // // 将对象转为 JSON 字符串
client.sendMessages(dto.getClientId(), msg); // client.sendMessages(dto.getClientId(), msg);
} // }
} else { // } else {
AgentHandler msgHandler = agentDispatcherManager.getHandler(dto.getDataType() + "&" + AgentDispatcher.VersionEnum.V1.value); // AgentHandler msgHandler = agentDispatcherManager.getHandler(dto.getDataType() + "&" + AgentDispatcher.VersionEnum.V1.value);
if (ObjectUtil.isNotEmpty(msgHandler)) { // if (ObjectUtil.isNotEmpty(msgHandler)) {
String msg = msgHandler.downHandle(dto.getData(), dto.getClientId()); // String msg = msgHandler.downHandle(dto.getData(), dto.getClientId());
Message chargeMessage = Message.builder().clientId(dto.getClientId()).dataType(dto.getDataType()).data(msg).build(); // Message chargeMessage = Message.builder().clientId(dto.getClientId()).dataType(dto.getDataType()).data(msg).build();
if (Objects.nonNull(sessionManager.getSessionById(dto.getClientId()))) { // if (Objects.nonNull(sessionManager.getSessionById(dto.getClientId()))) {
sessionManager.writeAndFlush(sessionManager.getSessionById(dto.getClientId()).getChannel(), chargeMessage); // sessionManager.writeAndFlush(sessionManager.getSessionById(dto.getClientId()).getChannel(), chargeMessage);
} // }
} // }
} // }
} catch (Exception e) { // } catch (Exception e) {
AssertLog.error("=====rocketmq-exception{}=====" + e.getMessage()); // AssertLog.error("=====rocketmq-exception{}=====" + e.getMessage());
} // }
} }
@@ -11,11 +11,18 @@ import lombok.NoArgsConstructor;
@AllArgsConstructor @AllArgsConstructor
@NoArgsConstructor @NoArgsConstructor
public enum MsgEnum { public enum MsgEnum {
建立连接("CONNECT"),
建立连接应答("CONNECT_RSP"),
注册("REGISTER"), 注册("REGISTER"),
注册应答("REGISTER_RSP"), 注册应答("REGISTER_RSP"),
获取最新策略("GET_POLICY"),
获取最新策略应答("GET_POLICY_RSP"),
断开("DISCONNECT"), 断开("DISCONNECT"),
断开应答("DISCONNECT_RSP"), 断开应答("DISCONNECT_RSP"),
@@ -34,6 +41,10 @@ public enum MsgEnum {
网络上报("NET"), 网络上报("NET"),
网络上报重试("NET_RECOVER"),
业务网络上报("BUSINESS_NET"),
挂载上报("POINT"), 挂载上报("POINT"),
系统其他上报("OTHER_SYSTEM"), 系统其他上报("OTHER_SYSTEM"),
@@ -68,7 +79,19 @@ public enum MsgEnum {
Agent版本更新("AGENT_VERSION_UPDATE"), Agent版本更新("AGENT_VERSION_UPDATE"),
Agent版本更新应答("AGENT_VERSION_UPDATE_RSP"); Agent版本更新应答("AGENT_VERSION_UPDATE_RSP"),
修改frp配置文件应答("UPDATE_FRP_RSP"),
macvlan状态上报("MACVLAN_STATUS_RSP"),
内存详情上报("MEMORY_DETAILS"),
iops结果上报("IOPS_RESULT"),
tcpdump结果上报("TCPDUMP_RESULT"),
多网IP探测上报("NETWORK_DETECT");
private String value; private String value;
@@ -0,0 +1,22 @@
package com.tongran.agent.server.netty;
import com.tongran.agent.server.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,56 @@
package com.tongran.agent.server.netty;
import com.tongran.agent.server.netty.handler.AgentDecoderHandler;
import com.tongran.agent.server.netty.handler.AgentDispatcherHandler;
import com.tongran.agent.server.netty.handler.AgentEncoderHandler;
import com.tongran.agent.server.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);
}
}
@@ -1,301 +0,0 @@
package com.tongran.agent.server.netty;
import com.tongran.agent.server.netty.handler.AgentDecoderHandler;
import com.tongran.agent.server.netty.handler.AgentDispatcherHandler;
import com.tongran.agent.server.netty.handler.AgentEncoderHandler;
import com.tongran.agent.server.netty.model.Message;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.util.AttributeKey;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@Component
public class MultiTargetNettyClient {
// 连接标识属性键
private static final AttributeKey<String> CONNECTION_KEY = AttributeKey.valueOf("connectionKey");
// 连接管理器
private final Map<String, Channel> connectionMap = new ConcurrentHashMap<>();
private final Map<Channel, String> reverseConnectionMap = new ConcurrentHashMap<>();
private EventLoopGroup workerGroup;
@Resource
private AgentDecoderHandler decoderHandler;
@Resource
private AgentEncoderHandler encoderHandler;
@Resource
private AgentDispatcherHandler dispatcherHandler;
/**
* 动态创建TCP连接
* @param connectionKey 连接唯一标识(格式:ip:port 或自定义)
* @param host 目标主机
* @param port 目标端口
* @param timeout 超时时间(秒)
* @return 是否创建成功
*/
public boolean createConnection(String connectionKey, String host, int port, int timeout) {
if (workerGroup == null) {
workerGroup = new NioEventLoopGroup();
}
if (connectionMap.containsKey(connectionKey)) {
System.out.println("连接已存在: " + connectionKey);
return true;
}
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(workerGroup)
.channel(NioSocketChannel.class)
.option(ChannelOption.TCP_NODELAY, true)
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, timeout * 1000)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) {
ChannelPipeline pipeline = ch.pipeline();
// 添加编解码器
pipeline.addLast(decoderHandler);
pipeline.addLast(encoderHandler);
// 添加心跳机制
// pipeline.addLast(new IdleStateHandler(0, 30, 0, TimeUnit.SECONDS));
// 添加自定义处理器
pipeline.addLast(dispatcherHandler);
}
});
try {
CountDownLatch latch = new CountDownLatch(1);
final boolean[] success = {false};
bootstrap.connect(host, port).addListener((ChannelFuture future) -> {
if (future.isSuccess()) {
Channel channel = future.channel();
connectionMap.put(connectionKey, channel);
reverseConnectionMap.put(channel, connectionKey);
success[0] = true;
System.out.println("连接建立成功: " + connectionKey + " -> " + host + ":" + port);
} else {
System.err.println("连接建立失败: " + connectionKey + " - " + future.cause().getMessage());
success[0] = false;
}
latch.countDown();
});
// 等待连接结果
return latch.await(timeout, TimeUnit.SECONDS) && success[0];
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return false;
}
}
/**
* 向指定连接发送消息
* @param connectionKey 连接标识
* @param message 消息内容
* @return 是否发送成功
*/
public boolean sendMessage(String connectionKey, String message) {
Channel channel = connectionMap.get(connectionKey);
if (channel == null) {
System.err.println("连接不存在: " + connectionKey);
return false;
}
if (!channel.isActive()) {
System.err.println("连接已断开: " + connectionKey);
removeConnection(connectionKey);
return false;
}
try {
ChannelFuture future = channel.writeAndFlush(message).sync();
System.out.println("消息发送成功到: " + connectionKey + " - " + message);
return future.isSuccess();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return false;
}
}
public boolean sendMessages(String connectionKey, Message message) {
Channel channel = connectionMap.get(connectionKey);
if (channel == null) {
System.err.println("连接不存在: " + connectionKey);
return false;
}
if (!channel.isActive()) {
System.err.println("连接已断开: " + connectionKey);
removeConnection(connectionKey);
return false;
}
try {
ChannelFuture future = channel.writeAndFlush(message).sync();
System.out.println("消息发送成功到: " + connectionKey + " - " + message);
return future.isSuccess();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return false;
}
}
/**
* 批量发送消息到不同连接
* @param messages Map<连接标识, 消息内容>
*/
public void sendMessages(Map<String, String> messages) {
messages.forEach((connectionKey, message) -> {
new Thread(() -> {
if (sendMessage(connectionKey, message)) {
System.out.println("批量发送成功: " + connectionKey);
} else {
System.err.println("批量发送失败: " + connectionKey);
}
}).start();
});
}
/**
* 关闭指定连接
* @param connectionKey 连接标识
*/
public void closeConnection(String connectionKey) {
Channel channel = connectionMap.get(connectionKey);
if (channel != null) {
channel.close();
removeConnection(connectionKey);
System.out.println("连接已关闭: " + connectionKey);
}
}
/**
* 移除连接记录
* @param connectionKey 连接标识
*/
private void removeConnection(String connectionKey) {
Channel channel = connectionMap.remove(connectionKey);
if (channel != null) {
reverseConnectionMap.remove(channel);
}
}
/**
* 关闭所有连接
*/
public void shutdown() {
// 关闭所有连接
connectionMap.forEach((key, channel) -> {
if (channel.isActive()) {
channel.close();
}
});
connectionMap.clear();
reverseConnectionMap.clear();
// 关闭EventLoopGroup
if (workerGroup != null) {
workerGroup.shutdownGracefully();
}
System.out.println("所有连接已关闭");
}
/**
* 获取连接状态
* @return 当前活跃连接数
*/
public int getActiveConnections() {
return (int) connectionMap.values().stream()
.filter(Channel::isActive)
.count();
}
// /**
// * 客户端处理器
// */
// private class ClientHandler extends SimpleChannelInboundHandler<String> {
// @Override
// protected void channelRead0(ChannelHandlerContext ctx, String msg) {
// String connectionKey = ctx.channel().attr(CONNECTION_KEY).get();
// System.out.println("收到来自 " + connectionKey + " 的消息: " + msg);
// }
//
// @Override
// public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
// String connectionKey = ctx.channel().attr(CONNECTION_KEY).get();
// System.err.println("连接 " + connectionKey + " 发生异常: " + cause.getMessage());
// ctx.close();
// }
//
// @Override
// public void channelInactive(ChannelHandlerContext ctx) {
// String connectionKey = ctx.channel().attr(CONNECTION_KEY).get();
// System.out.println("连接断开: " + connectionKey);
// removeConnection(connectionKey);
// }
// }
/**
* 使用示例
*/
public static void main(String[] args) throws InterruptedException {
MultiTargetNettyClient client = new MultiTargetNettyClient();
try {
// 动态创建多个不同IP和端口的连接
boolean success1 = client.createConnection("server1", "127.0.0.1", 8080, 5);
boolean success2 = client.createConnection("server2", "192.168.1.100", 8081, 5);
boolean success3 = client.createConnection("server3", "10.0.0.50", 8082, 5);
// 等待连接建立
Thread.sleep(2000);
if (success1) {
client.sendMessage("server1", "Hello Server 1 from 8080");
}
if (success2) {
client.sendMessage("server2", "Hello Server 2 from 8081");
}
if (success3) {
client.sendMessage("server3", "Hello Server 3 from 8082");
}
// // 批量发送示例
// Map<String, String> batchMessages = Map.of(
// "server1", "Batch message to server 1",
// "server2", "Batch message to server 2",
// "server3", "Batch message to server 3"
// );
//
// client.sendMessages(batchMessages);
// 保持运行一段时间
Thread.sleep(5000);
System.out.println("当前活跃连接数: " + client.getActiveConnections());
} finally {
client.shutdown();
}
}
public Channel get(String connectionKey){
Channel channel = connectionMap.get(connectionKey);
return channel;
}
}
@@ -2,7 +2,6 @@ package com.tongran.agent.server.netty.enpoint;
import com.alibaba.fastjson2.JSONObject; import com.alibaba.fastjson2.JSONObject;
import com.tongran.agent.server.core.enums.MsgEnum; import com.tongran.agent.server.core.enums.MsgEnum;
import com.tongran.agent.server.netty.MultiTargetNettyClient;
import com.tongran.agent.server.netty.annotation.AgentDispatcher; import com.tongran.agent.server.netty.annotation.AgentDispatcher;
import com.tongran.agent.server.netty.basics.AgentHandler; import com.tongran.agent.server.netty.basics.AgentHandler;
import com.tongran.agent.server.netty.model.UpMsgResponse; import com.tongran.agent.server.netty.model.UpMsgResponse;
@@ -15,8 +14,6 @@ import javax.annotation.Resource;
@Component @Component
public class AgentEndpoint { public class AgentEndpoint {
@Resource
private MultiTargetNettyClient client;
@Resource @Resource
private AgentMqConfig agentMqConfig; private AgentMqConfig agentMqConfig;
@@ -24,20 +21,66 @@ public class AgentEndpoint {
@Resource @Resource
private AgentProducer agentProducer; private AgentProducer agentProducer;
@AgentDispatcher(msgId = MsgEnum.注册应答) @AgentDispatcher(msgId = MsgEnum.建立连接)
public class RegisterRspHandler implements AgentHandler { public class ConnectHandler implements AgentHandler {
@Override
public UpMsgResponse upHandle(String data, String clientId) {
JSONObject json = new JSONObject();
json.put("resCode",1);
return UpMsgResponse.builder().clientId(clientId).dataType(MsgEnum.建立连接应答.getValue())
.content(json.toString()).build();
}
}
@AgentDispatcher(msgId = MsgEnum.建立连接应答)
public class ConnectRspHandler implements AgentHandler {
@Override
public UpMsgResponse upHandle(String data, String clientId) {
return null;
}
}
@AgentDispatcher(msgId = MsgEnum.注册)
public class RegisterHandler implements AgentHandler {
@Override @Override
public UpMsgResponse upHandle(String data, String clientId) { public UpMsgResponse upHandle(String data, String clientId) {
JSONObject jsonObject = new JSONObject(); JSONObject jsonObject = new JSONObject();
jsonObject.put("clientId", clientId); jsonObject.put("clientId", clientId);
jsonObject.put("dataType", MsgEnum.注册应答.getValue()); jsonObject.put("dataType", MsgEnum.注册.getValue());
jsonObject.put("data", data); jsonObject.put("data", data);
MqMsg mqMsg = MqMsg.builder().topic(agentMqConfig.getAgentTopic()).content(jsonObject.toString()).build(); MqMsg mqMsg = MqMsg.builder().topic(agentMqConfig.getAgentTopic()).content(jsonObject.toString()).build();
agentProducer.asyncSend(mqMsg); agentProducer.asyncSend(mqMsg);
JSONObject json = new JSONObject(); return null;
json.put("resCode",1); }
return UpMsgResponse.builder().clientId(clientId).dataType(MsgEnum.注册应答.getValue()) }
.content(json.toString()).build();
@AgentDispatcher(msgId = MsgEnum.注册应答)
public class RegisterRspHandler implements AgentHandler {
@Override
public String downHandle(String data, String clientId) {
return data;
}
}
@AgentDispatcher(msgId = MsgEnum.获取最新策略)
public class GetPolicyHandler implements AgentHandler {
@Override
public UpMsgResponse upHandle(String data, String clientId) {
JSONObject jsonObject = new JSONObject();
jsonObject.put("clientId", clientId);
jsonObject.put("dataType", MsgEnum.获取最新策略.getValue());
jsonObject.put("data", data);
MqMsg mqMsg = MqMsg.builder().topic(agentMqConfig.getAgentTopic()).content(jsonObject.toString()).build();
agentProducer.asyncSend(mqMsg);
return null;
}
}
@AgentDispatcher(msgId = MsgEnum.获取最新策略应答)
public class GetPolicyRspHandler implements AgentHandler {
@Override
public String downHandle(String data, String clientId) {
return data;
} }
} }
@@ -64,9 +107,9 @@ public class AgentEndpoint {
if(json.containsKey("resCode")){ if(json.containsKey("resCode")){
resCode = json.getInteger("resCode"); resCode = json.getInteger("resCode");
} }
if(resCode == 1){ // if(resCode == 1){
client.closeConnection(clientId); // client.closeConnection(clientId);
} // }
return UpMsgResponse.builder().clientId(clientId).build(); return UpMsgResponse.builder().clientId(clientId).build();
} }
} }
@@ -171,7 +214,39 @@ public class AgentEndpoint {
.content(json.toString()).build(); .content(json.toString()).build();
} }
} }
@AgentDispatcher(msgId = MsgEnum.网络上报重试)
public class NetRecoverHandler implements AgentHandler {
@Override
public UpMsgResponse upHandle(String data, String clientId) {
JSONObject jsonObject = new JSONObject();
jsonObject.put("clientId", clientId);
jsonObject.put("dataType", MsgEnum.网络上报重试.getValue());
jsonObject.put("data", data);
MqMsg mqMsg = MqMsg.builder().topic(agentMqConfig.getAgentTopic()).content(jsonObject.toString()).build();
agentProducer.sendOrderly(mqMsg, clientId);
JSONObject json = new JSONObject();
json.put("resCode",1);
return UpMsgResponse.builder().clientId(clientId).dataType(MsgEnum.采集上报应答.getValue())
.content(json.toString()).build();
}
}
@AgentDispatcher(msgId = MsgEnum.业务网络上报)
public class BusinessNetHandler implements AgentHandler {
@Override
public UpMsgResponse upHandle(String data, String clientId) {
JSONObject jsonObject = new JSONObject();
jsonObject.put("clientId", clientId);
jsonObject.put("dataType", MsgEnum.业务网络上报.getValue());
jsonObject.put("data", data);
MqMsg mqMsg = MqMsg.builder().topic(agentMqConfig.getAgentTopic()).content(jsonObject.toString()).build();
agentProducer.asyncSend(mqMsg);
JSONObject json = new JSONObject();
json.put("resCode",1);
return UpMsgResponse.builder().clientId(clientId).dataType(MsgEnum.采集上报应答.getValue())
.content(json.toString()).build();
}
}
@AgentDispatcher(msgId = MsgEnum.挂载上报) @AgentDispatcher(msgId = MsgEnum.挂载上报)
public class PointHandler implements AgentHandler { public class PointHandler implements AgentHandler {
@Override @Override
@@ -350,14 +425,94 @@ public class AgentEndpoint {
if(json.containsKey("resCode")){ if(json.containsKey("resCode")){
resCode = json.getInteger("resCode"); resCode = json.getInteger("resCode");
} }
if(resCode == 1){ // if(resCode == 1){
client.closeConnection(clientId); // client.closeConnection(clientId);
} // }
return null;
}
}
@AgentDispatcher(msgId = MsgEnum.多网IP探测上报)
public class DetectNetworkHandler implements AgentHandler {
@Override
public UpMsgResponse upHandle(String data, String clientId) {
JSONObject jsonObject = new JSONObject();
jsonObject.put("clientId", clientId);
jsonObject.put("dataType", MsgEnum.多网IP探测上报.getValue());
jsonObject.put("data", data);
MqMsg mqMsg = MqMsg.builder().topic(agentMqConfig.getAgentTopic()).content(jsonObject.toString()).build();
agentProducer.asyncSend(mqMsg);
return null;
}
}
@AgentDispatcher(msgId = MsgEnum.修改frp配置文件应答)
public class UpdateFrpHandler implements AgentHandler {
@Override
public UpMsgResponse upHandle(String data, String clientId) {
JSONObject jsonObject = new JSONObject();
jsonObject.put("clientId", clientId);
jsonObject.put("dataType", MsgEnum.修改frp配置文件应答.getValue());
jsonObject.put("data", data);
MqMsg mqMsg = MqMsg.builder().topic(agentMqConfig.getAgentTopic()).content(jsonObject.toString()).build();
agentProducer.asyncSend(mqMsg);
return null;
}
}
@AgentDispatcher(msgId = MsgEnum.macvlan状态上报)
public class upMacvlanStatusHandler implements AgentHandler {
@Override
public UpMsgResponse upHandle(String data, String clientId) {
JSONObject jsonObject = new JSONObject();
jsonObject.put("clientId", clientId);
jsonObject.put("dataType", MsgEnum.macvlan状态上报.getValue());
jsonObject.put("data", data);
MqMsg mqMsg = MqMsg.builder().topic(agentMqConfig.getAgentTopic()).content(jsonObject.toString()).build();
agentProducer.asyncSend(mqMsg);
return null;
}
}
@AgentDispatcher(msgId = MsgEnum.iops结果上报)
public class iopsResultHandler implements AgentHandler {
@Override
public UpMsgResponse upHandle(String data, String clientId) {
JSONObject jsonObject = new JSONObject();
jsonObject.put("clientId", clientId);
jsonObject.put("dataType", MsgEnum.iops结果上报.getValue());
jsonObject.put("data", data);
MqMsg mqMsg = MqMsg.builder().topic(agentMqConfig.getAgentTopic()).content(jsonObject.toString()).build();
agentProducer.asyncSend(mqMsg);
return null;
}
}
@AgentDispatcher(msgId = MsgEnum.tcpdump结果上报)
public class tcpdumpResultHandler implements AgentHandler {
@Override
public UpMsgResponse upHandle(String data, String clientId) {
JSONObject jsonObject = new JSONObject();
jsonObject.put("clientId", clientId);
jsonObject.put("dataType", MsgEnum.tcpdump结果上报.getValue());
jsonObject.put("data", data);
MqMsg mqMsg = MqMsg.builder().topic(agentMqConfig.getAgentTopic()).content(jsonObject.toString()).build();
agentProducer.asyncSend(mqMsg);
return null;
}
}
@AgentDispatcher(msgId = MsgEnum.内存详情上报)
public class MemoryDetailsHandler implements AgentHandler {
@Override
public UpMsgResponse upHandle(String data, String clientId) {
JSONObject jsonObject = new JSONObject();
jsonObject.put("clientId", clientId);
jsonObject.put("dataType", MsgEnum.内存详情上报.getValue());
jsonObject.put("data", data);
MqMsg mqMsg = MqMsg.builder().topic(agentMqConfig.getAgentTopic()).content(jsonObject.toString()).build();
agentProducer.asyncSend(mqMsg);
return null; return null;
} }
} }
} }
@@ -21,6 +21,9 @@ import io.netty.util.CharsetUtil;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects; import java.util.Objects;
@Component @Component
@@ -40,121 +43,181 @@ public class AgentDecoderHandler extends ChannelInboundHandlerAdapter {
AgentDispatcherManager agentDispatcherManager; AgentDispatcherManager agentDispatcherManager;
// 用来临时保留没有处理过的请求报文 // 用来临时保留没有处理过的请求报文
private final Cache<String, String> lruCache = CacheUtil.newLRUCache(5000); private final Cache<String, byte[]> byteCache = CacheUtil.newLRUCache(5000);
/** /**
* 消息解码器 * 消息解码器
*/ */
@Override @Override
public void channelRead(ChannelHandlerContext ctx, Object msg) { public void channelRead(ChannelHandlerContext ctx, Object msg) {
// 检查是否为 ByteBuf(未解码的原始数据)
if (msg instanceof ByteBuf) { if (msg instanceof ByteBuf) {
ByteBuf byteBuf = (ByteBuf) msg; ByteBuf byteBuf = (ByteBuf) msg;
String messages = byteBuf.toString(CharsetUtil.UTF_8); // 指定字符集解码 boolean released = false; // 标记是否已释放
AssertLog.info("<<[up1]:[up-content]==>{}", messages); try {
// 使用字节数组读取
byte[] bytes = new byte[byteBuf.readableBytes()];
byteBuf.readBytes(bytes);
String content = ""; String clientKey = sessionManager.client(ctx);
StringBuilder sb = new StringBuilder();
boolean isClear = false;
String tempMsg = lruCache.get(sessionManager.client(ctx));
tempMsg = tempMsg == null ? "" : tempMsg;
int tmpMsgSize = tempMsg.length();
boolean startsWith = messages.startsWith("agent-client:"); // 1. 获取缓存的字节数组
boolean endsWith = messages.endsWith("@tong-ran"); byte[] cachedBytes = byteCache.get(clientKey);
String ms = messages.replaceAll("agent-client:","");
String[] arr_msg = ms.split("@tong-ran"); // 2. 合并字节数组
if(startsWith){ byte[] mergedBytes;
//判定是否整包 if (cachedBytes != null && cachedBytes.length > 0) {
if(arr_msg.length == 1){ mergedBytes = new byte[cachedBytes.length + bytes.length];
//判定是否拆包 System.arraycopy(cachedBytes, 0, mergedBytes, 0, cachedBytes.length);
if(endsWith){ System.arraycopy(bytes, 0, mergedBytes, cachedBytes.length, bytes.length);
lruCache.remove(sessionManager.client(ctx)); // 合并后清除旧缓存
tmpMsgSize = 0; byteCache.remove(clientKey);
// sb.append(arr_msg[0]+"@tong-ran"); } else {
content = arr_msg[0]+"@tong-ran"; mergedBytes = bytes;
}else{ }
lruCache.remove(sessionManager.client(ctx));
tmpMsgSize = 0; // 3. 尝试解码
// sb.append(arr_msg[0]); String rawMessage = new String(mergedBytes, StandardCharsets.UTF_8);
content = arr_msg[0];
lruCache.put(sessionManager.client(ctx), content, DateUnit.SECOND.getMillis() * 5000); // 4. 检查是否有乱码
boolean hasGarbledChars = false;
for (int i = 0; i < rawMessage.length(); i++) {
if (rawMessage.charAt(i) == '\ufffd') {
hasGarbledChars = true;
break;
} }
} }
//判定是否粘包
if(arr_msg.length > 1){ if (hasGarbledChars) {
if(!endsWith){ // 有乱码,缓存字节数组等待后续数据
lruCache.remove(sessionManager.client(ctx)); byteCache.put(clientKey, mergedBytes, DateUnit.SECOND.getMillis() * 5000);
tmpMsgSize = 0;
for (int i = 0; i < arr_msg.length; i++) { // 打印调试信息
if(i == arr_msg.length -1){ int garbledIndex = rawMessage.indexOf('\ufffd');
lruCache.put(sessionManager.client(ctx), arr_msg[i], DateUnit.SECOND.getMillis() * 5000); AssertLog.info("<<[up1]:[bytes-cached]==>长度: {}, 乱码位置: {}, 预览: {}",
}else{ mergedBytes.length, garbledIndex, rawMessage);
content += arr_msg[i].replaceAll("agent-client:","")+"@tong-ran";
// sb.append(arr_msg[i].replaceAll("agent-client:","")+"@tong-ran"); byteBuf.release();
} released = true;
} return; // 跳过处理
}else{
lruCache.remove(sessionManager.client(ctx));
tmpMsgSize = 0;
// sb.append(String.join("@tong-ran", arr_msg));
content = String.join("@tong-ran", arr_msg);
}
}
}
// content = sb.toString();
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() * 5000);
} }
} // 5. 没有乱码,处理完整消息
AssertLog.info("<<[up2]:IP:{},[up-content]==>{}", sessionManager.client(ctx),content); AssertLog.info("<<[up1]:[raw-content]==>{}", rawMessage);
endsWith = content.endsWith("@tong-ran");
//判断是否是整包 // 6. 处理消息分割(原有逻辑)
if(endsWith){ // 这里不需要从缓存获取,因为已经在字节层面处理过了
content = content.replaceAll("agent-client:",""); String fullMessage = rawMessage;
String[] arr = content.split("@tong-ran");
try { // 7. 使用更精确的分割逻辑
for (String message : arr) { List<String> completeMessages = new ArrayList<>();
JSONObject jsonObject = JSONObject.parseObject(message); StringBuilder currentMsg = new StringBuilder();
String clientId = jsonObject.getString("clientId");
String dataType = jsonObject.getString("dataType"); // 按消息边界分割
String data = jsonObject.getString("data"); String[] parts = fullMessage.split("(?=agent-client:)");
if(Objects.nonNull(agentDispatcherManager)){ for (String part : parts) {
AgentHandler msgHandler = agentDispatcherManager.getHandler(dataType + "&" + AgentDispatcher.VersionEnum.V1.value); if (part.isEmpty()) continue;
if (ObjectUtil.isNotEmpty(msgHandler)) {
AssertLog.info("<<[up-after-handle]:clientId:{},type={},[handle-content]={}", clientId, dataType, data); currentMsg.append(part);
UpMsgResponse response = msgHandler.upHandle(data, clientId ); String current = currentMsg.toString();
if(Objects.nonNull(response)){
Message agentMessage = Message.builder().build(); // 检查是否包含完整消息边界
agentMessage.setClientId(clientId); if (current.contains("@tong-ran")) {
agentMessage.setDataType(response.getDataType()); String[] messages = current.split("@tong-ran", -1); // 使用limit=-1保留空字符串
agentMessage.setData(response.getContent());
ctx.fireChannelRead(agentMessage);//传递到下一个handler // 前面的都是完整消息
} for (int i = 0; i < messages.length - 1; i++) {
String completeMsg = messages[i];
if (!completeMsg.isEmpty()) {
completeMessages.add(completeMsg);
} }
} }
// 最后一部分可能是未完成的消息
String lastPart = messages[messages.length - 1];
currentMsg = new StringBuilder(lastPart);
} }
}catch (Exception e){ }
AssertLog.error("=====channelRead:{}=====" + e.getMessage());
// 8. 处理缓存逻辑
if (currentMsg.length() > 0) {
// 有未完成的消息,需要缓存
// 注意:这里缓存的是字符串,不是字节数组
String remainingMsg = currentMsg.toString();
if (remainingMsg.length() > 0) {
// 将未完成的字符串转为字节数组缓存
byte[] remainingBytes = remainingMsg.getBytes(StandardCharsets.UTF_8);
byteCache.put(clientKey, remainingBytes, DateUnit.SECOND.getMillis() * 5000);
}
} else {
// 所有消息都处理完成,清除缓存
byteCache.remove(clientKey);
}
// 9. 处理完整的消息
for (String message : completeMessages) {
processCompleteMessage(ctx, message);
}
} catch (Exception e) {
AssertLog.error("=====channelRead error:{}=====", e.getMessage(), e);
} finally {
if(!released && byteBuf.refCnt() > 0){
byteBuf.release(); // 确保资源释放
} }
} }
if (isClear) {
lruCache.remove(sessionManager.client(ctx));
}
byteBuf.release(); // 释放 ByteBuf 资源(重要!)
} else { } else {
System.out.println("Unexpected message type: " + msg.getClass()); System.out.println("Unexpected message type: " + msg.getClass());
} }
}
private void processCompleteMessage(ChannelHandlerContext ctx, String message) {
try {
// 清理消息格式
String cleanMessage = message.replaceAll("^agent-client:", "").trim();
if (cleanMessage.isEmpty()) return;
// 截断过长的日志
AssertLog.info("<<[up2]:IP:{},[clean-content]==>{}...",
sessionManager.client(ctx), cleanMessage);
// 解析JSON
JSONObject jsonObject = JSONObject.parseObject(cleanMessage);
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)) {
AssertLog.info("<<[up-after-handle]:clientId:{},type={},[handle-content]={}",
clientId, dataType, data);
UpMsgResponse response = msgHandler.upHandle(data, clientId);
if (Objects.nonNull(response)) {
Message agentMessage = Message.builder()
.clientId(clientId)
.dataType(response.getDataType())
.data(response.getContent())
.build();
ctx.fireChannelRead(agentMessage); // 传递到下一个handler
}
}
}
} catch (Exception e) {
// 如果解析失败,可能是消息不完整,尝试缓存字节
try {
String clientKey = sessionManager.client(ctx);
// 将失败的消息转为字节数组缓存
byte[] failedBytes = message.getBytes(StandardCharsets.UTF_8);
byteCache.put(clientKey, failedBytes, DateUnit.SECOND.getMillis() * 5000);
AssertLog.warn("<<[parse-failed-cached]: 消息解析失败,已缓存字节,长度: {}", failedBytes.length);
} catch (Exception cacheEx) {
AssertLog.error("=====缓存失败消息时出错:{}=====", cacheEx.getMessage(), cacheEx);
}
AssertLog.error("=====processCompleteMessage error, message: {}, error: {}=====",
message, e.getMessage(), e);
}
} }
@Override @Override
@@ -0,0 +1,66 @@
package com.tongran.agent.server.netty.handler;
import com.tongran.agent.server.core.session.SessionManager;
import com.tongran.agent.server.service.AgentService;
import com.tongran.agent.server.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 {
private final SessionManager sessionManager;
@Resource
private AgentService agentService;
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());
}
@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();
}
}
}
}
@@ -0,0 +1,24 @@
package com.tongran.agent.server.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();// 暂未实现
}
}
@@ -51,4 +51,19 @@ public class AgentProducer implements RocketMqService {
rocketMQTemplate.syncSend(msg.getTopic(), MessageBuilder.withPayload(msg.getContent()).build(), 30000, msg.getLevel()); rocketMQTemplate.syncSend(msg.getTopic(), MessageBuilder.withPayload(msg.getContent()).build(), 30000, msg.getLevel());
} }
// 新增顺序发送
@Override
public void sendOrderly(MqMsg mqMsg, String shardingKey) {
try {
AssertLog.info("sendOrderly发送消息:==>{}", mqMsg);
rocketMQTemplate.syncSendOrderly(
mqMsg.getTopic(),
MessageBuilder.withPayload(mqMsg.getContent()).build(),
shardingKey
);
} catch (Exception e) {
AssertLog.error("顺序发送消息失败", e);
}
}
} }
@@ -42,4 +42,11 @@ public interface RocketMqService {
*/ */
default void delayedSendOrderly(MqMsg msg){}; default void delayedSendOrderly(MqMsg msg){};
/**
* 发送顺序消息
* @param mqMsg
* @param shardingKey
*/
public void sendOrderly(MqMsg mqMsg, String shardingKey);
} }
@@ -2,19 +2,13 @@ package com.tongran.agent.server.rockermq.listener;
import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.tongran.agent.server.core.enums.MsgEnum; import com.tongran.agent.server.core.enums.MsgEnum;
import com.tongran.agent.server.core.session.SessionManager; import com.tongran.agent.server.core.session.SessionManager;
import com.tongran.agent.server.netty.MultiTargetNettyClient;
import com.tongran.agent.server.netty.annotation.AgentDispatcher; import com.tongran.agent.server.netty.annotation.AgentDispatcher;
import com.tongran.agent.server.netty.basics.AgentDispatcherManager; import com.tongran.agent.server.netty.basics.AgentDispatcherManager;
import com.tongran.agent.server.netty.basics.AgentHandler; import com.tongran.agent.server.netty.basics.AgentHandler;
import com.tongran.agent.server.netty.model.Message; import com.tongran.agent.server.netty.model.Message;
import com.tongran.agent.server.rockermq.AgentProducer;
import com.tongran.agent.server.rockermq.config.AgentMqConfig;
import com.tongran.agent.server.rockermq.model.MqMsg;
import com.tongran.agent.server.utils.AssertLog; import com.tongran.agent.server.utils.AssertLog;
import org.apache.commons.lang3.StringUtils;
import org.apache.rocketmq.spring.annotation.RocketMQMessageListener; import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
import org.apache.rocketmq.spring.core.RocketMQListener; import org.apache.rocketmq.spring.core.RocketMQListener;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
@@ -25,7 +19,7 @@ import java.util.Objects;
@Component @Component
@ConditionalOnProperty(name = "rocketmq.enabled", havingValue = "true") @ConditionalOnProperty(name = "rocketmq.enabled", havingValue = "true")
@RocketMQMessageListener(topic = "tongran_agent_down", consumerGroup = "tongran_agent_down_group") @RocketMQMessageListener(topic = "tr_agent_down", consumerGroup = "tr_agent_down_group")
public class AgentConsumerDownListener implements RocketMQListener<String> { public class AgentConsumerDownListener implements RocketMQListener<String> {
protected final SessionManager sessionManager; protected final SessionManager sessionManager;
@@ -33,15 +27,6 @@ public class AgentConsumerDownListener implements RocketMQListener<String> {
@Resource @Resource
private AgentDispatcherManager agentDispatcherManager; private AgentDispatcherManager agentDispatcherManager;
@Resource
private MultiTargetNettyClient client;
@Resource
private AgentMqConfig agentMqConfig;
@Resource
private AgentProducer agentProducer;
public AgentConsumerDownListener() { public AgentConsumerDownListener() {
this.sessionManager = SessionManager.getInstance(); this.sessionManager = SessionManager.getInstance();
} }
@@ -51,112 +36,14 @@ public class AgentConsumerDownListener implements RocketMQListener<String> {
AssertLog.info("consumer==> received down message: {}", message); AssertLog.info("consumer==> received down message: {}", message);
try { try {
Message dto = JSON.parseObject(message, Message.class); Message dto = JSON.parseObject(message, Message.class);
if(StringUtils.equals(dto.getDataType(), MsgEnum.注册.getValue())){ AgentHandler msgHandler = agentDispatcherManager.getHandler(dto.getDataType() + "&" + AgentDispatcher.VersionEnum.V1.value);
JSONObject jsonObject = JSONObject.parseObject(dto.getData()); if (ObjectUtil.isNotEmpty(msgHandler)) {
String clientIp = ""; String msg = msgHandler.downHandle(dto.getData(), dto.getClientId());
int clientPort = 0; Message chargeMessage = Message.builder().clientId(dto.getClientId()).dataType(dto.getDataType()).data(msg).build();
String switchBoard = ""; AssertLog.info("连接是否存在:{},clientId:{},session:{}",
long timestamp = 0; Objects.nonNull(sessionManager.getSessionById(dto.getClientId())), dto.getClientId(), sessionManager.getSessionById(dto.getClientId()));
if(jsonObject.containsKey("clientIp")){ if (Objects.nonNull(sessionManager.getSessionById(dto.getClientId()))) {
clientIp = jsonObject.getString("clientIp"); sessionManager.writeAndFlush(sessionManager.getSessionById(dto.getClientId()).getChannel(), chargeMessage);
}
if(jsonObject.containsKey("clientPort")){
clientPort = jsonObject.getInteger("clientPort");
}
if(jsonObject.containsKey("switchBoard")){
switchBoard = jsonObject.getString("switchBoard");
}
if(jsonObject.containsKey("timestamp")){
timestamp = jsonObject.getLongValue("timestamp");
}
if(StringUtils.isBlank(clientIp) || clientPort == 0){
//返回提醒ip 端口 空
JSONObject json = new JSONObject();
json.put("clientId",dto.getClientId());
json.put("dataType",MsgEnum.注册应答.getValue());
JSONObject dataJson = new JSONObject();
dataJson.put("resCode",0);
dataJson.put("resMag","客户端IP、端口不能为空");
dataJson.put("timestamp", System.currentTimeMillis());
json.put("data",dataJson.toString());
MqMsg mqMsg = MqMsg.builder().topic(agentMqConfig.getAgentTopic()).content(json.toString()).build();
agentProducer.asyncSend(mqMsg);
return;
}
boolean success = client.createConnection(dto.getClientId(), clientIp, clientPort, 5);
if(success){
//连接成功
// 连接建立后可以发送登录认证消息
JSONObject object = new JSONObject();
object.put("timestamp", timestamp);
if(StringUtils.isNotBlank(switchBoard)){
String community = "";
String switchIp = "";
int switchPort = 0;
String netOID = "";
String moduleOID = "";
String mpuOID = "";
String pwrOID = "";
String fanOID = "";
String otherOID = "";
String filters = "";
JSONObject json = JSONObject.parseObject(switchBoard);
if(json.containsKey("community")){
community = json.getString("community");
}
if(json.containsKey("ip")){
switchIp = json.getString("ip");
}
if(json.containsKey("port")){
switchPort = json.getInteger("port");
}
if(json.containsKey("netOID")){
netOID = json.getString("netOID");
}
if(json.containsKey("moduleOID")){
moduleOID = json.getString("moduleOID");
}
if(json.containsKey("mpuOID")){
mpuOID = json.getString("mpuOID");
}
if(json.containsKey("pwrOID")){
pwrOID = json.getString("pwrOID");
}
if(json.containsKey("fanOID")){
fanOID = json.getString("fanOID");
}
if(json.containsKey("otherOID")){
otherOID = json.getString("otherOID");
}
if(json.containsKey("filters")){
filters = json.getString("filters");
}
JSONObject switchBoardJson = new JSONObject();
switchBoardJson.put("community",community);
switchBoardJson.put("ip",switchIp);
switchBoardJson.put("port",switchPort);
switchBoardJson.put("netOID",netOID);
switchBoardJson.put("moduleOID",moduleOID);
switchBoardJson.put("mpuOID",mpuOID);
switchBoardJson.put("pwrOID",pwrOID);
switchBoardJson.put("fanOID",fanOID);
switchBoardJson.put("otherOID",otherOID);
switchBoardJson.put("filters",filters);
object.put("switchBoard",switchBoardJson.toString());
AssertLog.info("交换机配置信息={}",switchBoardJson.toString());
}
Message msg = Message.builder().clientId(dto.getClientId()).dataType(MsgEnum.注册.getValue()).data(object.toString()).build();
// 将对象转为 JSON 字符串
client.sendMessages(dto.getClientId(), msg);
}
} else {
AgentHandler msgHandler = agentDispatcherManager.getHandler(dto.getDataType() + "&" + AgentDispatcher.VersionEnum.V1.value);
if (ObjectUtil.isNotEmpty(msgHandler)) {
String msg = msgHandler.downHandle(dto.getData(), dto.getClientId());
Message chargeMessage = Message.builder().clientId(dto.getClientId()).dataType(dto.getDataType()).data(msg).build();
if (Objects.nonNull(sessionManager.getSessionById(dto.getClientId()))) {
sessionManager.writeAndFlush(sessionManager.getSessionById(dto.getClientId()).getChannel(), chargeMessage);
}
} }
} }
} catch (Exception e) { } catch (Exception e) {
@@ -3,37 +3,35 @@ package com.tongran.agent.server.service.impl;
import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject; import com.alibaba.fastjson2.JSONObject;
import com.tongran.agent.server.core.vo.RegisterVO; import com.tongran.agent.server.core.vo.RegisterVO;
import com.tongran.agent.server.netty.MultiTargetNettyClient;
import com.tongran.agent.server.netty.model.Message; import com.tongran.agent.server.netty.model.Message;
import com.tongran.agent.server.service.AgentService; import com.tongran.agent.server.service.AgentService;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@Service @Service
public class AgentServiceImpl implements AgentService { public class AgentServiceImpl implements AgentService {
@Resource // @Resource
private MultiTargetNettyClient client; // private MultiTargetNettyClient client;
@Override @Override
public boolean createConn(String clientId, String host, int port, int timeout) { public boolean createConn(String clientId, String host, int port, int timeout) {
// 动态创建不同IP和端口的连接 // 动态创建不同IP和端口的连接
boolean success = client.createConnection(clientId, host, port, timeout); // boolean success = client.createConnection(clientId, host, port, timeout);
if(success){ // if(success){
// 连接建立后可以发送建立消息 // // 连接建立后可以发送建立消息
long timestamp = System.currentTimeMillis(); // long timestamp = System.currentTimeMillis();
JSONObject object = new JSONObject(); // JSONObject object = new JSONObject();
object.put("clientId",clientId); // object.put("clientId",clientId);
object.put("timestamp",timestamp); // object.put("timestamp",timestamp);
Message message = Message.builder().clientId(clientId).dataType("CREATE").data(object.toString()).build(); // Message message = Message.builder().clientId(clientId).dataType("CREATE").data(object.toString()).build();
// 将对象转为 JSON 字符串 // // 将对象转为 JSON 字符串
String json = JSON.toJSONString(message); // String json = JSON.toJSONString(message);
System.out.println("连接建立成功,发送建立消息="+json); // System.out.println("连接建立成功,发送建立消息="+json);
client.sendMessages(clientId, message); // client.sendMessages(clientId, message);
} // }
return success; // return success;
return false;
} }
@Override @Override
@@ -52,6 +50,6 @@ public class AgentServiceImpl implements AgentService {
// 将对象转为 JSON 字符串 // 将对象转为 JSON 字符串
String json = JSON.toJSONString(message); String json = JSON.toJSONString(message);
System.out.println("发送注册消息="+json); System.out.println("发送注册消息="+json);
client.sendMessages(registerVO.getClientId(), message); // client.sendMessages(registerVO.getClientId(), message);
} }
} }
@@ -40,7 +40,7 @@ public class ClientExample {
} catch (InterruptedException e) { } catch (InterruptedException e) {
Thread.currentThread().interrupt(); Thread.currentThread().interrupt();
} finally { } finally {
manager.shutdown(); // manager.shutdown();
} }
} }
} }
@@ -1,6 +1,5 @@
package com.tongran.agent.server.utils; package com.tongran.agent.server.utils;
import com.tongran.agent.server.netty.MultiTargetNettyClient;
import com.tongran.agent.server.netty.config.ConnectionConfig; import com.tongran.agent.server.netty.config.ConnectionConfig;
import java.util.HashMap; import java.util.HashMap;
@@ -12,11 +11,11 @@ import java.util.concurrent.ConcurrentHashMap;
* 增强版连接管理器 * 增强版连接管理器
*/ */
public class EnhancedConnectionManager { public class EnhancedConnectionManager {
private final MultiTargetNettyClient client; // private final MultiTargetNettyClient client;
private final Map<String, ConnectionConfig> connectionConfigs; private final Map<String, ConnectionConfig> connectionConfigs;
public EnhancedConnectionManager() { public EnhancedConnectionManager() {
this.client = new MultiTargetNettyClient(); // this.client = new MultiTargetNettyClient();
this.connectionConfigs = new ConcurrentHashMap<>(); this.connectionConfigs = new ConcurrentHashMap<>();
} }
@@ -26,18 +25,18 @@ public class EnhancedConnectionManager {
public Map<String, Boolean> createConnections(List<ConnectionConfig> configs) { public Map<String, Boolean> createConnections(List<ConnectionConfig> configs) {
Map<String, Boolean> results = new HashMap<>(); Map<String, Boolean> results = new HashMap<>();
configs.forEach(config -> { // configs.forEach(config -> {
boolean success = client.createConnection( // boolean success = client.createConnection(
config.getConnectionKey(), // config.getConnectionKey(),
config.getHost(), // config.getHost(),
config.getPort(), // config.getPort(),
config.getTimeout() // config.getTimeout()
); // );
if (success) { // if (success) {
connectionConfigs.put(config.getConnectionKey(), config); // connectionConfigs.put(config.getConnectionKey(), config);
} // }
results.put(config.getConnectionKey(), success); // results.put(config.getConnectionKey(), success);
}); // });
return results; return results;
} }
@@ -47,21 +46,21 @@ public class EnhancedConnectionManager {
*/ */
public void routeMessageByType(String messageType, String message) { public void routeMessageByType(String messageType, String message) {
// 这里可以根据业务逻辑决定发送到哪个连接 // 这里可以根据业务逻辑决定发送到哪个连接
switch (messageType) { // switch (messageType) {
case "TYPE_A": // case "TYPE_A":
client.sendMessage("server1", "[TYPE_A] " + message); // client.sendMessage("server1", "[TYPE_A] " + message);
break; // break;
case "TYPE_B": // case "TYPE_B":
client.sendMessage("server2", "[TYPE_B] " + message); // client.sendMessage("server2", "[TYPE_B] " + message);
break; // break;
case "TYPE_C": // case "TYPE_C":
client.sendMessage("server3", "[TYPE_C] " + message); // client.sendMessage("server3", "[TYPE_C] " + message);
break; // break;
default: // default:
// 广播到所有连接 // // 广播到所有连接
connectionConfigs.keySet().forEach(key -> // connectionConfigs.keySet().forEach(key ->
client.sendMessage(key, "[BROADCAST] " + message)); // client.sendMessage(key, "[BROADCAST] " + message));
} // }
} }
/** /**
@@ -69,14 +68,14 @@ public class EnhancedConnectionManager {
*/ */
public Map<String, Boolean> getConnectionStatus() { public Map<String, Boolean> getConnectionStatus() {
Map<String, Boolean> status = new HashMap<>(); Map<String, Boolean> status = new HashMap<>();
connectionConfigs.forEach((key, config) -> { // connectionConfigs.forEach((key, config) -> {
// 这里可以添加更详细的状态检查 // // 这里可以添加更详细的状态检查
status.put(key, client.sendMessage(key, "PING")); // status.put(key, client.sendMessage(key, "PING"));
}); // });
return status; return status;
} }
public void shutdown() { // public void shutdown() {
client.shutdown(); // client.shutdown();
} // }
} }
+13 -13
View File
@@ -1,5 +1,5 @@
server: server:
port: 7012 port: -1
servlet: servlet:
context-path: /tr-agent-server context-path: /tr-agent-server
@@ -12,21 +12,21 @@ knife4j:
logging: logging:
file: file:
# path: /usr/local/tongran/logs # path: /usr/local/tongran/logs
path: /data/tr-agent-server/logs path: /usr/local/tongran_server/logs
# path: D:/job/agent-logs/logs # path: D:/job/agent-logs/logs
rocketmq: rocketmq:
#测试环境
#name-server: 172.16.15.52:9876
#生产环境
name-server: 172.16.15.103:9876 name-server: 172.16.15.103:9876
enabled: true enabled: true
netty: tcp:
server: netty:
# host: 172.16.15.103 charge:
host: 127.0.0.1 enable: true
port: 6610 name: AGENT-SERVER-服务
client: port: 6620
client-id: client-001 readerIdleTime: 300
reconnect-interval: 5
maxReconnectAttempts: 10 # 最大重连次数
initialReconnectDelay: 1000 # 初始重连延迟(毫秒)
maxReconnectDelay: 30000 # 最大重连延迟(毫秒)
+4 -4
View File
@@ -34,10 +34,10 @@ rocketmq:
producer: producer:
group: tongran-group group: tongran-group
topic: default_topic topic: default_topic
agent-group: tongran_agent_up_group agent-group: tr_agent_up_group
agent-topic: tongran_agent_up agent-topic: tr_agent_up
consumer: consumer:
group: tongran-group group: tongran-group
topic: default_topic topic: default_topic
agent-group: tongran_agent_down_group agent-group: tr_agent_down_group
agent-topic: tongran_agent_down agent-topic: tr_agent_down