update 桩端连接相关内容,解决频繁登录

This commit is contained in:
Lemon
2026-03-21 10:19:22 +08:00
parent c9d7f505b6
commit c10ac24323
6 changed files with 278 additions and 42 deletions

View File

@@ -19,6 +19,7 @@ import java.util.concurrent.CompletableFuture;
/**
* 充电桩心跳包
* 优化:将所有异步操作完全异步化,先返回心跳应答再执行其他逻辑
*/
@Slf4j
@Component
@@ -50,12 +51,6 @@ public class HeartbeatRequestHandler extends AbstractYkcHandler {
byte[] pileSnByte = BytesUtil.copyBytes(msgBody, startIndex, length);
String pileSn = BytesUtil.binary(pileSnByte, 16);
// 保存时间
saveLastTimeAndCheckChannel(pileSn, channel);
// 校验channel
// PileChannelEntity.checkChannel(pileSn, channel);
// 枪号
startIndex += length;
length = 1;
@@ -69,30 +64,27 @@ public class HeartbeatRequestHandler extends AbstractYkcHandler {
String connectorStatus = BytesUtil.binary(connectorStatusByte, 16);
// log.info("桩号:{}, 枪号:{}, 枪状态:{}", pileSn, pileConnectorNum, connectorStatus);
// updateStatus(pileSn, pileConnectorNum, connectorStatus);
// 优化:先构建并返回心跳应答,不阻塞心跳回复
byte[] flag = Constants.zeroByteArray;
byte[] messageBody = Bytes.concat(pileSnByte, pileConnectorNumByte, flag);
byte[] response = getResult(ykcDataProtocol, messageBody);
// 公共方法修改状态
// try {
// pileBasicInfoService.updateStatus(BytesUtil.bcd2Str(ykcDataProtocol.getFrameType()), pileSn, pileConnectorNum, connectorStatus, null);
// } catch (Exception e) {
// log.error("公共方法修改状态error", e);
// }
// 异步修改状态
// 优化所有其他操作Redis、数据库完全异步化不阻塞心跳回复
// 将 saveLastTimeAndCheckChannel 和 updateStatus 都改为异步执行
CompletableFuture.runAsync(() -> {
try {
pileBasicInfoService.updateStatus(BytesUtil.bcd2Str(ykcDataProtocol.getFrameType()), pileSn, pileConnectorNum, connectorStatus, null);
// 异步保存连接时间并检查通道Redis 操作)
saveLastTimeAndCheckChannel(pileSn, channel);
// 异步更新状态(数据库操作)
String frameType = BytesUtil.bcd2Str(ykcDataProtocol.getFrameType());
pileBasicInfoService.updateStatus(frameType, pileSn, pileConnectorNum, connectorStatus, null);
} catch (Exception e) {
log.error("公共方法修改状态error", e);
log.error("心跳异步处理error, pileSn:{}", pileSn, e);
}
}, executor);
// 心跳应答置0
byte[] flag = Constants.zeroByteArray;
// 消息体
byte[] messageBody = Bytes.concat(pileSnByte, pileConnectorNumByte, flag);
return getResult(ykcDataProtocol, messageBody);
return response;
}
}

View File

@@ -38,7 +38,7 @@ public class NettyServerChannelInitializer extends ChannelInitializer<SocketChan
pipeline.addLast(new IdleStateHandler(30, 0, 0, TimeUnit.SECONDS));
// pipeline.addLast("handler", nettyServerHandler);
pipeline.addLast(businessGroup, nettyServerHandler); // 消息先进入业务线程池
pipeline.addLast(echoServerHandler);
pipeline.addLast(businessGroup, echoServerHandler); // 回复Handler也绑定到业务线程池避免阻塞IO线程
}
}

View File

@@ -132,19 +132,23 @@ public class NettyServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object message) throws Exception {
long startTime = System.currentTimeMillis();
String pileSn = "";
String frameTypeStr = "";
try {
YKCDataProtocol ykcDataProtocol = (YKCDataProtocol) message;
// 获取帧类型
byte[] frameTypeBytes = ykcDataProtocol.getFrameType();
String frameType = YKCUtils.frameType2Str(frameTypeBytes);
frameTypeStr = YKCUtils.frameType2Str(frameTypeBytes);
// 获取序列号域
int serialNumber = BytesUtil.bytesToIntLittle(ykcDataProtocol.getSerialNumber());
// 获取channel
Channel channel = ctx.channel();
// 心跳包0x03日志太多造成日志文件过大改为不打印
if (!CollectionUtils.containsAny(notPrintFrameTypeList, frameType)) {
if (!CollectionUtils.containsAny(notPrintFrameTypeList, frameTypeStr)) {
log.info("【<<<<<平台收到消息<<<<<】channel:{}, 帧类型:{}, 帧名称:{}, 序列号域:{}, 报文:{}",
channel.id(), frameType, YKCFrameTypeCode.getFrameTypeStr(frameType), serialNumber,
channel.id(), frameTypeStr, YKCFrameTypeCode.getFrameTypeStr(frameTypeStr), serialNumber,
BytesUtil.binary(ykcDataProtocol.getBytes(), 16));
}
// 处理数据
@@ -154,18 +158,26 @@ public class NettyServerHandler extends ChannelInboundHandlerAdapter {
ByteBuf buffer = ctx.alloc().buffer().writeBytes(response);
// this.channelWrite(channel.id(), buffer);
super.channelRead(ctx, buffer);
if (!CollectionUtils.containsAny(notPrintFrameTypeList, frameType)) {
if (!CollectionUtils.containsAny(notPrintFrameTypeList, frameTypeStr)) {
// 应答帧类型
byte[] responseFrameTypeBytes = YKCFrameTypeCode.PlatformAnswersRelation.getResponseFrameTypeBytes(frameTypeBytes);
String responseFrameType = YKCUtils.frameType2Str(responseFrameTypeBytes);
log.info("【>>>>>平台响应消息>>>>>】channel:{}, 响应帧类型:{}, 响应帧名称:{}, 原帧类型:{}, 原帧名称:{}, 序列号域:{}, response:{}",
channel.id(), responseFrameType, YKCFrameTypeCode.getFrameTypeStr(responseFrameType),
frameType, YKCFrameTypeCode.getFrameTypeStr(frameType), serialNumber,
frameTypeStr, YKCFrameTypeCode.getFrameTypeStr(frameTypeStr), serialNumber,
BytesUtil.binary(response, 16));
}
}
} finally {
ReferenceCountUtil.release(message);
// 优化:增加消息处理耗时监控,用于排查性能问题
long elapsed = System.currentTimeMillis() - startTime;
// 心跳帧(0x03)处理超过50ms警告其他帧超过200ms警告
int warnThreshold = "0x03".equals(frameTypeStr) ? 50 : 200;
if (elapsed > warnThreshold) {
log.error("【性能警告】消息处理耗时过长: {}ms, 帧类型: {}, pileSn: {}", elapsed, frameTypeStr, pileSn);
}
}
}