HDFS1.0源代码解析—DataNode端数据存储和管理DataStorage和FSDataset解析
本部分主要介绍DN中的数据的存储和管理,我们知道从逻辑上来看我们把数据存储到HDFS这个文件系统中,但是具体数据在每个DN上是如何存储的呢,这其中就牵扯到几个比较大的类DataStorage、Storage、FSDataset等等。一开始读DN源代码这部分就搞得不是很清楚,现在理一下思路,也算给刚开始看得童鞋一些提示。
在配置hdfs-site.xml的时候我们会配置这样一个选项
[java]
16 <property>
17 <name>dfs.data.dir</name>
这个配置项就是每个DN具体存储数据的位置,存储目录底下文件有以下几类:
blocksBeingWritten current detach storage tmp
其中blocksBeingWritten 是当前正在写的block,写完会从blocksBeingWritten 转移到current文件底下,current是保存当前已经写入的数据,也就是数据存储的主目录。detach 是硬链接,主要用于写时拷贝。tmp存储一些临时信息,在DN启动检查时会将tmp中的数据删除。
首先来看DataStorage,它在DN数据管理中起着怎么样的作用。
[java]
public class DataStorage extends Storage {
从DataStorage类的声明可以看出DataStorage是用于DN数据管理的Storage的特殊类,那么很容易就会想到NN中也有一个这样的特殊类(是的,FSImage),torage类的主要作用就是存储两者公共的一些信息。
我们来看一下在DN启动时,DataStorage起到了什么样的作用。
[java]
311 void startDataNode(Configuration conf,
312 AbstractList<File> dataDirs, SecureResources resources
313 ) throws IOException {
343 storage = new DataStorage();
380 storage.recoverTransitionRead(nsInfo, dataDirs, startOpt);
上边的几行代码实在方法startDataNode中主要与DataStorage交互的代码。首先还是来看DataStorage的初始化,
[java]
59 DataStorage() {
60 super(NodeType.DATA_NODE);
61 storageID = "";
62 }
主要工作还是声明自己存储的类型。下面接着看recoverTransitionRead方法,核心代码如下:
[java]
106 for(Iterator<File> it = dataDirs.iterator(); it.hasNext();) {
107 File dataDir = it.next();
108 StorageDirectory sd = new StorageDirectory(dataDir);//初始化storage中的root
109 StorageState curState;
110 try {
111 curState = sd.analyzeStorage(startOpt);
112 // sd is locked but not opened
113 switch(curState) {
114 case NORMAL:
115 break;
116 case NON_EXISTENT:
117 // ignore this storage
118 LOG.info("Storage directory " + dataDir + " does not exist.");
119 it.remove();
120 continue;
121 case NOT_FORMATTED: // format
122 LOG.info("Storage directory " + dataDir + " is not formatted.");
123 LOG.info("Formatting ...");
124 format(sd, nsInfo);
125 break;
126 default: // recovery part is common
127 sd.doRecover(curState);
128 }
首选看到这个循环是对hdfs-site.xml这个配置文件中,配置的每个存储路径进行检查。具体检查的通过StorageDirectory的analyzeStorage方法进行,该方法会返回当前存储处于何种状态(这个状态在前边的博客中已经讲述,主要是升级过程可能造成DN处于多种状态)。如果是NORMAL说明一切正常,不作处理;如果是NON_EXISTENT说明配置项中的这个目录不存在;如果是NOT_FORMATTED,说明命令是进行格式化的操作(hadoop dfs -format),通过 format(sd, nsInfo); 方法格式化;其他状态通过sd.doRecover(curState)方法,进行处理。
通过上面的描述很容易发现StorageDirectory的对象,在处理的各个部分都起着关键作用。那么来分析下这个StorageDirectory类的主要作用,StorageDirectory是DataStorage父类Storage中的一个内部类
[java]
182 public class StorageDirectory {
183 File root; // root directory
184 FileLock lock; // storage lock
185 StorageDirType dirType; // storage dir type
主要的成员变量有3个,我们重点关注的是root,在初始化它被初始化为配置的存储路径中的某一个。下面分析其中的主要方法,先来看我们用到的analyzeStorage方法,这部分代码比较长,就不在贴了,简单的介绍下其中的逻辑。首先判断root这个路径是否已经存在,如果不存在并且目前执行的是StartupOption.FORMAT格式化命令的话,返回StorageState.NON_EXISTENT。在判断root是否是目录或者是否可写,如果有一个不满足返回StorageState.NON_EXISTENT。如果配置的路径存在,首先对文件进行加锁,对文件加锁的一个常用技巧就是建立一个空的filelock文件,将所加在这个空白文件上,通过对这个文件的加锁控制一个目录的同步行为。
[java]
469 boolean hasPrevious = getPreviousDir().exists();
470 boolean hasPreviousTmp = getPreviousTmp().exists();
471 boolean hasRemovedTmp = getRemovedTmp().exists();
472 boolean hasFinalizedTmp = getFinalizedTmp().exists();
473 boolean hasCheckpointTmp = getLastCheckpointTmp().exists();
之后判断个各种状态产什么的标志性文件是否存在,根据不同文件存在的情况返回不同的状态。
根据不同的返回状态,同样是该类中的doRecover方法进行处理,
[java]
534 public void doRecover(StorageState curState) throws IOException {
535 File curDir = getCurrentDir();
536 String rootPath = root.getCanonicalPath();
537 switch(curState) {
538 case COMPLETE_UPGRADE: // mv previous.tmp -> previous
539 LOG.info("Completing previous upgrade for storage directory "
540 + rootPath + ".");
541 rename(getPreviousTmp(), getPreviousDir());
542 return;
补充:软件开发 , Java ,