大數據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 file
cd ~/file
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中的output目錄,否則會報錯

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

以上就是本次博客的全部內容啦,希望閱讀學長博客的小夥伴能夠對MapReduce的學習產生濃厚的興趣,因爲這纔剛剛開始啦,後面還有更加精彩的等待我們,每日一學、日進斗金!
遇到問題的小夥伴記得評論區留言哦,林君學長看到會爲大家進行解答的,這個學長不太冷!

陳一月的又一天編程歲月^ _ ^

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章