2.8 访问与集成
本地访问HDFS的唯一方式是通过其提供的Java应用程序接口,其他的访问方式都是经过定义并建立在这些应用程序接口之上的,而且只能提供这些接口所允许的功能。为了使应用更容易使用和开发,HDFS借用了大量像Java I/O流这样的概念,因而HDFS 应用程序接口对开发者来讲非但不陌生,而且还非常简单。当然HDFS也对这些应用程序接口做了一些改动,以确保提供其所宣称的那些功能,但大部分的改动很容易理解,而且有很详细的文档。
为了访问HDFS,客户端——也就是用应用程序接口编写的应用程序——必须拥有一份配置数据副本,以获取NameNode的位置信息。这有些类似于Oracle客户端应用程序必须要有tnsname.ora文件一样。每个应用程序也必须能访问Hadoop程序库JAR文件,这也正如数据库应用必须依赖JDBC驱动程序JAR文件一样。多个客户端可以运行在集群里的任意物理主机上,也可以在集群之外。例如,MapReduce任务和HBase 区域服务器可以像任何其他客户程序一样访问HDFS,而有时它们也可以碰巧运行在同一台物理机器上,因为HDFS正好在这台机器上存储它们所需要的数据块。
在这里必须说明,为满足客户端直接访问DataNode的通信需要,客户程序与集群中所有节点相关端口间的网络连接必须保持畅通。这就隐含了网络规划、安全和带宽方面的要求,详情请参考4.6节“网络设计”。
2.8.1 命令行工具
Hadoop自带一组命令行工具,以便进行基本的文件系统操作。像所有的Hadoop工具一样,HDFS命令是hadoop命令行工具集的子集。运行hadoop fs可以显示基本的使用信息,如例2-1所示。
例2-1 hadoop fs帮助信息
[esammer@hadoop01 ~]$ hadoop fs
Usage: java FsShell
[-ls <path>]
[-lsr <path>]
[-df [<path>]]
[-du <path>]
[-dus <path>]
[-count[-q] <path>]
[-mv <src> <dst>]
[-cp <src> <dst>]
[-rm [-skipTrash] <path>]
[-rmr [-skipTrash] <path>]
[-expunge]
[-put <localsrc> ... <dst>]
[-copyFromLocal <localsrc> ... <dst>]
[-moveFromLocal <localsrc> ... <dst>]
[-get [-ignoreCrc] [-crc] <src> <localdst>]
[-getmerge <src> <localdst> [addnl]]
[-cat <src>]
[-text <src>]
[-copyToLocal [-ignoreCrc] [-crc] <src> <localdst>]
[-moveToLocal [-crc] <src> <localdst>]
[-mkdir <path>]
[-setrep [-R] [-w] <rep> <path/file>]
[-touchz <path>]
[-test -[ezd] <path>]
[-stat [format] <path>]
[-tail [-f] <file>]
[-chmod [-R] <MODE[,MODE]... | OCTALMODE> PATH...]
[-chown [-R] [OWNER][:[GROUP]] PATH...]
[-chgrp [-R] GROUP PATH...]
[-help [cmd]]
对于拥有基本shell经验的管理员而言,这些命令非常直观。主要的差别在于,因为HDFS是用户空间文件系统,所以没有当前工作目录的概念。所有路径要么是用户home目录下的绝对路径(推荐),要么是其相对路径[3]。绝对路径可以是/logs/2012/01/25形式,也可以是包含标明NameNode位置的完整URL,如hdfs://myNameNode.mycompany.com: 8020/logs/2012/01/25/。如果不使用完整URL语法,NameNode地址就是从core-site.xml的fs.default.name变量中获取的(参见例2-2)。
例2-2 列出HDFS文件和目录
[esammer@hadoop01 ~]$ hadoop fs -ls /user/esammer
Found 4 items
drwx------ - esammer supergroup 0 2012-01-11 15:06 /user/esammer/.staging
-rw-r--r-- 3 esammer supergroup 2788889 0 2012-01-10 13:41 /user/esammer/data.txt
drwxr-xr-x - esammer supergroup 0 2012-01-11 13:08 /user/esammer/teragen
drwxr-xr-x - esammer supergroup 0 2012-01-11 15:06 /user/esammer/terasort
为了证明HDFS名字空间与主机OS是完全独立的,可以尝试用标准的ls命令列出相同路径信息(参见例2-3)。
例2-3 尝试在OS中列出HDFS路径
esammer@hadoop01 ~]$ ls /user/esammer
ls: /user/esammer: No such file or directory
在很多方面,HDFS较之于本地OS文件系统更像一个远程文件系统。对HDFS文件的复制操作更像SCP或FTP操作,而非NFS上的文件系统操作。文件上传使用-put或-copyFromLocal,文件下载使用-get或-copyToLocal。为了方便,-copyFromLocal和-copyToLocal分别用来从本地复制文件到HDFS或将HDFS文件复制到本地,然后自动删除源文件(参见例2-4)。
例2-4 复制文件到HDFS和从HDFS复制文件
[esammer@hadoop01 ~]$ hadoop fs -ls /user/esammer/
Found 2 items
drwx------ - esammer supergroup 0 2012-01-11 15:06 /user/esammer/.staging
-rw-r--r-- 3 esammer supergroup 2788889 0 2012-01-10 13:41 /user/esammer/data.txt
[esammer@hadoop01 ~]$ hadoop fs -put /etc/passwd /user/esammer/
[esammer@hadoop01 ~]$ hadoop fs -ls /user/esammer/
Found 3 items
drwx------ - esammer supergroup 0 2012-01-11 15:06 /user/esammer/.staging
-rw-r--r-- 3 esammer supergroup 2788889 0 2012-01-10 13:41 /user/esammer/data.txt
-rw-r--r-- 3 esammer supergroup 2216 2012-01-25 21:07 /user/esammer/passwd
esammer@hadoop01 ~]$ ls -al passwd
ls: passwd: No such file or directory
[esammer@hadoop01 ~]$ hadoop fs -get /user/esammer/passwd ./
[esammer@hadoop01 ~]$ ls -al passwd
-rw-rw-r--+ 1 esammer esammer 2216 Jan 25 21:17 passwd
[esammer@hadoop01 ~]$ hadoop fs -rm /user/esammer/passwd
Deleted hdfs://hadoop01.sf.cloudera.com/user/esammer/passwd
HDFS的另一特别之处在于能够设置文件的复制因子。用-setrep命令,加上复制因子和可选标志(-R)表示要递归执行该操作(参见例2-5)。
例2-5 修改HDFS文件的复制因子
[esammer@hadoop01 ~]$ hadoop fs -setrep 5 -R /user/esammer/tmp/
Replication 5 set: hdfs://hadoop01.sf.cloudera.com/user/esammer/tmp/a
Replication 5 set: hdfs://hadoop01.sf.cloudera.com/user/esammer/tmp/b
[esammer@hadoop01 ~]$ hadoop fsck /user/esammer/tmp -files -blocks –locations
FSCK started by esammer from /10.1.1.160 for path /user/esammer/tmp at
Wed Jan 25 21:57:39 PST 2012
/user/esammer/tmp <dir>
/user/esammer/tmp/a 27888890 bytes, 1 block(s): OK
0. blk_2989979708465864046_2985473 len=27888890 repl=5 [10.1.1.162:50010,
10.1.1.161:50010, 10.1.1.163:50010, 10.1.1.165:50010, 10.1.1.164:50010]
/user/esammer/tmp/b 27888890 bytes, 1 block(s): OK
0. blk_-771344189932970151_2985474 len=27888890 repl=5 [10.1.1.164:50010,
10.1.1.163:50010, 10.1.1.161:50010, 10.1.1.162:50010, 10.1.1.165:50010]
在例2-5中,tmp目录下的文件a和b的复制因子被修改成为了5。然后,用fsck命令(请参见8.2.3节“用fsck来检查文件系统的一致性”)检查文件正确性,fsck命令的另一个好处是可以同时显示每个文件的块位置信息。在这个例子里,每个数据块的5个副本分别存储在集群中5个不同的DataNode中。注意,只有文件才有数据块列表,HDFS中的目录纯粹是元数据,并没有任何数据块。
2.8.2 用户空间文件系统(FUSE)
用户空间文件系统(FUSE),是一种允许开发者在用户空间实现的可安装文件系统的系统,不需要对内核模块进行开发。这样不仅可以让开发人员的工作变得更加简单,因为可以使用熟悉环境下的标准库;而且更加安全,因为开发人员引入的软件错误不会影响到内核。
Apache Hadoop和CDH都支持HDFS用户空间文件系统。你可能已经猜到,这样不仅可以将HDFS分布式文件系统挂载到任何其他的设备上,还可以兼容那些老的应用和系统,对Linux服务器上的普通目录文件进行读写,而其背后实际上是挂载了HDFS的设备。尽管FUSE很方便,但它并不是万能的,它有HDFS的所有属性,如不支持对文件进行修改、较大的处理延时、低效的随机访问、对大型流操作做了的一些优化以及支持大规模扩展。说得更清楚点,FUSE并没有将HDFS变成兼容POSIX的文件系统,而仅仅是加了一个兼容层,可以让应用通过使用HDFS来做一些基本的文件操作。
2.8.3 表示状态传输(REST)的支持
过去的几年中,表示性状态传输(Representational State Transfer,REST)作为一种与语言无关的方式被越来越广泛地应用,并与各种业务进行交互。Hadoop的所有原始的应用程序接口都是基于Java的,对于非Java的客户程序就会存在问题。应用程序可以通过hadoop fs命令行的方式解决问题,但是效率不高,而且容易发生错误。从Apache Hadoop 1.0.0和CDH4开始,WebHDFS(一种用于HDFS的REST API)已经成为标准软件的一部分。WebHDFS利用Hadoop HDFS守护进程中内嵌的Web服务器运行一套REST应用程序接口,模仿Java文件系统API(包括读/写操作)。WebHDFS支持包括Kerberos SPNEGO在内的完整的鉴权功能。 例2-6给出了一个基于WebHDFS的hadoop fs –ls /hbase的操作例子。
例2-6 用WebHDFS REST显示文件目录
[esammer@hadoop01 ~]$ curl http://hadoop01:50070/webhdfs/v1/hbase /?op= liststatus
{"FileStatuses":{"FileStatus":[
{"accessTime":0,"blockSize":0,"group":"hbase","length":0,"modificationTime":
1342560095961,"owner":"hbase","pathSuffix":"-ROOT-","permission":"755",
"replication":0,"type":"DIRECTORY"},
{"accessTime":0,"blockSize":0,"group":"hbase","length":0,"modificationTime":
1342560094415,"owner":"hbase","pathSuffix":".META.","permission":"755",
"replication":0,"type":"DIRECTORY"},
{"accessTime":0,"blockSize":0,"group":"hbase","length":0,"modificationTime":
1342561404890,"owner":"hbase","pathSuffix":".logs","permission":"755",
"replication":0,"type":"DIRECTORY"},
{"accessTime":0,"blockSize":0,"group":"hbase","length":0,"modificationTime":
1342561406399,"owner":"hbase","pathSuffix":".oldlogs","permission":"755",
"replication":0,"type":"DIRECTORY"},
{"accessTime":1342560093866,"blockSize":67108864,"group":"hbase","length":38,
"modificationTime":1342560093866,"owner":"hbase","pathSuffix":"hbase.id",
"permission":"644","replication":3,"type":"FILE"},
{"accessTime":1342560093684,"blockSize":67108864,"group":"hbase","length":3,
"modificationTime":1342560093684,"owner":"hbase","pathSuffix":"hbase.version",
"permission":"644","replication":3,"type":"FILE"}
]}}
与此同时,一个名为HttpFS的单独的REST HDFS代理服务也被开发出来。初看起来,似乎WebHDFS和HttpFS是用来解决同一个问题的,其实不然。虽然HttpFSAPI与WebHDFS完全兼容,但它们解决的是两种不同的架构问题。要使用每个守护进程的内嵌Web服务器,WebHDFS客户程序必须可以和集群中的所有节点进行通信,就像JAVA客户端一样。HttpFS的存在主要是为了解决这个问题,它更像一个跨不同网段的网关,客户程序只需和HttpFS守护进程连接,而由该守护进程使用标准JAVA API与HDFS集群进行通信。HttpFS的优点是可以减少与集群通信的数量,但代价是集群的总体规模和能力会受到限制,因为所有客户程序和HDFS之间的数据交换都是通过一个节点完成的。当然,设置多个HttpFS代理可以很好地解决单节点带宽限制的问题。另外,因为WebHDFS和HttpFS的API是完全兼容的,程序员在开发应用程序时需要充分考虑上述细节。最终的决策需要考虑数据吞吐率、网络规划和安全等方面。