大数据hadoop学习【12】-----基于ubuntu16.04上hadoop中的MapReduce编程实践


上次博客,林君学长讲到通过java进行hbase数据库的相关操作,通过上次几节博客的讲解,habse数据库我们就暂告一段落了;接下来,我们将要进行MapReduce的学习,后面几次大数据hadoop学习的博客,林君学长将带大家了解MapReduce的操作及深入学习,本次博客,就让我们先一下了解一下MapReduce,并进行相关项目配置和初始学习吧!

一、MapReduce简介

1、MapReduce的定义

MapReduce是面向大数据并行处理的计算模型、框架和平台,它隐含了以下三层含义:
1)、MapReduce是一个基于集群的高性能并行计算平台(Cluster Infrastructure)。

它允许用市场上普通的商用服务器构成一个包含数十、数百至数千个节点的分布和并行计算集群。

2)、MapReduce是一个并行计算与运行软件框架(Software Framework)。

它提供了一个庞大但设计精良的并行计算软件框架,能自动完成计算任务的并行化处理,自动划分计算数据和计算任务,在集群节点上自动分配和执行任务以及收集计算结果,将数据分布存储、数据通信、容错处理等并行计算涉及到的很多系统底层的复杂细节交由系统负责处理,大大减少了软件开发人员的负担。

3)、MapReduce是一个并行程序设计模型与方法(Programming Model & Methodology)。

它借助于函数式程序设计语言Lisp的设计思想,提供了一种简便的并行程序设计方法,用Map和Reduce两个函数编程实现基本的并行计算任务,提供了抽象的操作和并行编程接口,以简单方便地完成大规模数据的编程和计算处理

MapReduce是一种编程模型,用于大规模数据集(大于1TB)的并行运算。概念"Map(映射)“和"Reduce(归约)”,是它们的主要思想,都是从函数式编程语言里借来的,还有从矢量编程语言里借来的特性。它极大地方便了编程人员在不会分布式并行编程的情况下,将自己的程序运行在分布式系统上。
当前的软件实现是指定一个Map(映射)函数,用来把一组键值对映射成一组新的键值对,指定并发的Reduce(归约)函数,用来保证所有映射的键值对中的每一个共享相同的键组。 MapReduce是谷歌公司的核心计算模型,Hadoop开源实现了MapReduce

2、MapReduce的用途

在Google,MapReduce用在非常广泛的应用程序中,包括“分布grep,分布排序,web连接图反转,每台机器的词矢量,web访问日志分析,反向索引构建,文档聚类,机器学习,基于统计的机器翻译…”值得注意的是,MapReduce实现以后,它被用来重新生成Google的整个索引,并取代老的ad hoc程序去更新索引。
MapReduce会生成大量的临时文件,为了提高效率,它利用Google文件系统来管理和访问这些文件。
在谷歌,超过一万个不同的项目已经采用MapReduce来实现,包括大规模的算法图形处理、文字处理、数据挖掘、机器学习、统计机器翻译以及众多其他领域。

Nutch项目开发了一个实验性的MapReduce的实现,也即是后来大名鼎鼎的hadoop
Phoenix是斯坦福大学开发的基于多核/多处理器、共享内存的MapReduce实现

3、MapReduce的主要功能

1)、数据划分和计算任务调度

系统自动将一个作业(Job)待处理的大数据划分为很多个数据块,每个数据块对应于一个计算任务(Task),并自动调度计算节点来处理相应的数据块。作业和任务调度功能主要负责分配和调度计算节点(Map节点或Reduce节点),同时负责监控这些节点的执行状态,并负责Map节点执行的同步控制。

2)、数据/代码互定位

为了减少数据通信,一个基本原则是本地化数据处理,即一个计算节点尽可能处理其本地磁盘上所分布存储的数据,这实现了代码向数据的迁移;当无法进行这种本地化数据处理时,再寻找其他可用节点并将数据从网络上传送给该节点(数据向代码迁移),但将尽可能从数据所在的本地机架上寻 找可用节点以减少通信延迟

3)、系统优化

为了减少数据通信开销,中间结果数据进入Reduce节点前会进行一定的合并处理;一个Reduce节点所处理的数据可能会来自多个Map节点,为了避免Reduce计算阶段发生数据相关性,Map节点输出的中间结果需使用一定的策略进行适当的划分处理,保证相关性数据发送到同一个Reduce节点;此外,系统还进行一些计算性能优化处理,如对最慢的计算任务采用多备份执行、选最快完成者作为结果

4)、出错检测和恢复

以低端商用服务器构成的大规模MapReduce计算集群中,节点硬件(主机、磁盘、内存等)出错和软件出错是常态,因此MapReduce需要能检测并隔离出错节点,并调度分配新的节点接管出错节点的计算任务。同时,系统还将维护数据存储的可靠性,用多备份冗余存储机制提 高数据存储的可靠性,并能及时检测和恢复出错的数据

通过对以上的相关MapReduce的定义,大家应该对MapReduce有一定的了解,接下来,我们将通过ubuntu来实现MapReduce的相关操作,实现他可以做到的相关功能,一起往下看吧!

二、通过MapReduce进行词频统计

1、文件词频内容准备

1)、打开终端,在ubuntu本地文件系统中准备用于词频统计的TXT文件

mkdir lenovo
cd ~/lenovo
touch word1.txt
touch word2.txt

2)、将我们创建的两个文件写入对应的单词或者句子,用于进行词频统计
(1)、为word1.txt写入文件内容

gedit word1.txt

内容自己任意发挥,例如林君学长的内容如下:
在这里插入图片描述
写入文件后保存后关闭!
(2)、同样的,为word2.txt也写上不同的内容
在这里插入图片描述
提示:文件内容可以是中文,但同一行中,不同词记得用空格隔开,不然hadoop文件系统会将这一行记录为一个词,也就是一句话当做一个词,达不到词频统计的效果

注意: 该文件可以在系统的任意地方,但前提是你得记住路径,因为后面我们需要将该文件上传至hadoop的hdfs文件系统,当然呢,这里很多小伙伴会说,既然要上传至hdfs文件系统,为什么不直接在hdfs中创建txt文件系统然后写入内容呢?当然,这是可以的哈,林君学长通过这种方式上传主要是熟悉一下hdfs的相关命令,也让各位小伙伴顺便熟悉一下哦!

2、在Eclipse中创建MapReduce工程项目

1)、打开Eclipse创建MapReduce项目工程
在这里插入图片描述
2)、填写项目名称,名称大家自己发挥,以下为林君学长创建的名称wordCount
在这里插入图片描述

3、为MapReduce项目添加需要的JAR包

在这里插入图片描述

1)、添加路径为 /usr/local/hadoop/share/hadoop/common目录下的hadoop-common-3.1.3.jar和haoop-nfs-3.1.3.jar
注意:/usr/local/hadoop/ 为hadoop的安装路径哦,视自己的安装路径而定
在这里插入图片描述
2)、添加路径为 /usr/local/hadoop/share/hadoop/common/lib目录下的所有JAR包
在这里插入图片描述
3)、添加路径为: /usr/local/hadoop/share/hadoop/mapreduce目录下的所有JAR包
在这里插入图片描述
4)、添加路径为: /usr/local/hadoop/share/hadoop/mapreduce/lib目录下的所有JAR包。
在这里插入图片描述
5)、点击finsh,完成项目创建
在这里插入图片描述

4、 编写用于词频统计的Java代码

1)、下源文件Src下创建包wordCount,包名可以任取!
在这里插入图片描述
在这里插入图片描述
2)、创建JAVA类WordCount,并编写统计词频的java代码
在这里插入图片描述
在这里插入图片描述
写入java文件代码,如下所示:

package wordCount;
import java.io.IOException;
import java.util.Iterator;
import java.util.StringTokenizer;
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 org.apache.hadoop.util.GenericOptionsParser;
public class WordCount {
    public WordCount() {
    }
     public static void main(String[] args) throws Exception {
        Configuration conf = new Configuration();
        String[] otherArgs = (new GenericOptionsParser(conf, args)).getRemainingArgs();
        if(otherArgs.length < 2) {
            System.err.println("Usage: wordcount <in> [<in>...] <out>");
            System.exit(2);
        }
        Job job = Job.getInstance(conf, "word count");
        job.setJarByClass(WordCount.class);
        job.setMapperClass(WordCount.TokenizerMapper.class);
        job.setCombinerClass(WordCount.IntSumReducer.class);
        job.setReducerClass(WordCount.IntSumReducer.class);
        job.setOutputKeyClass(Text.class);
        job.setOutputValueClass(IntWritable.class); 
        for(int i = 0; i < otherArgs.length - 1; ++i) {
            FileInputFormat.addInputPath(job, new Path(otherArgs[i]));
        }
        FileOutputFormat.setOutputPath(job, new Path(otherArgs[otherArgs.length - 1]));
        System.exit(job.waitForCompletion(true)?0:1);
    }
    public static class TokenizerMapper extends Mapper<Object, Text, Text, IntWritable> {
        private static final IntWritable one = new IntWritable(1);
        private Text word = new Text();
        public TokenizerMapper() {
        }
        public void map(Object key, Text value, Mapper<Object, Text, Text, IntWritable>.Context context) throws IOException, InterruptedException {
            StringTokenizer itr = new StringTokenizer(value.toString()); 
            while(itr.hasMoreTokens()) {
                this.word.set(itr.nextToken());
                context.write(this.word, one);
            }
        }
    }
public static class IntSumReducer extends Reducer<Text, IntWritable, Text, IntWritable> {
        private IntWritable result = new IntWritable();
        public IntSumReducer() {
        }
        @SuppressWarnings("rawtypes")
		public void reduce(Text key, Iterable<IntWritable> values, Reducer<Text, IntWritable, Text, IntWritable>.Context context) throws IOException, InterruptedException {
            int sum = 0;
            IntWritable val;
            for(Iterator i$ = values.iterator(); i$.hasNext(); sum += val.get()) {
                val = (IntWritable)i$.next();
            }
            this.result.set(sum);
            context.write(key, this.result);
        }
    }
}

3)、运行结果
程序运行结束后,会在底部的“Console”面板中显示运行结果信息如下所示:
在这里插入图片描述
由于我们没有指定文件,所以这里暂时只会显示上面红色的东西,接下来,我们将项目导出、成为jar包,然后通过hadoop运行,然后再查看结果!

5、导出为JAR包项目,为终端命令行执行做准备

1)、进行项目导出
在这里插入图片描述
在这里插入图片描述
2)、浏览路径,确定导出名,然后进行导出
在这里插入图片描述
3)、出现以下界面点击ok
在这里插入图片描述
4)、导出成功界面
在这里插入图片描述
关闭eclipse,然后打开终端,进行jar包词频统计运行!

6、终端运行JAR包项目

1)、打开终端,运行hadoop

start-dfs.sh
jps

在这里插入图片描述
2)、在hdfs文件系统上新建input1文件夹,用于存放我们之前创建的文件夹
**注意:**记住不要与原来有的文件夹重名哦,这里大家可以随便取名称,例如林君学长的因为有input目录,所以自己新建input1目录

hdfs dfs -mkdir /user/hadoop/input1

3)、上传ubuntu本地文件到hdfs文件系统(上面创建的word12.txt文件哦)

hdfs dfs -put  ~/lenovo/word1.txt  /user/hadoop/input1
hdfs dfs -put  ~/lenovo/word2.txt  /user/hadoop/input1

在这里插入图片描述
4)、运行词频统计java的jar包

hadoop jar ~/lenovo/bigData/myapp/WordCount.jar /user/hadoop/input1 /user/hadoop/output1

成功运行结果如下所示:
在这里插入图片描述
提示:output1文件夹,用于存放统计的结果,我们不用创建,该命令会自动帮我们创建,但是保证output1路径下面不能有与他相同名称的文件夹
5)、现在通过终端命令列出output1目录下的词频统计结果吧:

hdfs dfs -cat /user/hadoop/output1/*

运行结果如下:
在这里插入图片描述
可以看到,他已经将我们上述两个文件word1.txt与word2.txt中的所有单词进行统计啦,统计单词个数终端可以看到啦!可以与前面的文件内容对比,单词个数肯定是完全一样的!
6)、实验结束,关闭hadoop

stop-dfs.sh

在这里插入图片描述
至此,词频统计程序顺利运行结束。需要注意的是:如果要再次运行WordCount.jar,需要首先删除HDFS中的output1目录,否则会报错

hdfs dfs -rm -r /user/hadoop/output1

以上就是本次博客的全部内容啦,希望阅读学长博客的小伙伴能够对MapReduce的学习产生浓厚的兴趣,因为这才刚刚开始啦,后面还有更加精彩的等待我们,每日一学、日进斗金!
遇到问题的小伙伴记得评论区留言哦,林君学长看到会为大家进行解答的,这个学长不太冷!

陈一月的又一天编程岁月^ _ ^

发布了84 篇原创文章 · 获赞 133 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/qq_42451251/article/details/105717351