一般而言,Hdfs是由一个NameNode节点和若干个DataNode节点组成(非高可用,高可用还有一个SecondNameNode)。
NameNode:管理分布式文件系统的元数据,这些元数据是一些诸如描述文件的存储路径以及block具体在哪些DataNode上的具体位置等; DataNode:DataNode节点用来保存文件数据块(block),它只负责接受存储、查询发送文件,不负责文件的切块; 文件切割:文件的切割默认是以128M为标准(该数值可通过参数设定),小于该标准的文件不切割,超过该标准的文件会被切成若干个block块;一个文件切割出来的多个block在存储时,每个block的存储地址都由NameNode决定,当一个block存储完毕后,下一个block要重新向NameNode申请存储地址;这些被切割出来的block块会根据replication(复制因子)复制出多个副本,且副本存放在不同的DataNode上; 汇报机制:DataNode会定时向NameNode汇报自身的block信息,NameNode会负责保存这些DataNode汇报上来的元数据,并保存文件的副本数量,一旦副本数量不满足复制因子规定的数目,则NameNode会指定一台DataNode(没有改副本的节点)从有该副本的DataNode上拷贝(一个副本ID在一台DataNode上有且仅有一份); 容错机制:如果存在DataNode宕机,那么当集群中的DataNode出发定时向NameNode汇报时,NameNode就会得知哪些机器宕机了,统计完副本分布情况之后,NameNode就知道哪些副本少了,于是NameNode就负责寻找一个没有这个副本节点从有这个副本的DataNode上拷贝一份(一个副本ID在一台DataNode上有且仅有一份); 客户端请求方式:Hdfs的内部工作机制对客户端保持透明,客户端请求访问Hdfs都是通过NameNode申请实现的。 写数据处理流程假设有一台NameNode和4台DataNode(非HA),我们的文件上传客户端可以在任何地方,前提是客户端能够与NameNode、DataNode连接。
一个大文件现在客户端进行分割,然后向NameNode请求上传block到指定文件夹,比如如下上传命令:
hadoop dfs -put 9F83F0668.mp4 /test/9F83F0668/
NameNode首先会看上传目标文件夹,然后它会去查询内部元数据该路径存在不存在,跟据查询结果会存在以下几种情况:
路径“/test/9F83F0668/”不存在如果符合文件上传条件,就意味着我们的客户端可以向Hdfs上传文件,这个时候客户端还不知往哪儿传送数据,客户端通过rpc请求上传一个block,NameNode分配三台DataNode(假设我们的复制因子为3)。
这里交代一下NameNode分配存储节点DataNode的机制:
客户端与DataNode1之间建立的连接管道名为pipeline,值得注意的是这个大小为“128M”的block块在往DataNode1上写的时候不是一次性传输的,而是又分成一个个packet数据包进行传输(默认64K),DataNode1每收到一个64k大小的packet包都要先验证其正确性,如果没有传输错误那就写到自己的文件目录下,在校验packet正确性和本地的过程之间还存在一个缓冲区,这个缓冲区向DataNode1自身的文件系统写校验正确的packet,同时向DataNode2发送该packet,DataNode2上面发生的事情和DataNode1上一样(也校验、进入缓冲区),DataNode3同理(校验、进入缓冲区,不发送了直接写入)。
宏观上看,客户端向DataNode1、DataNode2、DataNode3传输block的过程几乎是同步的,只会有一两个packet的差别。
假设有一台NameNode和4台DataNode(非HA),我们的读取数据客户端可以在任何地方,前提是客户端能够与NameNode、DataNode连接。
客户端向Hdfs发送请求下载路径“/test/9F83F0668/”下的9F83F0668.mp4,保存到本地的“/home/jack_roy/”路径下,命令如下:
hadoop dfs -get /test/9F83F0668/9F83F0668.mp4 /home/jack_roy/
==假设在Hdfs上9F83F0668.mp4的副分布在DataNode1、DataNode2、DataNode3、DataNode1上,我们的下载请求连接这四个节点,==现客户端向Hdfs读数据(下载文件)流程如下:
首先,客户端先去找NameNode,向NameNode发送rpc请求,请求下载“/test/9F83F0668/9F83F0668.mp4 ”; NameNode内维护的有一块元数据区域,该区域记录了以前存储在整个分布式文件系统里的文件的相关元数据信息,NameNode找到“/test/9F83F0668/9F83F0668.mp4 ”分成了哪几个block块、各个block块在相应节点上的存储路径等元数据信息; NameNode将元数据信息发送给客户端,客户端得知自己要下载的文件的第一个block块在哪些DatNode上,寻找条件最优的节点(对于“hadoop dfs -put ”命令来说,就是选择与客户端距离最近的节点,比如block在本地有,则优先从本地取该block)请求建立channel(管道),这里我们假设第一个block块的最优下载节点是DataNode1; DataNode1收到请求以后,找到自己文本区域下存储的block块,然后初始化一个FileinputStream来读取该block块,同时它也相应客户端建立管道(NioSocket通信); DataNode1在做好准备后,开始向管道输入流,该输入流通过SocketOutpuStream(端口输出流)实现; 客户端通过端口输入流SocketInpuStream接受,再由FileOutputStream写入到本地建立的文件目录中,来存储该block块(本地文件夹若不存在,则在NioSocket建立成功时建立); 待第一块block传输完成之后,再去寻找第二块block的最优存储节点,过程同理; 剩余的block块传输过程同理,直至将“/test/9F83F0668/9F83F0668.mp4 ”完全下载至本地。
注意:以上传输过程依旧是以packet(64K)为单位传输,客户端本地缓存,然后写入目标文件。
对Hdfs读、写数据处理流程的理解交代完了,后面再交代NameNode的元数据机制,个人理解恐有失偏颇,欢迎留言指正。
跳转【大数据入门笔记系列】写在前面
【大数据入门笔记系列】第一节 大数据常用组件
【大数据入门笔记系列】第二节 Zookeeper简介
【大数据入门笔记系列】第三节 Hdfs读、写数据处理流程
【大数据入门笔记系列】第四节 NameNode元数据缓存机制
【大数据入门笔记系列】第五节 SpringBoot集成hadoop开发环境(复杂版的WordCount)