HDFS
一、HDFS节点角色:
(1)namenode:1、用来存储HDFS的元数据信息,这里的元数据信息指的是文件系统的命名空间。启动时,将这些信息加载到namenode内存。
2、元数据信息也会在磁盘上保存成fsimage和edit log 文件。
3、namenode的内存中也会保存文件的具体信息,如:一个文件包含哪些数据块,分布在哪些节点上。这些信息在系统启动时从datenode上进行收集。
(2)secondary namenode:用于周期性的合并namenode中的fsimage文件和edit log文件。文件合并之后,会返回给namenode,同时secondary 里也会保存一份,用于namenode故障恢复。
(3)datenode:存储文件数据块数据。定时向namenode汇报存储的数据块信息。
补充:
数据块:HDFS将文件分块存储,文件块默认大小为128M,可以设置。mapreduce的maptask数量和文件块的数量对应。其中,文件块不宜太小,增加寻址时间;
同时文件也不宜过大,过大导致maptask太少,少于集群节点数量,没有充分利用集群资源,导致作业运行速度慢。
文件分块的优势:a、存储文件大小可以大于任何一个集群中磁盘容量;
b、不使用整个文件进行存储,简化存储子系统设计,这点比较虚
c、利于数据备份,有利于集群高可用。
二、HDFS读写机制:
1、HDFS的读机制:客户端读hdfs文件
1、初始化 DistributedFileSystem 对象 ,clinet客户端调用 DistributedFileSystem中的 open() 方法打开文件。
2、DistributedFileSystem对象调用远程RPC服务,获取namenode上的文件的数据块信息,每个数据块 namenode 返回数据块的节点地址。
3、DistributedFileSystem 创建一个 FSDataIputStream 给 client 客户端,client 客户端调用 FSDataIputStream 中的 read() 方法开始读取数据。
4、FSDataIputStream 连接保存此文件的第一个数据块的 DataNode,读取数据。
5、数据读取完毕,关闭流连接,连接文件的下一个数据块的 DataNode。
6、client 客户端将文件读取完毕后,调用 FSDataInputStream 的 close() 方法关闭文件输入流连接。
注意:若在读数据的过程中,客户端和DataNode的通信出现错误,则会尝试连接下一个 包含次文件块的 DataNode。同时记录失败的 DataNode,此后不再被连接。
2、HDFS写机制:客户端向hdfs中写数据
1、client客户端调用DistributedFileSystem对象 分布式文件系统对象 的create()方法,创建一个文件输出流FSDataOutputStream对象。
2、DistributedFileSystem对象和Hadoop中的namenode进行一次远程RPC调用,在namenode中创建一个文件条目Entry,该条目没有任何的block。
3、client通过 FSDataOutputStream 文件输出流向datanode中写数据,数据首先被写入 FSDataOutputStream 内部的 buffer 中,然后被分成一个个的 packet 数据包。
4、以 packet 数据包为最小单位,向集群中的一个 DataNode 发送数据,在这组 DataNode 组成的Pipeline线管上依次传输 packet 数据包。
5、在Pipeline线管的反向上依次发送ack,最终由第一个DataNode发送ack给client客户端。
6、文件传输结束,客户端调用 FSDataOutputStream 的 close 方法关闭流。
7、客户端调用 DistributedFileSystem 的 complete() 方法,通知 namenode 文件写入成功。
写数据流程
)客户端向namenode发送上传文件请求,namenode对要上传目录和文件进行检查,判断是否可以上传,并向客户端返回检查结果。
2)客户端得到上传文件的允许后读取客户端配置,如果没有指定配置则会读取默认配置(例如副本数和块大小默认为3和128M,副本是由客户端决定的)。向namenode请求上传一个数据块。
3)namenode会根据客户端的配置来查询datanode信息,如果使用默认配置,那么最终结果会返回同一个机架的两个datanode和另一个机架的datanode。这称为“机架感知”策略。这种策略设置可以将副本均匀分布在集群中,有利于当组件失效情况下的负载均衡。
4)客户端在开始传输数据块之前会把数据缓存在本地,当缓存大小超过了一个数据块的大小,客户端就会从namenode获取要上传的datanode列表。之后会在客户端和第一个datanode建立连接开始流式的传输数据,这个datanode会一小部分一小部分(4K)的接收数据然后写入本地仓库,同时会把这些数据传输到第二个datanode,第二个datanode也同样一小部分一小部分的接收数据并写入本地仓库,同时传输给第三个datanode,依次类推。
这样逐级调用和返回之后,待这个数据块传输完成客户端后告诉namenode数据块传输完成,这时候namenode才会更新元数据信息记录操作日志。
5)第一个数据块传输完成后会使用同样的方式传输下面的数据块直到整个文件上传完成。
HDFS数据写入细节
a.请求和应答是使用RPC的方式,客户端通过ClientProtocol与namenode通信,namenode和datanode之间使用DatanodeProtocol交互。在设计上,namenode不会主动发起RPC,而是响应来自客户端或datanode的RPC请求。客户端和datanode之间是使用socket进行数据传输,和namenode之间的交互采用nio封装的RPC。
b.HDFS有自己的序列化协议。
c.在数据块传输成功后但客户端没有告诉namenode之前如果namenode宕机那么这个数据块就会丢失。
d.在流式复制时,逐级传输和响应采用响应队列来等待传输结果。队列响应完成后返回给客户端。
c.在流式复制时如果有一台或两台(不是全部)没有复制成功,不影响最后结果,只不过datanode会定期向namenode汇报自身信息。如果发现异常namenode会指挥datanode删除残余数据和完善副本。如果副本数量少于某个最小值就会进入安全模式。
3、客户端从hdfs读写数据出错:
(1)Hdfs读数据出错:
若在读数据的过程中,客户端和DataNode的通信出现错误,则会尝试连接下一个 包含次文件块的DataNode。同时记录失败的DataNode,此后不再被连接。
(2)Hdfs在写某一个副本数据的时候出错:
1)首先会关闭dataNode联通的线管。
2)将已发送至线管内,还没有收到确认消息的数据包重新写回到数据队列中,保证数据不丢失。
3)将当前正常工作的dataNode赋予新的版本号,这样保证即使故障dataNode节点恢复了,由于版本号不对,故障dataNode也将会被剔除。这个新的版本号是利用nameNode的租约信息获取。
4)在当前正常的dataNode中选择一个主dataNode,并与其他的dataNode进行通信,来获取每个dataNode的当前数据块的大小,然后选择出一个最小的值,将当前的所有dataNode都同步到该大小。再重新建立线管。
5)在线管中删除故障节点,将数据写入线管中正常的dataNode,即新管道。
6)当文件关闭后,若nameNode发现副本数不足,会在其他的dataNode上创建新的副本。
三、HDFS的HA高可用机制:
1、Hdfs的HA高可用:
保证Hdfs高可用,其实就是保证namenode的高可用,保证namenode的高可用的机制有两个,edit log共享机制+ZKFC。ZKFC就是ZookeeperFailOverController,即zookeeper故障转移控制器。
2、nameNode的高可用机制:
(1)nameNode想要实现高可用,意味着集群中要存在多个nameNode,在nameNode出现故障的时候,能够进行快速切换。集群中平时只有一个nameNode在工作,这个nameNode就是active的,而其他nameNode是standby的。
(2)为了保证nameNode出现故障的时候,nameNode的切换速度。active nameNode将信息写入共享编辑日志文件,standby nameNode则读取共享文件,从而保持与active nameNode的同步。
(3)此外,集群中的dataNode要向所有的nameNode发送数据块处理报告。
(4)故障切换这个动作就需要ZKFC来保证,每个nameNode中都运行着一个ZKFC故障转移控制器,用于监视nameNode进程。而这个ZKFC是基于Zookeeper实现的,在启动的时候,会创建HealthMonitor和ActiveStandbyElector这两个组件,创建的同时,ZKFC也会向这两个组件中注册相应的回调方法。
(5)HealthMonitor初始化完成后会启动内部线程来定时调用nameNode的HAServiceProtocol Rpc接口,对nameNode进行健康监测。
(6)HealthMonitor如果检查到nameNode的健康状态发生了变化,就会回调ZKFC注册的相应方法进行处理。
(7)如果ZKFC经过判断后,认为需要进行主备切换话,会首先使用ActiveStandbyElector来进行自动的主备选举。
(8)ActiveStandbyElector完成了自动的主备选举后,会回调ZKFC的相应方法,通知相应的nameNode成为主nameNode或者备nameNode。
(9)ZKFC调用相应nameNode的HAServiceProtocol Rpc接口方法,将相应的nameNode设置成active或者standby。
3、Fencing实现:
当某个 NameNode 竞选成功,成功创建 ActiveStandbyElectorLock 临时节点后会创建另一个名为 ActiveBreadCrumb 的持久节点,该节点保存了 NameNode 的地址信息,正常情况下删除 ActiveStandbyElectorLock 节点时会主动删除 ActiveBreadCrumb,但如果由于异常情况导致 Zookeeper Session关闭,此时临时节点 ActiveStandbyElectorLock 会被删除,但持久节点 ActiveBreadCrumb 并不会删除,当有新的 NameNode 竞选成功后它会发现已经存在一个旧的 NameNode 遗留下来的 ActiveBreadCrumb 节点,此时会通知 ZKFC 对旧的 ANN 进行 fencing,
在进行 fencing 的时候,会执行以下的操作:
- 首先尝试调用这个旧 Active NameNode 的 HAServiceProtocol RPC 接口的 transitionToStandby 方法,看能不能把它转换为 Standby 状态。
- 如果 transitionToStandby 方法调用失败,那么就执行 Hadoop 配置文件之中预定义的隔离措施,Hadoop 目前主要提供两种隔离措施,通常会选择 sshfence:
- sshfence: 通过 SSH 登录到目标机器上,执行命令 fuser 将对应的进程杀死;
- shellfence: 执行一个用户自定义的 shell 脚本来将对应的进程隔离;
具体如何使用可以参阅Hadoop NameNode HA fencing
4、如何判断是否发生了脑裂:
判断持久化节点是否存在,持久化节点存在就是脑裂。