hadoop官方的二進制發布版本一直是32位平台編譯的,對於java來說跨平台不影響使用,但是為了在c/c++程序中操作hdfs就做不到了,因為libhdfs.so是二進制不兼容的。
我使用的是stable版本的hadoop 2.20,直接從官方下載了二進制發布版本,在目錄lib/native下有操作hadoop的c/c++庫(.a和.so),但由於服務器是64位的原因無法鏈接。
解決方法就是下載hadoop的src源碼,自行編譯生成so,這個過程如果沒有Yum會比較繁瑣,因為依賴很多,在編譯過程中會遇到若干錯誤,后續會將編譯步驟完善起來,這里只說明libhdfs.so如何編譯生成一個可運行的程序。
libhfds是基於libjvm運行的,也就是創造了一個java虛擬機然后直接調用hadoop的java jar包實現hdfs系統的訪問,所以在編譯程序時一定要注意以下幾點要素:
1, 如果你是64位系統,必須自行編譯hadoop生成64位版本的libhfds.so,如果你是32位系統那就不必了,可以直接使用目錄下的lib/native里的so。
2, 編譯時一定要鏈接libjvm.so,這個so在jdk/jre/lib/amd64/server/下面,其中amd64目錄代表我是64位系統,你需要根據你的系統選擇特定目錄下的libjvm.so。
3, 由於libhdfs直接基於jvm加載jar包,而jvm是依靠系統環境變量CLASSPATH尋找jar包的,所以你一定要export導出CLASSPATH環境變量,保證它包含了java jdk和jre的所有jar包以及hadoop/shared/hadoop內的所有jar包,這樣運行時jvm才能正確的load到jar包,否則會拋出java異常令程序終止。
4,由於程序依賴so動態庫,為了運行時能夠找到so,還需要導出libhdfs.so和libjvm.so的路徑到LD_LIBRARY_PATH變量中去。
下面是一段簡單的示例代碼,打開一個hdfs文件並向文件寫入了hello hdfs,之后關閉文件,重點關注其編譯命令:
#include "hdfs.h" int main(int argc, char **argv) { hdfsFS fs = hdfsConnect("10.46.120.32", 8010); if (!fs) { fprintf(stderr, "connect fail\n"); return -1; } hdfsFile writeFile = hdfsOpenFile(fs, "/first.txt", O_WRONLY, 4096, 0, 0); if (!writeFile) { fprintf(stderr, "openfile fali\n"); return -1; } hdfsWrite(fs, writeFile, "hello hdfs", 10); hdfsCloseFile(fs, writeFile); hdfsDisconnect(fs); return 0; }
編譯命令如下:
gcc -o hdfs hdfs.c -L /hadoop/lib/native -lhdfs -I /hadoop/include -L /jdk/jre/lib/amd64/server/ -ljvm
下面的代碼足以完成所有環境變量的初始化,不需要再做額外的任何工作,建議填好后放到.bashrc中,這里假設hadoop安裝在/hadoop中,jdk安裝在/jdk中,libhdfs在hadoop默認路徑下:
export JAVA_HOME=/jdk #jdk安裝路徑
export JRE_HOME=${JAVA_HOME}/jre #jre路徑
export LD_LIBRARY_PATH=/jdk/jre/lib/amd64/server:/hadoop/lib/native # libjvm.so目錄和libhdfs.so目錄
CLASSPATH=${JAVA_HOME}/lib:${JRE_HOME}/lib # jdk與jre的jar
HADOOP_HOME=/hadoop #hadoop安裝路徑
CLASSPATH=${CLASSPATH}":"`find ${HADOOP_HOME}/share/hadoop | awk '{path=path":"$0}END{print path}'` # hadoop的jar
export CLASSPATH