1.       概述
该内容主要设计基于ClientProtocol协议之上的方法,对其中的一些方法做出解释
2.       getBlockLocations
函数原型:public LocatedBlocks getBlockLocations(String src,long offset, long length)
函数作用:用于确定文件内容的位置,它的输入参数为:文件名、偏移量、长度、返回值是一个LocatedBlocks 对象的列表:
图1 LocatedBlocks 对象
它是定义在ClientProtocol中的,然后具体的调用都是在NameNode.java中的。具体实现如下:
public LocatedBlocks   getBlockLocations(String src,
                                          long offset,
                                          long length) throws IOException {
    myMetrics.numGetBlockLocations.inc();
    return namesystem.getBlockLocations(getClientMachine(),
                                        src, offset, length);
}
通过上面可以看到,这里调用了FSNamesystem.java中的getBlockLocations,具体的实现全部在这里。
NameSystem 中这样的方法首先会检查权限和对参数进行检查(如偏移量
和长度要大于0),然后再调用实际的方法。找LocatedBlocks 先找src 对应的INode,然后通过INode 的getBlocks 方法,可以拿到该节点的Block 列表,如果返回为空,表明该INode 不是文件,返回null;如果Block 列表长度为0,以空的Block 数组构造返回的LocatedBlocks。如果Block 数组不为空,则通过请求的偏移量和长度,就可以把这个区间涉及的Block 找出来,对于每一个block,执行:
1) 通过BlocksMap 我们可以找到它存在于几个DataNode (BlocksMap.numNodes 方法)
2) 计算包含该数据块但数据块是坏的DataNode 的数目(通过NameSystem.countNodes 方法,间接访问CorruptReplicasMap 中的信息)
3) 计算坏数据块的数目(CorruptReplicasMap.numCorruptReplicas 方法,应该和上面的数相等)
4) 通过上面的计算,我们得到现在还OK 的数据块数目;
5) 从BlocksMap 中找出所有OK 的数据块对应的DatanodeDescriptor(DatanodeInfo 的父类)
6) 创建对应的LocatedBlock
收集到每个数据块的LocatedBlock 信息后,很自然就能构造LocatedBlocks 对象。getBlockLocations 其实只是一个读的方法,请求到了NameNode 以后只需要查表就行了。
3. Create函数
函数原型:public void create(String src,
                     FsPermission masked,
                             String clientName,
                             boolean overwrite,
                             short replication,
                             long blockSize
                             ) throws IOException;
函数作用:create 方法,该方法用于在目录树上创建文件(创建目录使用mkdir),需要的参数比较多,包括文件名,权限,客户端名,是否覆盖已存在文件,副本数和块大小。NameNode 的create 调用NameSystem 的startFile 方法(startFile 需要的参数clientMachine 从线程局部变量获取)。
图2 create方法流程
startFileInternal 不但服务于startFile,也被appendFile 调用(通过参数append 区分)。方法的最开始是一堆检查,包括:安全模式,文件名src 是否正确,权限,租约,replication 参数,overwrite 参数(对append 操作是判断src 指向是否存在并且是文件)。租约检查很简单,如果通过FSDirectory.getFileINode(src)得到的文件是出于构造状态,表明有客户正在操作该文件,这时会抛出异常AlreadyBeingCreatedException。
如果对于创建操作,会通过FSDirectory 的addFile 往目录树上添加一个文件并在租约管理器里添加一条记录。总的来说,最简单的create 流程就是在目录树上创建一个INodeFileUnderConstruction 对象并往租约管理器里添加一条记录
4. Append
函数原型:public LocatedBlock append(String src, String clientName) throws IOException;
函数作用:它的返回值是LocatedBlock,比起getBlockLocations,它只需要返回数组的一项。appendFile
NameSystem 的实现方法,它首先调用上面讨论的startFileInternal 方法(已经在租约管理器里添加了一条记录)然后写日志。然后寻找对应文件INodeFile 中记录的最后一个block,并通过BlocksMap.getStoredBlock()方法得到BlockInfo,然后再从BlocksMap 中获得所有的DatanodeDescriptor,就可以构造LocatedBlock 了。需要注意的,如果该Block 在需要被复制的集合(UnderReplicatedBlocks)中,移除它。如果文件刚被创建或者是最后一个数据块已经写满,那么append 会返回null,这是客户端需要使用addBlock,为文件添加数据块。
return information about the last partial block if any,如果最后一个文件写满了的话,返回null,需要满足block了。
5. setReplication
函数原型:public boolean setReplication(String src, short replication)
函数作用:重新设置某一个文件的备份数,但是这是一种不好的方法
注意:方法的定义也是在NameNode.java中实现的,如下所示:
public boolean setReplication(String src,
                                short replication
                                ) throws IOException {
    return namesystem.setReplication(src, replication);
 }
其中src是路径,replication是备份的数量,然后调用对应的setReplication方法。在setReplication中调用setReplicationInternal方法,具体的实现全部在里面。
setReplicationInternal 上来自然是检查参数了,然后通过FSDirectory 的setReplication,设置新的副本数,并获取老的副本数。根据新旧数,决定删除/复制数据块
实现:fileBlocks = dir.setReplication(src, replication, oldReplication);
6. setPermission
函数原型:public void setPermission(String src, FsPermission permission)
函数作用:用于设置文件的访问权限,非常简单,首先检查是否有权限,然后调用FSDirectory.setPermission 修改文件访问权限
7. setSafeMode
函数原型:public boolean setSafeMode(FSConstants.SafeModeAction action) throws IOException;
函数作用:前面我们已经介绍了NameNode 的安全模式,客户端通过上面的方法,可以让NameNode 进入(SAFEMODE_ENTER)/退出
SAFEMODE_LEAVE)安全模式或查询(SAFEMODE_GET)状态。FSNamesystem 的setSafeMode 处理这个命令,对于进入安全模式的请求,如果系统现在不处于安全模式,那么创建一个SafeModeInfo 对象(创建的这个对象有别于启动时创建的那个SafeModeInfo,它不会自动退出,因为threshold=1.5f),这标志着系统进入安全模式。退出安全模式很简单,将safeMode
赋空就可以
8. getListing
函数原型:public FileStatus[] getListing(String src) throws IOException;
函数作用:getListing 对应于UNIX 系统的ls 命令,返回值是FileStatus 数组
3 FileStatus类图
FileStatus 的类图如上,它其实给出了文件的详细信息,如大小,文件主等等。其实,这些信息都存在INode*中,我们只需要把这些信息搬到FileStatus 中就OK 啦。FSNamesystem 和FSDirectory 中都有同名方法,真正干活的地方在FSDirectory 中。getListing 不需要写日志。public long[] getStats() throws IOException;getStatus 得到的是文件系统的信息,UNIX 对应命令为du,它的实现更简单,所有的信息都存放在FSNamesystem 对象里。public DatanodeInfo[] getDatanodeReport(FSConstants.DatanodeReportType type)
throws IOException;getDatanodeReport,获取当前DataNode 的状态,可能的选项有DatanodeReportType.ALL, IVE 和DEAD。FSNamesystem 的同名方法调用getDatanodeListForReport,通过HostsFileReader 读取对应信息。
public long getPreferredBlockSize(String filename) throws IOException;
9. abandonBlock(在DFSClient.java中调用)
函数原型:public void abandonBlock(Block b, String src, String holder) throws IOException;
函数作用:abandonBlock 用于放弃一个数据块。普通的文件系统中并没有“放弃”操作,HDFS 出现放弃数据块的原因,如下图所示。当客户端通过其他操作(如下面要介绍的addBlock 方法)获取LocatedBlock 后,可以打开到一个block 的输出流,由于从DataNode出错到NameNode 发现这个信息,需要有一段时间(NameNode 长时间收到DataNode 心跳),打开输出流可能出错,这时客户端可以向NameNode 请求放弃这个数据块。
10.addBlock
函数原型:public LocatedBlock addBlock(String src, String clientName) throws IOException;
函数作用:写HDFS 的文件时,如果数据块被写满,客户端可以通过addBlock 创建新的数据块。具体的创建工作由FSNamesystem 的getAdditionalBlock 方法完成,当然上来就是一通检查(是否安全模式,命名/存储空间限额,租约,数据块副本数,保证DataNode已经上报数据块状态),然后通过ReplicationTargetChooser,选择复制的目标(如果目标数不够副本数,又是一个异常),然后,就可以分配数据块了。allocateBlock 创建一个新的Block 对象,然后调用addBlock,检查参数后把数据块加到BlocksMap对象和对应的INodeFile 对象中。allocateBlock 返回后,getAdditionalBlock 还会继续更新一些需要记录的信息,最后返回一个新构造的LocatedBlock。
11.complete
函数原型:public boolean complete(String src, String clientName) throws IOException;
函数作用:当客户端完成对数据块的写操作后,调用complete 完成写操作。方法complete 如果返回是false,那么,客户端需要继续调用complete 方法。
12.rename
函数原型:public boolean rename(String src, String dst) throws IOException;
函数作用:修改文件名
流程:DFSClient.java中rename方法调用NameNode.java中的rename方法
然后调用FSNamesystem.java中的renameTo方法
13.delete
函数原型:public boolean delete(String src) throws IOException;
函数作用:删除文件(流程跟rename方法是一样的)
14.mkdirs
函数原型:public boolean mkdirs(String src, FsPermission masked) throws IOException;
函数作用:创建文件夹
15.getListing
函数原型:public FileStatus[] getListing(String src) throws IOException;
函数作用: