这个类维护了系统中多个重要的表:
1.dir
FSDirectory dir;
文件的目录结构,也就是传说中的元数据,具体是由iNode组成,可以理解为文件到块的映射,保存在NameNode的本地内存和硬盘中。
初始化:在FSNamesystem类的构造函数中,调用FSDirectory类构造函数,其参数dir是xml配置文件 中"dfs.name.dir"所对应的值,其默认值为"/tmp/hadoop/dfs/name"。
this.dir = new FSDirectory(dir);
FSDirectory类构造函数:
public FSDirectory(File dir) throws IOException {
File fullimage = new File(dir, "image");
if (! fullimage.exists()) {
throw new IOException("NameNode not formatted: " + dir);
}
File edits = new File(dir, "edits");
if (loadFSImage(fullimage, edits)) {
saveFSImage(fullimage, edits);
}
synchronized (this) {
this.ready = true;
this.notifyAll();
this.editlog = new DataOutputStream(new FileOutputStream(edits));
}
}
FS_IMAGE是给内存中的FSDirectory类实例做备份的,image是FS_IMAGE所存储的路径,也就是说如果这个路径不存在,说明NameNode还没有格式化,换句话说在格式化的函数中一定能找到类似下面这样的代码
File image = new File(dir, "image");
image.mkdirs();
接着FSDirectory类构造函数会读logEdit,目录的添加,删除,重命名,创建路径这四个操作会记录到logEdit,logEdit隔一段时间就会把这些修改作用到FS_IMAGE上,也就是说,对读到的FS_IMAGE做logEdit会得到最新的目录结构。至于从本地文件系统读FS_IMAGE和logEdit的过程,等讲到hdfs io时再说。也就是说,现在内存中的FSDirectory类实例已经是最新的了。至此FSDirectory类实例化结束。
在下列情况下其值发生变化:
(1)在调用ClientProtocol接口中的create()函数时,文件需要覆盖时会调用delete()函数,同样对应于logEdit的删除。
(2)在调用ClientProtocol接口中的complete()函数时,要把完成的文件添加到FSDirectory类实例中去,包括所包含的块(从pendingCreates中该文件对应的Vecor中获取的)。对应于logEdit的添加。
(3)在调用ClientProtocol接口中的rename()函数时,对应于logEdit的重命名。
(4)在调用ClientProtocol接口中的delete()函数时,对应于logEdit的删除。
(5)在调用ClientProtocol接口中的mkdir()函数时,对应于logEdit的创建路径。
2.datanodeMap
TreeMap datanodeMap = new TreeMap();
记录节点到块的映射,Key是UTF8类的实例(DataNode的Name),Value是DatanodeInfo类的实例。
DatanodeInfo类的成员变量有:
private UTF8 name;
private long capacityBytes, remainingBytes, lastUpdate;
private volatile TreeSet blocks;
在下列情况下其值发生变化:
(1)在调用DatanodeProtocol接口中的sendHeartbeat()函数时,如果该节点未被datanodeMap所记录,则增加该节点信息。当然这里不包含该节点的块信息,也就是说其存储DatanodeInfo类的实例并没有其所对应块信息。
(2)在调用DatanodeProtocol接口中的blockReport()函数时,如果Datanode所报告的块datanodeMap中没有,则添加,否则删除。这里增加了datanodeMap的块信息,也就是DatanodeInfo类的实例中所对应的块信息。
(3)在调用DatanodeProtocol接口中的blockReceived()函数时和ClientProtocol接口中的reportWrittenBlock()函数时,这里增加了datanodeMap的块信息,也就是DatanodeInfo类的实例中所对应的块信息。
(4)当心跳监视器监视到某个节点对应的心跳过期时,则把该心跳对应的节点从datanodeMap中删除。
3.heartbeats
TreeSet heartbeats = new TreeSet(new Comparator() {
public int compare(Object o1, Object o2) {
DatanodeInfo d1 = (DatanodeInfo) o1;
DatanodeInfo d2 = (DatanodeInfo) o2;
long lu1 = d1.lastUpdate();
long lu2 = d2.lastUpdate();
if (lu1 < lu2) {
return -1;
} else if (lu1 > lu2) {
return 1;
} else {
return d1.getName().compareTo(d2.getName());
}
}
});
记录当前活跃的节点,保存DatanodeInfo类的实例。与datanodeMap同步增删其对应的DatanodeInfo类的实例。
在下列情况下其值发生变化:
(1)在调用DatanodeProtocol接口中的sendHeartbeat()函数时,如果该节点未被heartbeats所记录,则增加该节点信息,当然这里不包含该节点的块信息,也就是说其存储DatanodeInfo类的实例并没有其所对应块信息。
(2)当心跳监视器监视到某个节点对应的心跳过期时,则把该节点对应的心跳从heartbeats中删除。
4.blocksMap
TreeMap blocksMap = new TreeMap();
块到节点的映射,KEY是Block类实例,Value是DatanodeInfo类实例的TreeSet。可看作与datanodeMap记录相反的信息。与datanodeMap同步增删其对应的DatanodeInfo类实例中的块信息。
在下列情况下其值发生变化:
(1)在调用DatanodeProtocol接口中的 blockReport()函数时,如果该块以前记录过(通过和datanodeMap的对比),而报告中没有,则删除blocksMap中该块所对应的现在这个报告节点。如果以前没有记录过,而报告中有,则添加blocksMap中该块所对应的现在这个报告节点。
(2)在调用DatanodeProtocol接口中的 blockReceived()函数时和ClientProtocol接口中的reportWrittenBlock()函数时,添加映射到blocksMap中。
(3)当心跳监视器监视到某个节点对应的心跳过期时,从blocksMap中删除该节点所对应的块。
5.recentInvalidateSets
TreeMap recentInvalidateSets = new TreeMap();
节点到需要删除的块的映射,通过Command形式发给DataNode节点以删除。Key是UTF8类的实例(DataNode)Value是block的类实例的Vector。
在下列情况下其值发生变化:
(1)在调用DatanodeProtocol接口中的 blockReport()函数时,如果该块以前没有记录过,而报告中有,则添加blocksMap中该块所对应的现在这个报告节点。如果该块在FSDirectory类实例中依然存在并且该块没有在excessReplicateMap节点所对应得多余块中(我再并且)存有该快的节点数大于了系统设定的块最大复制数,则随机的选一个节点,加上该多余的块添加到excessReplicateMap中,再加到recentInvalidateSets中。
(2)在调用DatanodeProtocol接口中的 blockReceived()函数时和ClientProtocol接口中的reportWrittenBlock()函数时,添加映射到blocksMap中。如果该块在FSDirectory类实例中依然存在并且该块没有在excessReplicateMap节点所对应得多余块中(我再并且)存有该快的节点数大于了系统设定的块最大复制数,则随机的选一个节点,加上该多余的块添加到excessReplicateMap中,再加到recentInvalidateSets中。
(3)在调用DatanodeProtocol接口中的 getBlockwork()函数时,如果是删除命令,从recentInvalidateSets删除该节点所对应的块。
(4)在调用ClientProtocol接口中的create()函数时,文件需要覆盖时会调用delete()函数,添加信息到recentInvalidateSets。
6. neededReplications
private TreeSet neededReplications = new TreeSet();
没有达到需要的复制块的数量,就把该块加入到neededReplications中。neededReplications中存储的是Block类的实例。
在下列情况下其值发生变化:
(1)在调用DatanodeProtocol接口中的 blockReport()函数时,如果该块以前记录过(通过和datanodeMap的对比),而报告中没有,则删除blocksMap中该块所对应的现在这个报告节点。同时如果该块在FSDirectory类实例中依然存在且现在块数小于需要复制的块数,说明该块不是通过ClientProtocol接口中的delete()函数删除的,所以导致该块没有被报告,是该DataNode节点本身的故障问题,所以需要另复制该块到其他节点,故添加该Block到neededReplications。
(2)在调用DatanodeProtocol接口中的 blockReport()函数时,如果该块以前没有记录过,而报告中有,则添加blocksMap中该块所对应的现在这个报告节点。同时,如果该块在FSDirectory类实例中依然存在且现在块数大于需要复制的块数,则从neededReplications删除该块。(可能失效的节点恢复了过来)否则,则添加该Block到neededReplications。
(3)在调用DatanodeProtocol接口中的 blockReceived()函数时和ClientProtocol接口中的reportWrittenBlock()函数时,添加映射到blocksMap中。同时,如果该块在FSDirectory类实例中依然存在且现在块数大于需要复制的块数,则从neededReplications删除该块。(可能失效的节点恢复了过来)否则,则添加该Block到neededReplications。
(4)在调用DatanodeProtocol接口中的 getBlockwork()函数时,在制定复制命令时,首先检查是否块在FSDirectory类实例中依然存在,如果不存在,从neededReplications中把块删除。如果该块在neededReplications中,则把该块从neededReplications中删除。
(5)在调用ClientProtocol接口中的complete()函数时,如果块所对应的节点数小于需要复制的块数时,添加该块到neededReplications中。(这里还不太明白,再调用complete()前,dfsclient应该已经调用过reportWrittenBlock()了,所以datanodeMap已经被修改了,为什么还会出现这种情况呢?)
(6)当心跳监视器监视到某个节点对应的心跳过期时,则删除节点所对应的块。同时如果该块在FSDirectory类实例中依然存在且现在块数小于需要复制的块数,则添加该块到neededReplications。
7. pendingReplications
private TreeSet pendingReplications = new TreeSet();
pendingReplications中存储的是Block类的实例。
在下列情况下其值发生变化:
(1)在调用DatanodeProtocol接口中的 blockReport()函数时,如果该块以前没有记录过,而报告中有,则添加blocksMap中该块所对应的现在这个报告节点。同时如果该块在FSDirectory类实例中依然存在且现在块数大于需要复制的块数,则从pendingReplications删除该块。
(2)在调用DatanodeProtocol接口中的 blockReceived()函数时和ClientProtocol接口中的reportWrittenBlock()函数时,添加映射到blocksMap中。同时如果该块在FSDirectory类实例中依然存在且现在块数大于需要复制的块数,则从pendingReplications删除该块。
(3)在调用DatanodeProtocol接口中的 getBlockwork()函数时,在制定复制命令时,首先检查是否块在FSDirectory类实例中依然存在,如果不存在,从neededReplications中把块删除。如果该块在neededReplications中,则把该块从neededReplications中删除,添加该块至pendingReplications。
8.excessReplicateMap(它的作用现在不是很清楚,和recentInvalidateSets有什么联系和区别呢?)
TreeMap excessReplicateMap = new TreeMap();
Key是UTF8类的实例(DataNode的Name),Value是Block类实例的TreeSet.
在下列情况下其值发生变化:
(1)在调用DatanodeProtocol接口中的 blockReport()函数时,如果该块以前记录过(通过和datanodeMap的对比),而报告中没有,则删除blocksMap中该块所对应的现在这个报告节点。因为报告中没有该块,说明该块已经不再该DataNode,所以也就谈不上该块在该节点是多余的了,故从excessReplicateMap中删除。
(2)在调用DatanodeProtocol接口中的 blockReport()函数时,如果该块以前没有记录过,而报告中有,则添加blocksMap中该块所对应的现在这个报告节点。如果该块在FSDirectory类实例中依然存在并且该块没有在excessReplicateMap节点所对应得多余块中(我再并且)存有该快的节点数大于了系统设定的块最大复制数,则随机的选一个节点,加上该多余的块添加到excessReplicateMap中。
(3)在调用DatanodeProtocol接口中的 blockReceived()函数时和ClientProtocol接口中的reportWrittenBlock()函数时,添加映射到blocksMap中。如果该块在FSDirectory类实例中依然存在并且该块没有在excessReplicateMap节点所对应得多余块中(我再并且)存有该快的节点数大于了系统设定的块最大复制数,则随机的选一个节点,加上该多余的块添加到excessReplicateMap中。
(4)当心跳监视器监视到某个节点对应的心跳过期时,则节点所对应的块。该块已经不再该DataNode,所以也就谈不上该块在该节点是多余的了,故从excessReplicateMap中删除。
9.pendingCreates
TreeMap pendingCreates = new TreeMap();
正在创建的文件,Key是UTF-8的实例(文件名),Value是该文件包含的块(Block类的实例的Vector)。
在下列情况下其值发生变化:
(1)在调用ClientProtocol接口中的create()函数时,把文件加入到pendingCreates,创建空的Vector 。
(2)在调用ClientProtocol接口中的abandonBlock()函数时,把块从pendingCreates中的Vector中删除,文件不删除。
(3)在调用ClientProtocol接口中的addBlock()函数时,如果文件正在被创建,则增加一个块到Vector中。
(4)在调用ClientProtocol接口中abandonFileInProgress()函数时,把文件从pendingCreates中删除。
(5)在调用ClientProtocol接口中complete()函数时,NameNode通过节点或客户上报块的信息把块的长度这个字段在pendingCreates中的Vector中存的Block加上,然后在FSDirectory类的实例修改过后,把文件从从pendingCreates中删除。
(6)如果锁监视器监视到锁过期且持有锁的文件正在被创建,则把文件从pendingCreates中删除。
10.pendingCreateBlocks
TreeSet pendingCreateBlocks = new TreeSet();
正在创建的块,存储的是Block类的实例。
(1)在调用ClientProtocol接口中的create()函数时,把文件对应的块加入到pendingCreateBlocks。
(2)在调用ClientProtocol接口中的abandonBlock()函数时,把文件对应的块从pendingCreateBlocks中删除。
(3)在调用ClientProtocol接口中的addBlock()函数时,如果文件正在被创建,则增加一个块到pendingCreateBlocks中。
(4)在调用ClientProtocol接口中abandonFileInProgress()函数时,把该文件对应的所有块从pendingCreateBlocks 中删除。
(5)在调用ClientProtocol接口中complete()函数时,把该文件对应的块从pendingCreateBlocks中删除。
(6)如果锁监视器监视到锁过期且持有锁的文件正在被创建,则把文件对应的块从pendingCreateBlocks中删除。