编写和运行第一个 Hadoop 程序是学习 Hadoop 的重要步骤。以下是一个经典的"WordCount"程序示例,它统计文本文件中每个单词出现的次数。我们将使用 Java 编写 MapReduce 程序,并在 Hadoop 集群上运行它。
一、WordCount 程序概述
WordCount 是 Hadoop 的"Hello World"程序。它的基本逻辑如下:
- Mapper:读取输入文件,将每一行文本拆分为单词,并输出每个单词及其出现次数(初始为1)。
- Reducer:对每个单词的计数进行汇总,输出最终的单词频率。
二、编写 WordCount 程序
1. 创建 Java 项目
在本地开发环境中(如 IntelliJ IDEA 或 Eclipse)创建一个 Java 项目,并添加 Hadoop 的依赖库。你可以从 Hadoop 安装目录的 share/hadoop/common
和 share/hadoop/mapreduce
中找到 JAR 文件。
2. 编写代码
以下是 WordCount 程序的完整代码:
java
import org.apache.hadoop.conf.Configuration;
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.Mapper;
import org.apache.hadoop.mapreduce.Reducer;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import java.io.IOException;
public class WordCount {
// Mapper 类
public static class TokenizerMapper extends Mapper<Object, Text, Text, IntWritable> {
private final static IntWritable one = new IntWritable(1);
private Text word = new Text();
public void map(Object key, Text value, Context context) throws IOException, InterruptedException {
// 将输入的文本行拆分为单词
String[] words = value.toString().split("\\s+");
for (String w : words) {
word.set(w);
context.write(word, one); // 输出单词及其计数(初始为1)
}
}
}
// Reducer 类
public static class IntSumReducer extends Reducer<Text, IntWritable, Text, IntWritable> {
private IntWritable result = new IntWritable();
public void reduce(Text key, Iterable<IntWritable> values, Context context) throws IOException, InterruptedException {
int sum = 0;
for (IntWritable val : values) {
sum += val.get(); // 汇总每个单词的计数
}
result.set(sum);
context.write(key, result); // 输出最终的单词及其计数
}
}
// 主程序
public static void main(String[] args) throws Exception {
Configuration conf = new Configuration(); // 配置 Hadoop 环境
Job job = Job.getInstance(conf, "word count"); // 创建一个作业
job.setJarByClass(WordCount.class); // 设置作业的主类
job.setMapperClass(TokenizerMapper.class); // 设置 Mapper 类
job.setCombinerClass(IntSumReducer.class); // 设置 Combiner(可选)
job.setReducerClass(IntSumReducer.class); // 设置 Reducer 类
// 设置输出的键值对类型
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(IntWritable.class);
// 设置输入和输出路径
FileInputFormat.addInputPath(job, new Path(args[0]));
FileOutputFormat.setOutputPath(job, new Path(args[1]));
System.exit(job.waitForCompletion(true) ? 0 : 1); // 等待作业完成
}
}
3. 编译和打包
将代码编译并打包为 JAR 文件。假设你的项目名为 WordCount
,可以使用以下命令:
bash
javac -classpath /path/to/hadoop-common.jar:/path/to/hadoop-mapreduce-client-core.jar -d ./out WordCount.java
jar -cvf WordCount.jar -C ./out/ .
三、运行 WordCount 程序
1. 上传输入文件
将一个文本文件上传到 HDFS,作为输入数据。例如:
bash
hdfs dfs -mkdir /input
hdfs dfs -put /path/to/your/input.txt /input
2. 运行程序
使用 Hadoop 命令运行 WordCount 程序:
bash
hadoop jar WordCount.jar WordCount /input /output
WordCount.jar
是打包后的 JAR 文件。WordCount
是主类的名称。/input
是输入目录。/output
是输出目录(运行前确保该目录不存在)。
3. 查看结果
运行完成后,查看输出结果:
bash
hdfs dfs -cat /output/part-r-00000
四、程序解释
-
Mapper:
- 读取输入的文本行,将其拆分为单词。
- 对每个单词输出键值对(单词,1)。
-
Reducer:
- 对每个单词的计数进行汇总。
- 输出最终的单词及其出现次数。
-
Combiner(可选):
- 在 Mapper 节点上对局部结果进行汇总,减少传输到 Reducer 的数据量。
五、常见问题
-
输入文件格式:
- 输入文件应为纯文本文件,每行包含一段文本。
-
输出目录:
- 输出目录不能预先存在,否则 Hadoop 会报错。
-
依赖冲突:
- 确保编译时的 Hadoop JAR 文件版本与集群中的 Hadoop 版本一致。
六、扩展
你可以尝试以下扩展:
- 优化性能:使用 Combiner 和 Partitioner。
- 处理更多文件:将多个文件放入输入目录。
- 自定义数据类型 :使用 Hadoop 提供的其他数据类型(如
LongWritable
)。
通过运行这个简单的 WordCount 程序,你可以熟悉 Hadoop 的 MapReduce 编程模型和运行流程。