HDFS1.0源代码解析―DataNode端数据存储和管理DataStorage和FSDataset解析(四)

2014-11-24 08:47:01 · 作者: · 浏览: 5
boolean supportAppends = conf.getBoolean("dfs.support.append", false);
480 File parent = currentDir.getParentFile();
481
482 this.detachDir = new File(parent, "detach");
483 if (detachDir.exists()) {
484 recoverDetachedBlocks(currentDir, detachDir);
485 }
[java] view plaincopyprint
522 this.usage = new DF(parent, conf);
523 this.dfsUsage = new DU(parent, conf);
524 this.dfsUsage.start();
首先来看FSVolume的数据成员,currentDir、tmpDir、blocksBeingWritten、detachDir这几个File类型的成员变量分别对应前面提到的配置路径地下的各个目录的名字。其中比较感兴趣的可能是dataDir、usage、dfsUsage这几个看上出类型比较奇怪的成员。
在紧接着的构造函数中,都有这个几个成员变量的初始化。首选来看dataDir,从代码中可以看出它是一个FSDir类型的成员,我们来看一个FSDir具体干了些什么。FSDir同样也是FSDataset的一个内部类。
[java]
127 public FSDir(File dir)
128 throws IOException {
129 this.dir = dir;
130 this.children = null;
131 if (!dir.exists()) {
132 if (!dir.mkdirs()) {
133 throw new IOException("Mkdirs failed to create " +
134 dir.toString());
135 }
136 } else {
137 File[] files = FileUtil.listFiles(dir);
138 int numChildren = 0;
139 for (int idx = 0; idx < files.length; idx++) {
140 if (files[idx].isDirectory()) {
141 numChildren++;
142 } else if (Block.isBlockFilename(files[idx])) {
143 numBlocks++;
144 }
145 }
146 if (numChildren > 0) {
147 children = new FSDir[numChildren];
148 int curdir = 0;
149 for (int idx = 0; idx < files.length; idx++) {
150 if (files[idx].isDirectory()) {
151 children[curdir] = new FSDir(files[idx]);
152 curdir++;
153 }
154 }
上边这段代码应该非常容易理解,这就是传说中的目录树的实现,而且还是链表实现版(^_^)。
继续看FSVolume的成员变量usage,usage是DF类型的对象,DF不是FSVolume的内部类,这个看上出诡异的命名其实对应的是Linux中的df命令,从DF的包含的函数很容易看出DF主要保存的是每个配置路径的磁盘空间的使用情况,主要应用在block写入的时候,通过容量确定block应该写入到哪个配置路径中。
再看成员dfsUsage ,它是DU类型的成员变量,那么很明显这个DU对应的就是Linux中的du命令,,主要作用是获取当前当前使用的空间的数量,也就是存储的内容的大小。比较值得注意的是其中的start方法
[java]
147 public void start() {
148 //only start the thread if the interval is sane
149 if(refreshInterval > 0) {
150 refreshUsed = new Thread(new DURefreshThread(),
151 "refreshUsed-"+dirPath);
152 refreshUsed.setDaemon(true);
153 refreshUsed.start();
154 }
155 }
在该方法中会创建一个线程对象refreshUsed,这个线程是一个后台线程会一直执行,执行的时间的间隔可以设置。执行调用的DU的父类Shell的run方法,具体执行的shell命令是
[java]
174 protected String[] getExecString() {
中的命令。
其他FSVolume构造函数的初始化主要是对data目录下的几种文件进行处理,比如删除tmp中的内容,根据是否接受append处理blocksBeingWritten的数据等。
现在我们回头来想一下,每个NN写入数据都会写到若干个配置的路径中,这样不容易管理,因为可能存在多个路径。下面要介绍的这个成员就是来解决这样一个问题的,
[java]
1149 volumes = new FSVolumeSet(volArray);
1150 //对每一个volum下的文件进行处理,加载volummap与sumindex volumes.getVolumeMap(volumeMap);
1151 volumes.getVolumeMap(volumeMap);
每个FSVolumeSet管理上边提到的所有的FSVolume(每个FSVolume对应一个配置的路径),按照逻辑我们还是来看一下FSVolumeSet的主要的组成部分,
[java]
750 synchronized FSVolume getNextVolume(long blockSize) throws IOException {

762 int startVolume = curVolume;
763 //因为一个datanode管理若干路径,选择一个合适的路径存储一个block
764 while (true) {
765 FSVolume volume = volumes[curVolume];
766 curVolume = (curVolume + 1) % volumes.length;
767