Hadoop学习笔记之如何运行一个MapReduce程序
MapReduce可以分为两个阶段来处理,一个阶段为map,另一个阶段为reduce.每个阶段都有键值对的输入和输出参数,输入输出键值对的类型由程序决定,程序同样指定了两个函数,map函数和reduce函数。
在这里,我们使用NCDC数据作为MapReduce例子的测试数据。
下面具体介绍下最简单的MapReduce.
快速索引:
1.编写Mapper程序
2.编写Reducer程序
3.编写Job Object
4.运行Job
1.编写Mapper程序
- package com.rickywag.hadoop;
- import org.apache.hadoop.io.IntWritable;
- import org.apache.hadoop.io.LongWritable;
- import org.apache.hadoop.io.Text;
- import org.apache.hadoop.mapreduce.Mapper;
- import java.io.IOException;
- /**
- * Mapper程序主要用于处理数据,过滤不需要的数据,把需要的程序输出到reduce程序。
- * @author Ricky
- * @since 2014/08/25
- */
- public class MaxTemperatureMapper
- extends Mapper<LongWritable, Text, Text, IntWritable> {
- private static final int MISSING = 9999;
- /**
- * map函数主要用于解析NCDC文件中每一行的年份和气温
- * @param key NCDC文件中信息行数的偏移量
- * @param value NCDC文件对应行数偏移量的信息
- * @param context 输出变量
- * @throws IOException IO异常
- * @throws InterruptedException 线程中断异常
- */
- @Override
- public void map(LongWritable key, Text value, Context context)
- throws IOException, InterruptedException {
- String line = value.toString(); //获取每一行NCDC信息
- String year = line.substring(15, 19);//获取每一行的年份
- //获取每一行的温度
- int airTemperature;
- if (line.charAt(87) == '+') { //解析int字符串不需要‘+’
- airTemperature = Integer.parseInt(line.substring(88, 92));
- } else {
- airTemperature = Integer.parseInt(line.substring(87, 92));
- }
- String quality = line.substring(92, 93);
- if (airTemperature != MISSING && quality.matches("[01459]")) {
- //输出年份,温度到reduce阶段,在输出到reduce之前,mapper会对数据进行排序和分组。
- context.write(new Text(year), new IntWritable(airTemperature));
- }
- }
- }
2.编写Reducer程序
- package com.rickywag.hadoop;
- import org.apache.hadoop.io.IntWritable;
- import org.apache.hadoop.io.Text;
- import org.apache.hadoop.mapreduce.Reducer;
- import java.io.IOException;
- /**
- * 找出一年中最高的温度
- * @author Ricky
- * @since 2014/08/25
- */
- public class MaxTemperatureReducer
- extends Reducer<Text, IntWritable, Text, IntWritable> {
- /**
- * 通过map处理过的数据,找出一年中的最高温度
- * @param key 年份
- * @param values 该年份的所有温度
- * @param context 把结果输出到下一个阶段
- * @throws IOException IO异常
- * @throws InterruptedException 线程中断异常
- */
- @Override
- public void reduce(Text key, Iterable<IntWritable> values,
- Context context)
- throws IOException, InterruptedException {
- int maxValue = Integer.MIN_VALUE;//int 类型数据的最小值
- //找出一年中的最高气温
- for (IntWritable value : values) {
- maxValue = Math.max(maxValue, value.get());
- }
- //把最高温度的年份作为key,最高的温度作为值,输出到文件中。
- context.write(key, new IntWritable(maxValue));
- }
- }
3.编写Job Object
- package com.rickywag.hadoop;
- import org.apache.hadoop.fs.Path;
- import org.apache.hadoop.io.IntWritable;
- import org.apache.hadoop.io.Text;
- import org.apache.hadoop.mapreduce.Job;
- import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
- import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
- /**
- * 控制求最大温度作业的执行
- * @author Ricky
- * @since 2014/08/25
- */
- public class MaxTemperature {
- public static void main(String[] args) throws Exception {
- /**
- * input path : NCDC数据文件在HDFS中的位置
- * output path: MapReduce处理完成后结果存放的位置(默认该路径在HDFS中不存在,若已经存在会抛异常)
- */
- if (args.length != 2) {
- System.err.println("Usage: MaxTemperature <input path> <output path>");
- System.exit(-1);
- }
- Job job = new Job();
- job.setJarByClass(MaxTemperature.class);//job会查询包涵该类的包。
- job.setJobName("Max temperature");//设置作业名称
- FileInputFormat.addInputPath(job, new Path(args[0]));//设置NCDC文件在HDFS存在的位置
- FileOutputFormat.setOutputPath(job, new Path(args[1]));//设置job运行结果存放的位置
- job.setMapperClass(MaxTemperatureMapper.class);//设置map阶段执行的代码
- job.setReducerClass(MaxTemperatureReducer.class);//设置reduce阶段执行的代码
- job.setOutputKeyClass(Text.class);//设置输出key
- job.setOutputValueClass(IntWritable.class);//设置输出的值
- System.exit(job.waitForCompletion(true) ? 0 : 1);//若作业正常执行完成,系统正常退出。
- }
- }
4.运行Job
在运行MapReduce程序之前,需要把MapReduce程序打包成一个可以执行的Jar包。
因为我用的是Maven管理,需要用插件才可以打包成可执行的Jar包。
- <?xml version="1.0" encoding="UTF-8"?>
- <project xmlns="http://maven.apache.org/POM/4.0.0"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
- <modelVersion>4.0.0</modelVersion>
- <groupId>com.rickywag.hadoop</groupId>
- <artifactId>hadoop-simpleMapperReducer</artifactId>
- <version>1.0-SNAPSHOT</version>
- <!-- All developers contact information-->
- <developers>
- <developer>
- <id>Ricky</id>
- <name>Ricky Lau</name>
- <email>Ricky_LS@163.com</email>
- <organization>com.rickywag</organization>
- <roles>
- <role>Project Creater</role>
- </roles>
- <timezone>+8</timezone>
- </developer>
- </developers>
- <!-- Project Licenses-->
- <licenses>
- <license>
- <name>Apache License, Version 2.0</name>
- <url>http://www.apache.org/licenses/LICENSE-2.0</url>
- </license>
- </licenses>
- <!-- Manage all plugin version-->
- <properties>
- <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
- <java-version>1.7</java-version>
- <slf4j.version>1.7.5</slf4j.version>
- <logback.version>1.0.13</logback.version>
- <org.apache.commons.lang.version>3.1</org.apache.commons.lang.version>
- <junit.version>4.11</junit.version>
- </properties>
- <!-- Manager all third party dependency - -->
- <dependencies>
- <!-- Log dependency -->
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-api</artifactId>
- <version>${slf4j.version}</version>
- </dependency>
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>jcl-over-slf4j</artifactId>
- <version>${slf4j.version}</version>
- </dependency>
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>jul-to-slf4j</artifactId>
- <version>${slf4j.version}</version>
- </dependency>
- <dependency>
- <groupId>ch.qos.logback</groupId>
- <artifactId>logback-classic</artifactId>
- <version>${logback.version}</version>
- </dependency>
- <dependency>
- <groupId>ch.qos.logback</groupId>
- <artifactId>logback-core</artifactId>
- <version>${logback.version}</version>
- </dependency>
- <!-- Junit -->
- <dependency>
- <groupId>junit</groupId>
- <artifactId>junit</artifactId>
- <version>${junit.version}</version>
- </dependency>
- <!-- org.apache.commons -->
- <dependency>
- <groupId>org.apache.commons</groupId>
- <artifactId>commons-lang3</artifactId>
- <version>${org.apache.commons.lang.version}</version>
- </dependency>
- <!-- hadoop 2.5.0 -->
- <dependency>
- <groupId>org.apache.hadoop</groupId>
- <artifactId>hadoop-common</artifactId>
- <version>2.5.0</version>
- </dependency>
- <dependency>
- <groupId>org.apache.hadoop</groupId>
- <artifactId>hadoop-mapreduce-client-core</artifactId>
- <version>2.5.0</version>
- </dependency>
- </dependencies>
- <build>
- <finalName>hadoop-simpleMapperReducer</finalName>
- <plugins>
- <!-- compiler plugin-->
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-compiler-plugin</artifactId>
- <version>3.1</version>
- <configuration>
- <source>${java-version}</source>
- <target>${java-version}</target>
- <encoding>${project.build.sourceEncoding}</encoding>
- <compilerArgument>-Xlint:all</compilerArgument>
- <showWarnings>true</showWarnings>
- <showDeprecation>true</showDeprecation>
- </configuration>
- </plugin>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-shade-plugin</artifactId>
- <version>2.3</version>
- <executions>
- <execution>
- <phase>package</phase>
- <goals>
- <goal>shade</goal>
- </goals>
- <configuration>
- <transformers>
- <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
- <mainClass>com.rickywag.hadoop.MaxTemperature</mainClass>
- </transformer>
- </transformers>
- </configuration>
- </execution>
- </executions>
- </plugin>
- </plugins>
- </build>
- </project>
由于maven中央库中没有hadoop 2.5.0的相关jar包,当前的2.5.0是我通过私服自己上传的。默认你们没有私服,但如果你们想用我的程序直接打包,需要把两个hadoop 2.5.0的jar包安装到本地仓库中去。这两个jar包分别位于%HADOOP_INSTALL%/share/hadoop/common/hadoop-common-2.5.0.jar 和 %HADOOP_INSTALL%/share/hadoop/mapreduce/hadoop-mapreduce-client-core-2.5.0.jar
用下面两条命令把两个jar包导入maven本地仓库,把JARPOSITION替换为jar包存放的路径
1.mvn install:install-file -Dfile=JARPOSITION/hadoop-common-2.5.0.jar -DgroupId=org.apache.hadoop -DartifactId=hadoop-common -Dversion=2.5.0 -Dpackaging=jar
2.mvn install:install-file -Dfile=JARPOSITION/hadoop-mapreduce-client-core-2.5.0.jar -DgroupId=org.apache.hadoop -DartifactId=hadoop-mapreduce-client-core -Dversion=2.5.0 -Dpackaging=jar
编译打包MapReduce程序
执行 mvn clean package,获取target下的已经打包好的MapReduce程序,hadoop-simpleMapperReducer.jar。
3.上传测试数据文件与打包的MapReduce程序至linux 根目录下
测试数据在百度云盘上,若无法使用请发邮件通知。 下载NCDC测试数据
,默认该文件已经下载到linux的根目录下,文件名为1901.
把已经打包好的程序也上传到linux的根性目录下。
4.上传测试数据文件到hdfs上。
cd %HADOOP_INSTALL%
bin/hdfs dfs -put /1901 /
export HADOOP_CLASSPATH=/hadoop-simpleMapperReducer.jar
hadoop com.rickywag.hadoop.MaxTemperature /1901 output
执行完成后,若没有报错,查询1901年的最高温度输出文件
bin/hdfs dfs -cat /user/root/output/part-r-00000 结果如下:
1901 317
到这里,最简单的MapReduce就运行成功了。在写的过程中,由于自己也是现学现卖,我学习的时候hadoop用的版本比较高会导致,有一些老API和新API同时使用的情况,等我了解在多一点的时候,我会抛弃旧的API完全使用新的API.