1.MapReduce基本知识的总结
MapReduc的相关知识可以简单分为以下几点:
1.1拆解数据(即Map):
将大规模数据分割成小块。
每个小块都有自己的任务。
这些任务是简单的处理,比如筛选、排序或计数。
1.2处理过程:
各个小块同时进行处理,互不干扰。
并行处理使得整体速度更快。
每个小块处理完后生成中间结果。
1.3组合结果(Reduce):
将各个小块的中间结果收集起来。
将这些结果按照某种方式组合起来。
最终得到一个完整的处理结果。
2.MapReduce的操作实践
2.1基于MapReduce的WordCount实践
0.首先,本文默认环境为Linux的Ubuntu操作环境且已经安装、配置好了hadooop、Maven、idea、jdk等前置配置。
1.编写对应代码,复制导入core-site.xml与hdfs-site.xml两文件(在安装的etc/hadoop路径下):
2.pom文件的配置:
如果你的idea,Maven配置是正常的那么在创建项目时构建系统选择Maven后文件夹会自动生成pom文件,但其中配置需要自己调整:
pom代码实现:
(注意,版本号是hadoop对应版本号,我的是2.10.0)
<?xml version="1.0" encoding="UTF-8"?>
<!-- Maven项目描述文件 -->
<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>org.example</groupId>
<!-- 项目唯一标识 -->
<artifactId>Main</artifactId>
<!-- 项目版本 -->
<version>1.0-SNAPSHOT</version>
<!-- 依赖项 -->
<dependencies>
<!-- Hadoop通用模块 -->
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-common</artifactId>
<version>2.10.0</version>
</dependency>
<!-- Hadoop分布式文件系统模块 -->
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-hdfs</artifactId>
<version>2.10.0</version>
</dependency>
<!-- Hadoop MapReduce核心模块 -->
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-mapreduce-client-core</artifactId>
<version>2.10.0</version>
</dependency>
<!-- Hadoop MapReduce作业客户端模块 -->
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-mapreduce-client-jobclient</artifactId>
<version>2.10.0</version>
</dependency>
<!-- Hadoop MapReduce通用模块 -->
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-mapreduce-client-common</artifactId>
<version>2.10.0</version>
</dependency>
</dependencies>
</project>
3.*Program arguments配置:
(某菜鸟因为没整这个卡了半小时)
如果你卡在一个莫名其妙的数组溢出的bug处(arg[]数组溢出),那么这就是溢出的原因:
打开中文的编辑配置
出现自己文件名的配置处
配置蓝框内内容
注意:对应的input与output需要自己提前创建,我创建在/user/ws/input output下,所以在hdfs://localhost:9000/user/ws/input。
然后就可以开始创建了,不过在点击构建前,请把那个该死的HDFS使用start-dfs.sh代码打开
创建成功会返回0哦:
输入cat查看单词计数成果:
代码实现
package com.hadoop;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.*;
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;
import java.util.StringTokenizer;
public class WordCount {
public static class MyMapper 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 {
StringTokenizer itr = new StringTokenizer(value.toString());
while (itr.hasMoreTokens()) {
word.set(itr.nextToken());
context.write(word, one);
}
}
}
public static class MyReducer 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();
Job job = Job.getInstance(conf, "word count");
job.setJarByClass(WordCount.class);
job.setMapperClass(MyMapper.class);
job.setReducerClass(MyReducer.class);
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(IntWritable.class);
// 检查输入路径是否存在
Path inputPath = new Path(args[0]);
FileSystem fs = inputPath.getFileSystem(conf);
if (!fs.exists(inputPath)) {
System.err.println("Input path does not exist: " + args[0]);
System.exit(1);
}
FileInputFormat.addInputPath(job, inputPath);
FileOutputFormat.setOutputPath(job, new Path(args[1]));
// 删除输出目录
Path outputPath = new Path(args[1]);
fs.delete(outputPath, true);
boolean result = job.waitForCompletion(true);
System.exit(result ? 0 : 1);
}
}
2.2 自定义Hadoop数据类型实践
如上图,修改主要如下:
Mapper类的实现:
在修改后版本中,Mapper类中的map方法将每个单词作为键,其长度作为值写入上下文。
在原版本中,Mapper类中的map方法将每个单词作为键,将固定值1作为值写入上下文。这种方法通常用于计数操作,即将每个单词视为一个计数单位。
Reducer类的实现:
在两个版本中,Reducer类都计算了单词出现的总次数,并将结果写入输出上下文。
在修改后版本中的Reducer还计算了单词的平均长度,并将其作为整数写入输出上下文中。
在原版本中Reducer只计算了单词出现的总次数,并将其作为整数写入输出上下文中。
main方法的实现:
在修改后版本中的main方法中,设置了输出值类型为Text,并且在输出结果时采用了Text类型。
在原版本的main方法中,设置了输出值类型为IntWritable,与Reducer中的输出值类型匹配。
其余配置(core-site.xml与hdfs-site.xml以及pom文件如2.1所示)
代码实现
package com.hadoop;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.*;
import org.apache.hadoop.io.*;
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;
import java.util.StringTokenizer;
public class WordCount {
public static class MyMapper extends Mapper<Object, Text, Text, IntWritable> {
private Text word = new Text();
private IntWritable length = new IntWritable();
public void map(Object key, Text value, Context context) throws IOException, InterruptedException {
StringTokenizer itr = new StringTokenizer(value.toString());
while (itr.hasMoreTokens()) {
String token = itr.nextToken();
word.set(token);
length.set(token.length());
context.write(word, length); // 将单词和其长度作为输出
}
}
}
public static class MyReducer 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;
int count = 0;
for (IntWritable val : values) {
sum += val.get();
count++;
}
int averageLength = sum / count; // 将平均长度转换为整数
result.set(averageLength);
context.write(key, result);
}
}
public static void main(String[] args) throws Exception {
Configuration conf = new Configuration();
Job job = Job.getInstance(conf, "word count");
job.setJarByClass(WordCount.class);
job.setMapperClass(MyMapper.class);
job.setReducerClass(MyReducer.class);
job.setOutputKeyClass(Text.class);
//job.setOutputValueClass(Text.class); // 输出值类型改为 Text
job.setOutputValueClass(IntWritable.class);
// 检查输入路径是否存在
Path inputPath = new Path(args[0]);
FileSystem fs = inputPath.getFileSystem(conf);
if (!fs.exists(inputPath)) {
System.err.println("Input path does not exist: " + args[0]);
System.exit(1);
}
FileInputFormat.addInputPath(job, inputPath);
FileOutputFormat.setOutputPath(job, new Path(args[1]));
// 删除输出目录
Path outputPath = new Path(args[1]);
fs.delete(outputPath, true);
boolean result = job.waitForCompletion(true);
System.exit(result ? 0 : 1);
}
}
2.3 多mapReduce任务的串联实践
(免责声明:这里本菜鸟有点不会,不能确保是对的哦,如果这里或者上面有什么问题,欢迎佬指出)
注意:环境配置如2.1所示。
实现方法:
Mapper阶段: Mapper将每个单词作为键,并将固定值1作为值写入上下文。
Combiner阶段(可选): Combiner是一个在Mapper和Reducer之间执行的本地聚合操作,它可以减少数据传输量。在这种情况下,Combiner可以简单地对相同的单词进行局部聚合,以减少网络传输。但在这个任务中,Combiner是可选的,因为单词的数量不影响最终结果。
Reducer阶段: Reducer对相同的单词进行聚合,并计算出单词出现总次数,而不是单词出现的次数。
设置Job: 设置MapReduce作业,并指定Mapper、Reducer和Combiner。
运行Job: 运行MapReduce作业,并查看输出结果,即所有单词的总数。
成果如下:
代码实现:
package com.hadoop;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.*;
import org.apache.hadoop.io.*;
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;
import java.util.StringTokenizer;
public class WordCount {
public static class MyMapper 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 {
StringTokenizer itr = new StringTokenizer(value.toString());
while (itr.hasMoreTokens()) {
word.set(itr.nextToken());
context.write(word, one);
}
}
}
public static class MyReducer 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(new Text("Total Words"), result); // 输出总单词数
}
}
public static void main(String[] args) throws Exception {
Configuration conf = new Configuration();
Job job = Job.getInstance(conf, "word count");
job.setJarByClass(WordCount.class);
job.setMapperClass(MyMapper.class);
job.setCombinerClass(MyReducer.class); // 可选,使用Reducer作为Combiner
job.setReducerClass(MyReducer.class);
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(IntWritable.class);
FileInputFormat.addInputPath(job, new Path(args[0]));
FileOutputFormat.setOutputPath(job, new Path(args[1]));
FileSystem fs = FileSystem.get(conf);
fs.delete(new Path(args[1]), true);
boolean result = job.waitForCompletion(true);
System.exit(result ? 0 : 1);
}
}