NameNode启动之后,回加载fsimage和editlog文件重建文件系统目录树,但是对于数据块与DataNode之间的映射关系却需要在DataNode上报后动态构建。
DataNode启动之后,除了会与NameNode握手,注册以及上报数据块,还会定时向NameNode发送心跳及数据块副本汇报,并执行NameNode的传回的指令。所以NameNode中会有很大一部分逻辑是与DataNode相关的,包括添加和删除DataNode,与DataNode启动过程的交互、处理DataNode发送心跳
一 DataNodeDescriptor
DataNodeDescriptor是NameNode对DataNode的抽象,继承自DataNodeInfo,继承关系DatanodeID<-DatanodeInfo<-DataNode
Descriptor
DatanodeID:用于表示一个唯一的DataNode
DataNodeInfo:对DataNode的简单描述,比如容量,使用的空间,剩余空间,数据块池使用量,缓存容量,缓存使用量等
DataNodeDescriptor: 详细描述DataNode的类
状态相关:isAlive,decommissionStatus,curApproxBlocksScheduled
指令相关:bandwith,replicateBlocks,recoverBlocks,invalidateBlocks
缓存相关:pendingCache,cached.pendingUncached
二 DataNodeStroageInfo
DataNodeStroageInfo描述了DataNode上的一个存储,一个DataNode可以定义多个存储,这些存储可以是不相同的。
blockList:DataNodeStroageInfo最重要的数据结构,用来记录当前存储上保存的数据块副本链表的头结点。当DataNode报告NameNode成功接收一个数据块副本后,NameNode会调用DatanodeStorageInfo的addBlock方法在DatanodeStorageInfo的blockList添加这个副本对应的BlockInfo
状态相关:heartbeatedSinceFailover,当NameNode出现失败后,会将这个字段设置为false,正常接收心跳后,会将这个字段设置为true.
blockCOntentStale:当NameNode出现失败或者正在启动时,DataNode会挂起上一次NameNode发起的删除操作。这个时候我们就认为当前存储为stale状态,直到NameNode收到了这个存储的的块汇报
当DataNode向NameNode汇报该DataNode存储上接受到了一个新的数据块副本的时候,BlockManager会调用addBlock在NameNode的映射关系中添加。AddBlock首先调用BlockInfo.addStorage将自己也就是DatanodeStorageInfo对象添加到数据块所属的Datanodes存储列表中,也就是BlockInfo的triplets[],然后调用BlockInfo.listInsert将数据插入到DataNode存储管理的数据块链表中。
三 DataNodeManager
datanodeMap:维护StorageId-> DatanodeDescriptor的映射关系
host2DataNodeMap:维护host-> DataNodeDescriptor的映射关系
networktopology:维护整个网络的拓扑结构
添加和撤销DataNode
当我们需要增加容量,可以动态的向集群中添加新的DataNode;同理,当HDFS需要减小规模时,可以动态的撤销已经存在的Datanode.且两种情况都不影响HDFS服务
HDFS提供了dfs.hosts文件,又称include文件以及dfs.hosts.exclude文件管理接入到HDFS的datanode。
Include文件指定了可以连接到NameNode的DataNode列表,exclude文件指定了不能连接到NameNode的DataNode列表
HDFS管理员讲一个DataNode添加到集群中,需要在include文件中添加一条该DataNode的记录,然后调用dfsadmin–refreshNodes命令刷新名字节点的信息,最后才能启动DataNode
撤销节点,将要撤销的节点记录添加到exclude文件,也是调用dfsadmin–refreshNodes命令,被撤销的节点上的数据块就会复制到其他节点上
dfsadmin–refreshNodes会通过ClientProtocal.refreshNodes方法通知NameNode更新include和exclude文件,这个操作最终会由DataNodeManager.refreshNode方法完成,首先调用refreshHostReader将include与exclude文件加载到hostFileManager对象中,之后调用refreshDataNodes刷新所有的数据节点。
refreshDataNodes会遍历DatanodeManager.datanodeMap字段中保存的所有的DatanodeDescriptor对象,对不可以连接到NameNode的DataNode设置对应的DatanodeDescriptor.isAllowed字段为false.
DataNode心跳报告
通过DataNode的BlockPoolManager的BPServiceActor,DataNode的
BPServiceActor会向NameNode发送心跳,包括注册信息,存储信息,缓存信息等,NameNode收到之后,调用BlockManager.handleHeartBeat
方法返回一个心跳响应。这个心跳响应包括一个DatanodeCommand数组,用来携带NameNode对DataNode的指令,例如数据块副本的复制删除等
NameNode还会启动一个线程,周期性检测所有DataNode上报的心跳,对于长时间没有上报心跳的DataNode,则认为DataNode出故障,不能正常工作