在用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的阶段总结,我在这里抛砖引玉,有错误的地方希望大家帮忙指出哈。
hadoop文件系统体系