介绍
在本实验中,你将学习如何为著名的魔法学院(Magical Academy)庞大的魔法典籍收藏优化编目流程。通过 Hadoop MapReduce 的强大功能,你将处理书籍数据的序列化,确保无缝的处理和分析。这将使你能够高效地存储和处理书籍信息,最终更好地服务于学院的教职员工和学生。
在本实验中,你将学习如何为著名的魔法学院(Magical Academy)庞大的魔法典籍收藏优化编目流程。通过 Hadoop MapReduce 的强大功能,你将处理书籍数据的序列化,确保无缝的处理和分析。这将使你能够高效地存储和处理书籍信息,最终更好地服务于学院的教职员工和学生。
在这一步中,我们将创建一个自定义的 Writable 类来表示书籍数据。该类将实现 Apache Hadoop 提供的 Writable
接口,以便在 MapReduce 过程中高效地进行数据的序列化和反序列化。
首先,你需要使用 su - hadoop
命令切换到 hadoop 用户,并在 /home/hadoop
目录下创建一个名为 Book.java
的 Java 文件,内容如下:
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import org.apache.hadoop.io.Writable;
// Book 类表示一本书,包含标题、作者和出版年份。
// 它实现了 Writable 接口以支持 Hadoop 的序列化。
public class Book implements Writable {
// 书籍的标题、作者和出版年份的私有字段。
private String title;
private String author;
private int year;
// 默认构造函数 - 用于反序列化时创建新实例。
public Book() {}
// 带参数的构造函数,用于使用给定值初始化字段。
public Book(String title, String author, int year) {
this.title = title;
this.author = author;
this.year = year;
}
// write 方法将对象的字段序列化到 DataOutput。
public void write(DataOutput out) throws IOException {
out.writeUTF(title); // 将标题以 UTF-8 格式写入
out.writeUTF(author); // 将作者以 UTF-8 格式写入
out.writeInt(year); // 将出版年份以整数格式写入
}
// readFields 方法从 DataInput 反序列化对象的字段。
public void readFields(DataInput in) throws IOException {
title = in.readUTF(); // 以 UTF-8 格式读取标题
author = in.readUTF(); // 以 UTF-8 格式读取作者
year = in.readInt(); // 以整数格式读取出版年份
}
// toString 方法提供对象的字符串表示形式,
// 这对于打印和日志记录非常有用。
@Override
public String toString() {
return "Title: " + title + ", Author: " + author + ", Year: " + year;
}
// 为了简洁起见,省略了 getter 和 setter 方法,但它们是访问字段所必需的。
}
这个 Book
类包含书籍的标题、作者和出版年份字段。write
方法将书籍数据序列化为字节流,而 readFields
方法从字节流中反序列化数据。这两个方法都是 Writable
接口所必需的。
接下来,你需要使用以下命令编译 Java 类:
## 编译 Java 类
javac -source 8 -target 8 -classpath $HADOOP_HOME/share/hadoop/common/hadoop-common-3.3.6.jar:$HADOOP_HOME/share/hadoop/mapreduce/hadoop-mapreduce-client-core-3.3.6.jar:. Book.java
在这一步中,我们将创建一个 Mapper 和一个 Reducer 类,使用 MapReduce 范式处理书籍数据。
首先,在 /home/hadoop
目录下创建一个名为 BookMapper.java
的 Java 文件,内容如下:
import java.io.IOException;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;
// BookMapper 继承 Mapper 类以处理文本输入文件
// 输入的键值对为 LongWritable(行号)和 Text(行内容)
// 输出的键值对为 Text(作者名)和 Book(书籍详情)
public class BookMapper extends Mapper<LongWritable, Text, Text, Book> {
@Override
protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
// 按逗号分割输入行
String[] bookData = value.toString().split(",");
// 从输入行中提取标题、作者和年份
String title = bookData[0];
String author = bookData[1];
int year = Integer.parseInt(bookData[2]);
// 将作者和书籍详情写入上下文
context.write(new Text(author), new Book(title, author, year));
}
}
这个 BookMapper
类接收格式为 "title,author,year"
的输入数据行,并输出以作者为键、Book
对象为值的键值对。
接下来,在 /home/hadoop
目录下创建一个名为 BookReducer.java
的 Java 文件,内容如下:
import java.io.IOException;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;
// BookReducer 继承 Reducer 类以按作者聚合书籍详情
// 输入的键值对为 Text(作者名)和 Book(书籍详情)
// 输出的键值对为 Text(作者名)和 Book(聚合后的书籍详情)
public class BookReducer extends Reducer<Text, Book, Text, Book> {
@Override
protected void reduce(Text key, Iterable<Book> values, Context context) throws IOException, InterruptedException {
// 遍历同一作者的所有书籍,并将每本书写入上下文
for (Book book : values) {
context.write(key, book);
}
}
}
这个 BookReducer
类简单地将输入的键值对原样输出,实际上按作者对书籍进行了分组。
最后,你需要使用以下命令编译 Java 类:
## 编译 Java 类
javac -source 8 -target 8 -classpath $HADOOP_HOME/share/hadoop/common/hadoop-common-3.3.6.jar:$HADOOP_HOME/share/hadoop/mapreduce/hadoop-mapreduce-client-core-3.3.6.jar:. BookMapper.java BookReducer.java
在这一步中,我们将创建一个 Driver
类来运行 MapReduce 任务并处理书籍数据。
首先,在 /home/hadoop
目录下创建一个名为 BookDriver.java
的 Java 文件,内容如下:
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
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;
// BookDriver 用于设置并提交 MapReduce 任务
public class BookDriver {
public static void main(String[] args) throws Exception {
// 创建一个新的 Hadoop 任务配置
Configuration conf = new Configuration();
// 使用任务配置实例化一个 Job 对象
Job job = Job.getInstance(conf, "Book Processing");
// 通过类指定任务的 jar 文件
job.setJarByClass(BookDriver.class);
// 设置 Mapper 类
job.setMapperClass(BookMapper.class);
// 设置 Reducer 类
job.setReducerClass(BookReducer.class);
// 设置任务的输出键类
job.setOutputKeyClass(Text.class);
// 设置任务的输出值类
job.setOutputValueClass(Book.class);
// 设置任务的输入路径
FileInputFormat.addInputPath(job, new Path(args[0]));
// 设置任务的输出路径
FileOutputFormat.setOutputPath(job, new Path(args[1]));
// 根据任务完成状态退出
System.exit(job.waitForCompletion(true) ? 0 : 1);
}
}
这个 BookDriver
类用于设置并运行 MapReduce 任务。它使用 BookMapper
和 BookReducer
类配置任务,设置输入和输出路径,并等待任务完成。
要运行 MapReduce 任务,你需要编译 Java 类并创建一个 JAR 文件。你可以使用以下命令:
## 编译 Java 类
javac -source 8 -target 8 -classpath $HADOOP_HOME/share/hadoop/common/hadoop-common-3.3.6.jar:$HADOOP_HOME/share/hadoop/mapreduce/hadoop-mapreduce-client-core-3.3.6.jar:. BookDriver.java
## 创建 JAR 文件
jar -cvf book.jar *.class
接下来,你需要创建一个输入目录并将一些示例书籍数据复制到其中。你可以使用以下命令:
## 创建输入目录
hdfs dfs -mkdir /input
## 将示例数据复制到输入目录
hdfs dfs -put ./data.txt /input
最后,你可以使用以下命令运行 MapReduce 任务:
hadoop jar book.jar BookDriver /input /output
该命令将运行 BookDriver
类,使用 /input
中的输入数据,并将结果输出到 /output
。你可以使用以下命令查看结果:
hdfs dfs -cat /output/part-r-00000
恭喜!你已经成功掌握了 Hadoop MapReduce 中的序列化技术,并完成了自定义 Writable 类的创建。通过魔法学院(Magical Academy)的图书管理员管理大量书籍的场景,你创建了一个实现 Writable
接口的 Book
类,以实现数据的无缝序列化和反序列化。你还编写了 BookMapper
类来提取书籍信息,以及 BookReducer
类来按作者高效地分组书籍,并通过 BookDriver
类协调整个流程。这包括编译 Java 类、创建 JAR 文件以及在 Hadoop 集群上执行任务等操作。在整个过程中,你积累了宝贵的 Hadoop MapReduce 经验,提升了在自定义 Writable 类、Mapper 和 Reducer 类以及协调大规模数据处理任务的 MapReduce 任务方面的技能。