第六篇:Eclipse上執行第一個Hadoop例項 - WordCount(單詞統計程式)

穆晨發表於2017-05-20

需求

       計算出檔案中每個單詞的頻數。要求輸出結果按照單詞的字母順序進行排序。每個單詞和其頻數佔一行,單詞和頻數之間有間隔。

       比如,輸入兩個檔案,其一內容如下:

       hello world

       hello hadoop

       hello mapreduce

       另一內容如下:

       bye world

       bye hadoop

       bye mapreduce

       對應上面給出的輸入樣例,其輸出樣例為:

       bye        3

       hadoop    2

       hello      3

       mapreduce   2

       world     2

方案制定

       對該案例,可設計出如下的MapReduce方案:

1. Map階段各節點完成由輸入資料到單詞切分再到單詞蒐集的工作

2. shuffle階段完成相同單詞的聚集再到分發到各個Reduce節點的工作 (shuffle階段是MapReduce的預設過程)

3. Reduce階段負責接收所有單詞並計算各自頻數

程式碼示例

  1 /**
  2  *  Licensed under the Apache License, Version 2.0 (the "License");
  3  *  you may not use this file except in compliance with the License.
  4  *  You may obtain a copy of the License at
  5  *
  6  *      http://www.apache.org/licenses/LICENSE-2.0
  7  *
  8  *  Unless required by applicable law or agreed to in writing, software
  9  *  distributed under the License is distributed on an "AS IS" BASIS,
 10  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 11  *  See the License for the specific language governing permissions and
 12  *  limitations under the License.
 13  */
 14 
 15 
 16 package org.apache.hadoop.examples;
 17 
 18 import java.io.IOException;
 19 import java.util.StringTokenizer;
 20 
 21 //匯入各種Hadoop包
 22 import org.apache.hadoop.conf.Configuration;
 23 import org.apache.hadoop.fs.Path;
 24 import org.apache.hadoop.io.IntWritable;
 25 import org.apache.hadoop.io.Text;
 26 import org.apache.hadoop.mapreduce.Job;
 27 import org.apache.hadoop.mapreduce.Mapper;
 28 import org.apache.hadoop.mapreduce.Reducer;
 29 import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
 30 import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
 31 import org.apache.hadoop.util.GenericOptionsParser;
 32 
 33 // 主類
 34 public class WordCount {
 35         
 36     // Mapper類
 37     public static class TokenizerMapper extends Mapper<Object, Text, Text, IntWritable>{
 38         
 39         // new一個值為1的整數物件 
 40         private final static IntWritable one = new IntWritable(1);
 41         // new一個空的Text物件
 42         private Text word = new Text();
 43       
 44         // 實現map函式
 45         public void map(Object key, Text value, Context context) throws IOException, InterruptedException {
 46             
 47             // 建立value的字串迭代器
 48             StringTokenizer itr = new StringTokenizer(value.toString());
 49         
 50             // 對資料進行再次分割並輸出map結果。初始格式為<位元組偏移量,單詞> 目標格式為<單詞,頻率>
 51             while (itr.hasMoreTokens()) {
 52                     word.set(itr.nextToken());
 53                     context.write(word, one);
 54             }
 55         }
 56     }
 57         
 58     // Reducer類
 59     public static class IntSumReducer extends Reducer<Text,IntWritable,Text,IntWritable> {
 60     
 61         // new一個值為空的整數物件
 62         private IntWritable result = new IntWritable();
 63 
 64         // 實現reduce函式
 65         public void reduce(Text key, Iterable<IntWritable> values, Context context) throws IOException, InterruptedException {
 66                 
 67             int sum = 0;
 68             for (IntWritable val : values) {
 69                 sum += val.get();
 70             }
 71                 
 72             // 得到本次計算的單詞的頻數
 73             result.set(sum);
 74                         
 75             // 輸出reduce結果
 76             context.write(key, result);
 77         }
 78     }
 79 
 80     // 主函式
 81     public static void main(String[] args) throws Exception {
 82     
 83         // 獲取配置引數
 84         Configuration conf = new Configuration();
 85         String[] otherArgs = new GenericOptionsParser(conf, args).getRemainingArgs();
 86                 
 87         // 檢查命令語法
 88         if (otherArgs.length != 2) {
 89                 System.err.println("Usage: wordcount <in> <out>");
 90                 System.exit(2);
 91         }
 92                 
 93         // 定義作業物件
 94         Job job = new Job(conf, "word count");
 95         // 註冊分散式類
 96         job.setJarByClass(WordCount.class);
 97         // 註冊Mapper類
 98         job.setMapperClass(TokenizerMapper.class);
 99         // 註冊合併類
100         job.setCombinerClass(IntSumReducer.class);
101         // 註冊Reducer類
102         job.setReducerClass(IntSumReducer.class);
103         // 註冊輸出格式類
104         job.setOutputKeyClass(Text.class);
105         job.setOutputValueClass(IntWritable.class);
106         // 設定輸入輸出路徑
107         FileInputFormat.addInputPath(job, new Path(otherArgs[0]));
108         FileOutputFormat.setOutputPath(job, new Path(otherArgs[1]));
109                 
110         // 執行程式
111         System.exit(job.waitForCompletion(true) ? 0 : 1);
112     }
113 }

執行方法

1. 開啟Eclipse並啟動Hdfs(方法請參考前文)

2. 新建一個MapReduce工程:”file" -> "new" -> "project",然後選擇 "Map/Reduce Project"

  

3. 設定輸入目錄及檔案

在專案工程包裡面新建一個名為input的目錄,裡面存放需要處理的輸入檔案。這裡選用2個檔名分別為file01和file02的檔案進行測試。檔案內容同需求示例。

       

4. 將輸入檔案傳輸入Hdfs

在終端輸入以下命令即可將整個目錄傳輸進Hdfs(input目錄下的所有檔案將會被送進Hdfs下名為input01的目錄裡),請根據MapReduce工程包實際路徑對如下命令略作修改即可:

1 ./bin/hadoop fs -put ../workspace/Hadoop_t1/input/ input01

5. 在工程包中新建一個WordCount類並將上面的原始碼拷貝進去。

6. 調整專案執行引數:右鍵專案 -> “Run As" -> ”Run Configurations"

       

需要新增的就是"Program arguments"下的那些程式碼。它們其實是作為命令列引數傳遞程式序的,第一段是輸入檔案路徑;第二段是輸出檔案路徑。

路徑的格式為 "[主機IP地址:hdfs埠] + [輸入/輸出目錄在hdfs中的路徑]"。

可以輸入以下命令檢視輸入目錄路徑:

1 ./bin/hadoop fs -ls

       

7. 點選"Run"執行程式。

8. 執行以下命令檢視結果:

1 ./bin/hadoop fs -cat output01/*

       

       這些主機和Hdfs的檔案傳遞,顯示也可以使用Eclipse,更方便容易。在此就不提了。

  

小結

1. 多多熟練Hadoop平臺下MapReduce專案基本建立流程。

2. WordCount是一個很經典的Hadoop示例,它雖然簡單,但具有很大的代表性。

3. 從某個程度上來說也反映了其設計的初衷,對日誌檔案的分析。

相關文章