本文转载自微信公众号「KK架构师」,作者wangkai。转载本文请联系KK架构师公众号。
一、分布式系统中的心跳技术
心跳是分布式技术中常用的技术手段。心跳,顾名思义,就是以固定的频率向其他节点汇报当前节点状态的方式。收到心跳,一般可以认为发送心跳的这个节点在当前的网络拓扑中是良好的。
当然,心跳汇报时,一般也会携带一些附加的状态、元数据信息、当前节点的信息,以便其他节点管理。
心跳有两种方式:
- 单向的 heartbeat;
- 交互的 ping-pong;
第一种方式下,target 进程需要定时给 detector 发送消息,告知自己的存活性。而 detector 无需给 target 回复任何消息,只是每隔一段时间去检测 target 进程有没有汇报。
第二种方式更为常见,比如我们的 redis 就是采用这种方式:
- detector -> target: Are you ok?
- target -> detector: Yeah, pretty good.
Detector 发起检测,如果 target 连续 N 次不回复消息,那么 detector 就认为其处于 non-active 状态。
那么常用的心跳检测机制有哪些:
(1)传统的周期检测心跳机制
其检测方法很粗暴:设定一个超时时间 T,只要在 T 之内没有收到对方的心跳包便可认为对方宕机,方法简单有效,使用比较广泛。
所以这个方法的重点就在于这个超时时间 T 的设置,设置的太短了,有可能会因为当前网络阻塞导致误判,让这个节点下线,产生其他不必要的后果;设置的太长,会导致判断“迟缓”,所以需要综合各种情况来权衡和设定。
HDFS 就是使用的这种心跳机制。
(2)累积失效检测机制
随着网路负载的加大,Server 心跳的接收时间可能会大于上限值 T;但当网络压力减少时,心跳接收时间又会小于 T ,如果用一成不变的T 来反映心跳状况,则会造成判断”迟缓“或误判。这个时候我们可以计算心跳延迟的概率,用这个概率来判断是否发生故障,提高准确性。
二、DataNode 是如何向 NameNode 发送心跳的
我们从 hadoop 源码看 DataNode 是如何发送心跳的
1、从 DataNode 类的 main 方法开始
2、创建 DataNode
3、实例化 DataNode
4、创建实例
5、 new 了 DataNode
6、 这个方法构造函数有点长,拉到最下面
7、然后来到这个方法里
这个方法表面看起来是刷新 NameNode,实际上里面做了两件事情,把自己注册到 NameNode 上,另外一件事情是向 NameNode 定时发送心跳。
8、点进去,现在是在 BlockPoolManager 里面
9、再来到这个方法里
10、发现它在遍历这个 BPOfferService
这里大致说明一下 offerServices 是个什么数据结构。
用一张图来表示吧
那个 offerServices 其实装的就是最左边的 BPOfferService。
每个 BPOfferService 里面有两个 BpServiceActor,每个 BpServiceActor 对应一个 NameNode。
如果是上图中的高可用,那么一主一备两个 NameNode,分别对应一个 BpServiceActor。
所以遍历 offerService 其实就是在遍历整个集群每个联邦的每个 NameNode 节点。
11、开始遍历(当前类:BpOfferService)
12、线程 start(当前类:BPServiceActor)
再点进去已经是 Thread 的方法了,可见它其实是个线程。
那我们应该看这个类的 run 方法。
13、线程的运行内容
run 方法的上半部分是往 NameNode 注册,下半部分是发送心跳
14、发送心跳
可以看到这是一个 while 循环,每隔一段时间(dnConf.heartBeatInterval = 3秒)就会执行一次
可以看到 DataNode 的心跳就是采用了周期性检测机制,每隔 3 s ,往所有的 NameNode 发送心跳。
15、再点进去,已经是 NameNode 的 proxy 代理方法了。
因为它是靠 rpc 通信的,此时 DataNode 是客户端,NameNode 是服务端。
这个时候,我们应该看 NameNodeRpcServer 方法,具体的实现是在这个方法里的。
16、看服务端的处理,此时我们在 NameNodeRpcServer 类中
这个方法里的具体就不看了,(其实也很重要的的)。
大致就是把 DataNode 心跳包的基本信息(比如本节点的存储容量信息等)更新到 NameNode 对应的结构中。
并且更新上一次心跳时间,以便下次判断 DataNode 是否心跳超时。
其实心跳就是这么朴素了。
最后,NameNode 会在心跳的响应中,告诉 DataNode 应该做些什么事情,比如把本节点的 Block 备份到其他节点上去。
也就是说,NameNode 本身不会和 DataNode 通信,而是在心跳信息中告诉 DataNode 该做什么。
三、小结
本次通过浏览 DataNode 代码了,知道了其实 DataNode 的心跳,就是DataNode 在后台启动了线程,定时向整个集群所有的 NameNode 发送心跳信息,NameNode 会在心跳响应信息中告诉 DataNode 本次该做些什么事情。