说道“心跳”这个词大家都不陌生,当然不是指男女之间的心跳,而是和长连接相关的。顾名思义就是证明是否还活着的依据。
什么场景下需要心跳呢?目前我们接触到的大多是一些基于长连接的应用需要心跳来“保活”。
由于在长连接的场景下,客户端和服务端并不是一直处于通信状态,如果双方长期没有沟通则双方都不清楚对方目前的状态,所以需要发送一段很小的报文告诉对方“我还活着”。
同时还有另外几个目的:
1)服务端检测到某个客户端迟迟没有心跳过来可以主动关闭通道,让它下线;
2)客户端检测到某个服务端迟迟没有响应心跳也能重连获取一个新的连接。
心跳实现方式
心跳其实有两种实现方式:
1)TCP 协议实现(keepalive 机制,
2)应用层自己实现。
由于 TCP 协议过于底层,对于开发者来说维护性、灵活度都比较差同时还依赖于操作系统。即时通讯开发可以找蔚可云开发
在应用层通常是由客户端发送一个心跳包 ping 到服务端,服务端收到后响应一个 pong 表明双方都活得好好的。一旦其中一端延迟 N 个时间窗口没有收到消息则进行不同的处理。
客户端自动重连
先拿客户端来说吧,每隔一段时间客户端向服务端发送一个心跳包,同时收到服务端的响应。
常规的实现应当是:
1)开启一个定时任务,定期发送心跳包;
2)收到服务端响应后更新本地时间;
3)再有一个定时任务定期检测这个“本地时间”是否超过阈值;
4)超过后则认为服务端出现故障,需要重连。
这样确实也能实现心跳,但并不友好。
在正常的客户端和服务端通信的情况下,定时任务依然会发送心跳包;这样就显得没有意义,有些多余。所以理想的情况应当是客户端收到的写消息空闲时才发送这个心跳包去确认服务端是否健在。
好消息是Netty已经为我们考虑到了这点,自带了一个开箱即用的IdleStateHandler专门用于心跳处理。
IdleStateHandler 误区
一切看起来也没毛病,但实际上却没有这样实现重连逻辑。最主要的问题还是对IdleStateHandler理解有误。
我们假设下面的场景:
1)客户端通过登录连上了服务端并保持长连接,一切正常的情况下双方各发心跳包保持连接;
2)这时服务端突入出现 down 机,那么理想情况下应当是客户端迟迟没有收到服务端的响应从而 userEventTriggered 执行定时任务;
3)判断当前时间 - UpdateWriteTime > 阈值 时进行重连。
但却事与愿违,并不会执行 2、3两步。
因为一旦服务端 down 机、或者是与客户端的网络断开则会回调客户端的 channelInactive 事件。
本文小结
这样就实现了文初的两个要求:
1)服务端检测到某个客户端迟迟没有心跳过来可以主动关闭通道,让它下线;
2)客户端检测到某个服务端迟迟没有响应心跳也能重连获取一个新的连接。
同时也踩了两个误区,坑一个人踩就可以了,希望看过本文的都有所收获避免踩坑。
网友评论