首先上官方说明:https://www.rabbitmq.com/heartbeats.html
从官方文档可以看到有个heartbeat timeout参数,服务端默认60s,这里的描述可能有些迷惑性
那么问题来了,如果客户端heartbeat timeout协商的是30s,那么服务端在断网多久会超时断开tcp连接?
经测试,是1-3倍heartbeat timeout,即30-90s。为什么上面说了15s发一次心跳,丢两次心跳认为不可达,最后却需要这么长时间?
实际上服务端在和客户端协商好heartbeat timeout后(以较小值为准),服务端会启动两个进程:一个负责以heartbeat timeout /2的间隔定期判断有没有数据发送,一个以heartbeat timeout 的间隔定期判断有没有收到数据。服务端源码:
ps:对于rabbitmq-c client,heartbeat参数实际上就是heartbeat timeout。
我们来梳理一下,首先要知道rabbitmq的心跳不是发送、确认这样的机制,不存在序号上成对的心跳。服务端判断超时只看有没有收到数据(心跳包、消息、消息确认都算数据变化),决定服务端超时断开连接的只有心跳接收定时任务。举个例子:
1.假定客户端设置heartbeat设置是30s,那么上图中服务端TimeoutSec也为30
2.服务端心跳发送定时器的时间间隔是1/2TimeoutSec,也就是15s,心跳接收定时器是TimeoutSec,30s
3.心跳发送定时任务逻辑:15秒后如果发送状态没有变化,则进入超时处理逻辑,向客户端发送一次心跳包;
4.心跳接收定时任务逻辑:30s后如果接收状态和上次相比没有变化,则计数值加1,再过30s,如果还是没有变化进入超时处理,告知父进程已超时,断开tcp连接。因此服务器端需要在连续两个检测周期没检测到,才会触发超时,即断网后最终需要1-3*30s才能检测到。
之所以要设计成2个周期检测失败超时,是为了避免短时间的网络波动导致链路不稳定。试想一下,客户端原本打算一个周期发一个数据给服务端以保活,两者的周期启动时间又很近,此时突然网络拥塞,那么即便是极短的时间也会导致断链。当然服务器计时原本可以设计得更加精确,个人认为目前策略过于粗糙,为了保证链接可靠,牺牲了断链灵敏度,极端场景3T都才会触发重连,这对系统可用性是有一定影响的。不过可以在客户端主动断链进行进一步修正断网超时区间。
例如rabbitmq-c客户端原始版本里,默认是按heartbeat,也就是至少30s有一次数据发送;同样,接收超时也是按照heartbeat,30s未收到数据认为超时,那么对于客户端而言,断网超过30s是必定主动断链的。其他语言的客户端心跳发给服务端的时间间隔一般不会超过一个协商的heartbeat;客户端接收心跳超时由于服务端以heartbeat timeout /2进行发送心跳,因此一般实现为一个heartbeat及以上即可。