当前位置: 首页 > news >正文

记录一次netty连接状态的填坑过程

记录一次netty连接状态的填坑过程

一、项目大致情况

springboot + netty 项目。服务器通过 netty 创建监听,多个终端通过5G网络向服务器建立TCP长连接,自定义协议,格式大概是:4字节固定内容起始 + 2字长包总长 + 8字节包头 + 动态长度 payload。

通信时有身份认证过程,连接建立之后需要两次交互:终端发请求签到 -> 服务器发8字节随机字符串 -> 终端使用密钥加密随机字符串,并将密文发给服务器,请求签到验证 -> 服务器验证成功,则认为签到成功

终端有断开自动重连机制,如果终端检测到连接断开,则重新发起签到

客户要求在WEB端能够随时查看终端的连接状态,并且连接终端的服务器和 WEB 服务器是分开的,因此我把终端的在线状态和实时数据放到 redis 中共享。WEB浏览器通过轮询的方式刷新终端连接状态和实时数据。

连接状态和实时数据分别定义了两个 json 格式,以 deviceId 为 key,存到了两个 redis 的 hash 中:

连接状态:

{

    "deviceId": 1,

    "ts": "2026-05-10 10:00:00.000",

    "connected": true

设备实时信号:

{

    "deviceId": 1,

    "signals": [

        { "signalId": 1, "signalValue": 19.3 }

        { "signalId": 2, "signalValue": 62.34 }

    ]

}

二、问题

在浏览器上看设备信息,有些设备明明有实时数据更新,浏览器也在实时刷新,但是设备连接状态却是离线!!

 

三、原因

终端连接状态的维护在两个地方:

1)终端与服务器建立连接,并且签到成功后,修改 redis 中的设备连接状态为“已连接”

2)channelInactive 方法被调用时,将对应状态改为“离线”

在分析日志中发现,这两个地方都没有问题,相应事件发生时,都成功完成了该有的数据更新操作。

但仔细分析后找到问题的根本原车,是事件的先后次序出现了问题:

对同一个终端来说:先发生了重新签到,后发生 channel inactive。

这将造成一个结果:先将终端状态改为“在线”,而后将终端状态改为“离线”!

根本原因是:终端发现TCP连接被断开后,立刻重建连接并完成签到,而后再断开连接!或者其它原因,延迟了主动断开TCP连接的操作!

 

四、解决

终端的软件工程师已经进入到其它项目研发中,不太可能一起配合解决这个问题。

因此服务端的处理是:将 redis 中的连接状态信息中,添加一个 channel 的 hashCode 值的字段,断开后,要判断当前的 redis 中存的状态信息是否与 channelInactive 事件中的 channel 是否是同一个,如果不同,则不更新连接状态。