靠谱实现
因此我们得有一个单独的线程来判断是否需要重连,不依赖于 IdleStateHandler
。
于是 cim
在客户端感知到网络断开时就会开启一个定时任务:
之所以不在客户端启动就开启,是为了节省一点线程消耗。网络问题虽然不可避免,但在需要的时候开启更能节省资源。
在这个任务重其实就是执行了重连,限于篇幅具体代码就不贴了,感兴趣的可以自行查阅。
同时来验证一下效果。
启动两个服务端,再启动客户端连接上一台并保持长连接。这时突然手动关闭一台服务,客户端可以自动重连到可用的那台服务节点。
启动客户端后服务端也能收到正常的 ping
消息。
利用 :info
命令查看当前客户端的链接状态发现连的是 9000
端口。
:info 是一个新增命令,可以查看一些客户端信息。
这时我关掉连接上的这台节点。
kill -9 2142
这时客户端会自动重连到可用的那台节点。 这个节点也收到了上线日志以及心跳包。
服务端自动剔除离线客户端
现在来看看服务端,它要实现的效果就是延迟 N 秒没有收到客户端的 ping
包则认为客户端下线了,在 cim
的场景下就需要把他踢掉置于离线状态。
消息发送误区
这里依然有一个误区,在调用 ctx.writeAndFlush()
发送消息获取回调时。
其中是 isSuccess
并不能作为消息发送成功与否的标准。
也就是说即便是客户端直接断网,服务端这里发送消息后拿到的 success
依旧是 true
。
这是因为这里的 success
只是告知我们消息写入了 TCP
缓冲区成功了而已。
和我之前有着一样错误理解的不在少数,这是 Netty
官方给的回复。
相关 issue
:
同时感谢 95老徐以及闪电侠的一起排查。
所以我们不能依据此来关闭客户端的连接,而是要像上文一样判断 Channel
上绑定的时间与当前时间只差是否超过了阈值。
以上则是 cim
服务端的实现,逻辑和开头说的一致,也和 Dubbo
的心跳机制有些类似。
于是来做个试验:正常通信的客户端和服务端,当我把客户端直接断网时,服务端会自动剔除客户端。
总结
这样就实现了文初的两个要求。
- 服务端检测到某个客户端迟迟没有心跳过来可以主动关闭通道,让它下线。
- 客户端检测到某个服务端迟迟没有响应心跳也能重连获取一个新的连接。
本文所有相关代码都在此处,感兴趣的可以自行查看: