连接保活

#保活 #心跳 #Netty

链路可用性检测

TCP连接是虚拟的. 两个状态机同时维护满足逻辑上认为是连接的状态,我们称之为连接. socket连接 是通过双方分别对IP和Port进行监听, 而实现的实时通信. 所以socket对连接是否畅通是无感知的. 如果网络没有问题, 则连接永远存在,不会断开. 当网络不稳定时,双方需要知道对方是否还在,则需要通过发送消息/ACK,来确认.所以心跳是为了确认连接是否还在.

Netty提供IdleStateHandler,对读写操作进行定时监测, 如果n秒内,ChannelRead()ChannelWrite没有被调用,则触发userEventTrigger() 用户继承ChannlInboundHandlerAdapter,实现userEventTrigger()方法,可以进行超时后的处理. 用户需要自己实现PING-PONG

连接保活

  1. TCP 中的 KeepAlive 机制。KeepAlive 并不是 TCP 协议的一部分,但是大多数操作系统都实现了这个机制。KeepAlive 机制开启后,在一定时间内(一般时间为 7200s,参数tcp_keepalive_time)在链路上没有数据传送的情况下,TCP 层将发送相应的KeepAlive探针以确定连接可用性,探测失败后重试 10(参数tcp_keepalive_probes)次,每次间隔时间 75s(参数tcp_keepalive_intvl),所有探测失败后,才认为当前连接已经不可用
  2. 应用层心跳.就是客户端会开启一个定时任务,定时对已经建立连接的对端应用发送请求(这里的请求是特殊的心跳请求),服务端则需要特殊处理该请求,返回响应。如果心跳持续多次没有收到响应,客户端会认为连接不可用,主动断开连接。不同的服务治理框架对心跳,建连,断连,拉黑的机制有不同的策略,但大多数的服务治理框架都会在应用层做心跳,Dubbo 也不例外。以 Dubbo 为例,支持应用层的心跳,客户端和服务端都会开启一个HeartBeatTask,客户端在HeaderExchangeClient中开启,服务端将在HeaderExchangeServer开启。文章开头埋了一个坑:Dubbo 为什么在服务端同时维护Map呢?主要就是为了给心跳做贡献,心跳定时任务在发现连接不可用时,会根据当前是客户端还是服务端走不同的分支,客户端发现不可用,是重连;服务端发现不可用,是直接 close。

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

发送消息时,网络不稳定造成的影响.

心跳是为了探测链路可用性, 结合重连可以保证当双方进行通信时, 网络尽量是可用的, 而不是断开的. 如果没有实现心跳:

  • 如果网络一直正常,那么连接一直存在.
  • 如果网络出现偶尔波动/闪断, 那么连接也一直存在. 即使闪断瞬间发送消息没有成功, 也会进行多次重试. 所以这种情况下,通信通常都会成功.
  • 如果网络出现长时间断开, 例如网线松动/断电. 默认情况下, 双方不知道连接已经不可用. 这时候发送消息,会失败(如果没有消息应答机制,甚至不知道发送失败). 为了避免第三种情况, 需要进行连接可用性检测与保活重连.
  • 客户端发送心跳,服务端响应, 如果多次没有收到响应,则认为连接不可用,关闭连接, 并进行重连, 如果重连失败, 会多次尝试, 直到重连成功或超过最大重试次数.
  • 服务端一定时间内没有收到客户端发送的心跳, 认为客户端已经断开, 则关闭连接.