【java源代碼】Java MapReduce



Java MapReduce




  明白MapReduce 程序的工作原理之後,下一步便是通過代碼來實現它。我們需要三樣東西:一個map 函數、一個reduce 函數和一些用來運行作業的代碼。map函數由Mapper 接口實現來表示,後者聲明瞭一個map()方法。例2-3 顯示了我們的map函數實現。


  例2-3. 查找最高氣溫的Mapper


  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.mapred.MapReduceBase;  import org.apache.hadoop.mapred.Mapper;   import org.apache.hadoop.mapred.OutputCollector;  import org.apache.hadoop.mapred.Reporter;   public class MaxTemperatureMapper extends MapReduceBase       implements Mapper<LongWritable, Text, Text, IntWritable> {     private static final int MISSING = 9999;          public void map(LongWritable key, Text value,               OutputCollector<Text, IntWritable> output, Reporter reporter)         throws IOException {                String line = value.toString();           String year = line.substring(15, 19);           int airTemperature;           if (line.charAt(87) == '+') { // parseIntdoesn't like leading plus signs               airTemperature = Integer.parseInt(line.substring(88, 92));           } else {               airTemperature = Integer.parseInt(line.substring(87, 92));           }           String quality = line.substring(92, 93);           if (airTemperature != MISSING && quality.matches("[01459]")) {               output.collect(new Text(year), new IntWritable(airTemperature));           }       }   }  該Mapper接口是一個泛型類型,它有四個形參類型,分別指定map函數的輸入鍵、輸入值、輸出鍵和輸出值的類型。就目前的示例來說,輸入鍵是一個長整數偏移量,輸入值是一行文本,輸出鍵是年份,輸出值是氣溫(整數)。Hadoop自身提供一套可優化網絡序列化傳輸的基本類型,而不直接使用Java內嵌的類型。這些類型均可在org.apache.hadoop.io包中找到。這裏我們使用LongWritable類型(相當於Java中的Long類型)、Text類型(相當於Java中的String類型)和IntWritable類型(相當於Java 中的Integer類型)。


  map()方法的輸入是一個鍵和一個值。我們首先將包含有一行輸入的Text值轉換成Java的String類型,之後使用substring()方法提取我們感興趣的列。


  map()方法還提供了OutputCollector實例用於輸出內容的寫入。在這種情況下,我們將年份數據按Text對象進行讀/寫 (因爲我們把年份當作鍵),將氣溫值封裝在IntWritable 類型中。


  我們只在氣溫數據不缺失並且所對應質量代碼顯示爲正確的氣溫讀數時,纔將其寫入輸出記錄中。


  reduce函數通過Reducer進行類似的定義,如例2-4 所示。


  例2-4. 查找最高氣溫的Reducer


  import java.io.IOException;   import java.util.Iterator;   import org.apache.hadoop.io.IntWritable;   import org.apache.hadoop.io.Text;   import org.apache.hadoop.mapred.MapReduceBase;   import org.apache.hadoop.mapred.OutputCollector;   import org.apache.hadoop.mapred.Reducer;   import org.apache.hadoop.mapred.Reporter;   public class MaxTemperatureReducer extends MapReduceBase       implements Reducer<Text, IntWritable, Text, IntWritable> {    public void reduce(Text key, Iterator<IntWritable> values,        OutputCollector<Text, IntWritable> output, Reporter reporter)               throws IOException {                int maxValue = Integer.MIN_VALUE;           while (values.hasNext()) {               maxValue = Math.max(maxValue, values.next().get());      }           output.collect(key, new IntWritable(maxValue));       }  }  同樣,針對reduce函數也有四個形式參數類型用於指定其輸入和輸出類型。reduce 函數的輸入類型必須與map 函數的輸出類型相匹配:即Text類型和IntWritable類型。在這種情況下,reduce函數的輸出類型也必須是Text和IntWritable這兩種類型,分別輸出年份和最高氣溫。該最高氣溫是通過循環比較當前氣溫與已看到的最高氣溫獲得的。


  第三部分代碼負責運行MapReduce 作業(請參見例2-5)。


  例2-5. 該應用程序在氣象數據集中找出最高氣溫


  import java.io.IOException;   import org.apache.hadoop.fs.Path;   import org.apache.hadoop.io.IntWritable;   import org.apache.hadoop.io.Text;   import org.apache.hadoop.mapred.FileInputFormat;   import org.apache.hadoop.mapred.FileOutputFormat;   import org.apache.hadoop.mapred.JobClient;   import org.apache.hadoop.mapred.JobConf;   public class MaxTemperature {     public static void main(String[] args) throws IOException {           if (args.length != 2) {        System.err.println("Usage: MaxTemperature<input path> <output path>");               System.exit(-1);           }                JobConf conf = new JobConf(MaxTemperature.class);           conf.setJobName("Max temperature");            FileInputFormat.addInputPath(conf, new Path(args[0]));           FileOutputFormat.setOutputPath(conf, new Path(args[1]));       conf.setMapperClass(MaxTemperatureMapper.class);               conf.setReducerClass(MaxTemperatureReducer.class);      conf.setOutputKeyClass(Text.class);           conf.setOutputValueClass(IntWritable.class);            JobClient.runJob(conf);       }  }  JobConf對象指定了作業執行規範。我們可以用它來控制整個作業的運行。在Hadoop 集羣上運行這個作業時,我們需要將代碼打包成一個JAR文件(Hadoop會在集羣上分發這個文件)。我們無需明確指定JAR 文件的名稱,而只需在JobConf的構造函數中傳遞一個類,Hadoop將通過該類查找包含有該類的JAR文件進而找到相關的JAR文件。


  構造JobConf對象之後,需要指定輸入和輸出數據的路徑。調用 FileInputFormat類的靜態函數addInputPath()來定義輸入數據的路徑,該路徑可以是單個文件、目錄(此時,將目錄下所有文件當作輸入)或符合特定文件模式的一組文件。由函數名可知,可以多次調用addInputPath()實現多路徑的輸入。


  通過調用FileOutputFormat 類中的靜態函數 setOutputPath()來指定輸出路徑。該函數指定了reduce 函數輸出文件的寫入目錄。在運行任務前該目錄不應該存在,否則Hadoop 會報錯並拒絕運行該任務。這種預防措施是爲了防止數據丟失(一個長時間運行任務的結果被意外地覆蓋將是非常惱人的)。


  接着,通過setMapperClass()和setReducerClass()指定map和reduce類型。


  setOutputKeyClass()和setOutputValueClass()控制map和reduce函數的輸出類型,正如本例所示,這兩個輸出類型往往相同。如果不同,map函數的輸出類型則通過setMapOutputKeyClass()和setMapOutputValueClass()函數來設置。


  輸入的類型通過InputFormat類來控制,我們的例子中沒有設置,因爲使用的是默認的TextInputFormat(文本輸入格式)。


  在設置定義map 和reduce 函數的類後,便可以開始運行任務。JobClient類的靜態函數runJob()會提交作業並等待完成,最後將其進展情況寫到控制檯。

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