第3章HDFS分布式文件系统 3.1相关基本概念 (1) 文件系统。文件系统是操作系统提供的用于解决“如何在磁盘上组织文件”的一系列方法和数据结构。 (2) 分布式文件系统。分布式文件系统是指利用多台计算机协同作用解决单台计算机所不能解决的存储问题的文件系统。如单机负载高、数据不安全等问题。 (3) HDFS(Hadoop Distributed File System,Hadoop分布式文件系统)。它是Hadoop项目的核心子项目,是分布式计算中数据存储管理的基础,它是基于流式数据访问和处理超大文件的需求而开发的分布式文件系统,可以运行于廉价的商用服务器上。HDFS源于谷歌公司在2003年10月份发表的GFS(Google File System)论文。 3.2HDFS存储架构 HDFS采用Master/Slave架构。一个HDFS集群是由一个或几个NameNode和一定数量的DataNode组成的。HDFS上的文件是以数据块的形式存放的,这些数据块存储在一组DataNode上。NameNode执行文件系统的命名空间操作,比如打开、关闭、重命名文件或目录; 也负责确定数据块到具体DataNode节点的映射。DataNode负责处理文件系统客户端的读写请求,并在NameNode的统一调度下执行数据块的创建、删除和复制。HDFS存储架构如图31所示。 图31HDFS存储架构 3.2.1HDFS写入流程 (1) Hadoop客户端和NameNode通信请求上传文件,NameNode检查目标文件是否已存在、父目录是否存在。 (2) NameNode返回信息给Hadoop客户端是否可以上传。 (3) Hadoop客户端会先对文件进行切分,如一个Block(块)大小为128MB,如果上传文件为300MB,文件会被切分成3个Block,其中两个Block为128MB,一个Block为44MB,并向NameNode发上传请求。 (4) NameNode返回DataNode的服务器信息给Hadoop客户端。 (5) Hadoop客户端请求一台DataNode上传数据(本质上是一个RPC调用,建立通道),第一个DataNode收到请求会继续调用第二个DataNode,然后第二个DataNode调用第三个DataNode,将整个通道建立完成,逐级返回Hadoop客户端。 (6) Hadoop客户端开始往第一个DataNode上传第一Block(先从磁盘读取数据放到一个本地内存缓存),以Packet为单位(一个Packet为64KB),当然在写入时通道会进行数据校验,它并不是通过一个Packet进行一次校验而是以checksum为单位进行校验(512B),第一台DataNode收到一个Packet就会传给第二台,第二台传给第三台; 第一台每传一个Packet会放入一个应答队列等待应答。 (7) 当一个Block传输完成之后,Hadoop客户端再次请求NameNode上传第二个Block的DataNode服务器,直至所有的Block上传完成。 3.2.2HDFS读取流程 (1) Hadoop客户端向NameNode发送请求,获得存放在NameNode节点上文件的Block位置映射信息。 (2) NameNode把文件所有Block的位置信息返回给Hadoop客户端。 (3) Hadoop客户端拿到Block的位置信息后并行读取Block信息,Block默认有3个副本,所以每一个Block只需要从一个副本读取。 (4) Hadoop客户端从DataNode上取回文件的所有Block按照一定的顺序组成最终需要的文件。 3.3HDFS的优点与缺点 3.3.1HDFS的优点 (1)高容错性。数据自动保存多个副本。它通过增加副本的形式提高容错性。某一个副本丢失以后,它可以自动恢复。 (2) 适合处理高吞吐量。HDFS通过移动计算而不是移动数据,并将数据位置暴露给计算框架,对计算的时延不敏感。 (3) 适合存储和管理大规模数据。HDFS处理数据达到太字节(TB)甚至皮字节(PB)级别的数据,文件数量达百万规模以上,节点处理可达1万节点的规模。 (4) 适合一次写入,多次读取。文件一旦写入不能修改,只能追加,能够保证数据的一致性。 (5) 适合处理非结构化数据。HDFS可以处理多类型的数据(音频、视频、文本)。 3.3.2HDFS的缺点 (1) 不适合低延时数据访问。HDFS适合高吞吐率的场景,就是在某一段时间内写入大量的数据。但是HDFS不适合低延时的数据访问,比如毫秒级以内读取数据是很难做到的。 (2) 不适合小文件存储。小文件是指小于HDFS系统的Block大小的文件(1.0版本默认为64MB,2.0版本默认为128MB)的文件,小文件存储会占用NameNode大量的内存来存储文件、目录和块信息。而NameNode的内存是有限的。 (3) 不支持文件随机修改。HDFS不允许多个线程同时写文件; 仅支持数据追加,不支持文件的随机修改。 3.4HDFS Shell常用命令 HDFS Shell可以直接与Hadoop分布式文件系统进行交互。HDFS Shell常用命令如表31所示。 表31HDFS Shell常用命令 命 令 参 数功 能 描 述命 令 参 数功 能 描 述 ls查看指定路径的目录结构text源文件输出为文本格式 du统计目录下所有文件大小mkdir创建空白文件夹 mv移动文件put上传文件 cp复制文件get下载文件 rm删除文件/空白文件夹help获得帮助信息 cat查看文件内容cat查看文件内容 例如,列出文件或文件夹Shell命令为hadoop fs ls/,运行结果如下: 创建文件夹Shell命令为hadoop fs mkdir/wcdoc,运行结果如下: 上传文件Shell命令为hadoop fs put/wc.txt/wcdoc,运行结果如下: 下载文件Shell命令为hadoop fs get/wcdoc/wc.txt/wc1.txt,运行结果如下: 查看文件内容Shell命令为hadoop fs cat/wcdoc/wc.txt,运行结果如下: 删除文件(夹)Shell命令为hadoop fs rm/wcdoc/wc.txt,运行结果如下: 3.5HDFS的Java API 通过编程的形式操作HDFS,其核心是使用HDFS提供的Java API构造一个访问客户端对象,然后通过客户端对象对HDFS上的文件进行操作(增加、删除、查找)。 在Java中操作HDFS,创建一个客户端实例主要涉及以下两个类: 一是Configuration类,该类的对象封装了客户端或者服务器的配置,从中获取Hadoop集群的配置信息; 二是FileSystem类,该类的对象是一个文件系统对象。 FileSystem对象的一些方法可以对文件进行操作,HDFS常用Java API如表32所示。 表32HDFS常用Java API 方法名功能 copyFromLocalFile(Path src,Path dst)从本地磁盘复制文件到HDFS copyToLocalFile(Path src,Path dst)从HDFS复制文件到本地磁盘 mkdirs(Path f)建立子目录 rename(Path src,Path dst)重命名文件或文件夹 delete(Path f)删除指定文件 搭建测试项目具体步骤如下所述。 (1) 创建一个MAVEN项目,并在项目的pom.xml文件中引入hadoopcommon、hadoophdfs、hadoopclient以及单元测试junit的依赖,pom.xml文件中的依赖包如下: org.apache.hadoop hadoop-common 2.7.4 org.apache.hadoop hadoop-hdfs 2.7.4 org.apache.hadoop hadoop-client 2.7.4 org.apache.hadoop hadoop-MapReduce-client-core 2.7.4 junit junit 4.12 org.apache.zookeeper zookeeper 3.4.10 (2) 编写Java类HDFS_API_TEST,代码和相关说明如下: package com.chapter03.hdfsdemo; import java.io.FileNotFoundException; import java.io.IOException; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.BlockLocation; import org.apache.hadoop.fs.FileStatus; import org.apache.hadoop.fs.FileSystem; import org.apache.hadoop.fs.LocatedFileStatus; import org.apache.hadoop.fs.Path; import org.apache.hadoop.fs.RemoteIterator; import org.junit.Before; import org.junit.Test; public class HDFS_API_TEST{ FileSystem fs = null; @Before public void init()throws Exception{ //构造配置参数对象 Configuration conf = new Configuration(); //设置访问的HDFS的URI conf.set("fs.defaultFS","hdfs://172.16.106.69:9000"); //设置本机的Hadoop路径 System.setProperty("hadoop.home.dir","D:\\hadoop"); //设置客户端访问身份 System.setProperty("HADOOP_USER_NAME","root"); //通过FileSystem的静态get()方法获取文件系统客户端对象 fs = FileSystem.get(conf); } @Test public void AddFileToHdfs()throws IOException{ //上传文件本地路径 Path src = new Path("D:/hdfs.txt"); //HDFS的目标路径 Path dst = new Path("/teshdfs"); //上传文件方法 fs.copyFromLocalFile(src,dst); //关闭资源 fs.close(); } //从HDFS中复制文件到本地文件系统 @Test public void DownloadFileToLocal()throws IllegalArgumentException,IOException { //下载文件 fs.copyToLocalFile(new Path("/testFile"),new Path("D:/")); } //创建、删除、重命名文件 @Test public void MkdirAndDeleteAndRename()throws Exception{ //创建目录 fs.mkdirs(new Path("/test1")); fs.rename(new Path("/test1"),new Path("/test3")); //删除文件夹,如果是非空文件夹,则参数2必须赋值true fs.delete(new Path("/test2"), true); } //查看目录信息 @Test public void ListFiles()throws FileNotFoundException, IllegalArgumentException, IOException { //获取迭代器对象 RemoteIterator listFiles = fs.listFiles(new Path("/"), true); while (listFiles.hasNext()){ LocatedFileStatus fileStatus = listFiles.next(); //打印当前文件名 System.out.println(fileStatus.getPath().getName()); //打印当前文件块大小 System.out.println(fileStatus.getBlockSize()); //打印当前文件权限 System.out.println(fileStatus.getPermission()); //打印当前文件内容长度 System.out.println(fileStatus.getLen()); //获取该文件块信息(包含长度、数据块、DataNode的信息) BlockLocation[] blockLocations = fileStatus.getBlockLocations(); for (BlockLocation bl : blockLocations){ System.out.println("block-length:" + bl.getLength()+ "--" + "block- offset:" + bl.getOffset()); String[] hosts = bl.getHosts(); for (String host : hosts){ System.out.println(host); } } } } //查看文件及文件夹信息 @Test public void ListFileAll()throws FileNotFoundException,IllegalArgumentException, IOException { //获取HDFS系统根目录的元数据信息 FileStatus[] listStatus = fs.listStatus(new Path("/")); String filelog = "文件夹--"; for (FileStatus fstatus : listStatus){ //判断是文件还是文件夹 if (fstatus.isFile()) filelog = "文件--"; System.out.println(filelog + fstatus.getPath().getName()); } } } (3) 运行测试。 对于Windows机器上没有Hadoop的环境的客户端,需要下载Hadoop的服务进程winutils.exe,下载地址为https://codeload.github.com/srccodes/hadoopcommon2.2.0bin/zip/master。解压后放在一个没有中文的文件夹下。在程序中指定路径,本例把它放在D盘hadoop目录下,同时在类的init()方法中加入语句: System.setProperty("hadoop.home.dir", "D:\\hadoop")。指定客户端Hadoop的伪安装路径后,可以利用单元测试junit进行各个方法的运行测试。 3.6本章小结 本章主要介绍了Hadoop中非常重要的分布式存储文件系统HDFS,分析了HDFS的存储架构以及常用Shell命令和Java API,并且对Java API的编程进行了实例演示。