【Hadoop基礎教程】5、Hadoop之單詞計數

單詞計數是最簡單也是最能體現MapReduce思想的程序之一,可以稱爲MapReduce版“Hello World”,該程序的完整代碼可以在Hadoop安裝包的src/example目錄下找到。單詞計數主要完成的功能:統計一系列文本文件中每個單詞出現的次數,如下圖所示。本blog將通過分析WordCount源碼來幫助大家摸清MapReduce程序的基本結構和運行機制。

單詞計數

開發環境


硬件環境:Centos 6.5 服務器4臺(一臺爲Master節點,三臺爲Slave節點)
軟件環境:Java 1.7.0_45、hadoop-1.2.1

1、 WordCount的Map過程


Map過程需要繼承org.apache.hadoop.mapreduce包中Mapper類,並重寫其map方法。Map方法中的value值存儲的是文本文件中的一行記錄(以回車符爲結束標記),而key值爲該行的首字符相對於文本文件的首地址的偏移量。然後StringTokenizer類將每一行拆分成一個個的單詞,並將

2、 WordCount的Reduce過程


Reduce過程需要繼承org.apache.hadoop.mapreduce包中的Reduce類,並重寫其reduce方法。Reduce方法的輸入參數key爲單個單詞,而values是由各Mapper上對應單詞的計數值所組成的列表,所以只要遍歷values並求和,即可得到某個單詞出現的總次數。
IntSumReducer類的實現代碼如下,詳細源碼請參考:WordCount\src\WordCount.java。

public static class IntSumReducer 
   extends Reducer<Text,IntWritable,Text,IntWritable> {
    private IntWritable result = new IntWritable();

    public void reduce(Text key, Iterable<IntWritable> values, Context context
                       ) throws IOException, InterruptedException {
      //輸入參數key爲單個單詞;
      //輸入參數Iterable<IntWritable> values爲各個Mapper上對應單詞的計數值所組成的列表。
      int sum = 0;
      for (IntWritable val : values) {//遍歷求和
        sum += val.get();
      }
      result.set(sum);
      context.write(key, result);//輸出求和後的<key,value>
    }
}

3、 WordCount的驅動執行過程


在MapReduce中,由Job對象負責管理和運行一個計算任務,並通過Job的一些方法對任務的參數進行相關的設置。此處設置了使用TokenizerMapper完成Map過程和使用IntSumReducer完成Combine和Reduce過程。還設置了Map過程和Reduce過程的輸出類型:key的類型爲Text,value的類型爲IntWritable。任務的輸入和輸出路徑則由命令行參數指定,並由FileInputFormat和FileOutputFormat分別設定。完成相應任務的參數設定後,即可調用job.waitForCompletion()方法執行任務。
驅動函數實現代碼如下,詳細源碼請參考:WordCount\src\WordCount.java。

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> <out>");
      System.exit(2);
    }
    Job job = new Job(conf, "word count");
    job.setJarByClass(WordCount.class);
    //設置Mapper、Combiner、Reducer方法
    job.setMapperClass(TokenizerMapper.class);
    job.setCombinerClass(IntSumReducer.class);
    job.setReducerClass(IntSumReducer.class);
    //設置了Map過程和Reduce過程的輸出類型,設置key的輸出類型爲Text,value的輸出類型爲IntWritable;
    job.setOutputKeyClass(Text.class);
    job.setOutputValueClass(IntWritable.class);
    //設置任務數據的輸入、輸出路徑;
    FileInputFormat.addInputPath(job, new Path(otherArgs[0]));
    FileOutputFormat.setOutputPath(job, new Path(otherArgs[1]));
    //執行job任務,執行成功後退出;
    System.exit(job.waitForCompletion(true) ? 0 : 1);
}

4、 WordCount的處理過程


如上所述給出了WordCount的設計思路及源碼分析過程,但很多細節都未被提及,本節將根據MapReduce的處理工程,對WordCount進行更詳細的講解。詳細的執行步驟如下:
1)將文件拆分成splits,由於測試用的文件較小,所以每個文件爲一個split,並將文件按行分割形成< key,value >對,如圖所示。這一步由MapReduce框架自動完成,其中偏移量(即key值)包括了回車符所佔的字符數(Windows和Linux環境下會不同)。

分割過程

2)將分割好的< key,value>對交給用戶定義的map方法進行處理,生成新的< key,value >對,如圖所示:

Map過程

3)得到map方法輸出的< key,value>對後,Mapper會將它們按照key值進行排序,並執行Combine過程,將key值相同的value值累加,得到Mapper的最終輸出結果,如圖所示:

Map過程與Combine過程

4) Reducer先對從Mapper接收的數據進行排序,再交由用戶自定義的reducer方法進行處理,得到新的< key,value>對,並作爲WordCount的輸出結果,如圖所示:

Reduce過程

5、 WordCount的最小驅動


MapReduce框架在幕後默默地完成了很多事情,如果不重寫map和reduce方法,它會不會就此罷工了?下面設計一個“WordCount最小驅動”MapReduce—LazyMapReduce,該類只對任務進行必要的初始化及輸入/輸出路徑的設置,其餘的參數(如輸入/輸出類型、map方法、reduce方法等)均保持默認狀態。LazyMapReduce的實現代碼如下:

public class LazyMapReduce {

  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> <out>");
      System.exit(2);
    }
    Job job = new Job(conf, "LazyMapReduce");
        //設置任務數據的輸入、輸出路徑;
    FileInputFormat.addInputPath(job, new Path(otherArgs[0]));
    FileOutputFormat.setOutputPath(job, new Path(otherArgs[1]));
        //執行job任務,執行成功後退出;
    System.exit(job.waitForCompletion(true) ? 0 : 1);
  }
}

可以看出在默認情況下,MapReduce原封不動地將輸入

6、部署運行


1)部署源碼

#設置工作環境
[hadoop@K-Master ~]$ mkdir -p /usr/hadoop/workspace/MapReduce
#部署源碼
將WordCount 文件夾拷貝到/usr/hadoop/workspace/MapReduce/ 路徑下;

… 你可以直接 下載 WordCount

2)編譯文件

在使用javac編譯命令時,我們用到了兩個參數:-classpath指定編譯該類所需要的核心包,-d指定編譯後生成的class文件的存放路徑;最後的WordCount.java表示編譯的對象是當前文件夾下的WordCount.java類。

[hadoop@K-Master ~]$ cd /usr/hadoop/workspace/MapReduce/WordCount
[hadoop@K-Master WordCount]$ javac -classpath /usr/hadoop/hadoop-core-1.2.1.jar:/usr/hadoop/lib/commons-cli-1.2.jar -d bin/ src/WordCount.java
#查看編譯結果
[hadoop@K-Master WordCount]$ ls bin/ -la
總用量 12
drwxrwxr-x 2 hadoop hadoop  102 9月  15 11:08 .
drwxrwxr-x 4 hadoop hadoop   69 9月  15 10:55 ..
-rw-rw-r-- 1 hadoop hadoop 1830 9月  15 11:08 WordCount.class
-rw-rw-r-- 1 hadoop hadoop 1739 9月  15 11:08 WordCount$IntSumReducer.class
-rw-rw-r-- 1 hadoop hadoop 1736 9月  15 11:08 WordCount$TokenizerMapper.class

3)打包jar文件

在使用jar命令進行打包class文件時,我們用到了兩個參數:-cvf表示打包class文件並顯示詳細的打包信息,-C指定打包的對象;命令最後的“.”表示將打包生成的文件保存在當前目錄下。

[hadoop@K-Master WordCount]$ jar -cvf WordCount.jar -C bin/ .
已添加清單
正在添加: WordCount$TokenizerMapper.class(輸入 = 1736) (輸出 = 754)(壓縮了 56%)
正在添加: WordCount$IntSumReducer.class(輸入 = 1739) (輸出 = 74

特別注意:打包命令最後一個字符爲“.”,表示將打包生成的文件WordCount.jar保存到當前文件夾下,輸入命令時特別留心。

4)啓動Hadoop集羣

如果HDFS已經啓動,則不需要執行以下命令,可通過jps命令查看HDFS是否已經啓動

[hadoop@K-Master WordCount]$ start-dfs.sh      #啓動HDFS文件系統
[hadoop@K-Master WordCount]$ start-mapred.sh       #啓動MapReducer服務
[hadoop@K-Master WordCount]$ jps
5082 JobTracker
4899 SecondaryNameNode
9048 Jps
4735 NameNode

5)上傳輸入文件到HDFS

在MapReduce中,一個準備提交執行的應用程序稱爲“作業(Job)”,Master節點將對該Job劃分成多個task運行於各計算節點上(Slave節點),而task任務輸入輸出的數據均是基於HDFS分佈式文件管理系統,故需要將輸入數據上傳到HDFS分佈式文件管理系統之上,如下所示。

#在HDFS上創建輸入/輸出文件夾
[hadoop@K-Master WordCount]$ hadoop fs -mkdir wordcount/input/ 
#傳本地file中文件到集羣的input目錄下
[hadoop@K-Master WordCount]$ hadoop fs -put input/file0*.txt wordcount/input
#查看上傳到HDFS輸入文件夾中到文件
[hadoop@K-Master WordCount]$ hadoop fs -ls wordcount/input
Found 2 items
-rw-r--r--   1 hadoop supergroup 22 2014-07-12 19:50 /user/hadoop/wordcount/input/file01.txt
-rw-r--r--   1 hadoop supergroup 28 2014-07-12 19:50 /user/hadoop/wordcount/input/file02.txt

6)運行Jar文件

我們通過hadoop jar命令運行一個job任務,關於該命令各個參數的含義如下圖所示:

hadoop-jar

[hadoop@K-Master WordCount]$ hadoop jar WordCount.jar WordCount wordcount/input wordcount/output
14/07/12 22:06:42 INFO input.FileInputFormat: Total input paths to process : 2
14/07/12 22:06:42 INFO util.NativeCodeLoader: Loaded the native-hadoop library
14/07/12 22:06:42 WARN snappy.LoadSnappy: Snappy native library not loaded
14/07/12 22:06:42 INFO mapred.JobClient: Running job: job_201407121903_0004
14/07/12 22:06:43 INFO mapred.JobClient:  map 0% reduce 0%
14/07/12 22:06:53 INFO mapred.JobClient:  map 50% reduce 0%
14/07/12 22:06:55 INFO mapred.JobClient:  map 100% reduce 0%
14/07/12 22:07:03 INFO mapred.JobClient:  map 100% reduce 33%
14/07/12 22:07:05 INFO mapred.JobClient:  map 100% reduce 100%
14/07/12 22:07:07 INFO mapred.JobClient: Job complete: job_201407121903_0004
14/07/12 22:07:07 INFO mapred.JobClient: Counters: 29

7)查看運行結果

結果文件一般由三部分組成:
1) _SUCCESS文件:表示MapReduce運行成功。
2) _logs文件夾:存放運行MapReduce的日誌。
3) Part-r-00000文件:存放結果,也是默認生成的結果文件。
使用hadoop fs -ls wordcount/output命令查看輸出結果目錄,如下所示:

#查看FS上output目錄內容
[hadoop@K-Master WordCount]$ hadoop fs -ls wordcount/output
Found 3 items
-rw-r--r--   1 hadoop supergroup  0 2014-09-15 11:11 /user/hadoop/wordcount/output/_SUCCESS
drwxr-xr-x   - hadoop supergroup  0 2014-09-15 11:10 /user/hadoop/wordcount/output/_logs
-rw-r--r--   1 hadoop supergroup 41 2014-09-15 11:11 /user/hadoop/wordcount/output/part-r-00000
使用 hadoop fs –cat wordcount/output/part-r-00000命令查看輸出結果,如下所示:
#查看結果輸出文件內容
[hadoop@K-Master WordCount]$ hadoop fs -cat wordcount/output/part-r-00000
Bye     1
Goodbye 1
Hadoop  2
Hello       2
World   2

到這裏,整個MapReduce的快速入門就結束了。本篇blog使用一個完整的案例,從開發到部署再到查看結果,讓大家對MapReduce的基本使用有所瞭解。

您可能喜歡

【Hadoop基礎教程】5、Hadoop之單詞計數
【Hadoop基礎教程】6、Hadoop之單表關聯查詢
【Hadoop基礎教程】7、Hadoop之一對多關聯查詢
【Hadoop基礎教程】8、Hadoop之一對多關聯查詢
【Hadoop基礎教程】9、Hadoop之倒排索引

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