HDFS源码分析数据块复制选取复制源节点
数据块的复制当然需要一个源数据节点,从其上拷贝数据块至目标数据节点。那么数据块复制是如何选取复制源节点的呢?本文我们将针对这一问题进行研究。
在BlockManager中,chooseSourceDatanode()方法就是用来选取数据块复制时的源节点的,它负责解析数据块所属数据节点列表,并选择一个,用它作为数据块的复制源。其核心逻辑如下:
我们优先选择正处于退役过程中的数据节点而不是其他节点,因为前者没有写数据传输量因此相对不是很繁忙。我们不使用已退役节点作为数据源。否则我们从它们之中随机选择一个数据节点,其复制工作量还没有达到阈值,然而,如果一个复制是最高优先级的复制的话,我们会随机选择一个数据节点,而不管复制阈值的限制。
chooseSourceDatanode()方法代码如下:
- @VisibleForTesting
- DatanodeDescriptor chooseSourceDatanode(Block block,
- List<DatanodeDescriptor> containingNodes,
- List<DatanodeStorageInfo> nodesContainingLiveReplicas,
- NumberReplicas numReplicas,
- int priority) {
- // 清空containingNodes列表
- // 包含指定block的节点列表
- containingNodes.clear();
- // 清空nodesContainingLiveReplicas列表
- // 包含指定block活跃副本的节点列表
- nodesContainingLiveReplicas.clear();
- DatanodeDescriptor srcNode = null;
- int live = 0;
- int decommissioned = 0;
- int corrupt = 0;
- int excess = 0;
- // 根据Block实例block从corruptReplicas中获取坏块副本所在数据节点集合nodesCorrupt
- Collection<DatanodeDescriptor> nodesCorrupt = corruptReplicas.getNodes(block);
- // 根据Block实例block从blocksMap中获取其对应的数据节点存储DatanodeStorageInfo实例storage
- for(DatanodeStorageInfo storage : blocksMap.getStorages(block)) {
- // 从数据节点存储DatanodeStorageInfo实例storage中获取数据节点描述信息node
- final DatanodeDescriptor node = storage.getDatanodeDescriptor();
- // 从excessReplicateMap集合中获取数据块集合excessBlocks,
- // 这些块对数据节点来说是多余的。我们最终会将这些多余的块删除。
- LightWeightLinkedSet<Block> excessBlocks =
- excessReplicateMap.get(node.getDatanodeUuid());
- // 根据数据节点的存储状态确定其是否为可用副本
- int countableReplica = storage.getState() == State.NORMAL ? 1 : 0;
- // 如果坏块节点集合nodesCorrupt中包含该节点,坏块数corrupt累加
- if ((nodesCorrupt != null) && (nodesCorrupt.contains(node)))
- corrupt += countableReplica;
- // 如果节点正在退役或者已经退役,退役数decommissioned累加
- else if (node.isDecommissionInProgress() || node.isDecommissioned())
- decommissioned += countableReplica;
- // 如果多余数据块集合中包含该数据块,则多余数excess累加
- else if (excessBlocks != null && excessBlocks.contains(block)) {
- excess += countableReplica;
- // 其他情况下
- } else {
- // 将该存储添加到nodesContainingLiveReplicas集合
- nodesContainingLiveReplicas.add(storage);
- // 累加活跃副本数live
- live += countableReplica;
- }
- // 将该节点添加到containingNodes集合
- containingNodes.add(node);
- // Check if this replica is corrupt
- // If so, do not select the node as src node
- // 如果为坏块,跳过
- if ((nodesCorrupt != null) && nodesCorrupt.contains(node))
- continue;
- // 如果复制级别不是最高级别,且数据节点正在复制的数据块数目大于等于最大复制块数maxReplicationStreams,跳过
- if(priority != UnderReplicatedBlocks.QUEUE_HIGHEST_PRIORITY
- && node.getNumberOfBlocksToBeReplicated() >= maxReplicationStreams)
- {
- continue; // already reached replication limit
- }
- // 如果数据节点getNumberOfBlocksToBeReplicated大于等于复制块数上线replicationStreamsHardLimit,跳过
- if (node.getNumberOfBlocksToBeReplicated() >= replicationStreamsHardLimit)
- {
- continue;
- }
- // the block must not be scheduled for removal on srcNode
- // 如果数据块为多余的数据块,直接跳过
- if(excessBlocks != null && excessBlocks.contains(block))
- continue;
- // never use already decommissioned nodes
- // 如果数据节点为已退役节点,跳过
- if(node.isDecommissioned())
- continue;
- // we prefer nodes that are in DECOMMISSION_INPROGRESS state
- // 如果数据节点正在退役,且srcNode还未选中,那么选择该数据节点为srcNode,并跳过
- if(node.isDecommissionInProgress() || srcNode == null) {
- srcNode = node;
- continue;
- }
- // 如果源数据节点srcNode正在退役,则跳过
- if(srcNode.isDecommissionInProgress())
- continue;
- // switch to a different node randomly
- // this to prevent from deterministically selecting the same node even
- // if the node failed to replicate the block on previous iterations
- if(DFSUtil.getRandom().nextBoolean())
- srcNode = node;
- }
- // 初始化数据块副本复制统计对象numReplicas
- if(numReplicas != null)
- numReplicas.initialize(live, decommissioned, corrupt, excess, 0);
- // 返回srcNode
- return srcNode;
- }
chooseSourceDatanode()方法的整体逻辑如下:
1、清空containingNodes列表:containingNodes为包含指定block的节点描述信息DatanodeDescriptor列表;
2、清空nodesContainingLiveReplicas列表:nodesContainingLiveReplicas为包含指定block活跃副本的节点存储DatanodeStorageInfo列表;
3、根据Block实例block从corruptReplicas中获取坏块副本所在数据节点集合nodesCorrupt;
4、根据Block实例block从blocksMap中获取其对应的数据节点存储DatanodeStorageInfo集合,并遍历每一个数据节点存储DatanodeStorageInfo实例storage:
4.1、从数据节点存储DatanodeStorageInfo实例storage中获取数据节点描述信息node;
4.2、从excessReplicateMap集合中获取数据块集合excessBlocks:这些块对数据节点来说是多余的,我们最终会将这些多余的块删除;
4.3、根据数据节点的存储状态确定其是否为可用副本countableReplica;
以下为统计数据块副本情况:
4.4、如果坏块节点集合nodesCorrupt中包含该节点,坏块数corrupt累加;
4.5、如果节点正在退役或者已经退役,退役数decommissioned累加;
4.6、如果多余数据块集合中包含该数据块,则多余数excess累加;
4.7、其他情况下:
4.7.1、将该存储添加到nodesContainingLiveReplicas集合;
4.7.2、累加活跃副本数live;
4.8、将该节点添加到containingNodes集合;
4.9、如果为坏块,跳过;
4.10、如果复制级别不是最高级别,且节点正在复制的数据块数目大于等于最大复制块数maxReplicationStreams,跳过;
4.11、如果数据节点getNumberOfBlocksToBeReplicated大于等于复制块数上线replicationStreamsHardLimit,跳过;
4.12、如果数据块为多余的数据块,直接跳过;
4.13、如果数据节点为已退役节点,跳过;
4.14、如果数据节点正在退役,且srcNode还未选中,那么选择该数据节点为srcNode,并跳过;
4.15、如果源数据节点srcNode正在退役,则跳过;
4.16、随机选择源数据节点;
5、初始化数据块副本复制统计对象numReplicas;
6、返回块复制源数据节点srcNode。
其中,有两个阈值需要单独说下,如下:
1、maxReplicationStreams:一个给定节点除最高优先级复制外复制流的最大数目,取参数dfs.namenode.replication.max-streams,参数未配置默认为2;
2、replicationStreamsHardLimit:一个给定节点全部优先级复制复制流的最大数目,取参数dfs.namenode.replication.max-streams-hard-limit,参数未配置默认为4。
从上述整理流程中,大致总结如下:
根据block从blocksMap中取数据块所在数据节点存储实例集合并遍历,统计数据块副本情况,包括损坏副本、多余副本、退役副本、活跃副本等,然后损坏副本、多余副本、退役节点直接跳过,这三种情况不能被选中为复制源数据节点,并且还有两种情况,一是如果复制级别不是最高级别,且数据节点正在复制的数据块数目大于等于最大复制块数maxReplicationStreams,二是如果数据节点正在复制的数据块数目大于等于复制块数上线replicationStreamsHardLimit,这两种情况也直接跳过,不能被选中为复制源数据节点,剩下的,则是随机选择源数据节点,并且其最喜欢选择正在退役的数据节点,这个最喜欢的意思是,选择的方式是随机选择,但是一旦正在退役节点被选中,则源节点不会再做变更,否则还是要通过随机选择来变更的。
HDFS源码分析数据块复制选取复制源节点的更多相关文章
- HDFS源码分析数据块复制监控线程ReplicationMonitor(二)
HDFS源码分析数据块复制监控线程ReplicationMonitor(二)
- HDFS源码分析数据块校验之DataBlockScanner
DataBlockScanner是运行在数据节点DataNode上的一个后台线程.它为所有的块池管理块扫描.针对每个块池,一个BlockPoolSliceScanner对象将会被创建,其运行在一个单独 ...
- HDFS源码分析数据块复制监控线程ReplicationMonitor(一)
ReplicationMonitor是HDFS中关于数据块复制的监控线程,它的主要作用就是计算DataNode工作,并将复制请求超时的块重新加入到待调度队列.其定义及作为线程核心的run()方法如下: ...
- HDFS源码分析数据块复制之PendingReplicationBlocks
PendingReplicationBlocks实现了所有正在复制的数据块的记账工作.它实现以下三个主要功能: 1.记录此时正在复制的块: 2.一种对复制请求进行跟踪的粗粒度计时器: 3.一个定期识别 ...
- HDFS源码分析数据块汇报之损坏数据块检测checkReplicaCorrupt()
无论是第一次,还是之后的每次数据块汇报,名字名字节点都会对汇报上来的数据块进行检测,看看其是否为损坏的数据块.那么,损坏数据块是如何被检测的呢?本文,我们将研究下损坏数据块检测的checkReplic ...
- HDFS源码分析数据块之CorruptReplicasMap
CorruptReplicasMap用于存储文件系统中所有损坏数据块的信息.仅当它的所有副本损坏时一个数据块才被认定为损坏.当汇报数据块的副本时,我们隐藏所有损坏副本.一旦一个数据块被发现完好副本达到 ...
- jQuery1.9.1源码分析--数据缓存Data模块
jQuery1.9.1源码分析--数据缓存Data模块 阅读目录 jQuery API中Data的基本使用方法介绍 jQuery.acceptData(elem)源码分析 jQuery.data(el ...
- TeamTalk源码分析(十一) —— pc客户端源码分析
--写在前面的话 在要不要写这篇文章的纠结中挣扎了好久,就我个人而已,我接触windows编程,已经六七个年头了,尤其是在我读研的三年内,基本心思都是花在学习和研究windows程序上 ...
- Netty源码分析 (七)----- read过程 源码分析
在上一篇文章中,我们分析了processSelectedKey这个方法中的accept过程,本文将分析一下work线程中的read过程. private static void processSele ...
随机推荐
- Shellcode开发辅助工具shellnoob
Shellcode开发辅助工具shellnoob Shellcode开发的过程中会遇到很多繁杂的工作,如编译.反编译.调试等.为了减少这部分工作,Kali Linux提供了开发辅助工具shelln ...
- [Beijing2010组队]次小生成树Tree
小C最近学了很多最小生成树的算法,Prim算法.Kurskal算法.消圈算法等等.正当小C洋洋得意之时,小P又来泼小C冷水了.小P说,让小C求出一个无向图的次小生成树,而且这个次小生成树还得是严格次小 ...
- centos忘记密码,重新设置密码的方法
(1)重新启动Centos,在启动过程中,长按“ESC”键,进入GNU GRUB界面. (2)选择要进入的系统,按“E”键(在启动之前编辑命令). (3)选择第二项操作系统的内核“kernel”,按& ...
- 删除VisualStudio 2013中的 "send Feedback" 按钮
在VisualStudio 2013中,在标题栏中增加了一个 "send Feedback" 按钮,用于给微软发送Bug和回馈(或者一个哭脸和笑脸).这个按钮对于开发来说基本上没用 ...
- SQL Server 2017 EXPRESS 安装 SQLCMD 设置远程连接
1.配置管理器内启动TCP/IP协议(端口改为1433)以及加入防火墙允许 2.进入本地实例: cmd Microsoft Windows [版本 ] (c) Microsoft Corporatio ...
- c++中resize这个函数怎么用
c++中序列式容器的一个共性函数, vv.resize(int n,element)表示调整容器vv的大小为n,扩容后的每个元素的值为element,默认为0 resize()会改变容器的容量和当前元 ...
- html 基础二
HTMLCSS基础2 一.HTML中的标签 1.1标签的特点 给文本加上含有语义的标签 应该学习更多具体语义标签 标签:用“<>”包起来的内容 2.1 更多的标签 2.1.1 h系列的标签 ...
- Qt之QStyledItemDelegate类
主要用于自定义项的display和编辑: 通常有两个重载函数: // 决定该单元格的推荐大小 virtual QSize sizeHint(const QStyleOptionViewItem &am ...
- 使用nvDXT.exe把图片转换成dds图片【转】
从nvidia官网下载工具包DDS Utilities [https://developer.nvidia.com/legacy-texture-tools] 转换图片格式需要的工具是 nvdxt.e ...
- 【Hadoop】Hadoop DataNode节点超时时间设置
hadoop datanode节点超时时间设置 datanode进程死亡或者网络故障造成datanode无法与namenode通信,namenode不会立即把该节点判定为死亡,要经过一段时间,这段时间 ...