1.介绍


 说“心跳”这个词并不陌生,它当然不是指男女之间的心跳,而是与长久的联系有关。顾名思义,这是证明他是否还活着的基础。

 

 在什么情况下你需要心跳?目前,我们接触到的大多数应用程序都需要心跳来保持活力。

 

 因为在长连接情况下,客户端和服务器并不总是处于通信状态,如果双方长时间不通信,双方都不知道对方的当前状态,所以有必要发送一条小消息告诉对方“我还活着”。

 

 同时,还有其他几个目的:

 1)当检测到客户端没有心跳时,服务器可以主动关闭信道并让其离线;

 2)如果客户端检测到服务器没有响应心跳,它可以重新连接以获得新的连接。

 

 在本文中,CIM系统只有两个要求(CIM是作者从零开始开发的一个学习型即时通讯系统,参见“拿起键盘就是干的:徒手开发一个分布式即时通讯系统”),只是谈谈我是如何理解即时通讯长连接的心跳和重连机制,以及如何踩坑和填坑。

 

 本文的CIM源代码地址:

 

 主图像:https://github.com/crossoverJie/cim

 备用镜子:https://github.com/52im/cim

 

 阅读本文需要一定的网络编程和网络知识。

 

 有关网络编程的基本知识,请阅读以下材料:

 

 TCP/IP的详细说明-第11章UDP:用户数据报协议

 TCP/IP的详细说明-第17章TCP:传输控制协议

 TCP/IP的详细说明-第18章TCP连接的建立和终止

 TCP/IP的详细说明-第21章TCP的超时和重传

 易于理解——对TCP协议的深刻理解(一):理论基础(推荐)

 惰性网络编程导论(一):网络通信协议的快速理解(上)

 惰性网络编程导论(二):网络通信协议的快速理解(下)

 

 要了解Netty框架,请阅读以下材料:

 

 网络资源在线阅读版(推荐)

 网上原料药文件(推荐)

 初学者:迄今为止对Netty的高性能原则和框架架构的最彻底的分析

 初学者:网络的学习方法和高级策略——一个Java高性能NIO框架

 “放开我!让您在一分钟内了解Java NIO和经典IO之间的区别

 历史上最强大的Java NIO简介:如果你担心开始和放弃,请阅读这篇文章!》

 

 2.关于作者

 

 正确理解即时消息长连接的心跳和重连机制,并实现它(用完整的即时消息源代码)_ aaa.jpg。

 跨界(陈杰):90年代后,毕业于重庆信息工程学院,现就职于重庆猪八戒网络有限公司..

 作者博客:https://crossverjie.top

 作者简介:https://github.com/crossoverJie

 

 本文作者的其他文章:

 

 "拿起键盘是干的:和我一起开发一个分布式即时通讯系统. "

 技术干货:从头开始,教你设计一个百万级的信息推送系统

 

 3.相关文章

 

 关于保持网络心跳的理论文章;

 

 “为什么基于TCP的移动即时消息仍然需要心跳保持机制?》

 “微信团队原创分享:安卓版微信背景保活战斗分享(网络保活文章)”

 “移动即时通讯实践:安卓版微信智能心跳机制的实现”

 “移动即时通讯实践:WhatsApp、Line和微信心跳策略分析”

 了解即时通讯应用中的网络心跳包机制:功能、原理、实现思路等。

 “融云技术共享:安卓即时通讯产品网络链接保持技术在融云的实践”

 

 关于通过网络心跳保持活力的实用文章;

 

 mobileimsdk-一套原始的开源移动即时消息框架(具有完整的心跳保持活动逻辑和代码实现)

 安卓即时通讯智能心跳算法的设计与实现探讨(含示例代码)

 教你用Netty实现网络通讯程序的心跳机制和断线重连机制

 适合初学者:从头开始开发即时消息服务器(基于Netty,带有完整的源代码)

 "拿起键盘是干的:和我一起开发一个分布式即时通讯系统. "

 自己开发即时通讯有这么难吗?手工教你一个Andriod版本的简单即时消息(带源代码)

 

 4.心跳的实现

 

 实际上有两种方法可以实现心跳:

 

 1)TCP协议实现(保活机制,参见TCP/IP详细第1卷:协议-第23章TCP保活定时器);

 2)应用层实现自身。

 

 因为TCP协议太低级,对开发人员来说可维护性和灵活性很差,而且它们还依赖于操作系统(参见:“为什么基于TCP的移动IM仍然需要心跳保持激活机制?).

 

 因此,我们在这里讨论的是应用层的实现:

 正确理解即时消息长连接的心跳和重连机制,并手动实现(带有完整的即时消息源代码)_1.jpg

 

 如上图所示,在应用层,客户端通常会向服务器发送心跳数据包ping,服务器在收到该数据包后会做出响应,表明双方都还活着。一旦其中一个终端延迟n个时间窗口而没有接收到消息,它将被不同地处理。

 5.客户端会自动重新连接

 

 让我们以客户为例。每隔一段时间,客户端向服务器发送一个心跳数据包,并从服务器接收响应。

 

 一般实现应该是:

 

 1)启动常规任务并定期发送心跳包;

 2)收到服务器响应后更新本地时间;

 3)另一个调度任务周期性地检测“本地时间”是否超过阈值;

 4)如果超过,则认为服务器出现故障,需要重新连接。

 

 这真的可以实现心跳,但它并不友好。

 

 在客户端和服务器正常通信的情况下,调度的任务仍然会发送心跳包;这使它变得毫无意义和多余。因此,理想的情况应该是,当客户端收到的写消息空闲时,发送此心跳数据包来确认服务器是否处于活动状态。

 

 好消息是,Netty已经为我们考虑到了这一点,并为心跳处理带来了一个现成的IdleStateHandler。

 

 让我们看看cim中的实现:

 正确理解即时消息长连接的心跳和重连机制,并手动实现(带有完整的即时消息源代码)_2.jpg

 

 将一个IdleStateHandler添加到10秒钟没有收到写消息的管道中,然后他将在ChannelInboundHandler中回调userEventTriggered方法。

 

 正确理解即时消息长连接的心跳和重连机制,并手动实现(带有完整的即时消息源代码)_3.jpg

 

 因此,一旦写入超时,心跳将被立即发送到服务器(更准确地说,心跳发送失败后应该有一定的重试次数)。

 

 这样,心跳数据包只有在空闲时才会被发送。但是,如果长时间没有收到服务器响应,重新连接的逻辑应该写在哪里呢?

 

 让我们先看看这个例子:

 

 当接收到服务器响应的pong消息时,在当前频道上记录一个时间,也就是说,可以在计时任务中取出该时间和当前时间之间的差值,以判断其是否超过阈值。

 

 如果超过,重新连接。

 

 正确理解即时消息长连接的心跳和重连机制,并手动实现(带有完整的即时消息源代码)_4.jpg

 

 正确理解即时消息长连接的心跳和重连机制,并手动实现(带有完整的即时消息源代码)_5.jpg

 

 同时,在每次心跳时,从服务器响应绑定到通道的时间中减去当前时间,以确定是否需要重新连接。

 

 也就是说,heartBeatHandler.process(ctx)。的执行逻辑。

 

 伪代码如下:

 0102030405060708091011 @ Override public void process(ChannelHandlerContext CTX)引发异常{ long HentBeatTime = AppConfiguration . GetHentBeatTime()* 1000;long LastReadTime = NettYatTrutil . GetReaderTime(CTX . channel());long now = System . CurrentiMemillis();if (lastReadTime!=空&& now - lastReadTime >心跳时间){重新连接();}}

 

 

 6.IdleStateHandler误解

 

 一切似乎都很好,但实际上它并没有以这种方式实现重新连接的逻辑。主要问题是对IdleStateHandler的误解。

 

 让我们假设以下场景:

 

 1)客户端通过登录连接到服务器,并保持长时间连接。当一切正常时,双方发送心跳包以保持连接;

 2)此时,服务器突然出现故障,所以理想情况下,客户端很长时间没有收到服务器的响应,所以用户事件触发执行定时任务;

 3)当判断当前时间-更新写时间>阈值时,重新连接。

 

 然而,它并没有像预期的那样成功,也不会执行两三个步骤。

 

 因为一旦服务器关闭或网络与客户端断开,它将回调客户端的信道化事件。

 

 IdleStateHandler作为一个信道化绑定也覆盖了channelInactive()方法。

 

 正确理解即时消息长连接的心跳和重连机制,并手动实现(带有完整的即时消息源代码)_6.jpg

 

 正确理解即时消息长连接的心跳和重连机制,并手动实现(带有完整的即时消息源代码)_7.jpg

 

 此处的destroy()方法将取消之前打开的所有计划任务。因此,将不会执行计划任务,也不会有机会执行此重新连接服务。

 7.可靠的实施

 

 因此,我们必须有一个单独的线程来判断是否需要重新连接,并且不要依赖于IdleStateHandler。

 

 因此,当客户端检测到网络断开时,cim将启动计划任务:

 正确理解即时消息长连接的心跳和重连机制,并手动实现(带有完整的即时消息源代码)_8.jpg

 它在客户端启动之前启动的原因是为了节省一点线程消耗。虽然网络问题是不可避免的,但它可以在需要时节省资源。

 

 

 正确理解即时消息长连接的心跳和重连机制,并手动实现(带有完整的即时消息源代码)_9.jpg

 

 正确理解即时消息长连接的心跳和重连机制,并手动实现(带有完整的即时消息源代码)_10.jpg

 

 在此任务中,实际上执行了重新连接。如果长度有限,具体的代码将不会公布,感兴趣的人可以自己参考。

 

 同时验证效果:

 启动两台服务器,然后启动客户端连接到最后一台服务器并保持长时间连接。此时,服务突然被手动关闭,客户端可以自动重新连接到可用的服务节点。

 

 

 正确理解即时消息长连接的心跳和重连机制,并手动实现(带有完整的即时消息源代码)_11.jpg

 

 正确理解即时消息长连接的心跳和重连机制,并手动实现(带有完整的即时消息源代码)_12.jpg

 

 启动客户端后,服务器还可以接收正常的ping消息:

 使用:info命令检查当前客户端的链接状态,并发现它已连接到端口9000。

 

 

 正确理解即时消息长连接的心跳和重连机制,并手动实现(带有完整的即时消息源代码)_13.jpg

 

 :info是查看一些客户端信息的新命令。

 

 此时,我关闭了连接上的这个节点:

 1kill -9 2142

 

 

 正确理解即时消息长连接的心跳和重连机制,并手动实现(带有完整的即时消息源代码)_14.jpg

 

 正确理解即时消息长连接的心跳和重连机制,并手动实现(带有完整的即时消息源代码)_15.jpg

 

 此时,客户端将自动重新连接到可用节点。该节点还接收在线日志和心跳数据包。

 8.服务器自动拒绝离线客户端

 

 现在,让我们看看服务器。它希望达到的效果是,如果客户端在n秒内没有收到来自客户端的ping数据包,则认为该客户端处于离线状态。在cim场景中,它需要被启动并脱机。

 

 关于信息发送的误解:

 

 当调用ctx.writeAndFlush()发送消息以获得回调时,这里仍然存在误解。

 

 其中,发布成功不能作为消息发送成功的标准:

 正确理解即时消息长连接的心跳和重连机制,并手动实现(带有完整的即时消息源代码)_16.jpg

 

 也就是说,即使客户端直接从网络断开,服务器在这里发送消息后获得的成功仍然是真实的。这是因为这里的成功只告诉我们消息被成功写入了TCP缓冲区。

 

 有不少人和以前有同样的误解。这是内蒂的官方回复:

 正确理解即时消息长连接的心跳和重连机制,并手动实现(带有完整的即时消息源代码)_17.jpg

 

 相关问题:https://github.com/netty/netty/issues/4915

 

 因此,我们不能据此关闭客户端的连接,而是判断通道上的绑定时间与当前时间之差是否超过上述阈值。

 

 正确理解即时消息长连接的心跳和重连机制,并手动实现(带有完整的即时消息源代码)_18.jpg

 

 正确理解即时消息长连接的心跳和重连机制,并手动实现(带有完整的即时消息源代码)_19.jpg

 

 正确理解即时消息长连接的心跳和重连机制,并实现它(用完整的即时消息源代码)_ 20.jpg。

 

 以上是cim服务器的实现。逻辑与开头提到的相同,它类似于Dubbo的心跳机制。

 

 让我们做一个实验:客户端和服务器正常通信,当我直接断开客户端时,服务器会自动移除客户端。

 

 正确理解即时消息长连接的心跳和重连机制,并实现它(用完整的即时消息源代码)_ 21.jpg。

 

 正确理解即时消息长连接的心跳和重连机制,并手动实现(带有完整的即时消息源代码)_22.jpg

 9.本文摘要

 

 这样,就实现了本文开头的两个要求:

 

 1)当检测到客户端没有心跳时,服务器可以主动关闭信道并让其离线;

 2)如果客户端检测到服务器没有响应心跳,它可以重新连接以获得新的连接。

 

 同时,我也踩到了两个误会。一个人踩在坑上是可以的。我希望每一个读过这篇文章的人都能有所收获,避免陷入困境。

 

 本文的所有相关代码都在这里,感兴趣的人可以自己查看:

 

 主图像:https://github.com/crossoverJie/cim

 备用镜子:https://github.com/52im/cim


----------------------------------------------------------------------------------

哇谷im_im即时通讯_私有云_公有云-哇谷云科技官网-JM沟通

IM下载体验 - 哇谷IM-企业云办公IM即时聊天社交系统-JM 沟通下载

IM功能与价格 - 哇谷IM-提供即时通讯IM开发-APP搭建私有化-公有云-私有化云-海外云搭建

新闻动态 - 哇谷IM-即时通讯热门动态博客聊天JM沟通APP

哇谷IM-JM沟通热门动态博客短视频娱乐生活

关于哇谷-哇谷IM-提供企业即时通讯IM开发-语音通话-APP搭建私有化-公有云-私有化云-海外云搭建

联系我们 - 哇谷IM-即时通讯IM私有化搭建提供接口与SDK及哇谷云服务

即时通讯IM融云世界

IM即时通讯钉钉技术:企业IM钉钉在后端架构上的优越之处

新的市场叫板环信、融云、腾讯云!开源版IM即使聊天工具

企业IM即时通讯聊天办公APP钉钉技术分析交流

哇谷云-怎么样正确认识海外云服务器

公有云和私有云之间有什么区别?类似融云、环信云、网易云、哇谷云?