HDFS写文件流程
当用户通过命令行或者JavaAPI向Hadoop集群发起写入文件操作时,将触发写文件流程,分为3个阶段:创建文件,建立数据流管道和写数据。
1. 创建文件
HDFS client调用DistributedFileSystem.create()
上述调用向namenode发起RPC create调用,请求指定路径创建一个新文件,并将操作记录存入edits.log,该调用返回一个FSDataOutputStream对象,该对象是DFSOutputStream的wrapper
2. 建立数据流管道
HDFS client调用FSDataOutputStream.write()写block1
上述write()方法向namenode发起RPC addBlock调用,请求添加一个新的数据块,namenode会返回LocatedBlock对象
LocatedBlock对象所含有的信息将告诉client数据会写到哪几个datanode上
HDFS client根据返回的datanode信息,建立数据流管道pipeline
3. 写数据
FSDataOutputStream对象开始写入数据,数据以512字节的chunk+4字节的checksum形式写入
每64KB形成一个packet,packet格式为header+checksums+DATA
packet放入data queue队列等待被发送
HDFS client将packet发送给datanode1,datanode1将packet发送到datanode2,datanode2将其发送到datanode3,以此类推
HDFS client同时将packet写入ack queue队列
最后一个datanode(即这里的datanode3)对收到的packet进行校验,然后向上一个datanode(即datanode2)发送ack,datanode2同样进行校验,然后发送ack到datanode1,datanode1做完校验发送ack给HDFS client
HDFS client判断收到的校验,如果成功,则删除ack queue队列中的packet,表示数据传递成功;如果失败,则将packet放回data queue进行重传
重复上述过程直到整个block发送完毕,此时所有datanode都收到了block1的完整副本,它们会向namenode的远程过程调用blockReceivedAndDeleted(),通知namenode该block已经发送成功,namenode更新内存中block与datanode的对应关系
关闭构建的pipeline,继续传输下一个block2时,从RPC addBlock重新开始,直到全部数据写完
调用DFSOutputStream的close()方法
客户端远程过程调用namenode的RPC complete(),告知namenode传输完成
4. 容错原理
数据传输过程中,如果datanode2突然挂掉了,HDFS会启动如下步骤进行容错。
将ack queue队列中所有的packet放回data queue队列
HDFS client向namenode发起RPC调用updateBlockForPipeline(),为当前block生成新版本ts1(本质是时间戳)
故障datanode2从pipeline中删除
DFSDataOutputStream发起RPC调用namenode的getAdditionalDatanode()方法,让namenode重新分配datanode,比如是datanode4
DFSDataOutputStream将dn1,dn2和dn4组成新管道,更新上面的block版本为ts1
HDFS client通知dn1和dn3将其上已完成传输的block数据拷贝到dn4,上,至此三者所获取的数据保持一致
新的数据管道建立后,DFSDataOutputStream调用upadatePipeline() RPC调用更新namenode元数据
HDFS client按正常写入流程完成文件上传
故障dn2重启后,namenode发现它上面的block时间戳是老的,就会通知dn2将其删除
HDFS读文件流程
HDFS client向namenode通过远程过程调用请求下载文件
namenode返回目标文件元数据
HDFS client根据“先就近,后随机”的原则选择datanode,请求读取数据
datanode开始以packet为单位传输数据给客户端
客户端以packet为单位接收,先本地缓存然后写入目标文件
客户端再向其他datanode请求下一个block,重复上述过程
元数据冷备份机制
namenode负责HDFS集群的元数据管理,要保证快速检索,namenode必须将数据放到内存中,但一旦断电或者故障,元数据会全部丢失,因此还必须在磁盘上做持久化。HDFS集群做元数据持久化的方式是edits.log+FSImage。edits.log存储近期的操作,FSImage存储以前的操作,这样是为了尽可能地保障namenode的启动速度。
每隔一分钟secondarynamenode会向namenode发起1次检查点请求
当发现edits.log中记录超过100w条或者时间达到一个小时,就会启动检查点机制
snn向nn请求滚动生成新的edits_inprogress_XX.log,新的对HDFS的操作可以写入这个新文件中
snn通过HTTP GET请求读取nn中的FSImage和原来的edits.log
snn读取FSImage到内存,然后执行edits.log中的操作,创建一个新的FSImage.ckpt
snn通过HTTP PUT将新的FSImage.ckpt发送到nn
nn用新的FSImage替换旧的,更新记录检查点时间
此时nn拥有新的FSImage和更小的edits.log
snn一般单独部署一台机器,因为它要占用大量CPU、内存和磁盘空间来执行合并操作