
TCP keepAlive 也是在一定时间内(默认2小时)socket 上没有接收到数据时主动断开连接,避免浪费资源,这时远端很可能已经down机了或中间网络有问题。也是通过发送一系列试探包看有没有回应来实现的。
TCP keepAlive 依赖 *** 作系统,默认是关闭的,需要修改 *** 作系统配置打开。
所以在应用层实现心跳包还是必须的。
这个是显而易见的,正常通信时说明两端连接是没有问题的,所以只在空闲的时候发送心跳包。如果每隔固定时间发送就会浪费资源占用正常通信的资源。
假设现在要做一个手机端推送的项目,所有手机通过 TCP 长连接连接到后台服务器。心跳机制是这样的:
看其他博客说不要回复,如果有 10万空闲连接,光回复心跳包就要占用大量资源。服务端读超时后直接关闭连接,客户端再进行重连。
断线重连也很简单就是在 channelInactive 的时候重新 connect 就行了。参考其他博客专门用一个 ChannelInboundHandler 来处理断线重连。
这个 watchDog Handler 应当放在 ChannelPipeline 的最前面
其实客户端和服务端都是相对的,这个看应用场景。如果客户端想要及时处理断网,路由故障等情况就需要接受服务端发来的心跳来检测。像断网,路由故障这种情况,两边都不知道TCP连接的状态,必须靠心跳。长连接服务端一般都要接收心跳包的,如果没有心跳可能会有大量的无效连接,直接耗尽服务器资源,无效的连接要尽早关闭掉。
DEMO:
https://github.com/lesliebeijing/Netty-Demo
基于 Netty 写的一个简单的推送 DEMO,可用在手机端推送
https://github.com/lesliebeijing/EncPush
Netty 客户端用在 Android 中也很稳定,我们的物联网项目Android和后台都是用的 Netty。
为什么要心跳检查?
因为目前讨论的数据连接场景,都是无源连接,排除NAT的情况,连接就是存在于src和dest两端OS中的状态机,为什么会要用无源连接呢,有源是连接建立带宽就分配好了,不传有效数据这个带宽也被占用着,这不就浪费了,虚拟信号时代的电话就是有源的。
心跳检查是两端都要做的,不做的那一端一样存在状态不对而不自知的情况。
状态机在两端是有可能不一致的,比如一端认为这条连接已经销毁,另外一端可能认为仍有效。心跳机制的作用之一就是解决这种不一致的情况,类似“校对”的作用。
在浏览器上请求一个需要长时间才得到结果的请求,最后返回超时错误。
你说的情况我们经常遇到,我估计你说的就是客户端自己的超时设置,也就是如果http响应无法在限定时间内完成(比如1秒内),那么客户端应用程序自己会报timeout错误。
这应该不是中间设备做了reset,因为如果是那样,这个中间设备应该会给客户端和服务端都发TCP RST,然后客户端报错会变成ECONNRESET这种(ECONNRESET是linux的网络协议栈报给用户空间程序的报错)。
心跳消息显示push ack是为何呢?
按照TCP协议(参考RFC793)规定, *** 作系统收到有PSH标志位的报文后,这些报文不会被驻留在接收缓冲区,而是要立即通知用户空间程序来接收。这样对于用户空间程序及时收发处理心跳报文是有利的。
AWS的nlb keep-alive最大是350s,我们的服务端keep-alive是默认配置,客户端使用了连接池,也进行了探测,但是有个骚 *** 作是随机选取池子里面的一个链接进行探测,然后那些超过350s还没探测的链接,再次使用就出问题了。
你说的“随机选取池子里面的一个链接进行探测”,我判断是健康检查,就是业务健康性(类似k8s的readiness probe),而不是keepalive。
如果是这样的话,这个健康检查确实不是为保活而生的,也很难达到保活的目的。建议这样做设置:client idle timeout <LB idle timeout <server idle timeout,这样可以尽可能的保证不出现连接失效的情况。
这里说的“保活时间”,不是心跳包的间隔,而是空闲保活时间,idle timeout。
保活机制:心跳包异常导致应用重启?
https://time.geekbang.org/column/article/482610
如何解决 Keep-Alive 导致 ECONNRESET 的问题
https://zhuanlan.zhihu.com/p/86953757
解决使用 KeepAlive Agent 遇到的 ECONNRESET
https://zhuanlan.zhihu.com/p/34147188
tcp的断线检测,分为两种:
① 利用tcp自带的keep –alive机制
② 自己组建心跳包的方式向对端发送
通过Keep-alive机制对tcp的连接保持,也就是Tcp的心跳包,见MSDN:
If keep-alive is enabled for a TCP socket with SO_KEEPALIVE, then the default TCP settings are used for the keep-alive timeout and interval unless these values have been changed by calling the WSAIoctl function with the SIO_KEEPALIVE_VALS option. The default settings when a TCP socket is initialized sets the keep-alive timeout to 2 hours and the keep-alive interval to 1 second.
也就是说协议栈会在2小时后发送向对端发送请求包。默认情况下,此Keep-alive机制是关闭的。
Keep-alive默认下是关闭的,也就是本端与对端是除非程序主动send,是不会发送数据包(心跳包),既是,处理本端与对端的系统里的socket状态是不会变化,这里,如果对端当机(或者网线断掉),本端是无法知道对端socket已经关闭,所以本端的socket会一直的存在。
通过实验发现,客户端网线拔掉之后,此时服务端的连接依然存在。
所以,tcp只是数据的发送与接收,包括握手,断开以及rst,time_wait,close_wait 等等。
心跳包之所以叫心跳包是因为:它像心跳一样每隔固定时间发一次,以此来告诉服务器,这个客户端还活着。事实上这是为了保持长连接,至于这个包的内容,是没有什么特别规定的,不过一般都是很小的包,或者只包含包头的一个空包。
在TCP的机制里面,本身是存在有心跳包的机制的,也就是TCP的选项:SO_KEEPALIVE。系统默认是设置的2小时的心跳频率。但是它检查不到机器断电、网线拔出、防火墙这些断线。而且逻辑层处理断线可能也不是那么好处理。一般,如果只是用于保活还是可以的。
心跳包一般来说都是在逻辑层发送空的echo包来实现的。下一个定时器,在一定时间间隔下发送一个空包给客户端,然后客户端反馈一个同样的空包回来,服务器如果在一定时间内收不到客户端发送过来的反馈包,那就只有认定说掉线了。其实,要判定掉线,只需要send或者recv一下,如果结果为零,则为掉线。
但是,在长连接下,有可能很长一段时间都没有数据往来。理论上说,这个连接是一直保持连接的,但是实际情况中,如果中间节点出现什么故障是难以知道的。更要命的是,有的节点(防火墙)会自动把一定时间之内没有数据交互的连接给断掉
。在这个时候,就需要我们的心跳包了,用于维持长连接,保活。
在获知了断线之后,服务器逻辑可能需要做一些事情,比如断线后的数据清理呀,重新连接呀……当然,这个自然是要由逻辑层根据需求去做了。
总的来说,心跳包主要也就是用于长连接的保活和断线处理。一般的应用下,判定时间在30-40秒比较不错。如果实在要求高,那就在6-9秒。
1、 客户端每隔一个时间间隔发生一个探测包给服务器
2、 客户端发包时启动一个超时定时器
3、 服务器端接收到检测包,应该回应一个包
4、 如果客户机收到服务器的应答包,则说明服务器正常,删除超时定时器
5、 如果客户端的超时定时器超时,依然没有收到应答包,则说明服务器挂了
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)