记录一次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 是否是同一个,如果不同,则不更新连接状态。
