datanode和namenode之间的心跳机制
Hadoop中,通过datanode定期向namenode发送心跳信息,来确定datanode的有效性。对于超时未收到心跳信息的datanode,将被视为失效,将其从系统中移除。从代码中可以看出,超时的时间限制:
ClassFSNameSystem{
……
long heartbeatInterval =conf.getLong("dfs.heartbeat.interval", 3) * 1000;
this.heartbeatRecheckInterval =conf.getInt(
"heartbeat.recheck.interval",5 * 60 * 1000); // 5 minutes
this.heartbeatExpireInterval = 2 *heartbeatRecheckInterval +
10 * heartbeatInterval;
private booleanisDatanodeDead(DatanodeDescriptor node) {
return (node.getLastUpdate() <
(now() - heartbeatExpireInterval));
}
……
}
从代码中可以看出dfs.heartbeat.interval和heartbeat.recheck.interval配置项单位是毫秒。Namenode在 2*(heartbeat.recheck.interval)+ 10* dfs.heartbeat.interval之后,认为datanode失效,默认值是10分30秒。
Datanode是一个runnable对象,启动的时候,做为一个daemon运行。Datanode启动之后,会连接namenode,并每隔dfs.heartbeat.interval毫秒,发送一次心跳,默认为3秒:
public classDataNode extends Configured
implements InterDatanodeProtocol,ClientDatanodeProtocol, FSConstants,
Runnable, DataNodeMXBean {
……
public voidofferService() throws Exception {
while(shouldRun) {
try {
long startTime = now();
if (startTime - lastHeartbeat >heartBeatInterval) {
lastHeartbeat = startTime;
/*调用远程Namenode的方法*/
DatanodeCommand[] cmds =namenode.sendHeartbeat(dnRegistration,
data.getCapacity(),
data.getDfsUsed(),
data.getRemaining(),
xmitsInProgress.get(),
getXceiverCount());
myMetrics.addHeartBeat(now() -startTime);
//LOG.info("Just sent heartbeat, withname " + localName);
if (!processCommand(cmds))
continue;
}
……
}
这个代码段中的namenode,是一个DatanodeProtocol对象。
publicDatanodeProtocol namenode = null;
在datanode启动的时候,namenode对象被初始化。初始化的同时,连接到namenode服务器:
// connect toname node
this.namenode = (DatanodeProtocol)
RPC.waitForProxy(DatanodeProtocol.class,
DatanodeProtocol.versionID,
nameNodeAddr,
conf);
从代码中可以看出,datanode中的namenode对象,是namenode在Datanode端的一个代理。Datanode使用这个代理调用了namenode中的sendHeartbeat方法,该方法调用handleHeartbeat对心跳信息进行处理,更新心跳信息列表。
在FSNamesystem类中的HeartbeatMonitor子类,是一个runnable的对象,在namenode启动的时候,将这个对象启动为一个线程,负责定期检查接收到的心跳信息,检查哪些datanode已经失效,将失效的datanode从系统中移除。
心跳机制的总体流程:datanode发起,调用namenode中的远程方法处理,HeartbeatMonitor线程负责维护。
ClassFSNamesystem{
class HeartbeatMonitor implements Runnable {
public voidrun() {
while (fsRunning) {
try {
long now = now();
if (lastHeartbeatCheck +heartbeatRecheckInterval < now) {
heartbeatCheck();
lastHeartbeatCheck = now;
}
……
try {
Thread.sleep(5000); // 5 seconds
} catch (InterruptedException ie) {
}
}
}
}
void heartbeatCheck() {
if (isInSafeMode()) {
//安全模式下不做心跳检测
return;
}
boolean allAlive = false;
while (!allAlive) {
boolean foundDead = false;
DatanodeID nodeID = null;
//取第一个失效的datanode
synchronized(heartbeats) {
for (Iterator<DatanodeDescriptor>it = heartbeats.iterator();
it.hasNext();) {
DatanodeDescriptor nodeInfo =it.next();
if (isDatanodeDead(nodeInfo)) {
foundDead = true;
nodeID = nodeInfo;
break;
}
}
}
//申请fsnamesystem锁,删除失效的datanode
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(…);
removeDatanode(nodeInfo);
}
}
}
}
}
allAlive = !foundDead;
}
}
private void removeDatanode(DatanodeDescriptornodeInfo) {
synchronized (heartbeats) {
if (nodeInfo.isAlive) {
updateStats(nodeInfo, false);
从heartbeats中移除
heartbeats.remove(nodeInfo);
更新datanode状态
nodeInfo.isAlive = false;
}
}
for (Iterator<Block> it =nodeInfo.getBlockIterator(); it.hasNext();) {
删除该节点存储的block信息
removeStoredBlock(it.next(), nodeInfo);
}
unprotectedRemoveDatanode(nodeInfo);
clusterMap.remove(nodeInfo);
if (safeMode != null) {
safeMode.checkMode();
}
}
更新块到datanode的映射关系。如果将被移除的块仍然有效,可能触发块复制任务。
synchronized void removeStoredBlock(Blockblock, DatanodeDescriptor node) {
NameNode.stateChangeLog.debug("BLOCK*NameSystem.removeStoredBlock: "
+block +" from "+node.getName());
if (!blocksMap.removeNode(block, node)) {
NameNode.stateChangeLog.debug("BLOCK* NameSystem.removeStoredBlock:"
+block+" has already been removed from node "+node);
return;
}
一个块可能因为datanode失效被删除。如果这个块仍然有效,检查是否需要被复制,如果需要,将这个块放入需要复制的列表里。
INode fileINode =blocksMap.getINode(block);
if (fileINode != null) {
decrementSafeBlockCount(block);
updateNeededReplications(block, -1, 0);
}
从冗余列表里删除被删除的块信息,使块不可访问。
Collection<Block> excessBlocks =excessReplicateMap.get(node.getStorageID());
if (excessBlocks != null) {
if (excessBlocks.remove(block)) {
excessBlocksCount--;
NameNode.stateChangeLog.debug("BLOCK* NameSystem.removeStoredBlock:"
+ block + " is removed fromexcessBlocks");
if (excessBlocks.size() == 0) {
excessReplicateMap.remove(node.getStorageID());
}
}
}
// Remove the replica from corruptReplicas
corruptReplicas.removeFromCorruptReplicasMap(block, node);
}
void decrementSafeBlockCount(Block b) {
if (safeMode == null) // mostly true
return;
safeMode.decrementSafeBlockCount((short)countNodes(b).liveReplicas());
}
刷新需要备份的块列表
synchronized voidupdateNeededReplications(Block block,
int curReplicasDelta,int expectedReplicasDelta) {
得到当前的副本数量。
NumberReplicas repl = countNodes(block);
int curExpectedReplicas =getReplication(block);
将块放入需要更新的块列表中。
neededReplications.update(block,
repl.liveReplicas(),
repl.decommissionedReplicas(),
curExpectedReplicas,
curReplicasDelta,expectedReplicasDelta);
}
}
classSafeModeInfo {
将相应的安全备份数量减1,并检查系统中块的安全备份数量是否降低到阀值,如果低于阀值,系统将进入安全模式。
synchronized void decrementSafeBlockCount(shortreplication) {
if (replication == safeReplication-1)
this.blockSafe--;
checkMode();
}
private void checkMode() {
当安全的block数比例降至安全值以下,进入安全模式
if (needEnter()) {
enter();
reportStatus("STATE* Safe modeON.", false);
return;
}
//如果安全模式关闭,或者配置文件中的阀值设置为小于0的值,则退出安全模式。
if (!isOn() || // safe mode is off
extension <= 0 || threshold <=0) {
this.leave(true);
return;
}
如果之前安全块数量已经低于阀值,系统处于安全模式,则直接返回。
if (reached > 0) {
reportStatus("STATE* Safe modeON.", false);
return;
}
启动一个线程,监控安全块数量。
reached = now();
smmthread = new Daemon(newSafeModeMonitor());
smmthread.start();
reportStatus("STATE* Safe modeextension entered.", true);
}
}