本节书摘来异步社区《Hadoop MapReduce实战手册》一书中的第2章,第2.8节,作者: 【美】Srinath Perera , Thilina Gunarathne 译者: 杨卓荦 责编: 杨海玲,更多章节内容可以访问云栖社区“异步社区”公众号查看。
2.8 使用HDFS的Java API
Hadoop MapReduce实战手册
HDFS Java API可用于任何Java程序与HDFS交互。该API使我们能够从其他Java程序中利用到存储在HDFS中的数据,也能够使用其他非Hadoop的计算框架处理该数据。有时,可能也会遇到要直接从MapReduce应用程序中访问HDFS的用例。但是,如果你是在HDFS中直接通过map或reduce任务写入或修改文件,那么你要知道,这样做实际上违反了MapReduce构架的无副作用的本质,可能会导致某些用例出现数据一致性问题。
准备工作
设置HADOOP_HOME环境变量指向Hadoop的安装根目录。
操作步骤
下列步骤显示了如何使用HDFS的Java API来对HDFS集群使用Java程序执行文件系统操作。
- 下面的示例程序会在HDFS中创建一个新文件,写一些文本内容到新建文件,并从HDFS中读回该文件:
importjava.io.IOException;
importorg.apache.hadoop.conf.Configuration;
importorg.apache.hadoop.fs.FSDataInputStream;
importorg.apache.hadoop.fs.FSDataOutputStream;
importorg.apache.hadoop.fs.FileSystem;
importorg.apache.hadoop.fs.Path;
public class HDFSJavaAPIDemo {
public static void main(String[] args) throws IOException {
Configuration conf = new Configuration();
FileSystemfs = FileSystem.get(conf);
System.out.println(fs.getUri());
Path file = new Path("demo.txt");
if (fs.exists(file)) {
System.out.println("File exists.");
} else {
// Writing to file
FSDataOutputStreamoutStream = fs.create(file);
outStream.writeUTF("Welcome to HDFS Java API!!!");
outStream.close();
}
// Reading from file
FSDataInputStreaminStream = fs.open(file);
String data = inStream.readUTF();
System.out.println(data);
inStream.close();
fs.close();
}
}
- 将上面的程序编译并打包成一个JAR包。解压本章提供的源码包,转到HDFS_Java_API文件夹,然后运行Ant构建即可。HDFSJavaAPI.jar文件将在build文件夹中被创建。
>cd HDFS_java_API
>ant
可以使用下面的Ant构建文件来编译上面的示例程序:
<project name="HDFSJavaAPI" default="compile" basedir=".">
<property name="build" location="build"/>
<property environment="env"/>
<path id="hadoop-classpath">
<filesetdir="${env.HADOOP_HOME}/lib">
<include name="**/*.jar"/>
</fileset>
<filesetdir="${env.HADOOP_HOME}">
<include name="**/*.jar"/>
</fileset>
</path>
<target name="compile">
<mkdirdir="${build}"/>
<javacsrcdir="src" destdir="${build}">
<classpathrefid="hadoop-classpath"/>
</javac>
<jar jarfile="HDFSJavaAPI.jar" basedir="${build}"/>
</target>
<target name="clean">
<delete dir="${build}"/>
</target>
</project>
- 可以在Hadoop上使用以下命令执行上述示例。使用hadoop脚本运行示例,可以确保它采用了当前配置的HDFS,并从Hadoop的类路径中加载了必要的依赖。
>bin/hadoop jar HDFSJavaAPI.jar HDFSJavaAPIDemo
hdfs://yourhost:9000
Welcome to HDFS Java API!!!
- 使用ls命令列出新创建的文件:
>/bin/hadoopfs -ls
Found 1 items
-rw-r--r-- 3 foosupergroup 20 2012-04-27 16:57 /user/
foo/demo.txt
工作原理
为了以编程方式与HDFS进行交互,首先需要得到当前配置文件系统的句柄。实例化一个Configuration对象,并获得一个Hadoop环境中的FileSystem句柄,它将指向当前环境的HDFS NameNode。有几种替代配置FileSystem对象的方法,将在本节的“更多参考”中的“配置文件系统对象”中讨论。
Configuration conf = new Configuration();
FileSystemfs = FileSystem.get(conf);
FileSystem.create(filePath)方法会在指定的路径创建一个新的文件,并提供一个到新创建的文件的FSDataOutputStream对象。FSDataOutputStream封装了java.io.DataOutputStream,并允许程序向文件中写入基本Java数据类型。如果该文件存在,FileSystem.Create()方法将覆盖该文件。在这个例子中,该文件将在HDFS中相对于用户的主目录进行创建,产生类似/user//demo.txt的路径。
Path file = new Path("demo.txt");
FSDataOutputStreamoutStream = fs.create(file);
outStream.writeUTF("Welcome to HDFS Java API!!!");
outStream.close();
FileSystem.open(filePath)打开给定文件的FSDataInputStream。FSDataInputStream封装了java.io.DataInputStream,允许程序从文件中读取基本Java数据类型。
FSDataInputStreaminStream = fs.open(file);
String data = inStream.readUTF();
System.out.println(data);
inStream.close();
更多参考
HDFS的Java API支持的文件系统操作比我们在上面的示例中用到的多得多。完整的API文档可以在
http://hadoop.apache.org/common/docs/current/api/org/apache/hadoop/fs/FileSystem.html找到。
配置文件系统对象
我们也可以在Hadoop的环境之外使用HDFS的Java API。当这样做时,必须显式配置HDFS的NameNode和端口。以下是几种进行该项配置的方法。
可以通过如下方式在获得FileSystem对象之前加载Configuration对象的配置文件。但需要确保将所有的Hadoop和依赖库都添加到类路径中。
Configuration conf = new Configuration();
conf.addResource(new Path("..._/hadoop/conf/core-site.xml_"));
conf.addResource(new Path("..._/hadoop/conf/hdfs-site.xml_"));
FileSystemfileSystem = FileSystem.get(conf);
还可以通过如下方式指定NameNode和端口。将NAMENODE_HOSTNAME和PORT替换为HDFS安装的NameNode的主机名和端口。
Configuration conf = new Configuration();
conf.set("fs.default.name", "hdfs://NAMENODE_HOSTNAME:PORT");
FileSystemfileSystem = FileSystem.get(conf);
HDFS的文件系统API,是一种支持多个文件系统的抽象。如果上述程序无法找到有效的HDFS配置,它将会指向本地文件系统,而不是HDFS。可以通过如下方式使用getUri()函数识别FileSystem对象的当前文件系统。在使用正确的HDFS配置文件的情况下,会返回hdfs://your_namenode:port,在使用本地文件系统的情况下,则会返回file:///。
fileSystem.getUri();
获取文件的数据块列表
FileSystem对象的getFileBlockLocations()函数,可以用来获取存储在HDFS中的文件数据块的列表,同时也可以获取存储块的主机名和块的偏移量。如果计划使用Hadoop MapReduce之外的其他框架来执行文件数据的任何数据本地化操作,那么这些信息将会非常有用。
FileStatusfileStatus = fs.getFileStatus(file);
BlockLocation[] blocks = fs.getFileBlockLocations(
fileStatus, 0, fileStatus.getLen());