分析的Hadoop目前最新版本-2.7.1。
ServiceLoader在FileSystem类的使用
客户端通过调用FileSystem对象的open方法来打开希望读取的文件。
FileSystem是一个抽象类。
首先,给出Hadoop的一个简单的代码如下:
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.LocalFileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.hdfs.DistributedFileSystem;
import org.apache.hadoop.io.IOUtils;
public class OpenFileSrc {
public static void main(String[] args) throws IOException,
URISyntaxException {
String uri = "file:///Users/arthur/Desktop/test.txt";
FileSystem dfs = FileSystem
.get(new URI(uri), new Configuration());
InputStream fsinput = dfs.open(new Path(uri));
System.out.println(dfs instanceof LocalFileSystem);
IOUtils.copyBytes(fsinput,System.out,4,true);
//read方法返回的是读取的下一个字节的ascii码
// System.out.println(fsinput.read());
}
}
研究的重点在于下面的代码。
FileSystem dfs = FileSystem.get(new URI(uri), new Configuration());
关联代码以后我们点进去查看它是如何工作的?
Step 1:
Step 2:
Step 3:
Step 4:
Step 5:
第五步时,我们可以通过SERVICE_FILE_SYSTEMS这个Map类来获取FileSytem类。那么问题来了,Map的初始化工作是怎么做的?
注意到下面这个类,这里利用了ServiceLoader类进行了加载。使用这个类的好处在于,可以很方便的给Hadoop添加我们自己的文件系统。关于ServiceLoader的介绍可以参考我的博文。
FileSystem的静态内部类–Cache类
FileSystem类里面有个内部静态类Cache。包含下面方法和变量。
Cache顾名思义,缓存。查看FileSystem类。定义了静态变量。
/** FileSystem cache */
static final Cache CACHE = new Cache();
定义为final类的理由我就不多说了,final的目的是为了防止变量再次被赋值。
在介绍Cache类之前,先介绍下它的一个静态内部类,Cache.Key类。Key类保存着scheme、authority、 UserGroupInformation,unique信息。
在调用方法FileSystem的get(URI uri, Configuration conf)的时候,我们会调用new Key(uri,conf,0)方法。而 FileSystem dfs1= FileSystem.newInstance(URI uri, Configuration conf)调用 new Key(uri, conf, unique.getAndIncrement())来实现的。而这种自增并不是基本类型,而是AtomicLonge类。
private static AtomicLong unique = new AtomicLong(1);
Atomiclong、AomicInteger等类是线程安全的,这样就保证了线程安全的自增操作,得到的实例化对象肯定唯一。
值得一提的是,FileSystem的另一个静态内部类 Statistics。用来记录FileSystem中完成的读写等操作的数据。
这样的弱引用,使得内存管理的更加合理。因为SattisticsData和Thread之间存在着依赖关系。
FileSystem兼容性
Hadoop会把已经Deprecated的参数也一起加载进行。而且低版本的HDFS的本地文件系统是通过“local”来访问的,现在通过“file”方法,这就设置到了一个fixname方法,解决兼容性问题。
1)fixname方法,当你使用低版本的命名格式(如,local的时候,会自动转换成版本匹配的字符)。
DistributedFileSystem类
DistributedFileSystem类继承自 FileSystem抽象类。
DistributedFileSystem是适配器模式的一个典型应用,它将DFSCl-ient提供的访问HDFS能力包装成FileSystem要求的界面。下面是从源代码上摘抄的成员变量。
private Path workingDir;
private URI uri;
private String homeDirPrefix =
DFSConfigKeys.DFS_USER_HOME_DIR_PREFIX_DEFAULT;
DFSClient dfs;
private boolean verifyChecksum = true;
其中成员变量dfs的类型是DFSClient,而其他四个成员变量(HDFS那本技术内幕的书上是三个,应该是版本更新后加了个成员变量),homeDirPrefix为主文件夹目录,默认为/user;workingDir保存了文件系统的当前工作;uri保存文件系统的URI模式和授权机构部分。
DistributedFileSystem.initialize()用于初始化对象,成员变量workingDir、uri和dfs都在这个方法被初始化。该方法需要一个Configuration对象,注意,类的static块会加载hdfs-default.xml和hdfs-site.xml两个配置文件,他们提供了访问Hadoop文件系统的一些默认配置。删除文件的delete()成员函数是典型的文件和目录相关事务方法,它的实现,就是简单地调用DFSClient的同名方法。DistributedFileSystem.open()用于打开HDFS文件,它返回一个FSDataInputStream对象,用于读取文件数据。方法open()实际返回一个DFSDataInputStream对象,它的FSDataInputStream的子类,也是为了兼容Hadoop文件系统界面而引入的包装器,它的大部分操作都委托给了通过DFSClient.open()创建的DFSInputStream对象。
DistributedFileSystem类提供了一系列对文件系统的操作,下面以mkdir和mkdirs两方法为例。
前者return mkdirsInternal(f, permission, false),后者return mkdirsInternal(f, permission, ture),这意味着前者不创建父文件夹,而mkdirs方法可以。
这里面很多操作都是和 FileSystemLinkResolver这个抽象类有关系,都是实现了这个抽象类的docall和next方法,再调用resolve方法。