第一步是创建一个项目,这样你可以创建MapReduce程序并通过命令行或IDE在本地运行。例6-3的POM显示了创建及测试
mapreduce程序所需要的依赖。
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.hadoopbook</groupId>
<artifactId>hadoop-book-mr-dev</artifactId>
<version>4.0</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<hadoop.version>2.5.1</hadoop.version>
</properties>
<dependencies>
<!-- Hadoop main client artifact -->
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-client</artifactId>
<version>${hadoop.version}</version>
</dependency>
<!-- Unit test artifacts -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.mrunit</groupId>
<artifactId>mrunit</artifactId>
<version>1.1.0</version>
<classifier>hadoop2</classifier>
<scope>test</scope>
</dependency>
<!-- Hadoop test artifact for running mini clusters -->
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-minicluster</artifactId>
<version>${hadoop.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<finalName>hadoop-examples</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.5</version>
<configuration>
<outputDirectory>${basedir}</outputDirectory>
</configuration>
</plugin>
</plugins>
</build>
</project>
依赖部分是POM的有趣的地方。创建MapReduce job,你只需要hadoop-client依赖,它包含了所有
与HDFS和MapReduce交互所需要的class。 为了运行单元测试,我全使用junit,写MapReduce测试,
我们使用mrunit。hadoop-minicluster库包含了迷你集群,对于在单JVM上测试HADOOP集群很有用。
许多IDE可以直接读取POM文件,你只需要指出包含的pom.xml的文件夹并开始写代码。或者,你可以
使用MAVEN来生成你的IDE的配置文件。例如,下面创建了ECLIPSE的配置文件,这样你可以把项目导入
ECLIPSE:
% mvn eclipse:eclipse -DdownloadSources=true -DdownloadJavadocs=true
配置管理
开发HADOOP应用时,应用经常在本地与集群之间切换运行。事实上,你可能有与多个集群一起工作,
或者你可能在一个本地的伪分布式集群上测试。
为了适应这种变化,可以在HADOOP配置文件中包含每一个集群的连接信息,当你运行HADOOP应用
或工具时指定用哪一个。作为最佳实践,建议这些文件都在HADOOP安装目录外,这样使切换HADOOP
版本时更简单,不会重复或丢失配置。
本书中,假设存在一个conf目录,包含三个配置文件:hadoop-local.xml,hadoop-localhost.xml,hadoop-
cluster.xml。注意,这些文件名称没有特别的含义;他们只是方便把一些配置打包。
hadoop-local.xml包含HADOOP默认文件系统配置及运行MapReduce job的本地(JVM中)框架:
<?xml version="1.0"?>
<configuration>
<property>
<name>fs.defaultFS</name>
<value>file:///</value>
</property>
<property>
<name>mapreduce.framework.name</name>
<value>local</value>
</property>
</configuration>
hadoop-localhost.xml的配置指向一个namenode和一个YARN资源管理器,同时运行在本地:
<?xml version="1.0"?>
<configuration>
<property>
<name>fs.defaultFS</name>
<value>hdfs://localhost/</value>
</property>
<property>
<name>mapreduce.framework.name</name>
<value>yarn</value>
</property>
<property>
<name>yarn.resourcemanager.address</name>
<value>localhost:8032</value>
</property>
</configuration>
最后,hadoop-cluster.xml包含集群namenode和yarn 资源管理器地址的详细信息:
<?xml version="1.0"?>
<configuration>
<property>
<name>fs.defaultFS</name>
<value>hdfs://namenode/</value>
</property>
<property>
<name>mapreduce.framework.name</name>
<value>yarn</value>
</property>
<property>
<name>yarn.resourcemanager.address</name>
<value>resourcemanager:8032</value>
</property>
</configuration>
你可以在这引起文件中按需添加配置。
这样设置后,可以通过-conf命令来切换到任意配置。例如,下面的命令显示一个在本地运行
的伪分布式模式的HDFS服务器的目录列表:
% hadoop fs -conf conf/hadoop-localhost.xml -ls .
Found 2 items
drwxr-xr-x - tom supergroup 0 2014-09-08 10:19 input
drwxr-xr-x - tom supergroup 0 2014-09-08 10:19 output
如果省略了-conf选项,会使用$HADOOP_HOME下的子目录etc/hadoop中的HADOOP配置。或者,如果设置
了HADOOP_CONF_DIR,会从这里读取配置。
HADOOP自带的工具运行-conf选项,但通过使用Tool接口,也可以使你的程序直接支持(如运行mapreduce
job的程序)。
GenericOptionsParser, Tool, and ToolRunner
HADOOP自带了一些工具类,使在命令行运行job更简单。GenericOptionsParser解释了普通的HADOOP命令
行选项,通过Configuration对象按照应用需要来设置它们。你通常不会直接使用GenericOptionsParser,因为实现
Tool接口,通过ToolRunner来运行你的应用更方便,它内部使用了GenericOptionsParser:
public interface Tool extends Configurable {
int run(String [] args) throws Exception;
}
例6-4显示了Tool的一个非常简单的实现,仅仅打印了Tool的Configuration对象的键和值。
//Example 6-4. An example Tool implementation for printing the properties in a Configuration
public class ConfigurationPrinter extends Configured implements Tool {
static {
Configuration.addDefaultResource("hdfs-default.xml");
Configuration.addDefaultResource("hdfs-site.xml");
Configuration.addDefaultResource("yarn-default.xml");
Configuration.addDefaultResource("yarn-site.xml");
Configuration.addDefaultResource("mapred-default.xml");
Configuration.addDefaultResource("mapred-site.xml");
}
@Override
public int run(String[] args) throws Exception {
Configuration conf = getConf();
for (Entry<String, String> entry : conf) {
System.out.printf("%s=%s\n", entry.getKey(), entry.getValue());
}
return 0;
}
public static void main(String[] args) throws Exception {
int exitCode = ToolRunner.run(new ConfigurationPrinter(), args);
System.exit(exitCode);
}
}
我们让ConfigurationPrinter继承了Configured,它是Configurable接口的一个实现。所有的Tool实现
需要实现Configurable(因为Tool继承了它),而继承Configured通常是达到这个目的的最简单的方式。
run()方法使用Configurable的getConf()方法获得Configuration,然后遍历它,把每一个属性打印到标准输出。
静态代码块确保加载了HDFS,YARN,MAPREDUCE的配置,除了core的配置(Configuration已经知道)。
ConfigurationPrinter的方法不直接调用它自己的run()方法,而是调用ToolRunner的静态run()方法,它
会在调用它的run()方法之前为Tool创建一个Configuration对象。ToolRunner也使用一个GenericOptionsParser
来加载定义在命令行的选项并通过Configuration对象来设置它们。我们可以通过运行下面的命令来查看加载
定义在conf/hadoop-localhost.xml的属性的效果:
% mvn compile
% export HADOOP_CLASSPATH=target/classes/
% hadoop ConfigurationPrinter -conf conf/hadoop-localhost.xml \
| grep yarn.resourcemanager.address=
yarn.resourcemanager.address=localhost:8032
GenericOptionsParser也可以让你设置自定义属性。例如:
% hadoop ConfigurationPrinter -D color=yellow | grep color
color=yellow
这里,-D选项用来设置键为color的配置属性的值为yellow。通过-D定义的属性优先于配置文件中的属性。
这非常有用,因为你可以把默认的值写在配置文件中,然后按需用-D来覆盖他们。它的一个常用的例子是设置
MapReduce job的reduce数量,通过-D mapreduce.job.reduces=n来设置。它会覆盖设置在集群或任何客户端
上的配置文件中设置的reduce数量。
其它GenericOptionsParser和ToolRunner支持的选项列举在表6-1中。