1 HDFS基本概念
数据块(Block)
128M 最小处理单元 冗余备份
名字节点(NameNode)
管理:
1、文件系统的命名空间:文件系统目录树+文件/目录信息+文件数据块索引
2、数据块之间的对应关系:启动时动态构建
以 命名空间镜像文件 和 编辑日志文件 形式存储在内存中
存在单点故障:使用备用(standby)名字节点
为了增强横向扩展解决内存的问题:联邦HDFS机制
数据节点(DataNode)
写入和读出
向名字节点发送心跳、数据块汇报、缓存汇报,根据返回值来执行指令
客户端(Client)
建立在DFSClient类的基础上
HDFS通信协议
Hadoop RPC接口:
流式接口:TCP HTTP
2 HDFS通信协议
2.1Hadoop RPC接口
六个接口:
客户端与名字节点的通信接口:ClientProtocol
客户端与数据节点的通信接口:ClientDataNodeProtocol
数据节点与名字节点通信接口:DatanodeProrocol(握手,注册,发送心跳,进行全量以及增量的数据块汇报)
数据节点与数据节点间的通信接口:InterDatanodeProtocol
第二名字节点与名字节点间的接口:NamenodeProtocol(2.X引入了HA机制,检查点操作也不由SecondNamenode来执行了)
其他接口:安全接口、HA接口
备注:心跳相关方法
在开启了HA的HDFS集群中,datanode是需要同时向active (活动)namenode 以及Standby nodename发送心跳。
2.2 流式接口
基于TCP的Data TransferProtocol接口,来实现写入和读取数据(TCP利于大文件的批处理和高吞吐率)
基于Active Namenode和Standby Namenode间的HTTP接口
备注:数据块复制操作时若数据流管道中的数据节点出现故障,需要用新的数据节点来替换异常的数据节点,就需要把数据流管道中的异常数据节点写入的数据块复制到新添加的数据节点上。
2.X前,HDFS使用SecondNamenode来定期合并fsimage和editlog;
2.X后,使用Standby Namenode会不断地将读入的editlog文件与当前的命名空间合并,定期写入到一个新的fsimage文件中,然后通过http协议将fsimage文件传回给Active Namenode。
3 HDFS客户端读流程
打开HDFS文件:
调用DistributedFileSystem的方法,底层返回一个DFSInputStream对象,用来读取数据块从Namenode获取Datanode地址:
通过ClientProtocol.getBlockLocations()方法向名字节点获取该HDFS文件起始位置数据块的位置信息(位置信息的远近进行了排序,可以选择最优的数据节点来读取)连接到Datanode读取数据块:
客户端通过DFSInputStream.read()方法从最优的数据节点读取数据,数据会以数据包(packet)为单位从数据节点通过流式接口传送给客户端。读取完毕,则继续获取下一个文件位置...关闭数据流:
通过DFSIputStream.close()方法关闭输入流
备注:数据包中不仅包含了数据,还包含了校验值,当校验错误时,客户端回向Namenode汇报并读取副本数据。
4 HDFS客户端写流程
创建文件:客户端调用DistributedFileSystem .create方法创建新的空文件,这个方法的底层对ClientProtocol.create远程调用Namenode执行对应的操作,校验并创建一个空文件,记录创建操作到editlog中。返回真正用于写数据的DFSOutputStream对象,
建立数据流管道:客户端调用DFSOutputStream.write()方法来写数据,在这之前,DFSOutputStream会首先调用ClientProtocol.addBlock()向Namenode申请一个新的空数据块,返回数据块的位置信息对象。获得了数据流管道中所有数据节点的信息后,DFSOutputStream就可以建立数据流管道进行写数据块了。
通过数据流管道写入数据:
写入DFSOutputStream中的数据会先被放到缓存队列中,之后被切分成一个个数据包(packet),通过数据流管道发送到数据节点。每个数据包都有个ack确认包,ack会逆序通过数据流管道回到输出流,输出流在确认所有数据包已经写入到数据节点上,就会从相应的缓存队列删除这个数据包。客户端写满一个数据块,会调用addBlock()申请新的数据块....关闭输入流并提交文件:
完成了所有数据块的写操作之后,调用DFSOutputStream.close()方法关闭输出流;并调用ClientProtocol.complete()方法通知Namenode提交这个文件中的所有数据块,正式完成整个文件的写入流程。
数据流管道中的数据节点出现故障时,输出流会进行如下的故障恢复:
输出流中的缓存没有确认的数据包会重新加入发送队列,这种机制确保了数据节点出现故障时不会丢失任何数据,所有的数据都是经过确认的。
故障数据节点会从输入流管道中删除,然后通过RPC通知Namenode来分配新的数据节点到数据流管道中,并使用新的时间戳重建数据流管道。
数据流管道重新建立之后,输出流会调用ClientProtocol.updatePipeline()更新Namenode中的元数据。
5 HDFS客户端追加写流程
打开已有的HDFS文件:
客户端调用ClientProtocol.append()获取最后一个数据块的位置信息;若写满则创建新的空数据块,获取文件租约,并构建新的DFSOutputStream对象返回建立数据流管道
通过数据流管道写入数据
关闭输入流并提交文件
DFSOutputStream.close()关闭;ClientProtocal.complete()提交
6 Datanode启动、心跳已经执行名字节点指令流程
握手:
Datanode启动,会通过DatanodeProtocol.versionRequest()获取Namenode版本以及存储信息,与Datanode的版本信息比较,确保一致注册:
Datanode通过DatanodeProtocol.register()方法向Namenode注册,Namenode接受到注册请求后,判断版本号是否一致块汇报和缓存汇报:
注册成功后,Datanode就需要将本地的所有数据块以及缓存数据块上报到Namenode,Namenode会利用这些信息重新建立内存中数据块与Datanode之间的对应关系
(名字节点就是通过DataNodeCommand对象在Namenode与DataNode之间传递命令,是所有名字节点对数据节点指令的基类。)
上报之后数据节点通过sendHeartBeat方法与namenode通过心跳进行通信,携带自身信息的datanodeRegistration对象以及当前数据节点的状态,返回一个DataNodeCommand对象。
Datanode成功启动后,需要定期向Namenode发送心跳,让Namenode知道当前的Datanode的状态。
Namenode会在给Datanode的心跳响应中携带名字节点指令,知道Datanode进行数据操作。
7 HA切换流程
Active Namenode的命名空间和Standby Namenode是实时同步的;
数据节点同时向他们两发送心跳
为了命名空间的一致性,两个节点都需要和一组独立运行的节点通信(Journal Nodes ,JNS);Active NameNode会定期将执行的操作记录在editlog中;Standby NameNode会一直监听JNS上的日志变化,进行读取editlog并于当前的命名空间合并。
- 手动主备切换
- 自动故障切换
使用Zookeeper集群
ZKFariloverControlller,会实时监控Namenode的HA,自动出发主备切换
Boy
20180725