关于机架感知官方文档
http://hadoop.apache.org/docs/r2.7.2/hadoop-project-dist/hadoop-common/RackAwareness.html
后文借鉴的官方文档
http://hadoop.apache.org/docs/r2.7.2/hadoop-project-dist/hadoop-hdfs/HdfsDesign.html#Data_Replication
官方文档对于机架感知是这么描述的:
对于常见情况,当复制因子为3时,HDFS的放置策略是将一个副本放在本地机架中的一个节点上,另一个放在本地机架中的另一个节点上,最后一个放在不同机架中的另一个节点上。此策略减少机架间写入通信量,这通常会提高写入性能。机架故障的几率远小于节点故障的几率;此策略不会影响数据可靠性和可用性保证。但是,它确实减少了读取数据时使用的聚合网络带宽,因为块只放置在两个唯一的机架中,而不是三个机架中。使用此策略,文件的副本不会均匀分布在机架上。三分之一的副本位于一个节点上,三分之二的副本位于一个机架上,另三分之一的副本均匀分布在其余机架上。此策略在不影响数据可靠性或读取性能的情况下提高写入性能。
第一个副本在Client所处的节点上。如果客户端在集群外,随机选一个。
第二个副本和第一个副本位于相同机架,随机节点。
第三个副本位于不同机架,随机节点。
机器感知的作用就是告诉Hadoop集群中哪台机器位于哪个机架
涉及到机架感知一般是在Hadoop 集群规模很大的情况,一般情况下Hadoop 的机架感知是没有被启用的。也就是说在通常情况下,HDFS在选择机器时是随机选择的,有的时候随机选择存储节点会影响性能,所以有的时候我们需要配置机架感知。
配置hadoop机架感知的功能需要在NameNode所在机器的core-site.xml配置文件中增加一个选项:
topology.script.file.name</name>
XXXX</value>
</property>
这个配置选项的value指定为一个可执行程序,通常为一个脚本,该脚本接受一个参数,输出一个值。接受的参数通常为某台DataNode机器的ip地址,而输出的值通常为该ip地址对应的DataNode所在的rack,例如”/rack1”。NameNode启动时,会判断该配置选项是否为空,如果非空,则表示已经用机架感知的配置,此时NameNode会根据配置寻找该脚本,并在接收到每一个DataNode的heartbeat时,将该DataNode的ip地址作为参数传给该脚本运行,并将得到的输出作为该DataNode所属的机架,保存到内存的一个map中。
配置的可执行程序可参考官方文档或百度
在HDFS写数据的过程中,NameNode会选择距离待上传数据最近距离的DataNode接收数据。那么这个最近距离怎么计算呢?
节点距离:两个节点到达最近的共同祖先的距离总和
在我们配好机架感知后,NameNode 就可以画出如图所示的DataNode 网络拓扑图,就可以去进行节点距离的计算,d1,r1 对应交换机,节点n0,n1,n2对应DataNode,那么集群d1中的机架r1中的节点n0就可以表示为 /d1/r1/n0
,有了这些信息就可以计算出任意两台datanode 之间的距离。
Distance(/d1/r1/n0, /d1/r1/n0)=0 (同一 节点上的进程)
Distamnce(/d1/r1/n1, /d1/r1/n2)=2 (同-机架上的不同节点,共同祖先r1,距离为1+1=2)
Distance(/d1/r2/n0, /d1/r3/n2)=4 (同-数据中心不同机架上的节点,共同祖先为d1,距离为1+1+1+1=4)
Distance(d1/r2/n1, /d2/r4/n1)=6 (不同数据中心的节点)
1 客户端通过Distributed FileSystem模块向NameNode请求上传文件,NameNode检查目标文件是否已存在,父目录是否存在。
2 NameNode返回是否可以上传。
3 客户端收到可以上传的响应后将目标文件进行切块(hadoop2.x默认块大小为128M),然后请求Block块上传到哪几个DataNode服务器上。
4 NameNode首先会检测其保存的DataNode信息,确定该文件块存储在那些节点上然后响应给客户端一组DataNode节点信息,这里返回3个DataNode节点,分别为dn1、dn2、dn3。
5 客户端收到那组DataNode节点信息后,首先就近
与某台DataNode建立网络连接;通过FSDataOutputStream模块请求dn1上传数据,dn1收到请求会继续调用dn2,然后dn2调用dn3,将这个通信管道建立完成。
6 dn1、dn2、dn3逐级应答客户端。
7 客户端开始往dn1上传第一个Block,以Packet为单位,DataNode收到数据后,首先会缓存起来;然后将缓存里数据保存一份到本地(序列化到磁盘中),一份发送到传输通道;让剩下的DataNode做备份,并不是写好一个块或一整个文件后才向后分发,每个DataNode写完一个块后,会返回确认信息。
(当客户机将数据写入HDFS文件时,其数据首先写入本地文件,如前一节所述。假设HDFS文件的复制因子为3。当本地文件累积完整的用户数据块时,客户端从NameNode检索数据节点列表。此列表包含将承载该块副本的数据节点。然后,客户端将数据块刷新到第一个数据节点。第一个数据节点开始接收小部分的数据,将每个部分写入其本地存储库,并将该部分传输到列表中的第二个数据节点。第二个数据节点依次开始接收数据块的每个部分,将该部分写入其存储库,然后将该部分刷新到第三个数据节点。最后,第三个数据节点将数据写入其本地存储库。因此,数据节点可以从管道中的前一个节点接收数据,同时将数据转发到管道中的下一个节点。因此,数据是从一个数据节点到下一个数据节点的流水线。)
8 当一个Block传输完成之后,客户端再次请求NameNode上传第二个Block的服务器。(重复执行3-7步)。
9 写完数据,关闭FSDataOutputStream模块,发送完成信号给NameNode。
1 客户端通过Distributed FileSystem向NameNode请求下载文件,NameNode通过查询元数据,找到文件块所在的DataNode地址,并将每个block的DataNode地址返回客户端。
2 客户端拿到block的位置信息后,就近
挑选一台DataNode,请求读取数据。34和56过程是并发运行的,默认block有3个副本,所以每一个block只需要从一个副本读取就可以。客户端开发库会选取离客户端最接近的DataNode来读取block。
3 DataNode开始传输数据给客户端(从磁盘里面读取数据输入流,以Packet为单位来做校验)。
4 客户端以Packet为单位接收,先在本地缓存,然后写入目标文件。