在用Hadoop框架處理大數據時使用最多就是HDFS--分布式文件系統,但Hadoop的文件系統不僅只有分布式文件系統,例如:hfs,HSFTP,HAR等在Hadoop中都是有集成的,用來處理存儲在不同體系中的數據。事實上應該這么說,Hadoop其實是一個綜合性的文件系統。
下面來看看文件系統的結構體系

當然上面的UML圖解事實上有些冗余,但是為了能清楚的表達fs這個體系中的成員,我盡量把所有的成員羅列了進來。在Hadoop的文件系統體系中FileSystem是其他成員的父類,它是一個抽象類,旨在通過它來定義一個訪問文件系統的標准,在Hadoop框架中的其他組件在使用到文件系統時只認識FileSystem類提供的標准,所以如果自己在定義一個文件系統是定要按照這個標准來(當然,說說而已,我也沒嘗試過),面對抽象編程在我看來就是按照某些標准來編程。至於在FileSystem的子類中,到底僅僅是實現了抽象的方法,還是同時也重寫了父類的方法,我覺得不重要,最重要的是明白這種設計思想。
在使用文件系統是一般的通過FileSystem.get(Configuration conf)或者是FileSystem.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) { // no scheme: use default FS return get(conf); } if (authority == null) { // no authority 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)) { return createFileSystem(uri, conf); } return CACHE.get(uri, conf); } /** Returns the configured filesystem implementation.*/ public static FileSystem get(Configuration conf) throws IOException { return get(getDefaultUri(conf), conf); } public static URI getDefaultUri(Configuration conf) { return URI.create(fixName(conf.get(FS_DEFAULT_NAME_KEY, "file:///"))); }
在調用FileSystem.get(Configuration conf)的時候,事實上還是調用了FileSystem.get(URI uri,Configuration conf),使用FileSystem.get(Configuration conf)來獲取文件系統的實例時會去讀取配置文件core-site.xml中鍵fs.default.name對應的value,如果不存在該項配置,默認使用的file:///也就是本地文件系統。使用FileSystem.get(conf)方法時,會去讀取配置文件,這樣在后期更換文件系統時就很方便了。當然使用FileSystem.get(uri,conf)時也可以很好的實現后期文件系統的修改,直接在程序運行時加入參數就行了。現在剩下的問題就落在FileSystem.get(uri,conf)的頭上了,大權握在它的手上了。來看看通過這個方法是怎么得到具體的文件系統實例的,在驗證uri的結構完整后,通過createFileSystem(uri, conf)來返回FileSystem的實例,在CACHE.get(uri, conf)方法的內部同樣也會調用createFileSystem(uri, conf),那么問題就落在了這個方法上了,下面來看看createFileSystem(uri, conf)的源碼,就明白了是怎么回事了
private static FileSystem createFileSystem(URI uri, Configuration conf ) throws IOException { Class<?> clazz = conf.getClass("fs." + uri.getScheme() + ".impl", null); LOG.debug("Creating filesystem for " + uri); if (clazz == null) { throw new IOException("No FileSystem for scheme: " + uri.getScheme()); } FileSystem fs = (FileSystem)ReflectionUtils.newInstance(clazz, conf); fs.initialize(uri, conf); return fs; }
這下就明了了,Class<?> clazz = conf.getClass("fs." + uri.getScheme() + ".impl", null);通過讀取core-default.xml中fs.[scheme].impl的value值來得到FileSystem實現類的字節碼文件,有了Class文件其他的事情就好說了,此時相當於是把整個類的所有細節都暴露給programmer了,通過反射技術操作Class從而就得到了文件系統的實例。下面是core-default.xml中關於這部分的配置片段
<property> <name>fs.file.impl</name> <value>org.apache.hadoop.fs.LocalFileSystem</value> <description>The FileSystem for file: uris.</description> </property> <property> <name>fs.hdfs.impl</name> <value>org.apache.hadoop.hdfs.DistributedFileSystem</value> <description>The FileSystem for hdfs: uris.</description> </property> <property> <name>fs.s3.impl</name> <value>org.apache.hadoop.fs.s3.S3FileSystem</value> <description>The FileSystem for s3: uris.</description> </property> <property> <name>fs.s3n.impl</name> <value>org.apache.hadoop.fs.s3native.NativeS3FileSystem</value> <description>The FileSystem for s3n: (Native S3) uris.</description> </property> <property> <name>fs.kfs.impl</name> <value>org.apache.hadoop.fs.kfs.KosmosFileSystem</value> <description>The FileSystem for kfs: uris.</description> </property> <property> <name>fs.hftp.impl</name> <value>org.apache.hadoop.hdfs.HftpFileSystem</value> </property> <property> <name>fs.hsftp.impl</name> <value>org.apache.hadoop.hdfs.HsftpFileSystem</value> </property> <property> <name>fs.webhdfs.impl</name> <value>org.apache.hadoop.hdfs.web.WebHdfsFileSystem</value> </property> <property> <name>fs.ftp.impl</name> <value>org.apache.hadoop.fs.ftp.FTPFileSystem</value> <description>The FileSystem for ftp: uris.</description> </property> <property> <name>fs.ramfs.impl</name> <value>org.apache.hadoop.fs.InMemoryFileSystem</value> <description>The FileSystem for ramfs: uris.</description> </property> <property> <name>fs.har.impl</name> <value>org.apache.hadoop.fs.HarFileSystem</value> <description>The filesystem for Hadoop archives. </description> </property>
在上面的文件系統體系圖中有三處被標記的類,下面通過這三個類來說說文件系統能否支持驗證檢驗和的設置。什么是檢驗和?檢驗和是在文件寫入或者是讀取階段用來驗證數據大小是否存在差池的數據統計信息(姑且這么說),檢驗和的作用主要是保證數據的完整性。在hadoop中保證數據的完整性就是通過讀寫時對檢驗和的驗證來實現。在hadoop的FS中,不是所有的底層文件系統都支持驗證檢驗和的。通過LocalFileSystem和RawLocalFileSystem來說明什么樣的底層文件系統支持驗證檢驗和。
LocalFileSystem是一個支持驗證檢驗和的文件系統
public LocalFileSystem() { this(new RawLocalFileSystem()); } public FileSystem getRaw() { return rfs; } public LocalFileSystem(FileSystem rawLocalFileSystem) { super(rawLocalFileSystem); rfs = rawLocalFileSystem; }
上面是LocalFileSystem的構造函數,從構造函數可以看出,LocalFileSystem底層使用就是RawLocalFileSystem(不支持驗證檢驗和),在這里應該可以看出此處使用的設計模式中的裝飾模式。

從圖中可以看出LocalFileSystem並沒直接實現FileSystem,並且在hadoop權威指南中這樣說的在hadoop的FS中繼承了ChecksumFileSystem的FS才具有驗證檢驗和的能力,不難想象,驗證和的功能是在FilterFileSystem中完成的。看看源碼就知道這種設計模式是策略模式,FilterFileSystem只對繼承了FileSystem的類感興趣。下面簡單的看下ChecksumFileSystem和FilterFileSystem的構造函數。
ChecksumFileSystem
public ChecksumFileSystem(FileSystem fs) { super(fs); }
FilterFileSystem
protected FileSystem fs; /* * so that extending classes can define it */ public FilterFileSystem() { } public FilterFileSystem(FileSystem fs) { this.fs = fs; this.statistics = fs.statistics; }
從上面的三個類構造函數可以看出,RawLocalFileSystem的實例最終存儲在FilterFileSystem的protected字段FileSystem fs中,然后調用FilterFileSystem的訪問文件系統的相關方法實際上最后都避免不了調用字段fs對應的方法。so,hadoop中的FS繼承了ChecksumFileSystem的就具有驗證檢驗和的能力,例如還有InMemoryFileSystem也支持驗證檢驗和。那么對於hadoop中其他的沒有繼承ChecksumFileSystem的文件系統該怎么支持驗證檢驗和?從checkSumFileSystem和FilterFileSystem的構造函數不能看出,將任何一個繼承了FileSystem的類包裝下就可以實現驗證檢驗和的功能。如想對DistributedFileSystem的實例添加驗證檢驗和的能力,使用 new ChecksumFileSystem(dfs)就ok了,我把ChecksumFileSystem這個類稱作文件系統的外套,披上了這層外套文件系統就變得更加強壯。
以上的內容是對自己學習hadoop的階段總結,我在這里拋磚引玉,有錯誤的地方希望大家幫忙指出哈。
