有了上面的基础,我们可以开始分析 '。' 实现了接口 ',)。',) 是
'; 对底层存储的抽象。
下面给出了 ' 的关键成员变量:
FSVolumeSet volumes;
private HashMap<Block,ActiveFile> ongoingCreates = new HashMap<Block,ActiveFile>();
private HashMap<Block,DatanodeBlockInfo> volumeMap = null;
其中, 就是 ' 使用的所有 ,)) 是 * 到 ! 的映射,也就是说,说有
正在创建的 *,都会记录在 )) 里。
下面我们讨论 ' 中的方法。
public long getMetaDataLength(Block b) throws IOException;
得到一个 F 的元数据长度。通过 F 的 *&,找对应的元数据文件,返回文件长度。
public&* &*%DF(throws*'+,!
得到一个 block 的元数据输入流。通过 F 的 *&,找对应的元数据文件,在上面打开输入流。下面对于类似的简单方法,我们就不再仔细讨论
了。
publicboolean3+,%DF(throws*'+,!
判断 block 的元数据的元数据文件是否存在。简单方法。
publiclong / )%DF(throws*'+,!
F 的长度。简单方法。
publicDF DF%longF(throws*'+,!
通过 DF 的 *&,找到对应的 DF。简单方法。
public* DF*%DF(throws*'+,!
public* DF*%DF:longF'M(throws*'+,!
得到 DF 数据的输入流。简单方法。
publicDF* <*%DF:longFM:longFM(throws*'+,!
得到 DF 的临时输入流。注意,临时输入流是指对应的文件处于 目录中。新创建块时,块数据应该写在 目录中,直到写操作成功,文
件才会被移动到 目录中,如果失败,就不会影响 目录了。简单方法。
publicDF$<DF%DF:boolean=(throws*'+,!
得到一个 F 的输出流。DF 既包含了数据输出流,也包含了元数据(校验文件)输出流,这是一个相当复杂的方法。
参数 说明这次写是不是对以前失败的写的一次恢复操作。我们先看正常的写操作流程:首先,如果输入的 *
是个正常的数据块,或当前的 * 已经有线程在写,1* 会抛出一个异常。否则,将创建相应的临时数据文件和
临时元数据文件,并把相关信息,创建一个 ! 对象,记录到 )) 中,并创建返回的
*0。前面我们已经提过,建立新的 ! 时,当前线程会自动保存在 ! 的 中。
我们以 *@.A8676B9.7C98.CA.A. 为例,当 '; 需要为 *,' 为 .A8676B9.7C98.CA.A. 创建写流时,
'; 创建文件 *@.A8676B9.7C98.CA.A. 做为临时数据文件,对应的 文件是
*@.A8676B9.7C98.CA.A.@GGGGGG。其中 GGGGGG 是版本号。
为 时,表明我们需要从某一次不成功的写中恢复,流程相对于正常流程复杂。如果不成功的写是由于提交
(参考 E)-* 方法)后的确认信息没有收到,先创建一个 文件(备份)。接着,1* 检查是否
有还有对文件写的线程,如果有,则通过线程的 ) 方法,强制结束线程。这就是说,如果有线程还在写对应的文件块,
该线程将被终止。同时,从 )) 中移除对应的信息。接下来将根据临时文件是否存在,创建复用临时数据文件
和临时数据元文件。后续操作就和正常流程一样,根据相关信息,创建一个 ! 对象,记录到 ))
中……
由于这块涉及了一些 "' 写文件时的策略,以后我们还会继续讨论这个话题。
publicvoidDF%DFF:DF$F(throws*'+,!
更新一个 F。这也是一个相当复杂的方法。
* 的最外层是一个死循环,循环的结束条件,是没有任何和这个数据块相关的写线程。每次循环, *
都会去调用一个叫 F * 的内部方法。F * 发现已经没有线程在写这个块,就会跟新和这个数据块相