首先看一下FileSystem类的get方法具体实现:
/**
* Returns the configured filesystem implementation.
* @param conf the configuration to use
*/
public static FileSystem get(Configuration conf) throws IOException {
return get(getDefaultUri(conf), conf);
}
/** Get the default filesystem URI from a configuration.
* @param conf the configuration to use
* @return the uri of the default filesystem
*/
public static URI getDefaultUri(Configuration conf) {
return URI.create(fixName(conf.get(FS_DEFAULT_NAME_KEY, DEFAULT_FS)));
}
该静态方法首先从配置文件中读取了URI信息,并调用了另一个异构get方法。如果是使用HDFS文件系统,其URI为hdfs://;如果没有配置,默认使用的是本地文件系统,URI为:file://。
/** Returns the FileSystem for this URI's scheme and authority. The scheme
* of the URI determines a configuration property name,
* <tt>fs.<i>scheme</i>.class</tt> whose value names the FileSystem class.
* The entire URI is passed to the FileSystem instance's initialize method.
*/
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
return get(conf);
}
if (scheme != null && 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);
}
这个get方法判断了是否配置了FileSystem实例的缓存功能。如果配置为true,当使用get获取实例的时候,如果先前创建的实例还可用,则使用已经存在的实例,而不进行重新创建。
接下来我们直接看创建实例的方法,不对cache功能进行深入分析。createFileSystem方法如下:
private static FileSystem createFileSystem(URI uri, Configuration conf
) throws IOException {
Class<> clazz = getFileSystemClass(uri.getScheme(), conf);
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;
}
该方法首先通过URI的前缀获取了URI中描述的文件系统的具体实现类,然后通过反射机制创建出FileSystem的实例。
接下来看下getFileSystemClass方法的实现,查看如何通过URI前缀获取相应文件系统的具体实现类。
public static Class< extends FileSystem> getFileSystemClass(String scheme,
Configuration conf) throws IOException {
if (!FILE_SYSTEMS_LOADED) {
loadFileSystems();
}
Class< extends FileSystem> clazz = null;
if (conf != null) {
clazz = (Class< extends FileSystem>) conf.getClass("fs." + scheme + ".impl", null);
}
if (clazz == null) {
clazz = SERVICE_FILE_SYSTEMS.get(scheme);
}
if (clazz == null) {
throw new IOException("No FileSystem for scheme: " + scheme);
}
return clazz;
}
该方法首先判断了file system是否已经加载,如果未加载,则加载file system相关的信息。具体加载内容如下:
private static void loadFileSystems() {
synchronized (FileSystem.class) {
if (!FILE_SYSTEMS_LOADED) {
ServiceLoader<FileSystem> serviceLoader = ServiceLoader.load(FileSystem.class);
for (FileSystem fs : serviceLoader) {
SERVICE_FILE_SYSTEMS.put(fs.getScheme(), fs.getClass());
}
FILE_SYSTEMS_LOADED = true;
}
}
}
这里通过了Java中的ServiceLoader加载了FileSystem的服务,所谓的FileSystem服务就是实现了FileSystem抽象类的具体实现类,具体ServiceLoader的相关介绍请看ServiceLoader详解。
通过ServiceLoader,把META-INF/services中描述的FileSystem服务的具体实现类加载进来了,并获取各个类的前缀和类,从而使得在getFileSystemClass方法中通过URI前缀,获取相应的实现类,并返回去创建相应实例。
通过以上的几个主要的方法,Hadoop把文件系统进行了抽象,提供给用户统一的接口进行文件系统的操作,另外也提供了统一的实现方式,可以更方便的集成第三方的文件系统。