【实验项目】项目1、项目2、项目3必做;项目4,及后续项目选做
【注】选做项目内容调整说明:
• 原“手机销售统计项目”,我做过之后感觉不太符合大数据分布式计算思维,所以舍弃了,大家吃透项目5和理论课中对项目2、3的讲解就基本达标了;
• 另一个销售案例,限于课时(其实是被假期和xx周冲掉),将放在第三次作业中供大家练习;
• 好友推荐案例,根据当前大家能力和水平,决定放在明年下一届同学再使用,因为后续这门课会改为
48+16,本身实验课就是用来交流技术、讨论问题和验收试验的,手把手的教仅对大一大二同学管用。
4.实验内容【大致步骤】
建议直接跟着重建,节省很多麻烦,配合我这篇一起
【参考链接】(梁老师博客)
https://blog.csdn.net/qq_42881421/article/details/83353640
参考注意
• 配置eclipse的Maven(提前下载,需要时间,注意镜像配置和网络)
• 编写代码和解释代码
package com.MyWordCount;
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.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
public class WordCountMain {
public static void main(String[] args) throws Exception {
//1.创建一个job和任务入口
Job job = Job.getInstance(new Configuration());
job.setJarByClass(WordCountMain.class); //main方法所在的class
//2.指定job的mapper和输出的类型
job.setMapperClass(WordCountMapper.class);//指定Mapper类
job.setMapOutputKeyClass(Text.class); //k2的类型
job.setMapOutputValueClass(IntWritable.class); //v2的类型
//3.指定job的reducer和输出的类型
job.setReducerClass(WordCountReducer.class);//指定Reducer类
job.setOutputKeyClass(Text.class); //k4的类型
job.setOutputValueClass(IntWritable.class); //v4的类型
//4.指定job的输入和输出
FileInputFormat.setInputPaths(job, new Path(args[0]));
FileOutputFormat.setOutputPath(job, new Path(args[1]));
//5.执行job
job.waitForCompletion(true);
}
}
package com.MyWordCount;
import java.io.IOException;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;
// 泛型 k1 v1 k2 v2
public class WordCountMapper extends Mapper<LongWritable, Text, Text, IntWritable>{
@Override
protected void map(LongWritable key1, Text value1, Context context)
throws IOException, InterruptedException {
//数据: I like MapReduce
String data = value1.toString();
//分词:按空格来分词
String[] words = data.split(" ");
//输出 k2 v2
for(String w:words){
context.write(new Text(w), new IntWritable(1));
}
}
}
map()方法接收三个参数:
throws IOException, InterruptedException这个的作用
在Java中,throws关键字用于声明一个方法可能抛出的异常,以便调用该方法的代码可以捕获并处理这些异常。在Hadoop MapReduce框架中,Mapper类的map()方法可能会抛出IOException和InterruptedException两种异常,因此在方法声明中需要使用throws关键字来声明这些异常。
package com.MyWordCount;
import java.io.IOException;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;
// k3 v3 k4 v4
public class WordCountReducer extends Reducer<Text, IntWritable, Text, IntWritable> {
@Override
protected void reduce(Text k3, Iterable<IntWritable> v3,Context context) throws IOException, InterruptedException {
//对v3求和
int total = 0;
for(IntWritable v:v3){
total += v.get();
}
//输出 k4 单词 v4 频率
context.write(k3, new IntWritable(total));
}
}
• Maven命令打包(jar包)
• 上传编译成功的jar包到Linux中
• 在Linux中打开hadoop服务,上传原始数据到HDFS(注意路径)
• 在Linux中执行MapReduce任务,并查看输出结果
【注】本项目先于理论/项目4讲解,且比较简单,所以请完成实验后认真思考每一行代码背后涉及的相关知识。
context.write的作用
MapReduce程序
Reducer程序
Reducer是MapReduce中的一个组件,用于对Mapper的输出结果进行进一步的处理。Reducer的作用是将具有相同键的键值对组合在一起,将它们的值合并为一个结果,最终输出一个键值对。Reducer的处理过程包括三个主要阶段:shuffle、排序和reduce。
Shuffle
在MapReduce程序中,Map任务的输出结果会被传递给Reducer进行进一步处理。在此之前,输出结果需要经过Shuffle操作。Shuffle的主要任务是将Mapper的输出结果根据键进行划分,并将相同键的结果发送给同一个Reducer。Shuffle过程包括以下几个步骤:
排序
在Reducer节点上,框架会对所有键值对按照键进行排序。排序的目的是将相同键的键值对放在一起,方便Reducer进行合并操作。
Reduce
Reducer的reduce()方法会接收所有相同键的键值对组成的迭代器,将它们的值进行合并,最终输出一个键值对。reduce()方法的参数包括:
在这个过程中,Reducer将相同键的所有值进行合并,得到键的总计数,最后将键和总计数组成一个键值对,输出到上下文对象中。Reducer的输出结果将被传递给MapReduce框架进行下一步处理,例如输出到HDFS或者传递给下一个Reducer。
WordCountMain.java程序
4.实验内容【大致步骤】
【思考题2】当前数据天然有空格(“ ”),自然就把计算元素分开了;那若没有类似的分隔符,我们应该怎么做呢?
如果没有类似分隔符,可以使用正则表达式来进行分词。
在Hadoop中,可以使用各种自定义分隔符或正则表达式来指定自定义分词规则。
可以在Mapper中使用Java的正则表达式库,对value值进行字符串拆分。
private final Pattern pattern = Pattern.compile(","); // 逗号分隔符正则表达式
@Override
protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
String line = value.toString();
String[] fields = pattern.split(line); // 使用正则表达式分割字符串
// 对分割后的每个字段进行处理
for (String field : fields) {
// 处理逻辑
context.write(new Text(field), new IntWritable(1));
}
Hadoop中是否存在工具类可以处理此问题?甚至让用户根据需求个性化操作?还是说用户只能手动拆分?
Hadoop还提供了TextInputFormat类
它可以自动将输入文件按行读取,并将每行数据解析为Text类型的对象,方便后续处理。
如果需要进行更加自由的操作,用户也可以自定义InputFormat类和RecordReader类.
还没有试过
【思考题3】在reducer中,为何输入
V3是Iterable是应为要处理Map阶段输出的相同键的多个值而设计的。
。
迭代器允许我们逐个遍历集合中的类型不同的元素,并在遍历过程中执行相应的操作。
。
在Map阶段,迭代器用于读取和处理输入数据。
Map函数通常需要处理大量的数据,由于内存和其他限制,不可能一次性将所有数据加载到内存中。
迭代器就允许Map函数逐个读取输入数据并进行处理。
迭代器提供了一种逐个访问输入数据的机制,使得Map函数能够有效地处理大规模数据集。
4.实验内容【大致步骤】
【思考题5】无论是map还是reduce,输出数据最后是如何同框架交互的?
在Map阶段中,Map函数输出的
在Reduce阶段中,Reducer接收到的输入数据为
最后,所有Reduce函数的输出都会被框架收集器收集,存储在指定的输出文件中。
【思考题6】仔细观察运行时控制台打印信息,在你的环境中有多少个map和reduce参与了计算?其中map()方法和reduce()方法又各自被调用了多少次?有什么依据?
4.实验内容【大致步骤】
【参考链接】 (梁老师博客)
https://blog.csdn.net/qq_42881421/article/details/84133800
参考注意
文章最后有代码的整个包导入就可。
4.实验内容【大致步骤】
Hadoop不使用Java原生的序列化。
Hadoop采用自己的序列化框架Writables。
Java原生的序列化机制存在一些性能和可移植性方面的限制
不适合在大规模数据处理环境中使用。
【思考题8】对于shuffle,MR框架分别对P值和key值做了排序,请问我们可以对这两个排序过程做更加精细的控制吗?即灵活地自定义排序规则?
在MapReduce框架中,对P值和Key值进行排序是为了确保相同Key的记录能够被发送到同一个Reducer进行处理。
对于P值排序,可以通过实现自定义的Partitioner类来控制。
对于Key值排序,可以通过实现自定义的Comparator类来控制。
MapReduce框架提供了默认的排序规则,也允许用户自定义排序规则。
自定义Partitioner和Comparator
可以通过调用setPartitionerClass()和setSortComparatorClass()方法
来指定自定义的Partitioner和Comparator。
【参考链接】 (梁老师博客)
https://blog.csdn.net/qq_42881421/article/details/84328787
参考注意
直接导入包而已,要注意的就注意Partitioner这个类而已