在这个教程中,我们将构建一个简单的 Hadoop MapReduce 程序,以计算一组数字的平均值。我们将详细讲解每个组件,强调关键概念和易错点,以帮助你更好地理解。
1. 项目结构
我们的项目包括以下三个类:
AvgMapper
: 负责读取输入数据,输出每个数字及其计数。AvgReducer
: 负责计算总和和计数,最终输出平均值。AvgDriver
: 负责设置和启动 MapReduce 作业。
2. AvgMapper 类
Mapper 的主要任务是将每个输入数字映射为 key-value
对。
package com.yao.mapreduce.demo6;
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;
public class AvgMapper extends Mapper<LongWritable, Text, IntWritable, IntWritable> {
# 初始化计数1
private final static IntWritable v = new IntWritable(1);
@Override
protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
# 类型转换
int number = Integer.parseInt(value.toString());
# 当扫描过的数值标注1
context.write(new IntWritable(number), v);
}
}
重点和易错点:
- 输入格式:确保输入数据每行都是一个整数。如果输入数据格式不正确,将导致
NumberFormatException
。 - 输出类型:
context.write(new IntWritable(number), v);
这里我们输出key
为数字,value
固定为1
。确认 Mapper 输出类型与 Driver 设置一致。
3. AvgReducer 类
Reducer 负责对所有输入的相同键的值进行汇总,计算总和和计数。
package com.yao.mapreduce.demo6;
import org.apache.hadoop.io.DoubleWritable;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;
import java.io.IOException;
public class AvgReducer extends Reducer<IntWritable, IntWritable, Text, DoubleWritable> {
private DoubleWritable result = new DoubleWritable();
private int totalSum = 0;
private int totalCount = 0;
@Override
protected void reduce(IntWritable key, Iterable<IntWritable> values, Context context) throws IOException, InterruptedException {
int count = 0;
# 循环处理key相同的value
for (IntWritable value : values) {
# 出现次数进行相加
count += value.get();
}
# 计算出总和
totalSum += key.get() * count;
totalCount += count;
}
@Override
protected void cleanup(Context context) throws IOException, InterruptedException {
# 判断除数为零
if (totalCount != 0) {
double avg = (double) totalSum / totalCount;
result.set(avg);
# 将结果写出
context.write(new Text("整体平均值"), result);
}
}
}
重点和易错点:
- 累加逻辑:
totalSum += key.get() * count;
和totalCount += count;
确保你正确累加当前数字的总和及其出现次数。 - 浮点数除法:在计算平均值时,务必使用
double
类型的除法,防止整数除法造成的精度损失。 - cleanup 方法:
cleanup
方法在所有 reduce 完成后被调用,确保在此方法中输出最终结果。 - 在reduce阶段避免将相同的key只处理第一个
- 在reduce的最后将数值进行总体的求和然后计算平均
- 注意除数的类型转换
4. AvgDriver 类
Driver 设置 MapReduce 作业的配置和运行。
package com.yao.mapreduce.demo6;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.DoubleWritable;
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;
import java.io.IOException;
import java.util.Random;
public class AvgDriver {
public static void main(String[] args) throws IOException, InterruptedException, ClassNotFoundException {
# 为了便于测试,使用随机数命名文件,避免重复
Random random = new Random();
int randomInt = random.nextInt();
Configuration conf = new Configuration();
Job job = Job.getInstance(conf);
job.setJarByClass(AvgDriver.class);
job.setMapperClass(AvgMapper.class);
job.setReducerClass(AvgReducer.class);
#定义map阶段输出的key的类型
job.setMapOutputKeyClass(IntWritable.class);
#定义map阶段输出的value的类型
job.setMapOutputValueClass(IntWritable.class);
#定义reduce最后输出key的类型
job.setOutputKeyClass(Text.class);
#定义reduce最后输出value的类型
job.setOutputValueClass(DoubleWritable.class);
FileInputFormat.setInputPaths(job, new Path(args[0]));
FileOutputFormat.setOutputPath(job, new Path(args[1]));
boolean result = job.waitForCompletion(true);
System.exit(result ? 0 : 1);
}
}
重点和易错点:
- 配置类:
Configuration conf = new Configuration();
确保 Hadoop 配置正确,避免因环境问题导致的错误。 - 输入输出路径:确保路径有效,输出路径必须不存在,否则会导致作业失败。可以在运行前手动删除旧的输出目录。
- 类路径设置:
job.setJarByClass(AvgDriver.class);
确保设置 JAR 包的路径。
5. 运行程序
-
编译打包:确保所有类都已编译并打包成 JAR 文件,包含所有依赖。
-
运行作业:在 Hadoop 集群上使用以下命令运行作业:
hadoop jar your-jar-file.jar com.yao.mapreduce.demo6.AvgDriver /input/path /output/path
重点和易错点:
- 命令行参数:确保传入的输入路径和输出路径正确。如果路径错误或文件不存在,作业将无法执行。
- 查看作业状态:可以通过 Hadoop 的 Web UI 查看作业执行状态和日志,有助于排查问题。
示例输入
1
1
2
2
1
9
9
9
9
9
9
9
9
9
9
9
9
999
999999999
示例输出
6. 总结
在这个教程中,我们详细介绍了如何使用 Hadoop MapReduce 计算一组数字的平均值。通过逐步分析代码,我们突出了关键逻辑和易错点,帮助你避免常见的错误。希望这能帮助你在实践中更好地应用 Hadoop 技术!