一:目的
之前面試曾遇到面試官讓用python代碼實現mapreduce中最簡單的demo WordCount,由於之前一直用java來寫hadoop程序,突然轉到python,是我產生了質疑,python與hadoop應該是不兼容的,即使寫出來程序,到時候怎麼運行?一頭霧水最後導致面試失敗。後來通過查閱資料,研究mapreduce的底層實現,發現儘管Hadoop框架是用Java編寫的,但是爲Hadoop編寫的程序不必非要Java寫,還可以使用其他語言開發,比如Python或C++(Haoop在0.14.1版本提供C++ API),而mapreduce只是一種思想,跟語言無關。$HADOOP_HOME/src/examples/python/WordCount.py,你就可以明白我的意思了。
現在將會採用python語言實現wordcount並在hadoop上運行實現。
二:Python代碼
map實現:
下面Python代碼的一個“竅門”是我們將使用Hadoop流API(可以看下相關的維基條目)來幫助我們通過STDIN(標準輸入)和STDOUT(標準輸出)在Map和Reduce代碼間傳遞數據。我們只是使用Python的sys.stdin讀取輸入數據和打印輸出到sys.stdout。這就是我們需要做的,因爲Hadoop流將處理好一切。將下面的代碼保存在文件 /home/hduser/mapper.py 中。它將從STDIN讀取數據,拆分爲單詞並輸出一組映射單詞和它們數量(中間值)的行到STDOUT。儘管這個Map腳本不會計算出單詞出現次數的總和(中間值)。相反,它會立即輸出( 1)元組的形式——即使某個特定的單詞可能會在輸入中出現多次。在我們的例子中,我們讓後續的Reduce做最終的總和計數。當然,你可以按照你的想法在你自己的腳本中修改這段代碼,但是,由於教學原因,我們在本教程中就先這樣做。:-)
請確保該文件具有可執行權限(chmod +x /home/hduser/mapper.py ),否則你會遇到問題。
reduce實現:
將下面的代碼保存在文件 /home/hduser/reducer.py 中。它將從STDIN讀取mapper.py的結果(因此mapper.py的輸出格式和reducer.py預期的輸入格式必須匹配),然後統計每個單詞出現的次數,最後將結果輸出到STDOUT中。
請確保該文件具有可執行權限(chmod +x /home/hduser/reducer.py ),否則你會遇到問題。
代碼測試(cat data | map | sort | reduce):
在MapReduce作業中使用它們之前,我建議先在本地測試你的mapper.py和reducer.py腳本。否則,你的作業可能成功完成了但沒有作業結果數據或得到了不是你想要的結果。如果發生這種情況,很有可能是你(或我)搞砸了。這裏有一些想法,關於如何測試這個Map和Reduce腳本的功能。
運行代碼:
下載示例輸入數據
下載每個文件爲純文本文件,以UTF-8編譯並且將這些文件存儲在一個臨時目錄中,如/tmp/gutenberg。
說明:你將需要在你的Cloudera虛擬機中打開瀏覽器。選擇適當的文件下載(UTF-8 版本),它將顯示在你的瀏覽器中。點擊鼠標右鍵按鈕來保存該文件。給它一個合適的名稱(如”Ulysses”),並注意它將保存在下載目錄中。
將本地示例數據拷貝到HDFS
在我們運行實際的MapReduce作業前,我們首先必須從我們本地文件系統中拷貝文件到Hadoop的HDFS內。
*說明:
我們假設你是在你的下載目錄中。我們必須在HDFS中創建一個子目錄,然後拷貝文件過來。最後,我們驗證拷貝文件成功。
首先,我們在HDFS中創建子目錄MyFirst:
[cloudera@quickstart Downloads]$ hadoop fs -mkdir MyFirst
然後,我們拷貝文件。注意,三個文件以.txt結尾:
[cloudera@quickstart Downloads]$ hadoop fs -copyFromLocal *.txt MyFirst
最後,我們驗證拷貝成功:
[cloudera@quickstart Downloads]$ hadoop fs -ls MyFirst
Found 3 items
-rw-r–r– 1 cloudera cloudera 1423803 2014-11-30 08:02 MyFirst/Leonardo.txt
-rw-r–r– 1 cloudera cloudera 674570 2014-11-30 08:02 MyFirst/OutlineOfScience.txt
-rw-r–r– 1 cloudera cloudera 1573150 2014-11-30 08:02 MyFirst/Ulysses.txt
運行MapReduce作業
*說明:
運行MapReduce作業,敲入如下命令:
[cloudera@quickstart ~]$ hadoop jar /usr/lib/hadoop-0.20-mapreduce/contrib/streaming/hadoop-streaming.jar -file mapper.py -mapper mapper.py
-file reducer.py -reducer reducer.py -input MyFirst/* -output MyFirst4-output
你會收到有關文件被棄用的警告,不用擔心。重要的是:當你發出這條命令時,輸出目錄(在這個示例中是MyFirst-output)不存在。
驗證這個程序工作正常。首先,輸入命令:hadoop fs -ls MyFirst4-output
[cloudera@quickstart ~]$ hadoop fs -ls MyFirst4-output
Found 2 items
-rw-r–r– 1 cloudera cloudera 0 2014-11-30 09:23 MyFirst4-output/_SUCCESS
-rw-r–r– 1 cloudera cloudera 880829 2014-11-30 09:23 MyFirst4-output/part-00000
然後,查看輸出文件:
[cloudera@quickstart ~]$ hadoop fs -cat MyFirst4-output/part-00000
將文件從HDFS中拷入到你本地文件系統中:
[cloudera@quickstart ~]$ hadoop fs -copyToLocal MyFirst4-output/part-00000
MyFirstOutputLocal.txt
現在,一切都準備好了,我們終於可以在Hadoop集羣上運行我們的Python MapReduce作業了。如上所述,我們使用Hadoop流API通過STDIN和STDOUT在Map和Reduce間傳遞數據。
如果你想要在運行的時候修改Hadoop參數,如增加Reduce任務的數量,你可以使用-D選項:
hduser@ubuntu:/usr/local/hadoop$ bin/hadoop jar contrib/streaming/hadoop-streaming.jar -D mapred.reduce.tasks=16 …
關於mapred.map.tasks說明:Hadoop does not honor mapred.map.tasks beyond considering it a hint。但是,Hadoop接受用戶指定mapred.reduce.tasks並且不操作。你不能強制指定mapred.map.tasks,但可以指定mapred.reduce.tasks。
這個任務將讀取HDFS目錄/user/hduser/gutenberg中的所有文件,處理它們,並將結果存儲在HDFS目錄/user/hduser/gutenberg-output中。一般情況下,Hadoop對每個reducer產生一個輸出文件;在我們的示例中,然而它將只創建單個文件因爲輸入的文件都很小。
在終端中前一個命令的輸出示例︰
使用Python語言寫Hadoop MapReduce程序
**譯者說明:截圖中的命令不完整,完整命令如下:
hduser@ubuntu:/usr/local/hadoop$ bin/hadoop jar contrib/streaming/hadoop-streaming.jar -mapper /home/hduser/mapper.py -reducer /home/hduser/reducer.py -input /user/hduser/gutenberg/* -output /user/hduser/gutenberg-output