1.程序初始化
此常規Java項目,不是Maven項目,也不是Java Enterprise項目。
打開 File->New->Project菜單,選擇Java即可,逐步點擊Next,在目錄D:\Java\hadoop\mr下創建一個項目名稱。
這里我們創建的項目叫groupbysum,表示groupbysum MapReduce小項目。
以后各種功能的mapreduce程序均已小項目形式放在mr目錄下。
其實我們也可以把mr創建為一個項目(類似空間),各個小mapreduce程序作為modules(項目)放在該項目(空間)下,但這種方式一般是是大項目,分組協同開發各個模塊功能的時候使用較多,這里就不采用了,一次只開發一個項目。
2.導入Hadoop核心依賴包
導入執行mapreduce程序依賴的Hadoop包:
- hadoop-core-1.2.1
- hadoop-hdfs-2.7.6
- hadoop-client-2.7.6
- hadoop-auth-2.7.6
- hadoop-mapreduce-client-core-2.7.6
- commons-io-2.6
- commons-logging-1.2
Apache Commons IO : 主要是文件處理,比如復制、輸入輸出、文件名處理、大小寫敏感等等。
The Apache Commons IO library contains utility classes, stream implementations, file filters, file comparators, endian transformation classes, and much more.
導入操作:打開項目結構(CTRL+ALT_SHIFT+S 或者使用工具欄的 圖標 ),點擊Modules,切換tab標簽到Dependencies,點擊 + 加號,選擇准備好的包含這些Jar包的目錄D:\Java\hadoop\jar,到這里功能上已經可以用了,但是為了便於區分管理我們導入的Jar包,這里可以點擊這個目錄,選擇右側的小鉛筆(EDIT),給這些包在這個項目里起一個分組名稱,這里我們起了hadoop-2.7.6。
3.編寫mapreduce程序
寫Java類,通常有兩種方式,一種是類中類,只寫一個Java文件,一種是一個類一個Java文件,多個Java文件。
這里我們選擇分開寫,增加對mapreduce原理對認識和理解。
創建包體,右擊src,new->package,輸入包名,這里我們命名包為com.leeyk99.com。(這個包名寫的瞎眼了,后續會寫com.leeyk99.hadoop)
重點參考:
https://www.cnblogs.com/bovenson/p/6275762.html?utm_source=itdadao&utm_medium=referral
個人學習的Java代碼(文件和目錄io代碼 ioReadWrite.java)
意外發現:
因為之前在公司通過Maven創建了WordCount官方示例MR項目,僅在pom.xml中配置了最重要的幾個Hadoop jar包依賴,成功運行。實際上Maven項目自動下載了相關的jar包,比如jackson-core-asl-1.9.13.jar、commons-configuration-1.10.ja等。
<!-- 基礎依賴hadoop-core和hadoop-common --> <!--hadoop-core的version一般為1.2.1--> <dependency> <groupId>org.apache.hadoop</groupId> <artifactId>hadoop-core</artifactId> <version>1.2.1</version> </dependency> <!--hadoop-common的version可以依照你的實際需要來--> <dependency> <groupId>org.apache.hadoop</groupId> <artifactId>hadoop-common</artifactId> <version>2.7.6</version> </dependency> <!--如果需要讀寫HDFS,則還需要依賴hadoop-hdfs和hadoop-client--> <dependency> <groupId>org.apache.hadoop</groupId> <artifactId>hadoop-hdfs</artifactId> <version>2.7.6</version> </dependency> <dependency> <groupId>org.apache.hadoop</groupId> <artifactId>hadoop-client</artifactId> <version>2.7.6</version> </dependency>
在這個普通Java項目導入第2步指定的jar包,報告了很多類缺失錯誤,最后都一一找來了。故,實際依賴完整的Jar包如下:
- commons-cli-1.4.jar
- commons-configuration-1.10.jar
- commons-httpclient-3.1.jar (這個就是去了一個warn)
- commons-io-2.6.jar
- commons-lang-2.6.jar
- commons-logging-1.2.jar
- hadoop-auth-2.7.6.jar
- hadoop-client-2.7.6.jar
- hadoop-common-2.7.6.jar
- hadoop-core-1.2.1.jar
- hadoop-hdfs-2.7.6.jar
- hadoop-mapreduce-client-core-2.7.6.jar
- jackson-core-asl-1.9.13.jar
- jackson-mapper-asl-1.8.8.jar
導入這些包后,運行無誤。
故,在對Hadoop的包的功能基本了解或者實際開發的時候,為提高效率,可以使用Maven項目。
4.MapReduce學習總結
本次Hadoop MR學習總結,主要集中在對整體運行邏輯和局部編寫細節的學習和測試,源碼原理未研究。
4.1單維度單度量統計
該MR程序測試了單度量在單維度上聚合統計的情形,即常用的:
select dim_a,sum(kpi_a) from table_name group by dim_a
4.2繼承Mapper類,重寫map方法
GroupBySumMapper類繼承抽象類:org.apache.hadoop.mapreduce.Mapper (新API)。(舊API:extends MapReduceBase implements Mapper<LongWritable, Text, Text, DoubleWritable>)
Mapper是一個泛型類型,有四個形參,分別是map函數的輸入鍵、輸入值、輸出鍵、輸出值。泛型類型的形參只能是引用類型,不能是原始類型(如int、double、char)。
Hadoop提供了一套可優化網絡序列化傳輸的引用類型(LongWritable、Text、DoubleWritable、IntWritable),而不是直接用Java的引用類型(Long、String、Double、Integer,被hadoop提供的四個類型替代).
map函數是對一行記錄進行處理,數據集的每一行是輸入,輸入鍵就是相對於文件起始位置的偏移量(如果從第一行開始,就是行號了),因為Hadoop通常是處理大數據量,因此輸入鍵類型通常指定為 LongWritable.
獲取到一行記錄后,將行轉換成成Java的String類型,提取相應的數據域(列),常用的方法:
- a.無分隔符但每組數據長度都是固定的,可以使用字符串截取,比如substring;
- b.有分隔符,可以使用split按分隔符分隔得到數組,獲取特定的數組元素;
- c.使用StringTokenizer(標記)類及其nextToken方法,按順序依次獲取數據列;
獲取到指定數據域后,conntext.write將結果輸出,寫出到臨時文件,作為reduce函數的輸入。
map任務將其輸出寫到本地硬盤,而非HDFS。因為map的輸出只是中間結果,一旦作業完成,map的輸出即可以刪除,存儲到HDFS並實現備份,難免小題大做(說白了,占用存儲,數據備份也需要時間和帶寬)。
4.3繼承Reducer類,重寫reduce方法
reduce函數以map的輸出作為輸入,因此reduce函數的輸入鍵、輸入值和map的輸出鍵、輸出值類型需要是一致的,這種情況下reduce函數的輸出類型也必須是Text和DoubleWritable。
reduce函數實現的操作通常是對輸入值的遍歷處理,比如求和、計數、比較、取均、去重等多種運算,然后將結果輸出,寫入到HDFS,作為最終產出結果。
4.4Hadoop Job配置及啟動
Job對象指定作業執行規范。可以將代碼打包成Jar文件,發不到Hadoop集群。不必指明JAR文件名稱,在Job對象的setJarByClass(GroupBySumRun.class)方法中傳遞類即可。Hadoop會根據這個類查找相關的JAR文件.
構造Job對象后,指定輸入(調用FileInputFormat類的靜態方法addInputPath(),可以多次調用實現多路徑輸入,路徑可以是單個文件、一個目錄、符合特定模式的一些列文件)、輸出(調用FileOutputFormat類的靜態方法setOutputPath(),只能一個,且在運行前不能存在)路徑。
接着setMapperClass、setReducerClass指定要使用的map類型、reduce類型。至於setCombinerClass根據需要來使用。combiner是對map的輸出在MR框架混洗后的分組結果,進行組內計算,減少需要傳遞給reduce函數的數據量。
setOutputKeyClass()、setOutputValueClass()控制reduce函數的輸出類型,並且必須和Reduce類產生的相匹配,map函數的輸出類型默認情況下和reduce函數是相同的,因此mapper產生出和reducer函數相同的類型時,不需要單獨設置map的輸出類型,否則需要通過setMapOutputKeyClass()、setMapOutputValueClass()方法來設置map函數的輸出類型。
文件的輸入輸出:
輸入的類型通過輸入格式來控制,setInputFormatClass(),如果不指定,則使用默認的格式TextInputFormat.class(文本輸入格式);
輸出的類型通過輸出格式來控制,setOutputFormatClass(),如果不指定,則使用默認的格式TextOutputFormat.class(文本輸入格式).
設置完成后,可以開始運行作業。waitForCompletion()方法提交作業並等待執行完成。其參數true表示作業會把其進度信息寫到控制台,返回結果是布爾值,true-成,false-敗。
這個例子的代碼不是最簡潔的,后續參照《Hadoo權威指南》再寫個 簡潔點的。
4.5Java io溫習
由於reduce函數的輸出目錄在運行前必須不存在,為方便調試代碼,不用每次都去手動刪目錄,寫了DelOutputDir類,在提交作業前執行刪除output目錄及其目錄下文件的方法。
這個方法要求output目錄里只有文件,不能有目錄,因為代碼未對子目錄作處理。
4.6 Intellij IDEA使用技巧
使用Intellij IDEA創建一個常規Java項目,導入外部依賴。參見1、2步。
這里未使用導入libraries的方法(這個是最推薦的方法,創建lib目錄,添加JAR文件,后續嘗試)。
5.MR程序打包提交到集群
Inerllij IDEA將程序打包成JAR文件,主要是做兩件事:1.創建MANIFEST.MF文件,這個文件指定MAIN CLASS的位置(包);2.將MANIFEST.MF和編譯后的class文件一起打包。
打開IDEA的項目結構 (CTRL+SHILT+ALT+S 或者 快捷圖標 ),在Artifacts菜單中新建一個空JAR文件,如果有就進行配置即可。流程步驟:(未完待續)
6.代碼、JAR包、測試數據下載
https://files.cnblogs.com/files/leeyuki/code.rar 代碼
JAR包大於10M,博客園傳不了,如需要自行下載或留言索取。