標籤:
一、概述
近年來,大資料技術如火如荼,如何儲存海量資料也成了當今的熱點和痛點問題,而HDFSDistributed File System作為Hadoop項目的分布式儲存基礎,也為HBASE提供資料持久化功能,它在大資料項目中有非常廣泛的應用。
HadoopDistributed File System(Hadoop Distributed File System,HDFS)被設計成適合運行在通用硬體(commodity hardware)上的Distributed File System。HDFS是Hadoop項目的核心子項目,是一種具有高容錯性、高可靠性、高可擴充性、高輸送量等特徵的Distributed File System,可用於雲端運算或其它大資料應用中海量資料的儲存(主要為大檔案的儲存)。
本文結合作者本人及同事對HDFS的學習和實踐的理解,首先介紹HDFS的特點和重要SHELL命令(hadoop和hdfs命令)的使用,接著介紹HDFS提供的C提供者LIB HDFS及其跟普通檔案系統的C API的異同,然後介紹如何利用LIB HDFS介面實現簡單的HDFS用戶端並列舉相關應用執行個體,最後針對編寫HDFS用戶端中遇到的問題進行描述和分析。
二、HDFS簡介
HDFS是Hadoop項目的核心子項目,是一種具有高容錯性、高可靠性、高可擴充性、高輸送量等特徵的Distributed File System。
1.HDFS特點
HDFS作為一種Distributed File System,主要有以下特點:
1)主要用於儲存和管理大資料檔案(由於HDFS預設資料區塊為128M,所以它主要適合於儲存百M層級及以上大小的檔案)。
2)其資料節點可橫向擴充,且可選擇廉價的商業硬體。
3)設計理念為“一次寫,多次讀”。
4)當前不支援在檔案任意位置修改檔案內容,只能在檔案尾部執行append操作。
5)不適合低延遲(幾十毫秒)資料訪問應用(低延遲應用可以考慮HBASE分散式資料庫或者ES+Distributed File System的架構)。
2.HDFS常用SHELL命令簡介
hadoop有兩個非常重要的SHELL命令:hadoop和hdfs。對於管理HDFS檔案系統而言,hadoop和hdfs指令碼功能有很大的重複性,下面以分別對這兩個命令進行介紹。
1)hadoop命令使用
hadoop幾乎所有的管理命令都被整合到了一個SHELL指令碼中,即bin/hadoop指令碼,通過執行帶參數的hadoop指令碼,就可以實現hadoop模組的管理工作。由於本文主要介紹HDFS檔案系統,所以這裡就主要介紹如何使用hadoop指令碼操作HDFS檔案系統,相關命令及參數見圖1所示。
圖1 hadoop fs命令及參數描述
下面舉一個具體的例子。例如,要在HDFS檔案系統中查看根目錄包含的所有目錄或檔案,再建立test目錄,並賦予777許可權,最後刪除該目錄。實現以上操作的主要命令如下:
hadoop fs –ls /hadoop fs -mkdir /testhadoop fs –chmod –R 777 /testhadoop fs –rmdir /test
hadoop fs命令能夠實現使用者組及許可權的管理、目錄和檔案和管理、檔案的上傳和下載等功能,但對於HDFS檔案系統的檢查(包括壞塊的清理)、節點管理、快照管理和格式化等深層次管理工作就無能為力了,這裡就需要用到hdfs命令了。
2)hdfs命令使用
hdfs所有管理命令都被整合到了一個SHELL指令碼中,即bin/hdfs指令碼,通過執行帶參數的hdfs指令碼,就可以實現對HDFS檔案系統的管理工作,包括基本的檔案、目錄、許可權和屬組操作以及資料區塊管理和格式化等功能。
hdfs指令碼實現的功能非常強大,hdfs dfs命令跟hadoop fs命令功能完全一致,所以我們可以利用hdfs dfs命令並攜帶圖1中的參數實現hadoop fs的所有功能。下面主要介紹下HDFS檔案系統格式化和資料區塊管理操作。
格式化一個HDFS檔案系統,使用如下命令:
hdfs namenode -format
刪除HDFS檔案系統中存在的壞塊及相應已損壞的檔案,使用如下命令:
hdfs fsck -delete -files /
三、LIB HDFS介面簡介
Hadoop FileSystem APIs是JAVA CLIENT API,HDFS並沒有提供原生的C語言提供者。但HDFS提供了基於JNI的C調用介面LIB HDFS,為C語言訪問HDFS提供了很大的便利。
LIB HDFS介面的標頭檔和庫檔案已包含在Hadoop發行版本中,可以直接使用。它的標頭檔hdfs.h一般位於HADOOPHOME/include目錄中,而其庫文件libhdfs.so通常則位於{HADOOP_HOME}/lib/native目錄中。因為Hadoop版本一直在更新中,所以不同版本的LIB HDFS介面功能通常不太一樣,主要表現為功能遞增的現象。
通過LIB HDFS訪問HDFS檔案系統與使用C語言API訪問普通作業系統的檔案系統類似,但還存在一些不足的地方,具體如下所示:
a)LIB HDFS介面實現的功能只是JAVA CLIENT API功能的一個子集,且跟JAVA CLIENT API相比可能還存在不少BUG未被發現或修複,如多線程、多進程訪問檔案系統時控制代碼資源釋放的問題。
b)另外由於是使用JNI方式調用JAVA CLASS,所以應用程式佔用記憶體較多,而且該介面運行可能會產生大量異常日誌,怎麼管理這些日誌是個問題。另外,當操作HDFS檔案系統出錯時,errno不一定會有正確的提示,也會增加排查問題的難度。
c)目前LIB HDFS可參考用例較少,而利用多線程等方式通過LIB HDFS大資料量讀寫HDFS檔案系統的用例更是少之又少。
d)目前LIB HDFS不支援在任意位置修改檔案內容,只能在檔案末尾執行append操作,或對整個檔案執行truncate操作,這個主要跟HDFS檔案系統設計有關,大資料存放區一般都缺乏更新功能的支援,這點我們只能通過業務層面來規避了。
e)由低到高Hadoop發行版本攜帶的LIB HDFS功能可能呈現遞增的情況,所以每當Hadoop版本更新了,都需要重新編譯我們的應用程式,增加升級難度。
目前雖然LIB HDFS介面還存在一些不足的地方,但相信未來隨著該介面版本的不斷更新,其功能和穩定性都會大大提高。
四、C語言訪問HDFS應用實踐
1.編譯和運行環境搭建
為了成功編譯C語言用戶端程式,我們需要預先安裝7.0及以上版本的JAVA JDK和Hadoop發行版,前者提供libjvm.so等庫,後者則提供LIB HDFS串連所需要的庫。
為了成功運行C語言用戶端程式,除了預先安裝上面提到的程式外,我們還需要正確地設定幾個關鍵環境變數,包括LD_LIBRARY_PATH和CLASSPATH的設定。
關於LD_LIBRARY_PATH環境變數,主要是需要添加libjvm.so和libhdfs.so庫所在路徑;而針對CLASSPATH則需要囊括Hadoop提供的所有jar包的全路徑資訊(具體可通過find+awk組合命令來實現),否則C語言用戶端程式總會報缺少某個類而無法啟動並執行錯誤。
2.LIB HDFS介面簡單應用實踐
這裡主要介紹部分API的使用樣本。
1)擷取HDFS檔案系統的容量和已使用空間大小資訊如GetHdfsInfo函數所示:
void GetHdfsInfo(void){ hdfsFS pfs = NULL; int iRet = 0;tOffset iTmp = 0; pfs = hdfsConnect("hdfs://127.0.0.1:9000/", 0); // 與HDFS檔案系統建立串連 if (NULL == pfs) { WRITELOGEX(LOG_ERROR, ("GetHdfsInfo(): hdfsConnect failed! errno=%d.", errno)); return; }WRITELOG(LOG_INFO, "GetHdfsInfo(): hdfsConnect success!"); iTmp = hdfsGetCapacity(pfs); // 擷取HDFS檔案系統容量 if (-1 == iTmp) { WRITELOGEX(LOG_ERROR, ("GetHdfsInfo(): hdfsGetCapacity failed! errno=%d.", errno)); hdfsDisconnect(pfs); pfs = NULL; return; }WRITELOGEX(LOG_INFO, ("GetHdfsInfo(): hdfsGetCapacity success! offset=%ld.", iTmp)); iTmp = hdfsGetUsed(pfs); // 擷取HDFS檔案系統中所有檔案佔用空間大小,即已使用量 if (-1 == iTmp) { WRITELOGEX(LOG_ERROR, ("GetHdfsInfo(): hdfsGetUsed failed! errno=%d.", errno)); hdfsDisconnect(pfs); pfs = NULL; return; } WRITELOGEX(LOG_INFO, ("GetHdfsInfo(): hdfsGetUsed success! offset=%ld.", iTmp)); iRet = hdfsDisconnect(pfs); // 關閉與HDFS檔案系統的串連 if (-1 == iRet) { WRITELOGEX(LOG_ERROR, ("GetHdfsInfo(): hdfsDisconnect failed! errno=%d.", errno)); return; } WRITELOGEX(LOG_INFO, ("GetHdfsInfo(): hdfsDisconnect success! ret=%d.", iRet));pfs = NULL; return;}
2)在HDFS檔案系統中新增檔案並寫入資料如HdfsWriteTest函數所示:
void HdfsWriteTest(hdfsFS pfs){ int iRet = 0; hdfsFile pfile = NULL; char szTestFile[200] = "/test/ write.test"; if (NULL == pfs) { WRITELOG(LOG_ERROR, "HdfsWriteTest():pfs is null."); return; } pfile = hdfsOpenFile(pfs, szTestFile, O_WRONLY || O_CREAT, 0, 0, 0); // 開啟檔案控制代碼 if (NULL == pfile) { WRITELOGEX(LOG_ERROR, ("HdfsWriteTest(): hdfsOpenFile failed! szFilePath=%s,errno=%d.", szTestFile, errno)); return; } WRITELOGEX(LOG_INFO, ("HdfsWriteTest(): hdfsOpenFile success! szFilePath=%s.", szTestFile)); iRet = hdfsWrite(pfs, pfile, "hello world!", strlen("hello world!")); // 寫入資料 if (-1 == iRet) { WRITELOGEX(LOG_ERROR, ("HdfsWriteTest(): hdfsWrite failed! ret=%d,errno=%d.", iRet, errno));hdfsCloseFile(pfs, pfile); pfile = NULL; return; }WRITELOGEX(LOG_INFO, ("HdfsWriteTest(): hdfsWrite success! ret=%d.", iRet)); iRet = hdfsHFlush(pfs, pfile); // 將緩衝區中資料寫入磁碟 if (-1 == iRet) { WRITELOGEX(LOG_ERROR, ("HdfsWriteTest(): hdfsFlush failed! ret=%d,errno=%d.", iRet, errno));hdfsCloseFile(pfs, pfile); pfile = NULL; return;} WRITELOGEX(LOG_INFO, ("HdfsWriteTest(): hdfsFlush success! ret=%d.", iRet)); iRet = hdfsCloseFile(pfs, pfile); // 關閉檔案控制代碼,釋放資源 if (-1 == iRet) { WRITELOGEX(LOG_ERROR, ("HdfsWriteTest(): hdfsCloseFile failed! ret=%d,errno=%d.", iRet, errno)); return; } WRITELOGEX(LOG_INFO, ("HdfsWriteTest(): hdfsCloseFile success! ret=%d.", iRet));pfile = NULL; return;}
3.遇到的主要問題描述與分析
對於LIB HDFS介面的不足之處,在本文第三部分(LIB HDFS介面簡介)已有大致描述。
在實際效能測試過程中,因LIB HDFS介面引起的問題主要包括:lease租約回收異常和程式控制代碼資源釋放異常等兩大類。
我們換了多種測試模型,基本確認LIB HDFS介面在某些異常情況下(如頻繁對同一個檔案執行append操作)會產生上述問題。所以如果在項目中需要實際應用LIB HDFS介面,就需要我們改進用戶端程式處理流程,盡量規避和減少上述問題的產生。可以採用如下方法:
1)在用戶端程式和HDFS檔案系統間增加緩衝的方式降低HDFS的讀寫密度;
2)減少對HDFS檔案系統的更新操作,例如檔案寫入完成後就不再執行append操作,只執行read操作。
五、總結
本文對HDFS和用C語言訪問HDFS的操作進行了詳細的介紹,可供相關項目的開發人員參考。
HDFS作為一種Distributed File System,並不是萬能的,例如並不適合於儲存量太小或要求低訪問延遲的應用情境,又或者需要頻繁更新資料的系統。即使應用了HDFS檔案系統,為了發揮HDFS檔案系統的最大效率,仍可能需要通過我們修改業務分層或邏輯實現等手段來規避HDFS的一些缺點。
本人公眾號:zhouzxi,請掃描以下二維碼:
著作權聲明:本文為博主原創文章,未經博主允許不得轉載。
HDFS簡介及用C語言訪問HDFS介面操作實踐