前面分析了与操作系统有关的Shell命令,它们用于与操作系统进行命令行方式的交互。在Hadoop中,自定义了FileSystem文件系统,这是基于Unix操作系统之上的文件系统,为了方便对FileSystem的管理,通过org.apache.hadoop.fs.FsShell类定义了对Hadoop FileSystem文件系统进行命令行方式管理的命令实现。
先给出对Hadoop文件系统进行管理的命令实现类的继承层次关系:
由于DFSAdmin类是对HDFS分布式文件系统提供基于命令行的管理功能,这里先不对DFSAdmin进行分析,在后面分析HDFS实现的时候,进行详细分析理解。
Configured就不用多说了,是Hadoop配置类的高层抽象。
Tool接口支持命令行方式的处理,如果需要通过命令行方式来执行一定的任务,都可以实现该接口,通过该接口定义的run方法来运行命令行。由于它继承自Configurable 接口,使得实现Tool的接口可以对特定的待执行的任务进行详细配置,满足执行一个命令能够完成任务的要求。下面是接口的定义:
在Hadoop中,Tool接口主要是为进行MapReduce并行计算而定义的,这里FsShell类实现了该接口,其实也是使得命令行执行与任务关联起来,通过执行命令行,而执行设置的待完成的任务。
下面来看FsShell类的具体实现。
既然,FsShell是与命令行有关的,那么我们就从其中对指定的命令实现的角度来看,分别对每个命令的实现进行阅读分析。在分析每个命令实现过程之前,先看一下该类中printUsage方法的执行,该方法能够打印出全部命令用法的信息,如下所示:
非常清晰明了,FsShell所支持的命令行,及其该命令的可以设置的参数,都在上述列表中显示出来。
另外,对于每个命令的帮助信息,都可以通过printHelp方法得到,例如,如果想要得到命令“ls”的帮助信息,调用printHelp("ls");即可。如果想要得到全部命令的帮助信息,只要给printHelp随便传入一个非命令字符串,如printHelp("hashyes3532333");,将打印出全部命令帮助信息,下面是一个帮助信息的片段:
下面介绍每个命令的实现:
- ls与lsr命令
执行ls命令,能够列出匹配指定Path下的全部文件,并且不递归列出子目录中文件;lsr能够列出指定Path下的所有文件,并且如果存在子目录,也会递归列出子目录中的文件。实现这两个命令的方法均为ls方法,如下所示:
实际上,执行ls命令真正实现执行的过程在重载的另一个方法ls中,如下所示:
lsr命令是递归列出满足给定模式的全部文件,也是基于上述方法实现的。
通过上面的ls的实现可知,列出FileSystem文件系统中的数据,是通过获取到该文件系统中保存的文件的FileStatus实例,因为FileStatus描述了位于该文件系统中对应文件的详细信息,然后通过它来打印出文件类表(包含必要的文件属性信息)。
- du与dus命令
du命令列出满足给定模式的全部文件对应的长度信息,dus执行后列出了满足给定模式的每个文件或目录的磁盘使用情况摘要信息,比du命令执行得到的结果信息要详细。
du命令实现是通过du方法,如下:
获取文件信息的方式,基本上都是一致的,通过文件系统来得到对应文件的统计信息。dus命令实现通过dus方法,与上面的实现基本类似,与ds实现不同的是,从FileSystem文件系统中获取到的文件不管是目录还行普通文件,都获取到其摘要信息(对应org.apache.hadoop.fs.ContentSummary)的长度,最后返回执行结果。
- mkdir命令
该命令根据跟定的字符串,创建该字符串标识的目录,实现方法为mkdir方法,实现比较简单易懂:
- touchz命令
该命令创建一个空文件,大小为0,通过touchz方法实现,实现的原理也是,通过调用文件系统的create方法执行文件的创建,如下所示:
- mv命令
该命令是移动文件,并支持文件的重命名,在FsShell类中通过rename方法实现的。方法实现如下所示:
接着,看一下上面调用的一个重载的rename方法,将一个文件进行移动和重命名操作:
可以看到,在FsShell类中定义的mv操作,不支持在不同的FileSystem文件系统之间进行文件的移动操作。
- rm与rmr命令
rm命令是删除文件,rmr是递归删除给定目录的子目录中的 文件,实现方式和ls与lsr类似。也存在两个重载的delete方法实现rm与rmr命令,先看其中一个:
上面,实际上在org.apache.hadoop.fs.FsShell.DelayedExceptionThrowing类中定义的globAndProcess方法中,循环执行了重载的delete方法,也就是真正真正实现删除的delete方法。也就是说,每调用执行delete方法,能够删除一个指定的文件。该方法实现如下所示:
执行删除文件操作的时候,是将存在于FileSystem文件系统上的文件移动到Hadoop定义的回收站.Trash目录中。
- cat命令
该命令取出全部满足给定模式的文件,并缓冲到标准输出流上。
该命令实现的方法为cat方法,如下所示:
调用方法printToStdout真正执行命令,该方法实现如下所示:
可以查阅IOUtils类中的具体实现。这里,先不对拷贝的具体实现进行分析,在后面会单独对涉及拷贝操作的实现进行详细分析。
- stat命令
该命令可以得到一个文件的详细统计信息,实现方法为stat方法,实现比较简单,不再累述。
- tail命令
tail命令执行显示一个文件的最后1KB内容,在tail方法中实现,如下所示:
- setrep命令
该命令是设置满足给定模式的文件的副本因子(replication factor)。不仅可以通过该类实现的setReplication方法对单个文件设置副本因子,也可以递归设置某个目录的所有文件的副本因子。实现设置副本因子的方法在该类中有多个,包括重载的方法,先队下面的方法来分析:
看一下重载的setReplication方法设置副本因子的实现过程:
继续看上面方法调用的setReplication方法,实现如下所示:
上面方法调用了setFileReplication方法,设置一个非目录文件的副本因子,实现过程如下所示:
我们再回到最前面重载的setReplication方法,已经完成了设置副本因子的任务,然后需要执行waitForReplication(waitList, rep);语句。此时,全部需要设置副本因子的文件都已经缓存到waitList列表中,下面看调用该方法对waitList列表中的文件执行的操作:
这里,有必要了解一下org.apache.hadoop.fs.BlockLocation的含义,可以看BlockLocation类定义的属性,如下所示:
可见,一个BlockLocation包含了一个文件的一个块的详细信息,包括这个块对应的全部副本(包含它本身) ,比如上述定义的有:所在主机、所在主机及其端口号、在网络拓扑结构中的全路径名称、块在文件中的偏移位置、块长度。显然,这些块副本长度和在文件中的偏移位置都是相同的,可以共享(分别对应length和offset属性),其他三个属性的信息就不相同了(可能存在某两个相同的情况)。
Hadoop文件系统中,一个文件对应多个块(Block),每个块默认大小设置为64M。那么,对于由多个块组成的文件来说,如果想要获取到该文件的全部块及其块副本的信息,就需要通过文件系统中文件的统计信息FileStatus来获取到一个BlockLocation[],该数组中对应的全部快就能够构成完整的该文件。
下面通过形式化语言来表达一下上面的含义:
假设一个文件F由n个块组成,则分别为:
B(1),B(2),……,B(n)
假设默认块的大小为BS,那么B(1)~B(n-1)一定是大小相同的块,大小都等于BS,而B(i)<=BS,这是显而易见的。
文件F的每个块B(i)都被存储在指定主机的文件系统中,假设存储到了主机H(i)上。为了快速计算,需要快速定位到文件F的Bi块上,也就是需要进行流式读取获取到,那么F的块B(i)需要有一个记录其详细信息的结构,也就是Hadoop定义的BlockLocation。假设Bi对应的描述信息对象为BL(i),那么BL(i)就包含了与块B(i)相关的全部块副本的信息,当然每个块副本同样包含与BL(i),相同的描述信息的属性,只是属性值不同而已。
假设文件F对应的块B(i)一共具有m个副本:
BR1(i),BR2(i),……,BRm(i)
这些块副本分别存储在对应如下的主机上:
H1(i),H2(i),……,Hm(i)
这些块副本分别对应指定主机的端口号分别如下:
H1(i):P1(i),H2(i):P2(i),……,Hm(i):Pm(i)
这些块副本对应的拓扑网络中的完整路径分别为:
U1(i),U2(i),……,Um(i)
假设块Bi的长度为LENGTH(i),偏移位置为OFFSET(i),那么,通过该文件的FileStatus获取的BlockLocation[i]的内容,形式化的可以描述为:
关于获取到一个文件(对应的FileStatus)的BlockLocation[],可以看到FileSystem类中getFileBlockLocations方法的实现,如下所示:
上面这个方法只能获取到本机上的一个块。如果在Hadoop分布式文件系统中,这个方法就需要被重写了,使得通过客户端能够获取到指定文件的块,在不同主机上分布的块副本。