明白MapReduce 程式的工作原理之後,下一步便是通過代碼來實現它。 我們需要三樣東西:一個map 函數、一個reduce 函數和一些用來運行作業的代碼。 map函數由Mapper 介面實現來表示,後者聲明瞭一個map()方法。 例2-3 顯示了我們的map函數實現。
例2-3. 查找最高氣溫的Mapper
import java.io.IOException; &HTTP://www.aliyun.com/zixun/aggregation/37954.html">nbsp; 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, Te xt, Text, IntWritable> { private static final int MISSING = 9999; publi c void map(LongWritable key, Text value, OutputCollector<Text, IntWrit able> output, Reporter reporter) throws IOException { &n bsp; 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.pars eInt(line.substring(87, 92)); } String quality = line.substring(92, 93); if (airTemperature != MISSING && quality.matches("[01459]")) { &n bsp; 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, IntWrita ble, Text, IntWritable> { public void reduce(Text key, Iterator<IntWritable> values, & nbsp; OutputCollector<Text, IntWritable> output, Reporter reporter) ; throws IOException { int maxValue = Integer.MIN_VALUE; ; while (values.hasNext()) { maxValue = Math.max(maxVa lue, 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 { &nbs p; if (args.length != 2) { System.err.println("Usage: MaxTemperature<input path> <output path>"); System.exit(-1); } JobConf conf = new JobConf(Max Temperature.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()會提交作業並等待完成,最後將其進展情況寫到主控台。