为了回答文章标题的问题,先去官网RBF相关界面查阅文档。
Hey,我是官网
找到如下配置信息:
dfs.federation.router.file.resolver.client.class
这个参数的描述信息是:解析文件属于哪个子集群,如果允许一个挂载点对应多个子集群,要把此参数设置为
org.apache.hadoop.hdfs.server.federation.resolver.MultipleDestinationMountTableResolver.
构造这个类的对象是在Router类:
调用了FederationUtil#newFileSubclusterResolver方法。
从配置文件中读出配置项,然后通过反射得到SubclusterResolver。最终赋值给Router类的成员。
通过注释可以看到这个resolver的功能就是把一个全局名字空间映射到HDFS 子集群的名字空间。举个栗子:
全局名字空间的路径是hdfs://cluster/data。然后有两个子集群subcluster1,subcluster2。那刚才的全局名字空间就可能对应hdfs://subcluster1/data 和 hdfs://subcluster2/data等等。
好的进入正题,就去看这个MultipleDestinationMountTableResolver类。
1. MultipleDestinationMountTableResolver
看类的名字,直观翻译就是“多目的地丶挂载表丶解析器”。很容易理解,就是这个类会把一个给定的src路径,(如有必要)解析成在文件系统里的多个路径。因为现在namenode是federation的,挂载表(Mount Table)中的一个entry可以对应为多个namespace下的路径。我们看这个类的JavaDoc:
看划红线的部分,它说:返回的location是包含了优先级的remote paths,优先级是从高到低。那就证明这个类有个方法是返回location的方法。看下这个类的成员变量以及方法:
果不其然,划红线的就是返回location的方法:getDestinationForPath
观察到这个方法是继承的,我们一路向上点。找到最顶层的方法声明:
这个方法的位置是:FileSubclusterResolver#getDestinationForPath
阅读JavaDoc:获取一个global path的目的地。Results是来自于mount table cache中的。如果global path确实有多个目的地,那Resuls中第一个是具有最高优先级的目的地。
接下来看成员变量:
这个是个EnumMap的类型,它其实就是个Map,只不过key必须是DestinationOrder这个枚举类型里面指定的。那肯定也有像这个Map里添加数据的方法。在本类中是addResolver
最后看一下构造方法:
主要是添加一些Resolver。假设我们要自定义Resolver的话,也要在这里添加。
接下来明确一下思路:我们就看orderedResolvers成员变量的Key和Value类型。也即DestinationOrder枚举类和OrderedResolver类。
2. DestinationOrder
枚举类:定义目的地排序规则。
3. OrderedResolver
这个是个接口,只有一个方法。看下Doc。
这个接口的作用是:多目的地的情况下,决定哪个会是第一个被访问的location。
只有一个getFirstNamespace方法。作用就是这个接口描述的那样。根据这个方法获得first namespace。
分析到这里我们有两条路可以走,一条路是看OrderedResolver的实现类是怎么实现的。
另一条路是看getFirstNamespace方法的第二个参数的PathLocation类型。
这里我们选第二条路,原因是选第一条路是属于“深度优先遍历”(进入细枝末节),选第二条路是“广度优先遍历”(了解整个框架)。
好的那下面就看PathLocation。
4. PathLocation
类介绍如下:
额外说一句:global path就是我们在客户端程序或者Web UI中的那个路径。
PathLocation它里面记录了关于一个global path的一些映射信息,比如global path映射到哪些目的地。它里面的数据是从MoutTable中的记录生成的。
三个成员变量分别代表 global path, 多个目的地的path(List), 还有一个DestinationOrder对象用来指定确定第一个location的规则。
看一下方法,红线框起来的部分:
阅读方法名和返回值大概知道是什含义。除去构造方法,其他都是一些获取成员变量的相关属性的方法。比如可以得到location的namespace名字的集合等。
那我们就得看是谁调用了PathLocation的构造方法,以及构造方法中的参数是怎么生成的。找到了下图这个构造方法是被MultipleDestinationMountTableResolver 调用的。
进到MultipleDestinationMountTableResolver这个类:
这个方法就是重写我们上面讲过的FileSubclusterResolver#getDestinationForPath方法。最后也是把mountTableResult这个PathLocation对象返回了。
看下这个方法的逻辑:
①首先调用父类MountTableResolver的同名方法的实现(挖的第一个坑),此时得到了一个PathLocation。
②上一步得到的PathLocation如果是null,打印些log error信息。如果PathLocation是多目的地的,就进入else if (mountTableResult.hasMultipleDestinations())`分支
③根据DestinationOrder对象得到对应的resolver对象。
④根据resolver的getFirstNamespace方法得到第一个命名空间的名字(String类型),这个方法的逻辑需要你使用的具体resolver的类去实现。一会儿我们再找一个Resolver的具体实现类分析(挖的第二个坑),现在我们假设,emmm,没错我们假设得到了firstNamespace。
⑤就把firstNamespace当作参数传给PathLocation的构造函数,构造函数会根据firstNamespace对传入的PathLocation对象mountTableResult排序。
⑥返回mountTableResult对象(无论是排序过的或者是没排序过的)
可以!刚刚我们挖了两个坑:
第一个:MountTableResolver#getDestinationForPath方法
第二个:找一个具体的resolver分析getFirstNamespace方法。
首先填第一个坑:MountTableResolver#getDestinationForPath方法
我们只需要知道原始的mountTableResult是怎么生成的就好了,于是根据下面这句代码走到MultipleDestinationMountTableResolver的父类MountTableResolver的这个方法的实现。
PathLocation mountTableResult = super.getDestinationForPath(path);
下面是MountTableResolver#getDestinationForPath方法的实现:
方法逻辑很简单:
①做一些验证工作,并拿到读锁,这个锁的底层实现是JDK的ReentrantReadWriteLock。这里我们就不深入探讨了。
②从locationCache缓存对象中找PathLocation,没找到的话使用lookupLocation方法查找locations,返回值是PathLocation类型。并添加进locationCache缓存对象。这个缓存对象我们也不用太过关注,是google提供的缓存框架。
③释放读锁。
所以我们还需要进入lookupLocation方法:
这个方法的逻辑也很简单:
①根据传入的path找到最深层的路径。理论上讲,最深层就是path自己,但是特殊情况是path传入的path不一定存在。举个栗子。我们传入 /ns1/data/study/。但是Mount中只存在/ns1/data/。那我们就找到能够最大匹配到的,也就是deepest的意思。findDeepest的代码我们就不看了,逻辑比较简单。
②如果返回的entry不为空,说明有部分目录或者全部匹配到了,那么直接就buildLocation。如果返回的entry为空,那么就使用默认的一些参数创建一个PathLocation返回。注意返回后,google的cache框架会进行缓存,这样下次就能查到了。buildLocation的逻辑也比较简单。里面用了for each语句去构造List<RemoteLocation\>对象。再把它传入PathLocation,这样我们就得到了心心念念的PathLocation对象,原来这么产生的。
接下来填第二个坑:找一个具体的resolver分析getFirstNamespace方法
此处我们找的是HashResolver类:
这个方法的目标(不限于HashResolver类)是对于给定的path,以及这个path对应的PathLocation。按照具体的实现规则从PathLocation中找到第一个要处理namespace名称。
到这里我们几乎已经分析完了Router based Federation情况下,是怎么根据传入的path进行解析的。最后剩下的一点就是RemoteLocation类。还记得构造PathLocation时,可以传入List对象么?我们就补充一下RemoteLocation类作为收尾。
5. RemoteLocation
先看Doc,RemoteLocation这个类表示在remote namespace上的一个位置。它包括四个成员变量。分别是nameservice ID, namenode ID,在federation中的路径(srcPath), 目的路径(HDFS path)
也就是说Remotelacation类似于一个原子的位置信息。他不能表示跨namenode的位置。但是可以通过List<RemoteLocation\>表示。
END!!
毁灭吧,赶紧的,累了。