-
初始化注册:当Datanode启动或重启时,将向NameNode进行注册,告知NameNode其可以处理HDFS的读写操作。
-
周期性心跳:所有的DataNode周期性(默认是每3秒)地向NameNode发送包含该节点使用统计的心跳信息,让NameNode知道DataNode活着。NameNode收到心跳后会给DataNode一个返回值,这个返回值里就包含对DataNode的指令,比如将数据块复制到另外一台节点上或删除某个块。一个DataNode如果超过10分钟没有发送心跳,此时NameNode就会判定该DataNode不可用,此时客户端的读写操作就不会再传达到该DataNode上,同时其他节点开始复制该死掉的节点上的数据,直到满足3个备份因子。
-
周期性块报告:默认情况下,每个DataNode每小时向NameNode发送一次块报告。通过块报告,可以将NameNode上存在的副本信息与DataNode上的副本信息进行同步。
-
DataNode同时向active NameNode 和standby NameNode发送块信息。
-
JournalNode负责共享NameNode的编辑日志,standy NameNode读取共享编辑目录并改变自己的命名空间。
-
每个block在datanode的本地存储两个文件,一个数据文件,一个元数据文件。元数据文件包含数据文件的checksum和generation stamp。
-
generation stamp:namenode为每个块维护一个单调递增的8字节数字,称为generation stamp。数据和副本都各有一个gs,如果副本的gs比数据块的gs旧,则意味着副本已经过期。当datanode重新加入到集群中时,gs还有助于检测长期死亡的dn上的过期副本。
客户端读取HDFS数据流程
-
当client请求读取HDFS文件时,首先向NameNode发起RPC请求,为了获取想要读的文件的数据块所在在datanode位置。
-
NameNode会视情况返回文件的部分或者全部block列表,对于每个block,NameNode都会返回含有该block副本的DataNode地址;这些返回的DataNode地址,会按照集群拓扑结构得出DataNode与客户端的距离,然后进行排序,排序两个规则:网络拓扑结构中距离Client近的排靠前,心跳机制中超时汇报的DataNode状态为STALE,这样的排靠后。
-
(1)Client 选取排序靠前的DataNode来读取block,如果客户端本身就是DataNode,那么将从本地直接获取数据(短路读取特性)。 (2)或者如果第一个datanode在读取时失败,client会自动连接到列表中的下一个datanode并读取该块。(3) client读完一个块后,会验证块的当前checksum和块首次存储在磁盘上时计算的原始checksum是否相同,如果不一致会读取下一个副本块,同时告知namenode发现了坏块。
-
当读完列表上的block后,若文件读取还没结束,client会继续向namenode获取下一批的block列表。
-
底层上本质是建立SocketStream(FSDataInputStream),重复的调用父类DataInputStream的read方法,直到这个块上的数据读取完毕。read方法是并行读取block信息,不是一块一块的读取,NameNode只是返回Client请求包含块的DataNode地址,并不是返回请求块的数据。
-
最终在client端合并所有的块,形成一个文件。
客户端向HDFS写入数据流程
-
client向namenode发出请求上传数据。
-
namenode收到请求后开始一系列检查,比如查找要上传的目录是否存在,上传者是否有这个目录的权限等,检查之后向client响应是否可上传文件。这个响应还包括第一个block要存放的datanode列表,这个列表被称为管道,若默认复制因子是3,则管道中有三个datanode。
-
client收到响应后开始上传第一个块数据(数据分块操作是在client端 )。client连接到管道中的第一个datanode,并开始向该节点写入数据块。
-
第一个datanode将连接管道列表中的第二个datanode,并在接收到数据块时转发给它,第二个再转给第三个。
-
client确认block1完成传输后,向NameNode发送请求,确认block1已存入,NN更新自己的Fsimage,并返回block2的存储地址。
-
client将所有数据写入完成后,关闭文件,通知namenode文件写入完成。