TCP连接探测中的Keepalive和心跳包,长连接和心跳的那些事儿

图片 6

TCP连接探测中的Keepalive和心跳包,长连接和心跳的那些事儿

图片 1

长连接和心跳那些事儿

简书 涤生。
转载请注明原创出处,谢谢!
如果读完觉得有收获的话,欢迎点赞加关注。

1. TCP保活的必要性

可能很多 Java 程序员对 TCP
的理解只有一个三次握手,四次挥手的认识,我觉得这样的原因主要在于 TCP
协议本身稍微有点抽象(相比较于应用层的 HTTP
协议);其次,非框架开发者不太需要接触到 TCP 的一些细节。其实我个人对
TCP
的很多细节也并没有完全理解,这篇文章主要针对微信交流群里有人提出的长连接,心跳的问题,做一个统一的整理。

介绍

  • 长连接
    首先这里所说的连接是指网络传输层的使用TCP协议经过三次握手建立的连接;长连接是指建立的连接长期保持,不管此时有无数据包的发送;有长连接自然也有短连接,短连接是指双方有数据发送时,就建立连接,发送几次请求后,就主动或者被动断开连接。

  • 心跳
    心跳这个名字比较形象,就像人体心跳一样,是用来检测一个系统是否存活或者网络链路是否通畅的一种方式,其一般做法是定时向被检测系统发送心跳包,被检测系统收到心跳包进行回复,收到回复说明对方存活。

心跳和长连接在一起介绍的原因是,心跳能够给长连接提供保活功能,能够检测长连接是否正常(这里所说的保活不能简单的理解为保证活着,具体来说应该是一旦链路死了,不可用了,能够尽快知道,然后做些其他的高可用措施,来保证系统的正常运行)。

1) 很多防火墙等对于空闲socket自动关闭

在 Java 中,使用 TCP 通信,大概率会涉及到
Socket、Netty,本文会借用它们的一些 API 和设置参数来辅助介绍。

优势

长连接的优势

  • 减少连接建立过程的耗时
    大家都知道TCP连接建立需要三次握手,三次握手也就说需要三次交互才能建立一个连接通道,同城的机器之间的大概是ms级别的延时,影响还不大,如果是北京和上海两地机房,走专线一来一回大概需要30ms,如果使用长连接,这个优化还是十分可观的。
  • 方便实现push数据
    数据交互-推模式实现的前提是网络长连接,有了长连接,连接两端很方便的互相push数据,来进行交互。

2) 对于非正常断开, 服务器并不能检测到. 为了回收资源, 必须提供一种检测机制.

长连接与短连接

疑问

  • TCP连接到底是什么?
    所谓的TCP连接不是物理的连接,是为了实现数据的可靠传输由通信双方进行三次握手交互而建立的逻辑上的连接,通信双方都需要维护这样的连接状态信息。比如netstat经常看到连接的状态为ESTABLISHED,表示当前处于连接状态。(这里需要注意的是这个ESTABLISHED的连接状态只是操作系统认为当前还处在连接状态)

  • 是不是建立了长连接,就可以高枕无忧了呢?
    建立好长连接,两端的操作系统都维护了连接已经建立的状态,是不是这时向对端发送数据一定能到达呢?
    答案是否定的。
    可能此时链路已经不通,只是TCP层还没有感知到这一信息,操作系统层面显示的状态依然是连接状态,而且因为TCP层还认为连接是ESTABLISHED,所以作为应用层自然也就无法感知当前的链路不通。
    这种情况会导致什么问题?
    如果此时有数据想要传输,显然,数据是无法传送到对端,但是TCP协议为了保证可靠性,会重传请求,如果问题只是网线接头松了,导致网络不通,此时如果及时将网线接头接好,数据还是能正常到达对端,且TCP的连接依然是ESTABLISHED,不会有任何变化。但不是任何时候都这么巧,有时就是某段链路瘫痪了,或者主机挂了,系统异常关闭了等。这时候如果应用系统不能感知到,是件很危险的事情。

  • 长连接怎么保活?
    TCP协议实现中,是有保活机制的,也就是TCP的KeepAlive机制(此机制并不是TCP协议规范中的内容,由操作系统去实现),KeepAlive机制开启后,在一定时间内(一般时间为7200s,参数tcp_keepalive_time)在链路上没有数据传送的情况下,TCP层将发送相应的KeepAlive探针以确定连接可用性,探测失败后重试10(参数tcp_keepalive_probes)次,每次间隔时间75s(参数tcp_keepalive_intvl),所有探测失败后,才认为当前连接已经不可用。这些参数是机器级别,可以调整。

  • 应用层需要做点什么吗?
    按照TCP的KeepAlive机制,默认的参数,显然不能满足要求。那是不是调小点就可以了呢?
    调整参数,当然是有用的,但是首先参数的机器级别的,调整起来不太方便,更换机器还得记得调整参数,对系统的使用方来说,未免增加了维护成本,而且很可能忘记;其次由于KeepAlive的保活机制只在链路空闲的情况下才会起到作用,假如此时有数据发送,且物理链路已经不通,操作系统这边的链路状态还是ESTABLISHED,这时会发生什么?自然会走TCP重传机制,要知道默认的TCP超时重传,指数退避算法也是一个相当长的过程。因此,一个可靠的系统,长连接的保活肯定是要依赖应用层的心跳来保证的。
    这里应用层的心跳举个例子,比如客户端每隔3s通过长连接通道发送一个心跳请求到服务端,连续失败5次就断开连接。这样算下来最长15s就能发现连接已经不可用,一旦连接不可用,可以重连,也可以做其他的failover处理,比如请求其他服务器。
    应用层心跳还有个好处,比如某台服务器因为某些原因导致负载超高,CPU飙高,或者线程池打满等等,无法响应任何业务请求,如果使用TCP自身的机制无法发现任何问题,然而对客户端而言,这时的最好选择就是断连后重新连接其他服务器,而不是一直认为当前服务器是可用状态,向当前服务器发送一些必然会失败的请求。

 

TCP 本身并没有长短连接的区别,长短与否,完全取决于我们怎么用它。

设计误区

  • 无心跳
    无心跳的设计,也是很常见的,为了省事,长连接断开,TCP传输层有通知,应用程序只要处理这种通知,一旦发现连接异常,就重连。但是此类通知可能来的特别晚,比如在机器奔溃,应用程序异常退出,链路不通等情况下。

  • 被连接方检测心跳
    心跳的实现分为心跳的发送和心跳的检测,心跳由谁来发都可以,也可以双方都发送,但是检测心跳,必须由发起连接的这端进行,才安全。因为只有发起连接的一端检测心跳,知道链路有问题,这时才会去断开连接,进行重连,或者重连到另一台服务器。
    例如,client去连接server,client定时发送心跳到server,server检测心跳,发现一段时间client没有传心跳过来,认为与client的链路已经出了问题或者client自身就已经出了问题。粗看上去貌似没什么问题,但是如果只是client与当前这个server之间的链路出了问题,作为一个高可用的系统,是不是应该还有另一个server作为备选,问题出在短时间内client根本不知道自己和第一个server出了问题,所以也不会主动去连接第二个server。

  • 第三方心跳
    还有一类心跳,使用第三方保活,也就是除了客户端和服务端之外,还有另一台机器,定时发送心跳去探测服务端的存活。这类探活方法使用在检测系统的存活与否的问题上是没有问题的,但是这类设计是无法用来检测客户端和服务端之间链路的好坏。

2. 导致TCP断连的因素

短连接:每次通信时,创建 Socket;一次通信结束,调用
socket.close()。这就是一般意义上的短连接,短连接的好处是管理起来比较简单,存在的连接都是可用的连接,不需要额外的控制手段。

参考方案

  • 方案一
    最简单的策略当然是客户端定时n秒发送心跳包,服务端收到心跳包后,回复客户端的心跳,如果客户端连续m秒没有收到心跳包,则主动断开连接,然后重连,将正常的业务请求暂时不发送的该台服务器上。

  • 方案二
    可能有人觉得,这样是不是传送一些无效的数据包有点多,是不是可以优化下,说实话,个人认为其实一点也不多。当然是可以做些优化的,因为心跳就是一种探测请求,业务上的正常请求除了做业务处理外,还可以用作探测的功能,比如此时有请求需要发送到服务端,这个请求就可以当作是一次心跳,服务端收到请求,处理后回复,只要服务端有回复,就表明链路还是通的,如果客户端请求比较空闲的时候,服务端一直没有数据回复,就使用心跳进行探测,这样就有效利用了正常的请求来作为心跳的功能,减少无效的数据传输。

图片 2

欢迎关注微信公共号

如果网络正常, socket也通过close操作来进行优雅的关闭, 那么一切完美. 可是有很多情况, 比如网线故障, 客户端一侧突然断电或者崩溃等等, 这些情况server并不能正常检测到连接的断开. 

长连接:每次通信完毕后,不会关闭连接,这样就可以做到连接的复用。长连接的好处便是省去了创建连接的耗时。

 

短连接和长连接的优势,分别是对方的劣势。想要图简单,不追求高性能,使用短连接合适,这样我们就不需要操心连接状态的管理;想要追求性能,使用长连接,我们就需要担心各种问题:比如端对端连接的维护,连接的保活。

3. 保活的两种方式:

长连接还常常被用来做数据的推送,我们大多数时候对通信的认知还是
request/response 模型,但 TCP
双工通信的性质决定了它还可以被用来做双向通信。在长连接之下,可以很方便的实现
push 模型。

1) 应用层面的心跳机制

短连接没有太多东西可以讲,所以下文我们将目光聚焦在长连接的一些问题上。纯讲理论未免有些过于单调,所以下文我借助
Dubbo 这个 RPC 框架的一些实践来展开 TCP 的相关讨论。

自定义心跳消息头. 一般客户端主动发送, 服务器接收后进行回应(也可以不回应). 这里不进行详述.

服务治理框架中的长连接

PS: 有人从软件的功能角度列出第三种方式, 就是通过第三方软件来进行探测, 确定连接的有效性. 这种方式局限性很大, 而且不属于软件内部的功能实现. 不进行讨论.

前面已经提到过,追求性能的时候,必然会选择使用长连接,所以借助 Dubbo
可以很好的来理解 TCP。我们开启两个 Dubbo 应用,一个 server 负责监听本地
20880(众所周知,这是 Dubbo 协议默认的端口),一个 client
负责循环发送请求。执行 lsof-i:20880 命令可以查看端口的相关使用情况:

2) TCP协议自带的保活功能

图片 3

打开keep-alive功能即可. 具体属性也可以通过API设定.

*:20880 说明了 Dubbo 正在监听本地的 20880 端口,处理发送到本地 20880
端口的请求

 

后两条信息说明请求的发送情况,验证了 TCP
是一个双向的通信过程,由于我是在同一个机器开启了两个 Dubbo
应用,所以你能够看到是本地的 53078 端口与 20880
端口在通信。我们并没有手动设置 53078
这个客户端端口,他是随机的,但也阐释了一个道理:即使是发送请求的一方,也需要占用一个端口。

4. 两种方式的优劣性

稍微说一下 FD
这个参数,他代表了文件句柄,每新增一条连接都会占用新的文件句柄,如果你在使用
TCP 通信的过程中出现了 open too many files
的异常,那就应该检查一下,你是不是创建了太多的连接,而没有关闭。细心的读者也会联想到长连接的另一个好处,那就是会占用较少的文件句柄。

TCP协议自带的保活功能, 使用起来简单, 减少了应用层代码的复杂度. 推测也会更节省流量, 因为一般来说应用层的数据传输到协议层时都会被加上额外的包头包尾. 由TCP协议提供的检活, 其发的探测包, 理论上实现的会更精妙(用更少的字节完成更多的目标), 耗费更少的流量.

长连接的维护

由应用自己实现的应用层的心跳, 为心跳消息额外定义一个消息类型就可以了. 就是应用正常的消息包, 只是这个包特殊点, 专门用来检活而已, 通常比较小, 可能只有消息头就可以了, 除非需要额外的信息. 

因为客户端请求的服务可能分布在多个服务器上,客户端端自然需要跟对端创建多条长连接,使用长连接,我们遇到的第一个问题就是要如何维护长连接。//客户端
public class NettyHandler extends SimpleChannelHandler { private final
Map

应用层心跳的好处我个人的理解有两点: 

channels = new ConcurrentHashMap

一是比较灵活, 因为协议层的心跳只能提供最纯粹的检活功能, 但是应用层自己可以随意控制, 包括协议可能提供的是秒级的, 但是你想做成毫秒级的都任意(虽然实际几乎不会有这种时间级别的心跳), 包里还甚至可以携带额外的信息, 这些都是灵活之处.

(); //

二是通用, 应用层的心跳不依赖协议. 如果有一天不用TCP要改为UDP了, 协议层不提供心跳机制了, 但是你应用层的心跳依旧是通用的, 可能只需要做少许改动就可以继续使用.

} //服务端 public class NettyServer extends AbstractServer implements
Server { private Map

应用层心跳的不好的地方也很显而易见, 增加开发工作量, 由于应用特定的网络框架, 还可能很增加代码结构的复杂度. 再就是根据上面的推测, 应用层心跳的流量消耗还是更大的, 毕竟这本质上还是个普通的数据包.

channels; //

 

}

5. 到底选用那种心跳方式?

在 Dubbo 中,客户端和服务端都使用 ip:port 维护了端对端的长连接,Channel
便是对连接的抽象。我们主要关注 NettyHandler
中的长连接,服务端同时维护一个长连接的集合是 Dubbo
的设计,我们将在后面提到。

优劣点第4节已经进行了阐述, 因此如果能确定你们更换协议的可能性非常小, 同时只是需要检活的功能, 那么用协议自带的就绝对OK了, 使用简单而且高效. 有些自负的人总喜欢用自己搞的, 来代替成熟协议自带的东西, 代替系统内核提供的东西, 其实往往你应用层实现的东西, 都是更拙劣的. 网上看了一些关于协议的Keep-alive不靠谱的说法, 也都比较空想和想当然, 都没有拿出任何事实论据或实验数据. 这点大家有见解, 欢迎交流哈~

连接的保活

 

这个话题就有的聊了,会牵扯到比较多的知识点。首先需要明确一点,为什么需要连接的报活?当双方已经建立了连接,但因为网络问题,链路不通,这样长连接就不能使用了。需要明确的一点是,通过
netstat,lsof 等指令查看到连接的状态处于 ESTABLISHED
状态并不是一件非常靠谱的事,因为连接可能已死,但没有被系统感知到,更不用提假死这种疑难杂症了。如果保证长连接可用是一件技术活。

6. 类Unix平台如何使用Keep-alive

连接的保活:KeepAlive

keepalive默认是关闭的, 因为虽然流量极小, 毕竟是开销. 因此需要用户手动开启. 有两种方式开启.

首先想到的是 TCP 中的 KeepAlive 机制。KeepAlive 并不是 TCP
协议的一部分,但是大多数操作系统都实现了这个机制。KeepAlive
机制开启后,在一定时间内(一般时间为 7200s,参数
tcp_keepalive_time)在链路上没有数据传送的情况下,TCP
层将发送相应的KeepAlive探针以确定连接可用性,探测失败后重试 10(参数
tcp_keepalive_probes)次,每次间隔时间 75s(参数
tcp_keepalive_intvl),所有探测失败后,才认为当前连接已经不可用。

1) 在代码里针对每个socket进行单独设定, 使用起来灵活.

在 Netty 中开启 KeepAlive:bootstrap.option(ChannelOption.TCP_NODELAY,
true)

除了keepAlive 开关, 还有keepIdle, keepInterval, keepCount 3个属性, 使用简单, 如下:

Linux 操作系统中设置 KeepAlive 相关参数,修改 /etc/sysctl.conf
文件:net.ipv4.tcp_keepalive_time=90

 

net.ipv4.tcp_keepalive_intvl=15

[cpp] view
plain copy

net.ipv4.tcp_keepalive_probes=2

 

KeepAlive
机制是在网络层面保证了连接的可用性,但站在应用框架层面我们认为这还不够。主要体现在两个方面:

 图片 4图片 5

KeepAlive
的开关是在应用层开启的,但是具体参数(如重试测试,重试间隔时间)的设置却是操作系统级别的,位于操作系统的
/etc/sysctl.conf 配置中,这对于应用来说不够灵活。

  1. int keepAlive = 1;   // 开启keepalive属性. 缺省值: 0(关闭)  
  2. int keepIdle = 60;   // 如果在60秒内没有任何数据交互,则进行探测. 缺省值:7200(s)  
  3. int keepInterval = 5;   // 探测时发探测包的时间间隔为5秒. 缺省值:75(s)  
  4. int keepCount = 2;   // 探测重试的次数. 全部超时则认定连接失效..缺省值:9(次)  
  5. setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, (void*)&keepAlive, sizeof(keepAlive));  
  6. setsockopt(s, SOL_TCP, TCP_KEEPIDLE, (void*)&keepIdle, sizeof(keepIdle));  
  7. setsockopt(s, SOL_TCP, TCP_KEEPINTVL, (void*)&keepInterval, sizeof(keepInterval));  
  8. setsockopt(s, SOL_TCP, TCP_KEEPCNT, (void*)&keepCount, sizeof(keepCount));  

KeepAlive
的保活机制只在链路空闲的情况下才会起到作用,假如此时有数据发送,且物理链路已经不通,操作系统这边的链路状态还是
ESTABLISHED,这时会发生什么?自然会走 TCP 重传机制,要知道默认的 TCP
超时重传,指数退避算法也是一个相当长的过程。

 

KeepAlive
本身是面向网络的,并不是面向于应用的,当连接不可用时,可能是由于应用本身
GC 问题,系统 load
高等情况,但网络仍然是通的,此时,应用已经失去了活性,所以连接自然应该认为是不可用的。

使用时需要#include <netinet/tcp.h>, 否则SOL_TCP和TCP_KEEPIDLE等3个宏找不到.

看来,应用层面的连接保活还是必须要做的。

ps: 忍不住吐槽一下, 网上大量毫不负责的转载, 千篇一律的搜索结果,
很多人根本都没进行过任何验证吧. 为了找这么个头文件都费了不小的事.
大多数帖子里的说的都是不可用的.

连接的保活:应用层心跳

2) 修改配置文件, 对整个系统所有的socket有效.

终于点题了,文题中提到的心跳便是一个本文想要重点强调的另一个 TCP
相关的知识点。上一节我们已经解释过了,网络层面的 KeepAlive
不足以支撑应用级别的连接可用性,本节就来聊聊应用层的心跳机制是实现连接保活的。

我们可以用cat命令查看到系统中这几个默认的值.

如何理解应用层的心跳?简单来说,就是客户端会开启一个定时任务,定时对已经建立连接的对端应用发送请求(这里的请求是特殊的心跳请求),服务端则需要特殊处理该请求,返回响应。如果心跳持续多次没有收到响应,客户端会认为连接不可用,主动断开连接。不同的服务治理框架对心跳,建连,断连,拉黑的机制有不同的策略,但大多数的服务治理框架都会在应用层做心跳,Dubbo
也不例外。

#cat /proc/sys/net/ipv4/tcp_keepalive_time  7200  

应用层心跳的设计细节

#cat /proc/sys/net/ipv4/tcp_keepalive_intvl  75  

以 Dubbo 为例,支持应用层的心跳,客户端和服务端都会开启一个
HeartBeatTask,客户端在 HeaderExchangeClient 中开启,服务端将在
HeaderExchangeServer 开启。文章开头埋了一个坑:Dubbo
为什么在服务端同时维护 Map

#cat /proc/sys/net/ipv4/tcp_keepalive_probes  9

呢?主要就是为了给心跳做贡献,心跳定时任务在发现连接不可用时,会根据当前是客户端还是服务端走不同的分支,客户端发现不可用,是重连;服务端发现不可用,是直接
close。// HeartBeatTask

修改它们:

if (channel instanceof Client) {

#echo 60 > /proc/sys/net/ipv4/tcp_keepalive_time  

channel).reconnect();

#echo 5 > /proc/sys/net/ipv4/tcp_keepalive_intvl  

} else {

#echo 3 > /proc/sys/net/ipv4/tcp_keepalive_probes

channel.close();

 

}

链接推荐:

熟悉其他 RPC
框架的同学会发现,不同框架的心跳机制真的是差距非常大。心跳设计还跟连接创建,重连机制,黑名单连接相关,还需要具体框架具体分析。

闲说HeartBeat心跳包和TCP协议的KeepAlive机制

除了定时任务的设计,还需要在协议层面支持心跳。最简单的例子可以参考 nginx
的健康检查,而针对 Dubbo
协议,自然也需要做心跳的支持,如果将心跳请求识别为正常流量,会造成服务端的压力问题,干扰限流等诸多问题。

TCP Keepalive
HOWTO

图片 6

其中 Flag 代表了 Dubbo 协议的标志位,一共 8
个地址位。低四位用来表示消息体数据用的序列化工具的类型(默认
hessian),高四位中,第一位为1表示是 request 请求,第二位为 1
表示双向传输(即有返回response),第三位为 1
表示是心跳事件。心跳请求应当和普通请求区别对待。

 

注意和 HTTP 的 KeepAlive 区别对待

很多应用层协议都有HeartBeat机制,通常是客户端每隔一小段时间向服务器发送一个数据包,通知服务器自己仍然在线,并传输一些可能必要的数据。使用心跳包的典型协议是IM,比如QQ/MSN/飞信等协议。

HTTP 协议的 KeepAlive
意图在于连接复用,同一个连接上串行方式传递请求-响应数据

学过TCP/IP的同学应该都知道,传输层的两个主要协议是UDP和TCP,其中UDP是无连接的、面向packet的,而TCP协议是有连接、面向流的协议。

TCP 的 KeepAlive 机制意图在于保活、心跳,检测连接错误。

所以非常容易理解,使用UDP协议的客户端(例如早期的“OICQ”,听说OICQ.com这两天被抢注了来着,好古老的回忆)需要定时向服务器发送心跳包,告诉服务器自己在线。

这压根是两个概念。

然而,MSN和现在的QQ往往使用的是TCP连接了,尽管TCP/IP底层提供了可选的KeepAlive(ACK-ACK包)机制,但是它们也还是实现了更高层的心跳包。似乎既浪费流量又浪费CPU,有点莫名其妙。

KeepAlive 常见异常

具体查了下,TCP的KeepAlive机制是这样的,首先它貌似默认是不打开的,要用setsockopt将SOL_SOCKET.SO_KEEPALIVE设置为1才是打开,并且可以设置三个参数tcp_keepalive_time/tcp_keepalive_probes/tcp_keepalive_intvl,分别表示连接闲置多久开始发keepalive的ack包、发几个ack包不回复才当对方死了、两个ack包之间间隔多长,在我测试的Ubuntu
Server
10.04下面默认值是7200秒(2个小时,要不要这么蛋疼啊!)、9次、75秒。于是连接就了有一个超时时间窗口,如果连接之间没有通信,这个时间窗口会逐渐减小,当它减小到零的时候,TCP协议会向对方发一个带有ACK标志的空数据包(KeepAlive探针),对方在收到ACK包以后,如果连接一切正常,应该回复一个ACK;如果连接出现错误了(例如对方重启了,连接状态丢失),则应当回复一个RST;如果对方没有回复,服务器每隔intvl的时间再发ACK,如果连续probes个包都被无视了,说明连接被断开了。

启用 TCP KeepAlive 的应用程序,一般可以捕获到下面几种类型错误

这里有一篇非常详细的介绍文章:  ,包括了KeepAlive的介绍、相关内核参数、C编程接口、如何为现有应用(可以或者不可以修改源码的)启用KeepAlive机制,很值得详读。

ETIMEOUT 超时错误,在发送一个探测保护包经过 (tcpkeepalivetime +
tcpkeepaliveintvl * tcpkeepaliveprobes)时间后仍然没有接收到 ACK
确认情况下触发的异常,套接字被关闭 java
java.io.IOException:Connectiontimedout

这篇文章的2.4节说的是“Preventing disconnection due to network
inactivity”,阻止因网络连接不活跃(长时间没有数据包)而导致的连接中断,说的是,很多网络设备,尤其是NAT路由器,由于其硬件的限制(例如内存、CPU处理能力),无法保持其上的所有连接,因此在必要的时候,会在连接池中选择一些不活跃的连接踢掉。典型做法是LRU,把最久没有数据的连接给T掉。通过使用TCP的KeepAlive机制(修改那个time参数),可以让连接每隔一小段时间就产生一些ack包,以降低被T掉的风险,当然,这样的代价是额外的网络和CPU负担。

EHOSTUNREACH host unreachable错误,这个应该是 ICMP 汇报给上层应用的。
java java.io.IOException:Noroute to host

前面说到,许多IM协议实现了自己的心跳机制,而不是直接依赖于底层的机制,不知道真正的原因是什么。

链接被重置,终端可能崩溃死机重启之后,接收到来自服务器的报文,然物是人非,前朝往事,只能报以无奈重置宣告之。
java java.io.IOException:Connectionresetbypeer

就我看来,一些简单的协议,直接使用底层机制就可以了,对上层完全透明,降低了开发难度,不用管理连接对应的状态。而那些自己实现心跳机制的协议,应该是期望通过发送心跳包的同时来传输一些数据,这样服务端可以获知更多的状态。例如某些客户端很喜欢收集用户的信息……反正是要发个包,不如再塞点数据,否则包头又浪费了……

总结

大概就是这样吧,如果有大牛知道真正的原因,还望不吝赐教。

有三种使用 KeepAlive 的实践方案:默认情况下使用 KeepAlive 周期为 2
个小时,如不选择更改,属于误用范畴,造成资源浪费:内核会为每一个连接都打开一个保活计时器,N
个连接会打开 N 个保活计时器。 优势很明显:

@2012-04-21 

TCP 协议层面保活探测机制,系统内核完全替上层应用自动给做好了

p.s.
通过咨询某个做过IM的同事,参考答案应该是,自己实现的心跳机制通用,可以无视底层的UDP或TCP协议。如果只是用TCP协议的话,那么直接使用KeepAlive机制就足够了。

内核层面计时器相比上层应用,更为高效

@2015-09-14
补充一下 @Jack的回复:
“心跳除了说明应用程序还活着(进程还在,网络通畅),更重要的是表明应用程序还能正常工作。而
TCP keepalive
有操作系统负责探查,即便进程死锁,或阻塞,操作系统也会如常收发 TCP
keepalive 消息。对方无法得知这一异常。摘自《Linux 多线程服务端编程》”

上层应用只需要处理数据收发、连接异常通知即可

数据包将更为紧凑

转载请注明出自  ,如是转载文则注明原出处,谢谢:)

关闭 TCP 的
KeepAlive,完全使用应用层心跳保活机制。由应用掌管心跳,更灵活可控,比如可以在应用级别设置心跳周期,适配私有协议。

1.KeepAlive机制很多情况无法检测出来,如网络连接被软件禁用等,不够可靠,网络状态复杂的情况下这种情况尤其严重。
2.自己实现心跳可以加入更灵活与实用的机制,比如少了一个心跳,可以马上再次检查,检查间隔递减,这样可以更快的感知网络状态,而不是等待固定的时间。

业务心跳 + TCP KeepAlive 一起使用,互相作为补充,但 TCP
保活探测周期和应用的心跳周期要协调,以互补方可,不能够差距过大,否则将达不到设想的效果。

 

各个框架的设计都有所不同,例如 Dubbo 使用的是方案三,但阿里内部的 HSF
框架则没有设置 TCP 的
KeepAlive,仅仅由应用心跳保活。和心跳策略一样,这和框架整体的设计相关。

应用级别的心跳优点在于它们能够使你了解两端的应用都是否存活,而不在于只是通信软件.

 

TCP 的 keep alive
非常耗费资源。一般来说服务器的CPU计算资源远比内存和IO多,所以我看到很多web服务器都把keep
alive设得很低,这样在有需要时重新建立连接,消耗CPU以求得更多可以使用的内存。可能和你说的不是一个东西……
问题是,比起底层的协议来说,在应用层实现的心跳肯定更耗资源啊。所以我觉得关键是,耗这个资源有没有必要;如果耗了资源,怎样尽量提高被消耗资源的利用率,对吧。

 

admin

网站地图xml地图