在撰寫Map Reduce Job時,我想 java.lang.ClassNotFoundException 這個問題一定排名前幾名,會發生這個問題的主要原因就是我們在寫Map Reduce Job時,可能會引用到外部 Jar 檔(3rd party Libraries),而運行Map Reduce Job的那一台機器的class path 找不到那個Jar檔而發生的問題。
當遇到這個問題大家一定馬上就會去Goolge找解決方法,而且很容易就會在網路上找到各種解法,但是很多解法你一看就會覺得很骯髒很暴力,下面是排名最常見的解法:
1. 把需要用到的Jar 檔 copy到每一台機器去,並且設定Class Path
其實這些建議都是正確的,但是只適合在開發階段和單機環境,一旦到了真正的叢集運行環境就不能這樣搞了,試想想100台以上的Cluster那不是要Copy到死!?而且一但Jar檔如果要升級和更新,那更是一場災難
2. 透過Hack的招數,寫程式把會用到的外部Jar檔Class Path找出來
太暴力了,直接跳過~:P
3. 最建議的方法是使用Hadoop Distributed Cache
- How to Include Third-Party Libraries in Your MapReduce Job
- Handling dependencies and configuration in Java + Hadoop projects efficiently
- Taiwan Hadoop Forum - Hadoop lib classpath, classNotFound的問題
作法如下:
1. 把所需要的Jar檔Copy到Hdfs路徑下
# bin/hadoop fs -copyFromLocal mylib.jar /lib/mylib.jar or # bin/hadoop fs -put myjar.jar /lib/.
2. 在你的程式裡面設定Distributed Cache 設定
JobConf job = new JobConf(); DistributedCache.addCacheFile(new URI("/lib/mylib.jar#mylib.jar"), job); DistributedCache.addCacheArchive(new URI("/lib/mylib.jar#mylib2.jar", job);
嗯看起來這樣就可以解決問題了!不過還是不夠方便,因為這時候已經把Lib 的名稱和路徑以及數量 Hard Code在程式裡面了,如果要改路徑怎麼辦?如果Lib要升級該怎麼辦?所以這時候就有請Spring Hadoop 出場~~
真的非常簡單,只要在Spring 的設定裡面加入下面內容,這樣甚至都不用寫在程式裡面,因為透過Spring Hadoop的設定,直接就把這些值寫入Hadoop Configuration 裡面了。
<hdp:cache create-symlink="true"> <hdp:classpath value="/lib/mylib.jar#mylib.jar" /> <hdp:classpath value="/lib/mylib.jar#mylib2.jar" /> </hdp:cache>
雖然看起來很簡單的幾行,卻花了好多時間才找到正確的用法,與最方便得設定方式....
深深覺得網路上關於Hadoop的教學資源大多只是極度簡化的範例程式(最常見的就是word count) 但是離開發真實商業應用系統還非常的遠,而且相關的文章更是少的可憐...Orz..
Reference:
[1] Hadoop Distributed Cache tutorial