每当Datanode启动的时候,Datanode需要向Namenode注册自己。对于Datanode来说,注册的目的有两个:1)报告Datanode当前正在提供的存储(storage);2)从Namenode获取registrationID用来标识当前的Datanode。对于Namenode来说:Namenode通过注册来区分Datanode是不是提供一个新的storage(这个新的storage Namenode是不知道的,storage是通过Namenode分配唯一的storageID来进行区分的),并向Namenode报告新的block;还是这个Datanode是原来storage的替代,该storage原来被该Datanode或其他Datanode提供(通过host:port进行区别)。最后,Namenode将其namespaceID返回给Datanode,作为Datanode的resistrationID。Namenode和Datanode的每一次交互都要检查namespaceID是否相符。
下面我们讨论一下如何来完成Datanode的注册。从上面的分析我们知道Datanode可能两种状态,一种是Datanode是全新的,里面没有storage;另外一种情况是Datanode已经保存了某个已有的storage。如果是全新的,那么处理的过程可能比较简单,将该Datanode保存到host2DataNodeMap中;给Datanode分配storageID,并将该storageID存放到datanodeMap中。如果Datanode保存了一个已有的storageID(我们称之为已有新服务storage),那么首先判断该Datanode原来是不是已经注册过并且保存了一个storage(我们称之为已有应删除storage),如果保存了就将该Datanode上原来的已有应删除storage删除,删除后再来服务已有新服务storage。然后修改已有新服务storage的Datanode,使之与当前Datanode一致。最后将该Datanode存放到host2DataNodeMap中。
我们看一下程序的源代码:
public synchronized void registerDatanode(DatanodeRegistration nodeReg)
throws IOException {
//获取Datanode的IP
String dnAddress = Server.getRemoteAddress();
if (dnAddress == null) {
// Mostly called inside an RPC.
// But if not, use address passed by the data-node.
dnAddress = nodeReg.getHost();
}
// check if the datanode is allowed to be connect to the namenode
//检查该Datanode是否允许连接到本Namenode,主要是检查该Datanode是否包含在
//slaves配置文件当中,并且不包含在exclude列表当中
if (!verifyNodeRegistration(nodeReg, dnAddress)) {
throw new DisallowedDatanodeException(nodeReg);
}
//获取Datanode的hostName
String hostName = nodeReg.getHost();
// update the datanode's name with ip:port
DatanodeID dnReg = new DatanodeID(dnAddress + ":" + nodeReg.getPort(),
nodeReg.getStorageID(), nodeReg.getInfoPort(),
nodeReg.getIpcPort());
nodeReg.updateRegInfo(dnReg);
nodeReg.exportedKeys = getBlockKeys();
NameNode.stateChangeLog.info("BLOCK* NameSystem.registerDatanode: "
+ "node registration from " + nodeReg.getName() + " storage "
+ nodeReg.getStorageID());
/*datanodeMap{storageID->DatanodeDescriptor},
* 如果nodeReg包含了一个storage,那么nodeReg.getStorageID()
* 将不为空,那么nodeS就是该storage对应的Datanode
* (注意该Datanode不一定是现在正在注册的Datanode)。
* 需要将该storage对应的Datanode更新到现在正在注册的Datanode
*/
DatanodeDescriptor nodeS = datanodeMap.get(nodeReg.getStorageID());
/*
* 从host2DataNodeMap中获取该hostName所对应的DatanodeDescriptor
*/
DatanodeDescriptor nodeN = host2DataNodeMap.getDatanodeByName(nodeReg
.getName());
/*
* nodeN!=null表明正在注册的Datanode原来已经注册过。
* 既然已经注册过,那么nodeN必然有了自己
* 的storage。当时nodeS!=nodeN,表明nodeN正在服务一个新
* 的storage。所以必须将其原来的storage删除
*/
if (nodeN != null && nodeN != nodeS) {
NameNode.LOG.info("BLOCK* NameSystem.registerDatanode: "
+ "node from name: " + nodeN.getName());
// nodeN previously served a different data storage,
// which is not served by anybody anymore.
removeDatanode(nodeN);
// physically remove node from datanodeMap
wipeDatanode(nodeN);
nodeN = null;
}
if (nodeS != null) {
if (nodeN == nodeS) {
// The same datanode has been just restarted to serve the same
// data
// storage. We do not need to remove old data blocks, the delta
// will
// be calculated on the next block report from the datanode
NameNode.stateChangeLog
.debug("BLOCK* NameSystem.registerDatanode: "
+ "node restarted.");
} else {
// nodeS is found
/*
* The registering datanode is a replacement node for the
* existing data storage, which from now on will be served by a
* new node. If this message repeats, both nodes might have same
* storageID by (insanely rare) random chance. User needs to
* restart one of the nodes with its data cleared (or user can
* just remove the StorageID value in "VERSION" file under the
* data directory of the datanode, but this is might not work if
* VERSION file format has changed
*/
NameNode.stateChangeLog
.info("BLOCK* NameSystem.registerDatanode: " + "node "
+ nodeS.getName() + " is replaced by "
+ nodeReg.getName()
+ " with the same storageID "
+ nodeReg.getStorageID());
}
// update cluster map
clusterMap.remove(nodeS);
/*
* 在这种情况下nodeS!=null,表明正在注册的Datanode已经
* 正在服务一个storage。如果nodeN==nodeS,表明该storage
* 原来就保存在该Datanode上,只是重启了该Datanode;
* 如果nodeN!=nodeS,表明该storage原来保存在其他Datanode上,
* 现在保存到了该Datanode。首先,从clusterMap中移除nodeS,
* 然后更新nodeS的Datanode,并在clusterMap中重新添加nodeS
*/
nodeS.updateRegInfo(nodeReg);
nodeS.setHostName(hostName);
// resolve network location
resolveNetworkLocation(nodeS);
clusterMap.add(nodeS);
// also treat the registration message as a heartbeat
synchronized (heartbeats) {
if (!heartbeats.contains(nodeS)) {
heartbeats.add(nodeS);
// update its timestamp
nodeS.updateHeartbeat(0L, 0L, 0L, 0);
nodeS.isAlive = true;
}
}
return;
}
// this is a new datanode serving a new data storage
/*
* 如果storageID为空,表明该Datanod是一个全新的Datanode,
* 需要分配一个storageID,并将该Datanode添加到datanodeMap
* 和host2DataNodeMap
*/
if (nodeReg.getStorageID().equals("")) {
// this data storage has never been registered
// it is either empty or was created by pre-storageID version of DFS
nodeReg.storageID = newStorageID();
NameNode.stateChangeLog
.debug("BLOCK* NameSystem.registerDatanode: "
+ "new storageID " + nodeReg.getStorageID()
+ " assigned.");
}
// register new datanode
DatanodeDescriptor nodeDescr = new DatanodeDescriptor(nodeReg,
NetworkTopology.DEFAULT_RACK, hostName);
resolveNetworkLocation(nodeDescr);
unprotectedAddDatanode(nodeDescr);
clusterMap.add(nodeDescr);
// also treat the registration message as a heartbeat
synchronized (heartbeats) {
heartbeats.add(nodeDescr);
nodeDescr.isAlive = true;
// no need to update its timestamp
// because its is done when the descriptor is created
}
return;
}