在前面的文章里面我已经讲过了,DataNode节点在注册时候就开始定期向NameNode节点发送心跳包,以此来告知NameNode节点自己当前还是可用的,同时从NameNode节点那里得到对自己的控制命令并执行之(这一点本文不表)。显然,当一个DataNode节点没有按时向NameNode节点发送心跳包时,NameNode就然为这个数据节点已经不可用了,清除与之相关的数据信息(比如在改数据节点上的数据块信息)。自然NameNode也再不会把该数据节点分配给客户端使用了。那么这个工作是由NameNode里面的那个部件来完成的呢?废话吗不是,这就是本文索要讲述的重点啊——FSNamesystem$HeartbeatMonitor后台工作线程。
看看代码先:
class HeartbeatMonitor implements Runnable {
/**
*/
public void run() {
while (fsRunning) {
try {
heartbeatCheck();
} catch (Exception e) {
FSNamesystem.LOG.error(StringUtils.stringifyException(e));
}
try {
Thread.sleep(heartbeatRecheckInterval);
} catch (InterruptedException ie) {
}
}
}
}
简单的说哈上面的代码,HeartbeatMonitor 会定期的检测已注册的数据节点的心跳包,每一次检测间隔heartbeatRecheckInterval 毫秒,这个值可以通过配置文件中的heartbeat.recheck.interval项来设置。再来看看HeartbeatMonitor是如何检测的。
void heartbeatCheck() {
boolean allAlive = false;
while (!allAlive) {
boolean foundDead = false;
DatanodeID nodeID = null;
// locate the first dead node.
synchronized(heartbeats) {
for (Iterator<DatanodeDescriptor> it = heartbeats.iterator();it.hasNext();) {
DatanodeDescriptor nodeInfo = it.next();
if (isDatanodeDead(nodeInfo)) {
foundDead = true;
nodeID = nodeInfo;
break;
}
}
}
// acquire the fsnamesystem lock, and then remove the dead node.
if (foundDead) {
synchronized (this) {
synchronized(heartbeats) {
synchronized (datanodeMap) {
DatanodeDescriptor nodeInfo = null;
try {
nodeInfo = getDatanode(nodeID);
} catch (IOException e) {
nodeInfo = null;
}
if (nodeInfo != null && isDatanodeDead(nodeInfo)) {
NameNode.stateChangeLog.info("BLOCK* NameSystem.heartbeatCheck: " + "lost heartbeat from " + nodeInfo.getName());
removeDatanode(nodeInfo);//清除与该数据节点相关的数据信息
}
}
}
}
}
allAlive = !foundDead;
}
private boolean isDatanodeDead(DatanodeDescriptor node) {
return (node.getLastUpdate() < (now() - heartbeatExpireInterval));
}
很明显NameNode节点是根据数据节点上一次发送的心跳包时间和现在的时间差是否超出heartbeatExpireInterval来判断它是否已dead。我先说的是NameNode节点还是比较的仁慈的,因为heartbeatExpireInterval的值等于2个heartbeatRecheckInterval再加10个heartbeatInterval的和,从这一点可以看出NameNode节点判定一个数据节点的dead还是比较的慎重的,毕竟一个数据节点再次注册到真正能提供服务还是很消耗NameNode节点资源的。哦,对了,heartbeatInterval的值可以通过配置文件的dfs.heartbeat.interval项来设置。
关于清除与数据节点相关的数据信息的方法removeDatanode()貌似是块硬骨头,不好啃啊,还是先放在这里以后在讲吧,因为还有些数据结构没有讲。