笔记:分布式系统中心跳协议的设计

1 分布式系统中是否需要应用层心跳?

在采用TCP连接作为进程间通信方式的分布式系统中。当任意一方进程意外退出的时候,对方能及时得到连接断开的通知,操作系统会关闭进程中使用的TCP scoket,会往对方发送FIN分节。keepalive并不是TCP协议规范的一部分,但在几乎所有的TCP/IP协议栈(不管是Linux还是Windows)中,都实现了keepalive。有了keepalive是不是就不需要应用层心跳了呢?答案是还是需要。心跳除了证明程序还活着之外,更重要的一点是证明程序可以正常工作。keepalive有操作系统处理,当进程发生阻塞或死锁,操作系统还是可以正常收发keepalive消息,而连接的另一端并不知道进程异常。另外还有几个异常情况,TCP keepalive 无法解决:

  • 1) 如果操作系统崩溃或机器硬件故障导致机器重启,则没有机会发送FIN分节;
  • 2) 如果是网络故障,连接双方得知这一情况的唯一方式是检测心跳超时。

所以为了保证程序一直在正常工作,应用层心跳是必须的。

2 分布式系统中心跳的设计和实现

心跳协议的基本形式是:如果进程C依赖进程S,那S应当规律性向C发送心跳,而C检查心跳。一般是服务端向客户端发送心跳,根据实际情况也可以反过来,客户端向服务端发送心跳。心跳检测很简单:如果当前时间与Receiver最后一次收到心跳的时间超过设定的timeout即为超时,判断Sender心跳失效。分布式系统没有全局的瞬时状态,不存在立刻判断对方出现故障的方法,这是分布式系统的本质困难。
心跳消息应该包含发送方的标识符。建议也包含当前负载,便于客户端做负载均衡。由于每个程序对“负载”的定义不同,因此心跳消息的格式也就各不相同。我认为可以在某些公共字段的基础上增加应用程序的特定字段,而不要强行规定全部程序都用相同的心跳消息格式。
如果Sender和Receiver之间有其他消息中转进程,那么还应该在心跳消息中加上Sender的发送时间,防止消息在传输过程中堆积而导致假心跳。

为了防止伪心跳,在心跳协议的实现上有2关键点:

  • 1) 要在工作线程发送,不要单独起一个“心跳线程”。
  • 2) 与业务消息用同一个连接,不要单独用“心跳连接”。

对于第1点,这是防止工作线程死锁或阻塞时还在继续发心跳。

对于第2点,心跳消息的作用之一是验证网络畅通,如果它验证的不是收发业务数椐的TCP连接畅通,那其意义就大为缩水了。特别要避免用TCP做业务连接,用UDP发送心跳消息,防止一旦TCP业务连接上出现消息堆积而影响正常业务处理时,程序还一如既往地发送UDP心跳,造成客户端误认为服务可用。

《Release It》一书第4.1节“The 5 a.m. Problem”讲了一个生动的例子,用于描述心跳也是合适的。这个例子说的是Sender和Receiver位于两个数据中心,之间有网络防火墙。网络防火墙的一个特点是会自动检测TCP死链接,即长期没有消息往来的TCP连接,并清除内存中的连通规则。原来的程序通过单独的TCP连接发送心跳,与业务数据不在同一 TCP连接。由于心跳始终在周期性地发送,因此,防火墙认为这个TCP连接是活动的。但是业务连接在每天晚t有很长一段时间没有数据交互,防火墙就判断其为死链接,并且不再转发此链接的IP packet。尽管Sender和Receiver还认为这个TCP业务连接活着,但防火墙实际h已经让连接断开了。当每天早上5点钟第一笔订单进来的时候,始终会出现超时错误,因为业务连接的TCP segment无法到达对方。 TCP协议要经过很长一段时间才能真正判断连接断开 (相当于中途断网,TCP会重试很多次),这时只有重启一方的进程才能快速修复错误。当把心跳消息放到业务连接上之后,问题就迎刃而解了。

猜你喜欢

转载自www.cnblogs.com/dengchj/p/7218565.html