Hadoop-2.4.1學習之FileSystem及實戰
一提到hadoop檔案系統,通常想到的就是HDFS,即Hadoop Distributed File System,但除了HDFS外,hadoop還支援其它型別的檔案系統,比如Amazon S3、Swift檔案系統等,而這些檔案系統都擴充套件自抽象基礎類FileSystem,該類提供了豐富的方法用於對檔案系統進行操作,比如建立目錄、刪除檔案、重新命名等。無論使用的是HDFS還是Swift檔案系統,或者其它所支援的檔案系統,推薦在應用程式使用FileSystem物件引用實際的檔案系統,比如FileSystem local= FileSystem.getLocal(Configuration conf),該語句返回LocalFileSystem。FileSystem的類層次關係如下圖所示,其中將重點學習紅色標記的DistributedFileSystem,其它檔案系統只進行簡要的概述。
FTPFileSystem,由Apache Commons Net提供的由FTP客戶端支援的檔案系統。S3FileSystem,由Amazon S3支援的基於block的檔案系統。NativeS3FileSystem,用於讀寫儲存在Amazon S3中檔案的檔案系統,與S3FileSystem不同的是,該實現將檔案按照原始格式儲存在S3,這樣其它S3工具可以讀取檔案。RawLocalFileSystem,原始的本地檔案系統,而繼承自ChecksumFileSystem的LocalFileSystem則為要計算校驗和的檔案系統。ChecksumFileSystem實現了一個客戶端掛載表,該類的規則和實現和ViewFs是相同的,詳細內容可以參考《
在簡要概述了FileSystem及其子類後,回過頭再詳細學習一下該類的重要方法。由於該類為抽象類,故沒有辦法直接建立該類的物件,但該類提供了靜態方法用於建立物件,分別為:
方法 | 描述 |
get(Configuration conf) | 根據conf獲取具體的檔案系統物件 |
get(URI uri, Configuration conf) | 基於uri和conf建立檔案系統物件 |
get(URI uri, Configuration conf, String user) | 基於uri,conf和user獲取檔案系統 |
getLocal(Configuration conf) | 獲取本地檔案系統 |
newInstance(Configuration conf) | 返回唯一的檔案系統物件,該方法總是返回新的物件 |
newInstance(URI uri, Configuration conf) | 基於uri返回新的檔案系統物件 |
newInstance(URI uri, Configuration conf, String user) | 基於uri,conf和user獲取檔案系統 |
newInstanceLocal(Configuration conf) | 返回新的本地檔案系統物件 |
同樣為獲取檔案系統物件的方法,get和newInstance有什麼不同呢?從描述可知,newInstance總是返回新的檔案系統物件,那get呢?要想了解其中的具體區別,最好的方法莫過於檢視原始碼了,首先檢視get(URI uri, Configuration conf)的原始碼:
public static FileSystem get(URI uri, Configuration conf) throws IOException {
String scheme = uri.getScheme();
String authority = uri.getAuthority();
if (scheme == null && authority == null) {// use default FS
//根據fs.defaultFS的值獲取檔案系統,若未設定該引數則根據file:///返回檔案系統
return get(conf);
}
if (scheme != null && authority == null) {// no authority
//根據fs.defaultFS的值建立URI,若未設定則使用file:///建立URI
URI defaultUri = getDefaultUri(conf);
if (scheme.equals(defaultUri.getScheme()) // if scheme matches default
&& defaultUri.getAuthority() != null) { // & default has authority
return get(defaultUri, conf); // return default
}
}
String disableCacheName = String.format("fs.%s.impl.disable.cache", scheme);
if (conf.getBoolean(disableCacheName, false)) {
//根據uri和conf建立FileSystem
return createFileSystem(uri, conf);
}
//若未設定快取引數為true,則預設從CACHE中獲取檔案系統物件
return CACHE.get(uri, conf);
}
從上面的程式碼可以得知,get方法不是每次都建立FileSystem物件,會從快取中獲取FileSystem物件,而newInstance方法則會每次都建立新物件。所以在使用該物件的API程式設計時,推薦使用get方法。在FileSystem中除了上述獲取物件的靜態方法外,還有一些其它方法,比如用於建立目錄的mkdirs,移動檔案的moveFromLocalFile和moveToLocalFile,複製檔案的copyToLocalFile和copyFromLocalFile,建立檔案的create等,這些方法都多個過載方法,可以根據需要選擇合適的方法。需要注意的是create方法並未建立檔案,而是返回了FSDataOutputStream物件,使用該物件就可以向檔案中寫入資料。
除了FileSystem外,還有幾個類需要了解,分別為Path、FileStatus、FsStatus。其中FsStatus用於表示檔案系統的容量,已用和未用空間,方法分別為getCapacity、getUse和getRemaining。FileStatus用於獲取檔案系統的元資料,比如檔案大小,塊大小,檔案所有者,是否為目錄或者檔案等。Path表示檔案系統的一個檔案或者目錄,也就是Path物件對應了檔案系統的一個路徑,該路徑既可以是檔案也可以是目錄,如果以/開頭,那麼Path所表達的為絕對路徑。Path提供了一些用於判斷是否為根路徑、是否絕對路徑的方法,還有用於getName和toUri等方法。
最後綜合上述知識,編寫一小段簡單的程式碼,該程式碼演示了部分方法的使用方式,更多內容可以參考官方API:
public class FileSystemTest {
public static void main(String[] args) {
Configuration conf = new Configuration();
try {
FileSystem local = FileSystem.getLocal(conf);
FileSystem hdfs = FileSystem.get(URI.create("hdfs://localhost:9000"),conf);
FsStatus fsLocal = local.getStatus();
FsStatus fsHdfs = hdfs.getStatus();
System.out.println("Capacity: " + fsLocal.getCapacity()/1024/1024/1024 +"GB");
System.out.println("Remaining :" + fsLocal.getRemaining()/1024/1024/1024 +"GB");
System.out.println("Used: " + fsLocal.getUsed()/1024/1024/1024 +"GB");
System.out.println();
System.out.println("Capacity: " + fsHdfs.getCapacity()/1024/1024/1024 +"GB");
System.out.println("Remaining :" + fsHdfs.getRemaining()/1024/1024/1024 +"GB");
System.out.println("Used: " + fsHdfs.getUsed()/1024/1024/1024 +"GB");
Path hdfsPath = new Path("/user/hadoop/");
lsFile(hdfsPath,hdfs);
} catch (IOException e) {
e.printStackTrace();
}
}
public static void lsFile(Path path,FileSystem fsystem) throws IOException{
FileStatus fs = fsystem.getFileStatus(path);
if(fs.isDirectory()){
System.out.println("Directory: " + fs.getPath().toString());
FileStatus[] fss = fsystem.listStatus(path);
for(FileStatus f : fss){
lsFile(f.getPath(),fsystem);
}
}else{
System.out.println("File Name is: " + fs.getPath().toString() +",Size is: " + fs.getLen()/1024 + "KB");
}
}
}