数据块的复制当然需要一个源数据节点,从其上拷贝数据块至目标数据节点。那么数据块复制是如何选取复制源节点的呢?本文我们将针对这一问题进行研究。

在BlockManager中,chooseSourceDatanode()方法就是用来选取数据块复制时的源节点的,它负责解析数据块所属数据节点列表,并选择一个,用它作为数据块的复制源。其核心逻辑如下:

我们优先选择正处于退役过程中的数据节点而不是其他节点,因为前者没有写数据传输量因此相对不是很繁忙。我们不使用已退役节点作为数据源。否则我们从它们之中随机选择一个数据节点,其复制工作量还没有达到阈值,然而,如果一个复制是最高优先级的复制的话,我们会随机选择一个数据节点,而不管复制阈值的限制。

chooseSourceDatanode()方法代码如下:

  1. @VisibleForTesting
  2. DatanodeDescriptor chooseSourceDatanode(Block block,
  3. List<DatanodeDescriptor> containingNodes,
  4. List<DatanodeStorageInfo>  nodesContainingLiveReplicas,
  5. NumberReplicas numReplicas,
  6. int priority) {
  7. // 清空containingNodes列表
  8. // 包含指定block的节点列表
  9. containingNodes.clear();
  10. // 清空nodesContainingLiveReplicas列表
  11. // 包含指定block活跃副本的节点列表
  12. nodesContainingLiveReplicas.clear();
  13. DatanodeDescriptor srcNode = null;
  14. int live = 0;
  15. int decommissioned = 0;
  16. int corrupt = 0;
  17. int excess = 0;
  18. // 根据Block实例block从corruptReplicas中获取坏块副本所在数据节点集合nodesCorrupt
  19. Collection<DatanodeDescriptor> nodesCorrupt = corruptReplicas.getNodes(block);
  20. // 根据Block实例block从blocksMap中获取其对应的数据节点存储DatanodeStorageInfo实例storage
  21. for(DatanodeStorageInfo storage : blocksMap.getStorages(block)) {
  22. // 从数据节点存储DatanodeStorageInfo实例storage中获取数据节点描述信息node
  23. final DatanodeDescriptor node = storage.getDatanodeDescriptor();
  24. // 从excessReplicateMap集合中获取数据块集合excessBlocks,
  25. // 这些块对数据节点来说是多余的。我们最终会将这些多余的块删除。
  26. LightWeightLinkedSet<Block> excessBlocks =
  27. excessReplicateMap.get(node.getDatanodeUuid());
  28. // 根据数据节点的存储状态确定其是否为可用副本
  29. int countableReplica = storage.getState() == State.NORMAL ? 1 : 0;
  30. // 如果坏块节点集合nodesCorrupt中包含该节点,坏块数corrupt累加
  31. if ((nodesCorrupt != null) && (nodesCorrupt.contains(node)))
  32. corrupt += countableReplica;
  33. // 如果节点正在退役或者已经退役,退役数decommissioned累加
  34. else if (node.isDecommissionInProgress() || node.isDecommissioned())
  35. decommissioned += countableReplica;
  36. // 如果多余数据块集合中包含该数据块,则多余数excess累加
  37. else if (excessBlocks != null && excessBlocks.contains(block)) {
  38. excess += countableReplica;
  39. // 其他情况下
  40. } else {
  41. // 将该存储添加到nodesContainingLiveReplicas集合
  42. nodesContainingLiveReplicas.add(storage);
  43. // 累加活跃副本数live
  44. live += countableReplica;
  45. }
  46. // 将该节点添加到containingNodes集合
  47. containingNodes.add(node);
  48. // Check if this replica is corrupt
  49. // If so, do not select the node as src node
  50. // 如果为坏块,跳过
  51. if ((nodesCorrupt != null) && nodesCorrupt.contains(node))
  52. continue;
  53. // 如果复制级别不是最高级别,且数据节点正在复制的数据块数目大于等于最大复制块数maxReplicationStreams,跳过
  54. if(priority != UnderReplicatedBlocks.QUEUE_HIGHEST_PRIORITY
  55. && node.getNumberOfBlocksToBeReplicated() >= maxReplicationStreams)
  56. {
  57. continue; // already reached replication limit
  58. }
  59. // 如果数据节点getNumberOfBlocksToBeReplicated大于等于复制块数上线replicationStreamsHardLimit,跳过
  60. if (node.getNumberOfBlocksToBeReplicated() >= replicationStreamsHardLimit)
  61. {
  62. continue;
  63. }
  64. // the block must not be scheduled for removal on srcNode
  65. // 如果数据块为多余的数据块,直接跳过
  66. if(excessBlocks != null && excessBlocks.contains(block))
  67. continue;
  68. // never use already decommissioned nodes
  69. // 如果数据节点为已退役节点,跳过
  70. if(node.isDecommissioned())
  71. continue;
  72. // we prefer nodes that are in DECOMMISSION_INPROGRESS state
  73. // 如果数据节点正在退役,且srcNode还未选中,那么选择该数据节点为srcNode,并跳过
  74. if(node.isDecommissionInProgress() || srcNode == null) {
  75. srcNode = node;
  76. continue;
  77. }
  78. // 如果源数据节点srcNode正在退役,则跳过
  79. if(srcNode.isDecommissionInProgress())
  80. continue;
  81. // switch to a different node randomly
  82. // this to prevent from deterministically selecting the same node even
  83. // if the node failed to replicate the block on previous iterations
  84. if(DFSUtil.getRandom().nextBoolean())
  85. srcNode = node;
  86. }
  87. // 初始化数据块副本复制统计对象numReplicas
  88. if(numReplicas != null)
  89. numReplicas.initialize(live, decommissioned, corrupt, excess, 0);
  90. // 返回srcNode
  91. return srcNode;
  92. }

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源码分析数据块复制选取复制源节点的更多相关文章

  1. HDFS源码分析数据块复制监控线程ReplicationMonitor(二)

    HDFS源码分析数据块复制监控线程ReplicationMonitor(二)

  2. HDFS源码分析数据块校验之DataBlockScanner

    DataBlockScanner是运行在数据节点DataNode上的一个后台线程.它为所有的块池管理块扫描.针对每个块池,一个BlockPoolSliceScanner对象将会被创建,其运行在一个单独 ...

  3. HDFS源码分析数据块复制监控线程ReplicationMonitor(一)

    ReplicationMonitor是HDFS中关于数据块复制的监控线程,它的主要作用就是计算DataNode工作,并将复制请求超时的块重新加入到待调度队列.其定义及作为线程核心的run()方法如下: ...

  4. HDFS源码分析数据块复制之PendingReplicationBlocks

    PendingReplicationBlocks实现了所有正在复制的数据块的记账工作.它实现以下三个主要功能: 1.记录此时正在复制的块: 2.一种对复制请求进行跟踪的粗粒度计时器: 3.一个定期识别 ...

  5. HDFS源码分析数据块汇报之损坏数据块检测checkReplicaCorrupt()

    无论是第一次,还是之后的每次数据块汇报,名字名字节点都会对汇报上来的数据块进行检测,看看其是否为损坏的数据块.那么,损坏数据块是如何被检测的呢?本文,我们将研究下损坏数据块检测的checkReplic ...

  6. HDFS源码分析数据块之CorruptReplicasMap

    CorruptReplicasMap用于存储文件系统中所有损坏数据块的信息.仅当它的所有副本损坏时一个数据块才被认定为损坏.当汇报数据块的副本时,我们隐藏所有损坏副本.一旦一个数据块被发现完好副本达到 ...

  7. jQuery1.9.1源码分析--数据缓存Data模块

    jQuery1.9.1源码分析--数据缓存Data模块 阅读目录 jQuery API中Data的基本使用方法介绍 jQuery.acceptData(elem)源码分析 jQuery.data(el ...

  8. TeamTalk源码分析(十一) —— pc客户端源码分析

           --写在前面的话  在要不要写这篇文章的纠结中挣扎了好久,就我个人而已,我接触windows编程,已经六七个年头了,尤其是在我读研的三年内,基本心思都是花在学习和研究windows程序上 ...

  9. Netty源码分析 (七)----- read过程 源码分析

    在上一篇文章中,我们分析了processSelectedKey这个方法中的accept过程,本文将分析一下work线程中的read过程. private static void processSele ...

随机推荐

  1. 浅析 Node.js 单线程模型

    总结笔记:对于每个用户请求,由主线程接收并存放于一个事件队列中(不做任何处理),当无请求发生时,即主线程空闲,主线程开始循环处理事件队列中的任务: 对于非阻塞JS程序: 1.若某事件需要I/O操作,则 ...

  2. elasticsearch5.3.0 安装

    公司有项目打算用elasticsearch,所以研究了下,目前最新版本5.3.0 安装 1.下载包  https://artifacts.elastic.co/downloads/elasticsea ...

  3. 导出/导入Eclipse的workspace配置(备份Eclipse配置)

    设置好workspace配置后可以将配置保存为 *.epf 文件. 进入 File -> Export : 选择 General -> Preferences ,下一步: 选择 Expor ...

  4. 有关奇葩的mex编程时的matlab出现栈内存错误的问题

    错误提示信息 (ntdll.dll) (MATLAB.exe中)处有未经处理的异常:0xC0000374:堆已损坏 该错误的表现是,matlab调用.mexw64函数时,第一次调用正常,第二次调用出现 ...

  5. 【IntellJ IDEA】idea启动测试类报错Error running 'Test1.test': Command line is too long. Shorten command line for Test1.test or also for JUnit default configuration.

    idea启动测试类报错 Error running 'Test1.test': Command line is too long. Shorten command line for Test1.tes ...

  6. VUE -- 十分钟入门 Less

    这篇文章来自 Danny Markov, 是我最喜欢的博主之一,实际上我最近翻译的一些文章全是出自他手.在查看本文之前你也可以 查看原文. 我们都知道写 CSS 代码是有些枯燥无味的,尤其是面对那些成 ...

  7. linux selenium运行chrome

    chrome版本要和chromedriver版本匹配才能正常运行.

  8. Maven配置Spring+Hibernate Shiro权限控制项目

    前言:在Eclipse中安装好Maven插件,然后创建一个Sample项目.在Eclipse中检出Shiro的官方演示样例.地址http://svn.apache.org/repos/asf/shir ...

  9. 点击Div,显示其innerHTML

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  10. mysql ubuntu 开启3306端口,设置远程访问

    远程登陆数据库的时候出现了下面出错信息 :ERROR 2003 ( HY000 ) : Can 't connect to MySQL server on ' xxx.xxx.xxx.xxx ',经过 ...