RBF
在前边,已经介绍了HDFS Federation,也提到了解决Federation但带来问题的ViewFS和3.x新带来的ViewFS Overload Schema特性。但之前应用更广泛的是Router-based Federation,一种基于服务端的实现解决方案。
此方案增加了额外的一层设计,能够实现用户透明的访问子集群。它能够管理并维护集群namespace的状态,将请求转发到正确的子集群,并且支持跨子集群的数据均衡。同时,它也实现可扩展、高可用和容错机制。
架构
RBF主要由两层组成,Router及State Store。Router主要有两个角色:
-
Federated interface:与namenode有相同的接口,所以可以直接接受客户端的请求并将请求转发到正确的集群。
-
Namenode heartbeats:这个意思在于Router会监控Namenode的状态和心跳信息并将信息存储到State Store(主要包括HA State及负载情况)
而State Store主要就负责:
-
存储Mount Table,是的,和ViewFS的基本一样。存储文件夹与子集群的映射关系。
-
存储Membership Table。这个表存储的就是上面Router监控的Namenode信息
-
存储Router本身的心跳。
看到这里,整个请求流程大概就清晰了:
-
客户端发出读写请求到Router
-
Router从State Store中的Mount Table中找出包含实际文件路径的集群
-
Router同时会通过State Store中的Membership Table,确认正确的Namenode,同时会检查该集群中目标Namenode的状态。
-
这些都完成之后,会将请求转发到对应的Namenode。
这里将Mount table存储到了State Store(默认是使用Zookeeper),也就是服务端,避免了重客户端的操作。下面讨论一下这个请求链路的容错及高可用性。
-
客户端配置了所有Router的endpoints,单个Router失败,客户端也可以请求其他Router。另外Router本身异常,也会自动进入安全模式而不提供服务。
-
最简单的实现是在每个Namenode的机器上部署Router,但是为了高可用及灵活性,会使用多个Router监控同一个Namenode(State Store中冲突的信息由quorum解决),这样即使Router失败也不会导致有问题。
-
而State Store一般会使用Zookeeper,所以也不用担心有单点问题。
这样两层都有了高可用及容错。
对于监控的Namenode,如果Router联系不到了active的Namenode,它会首先尝试访问standby Namenode,再去访问联系不上的Namenode。如果在这个过程中都失败,才会抛出异常。
同样如果在多个心跳周期内收不到Namenode的心跳,Router会将其状态更新为死亡,直到收到心跳信息才会更新状态。
Interface
为了与用户和管理员交互,Router暴露了多个接口。
-
RPC:实现了HDFS的大部分接口。比如 snapshot,encryption and tiered storage。
-
Admin:这个接口可以让用户通过命令行查询和修改信息。
-
Web UI:类似Namenode UI一样的可视化界面。展示了mount 和 membership table信息,比如每个子集群、Router的状态。
-
WebHDFS:提供了HDFS的WebHDFS接口。
-
JMX:提供UI提供了一些metric指标。
所有不允许的操作都将会抛出异常。
Mount table管理
一个好的建议是将federated namespaces的名字与destination namespaces相同。
可以通过Router暴露的Admin接口来管理mount table:
[hdfs]$ $HADOOP_HOME/bin/hdfs dfsrouteradmin -add /tmp ns1 /tmp
[hdfs]$ $HADOOP_HOME/bin/hdfs dfsrouteradmin -add /data/app1 ns2 /data/app1
[hdfs]$ $HADOOP_HOME/bin/hdfs dfsrouteradmin -add /data/app2 ns3 /data/app2
[hdfs]$ $HADOOP_HOME/bin/hdfs dfsrouteradmin -ls
也支持设置挂载点只读
[hdfs]$ $HADOOP_HOME/bin/hdfs dfsrouteradmin -add /readonly ns1 / -readonly
如果挂载点没有设置,Router默认匹配默认的namespace(dfs.federation.router.default.nameserviceId)
Mount table 也有类似linux的权限设置。写权限用户增删改。读权限允许查。默认是755
[hdfs]$ $HADOOP_HOME/bin/hdfs dfsrouteradmin -add /tmp ns1 /tmp -owner root -group supergroup -mode 0755
多个子集群
Multiple subclusters:
上面挂载点都是一对一,这里允许单个挂载映射到多个子集群。例如:
[hdfs]$ $HADOOP_HOME/bin/hdfs dfsrouteradmin -add /data ns1,ns2 /data -order SPACE
这样设置,当list该目录时,会展示两个子集群的文件夹及文件。而写入呢?可以看到-order参数,是由它指定的。有以下几个策略:
-
LOCAL:尝试写数据到本地子集群
-
RANDOM:会在所有子集群中创建文件夹,多个子集群随机写入
-
SPACE:会在所有子集群中创建文件夹,尝试在有更多可用空间的子集群上写数据
-
HASH:只对第一层目录采用hash
-
HASH_ALL:会在所有子集群中创建文件夹,对所有层采用hash。这种方法试图均衡各个子集群之间的读和写操作。
举个例子,理解其中的区别:
假设我们有一个/data/hash的HASH挂载点,那么/data/hash/folder0下的文件和文件夹都将在同一个子集群中。而对于/data/hash_all的HASH_ALL挂载点将把/data/hash_all/folder0下的文件分散到该挂载点的所有子集群(将为所有子集群创建子文件夹)。
如果要确认文件属于哪个子集群:
[hdfs]$ $HADOOP_HOME/bin/hdfs dfsrouteradmin -getDestination /user/user1/file.txt
Router不能保证跨子集群的数据一致性。默认如果一个Router失败,在当前子集群的写入可能会失败。
客户端配置
Client configuration:
在hdfs-site.xml中。假如集群有四个namespaces,这里还需要增加一个federated namespace(fs.defaultFS)指向Router。
<configuration>
<property>
<name>dfs.nameservices</name>
<value>ns0,ns1,ns2,ns3,ns-fed</value>
</property>
<property>
<name>dfs.ha.namenodes.ns-fed</name>
<value>r1,r2</value>
</property>
<property>
<name>dfs.namenode.rpc-address.ns-fed.r1</name>
<value>router1:rpc-port</value>
</property>
<property>
<name>dfs.namenode.rpc-address.ns-fed.r2</name>
<value>router2:rpc-port</value>
</property>
<property>
<name>dfs.client.failover.proxy.provider.ns-fed</name>
<value>org.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvider</value>
</property>
<property>
<name>dfs.client.failover.random.order</name>
<value>true</value>
</property>
</configuration>
其他关于RBF的配置,配置在hdfs-rbf-default.xml。官网文档列出的很详细。
RBF可支持多个独立的集群、联邦集群或两者混合的集群。同时RBF还支持了Quota和Security,相对于ViewFS功能更强大并且也解决其存在的问题,是一种很好的管理多集群的方式。