1、概述
近年來,大數據技術如火如荼,怎樣存儲海量數據也成了當今的熱點和難點問題,而HDFS分佈式文件系統做爲Hadoop項目的分佈式存儲基礎,也爲HBASE提供數據持久化功能,它在大數據項目中有很普遍的應用。
Hadoop分佈式文件系統(Hadoop Distributed File System。HDFS)被設計成適合運行在通用硬件(commodity hardware)上的分佈式文件系統。HDFS是Hadoop項目的核心子項目,是一種具備高容錯性、高可靠性、高可擴展性、高吞吐量等特徵的分佈式文件系統,可用於雲計算或其餘大數據應用中海量數據的存儲(主要爲大文件的存儲)。
本文結合做者本人及同事對HDFS的學習和實踐的理解,首先介紹HDFS的特色和重要SHELL命令(hadoop和hdfs命令)的使用。接着介紹HDFS提供的C訪問接口LIB HDFS及其跟普通文件系統的C API的異同。而後介紹怎樣利用LIB HDFS接口實現簡單的HDFSclient並列舉相關應用實例,最後針對編寫HDFSclient中遇到的問題進行描寫敘述和分析。node
2、HDFS簡單介紹
HDFS是Hadoop項目的核心子項目。是一種具備高容錯性、高可靠性、高可擴展性、高吞吐量等特徵的分佈式文件系統。數據庫
1.HDFS特色
HDFS做爲一種分佈式文件系統,主要有如下特色:
1)主要用於存儲和管理大數據文件(由於HDFS默認數據塊爲128M。因此它主要適合於存儲百M級別及以上大小的文件)。
2)其數據節點可橫向擴展,且可選擇便宜的商業硬件。緩存
3)設計理念爲「一次寫,屢次讀」。微信
4)當前不支持在文件任何位置改動文件內容。僅僅能在文件尾部運行append操做。
5)不適合低延遲(幾十毫秒)數據訪問應用(低延遲應用可以考慮HBASE分佈式數據庫或者ES+分佈式文件系統的架構)。markdown
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權限,最後刪除該文件夾。實現以上操做的主要命令例如如下:app
hadoop fs –ls /
hadoop fs -mkdir /test
hadoop fs –chmod –R 777 /test
hadoop fs –rmdir /test
hadoop fs命令可以實現用戶組及權限的管理、文件夾和文件和管理、文件的上傳和下載等功能,但對於HDFS文件系統的檢查(包含壞塊的清理)、節點管理、快照管理和格式化等深層次管理工做就無能爲力了,這裏就需要用到hdfs命令了。jvm
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 /
3、LIB HDFS接口簡單介紹
Hadoop FileSystem APIs是JAVA CLIENT API。HDFS並無提供原生的C語言訪問接口。
但HDFS提供了基於JNI的C調用接口LIB HDFS,爲C語言訪問HDFS提供了很大的便利。
LIB HDFS接口的頭文件和庫文件已包含在Hadoop發行版本號中,可以直接使用。它的頭文件hdfs.h通常位於
由於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接口還存在一些不足的地方。但相信將來隨着該接口版本號的不斷更新,其功能和穩定性都會大大提升。
4、C語言訪問HDFS應用實踐
1.編譯和運行環境搭建
爲了成功編譯C語言client程序,咱們需要預先安裝7.0及以上版本號的JAVA JDK和Hadoop發行版,前者提供libjvm.so等庫,後者則提供LIB HDFS鏈接所需要的庫。
爲了成功運行C語言client程序,除了預先安裝上面提到的程序外,咱們還需要正確地設置幾個關鍵環境變量。包含LD_LIBRARY_PATH和CLASSPATH的設置。
關於LD_LIBRARY_PATH環境變量。主要是需要加入libjvm.so和libhdfs.so庫所在路徑;而針對CLASSPATH則需要囊括Hadoop提供的所有jar包的全路徑信息(詳細可經過find+awk組合命令來實現)。不然C語言client程序總會報缺乏某個類而沒法運行的錯誤。
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接口,就需要咱們改進client程序處理流程,儘可能規避和下降上述問題的產生。可以採用例如如下方法:
1)在client程序和HDFS文件系統間添加緩存的方式下降HDFS的讀寫密度;
2)下降對HDFS文件系統的更新操做,好比文件寫入完畢後就再也不運行append操做,僅僅運行read操做。
5、總結
本文對HDFS和用C語言訪問HDFS的操做進行了詳細的介紹,可供相關項目的開發者參考。
HDFS做爲一種分佈式文件系統,並不是萬能的,好比並不適合於存儲量過小或要求低訪問延遲的應用場景,又或者需要頻繁更新數據的系統。即便應用了HDFS文件系統,爲了發揮HDFS文件系統的最大效率,仍可能需要經過咱們改動業務分層或邏輯實現等手段來規避HDFS的一些缺點。
本人微信公衆號:zhouzxi,請掃描如下二維碼: