feat:tio集群逻辑优化
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,80 @@
|
||||
package com.zt.plat.module.qms.iot.service;
|
||||
|
||||
import com.zt.plat.framework.common.pojo.CommonResult;
|
||||
import com.zt.plat.module.qms.iot.tcpserver.cluster.TioServerMessageConfig;
|
||||
import com.zt.plat.module.qms.iot.tcpserver.cluster.TioServerMessagePublisher;
|
||||
import com.zt.plat.module.qms.iot.tcpserver.cluster.TioServerMessageVo;
|
||||
import com.zt.plat.module.qms.iot.tcpserver.core.IotDeviceSessionContext;
|
||||
import com.zt.plat.module.qms.iot.tcpserver.device.RedisSessionComponent;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import tech.zzjc.tio.core.ChannelContext;
|
||||
import tech.zzjc.tio.core.Tio;
|
||||
import tech.zzjc.tio.starter.TioServerBootstrap;
|
||||
|
||||
import static java.lang.Thread.sleep;
|
||||
|
||||
@Service
|
||||
public class IotConnectManagerStatsService {
|
||||
|
||||
@Autowired
|
||||
private TioServerBootstrap tioServerBootstrap;
|
||||
@Autowired public RedisSessionComponent redisSessionComponent;
|
||||
@Autowired private TioServerMessagePublisher tioServerMessagePublisher;
|
||||
|
||||
public CommonResult<?> close(String id, String fromSubscribe) throws InterruptedException {
|
||||
ChannelContext channelContext = Tio.getByChannelContextId(tioServerBootstrap.getServerTioConfig(), id);
|
||||
if(channelContext == null){
|
||||
if("1".equals(fromSubscribe))
|
||||
return CommonResult.success("ok");
|
||||
//广播消息处理
|
||||
TioServerMessageVo message = new TioServerMessageVo();
|
||||
message.setServerClusterMsgType(TioServerMessageConfig.MESSAGE_TYPE_CLOSE_MANUAL);
|
||||
message.setChannelId(id);
|
||||
tioServerMessagePublisher.publish(message);
|
||||
//延迟0.5秒返回
|
||||
sleep(500);
|
||||
return CommonResult.success("ok");
|
||||
}
|
||||
IotDeviceSessionContext sessionContext = (IotDeviceSessionContext) channelContext.get(IotDeviceSessionContext.DEFAULT_DEVICE_SESSION_CONTEXT_KEY);
|
||||
String deviceId = sessionContext.getDeviceId();
|
||||
if(!ObjectUtils.isEmpty(deviceId))
|
||||
redisSessionComponent.deleteByDeviceId(deviceId);
|
||||
Tio.IpBlacklist.clear();//清空全局黑名单
|
||||
Tio.close(channelContext, "控制端主动关闭连接");
|
||||
return CommonResult.success("ok");
|
||||
}
|
||||
|
||||
public CommonResult<?> clearDeviceControl(String id, String fromSubscribe) throws InterruptedException {
|
||||
ChannelContext channelContext = Tio.getByChannelContextId(tioServerBootstrap.getServerTioConfig(), id);
|
||||
Tio.IpBlacklist.clear();//清空全局黑名单
|
||||
if(channelContext == null){
|
||||
if("1".equals(fromSubscribe))
|
||||
return CommonResult.success("ok");
|
||||
//广播消息处理
|
||||
TioServerMessageVo message = new TioServerMessageVo();
|
||||
message.setServerClusterMsgType(TioServerMessageConfig.MESSAGE_TYPE_CLEAR_MANUAL);
|
||||
message.setChannelId(id);
|
||||
tioServerMessagePublisher.publish(message);
|
||||
//延迟0.5秒返回
|
||||
sleep(500);
|
||||
return CommonResult.success("ok");
|
||||
}
|
||||
IotDeviceSessionContext deviceSessionContext = (IotDeviceSessionContext) channelContext.get(IotDeviceSessionContext.DEFAULT_DEVICE_SESSION_CONTEXT_KEY);
|
||||
String deviceId = deviceSessionContext.getDeviceId();
|
||||
if(!ObjectUtils.isEmpty(deviceId)){
|
||||
redisSessionComponent.clearControlByDeviceId(deviceId, channelContext);
|
||||
}
|
||||
// if(channelContext != null) {
|
||||
//设置控制点的channelContext
|
||||
// deviceSessionContext.setControlChannelContext(null);
|
||||
// deviceSessionContext.setControlRealName("");
|
||||
// channelContext.set(ChannelContextConstant.CONTROL_DEVICE_ID, "");
|
||||
// }
|
||||
return CommonResult.success("ok");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -86,7 +86,6 @@ public class IotServerAioListener implements ServerAioListener {
|
||||
if(!ObjectUtils.isEmpty(deviceCode)){
|
||||
redisSessionComponent.clearControlByDeviceCode(deviceCode.toString(), channelContext);
|
||||
}
|
||||
|
||||
//解绑业务ID
|
||||
Tio.unbindBsId(channelContext);
|
||||
//解绑群组
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,16 +1,19 @@
|
||||
package com.zt.plat.module.qms.iot.tcpserver.caaclient;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.fhs.common.spring.SpringContextUtil;
|
||||
import com.zt.plat.module.qms.iot.tcpserver.IotPacket;
|
||||
import com.zt.plat.module.qms.iot.tcpserver.consoleclient.bean.Register;
|
||||
import com.zt.plat.module.qms.iot.tcpserver.core.ClientType;
|
||||
import com.zt.plat.module.qms.iot.tcpserver.core.Command;
|
||||
import com.zt.plat.module.qms.iot.tcpserver.core.ReplyResult;
|
||||
import com.zt.plat.module.qms.iot.tcpserver.device.RedisSessionComponent;
|
||||
import com.zt.plat.module.qms.iot.tcpserver.handler.IotDataHander;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
import tech.zzjc.tio.core.ChannelContext;
|
||||
import tech.zzjc.tio.core.Tio;
|
||||
import tech.zzjc.tio.utils.SystemTimer;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Date;
|
||||
@@ -34,11 +37,7 @@ public class IotCaaClientRegisterHander implements IotDataHander {
|
||||
@Override
|
||||
public Object hander(String msgId, String data, ChannelContext channelContext) throws Exception {
|
||||
Register register = JSON.parseObject(data, Register.class);
|
||||
//获取客户端连接信息
|
||||
String realClientIp = channelContext.get("realClientIp") == null ? channelContext.getClientNode().getIp() : channelContext.get("realClientIp").toString();
|
||||
String realClientPort = channelContext.get("realClientPort") == null ? channelContext.getClientNode().getPort() + "" : channelContext.get("realClientPort").toString();
|
||||
String realClient = realClientIp + ":" + realClientPort;
|
||||
log.info("{},, APP客户端注册租户ID:{},用户ID:{}", realClient, register.getTenantId(), register.getUserId());
|
||||
|
||||
Tio.bindUser(channelContext, register.getUserId());
|
||||
Tio.bindToken(channelContext, register.getUserId());
|
||||
channelContext.set("regTime", new Date());
|
||||
@@ -49,6 +48,15 @@ public class IotCaaClientRegisterHander implements IotDataHander {
|
||||
Tio.send(channelContext, resppacket);
|
||||
//绑定群组
|
||||
Tio.bindGroup(channelContext, CAA_ALL_CLIENT);
|
||||
|
||||
//在redis记录客户端信息
|
||||
// RedisSessionComponent redisSessionComponent = (RedisSessionComponent) SpringContextUtil.getBean("redisSessionComponent");
|
||||
// redisSessionComponent.regClient(channelContext);
|
||||
|
||||
|
||||
//
|
||||
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ public class ControlDevice implements Serializable {
|
||||
|
||||
/** 控制者姓名 **/
|
||||
private String controlRealName;
|
||||
private String controlUserId;
|
||||
|
||||
/** 是否控制 **/
|
||||
private Boolean isControl;
|
||||
@@ -33,4 +34,7 @@ public class ControlDevice implements Serializable {
|
||||
|
||||
/** 回复app时使用 消息 **/
|
||||
private String msg;
|
||||
|
||||
/** 来自消息订阅 **/
|
||||
private String fromSubscribe;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.zt.plat.module.qms.iot.tcpserver.cluster;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@Component
|
||||
public class InstanceIdProvider {
|
||||
|
||||
private final String instanceId;
|
||||
|
||||
public InstanceIdProvider() {
|
||||
//使用随机 UUID(每次重启会变)
|
||||
this.instanceId = UUID.randomUUID().toString();
|
||||
}
|
||||
|
||||
public String getInstanceId() {
|
||||
return instanceId;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.zt.plat.module.qms.iot.tcpserver.cluster;
|
||||
|
||||
public class TioServerMessageConfig {
|
||||
|
||||
public static final String SERVER_CLUSTER_TOPIC_CHANNEL = "zzjc-cluster-tio-server";
|
||||
|
||||
|
||||
public static final String MESSAGE_TYPE_CONTROL = "control"; //设备控制消息
|
||||
public static final String MESSAGE_TYPE_CLEAR_MANUAL = "clearControlManual"; //手动清除控制
|
||||
public static final String MESSAGE_TYPE_CLOSE_MANUAL = "closeManual"; //手动断开连接
|
||||
public static final String MESSAGE_TYPE_NOTICE = "notice"; //通知消息。应该用不到。
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.zt.plat.module.qms.iot.tcpserver.cluster;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.redisson.api.RTopic;
|
||||
import org.redisson.api.RedissonClient;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import tech.zzjc.tio.server.ServerTioConfig;
|
||||
|
||||
import static com.zt.plat.module.qms.iot.tcpserver.cluster.TioServerMessageConfig.SERVER_CLUSTER_TOPIC_CHANNEL;
|
||||
|
||||
@Service
|
||||
@Slf4j
|
||||
public class TioServerMessagePublisher {
|
||||
|
||||
@Autowired
|
||||
private RedissonClient redissonClient;
|
||||
@Autowired
|
||||
private InstanceIdProvider instanceIdProvider; // 注入实例ID提供者
|
||||
|
||||
|
||||
|
||||
public void publish(TioServerMessageVo message) {
|
||||
message.setSenderInstanceId(instanceIdProvider.getInstanceId());
|
||||
RTopic topic = redissonClient.getTopic(SERVER_CLUSTER_TOPIC_CHANNEL);
|
||||
topic.publish(message);
|
||||
log.error("[Publisher] 已发布消息: " + message);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
package com.zt.plat.module.qms.iot.tcpserver.cluster;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.zt.plat.module.qms.iot.service.IotConnectManagerStatsService;
|
||||
import com.zt.plat.module.qms.iot.tcpserver.IotPacket;
|
||||
import com.zt.plat.module.qms.iot.tcpserver.handler.IotDataHander;
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.redisson.api.RTopic;
|
||||
import org.redisson.api.RedissonClient;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
import tech.zzjc.tio.core.ChannelContext;
|
||||
import tech.zzjc.tio.core.Tio;
|
||||
import tech.zzjc.tio.core.TioConfig;
|
||||
import tech.zzjc.tio.server.ServerTioConfig;
|
||||
import tech.zzjc.tio.server.TioServer;
|
||||
import tech.zzjc.tio.starter.TioServerBootstrap;
|
||||
import tech.zzjc.tio.utils.lock.SetWithLock;
|
||||
|
||||
import static com.zt.plat.module.qms.iot.tcpserver.cluster.TioServerMessageConfig.SERVER_CLUSTER_TOPIC_CHANNEL;
|
||||
|
||||
@Component
|
||||
@Slf4j
|
||||
public class TioServerMessageSubscriber {
|
||||
|
||||
@Autowired
|
||||
private RedissonClient redissonClient;
|
||||
@Autowired
|
||||
private InstanceIdProvider instanceIdProvider;
|
||||
|
||||
@Autowired
|
||||
private IotDataHander iotCaaClientControlDeviceHander;
|
||||
|
||||
@Autowired private TioServerBootstrap tioServerBootstrap;
|
||||
@Autowired private IotConnectManagerStatsService iotConnectManagerStatsService;
|
||||
|
||||
@PostConstruct
|
||||
public void subscribe() {
|
||||
RTopic topic = redissonClient.getTopic(SERVER_CLUSTER_TOPIC_CHANNEL);
|
||||
topic.addListener(TioServerMessageVo.class, (channel, msg) -> {
|
||||
String senderId = msg.getSenderInstanceId();
|
||||
String channelId = msg.getChannelId();
|
||||
String msgType = msg.getServerClusterMsgType();
|
||||
String currentId = instanceIdProvider.getInstanceId();
|
||||
if (currentId.equals(senderId)) {
|
||||
log.debug("[Subscriber] 跳过自己发送的消息");
|
||||
return;
|
||||
}
|
||||
log.debug("[Subscriber] 收到消息频道 '" + channel + "' 的内容: " + msg);
|
||||
|
||||
if(TioServerMessageConfig.MESSAGE_TYPE_CLEAR_MANUAL.equals(msgType)){
|
||||
try {
|
||||
iotConnectManagerStatsService.clearDeviceControl(channelId,"1");
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if(TioServerMessageConfig.MESSAGE_TYPE_CLOSE_MANUAL.equals(msgType)){
|
||||
try {
|
||||
iotConnectManagerStatsService.close(channelId,"1");
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
ServerTioConfig serverTioConfig = tioServerBootstrap.getServerTioConfig();
|
||||
SetWithLock<ChannelContext> all = Tio.getAll(serverTioConfig);
|
||||
ChannelContext targetContext = null;
|
||||
for(ChannelContext context : all.getObj()){
|
||||
targetContext = context;
|
||||
String id = context.getId();
|
||||
if(id.equals(channelId)){
|
||||
break;
|
||||
}
|
||||
}
|
||||
IotPacket packet = (IotPacket) msg.getPacket();
|
||||
byte[] body = packet.getBody();
|
||||
String bodyStr = new String(body);
|
||||
JSONObject json = JSONObject.parseObject(bodyStr);
|
||||
try {
|
||||
iotCaaClientControlDeviceHander.hander(json.getString("msgId"), bodyStr, targetContext);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user